diff --git a/.dependabot/config.yml b/.dependabot/config.yml new file mode 100644 index 000000000..49ddd61b0 --- /dev/null +++ b/.dependabot/config.yml @@ -0,0 +1,11 @@ +--- +version: 1 +update_configs: + - package_manager: "javascript" + directory: "/" + update_schedule: "live" + default_reviewers: + - "markmcdowell" + commit_message: + prefix: "chore" + include_scope: true diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..9cfd3227a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,34 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +# Matches multiple files with brace expansion notation +# Set default charset +[*.{js,ts,py}] +charset = utf-8 + +# 4 space indentation +[*.py] +indent_style = space +indent_size = 4 + +# Tab indentation (no size specified) +[Makefile] +indent_style = tab + +# Indentation override for all JS, TS +[*.{js,ts,tsx}] +indent_style = space +indent_size = 4 +insert_final_newline = true + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 000000000..94a82bed7 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,23 @@ +--- +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 60 + +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 + +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security + +# Label to use when marking an issue as stale +staleLabel: wontfix + +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. + +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..181da289b --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,26 @@ +--- +name: Node CI + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [8.x, 10.x, 12.x] + + steps: + - uses: actions/checkout@v1 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: npm ci, build, and test + run: | + npm ci + npm run build --if-present + npm test + env: + CI: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..09c09a023 --- /dev/null +++ b/.gitignore @@ -0,0 +1,66 @@ +# Logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# 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 + +# build output +dist/ +lib/ +server.js +dump.rdb + +# ignore mac files +.DS_Store diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..e50272b0b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "launch", + "name": "Launch Chrome against localhost", + "url": "http://localhost:8000", + "webRoot": "${workspaceFolder}" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..bf830bfcd --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "editor.codeActionsOnSave": { + "source.fixAll.tslint": true, + } +} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..9cbfe6b00 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# 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, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, 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 support@reactivemarkets.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and 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 https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..9b7340d69 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,21 @@ +# Contributing + +We welcome all contributions! + +Before creating a PR, please raise an issue to discuss it. + +## Style + +The codebase is written in typescript, this is set to be strict with all warnings and errors turned on. We also use ts-lint with pretty much all rules turned on. Both are run as part of the build. + +You can run `npm run lint` to show any style issues separate from the build. + +### Guidelines + +* Use Promises instead callbacks +* Files should be 100 lines or less +* Everything is testable + +## Tests + +Tests are using jest, please see existing tests for recommended formatting. diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..1ffc51b2f --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +The MIT License (MIT) +https://github.com/reactivemarkets/react-financial-charts + +Copyright (c) 2015-2018 Ragu Ramaswamy +Copyright (c) 2016 Julien Renaux +Copyright (c) 2019 Reactive Markets + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 000000000..b9c4da7e0 --- /dev/null +++ b/README.md @@ -0,0 +1,67 @@ +# React Financial Charts + +Create highly customizable stock charts. + +Built with [React JS](http://facebook.github.io/react/) and [d3](http://d3js.org/) + +- integrates multiple chart types +- over 60 technical indicators and overlays +- drawing objects + +Multiple [quick start examples](https://github.com/rrag/react-stockcharts-examples2) + +- svg and canvas for improved performance. +- pan and zoom, on touch devices too + +## Chart types + +- Scatter +- Area +- Line +- Candlestick +- OHLC +- HeikenAshi +- Renko +- Kagi +- Point & Figure + +## Indicators + +- EMA, SMA, WMA, TMA +- Bollinger band +- SAR +- MACD +- RSI +- ATR +- Stochastic (fast, slow, full) +- ForceIndex +- ElderRay +- Elder Impulse + +## Interactive Indicators + +- Trendline +- Fibonacci Retracements +- Gann Fan +- Channel +- Linear regression channel + +--- + +## Installation + +```sh +npm install react-financial-charts +``` + +## Documentation + +TODO + +## Contributing + +Refer to [CONTRIBUTING.md](./CONTRIBUTING.md) + +## LICENSE + +[ISC](./LICENSE) diff --git a/lerna.json b/lerna.json new file mode 100644 index 000000000..082563770 --- /dev/null +++ b/lerna.json @@ -0,0 +1,13 @@ +{ + "packages": [ + "packages/*" + ], + "version": "0.0.0", + "npmClient": "npm", + "useWorkspaces": true, + "command": { + "bootstrap": { + "hoist": true + } + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..3ea3f9222 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,13255 @@ +{ + "name": "root", + "requires": true, + "lockfileVersion": 1, + "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==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/core": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.5.5.tgz", + "integrity": "sha512-i4qoSr2KTtce0DmkuuQBV4AuQgGPUcPXMr9L5MyYAtk06z068lQ10a4O009fe5OB/DfNV+h+qqT7ddNV8UnRjg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.5.5", + "@babel/helpers": "^7.5.5", + "@babel/parser": "^7.5.5", + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.5.5", + "@babel/types": "^7.5.5", + "convert-source-map": "^1.1.0", + "debug": "^4.1.0", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "json5": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", + "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.5.tgz", + "integrity": "sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==", + "dev": true, + "requires": { + "@babel/types": "^7.5.5", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-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==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@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==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@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==", + "dev": true + }, + "@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==", + "dev": true, + "requires": { + "@babel/types": "^7.4.4" + } + }, + "@babel/helpers": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.5.5.tgz", + "integrity": "sha512-nRq2BUhxZFnfEn/ciJuhklHvFOqjJUD5wpx+1bxUF2axL9C+v4DE/dmp5sT2dKnpOs4orZWzpAZqlCy8QqE/7g==", + "dev": true, + "requires": { + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.5.5", + "@babel/types": "^7.5.5" + } + }, + "@babel/highlight": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.5.tgz", + "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==", + "dev": true + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", + "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.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==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.4.4", + "@babel/types": "^7.4.4" + } + }, + "@babel/traverse": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.5.tgz", + "integrity": "sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.5.5", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/parser": "^7.5.5", + "@babel/types": "^7.5.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + } + }, + "@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "@cnakazawa/watch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", + "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "@evocateur/libnpmaccess": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@evocateur/libnpmaccess/-/libnpmaccess-3.1.2.tgz", + "integrity": "sha512-KSCAHwNWro0CF2ukxufCitT9K5LjL/KuMmNzSu8wuwN2rjyKHD8+cmOsiybK+W5hdnwc5M1SmRlVCaMHQo+3rg==", + "dev": true, + "requires": { + "@evocateur/npm-registry-fetch": "^4.0.0", + "aproba": "^2.0.0", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.0.0", + "npm-package-arg": "^6.1.0" + }, + "dependencies": { + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + } + } + }, + "@evocateur/libnpmpublish": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@evocateur/libnpmpublish/-/libnpmpublish-1.2.2.tgz", + "integrity": "sha512-MJrrk9ct1FeY9zRlyeoyMieBjGDG9ihyyD9/Ft6MMrTxql9NyoEx2hw9casTIP4CdqEVu+3nQ2nXxoJ8RCXyFg==", + "dev": true, + "requires": { + "@evocateur/npm-registry-fetch": "^4.0.0", + "aproba": "^2.0.0", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.0.0", + "lodash.clonedeep": "^4.5.0", + "normalize-package-data": "^2.4.0", + "npm-package-arg": "^6.1.0", + "semver": "^5.5.1", + "ssri": "^6.0.1" + }, + "dependencies": { + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + } + } + }, + "@evocateur/npm-registry-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@evocateur/npm-registry-fetch/-/npm-registry-fetch-4.0.0.tgz", + "integrity": "sha512-k1WGfKRQyhJpIr+P17O5vLIo2ko1PFLKwoetatdduUSt/aQ4J2sJrJwwatdI5Z3SiYk/mRH9S3JpdmMFd/IK4g==", + "dev": true, + "requires": { + "JSONStream": "^1.3.4", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.4.1", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "npm-package-arg": "^6.1.0", + "safe-buffer": "^5.1.2" + } + }, + "@evocateur/pacote": { + "version": "9.6.5", + "resolved": "https://registry.npmjs.org/@evocateur/pacote/-/pacote-9.6.5.tgz", + "integrity": "sha512-EI552lf0aG2nOV8NnZpTxNo2PcXKPmDbF9K8eCBFQdIZwHNGN/mi815fxtmUMa2wTa1yndotICIDt/V0vpEx2w==", + "dev": true, + "requires": { + "@evocateur/npm-registry-fetch": "^4.0.0", + "bluebird": "^3.5.3", + "cacache": "^12.0.3", + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.1.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "minimatch": "^3.0.4", + "minipass": "^2.3.5", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "normalize-package-data": "^2.5.0", + "npm-package-arg": "^6.1.0", + "npm-packlist": "^1.4.4", + "npm-pick-manifest": "^3.0.0", + "osenv": "^0.1.5", + "promise-inflight": "^1.0.1", + "promise-retry": "^1.1.1", + "protoduck": "^5.0.1", + "rimraf": "^2.6.3", + "safe-buffer": "^5.2.0", + "semver": "^5.7.0", + "ssri": "^6.0.1", + "tar": "^4.4.10", + "unique-filename": "^1.1.1", + "which": "^1.3.1" + }, + "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" + } + } + } + }, + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/core": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.9.0.tgz", + "integrity": "sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/reporters": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-changed-files": "^24.9.0", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-resolve-dependencies": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "jest-watcher": "^24.9.0", + "micromatch": "^3.1.10", + "p-each-series": "^1.0.0", + "realpath-native": "^1.1.0", + "rimraf": "^2.5.4", + "slash": "^2.0.0", + "strip-ansi": "^5.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 + }, + "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" + } + }, + "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" + } + } + } + }, + "@jest/environment": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", + "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "dev": true, + "requires": { + "@jest/fake-timers": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/reporters": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.9.0.tgz", + "integrity": "sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.2", + "istanbul-lib-coverage": "^2.0.2", + "istanbul-lib-instrument": "^3.0.1", + "istanbul-lib-report": "^2.0.4", + "istanbul-lib-source-maps": "^3.0.1", + "istanbul-reports": "^2.2.6", + "jest-haste-map": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.6.0", + "node-notifier": "^5.4.2", + "slash": "^2.0.0", + "source-map": "^0.6.0", + "string-length": "^2.0.0" + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/test-sequencer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz", + "integrity": "sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A==", + "dev": true, + "requires": { + "@jest/test-result": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0" + } + }, + "@jest/transform": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", + "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.9.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.9.0", + "jest-regex-util": "^24.9.0", + "jest-util": "^24.9.0", + "micromatch": "^3.1.10", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@lerna/add": { + "version": "3.16.2", + "resolved": "https://registry.npmjs.org/@lerna/add/-/add-3.16.2.tgz", + "integrity": "sha512-RAAaF8aODPogj2Ge9Wj3uxPFIBGpog9M+HwSuq03ZnkkO831AmasCTJDqV+GEpl1U2DvnhZQEwHpWmTT0uUeEw==", + "dev": true, + "requires": { + "@evocateur/pacote": "^9.6.3", + "@lerna/bootstrap": "3.16.2", + "@lerna/command": "3.16.0", + "@lerna/filter-options": "3.16.0", + "@lerna/npm-conf": "3.16.0", + "@lerna/validation-error": "3.13.0", + "dedent": "^0.7.0", + "npm-package-arg": "^6.1.0", + "p-map": "^2.1.0", + "semver": "^6.2.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 + } + } + }, + "@lerna/batch-packages": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/batch-packages/-/batch-packages-3.16.0.tgz", + "integrity": "sha512-7AdMkANpubY/FKFI01im01tlx6ygOBJ/0JcixMUWoWP/7Ds3SWQF22ID6fbBr38jUWptYLDs2fagtTDL7YUPuA==", + "dev": true, + "requires": { + "@lerna/package-graph": "3.16.0", + "npmlog": "^4.1.2" + } + }, + "@lerna/bootstrap": { + "version": "3.16.2", + "resolved": "https://registry.npmjs.org/@lerna/bootstrap/-/bootstrap-3.16.2.tgz", + "integrity": "sha512-I+gs7eh6rv9Vyd+CwqL7sftRfOOsSzCle8cv/CGlMN7/p7EAVhxEdAw8SYoHIKHzipXszuqqy1Y3opyleD0qdA==", + "dev": true, + "requires": { + "@lerna/batch-packages": "3.16.0", + "@lerna/command": "3.16.0", + "@lerna/filter-options": "3.16.0", + "@lerna/has-npm-version": "3.16.0", + "@lerna/npm-install": "3.16.0", + "@lerna/package-graph": "3.16.0", + "@lerna/pulse-till-done": "3.13.0", + "@lerna/rimraf-dir": "3.14.2", + "@lerna/run-lifecycle": "3.16.2", + "@lerna/run-parallel-batches": "3.16.0", + "@lerna/symlink-binary": "3.16.2", + "@lerna/symlink-dependencies": "3.16.2", + "@lerna/validation-error": "3.13.0", + "dedent": "^0.7.0", + "get-port": "^4.2.0", + "multimatch": "^3.0.0", + "npm-package-arg": "^6.1.0", + "npmlog": "^4.1.2", + "p-finally": "^1.0.0", + "p-map": "^2.1.0", + "p-map-series": "^1.0.0", + "p-waterfall": "^1.0.0", + "read-package-tree": "^5.1.6", + "semver": "^6.2.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 + } + } + }, + "@lerna/changed": { + "version": "3.16.4", + "resolved": "https://registry.npmjs.org/@lerna/changed/-/changed-3.16.4.tgz", + "integrity": "sha512-NCD7XkK744T23iW0wqKEgF4R9MYmReUbyHCZKopFnsNpQdqumc3SOIvQUAkKCP6hQJmYvxvOieoVgy/CVDpZ5g==", + "dev": true, + "requires": { + "@lerna/collect-updates": "3.16.0", + "@lerna/command": "3.16.0", + "@lerna/listable": "3.16.0", + "@lerna/output": "3.13.0", + "@lerna/version": "3.16.4" + } + }, + "@lerna/check-working-tree": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/@lerna/check-working-tree/-/check-working-tree-3.14.2.tgz", + "integrity": "sha512-7safqxM/MYoAoxZxulUDtIJIbnBIgo0PB/FHytueG+9VaX7GMnDte2Bt1EKa0dz2sAyQdmQ3Q8ZXpf/6JDjaeg==", + "dev": true, + "requires": { + "@lerna/collect-uncommitted": "3.14.2", + "@lerna/describe-ref": "3.14.2", + "@lerna/validation-error": "3.13.0" + } + }, + "@lerna/child-process": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/@lerna/child-process/-/child-process-3.14.2.tgz", + "integrity": "sha512-xnq+W5yQb6RkwI0p16ZQnrn6HkloH/MWTw4lGE1nKsBLAUbmSU5oTE93W1nrG0X3IMF/xWc9UYvNdUGMWvZZ4w==", + "dev": true, + "requires": { + "chalk": "^2.3.1", + "execa": "^1.0.0", + "strong-log-transformer": "^2.0.0" + } + }, + "@lerna/clean": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/clean/-/clean-3.16.0.tgz", + "integrity": "sha512-5P9U5Y19WmYZr7UAMGXBpY7xCRdlR7zhHy8MAPDKVx70rFIBS6nWXn5n7Kntv74g7Lm1gJ2rsiH5tj1OPcRJgg==", + "dev": true, + "requires": { + "@lerna/command": "3.16.0", + "@lerna/filter-options": "3.16.0", + "@lerna/prompt": "3.13.0", + "@lerna/pulse-till-done": "3.13.0", + "@lerna/rimraf-dir": "3.14.2", + "p-map": "^2.1.0", + "p-map-series": "^1.0.0", + "p-waterfall": "^1.0.0" + } + }, + "@lerna/cli": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@lerna/cli/-/cli-3.13.0.tgz", + "integrity": "sha512-HgFGlyCZbYaYrjOr3w/EsY18PdvtsTmDfpUQe8HwDjXlPeCCUgliZjXLOVBxSjiOvPeOSwvopwIHKWQmYbwywg==", + "dev": true, + "requires": { + "@lerna/global-options": "3.13.0", + "dedent": "^0.7.0", + "npmlog": "^4.1.2", + "yargs": "^12.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 + }, + "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" + } + }, + "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==", + "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=", + "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" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "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 + }, + "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": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "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" + } + } + } + }, + "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" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "@lerna/collect-uncommitted": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/@lerna/collect-uncommitted/-/collect-uncommitted-3.14.2.tgz", + "integrity": "sha512-4EkQu4jIOdNL2BMzy/N0ydHB8+Z6syu6xiiKXOoFl0WoWU9H1jEJCX4TH7CmVxXL1+jcs8FIS2pfQz4oew99Eg==", + "dev": true, + "requires": { + "@lerna/child-process": "3.14.2", + "chalk": "^2.3.1", + "figgy-pudding": "^3.5.1", + "npmlog": "^4.1.2" + } + }, + "@lerna/collect-updates": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/collect-updates/-/collect-updates-3.16.0.tgz", + "integrity": "sha512-HwAIl815X2TNlmcp28zCrSdXfoZWNP7GJPEqNWYk7xDJTYLqQ+SrmKUePjb3AMGBwYAraZSEJLbHdBpJ5+cHmQ==", + "dev": true, + "requires": { + "@lerna/child-process": "3.14.2", + "@lerna/describe-ref": "3.14.2", + "minimatch": "^3.0.4", + "npmlog": "^4.1.2", + "slash": "^2.0.0" + } + }, + "@lerna/command": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/command/-/command-3.16.0.tgz", + "integrity": "sha512-u7tE4GC4/gfbPA9eQg+0ulnoJ+PMoMqomx033r/IxqZrHtmJR9+pF/37S0fsxJ2hX/RMFPC7c9Q/i8NEufSpdQ==", + "dev": true, + "requires": { + "@lerna/child-process": "3.14.2", + "@lerna/package-graph": "3.16.0", + "@lerna/project": "3.16.0", + "@lerna/validation-error": "3.13.0", + "@lerna/write-log-file": "3.13.0", + "dedent": "^0.7.0", + "execa": "^1.0.0", + "is-ci": "^2.0.0", + "lodash": "^4.17.14", + "npmlog": "^4.1.2" + } + }, + "@lerna/conventional-commits": { + "version": "3.16.4", + "resolved": "https://registry.npmjs.org/@lerna/conventional-commits/-/conventional-commits-3.16.4.tgz", + "integrity": "sha512-QSZJ0bC9n6FVaf+7KDIq5zMv8WnHXnwhyL5jG1Nyh3SgOg9q2uflqh7YsYB+G6FwaRfnPaKosh6obijpYg0llA==", + "dev": true, + "requires": { + "@lerna/validation-error": "3.13.0", + "conventional-changelog-angular": "^5.0.3", + "conventional-changelog-core": "^3.1.6", + "conventional-recommended-bump": "^5.0.0", + "fs-extra": "^8.1.0", + "get-stream": "^4.0.0", + "lodash.template": "^4.5.0", + "npm-package-arg": "^6.1.0", + "npmlog": "^4.1.2", + "pify": "^4.0.1", + "semver": "^6.2.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 + } + } + }, + "@lerna/create": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/create/-/create-3.16.0.tgz", + "integrity": "sha512-OZApR1Iz7awutbmj4sAArwhqCyKgcrnw9rH0aWAUrkYWrD1w4TwkvAcYAsfx5GpQGbLQwoXhoyyPwPfZRRWz3Q==", + "dev": true, + "requires": { + "@evocateur/pacote": "^9.6.3", + "@lerna/child-process": "3.14.2", + "@lerna/command": "3.16.0", + "@lerna/npm-conf": "3.16.0", + "@lerna/validation-error": "3.13.0", + "camelcase": "^5.0.0", + "dedent": "^0.7.0", + "fs-extra": "^8.1.0", + "globby": "^9.2.0", + "init-package-json": "^1.10.3", + "npm-package-arg": "^6.1.0", + "p-reduce": "^1.0.0", + "pify": "^4.0.1", + "semver": "^6.2.0", + "slash": "^2.0.0", + "validate-npm-package-license": "^3.0.3", + "validate-npm-package-name": "^3.0.0", + "whatwg-url": "^7.0.0" + }, + "dependencies": { + "globby": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz", + "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^1.0.2", + "dir-glob": "^2.2.2", + "fast-glob": "^2.2.6", + "glob": "^7.1.3", + "ignore": "^4.0.3", + "pify": "^4.0.1", + "slash": "^2.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 + }, + "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" + } + } + } + }, + "@lerna/create-symlink": { + "version": "3.16.2", + "resolved": "https://registry.npmjs.org/@lerna/create-symlink/-/create-symlink-3.16.2.tgz", + "integrity": "sha512-pzXIJp6av15P325sgiIRpsPXLFmkisLhMBCy4764d+7yjf2bzrJ4gkWVMhsv4AdF0NN3OyZ5jjzzTtLNqfR+Jw==", + "dev": true, + "requires": { + "@zkochan/cmd-shim": "^3.1.0", + "fs-extra": "^8.1.0", + "npmlog": "^4.1.2" + } + }, + "@lerna/describe-ref": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/@lerna/describe-ref/-/describe-ref-3.14.2.tgz", + "integrity": "sha512-qa5pzDRK2oBQXNjyRmRnN7E8a78NMYfQjjlRFB0KNHMsT6mCiL9+8kIS39sSE2NqT8p7xVNo2r2KAS8R/m3CoQ==", + "dev": true, + "requires": { + "@lerna/child-process": "3.14.2", + "npmlog": "^4.1.2" + } + }, + "@lerna/diff": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/diff/-/diff-3.16.0.tgz", + "integrity": "sha512-QUpVs5TPl8vBIne10/vyjUxanQBQQp7Lk3iaB8MnCysKr0O+oy7trWeFVDPEkBTCD177By7yPGyW5Yey1nCBbA==", + "dev": true, + "requires": { + "@lerna/child-process": "3.14.2", + "@lerna/command": "3.16.0", + "@lerna/validation-error": "3.13.0", + "npmlog": "^4.1.2" + } + }, + "@lerna/exec": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/exec/-/exec-3.16.0.tgz", + "integrity": "sha512-mH3O5NXf/O88jBaBBTUf+d56CUkxpg782s3Jxy7HWbVuSUULt3iMRPTh+zEXO5/555etsIVVDDyUR76meklrJA==", + "dev": true, + "requires": { + "@lerna/child-process": "3.14.2", + "@lerna/command": "3.16.0", + "@lerna/filter-options": "3.16.0", + "@lerna/run-topologically": "3.16.0", + "@lerna/validation-error": "3.13.0", + "p-map": "^2.1.0" + } + }, + "@lerna/filter-options": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/filter-options/-/filter-options-3.16.0.tgz", + "integrity": "sha512-InIi1fF8+PxpCwir9bIy+pGxrdE6hvN0enIs1eNGCVS1TTE8osNgiZXa838bMQ1yaEccdcnVX6Z03BNKd56kNg==", + "dev": true, + "requires": { + "@lerna/collect-updates": "3.16.0", + "@lerna/filter-packages": "3.16.0", + "dedent": "^0.7.0" + } + }, + "@lerna/filter-packages": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/filter-packages/-/filter-packages-3.16.0.tgz", + "integrity": "sha512-eGFzQTx0ogkGDCnbTuXqssryR6ilp8+dcXt6B+aq1MaqL/vOJRZyqMm4TY3CUOUnzZCi9S2WWyMw3PnAJOF+kg==", + "dev": true, + "requires": { + "@lerna/validation-error": "3.13.0", + "multimatch": "^3.0.0", + "npmlog": "^4.1.2" + } + }, + "@lerna/get-npm-exec-opts": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-3.13.0.tgz", + "integrity": "sha512-Y0xWL0rg3boVyJk6An/vurKzubyJKtrxYv2sj4bB8Mc5zZ3tqtv0ccbOkmkXKqbzvNNF7VeUt1OJ3DRgtC/QZw==", + "dev": true, + "requires": { + "npmlog": "^4.1.2" + } + }, + "@lerna/get-packed": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/get-packed/-/get-packed-3.16.0.tgz", + "integrity": "sha512-AjsFiaJzo1GCPnJUJZiTW6J1EihrPkc2y3nMu6m3uWFxoleklsSCyImumzVZJssxMi3CPpztj8LmADLedl9kXw==", + "dev": true, + "requires": { + "fs-extra": "^8.1.0", + "ssri": "^6.0.1", + "tar": "^4.4.8" + } + }, + "@lerna/github-client": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/github-client/-/github-client-3.16.0.tgz", + "integrity": "sha512-IVJjcKjkYaUEPJsDyAblHGEFFNKCRyMagbIDm14L7Ab94ccN6i4TKOqAFEJn2SJHYvKKBdp3Zj2zNlASOMe3DA==", + "dev": true, + "requires": { + "@lerna/child-process": "3.14.2", + "@octokit/plugin-enterprise-rest": "^3.6.1", + "@octokit/rest": "^16.28.4", + "git-url-parse": "^11.1.2", + "npmlog": "^4.1.2" + } + }, + "@lerna/gitlab-client": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/@lerna/gitlab-client/-/gitlab-client-3.15.0.tgz", + "integrity": "sha512-OsBvRSejHXUBMgwWQqNoioB8sgzL/Pf1pOUhHKtkiMl6aAWjklaaq5HPMvTIsZPfS6DJ9L5OK2GGZuooP/5c8Q==", + "dev": true, + "requires": { + "node-fetch": "^2.5.0", + "npmlog": "^4.1.2", + "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" + } + } + } + }, + "@lerna/global-options": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@lerna/global-options/-/global-options-3.13.0.tgz", + "integrity": "sha512-SlZvh1gVRRzYLVluz9fryY1nJpZ0FHDGB66U9tFfvnnxmueckRQxLopn3tXj3NU1kc3QANT2I5BsQkOqZ4TEFQ==", + "dev": true + }, + "@lerna/has-npm-version": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/has-npm-version/-/has-npm-version-3.16.0.tgz", + "integrity": "sha512-TIY036dA9J8OyTrZq9J+it2DVKifL65k7hK8HhkUPpitJkw6jwbMObA/8D40LOGgWNPweJWqmlrTbRSwsR7DrQ==", + "dev": true, + "requires": { + "@lerna/child-process": "3.14.2", + "semver": "^6.2.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 + } + } + }, + "@lerna/import": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/import/-/import-3.16.0.tgz", + "integrity": "sha512-trsOmGHzw0rL/f8BLNvd+9PjoTkXq2Dt4/V2UCha254hMQaYutbxcYu8iKPxz9x86jSPlH7FpbTkkHXDsoY7Yg==", + "dev": true, + "requires": { + "@lerna/child-process": "3.14.2", + "@lerna/command": "3.16.0", + "@lerna/prompt": "3.13.0", + "@lerna/pulse-till-done": "3.13.0", + "@lerna/validation-error": "3.13.0", + "dedent": "^0.7.0", + "fs-extra": "^8.1.0", + "p-map-series": "^1.0.0" + } + }, + "@lerna/init": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/init/-/init-3.16.0.tgz", + "integrity": "sha512-Ybol/x5xMtBgokx4j7/Y3u0ZmNh0NiSWzBFVaOs2NOJKvuqrWimF67DKVz7yYtTYEjtaMdug64ohFF4jcT/iag==", + "dev": true, + "requires": { + "@lerna/child-process": "3.14.2", + "@lerna/command": "3.16.0", + "fs-extra": "^8.1.0", + "p-map": "^2.1.0", + "write-json-file": "^3.2.0" + } + }, + "@lerna/link": { + "version": "3.16.2", + "resolved": "https://registry.npmjs.org/@lerna/link/-/link-3.16.2.tgz", + "integrity": "sha512-eCPg5Lo8HT525fIivNoYF3vWghO3UgEVFdbsiPmhzwI7IQyZro5HWYzLtywSAdEog5XZpd2Bbn0CsoHWBB3gww==", + "dev": true, + "requires": { + "@lerna/command": "3.16.0", + "@lerna/package-graph": "3.16.0", + "@lerna/symlink-dependencies": "3.16.2", + "p-map": "^2.1.0", + "slash": "^2.0.0" + } + }, + "@lerna/list": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/list/-/list-3.16.0.tgz", + "integrity": "sha512-TkvstoPsgKqqQ0KfRumpsdMXfRSEhdXqOLq519XyI5IRWYxhoqXqfi8gG37UoBPhBNoe64japn5OjphF3rOmQA==", + "dev": true, + "requires": { + "@lerna/command": "3.16.0", + "@lerna/filter-options": "3.16.0", + "@lerna/listable": "3.16.0", + "@lerna/output": "3.13.0" + } + }, + "@lerna/listable": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/listable/-/listable-3.16.0.tgz", + "integrity": "sha512-mtdAT2EEECqrJSDm/aXlOUFr1MRE4p6hppzY//Klp05CogQy6uGaKk+iKG5yyCLaOXFFZvG4HfO11CmoGSDWzw==", + "dev": true, + "requires": { + "@lerna/query-graph": "3.16.0", + "chalk": "^2.3.1", + "columnify": "^1.5.4" + } + }, + "@lerna/log-packed": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/log-packed/-/log-packed-3.16.0.tgz", + "integrity": "sha512-Fp+McSNBV/P2mnLUYTaSlG8GSmpXM7krKWcllqElGxvAqv6chk2K3c2k80MeVB4WvJ9tRjUUf+i7HUTiQ9/ckQ==", + "dev": true, + "requires": { + "byte-size": "^5.0.1", + "columnify": "^1.5.4", + "has-unicode": "^2.0.1", + "npmlog": "^4.1.2" + } + }, + "@lerna/npm-conf": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/npm-conf/-/npm-conf-3.16.0.tgz", + "integrity": "sha512-HbO3DUrTkCAn2iQ9+FF/eisDpWY5POQAOF1m7q//CZjdC2HSW3UYbKEGsSisFxSfaF9Z4jtrV+F/wX6qWs3CuA==", + "dev": true, + "requires": { + "config-chain": "^1.1.11", + "pify": "^4.0.1" + } + }, + "@lerna/npm-dist-tag": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/npm-dist-tag/-/npm-dist-tag-3.16.0.tgz", + "integrity": "sha512-MQrBkqJJB9+eNphuj9w90QPMOs4NQXMuSRk9NqzeFunOmdDopPCV0Q7IThSxEuWnhJ2n3B7G0vWUP7tNMPdqIQ==", + "dev": true, + "requires": { + "@evocateur/npm-registry-fetch": "^4.0.0", + "@lerna/otplease": "3.16.0", + "figgy-pudding": "^3.5.1", + "npm-package-arg": "^6.1.0", + "npmlog": "^4.1.2" + } + }, + "@lerna/npm-install": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/npm-install/-/npm-install-3.16.0.tgz", + "integrity": "sha512-APUOIilZCzDzce92uLEwzt1r7AEMKT/hWA1ThGJL+PO9Rn8A95Km3o2XZAYG4W0hR+P4O2nSVuKbsjQtz8CjFQ==", + "dev": true, + "requires": { + "@lerna/child-process": "3.14.2", + "@lerna/get-npm-exec-opts": "3.13.0", + "fs-extra": "^8.1.0", + "npm-package-arg": "^6.1.0", + "npmlog": "^4.1.2", + "signal-exit": "^3.0.2", + "write-pkg": "^3.1.0" + } + }, + "@lerna/npm-publish": { + "version": "3.16.2", + "resolved": "https://registry.npmjs.org/@lerna/npm-publish/-/npm-publish-3.16.2.tgz", + "integrity": "sha512-tGMb9vfTxP57vUV5svkBQxd5Tzc+imZbu9ZYf8Mtwe0+HYfDjNiiHLIQw7G95w4YRdc5KsCE8sQ0uSj+f2soIg==", + "dev": true, + "requires": { + "@evocateur/libnpmpublish": "^1.2.2", + "@lerna/otplease": "3.16.0", + "@lerna/run-lifecycle": "3.16.2", + "figgy-pudding": "^3.5.1", + "fs-extra": "^8.1.0", + "npm-package-arg": "^6.1.0", + "npmlog": "^4.1.2", + "pify": "^4.0.1", + "read-package-json": "^2.0.13" + } + }, + "@lerna/npm-run-script": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/@lerna/npm-run-script/-/npm-run-script-3.14.2.tgz", + "integrity": "sha512-LbVFv+nvAoRTYLMrJlJ8RiakHXrLslL7Jp/m1R18vYrB8LYWA3ey+nz5Tel2OELzmjUiemAKZsD9h6i+Re5egg==", + "dev": true, + "requires": { + "@lerna/child-process": "3.14.2", + "@lerna/get-npm-exec-opts": "3.13.0", + "npmlog": "^4.1.2" + } + }, + "@lerna/otplease": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/otplease/-/otplease-3.16.0.tgz", + "integrity": "sha512-uqZ15wYOHC+/V0WnD2iTLXARjvx3vNrpiIeyIvVlDB7rWse9mL4egex/QSgZ+lDx1OID7l2kgvcUD9cFpbqB7Q==", + "dev": true, + "requires": { + "@lerna/prompt": "3.13.0", + "figgy-pudding": "^3.5.1" + } + }, + "@lerna/output": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@lerna/output/-/output-3.13.0.tgz", + "integrity": "sha512-7ZnQ9nvUDu/WD+bNsypmPG5MwZBwu86iRoiW6C1WBuXXDxM5cnIAC1m2WxHeFnjyMrYlRXM9PzOQ9VDD+C15Rg==", + "dev": true, + "requires": { + "npmlog": "^4.1.2" + } + }, + "@lerna/pack-directory": { + "version": "3.16.4", + "resolved": "https://registry.npmjs.org/@lerna/pack-directory/-/pack-directory-3.16.4.tgz", + "integrity": "sha512-uxSF0HZeGyKaaVHz5FroDY9A5NDDiCibrbYR6+khmrhZtY0Bgn6hWq8Gswl9iIlymA+VzCbshWIMX4o2O8C8ng==", + "dev": true, + "requires": { + "@lerna/get-packed": "3.16.0", + "@lerna/package": "3.16.0", + "@lerna/run-lifecycle": "3.16.2", + "figgy-pudding": "^3.5.1", + "npm-packlist": "^1.4.4", + "npmlog": "^4.1.2", + "tar": "^4.4.10", + "temp-write": "^3.4.0" + } + }, + "@lerna/package": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/package/-/package-3.16.0.tgz", + "integrity": "sha512-2lHBWpaxcBoiNVbtyLtPUuTYEaB/Z+eEqRS9duxpZs6D+mTTZMNy6/5vpEVSCBmzvdYpyqhqaYjjSLvjjr5Riw==", + "dev": true, + "requires": { + "load-json-file": "^5.3.0", + "npm-package-arg": "^6.1.0", + "write-pkg": "^3.1.0" + }, + "dependencies": { + "load-json-file": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", + "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "parse-json": "^4.0.0", + "pify": "^4.0.1", + "strip-bom": "^3.0.0", + "type-fest": "^0.3.0" + } + } + } + }, + "@lerna/package-graph": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/package-graph/-/package-graph-3.16.0.tgz", + "integrity": "sha512-A2mum/gNbv7zCtAwJqoxzqv89As73OQNK2MgSX1SHWya46qoxO9a9Z2c5lOFQ8UFN5ZxqWMfFYXRCz7qzwmFXw==", + "dev": true, + "requires": { + "@lerna/prerelease-id-from-version": "3.16.0", + "@lerna/validation-error": "3.13.0", + "npm-package-arg": "^6.1.0", + "npmlog": "^4.1.2", + "semver": "^6.2.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 + } + } + }, + "@lerna/prerelease-id-from-version": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/prerelease-id-from-version/-/prerelease-id-from-version-3.16.0.tgz", + "integrity": "sha512-qZyeUyrE59uOK8rKdGn7jQz+9uOpAaF/3hbslJVFL1NqF9ELDTqjCPXivuejMX/lN4OgD6BugTO4cR7UTq/sZA==", + "dev": true, + "requires": { + "semver": "^6.2.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 + } + } + }, + "@lerna/project": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/project/-/project-3.16.0.tgz", + "integrity": "sha512-NrKcKK1EqXqhrGvslz6Q36+ZHuK3zlDhGdghRqnxDcHxMPT01NgLcmsnymmQ+gjMljuLRmvKYYCuHrknzX8VrA==", + "dev": true, + "requires": { + "@lerna/package": "3.16.0", + "@lerna/validation-error": "3.13.0", + "cosmiconfig": "^5.1.0", + "dedent": "^0.7.0", + "dot-prop": "^4.2.0", + "glob-parent": "^5.0.0", + "globby": "^9.2.0", + "load-json-file": "^5.3.0", + "npmlog": "^4.1.2", + "p-map": "^2.1.0", + "resolve-from": "^4.0.0", + "write-json-file": "^3.2.0" + }, + "dependencies": { + "globby": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz", + "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^1.0.2", + "dir-glob": "^2.2.2", + "fast-glob": "^2.2.6", + "glob": "^7.1.3", + "ignore": "^4.0.3", + "pify": "^4.0.1", + "slash": "^2.0.0" + } + }, + "load-json-file": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", + "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "parse-json": "^4.0.0", + "pify": "^4.0.1", + "strip-bom": "^3.0.0", + "type-fest": "^0.3.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 + } + } + }, + "@lerna/prompt": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@lerna/prompt/-/prompt-3.13.0.tgz", + "integrity": "sha512-P+lWSFokdyvYpkwC3it9cE0IF2U5yy2mOUbGvvE4iDb9K7TyXGE+7lwtx2thtPvBAfIb7O13POMkv7df03HJeA==", + "dev": true, + "requires": { + "inquirer": "^6.2.0", + "npmlog": "^4.1.2" + } + }, + "@lerna/publish": { + "version": "3.16.4", + "resolved": "https://registry.npmjs.org/@lerna/publish/-/publish-3.16.4.tgz", + "integrity": "sha512-XZY+gRuF7/v6PDQwl7lvZaGWs8CnX6WIPIu+OCcyFPSL/rdWegdN7HieKBHskgX798qRQc2GrveaY7bNoTKXAw==", + "dev": true, + "requires": { + "@evocateur/libnpmaccess": "^3.1.2", + "@evocateur/npm-registry-fetch": "^4.0.0", + "@evocateur/pacote": "^9.6.3", + "@lerna/check-working-tree": "3.14.2", + "@lerna/child-process": "3.14.2", + "@lerna/collect-updates": "3.16.0", + "@lerna/command": "3.16.0", + "@lerna/describe-ref": "3.14.2", + "@lerna/log-packed": "3.16.0", + "@lerna/npm-conf": "3.16.0", + "@lerna/npm-dist-tag": "3.16.0", + "@lerna/npm-publish": "3.16.2", + "@lerna/otplease": "3.16.0", + "@lerna/output": "3.13.0", + "@lerna/pack-directory": "3.16.4", + "@lerna/prerelease-id-from-version": "3.16.0", + "@lerna/prompt": "3.13.0", + "@lerna/pulse-till-done": "3.13.0", + "@lerna/run-lifecycle": "3.16.2", + "@lerna/run-topologically": "3.16.0", + "@lerna/validation-error": "3.13.0", + "@lerna/version": "3.16.4", + "figgy-pudding": "^3.5.1", + "fs-extra": "^8.1.0", + "npm-package-arg": "^6.1.0", + "npmlog": "^4.1.2", + "p-finally": "^1.0.0", + "p-map": "^2.1.0", + "p-pipe": "^1.2.0", + "semver": "^6.2.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 + } + } + }, + "@lerna/pulse-till-done": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@lerna/pulse-till-done/-/pulse-till-done-3.13.0.tgz", + "integrity": "sha512-1SOHpy7ZNTPulzIbargrgaJX387csN7cF1cLOGZiJQA6VqnS5eWs2CIrG8i8wmaUavj2QlQ5oEbRMVVXSsGrzA==", + "dev": true, + "requires": { + "npmlog": "^4.1.2" + } + }, + "@lerna/query-graph": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/query-graph/-/query-graph-3.16.0.tgz", + "integrity": "sha512-p0RO+xmHDO95ChJdWkcy9TNLysLkoDARXeRHzY5U54VCwl3Ot/2q8fMCVlA5UeGXDutEyyByl3URqEpcQCWI7Q==", + "dev": true, + "requires": { + "@lerna/package-graph": "3.16.0", + "figgy-pudding": "^3.5.1" + } + }, + "@lerna/resolve-symlink": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/resolve-symlink/-/resolve-symlink-3.16.0.tgz", + "integrity": "sha512-Ibj5e7njVHNJ/NOqT4HlEgPFPtPLWsO7iu59AM5bJDcAJcR96mLZ7KGVIsS2tvaO7akMEJvt2P+ErwCdloG3jQ==", + "dev": true, + "requires": { + "fs-extra": "^8.1.0", + "npmlog": "^4.1.2", + "read-cmd-shim": "^1.0.1" + } + }, + "@lerna/rimraf-dir": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/@lerna/rimraf-dir/-/rimraf-dir-3.14.2.tgz", + "integrity": "sha512-eFNkZsy44Bu9v1Hrj5Zk6omzg8O9h/7W6QYK1TTUHeyrjTEwytaNQlqF0lrTLmEvq55sviV42NC/8P3M2cvq8Q==", + "dev": true, + "requires": { + "@lerna/child-process": "3.14.2", + "npmlog": "^4.1.2", + "path-exists": "^3.0.0", + "rimraf": "^2.6.2" + }, + "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" + } + } + } + }, + "@lerna/run": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/run/-/run-3.16.0.tgz", + "integrity": "sha512-woTeLlB1OAAz4zzjdI6RyIxSGuxiUPHJZm89E1pDEPoWwtQV6HMdMgrsQd9ATsJ5Ez280HH4bF/LStAlqW8Ufg==", + "dev": true, + "requires": { + "@lerna/command": "3.16.0", + "@lerna/filter-options": "3.16.0", + "@lerna/npm-run-script": "3.14.2", + "@lerna/output": "3.13.0", + "@lerna/run-topologically": "3.16.0", + "@lerna/timer": "3.13.0", + "@lerna/validation-error": "3.13.0", + "p-map": "^2.1.0" + } + }, + "@lerna/run-lifecycle": { + "version": "3.16.2", + "resolved": "https://registry.npmjs.org/@lerna/run-lifecycle/-/run-lifecycle-3.16.2.tgz", + "integrity": "sha512-RqFoznE8rDpyyF0rOJy3+KjZCeTkO8y/OB9orPauR7G2xQ7PTdCpgo7EO6ZNdz3Al+k1BydClZz/j78gNCmL2A==", + "dev": true, + "requires": { + "@lerna/npm-conf": "3.16.0", + "figgy-pudding": "^3.5.1", + "npm-lifecycle": "^3.1.2", + "npmlog": "^4.1.2" + } + }, + "@lerna/run-parallel-batches": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/run-parallel-batches/-/run-parallel-batches-3.16.0.tgz", + "integrity": "sha512-2J/Nyv+MvogmQEfC7VcS21ifk7w0HVvzo2yOZRPvkCzGRu/rducxtB4RTcr58XCZ8h/Bt1aqQYKExu3c/3GXwg==", + "dev": true, + "requires": { + "p-map": "^2.1.0", + "p-map-series": "^1.0.0" + } + }, + "@lerna/run-topologically": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/run-topologically/-/run-topologically-3.16.0.tgz", + "integrity": "sha512-4Hlpv4zDtKWa5Z0tPkeu0sK+bxZEKgkNESMGmWrUCNfj7xwvAJurcraK8+a2Y0TFYwf0qjSLY/MzX+ZbJA3Cgw==", + "dev": true, + "requires": { + "@lerna/query-graph": "3.16.0", + "figgy-pudding": "^3.5.1", + "p-queue": "^4.0.0" + } + }, + "@lerna/symlink-binary": { + "version": "3.16.2", + "resolved": "https://registry.npmjs.org/@lerna/symlink-binary/-/symlink-binary-3.16.2.tgz", + "integrity": "sha512-kz9XVoFOGSF83gg4gBqH+mG6uxfJfTp8Uy+Cam40CvMiuzfODrGkjuBEFoM/uO2QOAwZvbQDYOBpKUa9ZxHS1Q==", + "dev": true, + "requires": { + "@lerna/create-symlink": "3.16.2", + "@lerna/package": "3.16.0", + "fs-extra": "^8.1.0", + "p-map": "^2.1.0" + } + }, + "@lerna/symlink-dependencies": { + "version": "3.16.2", + "resolved": "https://registry.npmjs.org/@lerna/symlink-dependencies/-/symlink-dependencies-3.16.2.tgz", + "integrity": "sha512-wnZqGJQ+Jvr1I3inxrkffrFZfmQI7Ta8gySw/UWCy95QtZWF/f5yk8zVIocCAsjzD0wgb3jJE3CFJ9W5iwWk1A==", + "dev": true, + "requires": { + "@lerna/create-symlink": "3.16.2", + "@lerna/resolve-symlink": "3.16.0", + "@lerna/symlink-binary": "3.16.2", + "fs-extra": "^8.1.0", + "p-finally": "^1.0.0", + "p-map": "^2.1.0", + "p-map-series": "^1.0.0" + } + }, + "@lerna/timer": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@lerna/timer/-/timer-3.13.0.tgz", + "integrity": "sha512-RHWrDl8U4XNPqY5MQHkToWS9jHPnkLZEt5VD+uunCKTfzlxGnRCr3/zVr8VGy/uENMYpVP3wJa4RKGY6M0vkRw==", + "dev": true + }, + "@lerna/validation-error": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@lerna/validation-error/-/validation-error-3.13.0.tgz", + "integrity": "sha512-SiJP75nwB8GhgwLKQfdkSnDufAaCbkZWJqEDlKOUPUvVOplRGnfL+BPQZH5nvq2BYSRXsksXWZ4UHVnQZI/HYA==", + "dev": true, + "requires": { + "npmlog": "^4.1.2" + } + }, + "@lerna/version": { + "version": "3.16.4", + "resolved": "https://registry.npmjs.org/@lerna/version/-/version-3.16.4.tgz", + "integrity": "sha512-ikhbMeIn5ljCtWTlHDzO4YvTmpGTX1lWFFIZ79Vd1TNyOr+OUuKLo/+p06mCl2WEdZu0W2s5E9oxfAAQbyDxEg==", + "dev": true, + "requires": { + "@lerna/check-working-tree": "3.14.2", + "@lerna/child-process": "3.14.2", + "@lerna/collect-updates": "3.16.0", + "@lerna/command": "3.16.0", + "@lerna/conventional-commits": "3.16.4", + "@lerna/github-client": "3.16.0", + "@lerna/gitlab-client": "3.15.0", + "@lerna/output": "3.13.0", + "@lerna/prerelease-id-from-version": "3.16.0", + "@lerna/prompt": "3.13.0", + "@lerna/run-lifecycle": "3.16.2", + "@lerna/run-topologically": "3.16.0", + "@lerna/validation-error": "3.13.0", + "chalk": "^2.3.1", + "dedent": "^0.7.0", + "minimatch": "^3.0.4", + "npmlog": "^4.1.2", + "p-map": "^2.1.0", + "p-pipe": "^1.2.0", + "p-reduce": "^1.0.0", + "p-waterfall": "^1.0.0", + "semver": "^6.2.0", + "slash": "^2.0.0", + "temp-write": "^3.4.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 + } + } + }, + "@lerna/write-log-file": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@lerna/write-log-file/-/write-log-file-3.13.0.tgz", + "integrity": "sha512-RibeMnDPvlL8bFYW5C8cs4mbI3AHfQef73tnJCQ/SgrXZHehmHnsyWUiE7qDQCAo+B1RfTapvSyFF69iPj326A==", + "dev": true, + "requires": { + "npmlog": "^4.1.2", + "write-file-atomic": "^2.3.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==", + "dev": true, + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.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 + }, + "@octokit/endpoint": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.3.5.tgz", + "integrity": "sha512-f8KqzIrnzPLiezDsZZPB+K8v8YSv6aKFl7eOu59O46lmlW4HagWl1U6NWl6LmT8d1w7NsKBI3paVtzcnRGO1gw==", + "dev": true, + "requires": { + "is-plain-object": "^3.0.0", + "universal-user-agent": "^4.0.0" + }, + "dependencies": { + "is-plain-object": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", + "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", + "dev": true, + "requires": { + "isobject": "^4.0.0" + } + }, + "isobject": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", + "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", + "dev": true + } + } + }, + "@octokit/plugin-enterprise-rest": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-3.6.2.tgz", + "integrity": "sha512-3wF5eueS5OHQYuAEudkpN+xVeUsg8vYEMMenEzLphUZ7PRZ8OJtDcsreL3ad9zxXmBbaFWzLmFcdob5CLyZftA==", + "dev": true + }, + "@octokit/request": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.1.0.tgz", + "integrity": "sha512-I15T9PwjFs4tbWyhtFU2Kq7WDPidYMvRB7spmxoQRZfxSmiqullG+Nz+KbSmpkfnlvHwTr1e31R5WReFRKMXjg==", + "dev": true, + "requires": { + "@octokit/endpoint": "^5.1.0", + "@octokit/request-error": "^1.0.1", + "deprecation": "^2.0.0", + "is-plain-object": "^3.0.0", + "node-fetch": "^2.3.0", + "once": "^1.4.0", + "universal-user-agent": "^4.0.0" + }, + "dependencies": { + "is-plain-object": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", + "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", + "dev": true, + "requires": { + "isobject": "^4.0.0" + } + }, + "isobject": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", + "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", + "dev": true + } + } + }, + "@octokit/request-error": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.0.4.tgz", + "integrity": "sha512-L4JaJDXn8SGT+5G0uX79rZLv0MNJmfGa4vb4vy1NnpjSnWDLJRy6m90udGwvMmavwsStgbv2QNkPzzTCMmL+ig==", + "dev": true, + "requires": { + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/rest": { + "version": "16.28.9", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.28.9.tgz", + "integrity": "sha512-IKGnX+Tvzt7XHhs8f4ajqxyJvYAMNX5nWfoJm4CQj8LZToMiaJgutf5KxxpxoC3y5w7JTJpW5rnWnF4TsIvCLA==", + "dev": true, + "requires": { + "@octokit/request": "^5.0.0", + "@octokit/request-error": "^1.0.2", + "atob-lite": "^2.0.0", + "before-after-hook": "^2.0.0", + "btoa-lite": "^1.0.0", + "deprecation": "^2.0.0", + "lodash.get": "^4.4.2", + "lodash.set": "^4.3.2", + "lodash.uniq": "^4.5.0", + "octokit-pagination-methods": "^1.1.0", + "once": "^1.4.0", + "universal-user-agent": "^4.0.0" + } + }, + "@types/anymatch": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", + "integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==", + "dev": true + }, + "@types/babel__core": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.3.tgz", + "integrity": "sha512-8fBo0UR2CcwWxeX7WIIgJ7lXjasFxoYgRnFHUj+hRvKkpiBJbxhdAPTCY6/ZKM0uxANFVzt4yObSLuTiTnazDA==", + "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.0.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.0.2.tgz", + "integrity": "sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", + "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.7.tgz", + "integrity": "sha512-CeBpmX1J8kWLcDEnI3Cl2Eo6RfbGvzUctA+CjZUhOKDFbLfcr7fc4usEqLNWetrlJd7RhAkyYe2czXop4fICpw==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/d3-format": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.3.1.tgz", + "integrity": "sha512-KAWvReOKMDreaAwOjdfQMm0HjcUMlQG47GwqdVKgmm20vTd2pucj0a70c3gUSHrnsmo6H2AMrkBsZU2UhJLq8A==", + "dev": true + }, + "@types/d3-time-format": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.1.1.tgz", + "integrity": "sha512-tJSyXta8ZyJ52wDDHA96JEsvkbL6jl7wowGmuf45+fAkj5Y+SQOnz0N7/H68OWmPshPsAaWMQh+GAws44IzH3g==", + "dev": true + }, + "@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==", + "dev": true, + "requires": { + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", + "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", + "integrity": "sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz", + "integrity": "sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "24.0.18", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.18.tgz", + "integrity": "sha512-jcDDXdjTcrQzdN06+TSVsPPqxvsZA/5QkYfIZlq1JMw7FdP5AZylbOc+6B/cuDurctRe+MziUMtQ3xQdrbjqyQ==", + "dev": true, + "requires": { + "@types/jest-diff": "*" + } + }, + "@types/jest-diff": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jest-diff/-/jest-diff-20.0.1.tgz", + "integrity": "sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==", + "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": "12.7.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.4.tgz", + "integrity": "sha512-W0+n1Y+gK/8G2P/piTkBBN38Qc5Q1ZSO6B5H3QmPCUewaiXOo2GCAWZ4ElZCcNhjJuBSUSLGFUJnmlCn5+nxOQ==", + "dev": true + }, + "@types/prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-f8JzJNWVhKtc9dg/dyDNfliTKNOJSLa7Oht/ElZdF/UbMUmAH3rLmAk3ODNjw0mZajDEgatA03tRjB4+Dp/tzA==", + "dev": true + }, + "@types/react": { + "version": "16.9.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.2.tgz", + "integrity": "sha512-jYP2LWwlh+FTqGd9v7ynUKZzjj98T8x7Yclz479QdRhHfuW9yQ+0jjnD31eXSXutmBpppj5PYNLYLRfnZJvcfg==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "csstype": "^2.2.0" + } + }, + "@types/react-dom": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.0.tgz", + "integrity": "sha512-OL2lk7LYGjxn4b0efW3Pvf2KBVP0y1v3wip1Bp7nA79NkOpElH98q3WdCEdDj93b2b0zaeBG9DvriuKjIK5xDA==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/react-virtualized-auto-sizer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.0.tgz", + "integrity": "sha512-NMErdIdSnm2j/7IqMteRiRvRulpjoELnXWUwdbucYCz84xG9PHcoOrr7QfXwB/ku7wd6egiKFrzt/+QK4Imeeg==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", + "dev": true + }, + "@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", + "dev": true + }, + "@types/tapable": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.4.tgz", + "integrity": "sha512-78AdXtlhpCHT0K3EytMpn4JNxaf5tbqbLcbIRoQIHzpTIyjpxLQKRoxU55ujBXAtg3Nl2h/XWvfDa9dsMOd0pQ==", + "dev": true + }, + "@types/uglify-js": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.0.4.tgz", + "integrity": "sha512-SudIN9TRJ+v8g5pTG8RRCqfqTMNqgWCKKd3vtynhGzkIIjxaicNAMuY5TRadJ6tzDu3Dotf3ngaMILtmOdmWEQ==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + } + }, + "@types/webpack": { + "version": "4.39.1", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.39.1.tgz", + "integrity": "sha512-rgO9ihNu/l72Sjx3shqwc9r6gi+tOMsqxhMEZhOEVIZt82GFOeUyEdpTk1BO2HqEHLS/XJW8ldUTIIfIMMyYFQ==", + "dev": true, + "requires": { + "@types/anymatch": "*", + "@types/node": "*", + "@types/tapable": "*", + "@types/uglify-js": "*", + "@types/webpack-sources": "*", + "source-map": "^0.6.0" + } + }, + "@types/webpack-sources": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.5.tgz", + "integrity": "sha512-zfvjpp7jiafSmrzJ2/i3LqOyTYTuJ7u1KOXlKgDlvsj9Rr0x7ZiYu5lZbXwobL7lmsRNtPXlBfmaUD8eU2Hu8w==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/source-list-map": "*", + "source-map": "^0.6.1" + } + }, + "@types/yargs": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.2.tgz", + "integrity": "sha512-lwwgizwk/bIIU+3ELORkyuOgDjCh7zuWDFqRtPPhhVgq9N1F7CvLNKg1TX4f2duwtKQ0p044Au9r1PLIXHrIzQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-13.0.0.tgz", + "integrity": "sha512-wBlsw+8n21e6eTd4yVv8YD/E3xq0O6nNnJIquutAsFGE7EyMKz7W6RNT6BRu1SmdgmlCZ9tb0X+j+D6HGr8pZw==", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", + "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5" + } + }, + "@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==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "@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==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "mamacro": "^0.0.3" + } + }, + "@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==", + "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" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", + "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", + "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@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==", + "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" + } + }, + "@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==", + "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" + } + }, + "@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==", + "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" + } + }, + "@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==", + "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" + } + }, + "@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==", + "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" + } + }, + "@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==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "@zkochan/cmd-shim": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@zkochan/cmd-shim/-/cmd-shim-3.1.0.tgz", + "integrity": "sha512-o8l0+x7C7sMZU3v9GuJIAU10qQLtwR1dtRQIOmlNMtyaqhmpXOzx1HWiYoWfmmf9HHZoAkXpc9TM9PQYF9d4Jg==", + "dev": true, + "requires": { + "is-windows": "^1.0.0", + "mkdirp-promise": "^5.0.1", + "mz": "^2.5.0" + } + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abab": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.1.tgz", + "integrity": "sha512-1zSbbCuoIjafKZ3mblY5ikvAb0ODUbqBnFuUb7f6uLeQhhGJ0vEV4ntmtxKLT2WgXCO94E07BjunsIw1jOMPZw==", + "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": "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.3", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.3.tgz", + "integrity": "sha512-vkR40VwS2SYO98AIeFvzWWh+xyc2qi9s7OoXSFEGIP/rOJKzjnhykaZJNnHdoq4BL2gGxI5EZOU16z896EYnOQ==", + "dev": true, + "requires": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" + }, + "dependencies": { + "acorn": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", + "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==", + "dev": true + } + } + }, + "acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", + "dev": true + }, + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "agentkeepalive": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", + "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", + "dev": true, + "requires": { + "humanize-ms": "^1.2.1" + } + }, + "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-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==", + "dev": true + }, + "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 + }, + "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 + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-html": { + "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": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "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=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "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.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "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" + } + } + } + }, + "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" + } + }, + "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-differ": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-2.1.0.tgz", + "integrity": "sha512-KbUpJgx909ZscOc/7CLATBFam7P1Z1QRQInvgT0UztM9Q72aGKCunKASAl7WNW0tnPmPyEMeMhdsfWhfmW037w==", + "dev": true + }, + "array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", + "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": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "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 + }, + "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 + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "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": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "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 + }, + "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": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "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==", + "dev": true + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "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 + }, + "atob-lite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", + "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=", + "dev": true + }, + "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.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true + }, + "babel-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", + "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", + "dev": true, + "requires": { + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "babel-preset-jest": "^24.9.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", + "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" + } + }, + "babel-plugin-jest-hoist": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", + "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", + "dev": true, + "requires": { + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", + "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", + "dev": true, + "requires": { + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^24.9.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "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" + } + }, + "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" + } + } + } + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "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" + } + }, + "before-after-hook": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz", + "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==", + "dev": true + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "bluebird": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", + "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", + "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==", + "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": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "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 + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "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" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "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" + } + } + } + }, + "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": "0.1.3", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", + "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==", + "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==", + "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 + } + } + }, + "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.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "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" + } + }, + "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" + } + }, + "bser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.0.tgz", + "integrity": "sha512-8zsjWrQkkBoLK6uxASk1nJ2SKv97ltiGDo6A3wA0/yRPz+CwmEyDo0hUrhIuukG2JHpAl3bvFIixw2/3Hi0DOg==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "btoa-lite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", + "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=", + "dev": true + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "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-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "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 + }, + "builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", + "dev": true + }, + "byline": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=", + "dev": true + }, + "byte-size": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-5.0.1.tgz", + "integrity": "sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw==", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "cacache": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", + "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", + "dev": true, + "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" + }, + "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" + } + } + } + }, + "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-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 + }, + "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" + }, + "dependencies": { + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + } + } + }, + "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": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.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=", + "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" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "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" + } + } + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "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": { + "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" + } + } + } + } + } + }, + "chownr": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz", + "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==", + "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" + } + }, + "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" + } + }, + "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" + } + } + } + }, + "clean-css": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", + "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + } + }, + "clean-webpack-plugin": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-3.0.0.tgz", + "integrity": "sha512-MciirUH5r+cYLGCOL5JX/ZLzOZbVr1ot3Fw+KcvbhUb6PM+yycqd9ZhIlcigQ5gl+XhppNmw3bEFuaaMNyLj3A==", + "dev": true, + "requires": { + "@types/webpack": "^4.4.31", + "del": "^4.1.1" + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "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 + }, + "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" + }, + "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-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" + } + } + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "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=", + "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-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 + }, + "columnify": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz", + "integrity": "sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs=", + "dev": true, + "requires": { + "strip-ansi": "^3.0.0", + "wcwidth": "^1.0.0" + } + }, + "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": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "compare-func": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-1.3.2.tgz", + "integrity": "sha1-md0LpFfh+bxyKxLAjsM+6rMfpkg=", + "dev": true, + "requires": { + "array-ify": "^1.0.0", + "dot-prop": "^3.0.0" + }, + "dependencies": { + "dot-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", + "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=", + "dev": true, + "requires": { + "is-obj": "^1.0.0" + } + } + } + }, + "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.17", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz", + "integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==", + "dev": true, + "requires": { + "mime-db": ">= 1.40.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "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=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "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==", + "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" + } + } + } + }, + "config-chain": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", + "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", + "dev": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "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 + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "^0.1.4" + } + }, + "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 + }, + "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 + }, + "conventional-changelog-angular": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.3.tgz", + "integrity": "sha512-YD1xzH7r9yXQte/HF9JBuEDfvjxxwDGGwZU1+ndanbY0oFgA+Po1T9JDSpPLdP0pZT6MhCAsdvFKC4TJ4MTJTA==", + "dev": true, + "requires": { + "compare-func": "^1.3.1", + "q": "^1.5.1" + } + }, + "conventional-changelog-core": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-3.2.3.tgz", + "integrity": "sha512-LMMX1JlxPIq/Ez5aYAYS5CpuwbOk6QFp8O4HLAcZxe3vxoCtABkhfjetk8IYdRB9CDQGwJFLR3Dr55Za6XKgUQ==", + "dev": true, + "requires": { + "conventional-changelog-writer": "^4.0.6", + "conventional-commits-parser": "^3.0.3", + "dateformat": "^3.0.0", + "get-pkg-repo": "^1.0.0", + "git-raw-commits": "2.0.0", + "git-remote-origin-url": "^2.0.0", + "git-semver-tags": "^2.0.3", + "lodash": "^4.2.1", + "normalize-package-data": "^2.3.5", + "q": "^1.5.1", + "read-pkg": "^3.0.0", + "read-pkg-up": "^3.0.0", + "through2": "^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=", + "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 + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + } + } + }, + "conventional-changelog-preset-loader": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.2.0.tgz", + "integrity": "sha512-zXB+5vF7D5Y3Cb/rJfSyCCvFphCVmF8mFqOdncX3BmjZwAtGAPfYrBcT225udilCKvBbHgyzgxqz2GWDB5xShQ==", + "dev": true + }, + "conventional-changelog-writer": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.7.tgz", + "integrity": "sha512-p/wzs9eYaxhFbrmX/mCJNwJuvvHR+j4Fd0SQa2xyAhYed6KBiZ780LvoqUUvsayP4R1DtC27czalGUhKV2oabw==", + "dev": true, + "requires": { + "compare-func": "^1.3.1", + "conventional-commits-filter": "^2.0.2", + "dateformat": "^3.0.0", + "handlebars": "^4.1.2", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.2.1", + "meow": "^4.0.0", + "semver": "^6.0.0", + "split": "^1.0.0", + "through2": "^3.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==", + "dev": true + }, + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + } + } + }, + "conventional-commits-filter": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.2.tgz", + "integrity": "sha512-WpGKsMeXfs21m1zIw4s9H5sys2+9JccTzpN6toXtxhpw2VNF2JUXwIakthKBy+LN4DvJm+TzWhxOMWOs1OFCFQ==", + "dev": true, + "requires": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.0" + } + }, + "conventional-commits-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.3.tgz", + "integrity": "sha512-KaA/2EeUkO4bKjinNfGUyqPTX/6w9JGshuQRik4r/wJz7rUw3+D3fDG6sZSEqJvKILzKXFQuFkpPLclcsAuZcg==", + "dev": true, + "requires": { + "JSONStream": "^1.0.4", + "is-text-path": "^2.0.0", + "lodash": "^4.2.1", + "meow": "^4.0.0", + "split2": "^2.0.0", + "through2": "^3.0.0", + "trim-off-newlines": "^1.0.0" + }, + "dependencies": { + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + } + } + }, + "conventional-recommended-bump": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-5.0.1.tgz", + "integrity": "sha512-RVdt0elRcCxL90IrNP0fYCpq1uGt2MALko0eyeQ+zQuDVWtMGAy9ng6yYn3kax42lCj9+XBxQ8ZN6S9bdKxDhQ==", + "dev": true, + "requires": { + "concat-stream": "^2.0.0", + "conventional-changelog-preset-loader": "^2.1.1", + "conventional-commits-filter": "^2.0.2", + "conventional-commits-parser": "^3.0.3", + "git-raw-commits": "2.0.0", + "git-semver-tags": "^2.0.3", + "meow": "^4.0.0", + "q": "^1.5.1" + }, + "dependencies": { + "concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + } + } + }, + "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==", + "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-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "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==", + "dev": true, + "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" + }, + "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" + } + } + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "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.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.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==", + "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" + } + }, + "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" + } + }, + "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-loader": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.2.0.tgz", + "integrity": "sha512-QTF3Ud5H7DaZotgdcJjGMvyDj5F3Pn1j/sC6VBEOVp94cbwqyIBdcs/quzj4MC1BKQSrTpQznegH/5giYbhnCQ==", + "dev": true, + "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" + } + }, + "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-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 + }, + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "cssstyle": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "dev": true, + "requires": { + "cssom": "0.3.x" + } + }, + "csstype": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.6.tgz", + "integrity": "sha512-RpFbQGUE74iyPgvr46U9t1xoQBM8T4BL8SxrN66Le2xYAPSaDJJKeztV3awugusb3g3G9iL8StmkBBXhcbbXhg==", + "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" + } + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "dev": true + }, + "d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + }, + "d3-collection": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", + "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" + }, + "d3-color": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.3.0.tgz", + "integrity": "sha512-NHODMBlj59xPAwl2BDiO2Mog6V+PrGRtBfWKqKRrs9MCqlSkIEb0Z/SfY7jW29ReHTDC/j+vwXhnZcXI3+3fbg==" + }, + "d3-dispatch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.5.tgz", + "integrity": "sha512-vwKx+lAqB1UuCeklr6Jh1bvC4SZgbSqbkGBLClItFBIYH4vqDJCA7qfoy14lXmJdnBOdxndAMxjCbImJYW7e6g==" + }, + "d3-force": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz", + "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==", + "requires": { + "d3-collection": "1", + "d3-dispatch": "1", + "d3-quadtree": "1", + "d3-timer": "1" + } + }, + "d3-format": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.3.2.tgz", + "integrity": "sha512-Z18Dprj96ExragQ0DeGi+SYPQ7pPfRMtUXtsg/ChVIKNBCzjO8XYJvRTC1usblx52lqge56V5ect+frYTQc8WQ==" + }, + "d3-interpolate": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.3.2.tgz", + "integrity": "sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w==", + "requires": { + "d3-color": "1" + } + }, + "d3-path": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.8.tgz", + "integrity": "sha512-J6EfUNwcMQ+aM5YPOB8ZbgAZu6wc82f/0WFxrxwV6Ll8wBwLaHLKCqQ5Imub02JriCVVdPjgI+6P3a4EWJCxAg==" + }, + "d3-quadtree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.6.tgz", + "integrity": "sha512-NUgeo9G+ENQCQ1LsRr2qJg3MQ4DJvxcDNCiohdJGHt5gRhBW6orIB5m5FJ9kK3HNL8g9F4ERVoBzcEwQBfXWVA==" + }, + "d3-scale": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.7.tgz", + "integrity": "sha512-KvU92czp2/qse5tUfGms6Kjig0AhHOwkzXG0+PqIJB3ke0WUv088AHMZI0OssO9NCkXt4RP8yju9rpH8aGB7Lw==", + "requires": { + "d3-array": "^1.2.0", + "d3-collection": "1", + "d3-color": "1", + "d3-format": "1", + "d3-interpolate": "1", + "d3-time": "1", + "d3-time-format": "2" + } + }, + "d3-selection": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.0.tgz", + "integrity": "sha512-EYVwBxQGEjLCKF2pJ4+yrErskDnz5v403qvAid96cNdCMr8rmCYfY5RGzWz24mdIbxmDf6/4EAH+K9xperD5jg==" + }, + "d3-shape": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.5.tgz", + "integrity": "sha512-VKazVR3phgD+MUCldapHD7P9kcrvPcexeX/PkMJmkUov4JM8IxsSg1DvbYoYich9AtdTsa5nNk2++ImPiDiSxg==", + "requires": { + "d3-path": "1" + } + }, + "d3-time": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.11.tgz", + "integrity": "sha512-Z3wpvhPLW4vEScGeIMUckDW7+3hWKOQfAWg/U7PlWBnQmeKQ00gCUsTtWSYulrKNA7ta8hJ+xXc6MHrMuITwEw==" + }, + "d3-time-format": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.3.tgz", + "integrity": "sha512-6k0a2rZryzGm5Ihx+aFMuO1GgelgIz+7HhB4PH4OEndD5q2zGn1mDfRdNrulspOfR6JXkb2sThhDK41CSK85QA==", + "requires": { + "d3-time": "1" + } + }, + "d3-timer": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.9.tgz", + "integrity": "sha512-rT34J5HnQUHhcLvhSB9GjCkN0Ddd5Y8nCwDBG2u6wQEeYxT/Lf51fTFFkldeib/sE/J0clIe0pnCfs6g/lRbyg==" + }, + "dargs": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz", + "integrity": "sha1-A6nbtLXC8Tm/FK5T8LiipqhvThc=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "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": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "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" + } + } + } + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true + }, + "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" + } + }, + "debuglog": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", + "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=", + "dev": true + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "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 + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.0.tgz", + "integrity": "sha512-ZbfWJq/wN1Z273o7mUSjILYqehAktR2NVoSrOukDkU9kg2v/Uv89yU4Cvz8seJeAmtN5oqiefKq8FPuXOboqLw==", + "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 + }, + "default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + } + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "define-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" + }, + "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==", + "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" + } + } + } + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "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": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "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-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, + "detect-indent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", + "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50=", + "dev": true + }, + "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==", + "dev": true + }, + "dezalgo": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", + "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", + "dev": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "diff-sequences": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", + "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "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" + } + }, + "dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "requires": { + "path-type": "^3.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "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" + } + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "requires": { + "utila": "~0.4" + } + }, + "dom-serializer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.1.tgz", + "integrity": "sha512-sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", + "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": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "dev": true, + "requires": { + "webidl-conversions": "^4.0.2" + } + }, + "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-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==", + "dev": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + }, + "dependencies": { + "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==", + "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" + } + } + } + }, + "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" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "elliptic": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz", + "integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==", + "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" + } + }, + "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 + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "dev": true, + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "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==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "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" + } + }, + "entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", + "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==", + "dev": true + }, + "env-paths": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz", + "integrity": "sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=", + "dev": true + }, + "err-code": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", + "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", + "dev": true + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "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" + } + }, + "es-abstract": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.14.1.tgz", + "integrity": "sha512-cp/Tb1oA/rh2X7vqeSOvM+TSo3UkJLX70eNihgVEvnzwAgikjkTFr/QVgRCaxjm0knCNQzNoxxxcw2zO2LJdZA==", + "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.0.0", + "string.prototype.trimright": "^2.0.0" + } + }, + "es-to-primitive": { + "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==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escodegen": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.12.0.tgz", + "integrity": "sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg==", + "dev": true, + "requires": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "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": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "dev": true + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "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": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", + "dev": true + }, + "events": { + "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==", + "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.2", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz", + "integrity": "sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==", + "dev": true + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "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" + } + }, + "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" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "expect": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.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": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "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 + } + } + }, + "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" + }, + "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==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "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-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" + } + } + } + }, + "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": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "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": { + "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" + } + } + } + } + } + }, + "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=", + "dev": true + }, + "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 + }, + "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" + } + }, + "fb-watchman": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", + "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", + "dev": true, + "requires": { + "bser": "^2.0.0" + } + }, + "figgy-pudding": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", + "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-loader": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.2.0.tgz", + "integrity": "sha512-+xZnaK5R8kBJrHK0/6HRlrKNamvVS5rjyuju+rnyxRGuwUJwpAMsVzUl5dz6rK8brkzjV6JpcFNjp6NqV0g1OQ==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.0.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" + } + } + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "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" + } + }, + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "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==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + }, + "dependencies": { + "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==", + "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" + } + } + } + }, + "follow-redirects": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.8.1.tgz", + "integrity": "sha512-micCIbldHioIegeKs41DoH0KS3AXfFzgS30qVkM6z/XOE/GJgvmsoc839NUqa1B9udYe9dQxgv7KFwng6+p/dw==", + "dev": true, + "requires": { + "debug": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "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 + }, + "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" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "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 + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + }, + "dependencies": { + "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==", + "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" + } + } + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.6.tgz", + "integrity": "sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ==", + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "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=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + }, + "dependencies": { + "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==", + "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" + } + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": 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, + "dev": 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, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true, + "optional": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.3.0", + "bundled": true, + "dev": 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, + "dev": 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, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": 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, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": 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" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "dev": 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, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "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 + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "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" + }, + "dependencies": { + "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": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "genfun": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", + "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-pkg-repo": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz", + "integrity": "sha1-xztInAbYDMVTbCyFP54FIyBWly0=", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "meow": "^3.3.0", + "normalize-package-data": "^2.3.0", + "parse-github-repo-url": "^1.3.0", + "through2": "^2.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 + }, + "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" + } + }, + "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" + } + }, + "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" + } + }, + "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" + } + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "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": { + "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" + } + }, + "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" + } + }, + "redent": { + "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" + } + }, + "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" + } + }, + "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": { + "get-stdin": "^4.0.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + } + } + }, + "get-port": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz", + "integrity": "sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==", + "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": "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" + } + }, + "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" + } + }, + "git-raw-commits": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.0.tgz", + "integrity": "sha512-w4jFEJFgKXMQJ0H0ikBk2S+4KP2VEjhCvLCNqbNRQC8BgGWgLKNCO7a9K9LI+TVT7Gfoloje502sEnctibffgg==", + "dev": true, + "requires": { + "dargs": "^4.0.1", + "lodash.template": "^4.0.2", + "meow": "^4.0.0", + "split2": "^2.0.0", + "through2": "^2.0.0" + } + }, + "git-remote-origin-url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", + "integrity": "sha1-UoJlna4hBxRaERJhEq0yFuxfpl8=", + "dev": true, + "requires": { + "gitconfiglocal": "^1.0.0", + "pify": "^2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "git-semver-tags": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-2.0.3.tgz", + "integrity": "sha512-tj4FD4ww2RX2ae//jSrXZzrocla9db5h0V7ikPl1P/WwoZar9epdUhwR7XHXSgc+ZkNq72BEEerqQuicoEQfzA==", + "dev": true, + "requires": { + "meow": "^4.0.0", + "semver": "^6.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==", + "dev": true + } + } + }, + "git-up": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.1.tgz", + "integrity": "sha512-LFTZZrBlrCrGCG07/dm1aCjjpL1z9L3+5aEeI9SBhAqSc+kiA9Or1bgZhQFNppJX6h/f5McrvJt1mQXTFm6Qrw==", + "dev": true, + "requires": { + "is-ssh": "^1.3.0", + "parse-url": "^5.0.0" + } + }, + "git-url-parse": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.1.2.tgz", + "integrity": "sha512-gZeLVGY8QVKMIkckncX+iCq2/L8PlwncvDFKiWkBn9EtCfYDbliRTTp6qzyQ1VMdITUfq7293zDzfpjdiGASSQ==", + "dev": true, + "requires": { + "git-up": "^4.0.0" + } + }, + "gitconfiglocal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", + "integrity": "sha1-QdBF84UaXqiPA/JMocYXgRRGS5s=", + "dev": true, + "requires": { + "ini": "^1.3.2" + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", + "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", + "dev": true, + "requires": { + "is-glob": "^4.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=", + "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": { + "global-prefix": "^3.0.0" + }, + "dependencies": { + "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": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + } + } + } + }, + "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" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "globby": { + "version": "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 + } + } + }, + "graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "dev": true + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true + }, + "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 + }, + "handlebars": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.2.0.tgz", + "integrity": "sha512-Kb4xn5Qh1cxAKvQnzNWZ512DhABzyFNmsaJf3OAkWNa4NkaqWcNI8Tao8Tasi0/F4JD9oyG0YxuFyvyR57d+Gw==", + "dev": true, + "requires": { + "neo-async": "^2.6.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + } + }, + "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.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "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": { + "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.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.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==", + "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 + }, + "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" + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", + "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==", + "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", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "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" + } + } + } + }, + "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==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.1" + } + }, + "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==", + "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" + } + }, + "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=", + "dev": true, + "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": { + "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==", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + } + } + }, + "html-webpack-template": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/html-webpack-template/-/html-webpack-template-6.2.0.tgz", + "integrity": "sha512-wyzIjbe9yXGyQ6yAeFjWmku7YOlW85w1dxqLnAQ564uRNNoBhpZVTQl7ouROoyQrfZUSoPUJiw7oWn31NDiuQQ==", + "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-cache-semantics": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", + "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", + "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.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" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "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=", + "dev": true + }, + "http-proxy": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", + "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "dev": true, + "requires": { + "eventemitter3": "^3.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", + "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "dev": true, + "requires": { + "agent-base": "4", + "debug": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "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 + } + } + }, + "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==", + "dev": true, + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + } + }, + "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 + }, + "https-proxy-agent": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz", + "integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==", + "dev": true, + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "dev": true, + "requires": { + "postcss": "^7.0.14" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "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 + }, + "ignore-walk": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "dev": true, + "requires": { + "import-from": "^2.1.0" + } + }, + "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": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "dev": true, + "requires": { + "resolve-from": "^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" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "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==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "init-package-json": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/init-package-json/-/init-package-json-1.10.3.tgz", + "integrity": "sha512-zKSiXKhQveNteyhcj1CoOP8tqp1QuxPIPBl8Bid99DGLFqA1p87M6lNgfjJHSBoWJJlidGOv5rWjyYKEB3g2Jw==", + "dev": true, + "requires": { + "glob": "^7.1.1", + "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0", + "promzard": "^0.3.0", + "read": "~1.0.1", + "read-package-json": "1 || 2", + "semver": "2.x || 3.x || 4 || 5", + "validate-npm-package-license": "^3.0.1", + "validate-npm-package-name": "^3.0.0" + } + }, + "inquirer": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "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" + }, + "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 + }, + "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" + }, + "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" + } + } + } + }, + "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" + }, + "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 + } + } + } + } + }, + "internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "dev": true, + "requires": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + } + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", + "dev": true + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.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 + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "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=", + "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==", + "dev": true + }, + "is-absolute-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.1.tgz", + "integrity": "sha512-c2QjUwuMxLsld90sj3xYzpFYWJtuxkIn1f5ua9RTEYJt/vV2IsM+Py00/6qjV7qExgifUvt7qfyBGBBKm+2iBg==", + "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=", + "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-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.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.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "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, + "requires": { + "ci-info": "^2.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=", + "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-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=", + "dev": true + }, + "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 + } + } + }, + "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-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-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-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "^1.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 + }, + "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 + }, + "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" + }, + "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-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "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==", + "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==", + "dev": true, + "requires": { + "is-path-inside": "^2.1.0" + } + }, + "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" + } + }, + "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=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "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=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-ssh": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.1.tgz", + "integrity": "sha512-0eRIASHZt1E68/ixClI8bp2YK2wmBPVWEismTs6M+M099jKgrzl/3E976zIbImSIob48N2/XGe9y7ZiYdImSlg==", + "dev": true, + "requires": { + "protocols": "^1.1.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-text-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", + "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", + "dev": true, + "requires": { + "text-extensions": "^2.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "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 + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "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": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.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==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "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" + } + } + } + }, + "istanbul-reports": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", + "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", + "dev": true, + "requires": { + "handlebars": "^4.1.2" + } + }, + "jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", + "integrity": "sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw==", + "dev": true, + "requires": { + "import-local": "^2.0.0", + "jest-cli": "^24.9.0" + }, + "dependencies": { + "jest-cli": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.9.0.tgz", + "integrity": "sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==", + "dev": true, + "requires": { + "@jest/core": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "import-local": "^2.0.0", + "is-ci": "^2.0.0", + "jest-config": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "prompts": "^2.0.1", + "realpath-native": "^1.1.0", + "yargs": "^13.3.0" + } + } + } + }, + "jest-changed-files": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz", + "integrity": "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "execa": "^1.0.0", + "throat": "^4.0.0" + } + }, + "jest-config": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", + "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.9.0", + "@jest/types": "^24.9.0", + "babel-jest": "^24.9.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.9.0", + "jest-environment-node": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.9.0", + "realpath-native": "^1.1.0" + } + }, + "jest-diff": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", + "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "diff-sequences": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-docblock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.9.0.tgz", + "integrity": "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==", + "dev": true, + "requires": { + "detect-newline": "^2.1.0" + } + }, + "jest-each": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", + "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-environment-jsdom": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", + "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0", + "jsdom": "^11.5.1" + } + }, + "jest-environment-node": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", + "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0" + } + }, + "jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "dev": true + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", + "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.9.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0", + "throat": "^4.0.0" + } + }, + "jest-leak-detector": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz", + "integrity": "sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA==", + "dev": true, + "requires": { + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", + "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", + "dev": true + }, + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + }, + "jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-resolve-dependencies": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz", + "integrity": "sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-snapshot": "^24.9.0" + } + }, + "jest-runner": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.9.0.tgz", + "integrity": "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.4.2", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-docblock": "^24.3.0", + "jest-haste-map": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-leak-detector": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.6.0", + "source-map-support": "^0.5.6", + "throat": "^4.0.0" + } + }, + "jest-runtime": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", + "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/source-map": "^24.3.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "strip-bom": "^3.0.0", + "yargs": "^13.3.0" + } + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true + }, + "jest-snapshot": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", + "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "expect": "^24.9.0", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.9.0", + "semver": "^6.2.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 + } + } + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + } + }, + "jest-validate": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", + "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "camelcase": "^5.3.1", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "leven": "^3.1.0", + "pretty-format": "^24.9.0" + } + }, + "jest-watcher": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.9.0.tgz", + "integrity": "sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==", + "dev": true, + "requires": { + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "jest-util": "^24.9.0", + "string-length": "^2.0.0" + } + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "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" + }, + "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==", + "dev": true + } + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "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" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "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": "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" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.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": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "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" + } + }, + "left-pad": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", + "dev": true + }, + "lerna": { + "version": "3.16.4", + "resolved": "https://registry.npmjs.org/lerna/-/lerna-3.16.4.tgz", + "integrity": "sha512-0HfwXIkqe72lBLZcNO9NMRfylh5Ng1l8tETgYQ260ZdHRbPuaLKE3Wqnd2YYRRkWfwPyEyZO8mZweBR+slVe1A==", + "dev": true, + "requires": { + "@lerna/add": "3.16.2", + "@lerna/bootstrap": "3.16.2", + "@lerna/changed": "3.16.4", + "@lerna/clean": "3.16.0", + "@lerna/cli": "3.13.0", + "@lerna/create": "3.16.0", + "@lerna/diff": "3.16.0", + "@lerna/exec": "3.16.0", + "@lerna/import": "3.16.0", + "@lerna/init": "3.16.0", + "@lerna/link": "3.16.2", + "@lerna/list": "3.16.0", + "@lerna/publish": "3.16.4", + "@lerna/run": "3.16.0", + "@lerna/version": "3.16.4", + "import-local": "^2.0.0", + "npmlog": "^4.1.2" + } + }, + "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.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" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.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 + } + } + }, + "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==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "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" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, + "lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=", + "dev": true + }, + "lodash.set": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", + "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", + "dev": true + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true + }, + "lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "loglevel": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.4.tgz", + "integrity": "sha512-p0b6mOGKcGa+7nnmKbpzR6qloPbrgLcnio++E+14Vo/XffOGwZtRpUhr8dTH/x2oCMmEoIU0Zwm3ZauhvYD17g==", + "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==", + "requires": { + "js-tokens": "^3.0.0 || ^4.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" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "macos-release": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz", + "integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "make-fetch-happen": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.0.tgz", + "integrity": "sha512-nFr/vpL1Jc60etMVKeaLOqfGjMMb3tAHFVJWxHOFCFS04Zmd7kGlMxo0l1tzfhoQje0/UPnd0X8OeGUiXXnfPA==", + "dev": true, + "requires": { + "agentkeepalive": "^3.4.1", + "cacache": "^12.0.0", + "http-cache-semantics": "^3.8.1", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.1", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "node-fetch-npm": "^2.0.2", + "promise-retry": "^1.1.1", + "socks-proxy-agent": "^4.0.0", + "ssri": "^6.0.0" + } + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "requires": { + "tmpl": "1.0.x" + } + }, + "mamacro": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", + "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==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", + "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": { + "object-visit": "^1.0.0" + } + }, + "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": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "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 + }, + "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" + }, + "dependencies": { + "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 + } + } + }, + "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" + }, + "dependencies": { + "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==", + "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" + } + } + } + }, + "meow": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", + "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", + "dev": true, + "requires": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist": "^1.1.3", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^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": { + "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 + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.4.tgz", + "integrity": "sha512-FYE8xI+6pjFOhokZu0We3S5NKCirLbCzSh2Usf3qEyr4X8U+0jNg9P8RZ4qz+V2UoECLVwSyzU3LxXBaLGtD3A==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "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" + } + }, + "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" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "dev": true + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "dev": true, + "requires": { + "mime-db": "1.40.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.0.tgz", + "integrity": "sha512-MNpRGbNA52q6U92i0qbVpQNsgk7LExy41MdAlG84FeytfDOtRIf/mCHdEgG8rpTKOaNKiqUnZdlptF469hxqOw==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "normalize-url": "1.9.1", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + }, + "dependencies": { + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.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==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0" + } + }, + "minipass": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.5.0.tgz", + "integrity": "sha512-9FwMVYhn6ERvMR8XFdOavRz4QK/VJV8elU1x50vYexf9lslDcWe/f4HBRxCPd185ekRSjU6CfYyJCECa/CQy7Q==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "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" + } + }, + "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" + }, + "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==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "mkdirp-promise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", + "integrity": "sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE=", + "dev": true, + "requires": { + "mkdirp": "*" + } + }, + "modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", + "dev": true + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "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" + }, + "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" + } + } + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "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": { + "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 + }, + "multimatch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-3.0.0.tgz", + "integrity": "sha512-22foS/gqQfANZ3o+W7ST2x25ueHDVNWl/b9OlGcLpy/iKxjCpvcNCM51YCenUi7Mt/jAjjqv8JwZRs8YP5sRjA==", + "dev": true, + "requires": { + "array-differ": "^2.0.3", + "array-union": "^1.0.2", + "arrify": "^1.0.1", + "minimatch": "^3.0.4" + } + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true, + "optional": 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": { + "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" + } + }, + "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 + }, + "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 + }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "requires": { + "lower-case": "^1.1.1" + } + }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", + "dev": true + }, + "node-fetch-npm": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz", + "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==", + "dev": true, + "requires": { + "encoding": "^0.1.11", + "json-parse-better-errors": "^1.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node-forge": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", + "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==", + "dev": true + }, + "node-gyp": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-5.0.3.tgz", + "integrity": "sha512-z/JdtkFGUm0QaQUusvloyYuGDub3nUbOo5de1Fz57cM++osBTvQatBUSTlF1k/w8vFHPxxXW6zxGvkxXSpaBkQ==", + "dev": true, + "requires": { + "env-paths": "^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", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^4.4.8", + "which": "1" + }, + "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" + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true + } + } + }, + "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": { + "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": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "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==", + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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-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": "5.4.3", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.3.tgz", + "integrity": "sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==", + "dev": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^1.1.0", + "semver": "^5.5.0", + "shellwords": "^0.1.1", + "which": "^1.3.0" + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "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==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-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 + }, + "npm-bundled": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", + "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", + "dev": true + }, + "npm-lifecycle": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/npm-lifecycle/-/npm-lifecycle-3.1.3.tgz", + "integrity": "sha512-M0QmmqbEHBXxDrmc6X3+eKjW9+F7Edg1ENau92WkYw1sox6wojHzEZJIRm1ItljEiaigZlKL8mXni/4ylAy1Dg==", + "dev": true, + "requires": { + "byline": "^5.0.0", + "graceful-fs": "^4.1.15", + "node-gyp": "^5.0.2", + "resolve-from": "^4.0.0", + "slide": "^1.1.6", + "uid-number": "0.0.6", + "umask": "^1.1.0", + "which": "^1.3.1" + }, + "dependencies": { + "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 + } + } + }, + "npm-package-arg": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", + "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", + "dev": true, + "requires": { + "hosted-git-info": "^2.7.1", + "osenv": "^0.1.5", + "semver": "^5.6.0", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.4.tgz", + "integrity": "sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw==", + "dev": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz", + "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "npm-package-arg": "^6.0.0", + "semver": "^5.4.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" + } + }, + "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": { + "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==", + "dev": true, + "requires": { + "boolbase": "~1.0.0" + } + }, + "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 + }, + "nwsapi": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.4.tgz", + "integrity": "sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw==", + "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==", + "dev": true + }, + "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=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "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" + } + }, + "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" + } + } + } + }, + "object-inspect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", + "dev": true + }, + "object-is": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz", + "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object-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": { + "isobject": "^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=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "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, + "requires": { + "isobject": "^3.0.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "octokit-pagination-methods": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz", + "integrity": "sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true + } + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "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" + }, + "dependencies": { + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "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=", + "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" + } + }, + "os-name": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", + "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==", + "dev": true, + "requires": { + "macos-release": "^2.2.0", + "windows-release": "^3.1.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=", + "dev": true + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "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-each-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", + "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", + "dev": true, + "requires": { + "p-reduce": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-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.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "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" + } + }, + "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-map-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-map-series/-/p-map-series-1.0.0.tgz", + "integrity": "sha1-v5j+V1cFZYqeE1G++4WuTB8Hvco=", + "dev": true, + "requires": { + "p-reduce": "^1.0.0" + } + }, + "p-pipe": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-pipe/-/p-pipe-1.2.0.tgz", + "integrity": "sha1-SxoROZoRUgpneQ7loMHViB1r7+k=", + "dev": true + }, + "p-queue": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-4.0.0.tgz", + "integrity": "sha512-3cRXXn3/O0o3+eVmUroJPSj/esxoEFIm0ZOno/T+NzG/VZgPOqQ8WKmlNqubSEpZmCIngEy34unkHGg83ZIBmg==", + "dev": true, + "requires": { + "eventemitter3": "^3.1.0" + } + }, + "p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", + "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" + }, + "dependencies": { + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + } + } + }, + "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 + }, + "p-waterfall": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-waterfall/-/p-waterfall-1.0.0.tgz", + "integrity": "sha1-ftlLPOszMngjU69qrhGqn8I1uwA=", + "dev": true, + "requires": { + "p-reduce": "^1.0.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==", + "dev": true, + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + }, + "dependencies": { + "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==", + "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" + } + } + } + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dev": true, + "requires": { + "no-case": "^2.2.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==", + "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-github-repo-url": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz", + "integrity": "sha1-nn2LslKmy2ukJZUGC3v23z28H1A=", + "dev": true + }, + "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": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "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 + }, + "parse-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.1.tgz", + "integrity": "sha512-d7yhga0Oc+PwNXDvQ0Jv1BuWkLVPXcAoQ/WREgd6vNNoKYaW52KI+RdOFjI63wjkmps9yUE8VS4veP+AgpQ/hA==", + "dev": true, + "requires": { + "is-ssh": "^1.3.0", + "protocols": "^1.4.0" + } + }, + "parse-url": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.1.tgz", + "integrity": "sha512-flNUPP27r3vJpROi0/R3/2efgKkyXqnXwyP1KQ2U0SfFRgdizOdWfvrrvJg1LuOoxs7GQhmxJlq23IpQ/BkByg==", + "dev": true, + "requires": { + "is-ssh": "^1.3.0", + "normalize-url": "^3.3.0", + "parse-path": "^4.0.0", + "protocols": "^1.4.0" + } + }, + "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==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "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 + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "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 + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-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=", + "dev": true + }, + "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 + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "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" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "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" + } + }, + "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.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz", + "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "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" + } + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "requires": { + "node-modules-regexp": "^1.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==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "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.23", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.23.tgz", + "integrity": "sha512-B729mL/uLklxtxuiJKfQ84WPxNw5a7Yhx3geQZdcA4GjNjZSTSSMMWyoennMVnTWSmAR0lMdzWYN0JLnHrg1KQ==", + "dev": true, + "requires": { + "async": "^1.5.2", + "debug": "^2.2.0", + "mkdirp": "0.5.x" + }, + "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 + } + } + }, + "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 + }, + "postcss": { + "version": "7.0.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.17.tgz", + "integrity": "sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-load-config": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz", + "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + } + }, + "postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.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==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.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==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + } + }, + "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==", + "dev": true, + "requires": { + "icss-utils": "^4.1.1", + "postcss": "^7.0.16", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.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==", + "dev": true, + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" + } + }, + "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==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "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==", + "dev": true + }, + "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=", + "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=", + "dev": true, + "requires": { + "renderkid": "^2.0.1", + "utila": "~0.4" + } + }, + "pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + }, + "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 + } + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "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 + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "promise-retry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", + "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", + "dev": true, + "requires": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + } + }, + "prompts": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.2.1.tgz", + "integrity": "sha512-VObPvJiWPhpZI6C5m60XOzTfnYg/xc/an+r9VYymj9WJW3B/DIH+REzjpAACPf8brwPeP+7vz3bIim3S+AaMjw==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.3" + } + }, + "promzard": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/promzard/-/promzard-0.3.0.tgz", + "integrity": "sha1-JqXW7ox97kyxIggwWs+5O6OCqe4=", + "dev": true, + "requires": { + "read": "1" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "dev": true + }, + "protocols": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.7.tgz", + "integrity": "sha512-Fx65lf9/YDn3hUX08XUc0J8rSux36rEsyiv21ZGUC1mOyeM3lTRpZLcrm8aAolzS4itwVfm7TAPyxC2E5zd6xg==", + "dev": true + }, + "protoduck": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz", + "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", + "dev": true, + "requires": { + "genfun": "^5.0.0" + } + }, + "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==", + "dev": true, + "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=", + "dev": true + }, + "psl": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.3.1.tgz", + "integrity": "sha512-2KLd5fKOdAfShtY2d/8XDWVRnmp3zp40Qt6ge2zBPFARLXOGUf2fHD5eg+TV/5oxBtQKVhjUaKFsAaE4HnwfSA==", + "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" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "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==", + "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=", + "dev": true, + "requires": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", + "dev": true + }, + "quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "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, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.4.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" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + } + } + }, + "react": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.9.0.tgz", + "integrity": "sha512-+7LQnFBwkiw+BobzOF6N//BdoNw0ouwmSJTEm9cglOOmsg/TMiFHZLe2sEoN5M7LgJTj9oHH0gxklfnQe66S1w==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-dom": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.9.0.tgz", + "integrity": "sha512-YFT2rxO9hM70ewk9jq0y6sQk8cL02xm4+IzYBz75CQGlClQQ1Bxq0nhHF6OtSbit+AIahujJgb/CPRibFkMNJQ==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.15.0" + } + }, + "react-financial-charts": { + "version": "file:packages/react-financial-charts", + "requires": { + "d3-array": "^1.2.1", + "d3-collection": "^1.0.7", + "d3-force": "^1.1.0", + "d3-format": "^1.2.1", + "d3-interpolate": "^1.1.6", + "d3-path": "^1.0.8", + "d3-scale": "^1.0.7", + "d3-selection": "^1.2.0", + "d3-shape": "^1.2.0", + "d3-time": "^1.0.11", + "d3-time-format": "^2.1.3", + "debug": "^4.1.1", + "lodash.flattendeep": "^4.4.0", + "prop-types": "^15.7.2", + "save-svg-as-png": "^1.4.14" + } + }, + "react-financial-charts-examples": { + "version": "file:packages/react-financial-charts-examples", + "requires": { + "d3-format": "^1.2.1", + "d3-scale": "^1.0.7", + "d3-time-format": "^2.1.3", + "react": "^16.9.0", + "react-dom": "^16.9.0", + "react-financial-charts": "file:packages/react-financial-charts", + "react-virtualized-auto-sizer": "^1.0.2" + } + }, + "react-is": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz", + "integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==" + }, + "react-virtualized-auto-sizer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.2.tgz", + "integrity": "sha512-MYXhTY1BZpdJFjUovvYHVBmkq79szK/k7V3MO+36gJkWGkrXKtyr4vCPtpphaTLRAdDNoYEYFZWE8LjN+PIHNg==" + }, + "read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "dev": true, + "requires": { + "mute-stream": "~0.0.4" + } + }, + "read-cmd-shim": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-1.0.4.tgz", + "integrity": "sha512-Pqpl3qJ/QdOIjRYA0q5DND/gLvGOfpIz/fYVDGYpOXfW/lFrIttmLsBnd6IkyK10+JHU9zhsaudfvrQTBB9YFQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2" + } + }, + "read-package-json": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.0.tgz", + "integrity": "sha512-KLhu8M1ZZNkMcrq1+0UJbR8Dii8KZUqB0Sha4mOx/bknfKI/fyrQVrG/YIt2UOtG667sD8+ee4EXMM91W9dC+A==", + "dev": true, + "requires": { + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "json-parse-better-errors": "^1.0.1", + "normalize-package-data": "^2.0.0", + "slash": "^1.0.0" + }, + "dependencies": { + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + } + } + }, + "read-package-tree": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz", + "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==", + "dev": true, + "requires": { + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0", + "util-promisify": "^2.1.0" + } + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.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==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdir-scoped-modules": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", + "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", + "dev": true, + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "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==", + "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" + } + } + } + }, + "realpath-native": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", + "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "dev": true, + "requires": { + "util.promisify": "^1.0.0" + } + }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "dev": true, + "requires": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "define-properties": "^1.1.2" + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "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=", + "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==", + "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" + } + }, + "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 + }, + "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 + }, + "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" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "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.0", + "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.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + } + } + }, + "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==", + "dev": true, + "requires": { + "lodash": "^4.17.11" + } + }, + "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==", + "dev": true, + "requires": { + "request-promise-core": "1.1.2", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-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 + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "dev": true, + "requires": { + "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=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "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" + } + } + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "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=", + "dev": true + }, + "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" + } + }, + "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 + }, + "retry": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", + "dev": true + }, + "rimraf": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", + "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rsvp": { + "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=", + "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=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "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" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "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" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sane": { + "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": "^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" + } + }, + "save-svg-as-png": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/save-svg-as-png/-/save-svg-as-png-1.4.14.tgz", + "integrity": "sha512-hJqOFSdRvhBVD2pQSM+mJStvQGfnvQCCF6ULtAxdjF4lDwXYfWZ9Eug0fcRl05YyPL2yknCDBEOpbO4Fkw5qmg==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "scheduler": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.15.0.tgz", + "integrity": "sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "schema-utils": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.2.0.tgz", + "integrity": "sha512-5EwsCNhfFTZvUreQhx/4vVQpJ/lnCAkgoIHLhSpp4ZirE+4hzFvdJi0FMub6hxbFVBJYSpeVVmon+2e7uEGRrA==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1" + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selfsigned": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.4.tgz", + "integrity": "sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==", + "dev": true, + "requires": { + "node-forge": "0.7.5" + } + }, + "semver": { + "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", + "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" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "ms": { + "version": "2.1.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==", + "dev": true + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.14.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", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "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 + }, + "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, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^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" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "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==", + "dev": true + }, + "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, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "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 + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "sisteransi": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.3.tgz", + "integrity": "sha512-SbEG75TzH8G7eVXFSN5f9EExILKfly7SUvVY5DhhYLvfhKqhDFY0OzevWa/zwak0RLRfWS5AvfMWpd9gJvr5Yg==", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", + "dev": true + }, + "smart-buffer": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.2.tgz", + "integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw==", + "dev": true + }, + "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, + "requires": { + "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" + }, + "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" + } + }, + "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" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "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, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^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" + } + }, + "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" + } + } + } + }, + "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, + "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==", + "dev": true, + "requires": { + "faye-websocket": "^0.10.0", + "uuid": "^3.0.1" + } + }, + "sockjs-client": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.3.0.tgz", + "integrity": "sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==", + "dev": true, + "requires": { + "debug": "^3.2.5", + "eventsource": "^1.0.7", + "faye-websocket": "~0.11.1", + "inherits": "^2.0.3", + "json3": "^3.3.2", + "url-parse": "^1.4.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "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, + "requires": { + "websocket-driver": ">=0.5.1" + } + } + } + }, + "socks": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.2.tgz", + "integrity": "sha512-pCpjxQgOByDHLlNqlnh/mNSAxIUkyBBuwwhTcV+enZGbDaClPvHdvm6uvOwZfFJkam7cGhBNbb4JxiP8UZkRvQ==", + "dev": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "4.0.2" + } + }, + "socks-proxy-agent": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz", + "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==", + "dev": true, + "requires": { + "agent-base": "~4.2.1", + "socks": "~2.3.2" + }, + "dependencies": { + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + } + } + }, + "sort-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", + "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "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 + }, + "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==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "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 + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "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==", + "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==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "spdy": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.1.tgz", + "integrity": "sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "requires": { + "through": "2" + } + }, + "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" + } + }, + "split2": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", + "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", + "dev": true, + "requires": { + "through2": "^2.0.2" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "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", + "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" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "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 + }, + "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": { + "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" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "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" + }, + "dependencies": { + "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==", + "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" + } + } + } + }, + "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==", + "dev": true, + "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" + }, + "dependencies": { + "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==", + "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" + } + } + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "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=", + "dev": true + }, + "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=", + "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" + } + } + } + }, + "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" + }, + "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-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" + } + } + } + }, + "string.prototype.trimleft": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.0.0.tgz", + "integrity": "sha1-aLaqjhYsaoDnbjqKDC50cYbicf8=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.0.2" + } + }, + "string.prototype.trimright": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.0.0.tgz", + "integrity": "sha1-q0pW2AKgH75yk+EehPJNyBZGYd0=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.0.2" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "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" + } + }, + "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-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, + "strong-log-transformer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz", + "integrity": "sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA==", + "dev": true, + "requires": { + "duplexer": "^0.1.1", + "minimist": "^1.2.0", + "through": "^2.3.4" + } + }, + "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" + } + }, + "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 + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "tar": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.10.tgz", + "integrity": "sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.5", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "temp-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", + "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=", + "dev": true + }, + "temp-write": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/temp-write/-/temp-write-3.4.0.tgz", + "integrity": "sha1-jP9jD7fp2gXwR8dM5M5NaFRX1JI=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "is-stream": "^1.1.0", + "make-dir": "^1.0.0", + "pify": "^3.0.0", + "temp-dir": "^1.0.0", + "uuid": "^3.0.1" + }, + "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 + } + } + }, + "terser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.2.1.tgz", + "integrity": "sha512-cGbc5utAcX4a9+2GGVX4DsenG6v0x3glnDi5hx8816X1McEAwPlPgRtXPJzSBsbpILxZ8MQMT0KvArLuE0HP5A==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "dependencies": { + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "dev": true + } + } + }, + "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==", + "dev": true, + "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" + }, + "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==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "dev": true, + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + } + }, + "text-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.0.0.tgz", + "integrity": "sha512-F91ZqLgvi1E0PdvmxMgp+gcf6q8fMH7mhdwWfzXnl1k+GbpQDmi8l7DzLC5JTASKbwpY3TfxajAUzAXcv2NmsQ==", + "dev": true + }, + "thenify": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", + "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", + "dev": true, + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "dev": true, + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, + "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==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + }, + "dependencies": { + "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==", + "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" + } + } + } + }, + "thunky": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.3.tgz", + "integrity": "sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==", + "dev": true + }, + "timers-browserify": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", + "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "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=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-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": { + "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" + } + } + } + }, + "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": { + "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=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.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 + }, + "toposort": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", + "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", + "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" + } + }, + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "dev": true + }, + "trim-off-newlines": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz", + "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", + "dev": true + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, + "ts-loader": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-6.0.4.tgz", + "integrity": "sha512-p2zJYe7OtwR+49kv4gs7v4dMrfYD1IPpOtqiSPCbe8oR+4zEBtdHwzM7A7M91F+suReqgzZrlClk4LRSSp882g==", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^1.0.2", + "micromatch": "^4.0.0", + "semver": "^6.0.0" + }, + "dependencies": { + "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" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "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 + }, + "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": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "dev": true + }, + "tslint": { + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.19.0.tgz", + "integrity": "sha512-1LwwtBxfRJZnUvoS9c0uj8XQtAnyhWr9KlNvDIdB+oXyT+VpsOAaEhEgKi1HrZ8rq0ki/AAnbGSv4KM6/AfVZw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.29.0" + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "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 + }, + "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" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-fest": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", + "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.2.tgz", + "integrity": "sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + } + } + }, + "uid-number": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=", + "dev": true + }, + "umask": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/umask/-/umask-1.1.0.tgz", + "integrity": "sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0=", + "dev": true + }, + "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, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "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==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "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": { + "imurmurhash": "^0.1.4" + } + }, + "universal-user-agent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz", + "integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==", + "dev": true, + "requires": { + "os-name": "^3.1.0" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "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": { + "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=", + "dev": true, + "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=", + "dev": true, + "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=", + "dev": true + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "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==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "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": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "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 + }, + "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 + } + } + }, + "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": "2.1.0", + "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz", + "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=", + "dev": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "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==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "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": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", + "dev": true + }, + "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==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", + "dev": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "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.0", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz", + "integrity": "sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==", + "dev": true + }, + "w3c-hr-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", + "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", + "dev": true, + "requires": { + "browser-process-hrtime": "^0.1.2" + } + }, + "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": "1.6.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", + "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "dev": true, + "requires": { + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true + }, + "webpack": { + "version": "4.39.3", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.39.3.tgz", + "integrity": "sha512-BXSI9M211JyCVc3JxHWDpze85CvjC842EvpRsVTc/d15YJGlox7GIDd38kJgWrb3ZluyvIjgenbLDMBQPDcxYQ==", + "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" + }, + "dependencies": { + "acorn": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", + "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==", + "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" + } + } + } + }, + "webpack-cli": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.7.tgz", + "integrity": "sha512-OhTUCttAsr+IZSMVwGROGRHvT+QAs8H6/mHIl4SvhAwYywjiylYjpwybGx7WQ9Hkb45FhjtsymkwiRRbGJ1SZQ==", + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "webpack-dev-middleware": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.1.tgz", + "integrity": "sha512-5MWu9SH1z3hY7oHOV6Kbkz5x7hXbxK56mGHNqHTe6d+ewxOwKUxoUJBs7QIaJb33lPjl9bJZ3X0vCoooUzC36A==", + "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" + }, + "dependencies": { + "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.8.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.8.0.tgz", + "integrity": "sha512-Hs8K9yI6pyMvGkaPTeTonhD6JXVsigXDApYk9JLW4M7viVBspQvb1WdAcWxqtmttxNW4zf2UFLsLNe0y87pIGQ==", + "dev": true, + "requires": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.1.6", + "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.0", + "killable": "^1.0.1", + "loglevel": "^1.6.3", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.21", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.4", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "0.3.19", + "sockjs-client": "1.3.0", + "spdy": "^4.0.1", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.0", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "12.0.5" + }, + "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" + } + } + } + }, + "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==", + "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=", + "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" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "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" + }, + "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" + } + } + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "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": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "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" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dev": true, + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + } + }, + "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, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "websocket-driver": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz", + "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==", + "dev": true, + "requires": { + "http-parser-js": ">=0.4.0 <0.4.11", + "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==", + "dev": true + }, + "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, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "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 + }, + "whatwg-url": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", + "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "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" + } + }, + "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 + }, + "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" + }, + "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 + }, + "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" + } + } + } + }, + "windows-release": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz", + "integrity": "sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA==", + "dev": true, + "requires": { + "execa": "^1.0.0" + } + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "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" + }, + "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-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" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write-file-atomic": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", + "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "write-json-file": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/write-json-file/-/write-json-file-3.2.0.tgz", + "integrity": "sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ==", + "dev": true, + "requires": { + "detect-indent": "^5.0.0", + "graceful-fs": "^4.1.15", + "make-dir": "^2.1.0", + "pify": "^4.0.1", + "sort-keys": "^2.0.0", + "write-file-atomic": "^2.4.2" + }, + "dependencies": { + "write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + } + } + }, + "write-pkg": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/write-pkg/-/write-pkg-3.2.0.tgz", + "integrity": "sha512-tX2ifZ0YqEFOF1wjRW2Pk93NLsj02+n1UP5RvO6rCs0K6R2g1padvf006cY74PQJKMGS2r42NK7FD0dG6Y6paw==", + "dev": true, + "requires": { + "sort-keys": "^2.0.0", + "write-json-file": "^2.2.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 + }, + "write-json-file": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/write-json-file/-/write-json-file-2.3.0.tgz", + "integrity": "sha1-K2TIozAE1UuGmMdtWFp3zrYdoy8=", + "dev": true, + "requires": { + "detect-indent": "^5.0.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "pify": "^3.0.0", + "sort-keys": "^2.0.0", + "write-file-atomic": "^2.0.0" + } + } + } + }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, + "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 + }, + "xtend": { + "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": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "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.1" + } + }, + "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" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..8d09fbc69 --- /dev/null +++ b/package.json @@ -0,0 +1,51 @@ +{ + "name": "root", + "private": true, + "scripts": { + "postinstall": "npm run bootstrap", + "bootstrap": "lerna bootstrap", + "build": "lerna run build", + "clean": "lerna run clean && lerna clean --yes", + "convert": "lerna link convert && npm run bootstrap", + "lint": "lerna run lint", + "start": "lerna run start --stream", + "test": "lerna run test --stream", + "minor": "lerna version minor --conventional-commits --no-git-tag-version --no-push --no-changelog --yes", + "patch": "lerna version patch --conventional-commits --no-git-tag-version --no-push --no-changelog --yes", + "prepublishOnly": "npm run build", + "publish": "lerna publish from-package --no-git-reset --yes" + }, + "workspaces": [ + "packages/*" + ], + "devDependencies": { + "@types/d3-format": "^1.3.1", + "@types/d3-time-format": "^2.1.1", + "@types/jest": "^24.0.18", + "@types/react": "^16.9.2", + "@types/react-dom": "^16.9.0", + "@types/react-virtualized-auto-sizer": "^1.0.0", + "clean-webpack-plugin": "^3.0.0", + "css-loader": "^3.2.0", + "file-loader": "^4.2.0", + "html-webpack-plugin": "^3.2.0", + "html-webpack-template": "^6.2.0", + "jest": "^24.9.0", + "lerna": "^3.14.1", + "mini-css-extract-plugin": "^0.8.0", + "postcss-loader": "^3.0.0", + "react": "^16.9.0", + "react-dom": "^16.9.0", + "rimraf": "^3.0.0", + "ts-loader": "^6.0.4", + "tslint": "^5.19.0", + "typescript": "^3.6.2", + "webpack": "^4.39.3", + "webpack-cli": "^3.3.7", + "webpack-dev-server": "^3.8.0" + }, + "dependencies": { + "react-financial-charts": "file:packages/react-financial-charts", + "react-financial-charts-examples": "file:packages/react-financial-charts-examples" + } +} diff --git a/packages/react-financial-charts-examples/LICENSE b/packages/react-financial-charts-examples/LICENSE new file mode 100644 index 000000000..1ffc51b2f --- /dev/null +++ b/packages/react-financial-charts-examples/LICENSE @@ -0,0 +1,24 @@ +The MIT License (MIT) +https://github.com/reactivemarkets/react-financial-charts + +Copyright (c) 2015-2018 Ragu Ramaswamy +Copyright (c) 2016 Julien Renaux +Copyright (c) 2019 Reactive Markets + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/packages/react-financial-charts-examples/README.md b/packages/react-financial-charts-examples/README.md new file mode 100644 index 000000000..87321e1b9 --- /dev/null +++ b/packages/react-financial-charts-examples/README.md @@ -0,0 +1,12 @@ +# Test UI + +Test UI loads up all the tiles and uses the default App. + +## Quick Start + +Install dependencies and start the server. + +```bash +npm ci +npm start:test-ui +``` diff --git a/packages/react-financial-charts-examples/package.json b/packages/react-financial-charts-examples/package.json new file mode 100644 index 000000000..b7bb73052 --- /dev/null +++ b/packages/react-financial-charts-examples/package.json @@ -0,0 +1,22 @@ +{ + "name": "react-financial-charts-examples", + "version": "0.1.0", + "description": "Example charts", + "private": true, + "author": "reactivemarkets", + "license": "MIT", + "scripts": { + "clean": "rimraf dist", + "build": "webpack --config webpack.config.js", + "start": "webpack-dev-server --progress --config webpack.config.js" + }, + "dependencies": { + "d3-format": "^1.2.1", + "d3-scale": "^1.0.7", + "d3-time-format": "^2.1.3", + "react": "^16.9.0", + "react-dom": "^16.9.0", + "react-financial-charts": "file:../react-financial-charts", + "react-virtualized-auto-sizer": "^1.0.2" + } +} diff --git a/packages/react-financial-charts-examples/src/@types/d3-scale.d.ts b/packages/react-financial-charts-examples/src/@types/d3-scale.d.ts new file mode 100644 index 000000000..09eb2668b --- /dev/null +++ b/packages/react-financial-charts-examples/src/@types/d3-scale.d.ts @@ -0,0 +1 @@ +declare module 'd3-scale'; diff --git a/packages/react-financial-charts-examples/src/components/index.ts b/packages/react-financial-charts-examples/src/components/index.ts new file mode 100644 index 000000000..67fb34209 --- /dev/null +++ b/packages/react-financial-charts-examples/src/components/index.ts @@ -0,0 +1 @@ +export * from "./responsiveStockChart"; diff --git a/packages/react-financial-charts-examples/src/components/responsiveStockChart.tsx b/packages/react-financial-charts-examples/src/components/responsiveStockChart.tsx new file mode 100644 index 000000000..2d0c9abf6 --- /dev/null +++ b/packages/react-financial-charts-examples/src/components/responsiveStockChart.tsx @@ -0,0 +1,131 @@ +import * as React from "react"; +import AutoSizer from "react-virtualized-auto-sizer"; +import StockChart from "./stockChart"; + +export class ResponsiveStockChart extends React.Component { + public render() { + + const data = [ + { + close: 1.10294, + high: 1.10301, + low: 1.10286, + open: 1.10291, + time: new Date(1567715580000), + }, + { + close: 1.10292, + high: 1.10297, + low: 1.10287, + open: 1.10294, + time: new Date(1567715640000), + }, + { + close: 1.10291, + high: 1.10299, + low: 1.10285, + open: 1.10293, + time: new Date(1567715700000), + }, + { + close: 1.10292, + high: 1.10296, + low: 1.10282, + open: 1.10291, + time: new Date(1567715760000), + }, + { + close: 1.10293, + high: 1.10298, + low: 1.10288, + open: 1.10288, + time: new Date(1567715820000), + }, + { + close: 1.10289, + high: 1.10294, + low: 1.10286, + open: 1.10288, + time: new Date(1567715880000), + }, + { + close: 1.10293, + high: 1.10295, + low: 1.10283, + open: 1.10288, + time: new Date(1567715940000), + }, + { + close: 1.10288, + high: 1.10299, + low: 1.10287, + open: 1.10293, + time: new Date(1567716000000), + }, + { + close: 1.10292, + high: 1.10302, + low: 1.10286, + open: 1.1029, + time: new Date(1567716060000), + }, + { + close: 1.10295, + high: 1.10295, + low: 1.10282, + open: 1.10293, + time: new Date(1567716120000), + }, + { + close: 1.10286, + high: 1.10298, + low: 1.10283, + open: 1.10297, + time: new Date(1567716180000), + }, + { + close: 1.10294, + high: 1.10296, + low: 1.10286, + open: 1.10289, + time: new Date(1567716240000), + }, + { + close: 1.10289, + high: 1.10296, + low: 1.10284, + open: 1.10294, + time: new Date(1567716300000), + }, + { + close: 1.10285, + high: 1.10302, + low: 1.10285, + open: 1.10289, + time: new Date(1567716360000), + }, + { + close: 1.10281, + high: 1.10285, + low: 1.10281, + open: 1.10284, + time: new Date(1567716420000), + }, + ]; + + return ( +
+ + {({ height, width }) => { + return ( + + ); + }} + +
+ ); + } +} diff --git a/packages/react-financial-charts-examples/src/components/stockChart.tsx b/packages/react-financial-charts-examples/src/components/stockChart.tsx new file mode 100644 index 000000000..0a382b2da --- /dev/null +++ b/packages/react-financial-charts-examples/src/components/stockChart.tsx @@ -0,0 +1,77 @@ +// @ts-ignore +import { format } from "d3-format"; +import { scaleTime } from "d3-scale"; +import { timeFormat } from "d3-time-format"; +import * as React from "react"; +import { Chart, ChartCanvas } from "react-financial-charts"; +import { XAxis, YAxis } from "react-financial-charts/lib/axes"; +import { CrossHairCursor, EdgeIndicator, MouseCoordinateX, MouseCoordinateY } from "react-financial-charts/lib/coordinates"; +import { CandlestickSeries } from "react-financial-charts/lib/series"; +import { last } from "react-financial-charts/lib/utils"; +import { IOHLCData } from "../stores"; +import withDimensions from "./withDimensions"; + +interface StockChartProps { + readonly data: any[]; + readonly height: number; + readonly width: number; + readonly ratio: number; +} + +class StockChart extends React.Component { + public render() { + + const { + data, + height, + ratio, + width, + } = this.props; + + const xAccessor = (d: IOHLCData) => d.time; + + const start = xAccessor(last(data)); + const end = xAccessor(data[0]); + const xExtents = [start, end]; + + const yExtents = [(d: IOHLCData) => [d.high, d.low]]; + + return ( + + + + + + + + d.close} /> + + + + ); + } +} + +export default withDimensions(StockChart); diff --git a/packages/react-financial-charts-examples/src/components/withDimensions.tsx b/packages/react-financial-charts-examples/src/components/withDimensions.tsx new file mode 100644 index 000000000..1ed4fa054 --- /dev/null +++ b/packages/react-financial-charts-examples/src/components/withDimensions.tsx @@ -0,0 +1,96 @@ +import * as React from "react"; +import * as ReactDOM from "react-dom"; + +export default function withDimensions(WrappedComponent, props: any = {}) { + + const { + minWidth = 100, + minHeight = 100, + ratio, + width, + height, + } = props; + + function getDimensions(el) { + const w = el.parentNode.clientWidth; + const h = el.parentNode.clientHeight; + + return { + width: width !== undefined ? width : Math.max(w, minWidth), + height: height !== undefined ? height : Math.max(h, minHeight), + }; + } + + class ResponsiveComponent extends React.Component { + private node; + private testCanvas; + constructor(props: any) { + super(props); + this.handleWindowResize = this.handleWindowResize.bind(this); + this.getWrappedInstance = this.getWrappedInstance.bind(this); + this.saveNode = this.saveNode.bind(this); + this.setTestCanvas = this.setTestCanvas.bind(this); + this.state = {}; + } + public saveNode(node) { + this.node = node; + } + public setTestCanvas(node) { + this.testCanvas = node; + } + public getRatio() { + if (this.testCanvas !== undefined) { + const context = this.testCanvas.getContext("2d"); + + const devicePixelRatio = window.devicePixelRatio || 1; + const backingStoreRatio = context.webkitBackingStorePixelRatio || + context.mozBackingStorePixelRatio || + context.msBackingStorePixelRatio || + context.oBackingStorePixelRatio || + context.backingStorePixelRatio || 1; + + return devicePixelRatio / backingStoreRatio; + + } + return 1; + } + public componentDidMount() { + window.addEventListener("resize", this.handleWindowResize); + const dimensions = getDimensions(this.node); + + this.setState({ + ...dimensions, + ratio: ratio !== undefined ? ratio : this.getRatio(), + }); + } + public componentWillUnmount() { + window.removeEventListener("resize", this.handleWindowResize); + } + public handleWindowResize() { + const node = ReactDOM.findDOMNode(this.node); + this.setState(getDimensions(node)); + } + public getWrappedInstance() { + return this.node; + } + public render() { + const ref = { ref: this.saveNode }; + + if (this.state.width) { + return ; + } else { + return
+ +
; + } + } + } + + return ResponsiveComponent; +} diff --git a/packages/react-financial-charts-examples/src/index.tsx b/packages/react-financial-charts-examples/src/index.tsx new file mode 100644 index 000000000..3ce9c9565 --- /dev/null +++ b/packages/react-financial-charts-examples/src/index.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; +import { render } from "react-dom"; +import { ResponsiveStockChart } from "./components"; + +const app = ( + +); + +const containerElement = document.getElementById("app"); + +render(app, containerElement); diff --git a/packages/react-financial-charts-examples/src/stores/iOHLCData.ts b/packages/react-financial-charts-examples/src/stores/iOHLCData.ts new file mode 100644 index 000000000..a1ccda38a --- /dev/null +++ b/packages/react-financial-charts-examples/src/stores/iOHLCData.ts @@ -0,0 +1,7 @@ +export interface IOHLCData { + readonly close: number; + readonly high: number; + readonly low: number; + readonly open: number; + readonly time: Date; +} diff --git a/packages/react-financial-charts-examples/src/stores/index.ts b/packages/react-financial-charts-examples/src/stores/index.ts new file mode 100644 index 000000000..6f5df441c --- /dev/null +++ b/packages/react-financial-charts-examples/src/stores/index.ts @@ -0,0 +1 @@ +export * from "./iOHLCData"; diff --git a/packages/react-financial-charts-examples/tsconfig.json b/packages/react-financial-charts-examples/tsconfig.json new file mode 100644 index 000000000..011dd597c --- /dev/null +++ b/packages/react-financial-charts-examples/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "lib": [ + "esnext.array", + "esnext.asynciterable", + "esnext", + "dom" + ], + "moduleResolution": "node", + "module": "esnext", + "noImplicitAny": false, + "noImplicitThis": true, + "noUnusedLocals": true, + "outDir": "./dist/", + "sourceMap": true, + "strict": false, + "target": "es2017", + "baseUrl": "../.." + }, + "include": [ + "src" + ], + "exclude": [ + "node_modules", + "lib", + "dist", + "**/__tests__/**" + ] +} diff --git a/packages/react-financial-charts-examples/tslint.yaml b/packages/react-financial-charts-examples/tslint.yaml new file mode 100644 index 000000000..7325d2a01 --- /dev/null +++ b/packages/react-financial-charts-examples/tslint.yaml @@ -0,0 +1,10 @@ +--- +extends: "tslint:recommended" +rules: + completed-docs: false + interface-name: false + object-literal-sort-keys: false + only-arrow-functions: false + space-before-function-paren: false + max-line-length: + options: [140] diff --git a/packages/react-financial-charts-examples/webpack.config.js b/packages/react-financial-charts-examples/webpack.config.js new file mode 100644 index 000000000..e52840291 --- /dev/null +++ b/packages/react-financial-charts-examples/webpack.config.js @@ -0,0 +1,103 @@ +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); +const HtmlWebpackTemplate = require("html-webpack-template"); +const { CleanWebpackPlugin } = require("clean-webpack-plugin"); +const path = require("path"); + +module.exports = { + devtool: "source-map", + devServer: { + compress: true, + contentBase: "./dist", + historyApiFallback: true, + inline: true, + watchContentBase: true, + port: 8000, + }, + mode: "development", + entry: { + main: "./src/index.tsx", + }, + output: { + filename: "[name].[chunkhash:8].js", + chunkFilename: "[name].[chunkhash:8].js", + devtoolModuleFilenameTemplate: "[absolute-resource-path]", + path: path.join(__dirname, "dist"), + publicPath: "/" + }, + resolve: { + extensions: [".ts", ".tsx", ".js"] + }, + module: { + rules: [{ + test: /\.tsx?$/, + use: "ts-loader", + exclude: /node_modules/, + }, { + test: /\.(css)$/, + use: [{ + loader: MiniCssExtractPlugin.loader + }, { + loader: "css-loader" + }, { + loader: "postcss-loader", + }] + }, { + test: /\.(png|jpe?g|gif|woff2?|ttf|eot|ico|mp4)$/, + use: [{ + loader: "file-loader", + options: { + name: "[name].[hash:8].[ext]" + } + }] + }] + }, + node: { + dgram: "empty", + fs: "empty", + net: "empty", + tls: "empty", + child_process: "empty", + }, + optimization: { + splitChunks: { + chunks: "all", + }, + runtimeChunk: true, + }, + plugins: [ + new CleanWebpackPlugin(), + new MiniCssExtractPlugin({ + filename: "[name].[contenthash:8].css", + chunkFilename: '[name].[contenthash:8].chunk.css', + }), + new HtmlWebpackPlugin({ + inject: false, + template: HtmlWebpackTemplate, + appMountId: "app", + headHtmlSnippet: "", + bodyHtmlSnippet: "", + lang: "en", + meta: [{ + name: 'viewport', + content: 'width=device-width,initial-scale=1,minimum-scale=1' + }, { + name: "description", + content: "React Financial Charts" + }], + minify: { + removeComments: true, + collapseWhitespace: true, + removeRedundantAttributes: true, + useShortDoctype: true, + removeEmptyAttributes: true, + removeStyleLinkTypeAttributes: true, + keepClosingSlash: true, + minifyJS: true, + minifyCSS: true, + minifyURLs: true, + }, + title: "React Financial Charts" + }), + ] +}; diff --git a/packages/react-financial-charts/LICENSE b/packages/react-financial-charts/LICENSE new file mode 100644 index 000000000..1ffc51b2f --- /dev/null +++ b/packages/react-financial-charts/LICENSE @@ -0,0 +1,24 @@ +The MIT License (MIT) +https://github.com/reactivemarkets/react-financial-charts + +Copyright (c) 2015-2018 Ragu Ramaswamy +Copyright (c) 2016 Julien Renaux +Copyright (c) 2019 Reactive Markets + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/packages/react-financial-charts/jest.config.js b/packages/react-financial-charts/jest.config.js new file mode 100644 index 000000000..157ec0878 --- /dev/null +++ b/packages/react-financial-charts/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + preset: 'ts-jest', + collectCoverage: true, + coverageReporters: ["text-summary"], + errorOnDeprecated: true, + testMatch: [ + "**/__tests__/**/*.+(ts|tsx)" + ], +}; diff --git a/packages/react-financial-charts/package.json b/packages/react-financial-charts/package.json new file mode 100644 index 000000000..c93ad98ee --- /dev/null +++ b/packages/react-financial-charts/package.json @@ -0,0 +1,52 @@ +{ + "name": "react-financial-charts", + "version": "0.1.0", + "description": "React charts specific to finance.", + "main": "./lib/index.js", + "typings": "./lib/index.d.ts", + "files": [ + "lib" + ], + "sideEffects": false, + "author": "reactivemarkets", + "keywords": [ + "charts", + "charting", + "stockcharts", + "finance", + "finanical", + "finance-chart", + "react", + "d3" + ], + "license": "MIT", + "scripts": { + "build": "npm run clean && npm run compile", + "clean": "rimraf lib", + "compile": "tsc -p tsconfig.json", + "lint": "tslint -p tsconfig.json -c tslint.yaml", + "test": "jest", + "watch": "tsc -p tsconfig.json --watch --preserveWatchOutput" + }, + "dependencies": { + "d3-array": "^1.2.1", + "d3-collection": "^1.0.7", + "d3-force": "^1.1.0", + "d3-format": "^1.2.1", + "d3-interpolate": "^1.1.6", + "d3-path": "^1.0.8", + "d3-scale": "^1.0.7", + "d3-selection": "^1.2.0", + "d3-shape": "^1.2.0", + "d3-time": "^1.0.11", + "d3-time-format": "^2.1.3", + "debug": "^4.1.1", + "lodash.flattendeep": "^4.4.0", + "prop-types": "^15.7.2", + "save-svg-as-png": "^1.4.14" + }, + "peerDependencies": { + "react": "^16.0.0", + "react-dom": "^16.0.0" + } +} diff --git a/packages/react-financial-charts/src/BackgroundText.tsx b/packages/react-financial-charts/src/BackgroundText.tsx new file mode 100644 index 000000000..5e69b74c0 --- /dev/null +++ b/packages/react-financial-charts/src/BackgroundText.tsx @@ -0,0 +1,96 @@ +import * as PropTypes from "prop-types"; +import * as React from "react"; + +import { hexToRGBA, isDefined } from "./utils"; +import { PureComponent } from "./utils/PureComponent"; + +interface BackgroundTextProps { + x: number; + y: number; + fontFamily?: string; + fontSize?: number; + fill?: string; + stroke?: string; + opacity?: number; + strokeOpacity?: number; + textAnchor?: string; + children: (interval: number) => string; +} + +class BackgroundText extends PureComponent { + + public static defaultProps = { + opacity: 0.3, + fill: "#9E7523", + stroke: "#9E7523", + strokeOpacity: 1, + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 12, + textAnchor: "middle", + }; + + public static contextTypes = { + interval: PropTypes.string.isRequired, + getCanvasContexts: PropTypes.func, + chartCanvasType: PropTypes.string, + }; + + public componentDidMount() { + if (this.context.chartCanvasType !== "svg" && isDefined(this.context.getCanvasContexts)) { + const contexts = this.context.getCanvasContexts(); + if (contexts) { + this.drawOnCanvas(contexts.bg, this.props, this.context, this.props.children); + } + } + } + + public componentDidUpdate() { + this.componentDidMount(); + } + + public render() { + const { chartCanvasType, interval } = this.context; + + if (chartCanvasType !== "svg") { + return null; + } + + const { x, y, fill, opacity, stroke, strokeOpacity, fontFamily, fontSize, textAnchor } = this.props; + + const props = { x, y, fill, opacity, stroke, strokeOpacity, fontFamily, fontSize, textAnchor }; + + return ( + + {this.props.children(interval)} + + ); + } + + private readonly drawOnCanvas = (ctx, props, { interval }, getText) => { + ctx.clearRect(-1, -1, ctx.canvas.width + 2, ctx.canvas.height + 2); + ctx.save(); + + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.translate(0.5, 0.5); + + const { x, y, fill, opacity, stroke, strokeOpacity, fontFamily, fontSize, textAnchor } = props; + + const text = getText(interval); + + ctx.strokeStyle = hexToRGBA(stroke, strokeOpacity); + + ctx.font = `${fontSize}px ${fontFamily}`; + ctx.fillStyle = hexToRGBA(fill, opacity); + ctx.textAlign = textAnchor === "middle" ? "center" : textAnchor; + + if (stroke !== "none") { + ctx.strokeText(text, x, y); + } + + ctx.fillText(text, x, y); + + ctx.restore(); + } +} + +export default BackgroundText; diff --git a/packages/react-financial-charts/src/CanvasContainer.tsx b/packages/react-financial-charts/src/CanvasContainer.tsx new file mode 100644 index 000000000..c72dd8f37 --- /dev/null +++ b/packages/react-financial-charts/src/CanvasContainer.tsx @@ -0,0 +1,68 @@ +import * as React from "react"; + +import { isDefined } from "./utils"; + +interface CanvasContainerProps { + readonly width: number; + readonly height: number; + readonly type: string; + readonly zIndex?: number; + readonly ratio: number; +} + +class CanvasContainer extends React.Component { + + private drawCanvas: any; + + public constructor(props: CanvasContainerProps) { + super(props); + this.setDrawCanvas = this.setDrawCanvas.bind(this); + this.drawCanvas = {}; + } + + public setDrawCanvas(node) { + if (isDefined(node)) { + this.drawCanvas[node.id] = node.getContext("2d"); + } else { + this.drawCanvas = {}; + } + } + + public getCanvasContexts() { + if (isDefined(this.drawCanvas.axes)) { + return this.drawCanvas; + } + } + + public render() { + const { height, width, type, zIndex, ratio } = this.props; + if (type === "svg") { + return null; + } + + return ( +
+ + + +
+ ); + } +} + +export default CanvasContainer; diff --git a/packages/react-financial-charts/src/Chart.tsx b/packages/react-financial-charts/src/Chart.tsx new file mode 100644 index 000000000..dc8c67409 --- /dev/null +++ b/packages/react-financial-charts/src/Chart.tsx @@ -0,0 +1,109 @@ +import { scaleLinear } from "d3-scale"; +import * as PropTypes from "prop-types"; +import * as React from "react"; + +import { + find, + noop, +} from "./utils"; +import { PureComponent } from "./utils/PureComponent"; + +interface ChartProps { + height?: number; + origin?: number[] | any; // func + id: number | string; + yExtents?: number[] | any; // func + yExtentsCalculator?: any; // func + onContextMenu?: (props: any, event: React.MouseEvent) => void; + yScale?: any; // func + flipYScale?: boolean; + padding?: number | { top: number; bottom: number; }; +} + +export class Chart extends PureComponent { + + public static defaultProps = { + id: 0, + origin: [0, 0], + padding: 0, + yScale: scaleLinear(), + flipYScale: false, + yPan: true, + yPanEnabled: false, + onContextMenu: noop, + }; + + public static contextTypes = { + chartConfig: PropTypes.array, + subscribe: PropTypes.func.isRequired, + unsubscribe: PropTypes.func.isRequired, + }; + + public static childContextTypes = { + chartConfig: PropTypes.object.isRequired, + chartId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + }; + + constructor(props: ChartProps, context) { + super(props, context); + + this.yScale = this.yScale.bind(this); + this.listener = this.listener.bind(this); + } + + public componentDidMount() { + const { id } = this.props; + const { subscribe } = this.context; + + subscribe(`chart_${id}`, { + listener: this.listener, + }); + } + + public componentWillUnmount() { + const { id } = this.props; + const { unsubscribe } = this.context; + + unsubscribe(`chart_${id}`); + } + + public listener(type, moreProps, state, e) { + const { id, onContextMenu } = this.props; + + if (type === "contextmenu") { + const { currentCharts } = moreProps; + if (currentCharts.indexOf(id) > -1) { + if (onContextMenu !== undefined) { + onContextMenu(moreProps, e); + } + } + } + } + + public yScale() { + const chartConfig = find(this.context.chartConfig, (each) => each.id === this.props.id); + return chartConfig.yScale.copy(); + } + + public getChildContext() { + const { id: chartId } = this.props; + + const chartConfig = find(this.context.chartConfig, (each) => each.id === chartId); + + return { + chartId, + chartConfig, + }; + } + + public render() { + const { origin } = find(this.context.chartConfig, (each) => each.id === this.props.id); + const [x, y] = origin; + + return ( + + {this.props.children} + + ); + } +} diff --git a/packages/react-financial-charts/src/ChartCanvas.tsx b/packages/react-financial-charts/src/ChartCanvas.tsx new file mode 100644 index 000000000..89605c886 --- /dev/null +++ b/packages/react-financial-charts/src/ChartCanvas.tsx @@ -0,0 +1,1253 @@ +import { extent as d3Extent, max, min } from "d3-array"; +import * as PropTypes from "prop-types"; +import * as React from "react"; + +import { + clearCanvas, + functor, + head, + identity, + isDefined, + isNotDefined, + last, + noop, + shallowEqual, +} from "./utils"; + +import { + // @ts-ignore + lastVisibleItemBasedZoomAnchor, + mouseBasedZoomAnchor, + // @ts-ignore + rightDomainBasedZoomAnchor, +} from "./utils/zoomBehavior"; + +import { getChartConfigWithUpdatedYScales, getCurrentCharts, getCurrentItem, getNewChartConfig } from "./utils/ChartDataUtil"; + +import { EventCapture } from "./EventCapture"; + +import CanvasContainer from "./CanvasContainer"; +import evaluator from "./scale/evaluator"; + +const CANDIDATES_FOR_RESET = [ + "seriesName", +]; + +function shouldResetChart(thisProps, nextProps) { + return !CANDIDATES_FOR_RESET.every((key) => { + const result = shallowEqual(thisProps[key], nextProps[key]); + // console.log(key, result); + return result; + }); +} + +function getCursorStyle() { + const tooltipStyle = ` + .react-stockcharts-grabbing-cursor { + pointer-events: all; + cursor: -moz-grabbing; + cursor: -webkit-grabbing; + cursor: grabbing; + } + .react-stockcharts-crosshair-cursor { + pointer-events: all; + cursor: crosshair; + } + .react-stockcharts-tooltip-hover { + pointer-events: all; + cursor: pointer; + } + .react-stockcharts-avoid-interaction { + pointer-events: none; + } + .react-stockcharts-enable-interaction { + pointer-events: all; + } + .react-stockcharts-tooltip { + pointer-events: all; + cursor: pointer; + } + .react-stockcharts-default-cursor { + cursor: default; + } + .react-stockcharts-move-cursor { + cursor: move; + } + .react-stockcharts-pointer-cursor { + cursor: pointer; + } + .react-stockcharts-ns-resize-cursor { + cursor: ns-resize; + } + .react-stockcharts-ew-resize-cursor { + cursor: ew-resize; + }`; + return (); +} + +function getDimensions(props) { + return { + height: props.height - props.margin.top - props.margin.bottom, + width: props.width - props.margin.left - props.margin.right, + }; +} + +function getXScaleDirection(flipXScale) { + return flipXScale ? -1 : 1; +} + +function calculateFullData(props) { + const { data: fullData, plotFull, xScale, clamp, pointsPerPxThreshold, flipXScale } = props; + const { xAccessor, displayXAccessor, minPointsPerPxThreshold } = props; + + const useWholeData = isDefined(plotFull) + ? plotFull + : xAccessor === identity; + + const { filterData } = evaluator({ + xScale, + useWholeData, + clamp, + pointsPerPxThreshold, + minPointsPerPxThreshold, + flipXScale, + }); + + return { + xAccessor, + displayXAccessor: displayXAccessor || xAccessor, + xScale: xScale.copy(), + fullData, + filterData, + }; +} +function resetChart(props, firstCalculation = false) { + + const state = calculateState(props); + const { xAccessor, displayXAccessor, fullData } = state; + const { plotData: initialPlotData, xScale } = state; + const { postCalculator, children } = props; + + const plotData = postCalculator(initialPlotData); + + const dimensions = getDimensions(props); + + // @ts-ignore + const chartConfig = getChartConfigWithUpdatedYScales( + getNewChartConfig(dimensions, children), + { plotData, xAccessor, displayXAccessor, fullData }, + xScale.domain(), + ); + + return { + ...state, + xScale, + plotData, + chartConfig, + }; +} + +function updateChart( + newState, + initialXScale, + props, + lastItemWasVisible, + initialChartConfig, +) { + + const { fullData, xScale, xAccessor, displayXAccessor, filterData } = newState; + + const lastItem = last(fullData); + const [start, end] = initialXScale.domain(); + + const { postCalculator, children, padding, flipXScale } = props; + const { maintainPointsPerPixelOnResize } = props; + const direction = getXScaleDirection(flipXScale); + const dimensions = getDimensions(props); + + const updatedXScale = setXRange(xScale, dimensions, padding, direction); + + // console.log("lastItemWasVisible =", lastItemWasVisible, end, xAccessor(lastItem), end >= xAccessor(lastItem)); + let initialPlotData; + if (!lastItemWasVisible || end >= xAccessor(lastItem)) { + // resize comes here... + // get plotData between [start, end] and do not change the domain + const [rangeStart, rangeEnd] = initialXScale.range(); + const [newRangeStart, newRangeEnd] = updatedXScale.range(); + const newDomainExtent = ((newRangeEnd - newRangeStart) / (rangeEnd - rangeStart)) * (end - start); + const newStart = maintainPointsPerPixelOnResize + ? end - newDomainExtent + : start; + + const lastItemX = initialXScale(xAccessor(lastItem)); + // console.log("pointsPerPixel => ", newStart, start, end, updatedXScale(end)); + const response = filterData( + fullData, [newStart, end], xAccessor, updatedXScale, + { fallbackStart: start, fallbackEnd: { lastItem, lastItemX } }, + ); + initialPlotData = response.plotData; + updatedXScale.domain(response.domain); + // console.log("HERE!!!!!", start, end); + } else if (lastItemWasVisible + && end < xAccessor(lastItem)) { + // this is when a new item is added and last item was visible + // so slide over and show the new item also + + // get plotData between [xAccessor(l) - (end - start), xAccessor(l)] and DO change the domain + const dx = initialXScale(xAccessor(lastItem)) - initialXScale.range()[1]; + const [newStart, newEnd] = initialXScale.range().map((x) => x + dx).map(initialXScale.invert); + + const response = filterData(fullData, [newStart, newEnd], xAccessor, updatedXScale); + initialPlotData = response.plotData; + updatedXScale.domain(response.domain); // if last item was visible, then shift + } + // plotData = getDataOfLength(fullData, showingInterval, plotData.length) + const plotData = postCalculator(initialPlotData); + + // @ts-ignore + const chartConfig = getChartConfigWithUpdatedYScales( + getNewChartConfig(dimensions, children, initialChartConfig), + { plotData, xAccessor, displayXAccessor, fullData }, + updatedXScale.domain(), + ); + + return { + xScale: updatedXScale, + xAccessor, + chartConfig, + plotData, + fullData, + filterData, + }; +} + +function calculateState(props) { + + const { + xAccessor: inputXAccesor, xExtents: xExtentsProp, data, padding, flipXScale, + } = props; + + if (process.env.NODE_ENV !== "production" && isDefined(props.xScale.invert)) { + for (let i = 1; i < data.length; i++) { + const prev = data[i - 1]; + const curr = data[i]; + if (inputXAccesor(prev) > inputXAccesor(curr)) { + throw new Error("'data' is not sorted on 'xAccessor', send 'data' sorted in ascending order of 'xAccessor'"); + } + } + } + + const direction = getXScaleDirection(flipXScale); + const dimensions = getDimensions(props); + + const extent = typeof xExtentsProp === "function" + ? xExtentsProp(data) + : d3Extent(xExtentsProp.map((d) => functor(d)).map((each) => each(data, inputXAccesor))); + + const { xAccessor, displayXAccessor, xScale, fullData, filterData } = calculateFullData(props); + const updatedXScale = setXRange(xScale, dimensions, padding, direction); + + const { plotData, domain } = filterData(fullData, extent, inputXAccesor, updatedXScale); + + if (process.env.NODE_ENV !== "production" && plotData.length <= 1) { + throw new Error(`Showing ${plotData.length} datapoints, review the 'xExtents' prop of ChartCanvas`); + } + return { + plotData, + xScale: updatedXScale.domain(domain), + xAccessor, + displayXAccessor, + fullData, + filterData, + }; +} + +function setXRange(xScale, dimensions, padding, direction = 1) { + if (xScale.rangeRoundPoints) { + if (isNaN(padding)) { throw new Error("padding has to be a number for ordinal scale"); } + xScale.rangeRoundPoints([0, dimensions.width], padding); + } else if (xScale.padding) { + if (isNaN(padding)) { throw new Error("padding has to be a number for ordinal scale"); } + xScale.range([0, dimensions.width]); + xScale.padding(padding / 2); + } else { + const { left, right } = isNaN(padding) + ? padding + : { left: padding, right: padding }; + if (direction > 0) { + xScale.range([left, dimensions.width - right]); + } else { + xScale.range([dimensions.width - right, left]); + } + } + return xScale; +} + +function pinchCoordinates(pinch) { + const { touch1Pos, touch2Pos } = pinch; + + return { + topLeft: [Math.min(touch1Pos[0], touch2Pos[0]), Math.min(touch1Pos[1], touch2Pos[1])], + bottomRight: [Math.max(touch1Pos[0], touch2Pos[0]), Math.max(touch1Pos[1], touch2Pos[1])], + }; +} + +function isInteractionEnabled(xScale, xAccessor, data) { + const interaction = !isNaN(xScale(xAccessor(head(data)))) && isDefined(xScale.invert); + return interaction; +} + +interface ChartCanvasProps { + width: number; + height: number; + margin?: { + bottom: number; + left: number; + right: number; + top: number; + }; + ratio: number; + type: "svg" | "hybrid"; + pointsPerPxThreshold?: number; + minPointsPerPxThreshold?: number; + data: any[]; + xAccessor?: any; // func + xExtents?: any[] | any; // func + zoomAnchor?: any; // func + className?: string; + seriesName: string; + zIndex?: number; + xScale: any; // func + postCalculator?: any; // func + flipXScale?: boolean; + useCrossHairStyleCursor?: boolean; + padding?: number | { + bottom: number; + left: number; + right: number; + top: number; + }; + defaultFocus?: boolean; + zoomMultiplier?: number; + onLoadMore?: any; // func + displayXAccessor: any; // func + mouseMoveEvent?: boolean; + panEvent?: boolean; + clamp?: string | boolean | any; // func + zoomEvent?: boolean; + onSelect?: any; // func + maintainPointsPerPixelOnResize?: boolean; + disableInteraction?: boolean; +} + +interface ChartCanvasState { + xAccessor?: any; + displayXAccessor?: any; + filterData?: any; + chartConfig?: any; + plotData?: any; + xScale?: any; +} + +export class ChartCanvas extends React.Component { + + public static defaultProps = { + margin: { top: 20, right: 30, bottom: 30, left: 80 }, + type: "hybrid", + pointsPerPxThreshold: 2, + minPointsPerPxThreshold: 1 / 100, + className: "react-stockchart", + zIndex: 1, + xExtents: [min, max], + postCalculator: identity, + padding: 0, + xAccessor: identity, + flipXScale: false, + useCrossHairStyleCursor: true, + defaultFocus: true, + onLoadMore: noop, + onSelect: noop, + mouseMoveEvent: true, + panEvent: true, + zoomEvent: true, + zoomMultiplier: 1.1, + clamp: false, + zoomAnchor: mouseBasedZoomAnchor, + maintainPointsPerPixelOnResize: true, + disableInteraction: false, + }; + + public static childContextTypes = { + plotData: PropTypes.array, + fullData: PropTypes.array, + chartConfig: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + origin: PropTypes.arrayOf(PropTypes.number).isRequired, + padding: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.shape({ + top: PropTypes.number, + bottom: PropTypes.number, + }), + ]), + yExtents: PropTypes.arrayOf(PropTypes.func), + yExtentsProvider: PropTypes.func, + yScale: PropTypes.func.isRequired, + mouseCoordinates: PropTypes.shape({ + at: PropTypes.string, + format: PropTypes.func, + }), + width: PropTypes.number.isRequired, + height: PropTypes.number.isRequired, + }), + ).isRequired, + xScale: PropTypes.func.isRequired, + xAccessor: PropTypes.func.isRequired, + displayXAccessor: PropTypes.func.isRequired, + width: PropTypes.number.isRequired, + height: PropTypes.number.isRequired, + chartCanvasType: PropTypes.oneOf(["svg", "hybrid"]).isRequired, + margin: PropTypes.object.isRequired, + ratio: PropTypes.number.isRequired, + getCanvasContexts: PropTypes.func, + xAxisZoom: PropTypes.func, + yAxisZoom: PropTypes.func, + amIOnTop: PropTypes.func, + redraw: PropTypes.func, + subscribe: PropTypes.func, + unsubscribe: PropTypes.func, + setCursorClass: PropTypes.func, + generateSubscriptionId: PropTypes.func, + getMutableState: PropTypes.func, + }; + + public static ohlcv = (d) => ({ date: d.date, open: d.open, high: d.high, low: d.low, close: d.close, volume: d.volume }); + + private canvasContainerNode; + private eventCaptureNode; + private finalPinch?: boolean; + private fullData; + private lastSubscriptionId; + private mutableState; + private panInProgress: boolean; + private prevMouseXY; + private subscriptions; + private waitingForPinchZoomAnimationFrame?: boolean; + private waitingForPanAnimationFrame?: boolean; + private waitingForMouseMoveAnimationFrame?: boolean; + + // tslint:disable-next-line: variable-name + private hackyWayToStopPanBeyondBounds__plotData; + // tslint:disable-next-line: variable-name + private hackyWayToStopPanBeyondBounds__domain; + + constructor(props: ChartCanvasProps) { + super(props); + + this.getDataInfo = this.getDataInfo.bind(this); + this.getCanvasContexts = this.getCanvasContexts.bind(this); + + this.handleMouseMove = this.handleMouseMove.bind(this); + this.handleMouseEnter = this.handleMouseEnter.bind(this); + this.handleMouseLeave = this.handleMouseLeave.bind(this); + this.handleZoom = this.handleZoom.bind(this); + this.handlePinchZoom = this.handlePinchZoom.bind(this); + this.handlePinchZoomEnd = this.handlePinchZoomEnd.bind(this); + this.handlePan = this.handlePan.bind(this); + this.handlePanEnd = this.handlePanEnd.bind(this); + this.handleClick = this.handleClick.bind(this); + this.handleMouseDown = this.handleMouseDown.bind(this); + this.handleDoubleClick = this.handleDoubleClick.bind(this); + this.handleContextMenu = this.handleContextMenu.bind(this); + this.handleDragStart = this.handleDragStart.bind(this); + this.handleDrag = this.handleDrag.bind(this); + this.handleDragEnd = this.handleDragEnd.bind(this); + + this.panHelper = this.panHelper.bind(this); + this.pinchZoomHelper = this.pinchZoomHelper.bind(this); + this.xAxisZoom = this.xAxisZoom.bind(this); + this.yAxisZoom = this.yAxisZoom.bind(this); + this.resetYDomain = this.resetYDomain.bind(this); + this.calculateStateForDomain = this.calculateStateForDomain.bind(this); + this.generateSubscriptionId = this.generateSubscriptionId.bind(this); + this.draw = this.draw.bind(this); + this.redraw = this.redraw.bind(this); + this.getAllPanConditions = this.getAllPanConditions.bind(this); + + this.subscriptions = []; + this.subscribe = this.subscribe.bind(this); + this.unsubscribe = this.unsubscribe.bind(this); + this.amIOnTop = this.amIOnTop.bind(this); + this.saveEventCaptureNode = this.saveEventCaptureNode.bind(this); + this.saveCanvasContainerNode = this.saveCanvasContainerNode.bind(this); + this.setCursorClass = this.setCursorClass.bind(this); + this.getMutableState = this.getMutableState.bind(this); + + this.panInProgress = false; + this.state = {}; + this.mutableState = {}; + this.lastSubscriptionId = 0; + + const { fullData, ...state } = resetChart(props, true); + this.state = state; + this.fullData = fullData; + } + + public saveEventCaptureNode(node) { + this.eventCaptureNode = node; + } + + public saveCanvasContainerNode(node) { + this.canvasContainerNode = node; + } + + public getMutableState() { + return this.mutableState; + } + + public getDataInfo() { + return { + ...this.state, + fullData: this.fullData, + }; + } + public getCanvasContexts() { + if (this.canvasContainerNode) { + return this.canvasContainerNode.getCanvasContexts(); + } + } + public generateSubscriptionId() { + this.lastSubscriptionId++; + return this.lastSubscriptionId; + } + public clearBothCanvas() { + const canvases = this.getCanvasContexts(); + if (canvases && canvases.axes) { + clearCanvas([ + canvases.axes, + // canvases.hover, + canvases.mouseCoord, + ], this.props.ratio); + } + } + public clearMouseCanvas() { + const canvases = this.getCanvasContexts(); + if (canvases && canvases.mouseCoord) { + clearCanvas([ + canvases.mouseCoord, + // canvases.hover, + ], this.props.ratio); + } + } + public clearThreeCanvas() { + const canvases = this.getCanvasContexts(); + if (canvases && canvases.axes) { + clearCanvas([ + canvases.axes, + // canvases.hover, + canvases.mouseCoord, + canvases.bg, + ], this.props.ratio); + } + } + public subscribe(id, rest) { + const { getPanConditions = functor({ + draggable: false, + panEnabled: true, + }) } = rest; + this.subscriptions = this.subscriptions.concat({ + id, + ...rest, + getPanConditions, + }); + } + public unsubscribe(id) { + this.subscriptions = this.subscriptions.filter((each) => each.id !== id); + } + public getAllPanConditions() { + return this.subscriptions + .map((each) => each.getPanConditions()); + } + public setCursorClass(className) { + if (this.eventCaptureNode != null) { + this.eventCaptureNode.setCursorClass(className); + } + } + public amIOnTop(id) { + const dragableComponents = this.subscriptions + .filter((each) => each.getPanConditions().draggable); + + return dragableComponents.length > 0 + && last(dragableComponents).id === id; + } + public handleContextMenu(mouseXY, e) { + const { xAccessor, chartConfig, plotData, xScale } = this.state; + + const currentCharts = getCurrentCharts(chartConfig, mouseXY); + const currentItem = getCurrentItem(xScale, xAccessor, mouseXY, plotData); + + this.triggerEvent("contextmenu", { + mouseXY, + currentItem, + currentCharts, + }, e); + } + public calculateStateForDomain(newDomain) { + const { + xAccessor, + displayXAccessor, + xScale: initialXScale, + chartConfig: initialChartConfig, + plotData: initialPlotData, + } = this.state; + + const { filterData } = this.state; + const { fullData } = this; + const { postCalculator } = this.props; + + const { plotData: beforePlotData, domain } = filterData( + fullData, + newDomain, + xAccessor, + initialXScale, + { + currentPlotData: initialPlotData, + currentDomain: initialXScale.domain(), + }, + ); + + const plotData = postCalculator(beforePlotData); + const updatedScale = initialXScale.copy().domain(domain); + + // @ts-ignore + const chartConfig = getChartConfigWithUpdatedYScales( + initialChartConfig, + { plotData, xAccessor, displayXAccessor, fullData }, + updatedScale.domain(), + ); + + return { + xScale: updatedScale, + plotData, + chartConfig, + }; + } + public pinchZoomHelper(initialPinch, finalPinch) { + const { xScale: initialPinchXScale } = initialPinch; + + const { + xScale: initialXScale, + chartConfig: initialChartConfig, + plotData: initialPlotData, + xAccessor, + displayXAccessor, + } = this.state; + const { filterData } = this.state; + const { fullData } = this; + const { postCalculator } = this.props; + + const { topLeft: iTL, bottomRight: iBR } = pinchCoordinates(initialPinch); + const { topLeft: fTL, bottomRight: fBR } = pinchCoordinates(finalPinch); + + const e = initialPinchXScale.range()[1]; + + const xDash = Math.round(-(iBR[0] * fTL[0] - iTL[0] * fBR[0]) / (iTL[0] - iBR[0])); + const yDash = Math.round(e + ((e - iBR[0]) * (e - fTL[0]) - (e - iTL[0]) * (e - fBR[0])) / ((e - iTL[0]) - (e - iBR[0]))); + + const x = Math.round(-xDash * iTL[0] / (-xDash + fTL[0])); + const y = Math.round(e - (yDash - e) * (e - iTL[0]) / (yDash + (e - fTL[0]))); + + const newDomain = [x, y].map(initialPinchXScale.invert); + // var domainR = initial.right + right; + + const { plotData: beforePlotData, domain } = filterData( + fullData, + newDomain, + xAccessor, + initialPinchXScale, + { + currentPlotData: initialPlotData, + currentDomain: initialXScale.domain(), + }, + ); + + const plotData = postCalculator(beforePlotData); + const updatedScale = initialXScale.copy().domain(domain); + + const mouseXY = finalPinch.touch1Pos; + + // @ts-ignore + const chartConfig = getChartConfigWithUpdatedYScales( + initialChartConfig, + { plotData, xAccessor, displayXAccessor, fullData }, + updatedScale.domain(), + ); + const currentItem = getCurrentItem(updatedScale, xAccessor, mouseXY, plotData); + + return { + chartConfig, + xScale: updatedScale, + plotData, + mouseXY, + currentItem, + }; + } + + public cancelDrag() { + this.eventCaptureNode.cancelDrag(); + this.triggerEvent("dragcancel"); + } + + public handlePinchZoom(initialPinch, finalPinch, e) { + if (!this.waitingForPinchZoomAnimationFrame) { + this.waitingForPinchZoomAnimationFrame = true; + const state = this.pinchZoomHelper(initialPinch, finalPinch); + + this.triggerEvent("pinchzoom", state, e); + + this.finalPinch = finalPinch; + + requestAnimationFrame(() => { + this.clearBothCanvas(); + this.draw({ trigger: "pinchzoom" }); + this.waitingForPinchZoomAnimationFrame = false; + }); + } + } + public handlePinchZoomEnd(initialPinch, e) { + const { xAccessor } = this.state; + + if (this.finalPinch) { + const state = this.pinchZoomHelper(initialPinch, this.finalPinch); + const { xScale } = state; + this.triggerEvent("pinchzoom", state, e); + + this.finalPinch = undefined; + + this.clearThreeCanvas(); + + const { fullData } = this; + const firstItem = head(fullData); + + const start = head(xScale.domain()); + const end = xAccessor(firstItem); + const { onLoadMore } = this.props; + + this.setState(state, () => { + if (start < end) { + onLoadMore(start, end); + } + }); + } + } + public handleZoom(zoomDirection, mouseXY, e) { + if (this.panInProgress) { + return; + } + // console.log("zoomDirection ", zoomDirection, " mouseXY ", mouseXY); + const { xAccessor, xScale: initialXScale, plotData: initialPlotData } = this.state; + const { + zoomMultiplier = ChartCanvas.defaultProps.zoomMultiplier, + zoomAnchor, + } = this.props; + const { fullData } = this; + const item = zoomAnchor({ + xScale: initialXScale, + xAccessor, + mouseXY, + plotData: initialPlotData, + fullData, + }); + + const cx = initialXScale(item); + const c = zoomDirection > 0 ? 1 * zoomMultiplier : 1 / zoomMultiplier; + const newDomain = initialXScale.range().map((x) => cx + (x - cx) * c).map(initialXScale.invert); + + const { xScale, plotData, chartConfig } = this.calculateStateForDomain(newDomain); + + const currentItem = getCurrentItem(xScale, xAccessor, mouseXY, plotData); + const currentCharts = getCurrentCharts(chartConfig, mouseXY); + + this.clearThreeCanvas(); + + const firstItem = head(fullData); + + const start = head(xScale.domain()); + const end = xAccessor(firstItem); + const { onLoadMore } = this.props; + + this.mutableState = { + mouseXY, + currentItem, + currentCharts, + }; + + this.triggerEvent("zoom", { + xScale, + plotData, + chartConfig, + mouseXY, + currentCharts, + currentItem, + show: true, + }, e); + + this.setState({ + xScale, + plotData, + chartConfig, + }, () => { + if (start < end) { + onLoadMore(start, end); + } + }); + } + public xAxisZoom(newDomain) { + const { xScale, plotData, chartConfig } = this.calculateStateForDomain(newDomain); + this.clearThreeCanvas(); + + const { xAccessor } = this.state; + const { fullData } = this; + const firstItem = head(fullData); + const start = head(xScale.domain()); + const end = xAccessor(firstItem); + const { onLoadMore } = this.props; + + this.setState({ + xScale, + plotData, + chartConfig, + }, () => { + if (start < end) { onLoadMore(start, end); } + }); + } + public yAxisZoom(chartId, newDomain) { + this.clearThreeCanvas(); + const { chartConfig: initialChartConfig } = this.state; + const chartConfig = initialChartConfig + .map((each) => { + if (each.id === chartId) { + const { yScale } = each; + return { + ...each, + yScale: yScale.copy().domain(newDomain), + yPanEnabled: true, + }; + } else { + return each; + } + }); + + this.setState({ + chartConfig, + }); + } + + public triggerEvent(type, props?, e?) { + + this.subscriptions.forEach((each) => { + const state = { + ...this.state, + fullData: this.fullData, + subscriptions: this.subscriptions, + }; + each.listener(type, props, state, e); + }); + } + + public draw(props) { + this.subscriptions.forEach((each) => { + if (isDefined(each.draw)) { + each.draw(props); + } + }); + } + public redraw() { + this.clearThreeCanvas(); + this.draw({ force: true }); + } + public panHelper(mouseXY, initialXScale, { dx, dy }, chartsToPan) { + const { xAccessor, displayXAccessor, chartConfig: initialChartConfig } = this.state; + const { filterData } = this.state; + const { fullData } = this; + const { postCalculator } = this.props; + + // console.log(dx, dy); + if (isNotDefined(initialXScale.invert)) { + throw new Error("xScale provided does not have an invert() method." + + "You are likely using an ordinal scale. This scale does not support zoom, pan"); + } + + const newDomain = initialXScale.range().map((x) => x - dx).map(initialXScale.invert); + + const { plotData: beforePlotData, domain } = filterData( + fullData, + newDomain, + xAccessor, + initialXScale, + { + currentPlotData: this.hackyWayToStopPanBeyondBounds__plotData, + currentDomain: this.hackyWayToStopPanBeyondBounds__domain, + }, + ); + + const updatedScale = initialXScale.copy().domain(domain); + const plotData = postCalculator(beforePlotData); + // console.log(last(plotData)); + + const currentItem = getCurrentItem(updatedScale, xAccessor, mouseXY, plotData); + const chartConfig = getChartConfigWithUpdatedYScales( + initialChartConfig, + { plotData, xAccessor, displayXAccessor, fullData }, + updatedScale.domain(), + dy, + chartsToPan, + ); + const currentCharts = getCurrentCharts(chartConfig, mouseXY); + + // console.log(initialXScale.domain(), newDomain, updatedScale.domain()); + return { + xScale: updatedScale, + plotData, + chartConfig, + mouseXY, + currentCharts, + currentItem, + }; + } + public handlePan(mousePosition, panStartXScale, dxdy, chartsToPan, e) { + if (!this.waitingForPanAnimationFrame) { + this.waitingForPanAnimationFrame = true; + + this.hackyWayToStopPanBeyondBounds__plotData = this.hackyWayToStopPanBeyondBounds__plotData || this.state.plotData; + this.hackyWayToStopPanBeyondBounds__domain = this.hackyWayToStopPanBeyondBounds__domain || this.state.xScale.domain(); + + const state = this.panHelper(mousePosition, panStartXScale, dxdy, chartsToPan); + + this.hackyWayToStopPanBeyondBounds__plotData = state.plotData; + this.hackyWayToStopPanBeyondBounds__domain = state.xScale.domain(); + + this.panInProgress = true; + // console.log(panStartXScale.domain(), state.xScale.domain()); + + this.triggerEvent("pan", state, e); + + this.mutableState = { + mouseXY: state.mouseXY, + currentItem: state.currentItem, + currentCharts: state.currentCharts, + }; + requestAnimationFrame(() => { + this.waitingForPanAnimationFrame = false; + this.clearBothCanvas(); + this.draw({ trigger: "pan" }); + }); + } + } + public handlePanEnd(mousePosition, panStartXScale, dxdy, chartsToPan, e) { + const state = this.panHelper(mousePosition, panStartXScale, dxdy, chartsToPan); + // console.log(this.canvasDrawCallbackList.map(d => d.type)); + this.hackyWayToStopPanBeyondBounds__plotData = null; + this.hackyWayToStopPanBeyondBounds__domain = null; + + this.panInProgress = false; + + // console.log("PANEND", panEnd++); + const { + xScale, + plotData, + chartConfig, + } = state; + + this.triggerEvent("panend", state, e); + + requestAnimationFrame(() => { + const { xAccessor } = this.state; + const { fullData } = this; + + const firstItem = head(fullData); + const start = head(xScale.domain()); + const end = xAccessor(firstItem); + // console.log(start, end, start < end ? "Load more" : "I have it"); + + const { onLoadMore } = this.props; + + this.clearThreeCanvas(); + + this.setState({ + xScale, + plotData, + chartConfig, + }, () => { + if (start < end) { onLoadMore(start, end); } + }); + }); + } + public handleMouseDown(mousePosition, currentCharts, e) { + this.triggerEvent("mousedown", this.mutableState, e); + } + public handleMouseEnter(e) { + this.triggerEvent("mouseenter", { + show: true, + }, e); + } + public handleMouseMove(mouseXY, inputType, e) { + if (!this.waitingForMouseMoveAnimationFrame) { + this.waitingForMouseMoveAnimationFrame = true; + + const { chartConfig, plotData, xScale, xAccessor } = this.state; + const currentCharts = getCurrentCharts(chartConfig, mouseXY); + const currentItem = getCurrentItem(xScale, xAccessor, mouseXY, plotData); + this.triggerEvent("mousemove", { + show: true, + mouseXY, + // prevMouseXY is used in interactive components + prevMouseXY: this.prevMouseXY, + currentItem, + currentCharts, + }, e); + + this.prevMouseXY = mouseXY; + this.mutableState = { + mouseXY, + currentItem, + currentCharts, + }; + + requestAnimationFrame(() => { + this.clearMouseCanvas(); + this.draw({ trigger: "mousemove" }); + this.waitingForMouseMoveAnimationFrame = false; + }); + } + } + public handleMouseLeave(e) { + this.triggerEvent("mouseleave", { show: false }, e); + this.clearMouseCanvas(); + this.draw({ trigger: "mouseleave" }); + } + public handleDragStart({ startPos }, e) { + this.triggerEvent("dragstart", { startPos }, e); + } + public handleDrag({ startPos, mouseXY }, e) { + const { chartConfig, plotData, xScale, xAccessor } = this.state; + const currentCharts = getCurrentCharts(chartConfig, mouseXY); + const currentItem = getCurrentItem(xScale, xAccessor, mouseXY, plotData); + + this.triggerEvent("drag", { + startPos, + mouseXY, + currentItem, + currentCharts, + }, e); + + this.mutableState = { + mouseXY, + currentItem, + currentCharts, + }; + + requestAnimationFrame(() => { + this.clearMouseCanvas(); + this.draw({ trigger: "drag" }); + }); + } + public handleDragEnd({ mouseXY }, e) { + this.triggerEvent("dragend", { mouseXY }, e); + + requestAnimationFrame(() => { + this.clearMouseCanvas(); + this.draw({ trigger: "dragend" }); + }); + } + public handleClick(mousePosition, e) { + this.triggerEvent("click", this.mutableState, e); + + requestAnimationFrame(() => { + this.clearMouseCanvas(); + this.draw({ trigger: "click" }); + }); + } + public handleDoubleClick(mousePosition, e) { + this.triggerEvent("dblclick", {}, e); + } + public getChildContext() { + const dimensions = getDimensions(this.props); + return { + fullData: this.fullData, + plotData: this.state.plotData, + width: dimensions.width, + height: dimensions.height, + chartConfig: this.state.chartConfig, + xScale: this.state.xScale, + xAccessor: this.state.xAccessor, + displayXAccessor: this.state.displayXAccessor, + chartCanvasType: this.props.type, + margin: this.props.margin, + ratio: this.props.ratio, + xAxisZoom: this.xAxisZoom, + yAxisZoom: this.yAxisZoom, + getCanvasContexts: this.getCanvasContexts, + redraw: this.redraw, + subscribe: this.subscribe, + unsubscribe: this.unsubscribe, + generateSubscriptionId: this.generateSubscriptionId, + getMutableState: this.getMutableState, + amIOnTop: this.amIOnTop, + setCursorClass: this.setCursorClass, + }; + } + + public UNSAFE_componentWillReceiveProps(nextProps) { + const reset = shouldResetChart(this.props, nextProps); + + const interaction = isInteractionEnabled(this.state.xScale, this.state.xAccessor, this.state.plotData); + const { chartConfig: initialChartConfig } = this.state; + + let newState; + if (!interaction || reset || !shallowEqual(this.props.xExtents, nextProps.xExtents)) { + // do reset + newState = resetChart(nextProps); + this.mutableState = {}; + } else { + + const [start, end] = this.state.xScale.domain(); + const prevLastItem = last(this.fullData); + + const calculatedState = calculateFullData(nextProps); + const { xAccessor } = calculatedState; + const lastItemWasVisible = xAccessor(prevLastItem) <= end && xAccessor(prevLastItem) >= start; + + newState = updateChart( + calculatedState, + this.state.xScale, + nextProps, + lastItemWasVisible, + initialChartConfig, + ); + } + + const { fullData, ...state } = newState; + + if (!this.panInProgress) { + this.clearThreeCanvas(); + + this.setState(state); + } + this.fullData = fullData; + } + + public resetYDomain(chartId) { + const { chartConfig } = this.state; + let changed = false; + const newChartConfig = chartConfig + .map((each) => { + if ((isNotDefined(chartId) || each.id === chartId) + && !shallowEqual(each.yScale.domain(), each.realYDomain)) { + changed = true; + return { + ...each, + yScale: each.yScale.domain(each.realYDomain), + yPanEnabled: false, + }; + } + return each; + }); + + if (changed) { + this.clearThreeCanvas(); + this.setState({ + chartConfig: newChartConfig, + }); + } + } + + public shouldComponentUpdate() { + return !this.panInProgress; + } + + public render() { + + const { + type, + height, + width, + margin = ChartCanvas.defaultProps.margin, + className, + zIndex = ChartCanvas.defaultProps.zIndex, + defaultFocus, ratio, mouseMoveEvent, panEvent, zoomEvent } = this.props; + const { useCrossHairStyleCursor, onSelect } = this.props; + + const { plotData, xScale, xAccessor, chartConfig } = this.state; + const dimensions = getDimensions(this.props); + + const interaction = isInteractionEnabled(xScale, xAccessor, plotData); + + const cursorStyle = useCrossHairStyleCursor && interaction; + const cursor = getCursorStyle(); + return ( +
+ + + {cursor} + + + + + {chartConfig + .map((each, idx) => + + )} + + + + + + {this.props.children} + + + +
+ ); + } +} diff --git a/packages/react-financial-charts/src/EventCapture.tsx b/packages/react-financial-charts/src/EventCapture.tsx new file mode 100644 index 000000000..d196e60bd --- /dev/null +++ b/packages/react-financial-charts/src/EventCapture.tsx @@ -0,0 +1,644 @@ +import { event as d3Event, mouse, select, touches } from "d3-selection"; +import * as React from "react"; + +import { + d3Window, getTouchProps, isDefined, MOUSEENTER, + MOUSELEAVE, + MOUSEMOVE, mousePosition, + MOUSEUP, noop, + TOUCHEND, TOUCHMOVE, + touchPosition, +} from "./utils"; +import { getCurrentCharts } from "./utils/ChartDataUtil"; + +interface EventCaptureProps { + mouseMove: boolean; + zoom: boolean; + pan: boolean; + panSpeedMultiplier: number; + focus: boolean; + useCrossHairStyleCursor?: boolean; + width: number; + height: number; + chartConfig: Array<{ origin: number[], height: number }>; + xAccessor: any; // func + xScale: any; // func + disableInteraction: boolean; + getAllPanConditions: () => Array<{ panEnabled: boolean, draggable: boolean }>; + onMouseMove?: (touchXY: number[], eventType: string, event: React.TouchEvent) => void; + onMouseEnter?: (event: any) => void; + onMouseLeave?: (event: React.MouseEvent) => void; + onZoom?: (zoomDir: 1 | -1, mouseXY: number[], event: React.WheelEvent) => void; + onPinchZoom?: any; // func + onPinchZoomEnd: any; // func + onPan?: any; // func + onPanEnd?: any; // func + onDragStart?: (details: { startPos: number[] }, event: React.MouseEvent) => void; + onDrag?: (details: { startPos: number[], mouseXY: number[] }, event: React.MouseEvent) => void; + onDragComplete?: (details: { mouseXY: number[] }, event: React.MouseEvent) => void; + onClick?: (mouseXY: number[], event: React.MouseEvent) => void; + onDoubleClick?: (mouseXY: number[], event: React.MouseEvent) => void; + onContextMenu?: (mouseXY: number[], event: React.MouseEvent) => void; + onMouseDown?: (mouseXY: number[], currentCharts: any, event: React.MouseEvent) => void; +} + +interface EventCaptureState { + cursorOverrideClass?: string; + dragInProgress?: boolean; + dragStartPosition?: any; + panInProgress: boolean; + panStart?: any; + pinchZoomStart?: any; +} + +export class EventCapture extends React.Component { + + public static defaultProps = { + mouseMove: false, + zoom: false, + pan: false, + panSpeedMultiplier: 1, + focus: false, + onDragComplete: noop, + disableInteraction: false, + }; + + private clicked?: boolean; + private dx: number = 0; + private dy: number = 0; + private dragHappened?: boolean; + private focus?: boolean; + private lastNewPos?; + private mouseInside: boolean; + private mouseInteraction: boolean; + private node; + private panEndTimeout?; + private panHappened?: boolean; + + constructor(props: EventCaptureProps) { + super(props); + + this.handleEnter = this.handleEnter.bind(this); + this.handleLeave = this.handleLeave.bind(this); + this.handleWheel = this.handleWheel.bind(this); + this.handleMouseMove = this.handleMouseMove.bind(this); + this.handleMouseDown = this.handleMouseDown.bind(this); + this.handlePanEnd = this.handlePanEnd.bind(this); + this.handlePan = this.handlePan.bind(this); + this.handleTouchStart = this.handleTouchStart.bind(this); + this.handleTouchMove = this.handleTouchMove.bind(this); + this.handlePinchZoom = this.handlePinchZoom.bind(this); + this.handlePinchZoomEnd = this.handlePinchZoomEnd.bind(this); + this.handleClick = this.handleClick.bind(this); + this.handleRightClick = this.handleRightClick.bind(this); + this.handleDrag = this.handleDrag.bind(this); + this.handleDragEnd = this.handleDragEnd.bind(this); + this.shouldPan = this.shouldPan.bind(this); + this.canPan = this.canPan.bind(this); + this.setCursorClass = this.setCursorClass.bind(this); + this.saveNode = this.saveNode.bind(this); + this.mouseInside = false; + this.mouseInteraction = true; + this.focus = props.focus; + this.state = { + panInProgress: false, + }; + } + + public saveNode(node) { + this.node = node; + } + + public componentDidMount() { + if (this.node) { + select(this.node) + .on(MOUSEENTER, this.handleEnter) + .on(MOUSELEAVE, this.handleLeave); + } + } + + public componentDidUpdate() { + this.componentDidMount(); + } + + public componentWillUnmount() { + if (this.node) { + select(this.node) + .on(MOUSEENTER, null) + .on(MOUSELEAVE, null); + const win = d3Window(this.node); + select(win) + .on(MOUSEMOVE, null); + } + } + + public handleEnter() { + const { onMouseEnter } = this.props; + if (onMouseEnter === undefined) { + return; + } + + const e = d3Event; + this.mouseInside = true; + if (!this.state.panInProgress + && !this.state.dragInProgress) { + const win = d3Window(this.node); + select(win) + .on(MOUSEMOVE, this.handleMouseMove); + } + onMouseEnter(e); + } + + public handleLeave(e: React.MouseEvent) { + const { onMouseLeave } = this.props; + if (onMouseLeave === undefined) { + return; + } + + this.mouseInside = false; + if (!this.state.panInProgress + && !this.state.dragInProgress) { + const win = d3Window(this.node); + select(win) + .on(MOUSEMOVE, null); + } + onMouseLeave(e); + } + + public handleWheel(e: React.WheelEvent) { + const { zoom, onZoom } = this.props; + const { panInProgress } = this.state; + + const yZoom = Math.abs(e.deltaY) > Math.abs(e.deltaX) && Math.abs(e.deltaY) > 0; + const mouseXY = mousePosition(e); + e.preventDefault(); + + if (zoom && this.focus && yZoom && !panInProgress) { + const zoomDir = e.deltaY > 0 ? 1 : -1; + + if (onZoom !== undefined) { + onZoom(zoomDir, mouseXY, e); + } + } else if (this.focus) { + if (this.shouldPan()) { + // pan already in progress + const { + panStartXScale, + chartsToPan, + } = this.state.panStart; + this.lastNewPos = mouseXY; + this.panHappened = true; + + this.dx += e.deltaX; + this.dy += e.deltaY; + const dxdy = { dx: this.dx, dy: this.dy }; + + this.props.onPan(mouseXY, panStartXScale, dxdy, chartsToPan, e); + } else { + const { xScale, chartConfig } = this.props; + const currentCharts = getCurrentCharts(chartConfig, mouseXY); + + this.dx = 0; + this.dy = 0; + this.setState({ + panInProgress: true, + panStart: { + panStartXScale: xScale, + panOrigin: mouseXY, + chartsToPan: currentCharts, + }, + }); + } + this.queuePanEnd(); + } + } + + public queuePanEnd() { + if (isDefined(this.panEndTimeout)) { + clearTimeout(this.panEndTimeout); + } + this.panEndTimeout = setTimeout(() => { + this.handlePanEnd(); + }, 100); + } + + public handleMouseMove() { + const e = d3Event; + + const { onMouseMove, mouseMove } = this.props; + + if (this.mouseInteraction && + mouseMove && + !this.state.panInProgress) { + + const newPos = mouse(this.node); + + if (onMouseMove !== undefined) { + onMouseMove(newPos, "mouse", e); + } + } + } + + public handleClick(e: React.MouseEvent) { + const mouseXY = mousePosition(e); + const { onClick, onDoubleClick } = this.props; + + if (!this.panHappened && !this.dragHappened) { + if (this.clicked && onDoubleClick !== undefined) { + onDoubleClick(mouseXY, e); + this.clicked = false; + } else if (onClick !== undefined) { + onClick(mouseXY, e); + this.clicked = true; + setTimeout(() => { + if (this.clicked) { + this.clicked = false; + } + }, 400); + } + } + } + + public handleRightClick(e: React.MouseEvent) { + e.stopPropagation(); + e.preventDefault(); + const { onContextMenu, onPanEnd } = this.props; + + const mouseXY = mousePosition(e, this.node.getBoundingClientRect()); + + if (isDefined(this.state.panStart)) { + const { panStartXScale, panOrigin, chartsToPan } = this.state.panStart; + if (this.panHappened) { + onPanEnd(mouseXY, panStartXScale, panOrigin, chartsToPan, e); + } + const win = d3Window(this.node); + select(win) + .on(MOUSEMOVE, null) + .on(MOUSEUP, null); + + this.setState({ + panInProgress: false, + panStart: null, + }); + } + + if (onContextMenu !== undefined) { + onContextMenu(mouseXY, e); + } + } + + public handleDrag() { + const e = d3Event; + if (this.props.onDrag) { + this.dragHappened = true; + const mouseXY = mouse(this.node); + this.props.onDrag({ + startPos: this.state.dragStartPosition, + mouseXY, + }, e); + } + } + + public cancelDrag() { + const win = d3Window(this.node); + select(win) + .on(MOUSEMOVE, this.mouseInside ? this.handleMouseMove : null) + .on(MOUSEUP, null); + + this.setState({ + dragInProgress: false, + }); + this.mouseInteraction = true; + } + + public handleDragEnd() { + const e = d3Event; + const mouseXY = mouse(this.node); + + const win = d3Window(this.node); + select(win) + .on(MOUSEMOVE, this.mouseInside ? this.handleMouseMove : null) + .on(MOUSEUP, null); + + if (this.dragHappened) { + const { onDragComplete } = this.props; + if (onDragComplete !== undefined) { + onDragComplete({ mouseXY }, e); + } + } + + this.setState({ + dragInProgress: false, + }); + this.mouseInteraction = true; + } + + public canPan() { + const { getAllPanConditions } = this.props; + const { pan: initialPanEnabled } = this.props; + + const { + panEnabled, + draggable: somethingSelected, + } = getAllPanConditions() + .reduce((returnObj, a) => { + return { + draggable: returnObj.draggable || a.draggable, + panEnabled: returnObj.panEnabled && a.panEnabled, + }; + }, { + draggable: false, + panEnabled: initialPanEnabled, + }); + + return { + panEnabled, + somethingSelected, + }; + } + + public handleMouseDown(e: React.MouseEvent) { + if (e.button !== 0) { + return; + } + const { xScale, chartConfig, onMouseDown } = this.props; + + this.panHappened = false; + this.dragHappened = false; + this.focus = true; + + if (!this.state.panInProgress + && this.mouseInteraction + ) { + + const mouseXY = mousePosition(e); + const currentCharts = getCurrentCharts(chartConfig, mouseXY); + const { + panEnabled, somethingSelected, + } = this.canPan(); + const pan = panEnabled && !somethingSelected; + + if (pan) { + this.setState({ + panInProgress: pan, + panStart: { + panStartXScale: xScale, + panOrigin: mouseXY, + chartsToPan: currentCharts, + }, + }); + + const win = d3Window(this.node); + select(win) + .on(MOUSEMOVE, this.handlePan) + .on(MOUSEUP, this.handlePanEnd); + + } else if (somethingSelected) { + this.setState({ + panInProgress: false, + dragInProgress: true, + panStart: null, + dragStartPosition: mouseXY, + }); + + const { onDragStart } = this.props; + + if (onDragStart !== undefined) { + onDragStart({ startPos: mouseXY }, e); + } + + const win = d3Window(this.node); + select(win) + .on(MOUSEMOVE, this.handleDrag) + .on(MOUSEUP, this.handleDragEnd); + } + + if (onMouseDown !== undefined) { + onMouseDown(mouseXY, currentCharts, e); + } + } + e.preventDefault(); + } + + public shouldPan() { + const { pan: panEnabled, onPan } = this.props; + return panEnabled + && onPan + && isDefined(this.state.panStart); + } + + public handlePan() { + const e = d3Event; + + if (this.shouldPan()) { + this.panHappened = true; + + const { panStartXScale, panOrigin, chartsToPan } = this.state.panStart; + + const mouseXY = this.mouseInteraction + ? mouse(this.node) + : touches(this.node)[0]; + + this.lastNewPos = mouseXY; + const dx = mouseXY[0] - panOrigin[0]; + const dy = mouseXY[1] - panOrigin[1]; + + this.dx = dx; + this.dy = dy; + + this.props.onPan( + mouseXY, panStartXScale, { dx, dy }, chartsToPan, e, + ); + } + } + + public handlePanEnd() { + const e = d3Event; + const { pan: panEnabled, onPanEnd } = this.props; + + if (isDefined(this.state.panStart)) { + const { panStartXScale, chartsToPan } = this.state.panStart; + + const win = d3Window(this.node); + select(win) + .on(MOUSEMOVE, this.mouseInside ? this.handleMouseMove : null) + .on(MOUSEUP, null) + .on(TOUCHMOVE, null) + .on(TOUCHEND, null); + + if (this.panHappened + // && !this.contextMenuClicked + && panEnabled + && onPanEnd) { + const { dx, dy } = this; + + delete this.dx; + delete this.dy; + onPanEnd(this.lastNewPos, panStartXScale, { dx, dy }, chartsToPan, e); + } + + this.setState({ + panInProgress: false, + panStart: null, + }); + } + } + + public handleTouchMove(e: React.TouchEvent) { + const { onMouseMove } = this.props; + if (onMouseMove === undefined) { + return; + } + + const touchXY = touchPosition(getTouchProps(e.touches[0]), e); + onMouseMove(touchXY, "touch", e); + } + + public handleTouchStart(e: React.TouchEvent) { + this.mouseInteraction = false; + + const { pan: panEnabled, chartConfig, onMouseMove } = this.props; + const { xScale, onPanEnd } = this.props; + + if (e.touches.length === 1) { + + this.panHappened = false; + const touchXY = touchPosition(getTouchProps(e.touches[0]), e); + if (onMouseMove !== undefined) { + onMouseMove(touchXY, "touch", e); + } + + if (panEnabled) { + const currentCharts = getCurrentCharts(chartConfig, touchXY); + + this.setState({ + panInProgress: true, + panStart: { + panStartXScale: xScale, + panOrigin: touchXY, + chartsToPan: currentCharts, + }, + }); + + const win = d3Window(this.node); + select(win) + .on(TOUCHMOVE, this.handlePan, false) + .on(TOUCHEND, this.handlePanEnd, false); + + } + } else if (e.touches.length === 2) { + // pinch zoom begin + // do nothing pinch zoom is handled in handleTouchMove + const { panInProgress, panStart } = this.state; + + if (panInProgress && panEnabled && onPanEnd) { + const { panStartXScale, panOrigin, chartsToPan } = panStart; + + const win = d3Window(this.node); + select(win) + .on(MOUSEMOVE, this.mouseInside ? this.handleMouseMove : null) + .on(MOUSEUP, null) + .on(TOUCHMOVE, this.handlePinchZoom, false) + .on(TOUCHEND, this.handlePinchZoomEnd, false); + + const touch1Pos = touchPosition(getTouchProps(e.touches[0]), e); + const touch2Pos = touchPosition(getTouchProps(e.touches[1]), e); + + if (this.panHappened + // && !this.contextMenuClicked + && panEnabled + && onPanEnd) { + + onPanEnd(this.lastNewPos, panStartXScale, panOrigin, chartsToPan, e); + } + + this.setState({ + panInProgress: false, + pinchZoomStart: { + xScale, + touch1Pos, + touch2Pos, + range: xScale.range(), + chartsToPan, + }, + }); + } + } + } + + public handlePinchZoom() { + const e = d3Event; + const [touch1Pos, touch2Pos] = touches(this.node); + const { xScale, zoom: zoomEnabled, onPinchZoom } = this.props; + + // eslint-disable-next-line no-unused-vars + const { chartsToPan, ...initialPinch } = this.state.pinchZoomStart; + + if (zoomEnabled && onPinchZoom) { + onPinchZoom(initialPinch, { + touch1Pos, + touch2Pos, + xScale, + }, e); + } + } + + public handlePinchZoomEnd() { + const e = d3Event; + + const win = d3Window(this.node); + select(win) + .on(TOUCHMOVE, null) + .on(TOUCHEND, null); + + const { zoom: zoomEnabled, onPinchZoomEnd } = this.props; + + // eslint-disable-next-line no-unused-vars + const { chartsToPan, ...initialPinch } = this.state.pinchZoomStart; + + if (zoomEnabled && onPinchZoomEnd) { + onPinchZoomEnd(initialPinch, e); + } + + this.setState({ + pinchZoomStart: undefined, + }); + } + + public setCursorClass(cursorOverrideClass) { + if (cursorOverrideClass !== this.state.cursorOverrideClass) { + this.setState({ + cursorOverrideClass, + }); + } + } + + public render() { + const { height, width, disableInteraction, useCrossHairStyleCursor } = this.props; + const className = this.state.cursorOverrideClass !== undefined + ? this.state.cursorOverrideClass + : !useCrossHairStyleCursor ? "" : this.state.panInProgress + ? "react-stockcharts-grabbing-cursor" + : "react-stockcharts-crosshair-cursor"; + + const interactionProps = disableInteraction || { + onWheel: this.handleWheel, + onMouseDown: this.handleMouseDown, + onClick: this.handleClick, + onContextMenu: this.handleRightClick, + onTouchStart: this.handleTouchStart, + onTouchMove: this.handleTouchMove, + }; + + return ( + + ); + } +} diff --git a/packages/react-financial-charts/src/GenericChartComponent.tsx b/packages/react-financial-charts/src/GenericChartComponent.tsx new file mode 100644 index 000000000..3426083d4 --- /dev/null +++ b/packages/react-financial-charts/src/GenericChartComponent.tsx @@ -0,0 +1,140 @@ +import * as PropTypes from "prop-types"; + +import GenericComponent from "./GenericComponent"; +import { + find, + isDefined, +} from "./utils"; + +const ALWAYS_TRUE_TYPES = [ + "drag", + "dragend", +]; + +class GenericChartComponent extends GenericComponent { + + public static defaultProps = GenericComponent.defaultProps; + + public static contextTypes = { + width: PropTypes.number.isRequired, + height: PropTypes.number.isRequired, + margin: PropTypes.object.isRequired, + chartId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + getCanvasContexts: PropTypes.func, + chartCanvasType: PropTypes.string, + xScale: PropTypes.func.isRequired, + xAccessor: PropTypes.func.isRequired, + displayXAccessor: PropTypes.func.isRequired, + plotData: PropTypes.array.isRequired, + fullData: PropTypes.array.isRequired, + chartConfig: PropTypes.oneOfType([ + PropTypes.array, + PropTypes.object, + ]).isRequired, + morePropsDecorator: PropTypes.func, + generateSubscriptionId: PropTypes.func, + getMutableState: PropTypes.func.isRequired, + amIOnTop: PropTypes.func.isRequired, + subscribe: PropTypes.func.isRequired, + unsubscribe: PropTypes.func.isRequired, + setCursorClass: PropTypes.func.isRequired, + canvasOriginX: PropTypes.number, + canvasOriginY: PropTypes.number, + ratio: PropTypes.number.isRequired, + }; + + public constructor(props, context) { + super(props, context); + + this.preCanvasDraw = this.preCanvasDraw.bind(this); + this.postCanvasDraw = this.postCanvasDraw.bind(this); + this.shouldTypeProceed = this.shouldTypeProceed.bind(this); + this.preEvaluate = this.preEvaluate.bind(this); + } + + public preCanvasDraw(ctx, moreProps) { + + super.preCanvasDraw(ctx, moreProps); + + ctx.save(); + const { margin, ratio } = this.context; + const { chartConfig } = moreProps; + + const canvasOriginX = (0.5 * ratio) + chartConfig.origin[0] + margin.left; + const canvasOriginY = (0.5 * ratio) + chartConfig.origin[1] + margin.top; + + const { chartConfig: { width, height } } = moreProps; + const { clip, edgeClip } = this.props; + + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.scale(ratio, ratio); + if (edgeClip) { + ctx.beginPath(); + ctx.rect(-1, canvasOriginY - 10, width + margin.left + margin.right + 1, height + 20); + ctx.clip(); + } + + ctx.translate(canvasOriginX, canvasOriginY); + + if (clip) { + ctx.beginPath(); + ctx.rect(-1, -1, width + 1, height + 1); + ctx.clip(); + } + } + + public postCanvasDraw(ctx, moreProps) { + super.postCanvasDraw(ctx, moreProps); + ctx.restore(); + } + + public updateMoreProps(moreProps) { + super.updateMoreProps(moreProps); + const { chartConfig: chartConfigList } = moreProps; + + if (chartConfigList && Array.isArray(chartConfigList)) { + const { chartId } = this.context; + const chartConfig = find(chartConfigList, (each) => each.id === chartId); + this.moreProps.chartConfig = chartConfig; + } + if (isDefined(this.moreProps.chartConfig)) { + const { origin: [ox, oy] } = this.moreProps.chartConfig; + if (isDefined(moreProps.mouseXY)) { + const { mouseXY: [x, y] } = moreProps; + this.moreProps.mouseXY = [ + x - ox, + y - oy, + ]; + } + if (isDefined(moreProps.startPos)) { + const { startPos: [x, y] } = moreProps; + this.moreProps.startPos = [ + x - ox, + y - oy, + ]; + } + } + } + + public preEvaluate(/* type, moreProps */) { + /// + } + + public shouldTypeProceed(type, moreProps) { + if ( + (type === "mousemove" || type === "click") + && this.props.disablePan) { + return true; + } + if ( + ALWAYS_TRUE_TYPES.indexOf(type) === -1 + && isDefined(moreProps) + && isDefined(moreProps.currentCharts) + ) { + return (moreProps.currentCharts.indexOf(this.context.chartId) > -1); + } + return true; + } +} + +export default GenericChartComponent; diff --git a/packages/react-financial-charts/src/GenericComponent.tsx b/packages/react-financial-charts/src/GenericComponent.tsx new file mode 100644 index 000000000..0c29ad4f6 --- /dev/null +++ b/packages/react-financial-charts/src/GenericComponent.tsx @@ -0,0 +1,505 @@ +import * as PropTypes from "prop-types"; +import * as React from "react"; + +import { + functor, + identity, + isDefined, + isNotDefined, + noop, +} from "./utils"; + +const aliases = { + mouseleave: "mousemove", // to draw interactive after mouse exit + panend: "pan", + pinchzoom: "pan", + mousedown: "mousemove", + click: "mousemove", + contextmenu: "mousemove", + dblclick: "mousemove", + dragstart: "drag", + dragend: "drag", + dragcancel: "drag", +}; + +interface GenericComponentProps { + svgDraw: any; // func + canvasDraw?: any; // func + drawOn: any[]; + clip?: boolean; + edgeClip?: boolean; + interactiveCursorClass?: string; + selected?: boolean; + enableDragOnHover?: boolean; + disablePan?: boolean; + canvasToDraw: any; // func + isHover?: any; // func + onClick?: any; // func + onClickWhenHover?: any; // func + onClickOutside?: any; // func + onPan?: any; // func + onPanEnd?: any; // func + onDragStart?: any; // func + onDrag?: any; // func + onDragComplete?: any; // func + onDoubleClick?: any; // func + onDoubleClickWhenHover?: any; // func + onContextMenu?: any; // func + onContextMenuWhenHover?: any; // func + onMouseMove?: any; // func + onMouseDown?: any; // func + onHover?: any; // func + onUnHover?: any; // func + debug?: any; // func +} + +interface GenericComponentState { + updateCount: number; +} + +class GenericComponent extends React.Component { + + public static defaultProps = { + svgDraw: functor(null), + draw: [], + canvasToDraw: (contexts) => contexts.mouseCoord, + clip: true, + edgeClip: false, + selected: false, + disablePan: false, + enableDragOnHover: false, + onClickWhenHover: noop, + onClickOutside: noop, + onDragStart: noop, + onMouseMove: noop, + onMouseDown: noop, + debug: noop, + }; + + public static contextTypes = { + width: PropTypes.number.isRequired, + height: PropTypes.number.isRequired, + margin: PropTypes.object.isRequired, + chartId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + getCanvasContexts: PropTypes.func, + chartCanvasType: PropTypes.string, + xScale: PropTypes.func.isRequired, + xAccessor: PropTypes.func.isRequired, + displayXAccessor: PropTypes.func.isRequired, + plotData: PropTypes.array.isRequired, + fullData: PropTypes.array.isRequired, + chartConfig: PropTypes.oneOfType([ + PropTypes.array, + PropTypes.object, + ]).isRequired, + morePropsDecorator: PropTypes.func, + generateSubscriptionId: PropTypes.func, + getMutableState: PropTypes.func.isRequired, + amIOnTop: PropTypes.func.isRequired, + subscribe: PropTypes.func.isRequired, + unsubscribe: PropTypes.func.isRequired, + setCursorClass: PropTypes.func.isRequired, + }; + + public moreProps; + + private dragInProgress; + private evaluationInProgress; + private iSetTheCursorClass; + private suscriberId; + + public constructor(props, context) { + super(props, context); + this.drawOnCanvas = this.drawOnCanvas.bind(this); + this.getMoreProps = this.getMoreProps.bind(this); + this.listener = this.listener.bind(this); + this.draw = this.draw.bind(this); + this.updateMoreProps = this.updateMoreProps.bind(this); + this.evaluateType = this.evaluateType.bind(this); + this.isHover = this.isHover.bind(this); + this.preCanvasDraw = this.preCanvasDraw.bind(this); + this.postCanvasDraw = this.postCanvasDraw.bind(this); + this.getPanConditions = this.getPanConditions.bind(this); + this.shouldTypeProceed = this.shouldTypeProceed.bind(this); + this.preEvaluate = this.preEvaluate.bind(this); + + const { generateSubscriptionId } = context; + this.suscriberId = generateSubscriptionId(); + + this.moreProps = {}; + + this.state = { + updateCount: 0, + }; + } + + public updateMoreProps(moreProps) { + Object.keys(moreProps).forEach((key) => { + this.moreProps[key] = moreProps[key]; + }); + } + + public shouldTypeProceed(type, moreProps) { + return true; + } + + public preEvaluate() { + /// empty + } + + public listener(type, moreProps, state, e) { + if (isDefined(moreProps)) { + this.updateMoreProps(moreProps); + } + this.evaluationInProgress = true; + this.evaluateType(type, e); + this.evaluationInProgress = false; + } + + public evaluateType(type, e) { + const newType = aliases[type] || type; + const proceed = this.props.drawOn.indexOf(newType) > -1; + + if (!proceed) { + return; + } + + // @ts-ignore + this.preEvaluate(type, this.moreProps, e); + + if (!this.shouldTypeProceed(type, this.moreProps)) { + return; + } + + switch (type) { + case "zoom": + case "mouseenter": + // DO NOT DRAW FOR THESE EVENTS + break; + case "mouseleave": { + this.moreProps.hovering = false; + const moreProps = this.getMoreProps(); + + if (this.props.onUnHover) { + this.props.onUnHover(moreProps, e); + } + break; + } + case "contextmenu": { + if (this.props.onContextMenu) { + this.props.onContextMenu(this.getMoreProps(), e); + } + if ( + this.moreProps.hovering + && this.props.onContextMenuWhenHover + ) { + this.props.onContextMenuWhenHover(this.getMoreProps(), e); + } + break; + } + case "mousedown": { + if (this.props.onMouseDown) { + this.props.onMouseDown(this.getMoreProps(), e); + } + break; + } + case "click": { + const moreProps = this.getMoreProps(); + if (this.moreProps.hovering) { + this.props.onClickWhenHover(moreProps, e); + } else { + this.props.onClickOutside(moreProps, e); + } + if (this.props.onClick) { + this.props.onClick(moreProps, e); + } + break; + } + case "mousemove": { + + const prevHover = this.moreProps.hovering; + this.moreProps.hovering = this.isHover(e); + + const { amIOnTop, setCursorClass } = this.context; + + if (this.moreProps.hovering + && !this.props.selected + /* && !prevHover */ + && amIOnTop(this.suscriberId) + && isDefined(this.props.onHover)) { + setCursorClass("react-stockcharts-pointer-cursor"); + this.iSetTheCursorClass = true; + } else if (this.moreProps.hovering + && this.props.selected + && amIOnTop(this.suscriberId)) { + setCursorClass(this.props.interactiveCursorClass); + this.iSetTheCursorClass = true; + } else if (prevHover + && !this.moreProps.hovering + && this.iSetTheCursorClass) { + this.iSetTheCursorClass = false; + setCursorClass(null); + } + const moreProps = this.getMoreProps(); + + if (this.moreProps.hovering && !prevHover) { + if (this.props.onHover) { + this.props.onHover(moreProps, e); + } + } + if (prevHover && !this.moreProps.hovering) { + if (this.props.onUnHover) { + this.props.onUnHover(moreProps, e); + } + } + + if (this.props.onMouseMove) { + this.props.onMouseMove(moreProps, e); + } + break; + } + case "dblclick": { + const moreProps = this.getMoreProps(); + + if (this.props.onDoubleClick) { + this.props.onDoubleClick(moreProps, e); + } + if ( + this.moreProps.hovering + && this.props.onDoubleClickWhenHover + ) { + this.props.onDoubleClickWhenHover(moreProps, e); + } + break; + } + case "pan": { + this.moreProps.hovering = false; + if (this.props.onPan) { + this.props.onPan(this.getMoreProps(), e); + } + break; + } + case "panend": { + if (this.props.onPanEnd) { + this.props.onPanEnd(this.getMoreProps(), e); + } + break; + } + case "dragstart": { + if (this.getPanConditions().draggable) { + const { amIOnTop } = this.context; + if (amIOnTop(this.suscriberId)) { + this.dragInProgress = true; + this.props.onDragStart(this.getMoreProps(), e); + } + } + break; + } + case "drag": { + if (this.dragInProgress && this.props.onDrag) { + this.props.onDrag(this.getMoreProps(), e); + } + break; + } + case "dragend": { + if (this.dragInProgress && this.props.onDragComplete) { + this.props.onDragComplete(this.getMoreProps(), e); + } + this.dragInProgress = false; + break; + } + case "dragcancel": { + if (this.dragInProgress || this.iSetTheCursorClass) { + const { setCursorClass } = this.context; + setCursorClass(null); + } + break; + } + } + } + + public isHover(e) { + return isDefined(this.props.isHover) + ? this.props.isHover(this.getMoreProps(), e) + : false; + } + + public getPanConditions() { + const draggable = ( + !!(this.props.selected && this.moreProps.hovering) + || (this.props.enableDragOnHover && this.moreProps.hovering) + ); + + return { + draggable, + panEnabled: !this.props.disablePan, + }; + } + + // @ts-ignore + public draw({ trigger, force } = { force: false }) { + const type = aliases[trigger] || trigger; + const proceed = this.props.drawOn.indexOf(type) > -1; + + if (proceed + || this.props.selected /* this is to draw as soon as you select */ + || force + ) { + const { chartCanvasType } = this.context; + const { canvasDraw } = this.props; + + if (isNotDefined(canvasDraw) || chartCanvasType === "svg") { + const { updateCount } = this.state; + this.setState({ + updateCount: updateCount + 1, + }); + } else { + this.drawOnCanvas(); + } + } + } + + public UNSAFE_componentWillMount() { + const { subscribe, chartId } = this.context; + const { clip, edgeClip } = this.props; + + subscribe(this.suscriberId, + { + chartId, clip, edgeClip, + listener: this.listener, + draw: this.draw, + getPanConditions: this.getPanConditions, + }, + ); + this.UNSAFE_componentWillReceiveProps(this.props, this.context); + } + + public componentWillUnmount() { + const { unsubscribe } = this.context; + unsubscribe(this.suscriberId); + if (this.iSetTheCursorClass) { + const { setCursorClass } = this.context; + setCursorClass(null); + } + } + + public componentDidMount() { + this.componentDidUpdate(this.props); + } + + public componentDidUpdate(prevProps) { + const { chartCanvasType } = this.context; + const { canvasDraw, selected, interactiveCursorClass } = this.props; + + if (prevProps.selected !== selected) { + const { setCursorClass } = this.context; + if (selected && this.moreProps.hovering) { + this.iSetTheCursorClass = true; + setCursorClass(interactiveCursorClass); + } else { + this.iSetTheCursorClass = false; + setCursorClass(null); + } + } + if (isDefined(canvasDraw) + && !this.evaluationInProgress + && chartCanvasType !== "svg") { + + this.updateMoreProps(this.moreProps); + this.drawOnCanvas(); + } + } + + public UNSAFE_componentWillReceiveProps(nextProps, nextContext) { + const { xScale, plotData, chartConfig, getMutableState } = nextContext; + + this.props.debug(nextContext); + this.moreProps = { + ...this.moreProps, + ...getMutableState(), + /* + ^ this is so + mouseXY, currentCharts, currentItem are available to + newly created components like MouseHoverText which + is created right after a new interactive object is drawn + */ + xScale, plotData, chartConfig, + }; + } + + public getMoreProps() { + const { + xScale, + plotData, + chartConfig, + morePropsDecorator, + xAccessor, + displayXAccessor, + width, + height, + } = this.context; + + const { chartId, fullData } = this.context; + + const moreProps = { + xScale, plotData, chartConfig, + xAccessor, displayXAccessor, + width, height, + chartId, + fullData, + ...this.moreProps, + }; + + return (morePropsDecorator || identity)(moreProps); + } + + public preCanvasDraw(ctx, moreProps) { + // do nothing + } + + public postCanvasDraw(ctx, moreProps) { + // empty + } + + public drawOnCanvas() { + const { canvasDraw, canvasToDraw } = this.props; + const { getCanvasContexts } = this.context; + + const moreProps = this.getMoreProps(); + + const ctx = canvasToDraw(getCanvasContexts()); + + this.preCanvasDraw(ctx, moreProps); + canvasDraw(ctx, moreProps); + this.postCanvasDraw(ctx, moreProps); + } + + public render() { + const { chartCanvasType, chartId } = this.context; + const { canvasDraw, clip, svgDraw } = this.props; + + if (isDefined(canvasDraw) && chartCanvasType !== "svg") { + return null; + } + + const suffix = isDefined(chartId) ? "-" + chartId : ""; + + const style = clip ? { clipPath: `url(#chart-area-clip${suffix})` } : undefined; + + return ( + + {svgDraw(this.getMoreProps())} + + ); + } +} + +export default GenericComponent; + +export function getAxisCanvas(contexts) { + return contexts.axes; +} + +export function getMouseCanvas(contexts) { + return contexts.mouseCoord; +} diff --git a/packages/react-financial-charts/src/ZoomButtons.tsx b/packages/react-financial-charts/src/ZoomButtons.tsx new file mode 100644 index 000000000..f2f27d440 --- /dev/null +++ b/packages/react-financial-charts/src/ZoomButtons.tsx @@ -0,0 +1,217 @@ +import { interpolateNumber } from "d3-interpolate"; +import { path as d3Path } from "d3-path"; +import * as PropTypes from "prop-types"; +import * as React from "react"; + +import { last, noop } from "./utils"; + +interface ZoomButtonsProps { + zoomMultiplier: number; + size: number[]; + heightFromBase: number; + rx: number; + ry: number; + stroke: string; + strokeWidth: number; + strokeOpacity: number; + fill: string; + fillOpacity: number; + fontSize: number; + textDy: string; + textFill: string; + textStrokeWidth: number; + onReset: any; // func +} + +class ZoomButtons extends React.Component { + + public static defaultProps = { + size: [30, 24], + heightFromBase: 50, + rx: 3, + ry: 3, + stroke: "#000000", + strokeOpacity: 0.3, + strokeWidth: 1, + fill: "#D6D6D6", + fillOpacity: 0.4, + fontSize: 16, + textDy: ".3em", + textFill: "#000000", + textStrokeWidth: 2, + zoomMultiplier: 1.5, + onReset: noop, + }; + + public static contextTypes = { + xScale: PropTypes.func.isRequired, + chartConfig: PropTypes.object.isRequired, + plotData: PropTypes.array.isRequired, + xAccessor: PropTypes.func.isRequired, + xAxisZoom: PropTypes.func.isRequired, + }; + + private interval?: any; + + public render() { + const { chartConfig } = this.context; + const { width, height } = chartConfig; + const { size, heightFromBase, rx, ry } = this.props; + const { stroke, strokeOpacity, fill, strokeWidth, fillOpacity } = this.props; + const { textFill, textStrokeWidth } = this.props; + const { onReset } = this.props; + const centerX = Math.round(width / 2); + const y = height - heightFromBase; + + const [w, h] = size; + const hLength = 5; + const wLength = 6; + + const textY = Math.round(y + h / 2); + + const resetX = centerX; + + const zoomOut = d3Path(); + const zoomOutX = centerX - w - 2 * strokeWidth; + zoomOut.moveTo(zoomOutX - wLength, textY); + zoomOut.lineTo(zoomOutX + wLength, textY); + zoomOut.closePath(); + + const zoomIn = d3Path(); + const zoomInX = centerX + w + 2 * strokeWidth; + + zoomIn.moveTo(zoomInX - wLength, textY); + zoomIn.lineTo(zoomInX + wLength, textY); + + zoomIn.moveTo(zoomInX, textY - hLength); + zoomIn.lineTo(zoomInX, textY + hLength); + + return ( + + + + + + + + + + + + + + ); + } + + private readonly handleZoomIn = () => { + if (this.interval) { return; } + this.zoom(-1); + } + + private readonly handleZoomOut = () => { + if (this.interval) { return; } + this.zoom(1); + } + + private readonly zoom = (direction: number) => { + const { xAxisZoom, xScale, plotData, xAccessor } = this.context; + + const cx = xScale(xAccessor(last(plotData))); + + const { zoomMultiplier } = this.props; + + const c = direction > 0 ? 1 * zoomMultiplier : 1 / zoomMultiplier; + + const [start, end] = xScale.domain(); + const [newStart, newEnd] = xScale + .range() + .map((x) => cx + (x - cx) * c) + .map(xScale.invert); + + const left = interpolateNumber(start, newStart); + const right = interpolateNumber(end, newEnd); + + const foo = [0.25, 0.3, 0.5, 0.6, 0.75, 1].map((i) => { + return [left(i), right(i)]; + }); + + this.interval = setInterval(() => { + xAxisZoom(foo.shift()); + if (foo.length === 0) { + clearInterval(this.interval); + delete this.interval; + } + }, 10); + } +} + +export default ZoomButtons; diff --git a/packages/react-financial-charts/src/algorithm/index.ts b/packages/react-financial-charts/src/algorithm/index.ts new file mode 100644 index 000000000..83abef6ec --- /dev/null +++ b/packages/react-financial-charts/src/algorithm/index.ts @@ -0,0 +1,53 @@ +// tslint:disable: only-arrow-functions space-before-function-paren + +import { identity, merge, slidingWindow } from "../utils"; + +export default function () { + + let windowSize = 1; + let accumulator = identity; + let mergeAs = identity; + + function algorithm(data: any) { + + const defaultAlgorithm = slidingWindow() + .windowSize(windowSize) + // @ts-ignore + .accumulator(accumulator); + + const calculator = merge() + .algorithm(defaultAlgorithm) + // @ts-ignore + .merge(mergeAs); + + const newData = calculator(data); + + return newData; + } + + algorithm.accumulator = function (x: any) { + if (!arguments.length) { + return accumulator; + } + accumulator = x; + return algorithm; + }; + + algorithm.windowSize = function (x: any) { + if (!arguments.length) { + return windowSize; + } + windowSize = x; + return algorithm; + }; + + algorithm.merge = function (x: any) { + if (!arguments.length) { + return mergeAs; + } + mergeAs = x; + return algorithm; + }; + + return algorithm; +} diff --git a/packages/react-financial-charts/src/annotation/Annotate.tsx b/packages/react-financial-charts/src/annotation/Annotate.tsx new file mode 100644 index 000000000..13262dbd8 --- /dev/null +++ b/packages/react-financial-charts/src/annotation/Annotate.tsx @@ -0,0 +1,54 @@ +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; + +interface AnnotateProps { + readonly className?: string; + readonly with?: any; // func + readonly when?: any; // func + readonly usingProps?: object; +} + +export class Annotate extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-annotate react-stockcharts-default-cursor", + }; + + public render() { + return ( + + ); + } + + private readonly renderSVG = (moreProps: any) => { + const { xAccessor } = moreProps; + const { xScale, chartConfig: { yScale }, plotData } = moreProps; + + const { className, usingProps, with: Annotation, when } = this.props; + + const data = this.plotfilter(when, plotData); + + return ( + + {data.map((d: any, idx) => { + return ( + + ); + })} + + ); + } + + private readonly plotfilter = (when: any, plotData: any[]) => { + return plotData.filter(when); + } +} diff --git a/packages/react-financial-charts/src/annotation/BarAnnotation.tsx b/packages/react-financial-charts/src/annotation/BarAnnotation.tsx new file mode 100644 index 000000000..478f31615 --- /dev/null +++ b/packages/react-financial-charts/src/annotation/BarAnnotation.tsx @@ -0,0 +1,162 @@ +import * as React from "react"; +import { functor } from "../utils"; + +interface BarAnnotationProps { + readonly className?: string; + readonly path?: any; // func + readonly onClick?: any; // func + readonly xAccessor?: any; // func + readonly xScale?: any; // func + readonly yScale?: any; // func + readonly datum?: object; + readonly stroke?: string; + readonly fill?: string; + readonly opacity?: number; + readonly text?: string; + readonly textAnchor?: string; + readonly fontFamily?: string; + readonly fontSize?: number; + readonly textOpacity?: number; + readonly textFill?: string; + readonly textRotate?: number; + readonly textXOffset?: number; + readonly textYOffset?: number; + readonly textIcon?: string; + readonly textIconFontSize?: number; + readonly textIconOpacity?: number; + readonly textIconFill?: string; + readonly textIconRotate?: number; + readonly textIconXOffset?: number; + readonly textIconYOffset?: number; + readonly textIconAnchor?: string; +} + +export class BarAnnotation extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-bar-annotation", + opacity: 1, + fill: "#000000", + textAnchor: "middle", + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 10, + textFill: "#000000", + textOpacity: 1, + textIconFill: "#000000", + textIconFontSize: 10, + x: ({ xScale, xAccessor, datum }) => xScale(xAccessor(datum)), + }; + + public render() { + const { className, stroke, opacity } = this.props; + const { xAccessor, xScale, yScale, path } = this.props; + const { + text, + textXOffset, + textYOffset, + textAnchor, + fontFamily, + fontSize, + textFill, + textOpacity, + textRotate, + } = this.props; + + const { x, y, fill, tooltip } = helper( + this.props, + xAccessor, + xScale, + yScale, + ); + + const { + textIcon, + textIconFontSize, + textIconFill, + textIconOpacity, + textIconRotate, + textIconXOffset, + textIconYOffset, + } = this.props; + + return ( + + {tooltip != null ? {tooltip} : null} + {text != null ? ( + + {text} + + ) : null} + {textIcon != null ? ( + + {textIcon} + + ) : null} + {path != null ? ( + + ) : null} + + ); + } + + private readonly onClick = (e: React.MouseEvent) => { + const { onClick } = this.props; + + if (onClick) { + const { xScale, yScale, datum } = this.props; + onClick({ xScale, yScale, datum }, e); + } + } +} + +function helper(props, xAccessor, xScale, yScale) { + const { x, y, datum, fill, tooltip, plotData } = props; + + const xFunc = functor(x); + const yFunc = functor(y); + + const [xPos, yPos] = [ + xFunc({ xScale, xAccessor, datum, plotData }), + yFunc({ yScale, datum, plotData }), + ]; + + return { + x: xPos, + y: yPos, + fill: functor(fill)(datum), + tooltip: functor(tooltip)(datum), + }; +} diff --git a/packages/react-financial-charts/src/annotation/Label.tsx b/packages/react-financial-charts/src/annotation/Label.tsx new file mode 100644 index 000000000..b920d899e --- /dev/null +++ b/packages/react-financial-charts/src/annotation/Label.tsx @@ -0,0 +1,117 @@ +import * as PropTypes from "prop-types"; +import * as React from "react"; +import GenericComponent from "../GenericComponent"; + +import { functor, hexToRGBA, isDefined } from "../utils"; +import { helper, LabelAnnotation } from "./LabelAnnotation"; + +interface LabelProps { + readonly className?: string; + readonly selectCanvas?: any; // func + readonly text?: string | any; // func + readonly textAnchor?: string; + readonly fontFamily?: string; + readonly fontSize?: number; + readonly opacity?: number; + readonly rotate?: number; + readonly onClick?: any; // func + readonly xAccessor?: any; // func + readonly xScale?: any; // func + readonly yScale?: any; // func + readonly datum?: object; + readonly x?: number | any; // func + readonly y?: number | any; // func +} + +export class Label extends React.Component { + + public static defaultProps = { + textAnchor: "middle", + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 12, + fill: "#000000", + opacity: 1, + rotate: 0, + x: ({ xScale, xAccessor, datum }) => xScale(xAccessor(datum)), + selectCanvas: (canvases) => canvases.bg, + }; + + public static contextTypes = { + canvasOriginX: PropTypes.number, + canvasOriginY: PropTypes.number, + + margin: PropTypes.object.isRequired, + ratio: PropTypes.number.isRequired, + }; + + public render() { + return ( + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + drawOnCanvas2(ctx, this.props, this.context, moreProps); + } + + private readonly renderSVG = (moreProps) => { + const { chartConfig } = moreProps; + + return ( + + ); + } +} + +function getText(props) { + return functor(props.text)(props); +} + +function getYScale(chartConfig) { + return Array.isArray(chartConfig) ? undefined : chartConfig.yScale; +} + +function drawOnCanvas2(ctx, props, context, moreProps) { + ctx.save(); + + const { canvasOriginX, canvasOriginY, margin, ratio } = context; + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.scale(ratio, ratio); + + if (isDefined(canvasOriginX)) { + ctx.translate(canvasOriginX, canvasOriginY); + } else { + ctx.translate(margin.left + (0.5 * ratio), margin.top + (0.5 * ratio)); + } + + drawOnCanvas(ctx, props, moreProps); + + ctx.restore(); + +} + +function drawOnCanvas(ctx, props, moreProps) { + const { textAnchor, fontFamily, fontSize, opacity, rotate } = props; + const { xScale, chartConfig, xAccessor } = moreProps; + + const { xPos, yPos, fill, text } = helper(props, xAccessor, xScale, getYScale(chartConfig)); + + const radians = (rotate / 180) * Math.PI; + ctx.save(); + ctx.translate(xPos, yPos); + ctx.rotate(radians); + + ctx.font = `${fontSize}px ${fontFamily}`; + ctx.fillStyle = hexToRGBA(fill, opacity); + ctx.textAlign = textAnchor === "middle" ? "center" : textAnchor; + + ctx.beginPath(); + ctx.fillText(text, 0, 0); + ctx.restore(); +} diff --git a/packages/react-financial-charts/src/annotation/LabelAnnotation.tsx b/packages/react-financial-charts/src/annotation/LabelAnnotation.tsx new file mode 100644 index 000000000..3f6976937 --- /dev/null +++ b/packages/react-financial-charts/src/annotation/LabelAnnotation.tsx @@ -0,0 +1,77 @@ +import * as React from "react"; +import { functor } from "../utils"; + +interface LabelAnnotationProps { + readonly className?: string; + readonly text?: string | any; // func + readonly textAnchor?: string; + readonly fontFamily?: string; + readonly fontSize?: number; + readonly opacity?: number; + readonly rotate?: number; + readonly onClick?: any; // func + readonly xAccessor?: any; // func + readonly xScale?: any; // func + readonly yScale?: any; // func + readonly datum?: object; + readonly x?: number | any; // func + readonly y?: number | any; // func +} + +export class LabelAnnotation extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-labelannotation", + textAnchor: "middle", + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 12, + fill: "#000000", + opacity: 1, + rotate: 0, + x: ({ xScale, xAccessor, datum }) => xScale(xAccessor(datum)), + }; + + public render() { + const { className, textAnchor, fontFamily, fontSize, opacity, rotate } = this.props; + const { xAccessor, xScale, yScale } = this.props; + + const { xPos, yPos, fill, text, tooltip } = helper(this.props, xAccessor, xScale, yScale); + + return ( + {tooltip} + {text} + ); + } + + private readonly handleClick = (e) => { + const { onClick } = this.props; + + if (onClick) { + const { xScale, yScale, datum } = this.props; + onClick({ xScale, yScale, datum }, e); + } + } +} + +export const helper = (props, xAccessor, xScale, yScale) => { + const { x, y, datum, fill, text, tooltip, plotData } = props; + + const xFunc = functor(x); + const yFunc = functor(y); + + const [xPos, yPos] = [xFunc({ xScale, xAccessor, datum, plotData }), yFunc({ yScale, datum, plotData })]; + + return { + xPos, + yPos, + text: functor(text)(datum), + fill: functor(fill)(datum), + tooltip: functor(tooltip)(datum), + }; +}; diff --git a/packages/react-financial-charts/src/annotation/SvgPathAnnotation.tsx b/packages/react-financial-charts/src/annotation/SvgPathAnnotation.tsx new file mode 100644 index 000000000..2d66c830f --- /dev/null +++ b/packages/react-financial-charts/src/annotation/SvgPathAnnotation.tsx @@ -0,0 +1,62 @@ +import * as React from "react"; +import { functor } from "../utils"; + +interface SvgPathAnnotationProps { + readonly className?: string; + readonly path: any; // func + readonly onClick?: any; // func + readonly xAccessor?: any; // func + readonly xScale?: any; // func + readonly yScale?: any; // func + readonly datum?: object; + readonly stroke?: string; + readonly fill?: string; + readonly opacity?: number; +} + +export class SvgPathAnnotation extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-svgpathannotation", + opacity: 1, + x: ({ xScale, xAccessor, datum }) => xScale(xAccessor(datum)), + }; + + public render() { + const { className, stroke, opacity } = this.props; + + const { xAccessor, xScale, yScale, path } = this.props; + + const { x, y, fill, tooltip } = helper(this.props, xAccessor, xScale, yScale); + + return ( + {tooltip} + + ); + } + + private readonly handleClick = (e: React.MouseEvent) => { + const { onClick } = this.props; + + if (onClick) { + const { xScale, yScale, datum } = this.props; + onClick({ xScale, yScale, datum }, e); + } + } +} + +function helper(props, xAccessor, xScale, yScale) { + const { x, y, datum, fill, tooltip, plotData } = props; + + const xFunc = functor(x); + const yFunc = functor(y); + + const [xPos, yPos] = [xFunc({ xScale, xAccessor, datum, plotData }), yFunc({ yScale, datum, plotData })]; + + return { + x: xPos, + y: yPos, + fill: functor(fill)(datum), + tooltip: functor(tooltip)(datum), + }; +} diff --git a/packages/react-financial-charts/src/annotation/index.ts b/packages/react-financial-charts/src/annotation/index.ts new file mode 100644 index 000000000..cb5f4ce97 --- /dev/null +++ b/packages/react-financial-charts/src/annotation/index.ts @@ -0,0 +1,30 @@ +export * from "./Annotate"; +export { LabelAnnotation } from "./LabelAnnotation"; +export * from "./SvgPathAnnotation"; +export * from "./Label"; + +const halfWidth = 10; +const bottomWidth = 3; +const height = 20; + +export const buyPath = ({ x, y }: { x: number; y: number }) => { + return `M${x} ${y} ` + + `L${x + halfWidth} ${y + halfWidth} ` + + `L${x + bottomWidth} ${y + halfWidth} ` + + `L${x + bottomWidth} ${y + height} ` + + `L${x - bottomWidth} ${y + height} ` + + `L${x - bottomWidth} ${y + halfWidth} ` + + `L${x - halfWidth} ${y + halfWidth} ` + + "Z"; +}; + +export const sellPath = ({ x, y }: { x: number; y: number }) => { + return `M${x} ${y} ` + + `L${x + halfWidth} ${y - halfWidth} ` + + `L${x + bottomWidth} ${y - halfWidth} ` + + `L${x + bottomWidth} ${y - height} ` + + `L${x - bottomWidth} ${y - height} ` + + `L${x - bottomWidth} ${y - halfWidth} ` + + `L${x - halfWidth} ${y - halfWidth} ` + + "Z"; +}; diff --git a/packages/react-financial-charts/src/axes/Axis.tsx b/packages/react-financial-charts/src/axes/Axis.tsx new file mode 100644 index 000000000..bc9029cc3 --- /dev/null +++ b/packages/react-financial-charts/src/axes/Axis.tsx @@ -0,0 +1,452 @@ +import { range as d3Range } from "d3-array"; +import { forceCollide, forceSimulation, forceX } from "d3-force"; +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getAxisCanvas } from "../GenericComponent"; +import { AxisZoomCapture } from "./AxisZoomCapture"; + +import { first, getStrokeDasharray, hexToRGBA, identity, isDefined, isNotDefined, last, strokeDashTypes, zipper } from "../utils"; + +interface AxisProps { + innerTickSize?: number; + outerTickSize?: number; + tickFormat?: any; // func + tickPadding?: number; + tickSize?: number; + ticks?: number; + tickLabelFill?: string; + tickStroke?: string; + tickStrokeOpacity?: number; + tickStrokeWidth?: number; + tickStrokeDasharray?: strokeDashTypes; + tickValues?: number[] | any; // func + tickInterval?: number; + tickIntervalFunction?: any; // func + showDomain?: boolean; + showTicks?: boolean; + className?: string; + axisZoomCallback?: any; // func + zoomEnabled?: boolean; + inverted?: boolean; + zoomCursorClassName?: string; + transform: number[]; + range: number[]; + getMouseDelta: any; // func + getScale: any; // func + bg: { + h: number; + x: number; + w: number; + y: number; + }; + edgeClip: boolean; + onContextMenu?: any; // func + onDoubleClick?: any; // func +} + +class Axis extends React.Component { + + public static defaultProps = { + zoomEnabled: false, + zoomCursorClassName: "", + edgeClip: false, + }; + + private node; + + public render() { + const { bg, axisZoomCallback, className, zoomCursorClassName, zoomEnabled, getScale, inverted } = this.props; + const { transform, getMouseDelta, edgeClip } = this.props; + const { onContextMenu, onDoubleClick } = this.props; + + const zoomCapture = zoomEnabled + ? + : null; + + return ( + + {zoomCapture} + + + ); + } + + private readonly saveNode = (node) => { + this.node = node; + } + + private readonly getMoreProps = () => { + return this.node.getMoreProps(); + } + + private readonly renderSVG = (moreProps) => { + const { className } = this.props; + const { showDomain, showTicks, range, getScale } = this.props; + + const ticks = showTicks ? axisTicksSVG(this.props, getScale(moreProps)) : null; + const domain = showDomain ? axisLineSVG(this.props, range) : null; + + return + {ticks} + {domain} + ; + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { showDomain, showTicks, transform, range, getScale } = this.props; + + ctx.save(); + ctx.translate(transform[0], transform[1]); + + if (showDomain) { drawAxisLine(ctx, this.props, range); } + if (showTicks) { + const tickProps = tickHelper(this.props, getScale(moreProps)); + drawTicks(ctx, tickProps); + } + + ctx.restore(); + } +} + +function tickHelper(props, scale) { + const { + orient, innerTickSize, tickFormat, tickPadding, + tickLabelFill, tickStrokeWidth, tickStrokeDasharray, + fontSize, fontFamily, fontWeight, showTicks, flexTicks, + showTickLabel, + } = props; + const { + ticks: tickArguments, tickValues: tickValuesProp, + tickStroke, tickStrokeOpacity, tickInterval, tickIntervalFunction, + } = props; + + let tickValues; + if (isDefined(tickValuesProp)) { + if (typeof tickValuesProp === "function") { + tickValues = tickValuesProp(scale.domain()); + } else { + tickValues = tickValuesProp; + } + } else if (isDefined(tickInterval)) { + const [min, max] = scale.domain(); + const baseTickValues = d3Range(min, max, (max - min) / tickInterval); + + tickValues = tickIntervalFunction + ? tickIntervalFunction(min, max, tickInterval) + : baseTickValues; + } else if (isDefined(scale.ticks)) { + tickValues = scale.ticks(tickArguments, flexTicks); + } else { + tickValues = scale.domain(); + } + + const baseFormat = scale.tickFormat + ? scale.tickFormat(tickArguments) + : identity; + + const format = isNotDefined(tickFormat) + ? baseFormat + : (d) => tickFormat(d) || ""; + + const sign = orient === "top" || orient === "left" ? -1 : 1; + const tickSpacing = Math.max(innerTickSize, 0) + tickPadding; + + let ticks; + let dy; + // tslint:disable-next-line: variable-name + let canvas_dy; + let textAnchor; + + if (orient === "bottom" || orient === "top") { + dy = sign < 0 ? "0em" : ".71em"; + canvas_dy = sign < 0 ? 0 : (fontSize * .71); + textAnchor = "middle"; + + ticks = tickValues.map((d) => { + const x = Math.round(scale(d)); + return { + value: d, + x1: x, + y1: 0, + x2: x, + y2: sign * innerTickSize, + labelX: x, + labelY: sign * tickSpacing, + }; + }); + + if (showTicks && flexTicks) { + + const nodes = ticks.map((d) => ({ id: d.value, value: d.value, fy: d.y2, origX: d.x1 })); + + const simulation = forceSimulation(nodes) + .force("x", forceX((d) => d.origX).strength(1)) + .force("collide", forceCollide(22)) + .stop(); + + for (let i = 0; i < 100; ++i) { simulation.tick(); } + + const zip = zipper() + .combine((a, b) => { + if (Math.abs(b.x - b.origX) > 0.01) { + return { + ...a, + x2: b.x, + labelX: b.x, + }; + } + return a; + }); + + // @ts-ignore + ticks = zip(ticks, nodes); + } + + } else { + ticks = tickValues.map((d) => { + const y = Math.round(scale(d)); + return { + value: d, + x1: 0, + y1: y, + x2: sign * innerTickSize, + y2: y, + labelX: sign * tickSpacing, + labelY: y, + }; + }); + + dy = ".32em"; + canvas_dy = (fontSize * .32); + textAnchor = sign < 0 ? "end" : "start"; + } + + return { + ticks, scale, tickStroke, + tickLabelFill: (tickLabelFill || tickStroke), + tickStrokeOpacity, + tickStrokeWidth, + tickStrokeDasharray, + dy, + canvas_dy, + textAnchor, + fontSize, + fontFamily, + fontWeight, + format, + showTickLabel, + }; +} + +/* eslint-disable react/prop-types */ +function axisLineSVG(props, range) { + const { orient, outerTickSize } = props; + const { domainClassName, fill, stroke, strokeWidth, opacity } = props; + + const sign = orient === "top" || orient === "left" ? -1 : 1; + + let d; + + if (orient === "bottom" || orient === "top") { + d = "M" + range[0] + "," + sign * outerTickSize + "V0H" + range[1] + "V" + sign * outerTickSize; + } else { + d = "M" + sign * outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + sign * outerTickSize; + } + + return ( + + + ); +} +/* eslint-enable react/prop-types */ + +function drawAxisLine(ctx, props, range) { + // props = { ...AxisLine.defaultProps, ...props }; + + const { orient, outerTickSize, stroke, strokeWidth, opacity } = props; + + const sign = orient === "top" || orient === "left" ? -1 : 1; + const xAxis = (orient === "bottom" || orient === "top"); + + // var range = d3_scaleRange(xAxis ? xScale : yScale); + + ctx.lineWidth = strokeWidth; + ctx.strokeStyle = hexToRGBA(stroke, opacity); + + ctx.beginPath(); + + if (xAxis) { + ctx.moveTo(first(range), sign * outerTickSize); + ctx.lineTo(first(range), 0); + ctx.lineTo(last(range), 0); + ctx.lineTo(last(range), sign * outerTickSize); + } else { + ctx.moveTo(sign * outerTickSize, first(range)); + ctx.lineTo(0, first(range)); + ctx.lineTo(0, last(range)); + ctx.lineTo(sign * outerTickSize, last(range)); + } + ctx.stroke(); +} + +interface TickProps { + children: string; + x1: number; + y1: number; + x2: number; + y2: number; + labelX: number; + labelY: number; + dy: string; + tickStroke?: string; + tickLabelFill?: string; + tickStrokeWidth?: number; + tickStrokeOpacity?: number; + tickStrokeDasharray?: strokeDashTypes; + textAnchor?: string; + fontSize?: number; + fontFamily?: string; + fontWeight?: number | string; +} + +function Tick(props: TickProps) { + const { + tickLabelFill, + tickStroke, + tickStrokeOpacity, + tickStrokeDasharray, + tickStrokeWidth, + textAnchor, + fontSize, + fontFamily, + fontWeight, + } = props; + + const { x1, y1, x2, y2, labelX, labelY, dy } = props; + + return ( + + + + {props.children} + + + ); +} + +function axisTicksSVG(props, scale) { + const result = tickHelper(props, scale); + + const { tickLabelFill, tickStroke, tickStrokeOpacity, tickStrokeWidth, tickStrokeDasharray, textAnchor } = result; + const { fontSize, fontFamily, fontWeight, ticks, format } = result; + + const { dy } = result; + + return ( + + {ticks.map((tick, idx) => { + return ( + {format(tick.value)} + ); + })} + + ); +} + +function drawTicks(ctx, result) { + + const { tickStroke, tickStrokeOpacity, tickLabelFill } = result; + const { textAnchor, fontSize, fontFamily, fontWeight, ticks, showTickLabel } = result; + + ctx.strokeStyle = hexToRGBA(tickStroke, tickStrokeOpacity); + + ctx.fillStyle = tickStroke; + // ctx.textBaseline = 'middle'; + + ticks.forEach((tick) => { + drawEachTick(ctx, tick, result); + }); + + ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`; + ctx.fillStyle = tickLabelFill; + ctx.textAlign = textAnchor === "middle" ? "center" : textAnchor; + + if (showTickLabel) { + ticks.forEach((tick) => { + drawEachTickLabel(ctx, tick, result); + }); + } +} + +function drawEachTick(ctx, tick, result) { + const { tickStrokeWidth, tickStrokeDasharray } = result; + + ctx.beginPath(); + + ctx.moveTo(tick.x1, tick.y1); + ctx.lineTo(tick.x2, tick.y2); + ctx.lineWidth = tickStrokeWidth; + ctx.setLineDash(getStrokeDasharray(tickStrokeDasharray).split(",")); + ctx.stroke(); +} + +function drawEachTickLabel(ctx, tick, result) { + const { canvas_dy, format } = result; + + ctx.beginPath(); + ctx.fillText(format(tick.value), tick.labelX, tick.labelY + canvas_dy); +} + +export default Axis; diff --git a/packages/react-financial-charts/src/axes/AxisLine.tsx b/packages/react-financial-charts/src/axes/AxisLine.tsx new file mode 100644 index 000000000..986f07ac2 --- /dev/null +++ b/packages/react-financial-charts/src/axes/AxisLine.tsx @@ -0,0 +1,104 @@ +import * as React from "react"; +import { first, hexToRGBA, last } from "../utils"; + +interface AxisLineProps { + className?: string; + shapeRendering?: string; + orient: string; + scale: any; // func + outerTickSize?: number; + fill?: string; + stroke?: string; + strokeWidth?: number; + opacity?: number; + range: number[]; +} + +/* +function d3_scaleExtent(domain) { + var start = domain[0], stop = domain[domain.length - 1]; + return start < stop ? [start, stop] : [stop, start]; +} + +function d3_scaleRange(scale) { + return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range()); +} +*/ +export class AxisLine extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-axis-line", + shapeRendering: "crispEdges", + fill: "none", + stroke: "#000000", + strokeWidth: 1, + opacity: 1, + }; + + public static drawOnCanvasStatic = (props, ctx/* , xScale, yScale*/) => { + props = { ...AxisLine.defaultProps, ...props }; + + const { orient, outerTickSize, stroke, strokeWidth, opacity, range } = props; + + const sign = orient === "top" || orient === "left" ? -1 : 1; + const xAxis = (orient === "bottom" || orient === "top"); + + // var range = d3_scaleRange(xAxis ? xScale : yScale); + + ctx.lineWidth = strokeWidth; + ctx.strokeStyle = hexToRGBA(stroke, opacity); + + ctx.beginPath(); + + if (xAxis) { + ctx.moveTo(first(range), sign * outerTickSize); + ctx.lineTo(first(range), 0); + ctx.lineTo(last(range), 0); + ctx.lineTo(last(range), sign * outerTickSize); + } else { + ctx.moveTo(sign * outerTickSize, first(range)); + ctx.lineTo(0, first(range)); + ctx.lineTo(0, last(range)); + ctx.lineTo(sign * outerTickSize, last(range)); + } + ctx.stroke(); + } + + public render() { + const { + orient, + outerTickSize = 0, + fill, + stroke, + strokeWidth, + className, + shapeRendering, + opacity, + range, + } = this.props; + + const sign = orient === "top" || orient === "left" ? -1 : 1; + + // var range = d3_scaleRange(scale); + + let d; + + if (orient === "bottom" || orient === "top") { + d = "M" + range[0] + "," + sign * outerTickSize + "V0H" + range[1] + "V" + sign * outerTickSize; + } else { + d = "M" + sign * outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + sign * outerTickSize; + } + + return ( + + + ); + } +} diff --git a/packages/react-financial-charts/src/axes/AxisTicks.tsx b/packages/react-financial-charts/src/axes/AxisTicks.tsx new file mode 100644 index 000000000..bc1eacf03 --- /dev/null +++ b/packages/react-financial-charts/src/axes/AxisTicks.tsx @@ -0,0 +1,193 @@ +import * as React from "react"; + +import { hexToRGBA, identity, isNotDefined } from "../utils"; + +function tickTransform_svg_axisX(scale, tick) { + return [Math.round(scale(tick)), 0]; +} + +function tickTransform_svg_axisY(scale, tick) { + return [0, Math.round(scale(tick))]; +} + +interface TickProps { + transform: number[]; + tickStroke?: string; + tickStrokeOpacity?: number; + textAnchor?: string; + fontSize?: number; + fontFamily?: string; + x?: number; + y?: number; + x2?: number; + y2?: number; + dy?: string; +} + +class Tick extends React.Component { + + public static drawOnCanvasStatic = (tick, ctx, result) => { + const { scale, tickTransform, canvas_dy, x, y, x2, y2, format } = result; + + const origin = tickTransform(scale, tick); + + ctx.beginPath(); + + ctx.moveTo(origin[0], origin[1]); + ctx.lineTo(origin[0] + x2, origin[1] + y2); + ctx.stroke(); + + ctx.fillText(format(tick), origin[0] + x, origin[1] + y + canvas_dy); + } + + public render() { + const { transform, tickStroke, tickStrokeOpacity, textAnchor, fontSize, fontFamily } = this.props; + const { x, y, x2, y2, dy } = this.props; + return ( + + + + {this.props.children} + + + ); + } +} + +interface AxisTicksProps { + orient: "top" | "bottom" | "left" | "right"; + innerTickSize?: number; + tickFormat?: any; // func + tickPadding?: number; + ticks?: number[]; + tickValues?: number[]; + scale: any; // func + tickStroke?: string; + tickStrokeOpacity?: number; +} + +// tslint:disable-next-line: max-classes-per-file +export class AxisTicks extends React.Component { + + public static defaultProps = { + innerTickSize: 5, + tickPadding: 6, + ticks: [10], + tickStroke: "#000", + tickStrokeOpacity: 1, + }; + + public static drawOnCanvasStatic = (props, ctx, xScale, yScale) => { + props = { ...AxisTicks.defaultProps, ...props }; + + const { orient } = props; + const xAxis = (orient === "bottom" || orient === "top"); + + const result = AxisTicks.helper(props, xAxis ? xScale : yScale); + + const { tickStroke, tickStrokeOpacity, textAnchor, fontSize, fontFamily } = result; + + ctx.strokeStyle = hexToRGBA(tickStroke, tickStrokeOpacity); + + ctx.font = `${fontSize}px ${fontFamily}`; + ctx.fillStyle = tickStroke; + ctx.textAlign = textAnchor === "middle" ? "center" : textAnchor; + + result.ticks.forEach((tick) => { + Tick.drawOnCanvasStatic(tick, ctx, result); + }); + } + + public static helper = (props, scale) => { + const { orient, innerTickSize, tickFormat, tickPadding, fontSize, fontFamily } = props; + const { ticks: tickArguments, tickValues, tickStroke, tickStrokeOpacity } = props; + + const ticks = isNotDefined(tickValues) + ? (scale.ticks + ? scale.ticks(...tickArguments) + : scale.domain()) + : tickValues; + + const baseFormat = scale.tickFormat + ? scale.tickFormat(...tickArguments) + : identity; + + const format = isNotDefined(tickFormat) + ? baseFormat + : (d) => baseFormat(d) ? tickFormat(d) : ""; + + const sign = orient === "top" || orient === "left" ? -1 : 1; + const tickSpacing = Math.max(innerTickSize, 0) + tickPadding; + + let tickTransform; + let x; + let y; + let x2; + let y2; + let dy; + // tslint:disable-next-line: variable-name + let canvas_dy; + let textAnchor; + + if (orient === "bottom" || orient === "top") { + tickTransform = tickTransform_svg_axisX; + x2 = 0; + y2 = sign * innerTickSize; + x = 0; + y = sign * tickSpacing; + dy = sign < 0 ? "0em" : ".71em"; + canvas_dy = sign < 0 ? 0 : (fontSize * .71); + textAnchor = "middle"; + } else { + tickTransform = tickTransform_svg_axisY; + x2 = sign * innerTickSize; + y2 = 0; + x = sign * tickSpacing; + y = 0; + dy = ".32em"; + canvas_dy = (fontSize * .32); + textAnchor = sign < 0 ? "end" : "start"; + } + return { + ticks, + scale, + tickTransform, + tickStroke, tickStrokeOpacity, dy, canvas_dy, x, y, x2, y2, textAnchor, fontSize, fontFamily, format, + }; + } + + public render() { + const result = AxisTicks.helper(this.props, this.props.scale); + const { + ticks, + scale, + tickTransform, + tickStroke, + tickStrokeOpacity, + dy, + x, + y, + x2, + y2, + textAnchor, fontSize, fontFamily, format } = result; + + return ( + + {ticks.map((tick, idx) => { + return ( + {format(tick)} + ); + })} + + ); + } +} diff --git a/packages/react-financial-charts/src/axes/AxisZoomCapture.tsx b/packages/react-financial-charts/src/axes/AxisZoomCapture.tsx new file mode 100644 index 000000000..ce18f1985 --- /dev/null +++ b/packages/react-financial-charts/src/axes/AxisZoomCapture.tsx @@ -0,0 +1,219 @@ +import { mean } from "d3-array"; +import { event as d3Event, mouse, select, touches } from "d3-selection"; +import * as React from "react"; + +import { + d3Window, + first, + getTouchProps, + isDefined, + last, + MOUSEMOVE, + mousePosition, + MOUSEUP, + noop, + sign, + TOUCHEND, + TOUCHMOVE, + touchPosition, +} from "../utils"; + +interface AxisZoomCaptureProps { + innerTickSize?: number; + outerTickSize?: number; + tickFormat?: any; // func + tickPadding?: number; + tickSize?: number; + ticks?: number; + tickValues?: number[]; + showDomain?: boolean; + showTicks?: boolean; + className?: string; + axisZoomCallback?: any; // func + inverted?: boolean; + bg: { + h: number; + x: number; + w: number; + y: number; + }; + zoomCursorClassName?: string; + getMoreProps: any; // func + getScale: any; // func + getMouseDelta: any; // func + onDoubleClick: any; // func + onContextMenu: any; // func +} + +interface AxisZoomCaptureState { + startPosition: any | null; +} + +export class AxisZoomCapture extends React.Component { + + public static defaultProps = { + onDoubleClick: noop, + onContextMenu: noop, + inverted: true, + }; + + private node; + private mouseInteraction; + private clicked; + private dragHappened; + + public constructor(props) { + super(props); + this.state = { + startPosition: null, + }; + } + + public render() { + const { bg, className, zoomCursorClassName } = this.props; + + const cursor = isDefined(this.state.startPosition) + ? zoomCursorClassName + : "react-stockcharts-default-cursor"; + + return ( + + ); + } + + private readonly handleDragEnd = () => { + + if (!this.dragHappened) { + if (this.clicked) { + const e = d3Event; + const mouseXY = this.mouseInteraction + ? mouse(this.node) + : touches(this.node)[0]; + const { onDoubleClick } = this.props; + + onDoubleClick(mouseXY, e); + } else { + this.clicked = true; + setTimeout(() => { + this.clicked = false; + }, 300); + } + } + + select(d3Window(this.node)) + .on(MOUSEMOVE, null) + .on(MOUSEUP, null) + .on(TOUCHMOVE, null) + .on(TOUCHEND, null); + + this.setState({ + startPosition: null, + }); + } + + private readonly handleDrag = () => { + const { startPosition } = this.state; + const { getMouseDelta, inverted } = this.props; + + this.dragHappened = true; + if (isDefined(startPosition)) { + const { startScale } = startPosition; + const { startXY } = startPosition; + + const mouseXY = this.mouseInteraction + ? mouse(this.node) + : touches(this.node)[0]; + + const diff = getMouseDelta(startXY, mouseXY); + + const center = mean(startScale.range()); + + const tempRange = startScale.range() + .map((d) => inverted ? d - sign(d - center) * diff : d + sign(d - center) * diff); + + const newDomain = tempRange.map(startScale.invert); + + if (sign(last(startScale.range()) - first(startScale.range())) === sign(last(tempRange) - first(tempRange))) { + + const { axisZoomCallback } = this.props; + axisZoomCallback(newDomain); + } + } + } + + private readonly handleDragStartTouch = (e) => { + this.mouseInteraction = false; + + const { getScale, getMoreProps } = this.props; + const startScale = getScale(getMoreProps()); + this.dragHappened = false; + + if (e.touches.length === 1 && startScale.invert) { + select(d3Window(this.node)) + .on(TOUCHMOVE, this.handleDrag) + .on(TOUCHEND, this.handleDragEnd); + + const startXY = touchPosition(getTouchProps(e.touches[0]), e); + + this.setState({ + startPosition: { + startXY, + startScale, + }, + }); + } + } + + private readonly handleDragStartMouse = (e) => { + this.mouseInteraction = true; + + const { getScale, getMoreProps } = this.props; + const startScale = getScale(getMoreProps()); + this.dragHappened = false; + + if (startScale.invert) { + select(d3Window(this.node)) + .on(MOUSEMOVE, this.handleDrag, false) + .on(MOUSEUP, this.handleDragEnd, false); + + const startXY = mousePosition(e); + + this.setState({ + startPosition: { + startXY, + startScale, + }, + }); + } + e.preventDefault(); + } + + private readonly handleRightClick = (e) => { + e.stopPropagation(); + e.preventDefault(); + + const { onContextMenu } = this.props; + + const mouseXY = mousePosition(e, this.node.getBoundingClientRect()); + + select(d3Window(this.node)) + .on(MOUSEMOVE, null) + .on(MOUSEUP, null); + this.setState({ + startPosition: null, + }); + + onContextMenu(mouseXY, e); + } + + private readonly saveNode = (node) => { + this.node = node; + } +} diff --git a/packages/react-financial-charts/src/axes/LICENSE b/packages/react-financial-charts/src/axes/LICENSE new file mode 100644 index 000000000..68b028aa2 --- /dev/null +++ b/packages/react-financial-charts/src/axes/LICENSE @@ -0,0 +1,28 @@ +Implementation of AxisTicks and AxisLine +derived from d3's axis.js +Copyright (c) 2010-2015, Michael Bostock +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* The name Michael Bostock may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/react-financial-charts/src/axes/XAxis.tsx b/packages/react-financial-charts/src/axes/XAxis.tsx new file mode 100644 index 000000000..bcfa7d6ae --- /dev/null +++ b/packages/react-financial-charts/src/axes/XAxis.tsx @@ -0,0 +1,127 @@ +import * as PropTypes from "prop-types"; +import * as React from "react"; +import Axis from "./Axis"; + +interface XAxisProps { + readonly axisAt: number | "top" | "bottom" | "middle"; + readonly orient: "top" | "bottom"; + readonly innerTickSize?: number; + readonly outerTickSize?: number; + readonly tickFormat?: any; // func + readonly tickPadding?: number; + readonly tickSize?: number; + readonly ticks?: number; + readonly tickValues?: number[]; + readonly showTicks?: boolean; + readonly className?: string; + readonly zoomEnabled?: boolean; + readonly onContextMenu?: any; // func + readonly onDoubleClick?: any; // func + readonly getMouseDelta?: (startXY: number[], mouseXY: number[]) => number; + readonly xZoomHeight?: number; +} + +export class XAxis extends React.Component { + + public static defaultProps = { + showTicks: true, + showTickLabel: true, + showDomain: true, + className: "react-stockcharts-x-axis", + ticks: 10, + outerTickSize: 0, + fill: "none", + stroke: "#000000", + strokeWidth: 1, + opacity: 1, + domainClassName: "react-stockcharts-axis-domain", + innerTickSize: 5, + tickPadding: 6, + tickStroke: "#000000", + tickStrokeOpacity: 1, + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 12, + fontWeight: 400, + xZoomHeight: 25, + zoomEnabled: true, + getMouseDelta: (startXY, mouseXY) => startXY[0] - mouseXY[0], + }; + + public static contextTypes = { + chartConfig: PropTypes.object.isRequired, + xAxisZoom: PropTypes.func.isRequired, + }; + + public render() { + const { + getMouseDelta = XAxis.defaultProps.getMouseDelta, + zoomEnabled, + ...rest + } = this.props; + + const { ...moreProps } = helper(this.props, this.context); + + return ( + + ); + } + + private readonly axisZoomCallback = (newXDomain) => { + const { xAxisZoom } = this.context; + + xAxisZoom(newXDomain); + } +} + +function helper(props: XAxisProps, context) { + const { + axisAt, + xZoomHeight = XAxis.defaultProps.xZoomHeight, + orient, + } = props; + const { chartConfig: { width, height } } = context; + + let axisLocation; + const x = 0; + const w = width; + const h = xZoomHeight; + + if (axisAt === "top") { + axisLocation = 0; + } else if (axisAt === "bottom") { + axisLocation = height; + } else if (axisAt === "middle") { + axisLocation = (height) / 2; + } else { + axisLocation = axisAt; + } + + const y = (orient === "top") ? -xZoomHeight : 0; + + return { + transform: [0, axisLocation], + range: [0, width], + getScale: getXScale, + bg: { x, y, h, w }, + }; +} + +function getXScale(moreProps) { + const { xScale: scale, width } = moreProps; + + if (scale.invert) { + const trueRange = [0, width]; + const trueDomain = trueRange.map(scale.invert); + return scale.copy() + .domain(trueDomain) + .range(trueRange); + } + + return scale; +} diff --git a/packages/react-financial-charts/src/axes/YAxis.tsx b/packages/react-financial-charts/src/axes/YAxis.tsx new file mode 100644 index 000000000..a6f72f0a0 --- /dev/null +++ b/packages/react-financial-charts/src/axes/YAxis.tsx @@ -0,0 +1,127 @@ +import * as PropTypes from "prop-types"; +import * as React from "react"; +import Axis from "./Axis"; + +interface YAxisProps { + readonly axisAt: number | "left" | "right" | "middle"; + readonly orient: "left" | "right"; + readonly innerTickSize?: number; + readonly outerTickSize?: number; + readonly tickFormat?: any; // func + readonly tickPadding?: number; + readonly tickSize?: number; + readonly ticks?: number; + readonly yZoomWidth?: number; + readonly tickValues?: number[]; + readonly showTicks?: boolean; + readonly className?: string; + readonly zoomEnabled?: boolean; + readonly onContextMenu?: any; // func + readonly onDoubleClick?: any; // func + readonly getMouseDelta?: (startXY: number[], mouseXY: number[]) => number; +} + +export class YAxis extends React.Component { + + public static defaultProps = { + showTicks: true, + showTickLabel: true, + showDomain: true, + className: "react-stockcharts-y-axis", + ticks: 10, + outerTickSize: 0, + domainClassName: "react-stockcharts-axis-domain", + fill: "none", + stroke: "#000000", + strokeWidth: 1, + opacity: 1, + innerTickSize: 5, + tickPadding: 6, + tickStroke: "#000000", + tickStrokeOpacity: 1, + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 12, + fontWeight: 400, + yZoomWidth: 40, + zoomEnabled: true, + getMouseDelta: (startXY, mouseXY) => startXY[1] - mouseXY[1], + }; + + public static contextTypes = { + yAxisZoom: PropTypes.func.isRequired, + chartId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + chartConfig: PropTypes.object.isRequired, + }; + + public render() { + + const { + getMouseDelta = YAxis.defaultProps.getMouseDelta, + ...rest + } = this.props; + + const { zoomEnabled, ...moreProps } = helper(this.props, this.context); + + return ( + + ); + } + + private readonly axisZoomCallback = (newYDomain) => { + const { chartId, yAxisZoom } = this.context; + yAxisZoom(chartId, newYDomain); + } +} + +function helper(props: YAxisProps, context) { + const { + axisAt, + yZoomWidth = YAxis.defaultProps.yZoomWidth, + orient, + } = props; + const { chartConfig: { width, height } } = context; + + let axisLocation; + const y = 0; + const w = yZoomWidth; + const h = height; + + if (axisAt === "left") { + axisLocation = 0; + } else if (axisAt === "right") { + axisLocation = width; + } else if (axisAt === "middle") { + axisLocation = (width) / 2; + } else { + axisLocation = axisAt; + } + + const x = (orient === "left") ? -yZoomWidth : 0; + + return { + transform: [axisLocation, 0], + range: [0, height], + getScale: getYScale, + bg: { x, y, h, w }, + zoomEnabled: context.chartConfig.yPan, + }; +} + +function getYScale(moreProps) { + const { yScale: scale, flipYScale, height } = moreProps.chartConfig; + if (scale.invert) { + const trueRange = flipYScale ? [0, height] : [height, 0]; + const trueDomain = trueRange.map(scale.invert); + return scale.copy() + .domain(trueDomain) + .range(trueRange); + } + return scale; +} diff --git a/packages/react-financial-charts/src/axes/index.ts b/packages/react-financial-charts/src/axes/index.ts new file mode 100644 index 000000000..d24baa079 --- /dev/null +++ b/packages/react-financial-charts/src/axes/index.ts @@ -0,0 +1,2 @@ +export * from "./XAxis"; +export * from "./YAxis"; diff --git a/packages/react-financial-charts/src/calculator/atr.ts b/packages/react-financial-charts/src/calculator/atr.ts new file mode 100644 index 000000000..e0ce5bb04 --- /dev/null +++ b/packages/react-financial-charts/src/calculator/atr.ts @@ -0,0 +1,69 @@ +import { sum } from "d3-array"; + +import { isDefined, last, slidingWindow } from "../utils"; + +import { ATR as defaultOptions } from "./defaultOptionsForComputation"; + +export default function () { + + let options = defaultOptions; + let source = (d) => ({ open: d.open, high: d.high, low: d.low, close: d.close }); + + function calculator(data) { + const { windowSize } = options; + + const trueRangeAlgorithm = slidingWindow() + .windowSize(2) + // @ts-ignore + .source(source) + .undefinedValue((d) => d.high - d.low) // the first TR value is simply the High minus the Low + .accumulator((values) => { + const prev = values[0]; + const d = values[1]; + return Math.max(d.high - d.low, + d.high - prev.close, + d.low - prev.close); + }); + + let prevATR; + + const atrAlgorithm = slidingWindow() + .skipInitial(1) // trueRange starts from index 1 so ATR starts from 1 + // @ts-ignore + .windowSize(windowSize) + .accumulator((values) => { + const tr = last(values); + const atr = isDefined(prevATR) + ? ((prevATR * (windowSize - 1)) + tr) / windowSize + : sum(values) / windowSize; + + prevATR = atr; + return atr; + }); + + const newData = atrAlgorithm(trueRangeAlgorithm(data)); + + return newData; + } + calculator.undefinedLength = function () { + const { windowSize } = options; + return windowSize - 1; + }; + calculator.options = function (x) { + if (!arguments.length) { + return options; + } + options = { ...defaultOptions, ...x }; + return calculator; + }; + + calculator.source = function (x) { + if (!arguments.length) { + return source; + } + source = x; + return calculator; + }; + + return calculator; +} diff --git a/packages/react-financial-charts/src/calculator/bollingerband.ts b/packages/react-financial-charts/src/calculator/bollingerband.ts new file mode 100644 index 000000000..9c72aeeb7 --- /dev/null +++ b/packages/react-financial-charts/src/calculator/bollingerband.ts @@ -0,0 +1,84 @@ +/* +https://github.com/ScottLogic/d3fc/blob/master/src/indicator/algorithm/calculator/bollingerBands.js + +The MIT License (MIT) + +Copyright (c) 2014-2015 Scott Logic Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +import { deviation, mean } from "d3-array"; + +import { last, path, slidingWindow, zipper } from "../utils"; +import ema from "./ema"; + +import { BollingerBand as defaultOptions } from "./defaultOptionsForComputation"; + +export default function () { + let options = defaultOptions; + + function calculator(data) { + const { windowSize, multiplier, movingAverageType, sourcePath } = options; + + // @ts-ignore + const source = path(sourcePath); + + const meanAlgorithm = movingAverageType === "ema" + ? ema().options({ windowSize, sourcePath }) + : slidingWindow() + .windowSize(windowSize) + // @ts-ignore + .accumulator((values) => mean(values)) + .sourcePath(sourcePath); + + const bollingerBandAlgorithm = slidingWindow() + .windowSize(windowSize) + // @ts-ignore + .accumulator((values) => { + const avg = last(values).mean; + const stdDev = deviation(values, (each) => source(each.datum)); + return { + top: avg + multiplier * stdDev, + middle: avg, + bottom: avg - multiplier * stdDev, + }; + }); + + const zip = zipper() + .combine((datum, meanValue) => ({ datum, meanValue })); + + // @ts-ignore + const tuples = zip(data, meanAlgorithm(data)); + return bollingerBandAlgorithm(tuples); + } + calculator.undefinedLength = function () { + const { windowSize } = options; + return windowSize - 1; + }; + calculator.options = function (x) { + if (!arguments.length) { + return options; + } + options = { ...defaultOptions, ...x }; + return calculator; + }; + + return calculator; +} diff --git a/packages/react-financial-charts/src/calculator/change.ts b/packages/react-financial-charts/src/calculator/change.ts new file mode 100644 index 000000000..6c869dbc4 --- /dev/null +++ b/packages/react-financial-charts/src/calculator/change.ts @@ -0,0 +1,36 @@ +import { slidingWindow } from "../utils"; +import { Change as defaultOptions } from "./defaultOptionsForComputation"; + +export default function () { + let options = defaultOptions; + + function calculator(data) { + const { sourcePath } = options; + + const algo = slidingWindow() + .windowSize(2) + // @ts-ignore + .sourcePath(sourcePath) + .accumulator(([prev, curr]) => { + const absoluteChange = curr - prev; + const percentChange = absoluteChange * 100 / prev; + return { absoluteChange, percentChange }; + }); + + const newData = algo(data); + + return newData; + } + calculator.undefinedLength = function () { + return 1; + }; + calculator.options = function (x) { + if (!arguments.length) { + return options; + } + options = { ...defaultOptions, ...x }; + return calculator; + }; + + return calculator; +} diff --git a/packages/react-financial-charts/src/calculator/compare.ts b/packages/react-financial-charts/src/calculator/compare.ts new file mode 100644 index 000000000..16c3b1a5e --- /dev/null +++ b/packages/react-financial-charts/src/calculator/compare.ts @@ -0,0 +1,58 @@ +import { head, isDefined, isNotDefined, path } from "../utils"; +import { Change as defaultOptions } from "./defaultOptionsForComputation"; + +export default function () { + let options = defaultOptions; + + function calculator(data) { + const { basePath, mainKeys, compareKeys } = options; + + // @ts-ignore + const base = path(basePath); + + const first = head(data); + const b = base(first); + + const firsts = {}; + + const compareData = data.map((d) => { + const result = {}; + + mainKeys.forEach((key) => { + if (typeof d[key] === "object") { + // @ts-ignore + result[key] = {}; + Object.keys(d[key]).forEach((subkey) => { + // @ts-ignore + result[key][subkey] = (d[key][subkey] - b) / b; + }); + } else { + // @ts-ignore + result[key] = (d[key] - b) / b; + } + }); + + compareKeys.forEach((key) => { + if (isDefined(d[key]) && isNotDefined(firsts[key])) { + // @ts-ignore + firsts[key] = d[key]; + } + if (isDefined(d[key]) && isDefined(firsts[key])) { + // @ts-ignore + result[key] = (d[key] - firsts[key]) / firsts[key]; + } + }); + return result; + }); + + return compareData; + } + calculator.options = function (x) { + if (!arguments.length) { + return options; + } + options = { ...defaultOptions, ...x }; + return calculator; + }; + return calculator; +} diff --git a/packages/react-financial-charts/src/calculator/defaultOptionsForComputation.ts b/packages/react-financial-charts/src/calculator/defaultOptionsForComputation.ts new file mode 100644 index 000000000..5372ee57c --- /dev/null +++ b/packages/react-financial-charts/src/calculator/defaultOptionsForComputation.ts @@ -0,0 +1,116 @@ +export const BollingerBand = { + windowSize: 20, + // source: d => d.close, // "high", "low", "open", "close" + sourcePath: "close", + multiplier: 2, + movingAverageType: "sma", +}; + +export const ATR = { + windowSize: 14, +}; + +export const ForceIndex = { + sourcePath: "close", // "high", "low", "open", "close" + volumePath: "volume", +}; + +export const SmoothedForceIndex = { + sourcePath: "close", // "high", "low", "open", "close" + volumePath: "volume", + smoothingType: "ema", + smoothingWindow: 13, +}; + +export const Change = { + sourcePath: "close", // "high", "low", "open", "close" + basePath: "close", + mainKeys: [], + compareKeys: [], +}; + +export const Compare = { + basePath: "close", + mainKeys: ["open", "high", "low", "close"], + compareKeys: [], +}; + +export const ElderRay = { + windowSize: 13, + // source: d => d.close, // "high", "low", "open", "close" + sourcePath: "close", // "high", "low", "open", "close" + movingAverageType: "sma", +}; + +export const ElderImpulse = { + sourcePath: "close", // "high", "low", "open", "close" +}; + +export const SAR = { + accelerationFactor: 0.02, + maxAccelerationFactor: 0.2, +}; + +export const MACD = { + fast: 12, + slow: 26, + signal: 9, + // source: d => d.close, // "high", "low", "open", "close" + sourcePath: "close", +}; + +export const FullStochasticOscillator = { + windowSize: 12, + kWindowSize: 3, + dWindowSize: 3, +}; + +export const RSI = { + windowSize: 14, + // source: d => d.close, // "high", "low", "open", "close" + sourcePath: "close", // "high", "low", "open", "close" +}; + +export const EMA = { + // source: d => d.close, // "high", "low", "open", "close" + sourcePath: "close", + windowSize: 10, +}; + +export const SMA = { + // source: d => d.close, // "high", "low", "open", "close" + sourcePath: "close", + windowSize: 10, +}; + +export const WMA = { + // source: d => d.close, // "high", "low", "open", "close" + sourcePath: "close", + windowSize: 10, +}; + +export const TMA = { + // source: d => d.close, // "high", "low", "open", "close" + sourcePath: "close", + windowSize: 10, +}; + +export const Kagi = { + reversalType: "ATR", // "ATR", "FIXED" + windowSize: 14, + reversal: 2, + sourcePath: "close", // "high", "low", "open", "close" +}; + +export const Renko = { + reversalType: "ATR", // "ATR", "FIXED" + windowSize: 14, + fixedBrickSize: 2, + sourcePath: "high/low", // "close", "high/low" +}; + +export const PointAndFigure = { + boxSize: 0.5, + reversal: 3, + sourcePath: "high/low", // "close", "high/low" +}; diff --git a/packages/react-financial-charts/src/calculator/elderRay.ts b/packages/react-financial-charts/src/calculator/elderRay.ts new file mode 100644 index 000000000..b2a349c5b --- /dev/null +++ b/packages/react-financial-charts/src/calculator/elderRay.ts @@ -0,0 +1,80 @@ +/* +https://github.com/ScottLogic/d3fc/blob/master/src/indicator/algorithm/calculator/elderRay.js + +The MIT License (MIT) + +Copyright (c) 2014-2015 Scott Logic Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +import { mean } from "d3-array"; + +import ema from "./ema"; + +import { isDefined, slidingWindow, zipper } from "../utils"; +import { ElderRay as defaultOptions } from "./defaultOptionsForComputation"; + +export default function () { + + let options = defaultOptions; + let ohlc = (d) => ({ open: d.open, high: d.high, low: d.low, close: d.close }); + + function calculator(data) { + const { windowSize, sourcePath, movingAverageType } = options; + + const meanAlgorithm = movingAverageType === "ema" + ? ema().options({ windowSize, sourcePath }) + : slidingWindow() + .windowSize(windowSize) + // @ts-ignore + .accumulator((values) => mean(values)).sourcePath(sourcePath); + + const zip = zipper() + .combine((datum, meanValue) => { + const bullPower = isDefined(meanValue) ? ohlc(datum).high - meanValue : undefined; + const bearPower = isDefined(meanValue) ? ohlc(datum).low - meanValue : undefined; + return { bullPower, bearPower }; + }); + + // @ts-ignore + const newData = zip(data, meanAlgorithm(data)); + return newData; + } + calculator.undefinedLength = function () { + const { windowSize } = options; + return windowSize - 1; + }; + calculator.ohlc = function (x) { + if (!arguments.length) { + return ohlc; + } + ohlc = x; + return calculator; + }; + calculator.options = function (x) { + if (!arguments.length) { + return options; + } + options = { ...defaultOptions, ...x }; + return calculator; + }; + + return calculator; +} diff --git a/packages/react-financial-charts/src/calculator/ema.ts b/packages/react-financial-charts/src/calculator/ema.ts new file mode 100644 index 000000000..1a0c9f35c --- /dev/null +++ b/packages/react-financial-charts/src/calculator/ema.ts @@ -0,0 +1,77 @@ +/* +https://github.com/ScottLogic/d3fc/blob/master/src/indicator/algorithm/calculator/exponentialMovingAverage.js + +The MIT License (MIT) + +Copyright (c) 2014-2015 Scott Logic Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +import { isNotDefined, path } from "../utils"; +import { EMA as defaultOptions } from "./defaultOptionsForComputation"; + +export default function () { + + let options = defaultOptions; + + function calculator(data) { + const { windowSize, sourcePath } = options; + + // @ts-ignore + const source = path(sourcePath); + const alpha = 2 / (windowSize + 1); + let previous; + let initialAccumulator = 0; + let skip = 0; + + return data.map(function (d, i) { + const v = source(d, i); + if (isNotDefined(previous) && isNotDefined(v)) { + skip++; + return undefined; + } else if (i < windowSize + skip - 1) { + initialAccumulator += v; + return undefined; + } else if (i === windowSize + skip - 1) { + initialAccumulator += v; + const initialValue = initialAccumulator / windowSize; + previous = initialValue; + return initialValue; + } else { + const nextValue = v * alpha + (1 - alpha) * previous; + previous = nextValue; + return nextValue; + } + }); + } + calculator.undefinedLength = function () { + const { windowSize } = options; + return windowSize - 1; + }; + calculator.options = function (x) { + if (!arguments.length) { + return options; + } + options = { ...defaultOptions, ...x }; + return calculator; + }; + + return calculator; +} diff --git a/packages/react-financial-charts/src/calculator/forceIndex.ts b/packages/react-financial-charts/src/calculator/forceIndex.ts new file mode 100644 index 000000000..5c26dfb6f --- /dev/null +++ b/packages/react-financial-charts/src/calculator/forceIndex.ts @@ -0,0 +1,38 @@ +import { path, slidingWindow } from "../utils"; +import { ForceIndex as defaultOptions } from "./defaultOptionsForComputation"; + +export default function () { + + let options = defaultOptions; + + function calculator(data) { + const { sourcePath, volumePath } = options; + + // @ts-ignore + const source = path(sourcePath); + + // @ts-ignore + const volume = path(volumePath); + + const forceIndexCalulator = slidingWindow() + .windowSize(2) + // @ts-ignore + .accumulator(([prev, curr]) => (source(curr) - source(prev)) * volume(curr)); + + const forceIndex = forceIndexCalulator(data); + + return forceIndex; + } + calculator.undefinedLength = function () { + return 2; + }; + calculator.options = function (x) { + if (!arguments.length) { + return options; + } + options = { ...defaultOptions, ...x }; + return calculator; + }; + + return calculator; +} diff --git a/packages/react-financial-charts/src/calculator/heikinAshi.ts b/packages/react-financial-charts/src/calculator/heikinAshi.ts new file mode 100644 index 000000000..7add11109 --- /dev/null +++ b/packages/react-financial-charts/src/calculator/heikinAshi.ts @@ -0,0 +1,36 @@ +import { identity, mappedSlidingWindow } from "../utils"; + +export default function () { + + let source = identity; + + function calculator(data) { + const algorithm = mappedSlidingWindow() + .windowSize(2) + // @ts-ignore + .undefinedValue(({ open, high, low, close }) => { + close = (open + high + low + close) / 4; + return { open, high, low, close }; + }) + .accumulator(([prev, now]) => { + // console.log(prev, now); + const { date, volume } = now; + const close = (now.open + now.high + now.low + now.close) / 4; + const open = (prev.open + prev.close) / 2; + const high = Math.max(open, now.high, close); + const low = Math.min(open, now.low, close); + return { date, open, high, low, close, volume }; + }); + + return algorithm(data); + } + calculator.source = function (x) { + if (!arguments.length) { + return source; + } + source = x; + return calculator; + }; + + return calculator; +} diff --git a/packages/react-financial-charts/src/calculator/index.ts b/packages/react-financial-charts/src/calculator/index.ts new file mode 100644 index 000000000..aae131da9 --- /dev/null +++ b/packages/react-financial-charts/src/calculator/index.ts @@ -0,0 +1,19 @@ +export { default as ema } from "./ema"; +export { default as sma } from "./sma"; +export { default as wma } from "./wma"; +export { default as tma } from "./tma"; +export { default as bollingerband } from "./bollingerband"; +export { default as heikinAshi } from "./heikinAshi"; +export { default as kagi } from "./kagi"; +export { default as pointAndFigure } from "./pointAndFigure"; +export { default as renko } from "./renko"; +export { default as macd } from "./macd"; +export { default as rsi } from "./rsi"; +export { default as sto } from "./sto"; +export { default as atr } from "./atr"; +export { default as forceIndex } from "./forceIndex"; +export { default as smoothedForceIndex } from "./smoothedForceIndex"; +export { default as elderRay } from "./elderRay"; +export { default as sar } from "./sar"; +export { default as compare } from "./compare"; +export { default as change } from "./change"; diff --git a/packages/react-financial-charts/src/calculator/kagi.ts b/packages/react-financial-charts/src/calculator/kagi.ts new file mode 100644 index 000000000..5adf8e9ce --- /dev/null +++ b/packages/react-financial-charts/src/calculator/kagi.ts @@ -0,0 +1,226 @@ +import { functor, isNotDefined, merge, path } from "../utils"; +import atr from "./atr"; + +import { Kagi as defaultOptions } from "./defaultOptionsForComputation"; + +export default function () { + + let options = defaultOptions; + let dateAccessor = (d) => d.date; + let dateMutator = (d, date) => { d.date = date; }; + + function calculator(data) { + const { reversalType, windowSize, reversal, sourcePath } = options; + + // @ts-ignore + const source = path(sourcePath); + let reversalThreshold; + + if (reversalType === "ATR") { + // calculateATR(rawData, period); + const atrAlgorithm = atr().options({ windowSize }); + + const atrCalculator = merge() + .algorithm(atrAlgorithm) + // @ts-ignore + .merge((d, c) => { d["atr" + windowSize] = c; }); + + atrCalculator(data); + reversalThreshold = (d) => d["atr" + windowSize]; + } else { + reversalThreshold = functor(reversal); + } + + const kagiData: any[] = []; + + let prevPeak; + let prevTrough; + let direction; + + let line: { + added?: any; + date?: any; + from?: any; + to?: any; + open?: number; + high?: number; + low?: number; + close?: number; + current?: any; + changePoint?: any; + changeTo?: any; + volume?: number; + reverseAt?: any; + startAs?: any; + startOfYear?: any; + startOfQuarter?: any; + startOfMonth?: any; + startOfWeek?: any; + } = {}; + + data.forEach(function (d) { + if (isNotDefined(line.from)) { + dateMutator(line, dateAccessor(d)); + line.from = dateAccessor(d); + + if (!line.open) { line.open = d.open; } + line.high = d.high; + line.low = d.low; + if (!line.close) { line.close = source(d); } + line.startOfYear = d.startOfYear; + line.startOfQuarter = d.startOfQuarter; + line.startOfMonth = d.startOfMonth; + line.startOfWeek = d.startOfWeek; + } + + if (!line.startOfYear) { + line.startOfYear = d.startOfYear; + if (line.startOfYear) { + line.date = d.date; + // line.displayDate = d.displayDate; + } + } + + if (!line.startOfQuarter) { + line.startOfQuarter = d.startOfQuarter; + if (line.startOfQuarter && !line.startOfYear) { + line.date = d.date; + // line.displayDate = d.displayDate; + } + } + + if (!line.startOfMonth) { + line.startOfMonth = d.startOfMonth; + if (line.startOfMonth && !line.startOfQuarter) { + line.date = d.date; + // line.displayDate = d.displayDate; + } + } + if (!line.startOfWeek) { + line.startOfWeek = d.startOfWeek; + if (line.startOfWeek && !line.startOfMonth) { + line.date = d.date; + // line.displayDate = d.displayDate; + } + } + line.volume = (line.volume || 0) + d.volume; + // @ts-ignore + line.high = Math.max(line.high, d.high); + // @ts-ignore + line.low = Math.min(line.low, d.low); + line.to = dateAccessor(d); + + // @ts-ignore + const priceMovement = (source(d) - line.close); + + // console.log(source(d), priceMovement) + // @ts-ignore + if ((line.close >= line.open /* going up */ && priceMovement > 0 /* and moving in same direction */) + // @ts-ignore + || (line.close < line.open /* going down */ && priceMovement < 0 /* and moving in same direction */)) { + line.close = source(d); + // @ts-ignore + if (prevTrough && line.close < prevTrough) { + // going below the prevTrough, so change from yang to yin + // A yin line forms when a Kagi line breaks below the prior trough. + line.changePoint = prevTrough; + if (line.startAs !== "yin") { + line.changeTo = "yin"; + // line.startAs = "yang"; + } + } + + // @ts-ignore + if (prevPeak && line.close > prevPeak) { + // going above the prevPeak, so change from yin to yang + // A yang line forms when a Kagi line breaks above the prior peak + line.changePoint = prevPeak; + if (line.startAs !== "yang") { + line.changeTo = "yang"; + // line.startAs = "yin"; + } + } + // @ts-ignore + } else if ((line.close >= line.open /* going up */ + && priceMovement < 0 /* and moving in other direction */ + && Math.abs(priceMovement) > reversalThreshold(d) /* and the movement is big enough for reversal */) + // @ts-ignore + || (line.close < line.open /* going down */ + && priceMovement > 0 /* and moving in other direction */ + /* and the movement is big enough for reversal */ + && Math.abs(priceMovement) > reversalThreshold(d))) { + // reverse direction + const nextLineOpen = line.close; + + // @ts-ignore + direction = (line.close - line.open) / Math.abs(line.close - line.open); + + let nextChangePoint; + let nextChangeTo; + if (direction < 0 /* if direction so far has been -ve*/) { + // compare with line.close becomes prevTrough + if (isNotDefined(prevPeak)) { prevPeak = line.open; } + prevTrough = line.close; + if (source(d) > prevPeak) { + nextChangePoint = prevPeak; + nextChangeTo = "yang"; + } + } else { + if (isNotDefined(prevTrough)) { prevTrough = line.open; } + prevPeak = line.close; + if (source(d) < prevTrough) { + nextChangePoint = prevTrough; + nextChangeTo = "yin"; + } + } + if (isNotDefined(line.startAs)) { + line.startAs = direction > 0 ? "yang" : "yin"; + } + + const startAs = line.changeTo || line.startAs; + line.added = true; + kagiData.push(line); + direction = -1 * direction; // direction is reversed + + line = { ...line }; + line.open = nextLineOpen; + line.close = source(d); + line.startAs = startAs; + line.changePoint = nextChangePoint; + line.changeTo = nextChangeTo; + line.added = false; + line.from = undefined; + line.volume = 0; + } else { + // console.log("MOVING IN REV DIR BUT..", line.open, line.close, source(d)); + } + line.current = source(d); + // @ts-ignore + let dir = line.close - line.open; + dir = dir === 0 ? 1 : dir / Math.abs(dir); + // @ts-ignore + line.reverseAt = dir > 0 ? line.close - reversalThreshold(d) : line.open - reversalThreshold(d); + }); + if (!line.added) { kagiData.push(line); } + + return kagiData; + } + calculator.options = function (x) { + if (!arguments.length) { + return options; + } + options = { ...defaultOptions, ...x }; + return calculator; + }; + calculator.dateMutator = function (x) { + if (!arguments.length) { return dateMutator; } + dateMutator = x; + return calculator; + }; + calculator.dateAccessor = function (x) { + if (!arguments.length) { return dateAccessor; } + dateAccessor = x; + return calculator; + }; + return calculator; +} diff --git a/packages/react-financial-charts/src/calculator/macd.ts b/packages/react-financial-charts/src/calculator/macd.ts new file mode 100644 index 000000000..fb6b20eac --- /dev/null +++ b/packages/react-financial-charts/src/calculator/macd.ts @@ -0,0 +1,85 @@ +/* +https://github.com/ScottLogic/d3fc/blob/master/src/indicator/algorithm/calculator/macd.js + +The MIT License (MIT) + +Copyright (c) 2014-2015 Scott Logic Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +import ema from "./ema"; + +import { isDefined, zipper } from "../utils"; +import { MACD as defaultOptions } from "./defaultOptionsForComputation"; + +export default function () { + let options = defaultOptions; + + function calculator(data) { + const { fast, slow, signal, sourcePath } = options; + + const fastEMA = ema() + .options({ windowSize: fast, sourcePath }); + + const slowEMA = ema() + .options({ windowSize: slow, sourcePath }); + + const signalEMA = ema() + .options({ windowSize: signal, sourcePath: undefined }); + + const macdCalculator = zipper() + .combine((fastEMAValue, slowEMAValue) => { + return (isDefined(fastEMAValue) && isDefined(slowEMAValue)) ? fastEMAValue - slowEMAValue : undefined; + }); + + // @ts-ignore + const macdArray = macdCalculator(fastEMA(data), slowEMA(data)); + + const undefinedArray = new Array(slow); + // @ts-ignore + const signalArray = undefinedArray.concat(signalEMA(macdArray.slice(slow))); + + const zip = zipper() + .combine((macdValue, signalValue) => ({ + macdValue, + signalValue, + divergence: (isDefined(macdValue) && isDefined(signalValue)) ? macdValue - signalValue : undefined, + })); + + // @ts-ignore + const macd = zip(macdArray, signalArray); + + return macd; + } + + calculator.undefinedLength = function () { + const { slow, signal } = options; + return slow + signal - 1; + }; + calculator.options = function (x) { + if (!arguments.length) { + return options; + } + options = { ...defaultOptions, ...x }; + return calculator; + }; + + return calculator; +} diff --git a/packages/react-financial-charts/src/calculator/pointAndFigure.ts b/packages/react-financial-charts/src/calculator/pointAndFigure.ts new file mode 100644 index 000000000..0edafa439 --- /dev/null +++ b/packages/react-financial-charts/src/calculator/pointAndFigure.ts @@ -0,0 +1,244 @@ +import { isNotDefined } from "../utils"; +import { PointAndFigure as defaultOptions } from "./defaultOptionsForComputation"; + +function createBox(d, dateAccessor, dateMutator) { + const box = { + open: d.open, + fromDate: dateAccessor(d), + toDate: dateAccessor(d), + startOfYear: d.startOfYear, + startOfQuarter: d.startOfQuarter, + startOfMonth: d.startOfMonth, + startOfWeek: d.startOfWeek, + }; + dateMutator(box, dateAccessor(d)); + return box; +} + +function updateColumns(columnData, dateAccessor, dateMutator) { + columnData.forEach(function (d) { + // var lastBox = d.boxes[d.boxes.length - 1]; + + d.startOfYear = false; + d.startOfQuarter = false; + d.startOfMonth = false; + d.startOfWeek = false; + + d.boxes.forEach(function (eachBox) { + if (isNotDefined(d.open)) { d.open = eachBox.open; } + d.close = eachBox.close; + d.high = Math.max(d.open, d.close); + d.low = Math.min(d.open, d.close); + + if (isNotDefined(d.fromDate)) { d.fromDate = eachBox.fromDate; } + if (isNotDefined(d.date)) { d.date = eachBox.date; } + d.toDate = eachBox.toDate; + + if (eachBox.startOfYear) { + d.startOfYear = d.startOfYear || eachBox.startOfYear; + d.startOfQuarter = eachBox.startOfQuarter; + d.startOfMonth = eachBox.startOfMonth; + d.startOfWeek = eachBox.startOfWeek; + + dateMutator(d, dateAccessor(eachBox)); + } + if (d.startOfQuarter !== true && eachBox.startOfQuarter) { + d.startOfQuarter = eachBox.startOfQuarter; + d.startOfMonth = eachBox.startOfMonth; + d.startOfWeek = eachBox.startOfWeek; + // d.displayDate = eachBox.displayDate; + dateMutator(d, dateAccessor(eachBox)); + } + if (d.startOfMonth !== true && eachBox.startOfMonth) { + d.startOfMonth = eachBox.startOfMonth; + d.startOfWeek = eachBox.startOfWeek; + // d.displayDate = eachBox.displayDate; + dateMutator(d, dateAccessor(eachBox)); + } + if (d.startOfWeek !== true && eachBox.startOfWeek) { + d.startOfWeek = eachBox.startOfWeek; + // d.displayDate = eachBox.displayDate; + dateMutator(d, dateAccessor(eachBox)); + } + }); + + }); + + // console.table(columnData); + // console.table(rawData); + return columnData; +} + +export default function () { + let options = defaultOptions; + let dateAccessor = (d) => d.date; + let dateMutator = (d, date) => { d.date = date; }; + + function calculator(rawData) { + const { reversal, boxSize, sourcePath } = options; + + const source = sourcePath === "high/low" + ? (d) => ({ high: d.high, low: d.low }) + : (d) => ({ high: d.close, low: d.close }); + + const pricingMethod = source; + const columnData: any[] = []; + + // @ts-ignore + let column: { + boxes: any[]; + direction: any; + open: number; + close?: number; + } = { + boxes: [], + open: rawData[0].open, + }; + let box = createBox(rawData[0], dateAccessor, dateMutator); + + columnData.push(column); + + rawData.forEach(function (d) { + // @ts-ignore + column.volume = (column.volume || 0) + d.volume; + + if (!box.startOfYear) { + box.startOfYear = d.startOfYear; + if (box.startOfYear) { + dateMutator(box, dateAccessor(d)); + // box.displayDate = d.displayDate; + } + } + + if (!box.startOfYear && !box.startOfQuarter) { + box.startOfQuarter = d.startOfQuarter; + if (box.startOfQuarter && !box.startOfYear) { + dateMutator(box, dateAccessor(d)); + // box.displayDate = d.displayDate; + } + } + + if (!box.startOfQuarter && !box.startOfMonth) { + box.startOfMonth = d.startOfMonth; + if (box.startOfMonth && !box.startOfQuarter) { + dateMutator(box, dateAccessor(d)); + // box.displayDate = d.displayDate; + } + } + if (!box.startOfMonth && !box.startOfWeek) { + box.startOfWeek = d.startOfWeek; + if (box.startOfWeek && !box.startOfMonth) { + dateMutator(box, dateAccessor(d)); + // box.displayDate = d.displayDate; + } + } + + if (columnData.length === 1 && column.boxes.length === 0) { + const upwardMovement = (Math.max((pricingMethod(d).high - column.open), 0)); // upward movement + const downwardMovement = Math.abs(Math.min((column.open - pricingMethod(d).low), 0)); // downward movement + column.direction = upwardMovement > downwardMovement ? 1 : -1; + if (boxSize * reversal < upwardMovement + || boxSize * reversal < downwardMovement) { + // enough movement to trigger a reversal + box.toDate = dateAccessor(d); + box.open = column.open; + const noOfBoxes = column.direction > 0 + ? Math.floor(upwardMovement / boxSize) + : Math.floor(downwardMovement / boxSize); + for (let i = 0; i < noOfBoxes; i++) { + // @ts-ignore + box.close = box.open + column.direction * boxSize; + // @ts-ignore + const prevBoxClose = box.close; + column.boxes.push(box); + box = createBox(box, dateAccessor, dateMutator); + // box = cloneMe(box); + box.open = prevBoxClose; + } + box.fromDate = dateAccessor(d); + // @ts-ignore + box.date = dateAccessor(d); + } + } else { + // one or more boxes already formed in the current column + const upwardMovement = (Math.max((pricingMethod(d).high - box.open), 0)); // upward movement + const downwardMovement = Math.abs(Math.min((pricingMethod(d).low - box.open), 0)); // downward movement + + if ((column.direction > 0 && upwardMovement > boxSize) /* rising column AND box can be formed */ + || (column.direction < 0 && downwardMovement > boxSize) /* falling column AND box can be formed */) { + // form another box + // @ts-ignore + box.close = box.open + column.direction * boxSize; + box.toDate = dateAccessor(d); + + // @ts-ignore + const prevBoxClose = box.close; + column.boxes.push(box); + box = createBox(d, dateAccessor, dateMutator); + box.open = prevBoxClose; + box.fromDate = dateAccessor(d); + dateMutator(box, dateAccessor(d)); + } else if ( + /* rising column and there is downward movement to trigger a reversal */ + (column.direction > 0 && downwardMovement > boxSize * reversal) + /* falling column and there is downward movement to trigger a reversal */ + || (column.direction < 0 && upwardMovement > boxSize * reversal)) { + // reversal + + box.open = box.open + -1 * column.direction * boxSize; + box.toDate = dateAccessor(d); + // box.displayDate = d.displayDate; + dateMutator(box, dateAccessor(d)); + // box.startOfYear = d.startOfYear; + // box.startOfQuarter = d.startOfQuarter; + // box.startOfMonth = d.startOfMonth; + // box.startOfWeek = d.startOfWeek; + // console.table(column.boxes); + // var idx = index + 1; + column = { + boxes: [], + // @ts-ignore + volume: 0, + direction: -1 * column.direction, + }; + const noOfBoxes = column.direction > 0 + ? Math.floor(upwardMovement / boxSize) + : Math.floor(downwardMovement / boxSize); + for (let i = 0; i < noOfBoxes; i++) { + // @ts-ignore + box.close = box.open + column.direction * boxSize; + // @ts-ignore + const prevBoxClose = box.close; + column.boxes.push(box); + box = createBox(d, dateAccessor, dateMutator); + box.open = prevBoxClose; + } + + columnData.push(column); + } + } + }); + updateColumns(columnData, dateAccessor, dateMutator); + + return columnData; + } + calculator.options = function (x) { + if (!arguments.length) { + return options; + } + options = { ...defaultOptions, ...x }; + return calculator; + }; + calculator.dateMutator = function (x) { + if (!arguments.length) { return dateMutator; } + dateMutator = x; + return calculator; + }; + calculator.dateAccessor = function (x) { + if (!arguments.length) { return dateAccessor; } + dateAccessor = x; + return calculator; + }; + + return calculator; +} diff --git a/packages/react-financial-charts/src/calculator/renko.ts b/packages/react-financial-charts/src/calculator/renko.ts new file mode 100644 index 000000000..0e31a0469 --- /dev/null +++ b/packages/react-financial-charts/src/calculator/renko.ts @@ -0,0 +1,217 @@ + +import { functor, isNotDefined, merge } from "../utils"; + +import atr from "./atr"; + +import { Renko as defaultOptions } from "./defaultOptionsForComputation"; + +export default function () { + let options = defaultOptions; + + let dateAccessor = (d) => d.date; + let dateMutator = (d, date) => { d.date = date; }; + + function calculator(rawData) { + const { reversalType, fixedBrickSize, sourcePath, windowSize } = options; + + const source = sourcePath === "high/low" + ? (d) => ({ high: d.high, low: d.low }) + : (d) => ({ high: d.close, low: d.close }); + + const pricingMethod = source; + let brickSize; + + if (reversalType === "ATR") { + // calculateATR(rawData, period); + const atrAlgorithm = atr().options({ windowSize }); + + const atrCalculator = merge() + .algorithm(atrAlgorithm) + // @ts-ignore + .merge((d, c) => { d["atr" + windowSize] = c; }); + + atrCalculator(rawData); + brickSize = (d) => d["atr" + windowSize]; + } else { + brickSize = functor(fixedBrickSize); + } + + const renkoData: any[] = []; + + let index = 0; + let prevBrickClose = rawData[index].open; + let prevBrickOpen = rawData[index].open; + let brick: { + added?: any; + date?: any; + direction?: any; + from?: any; + fromDate?: any; + to?: any; + toDate?: any; + open?: number; + high?: number; + low?: number; + close?: number; + fullyFormed?: any; + current?: any; + changePoint?: any; + changeTo?: any; + volume?: number; + reverseAt?: any; + startAs?: any; + startOfYear?: any; + startOfQuarter?: any; + startOfMonth?: any; + startOfWeek?: any; + } = {}; + let direction = 0; + + rawData.forEach(function (d, idx) { + if (isNotDefined(brick.from)) { + brick.high = d.high; + brick.low = d.low; + brick.startOfYear = d.startOfYear; + brick.startOfQuarter = d.startOfQuarter; + brick.startOfMonth = d.startOfMonth; + brick.startOfWeek = d.startOfWeek; + + brick.from = idx; + brick.fromDate = dateAccessor(d); + // indexMutator(brick, index++); + dateMutator(brick, dateAccessor(d)); + } + brick.volume = (brick.volume || 0) + d.volume; + + const prevCloseToHigh = (prevBrickClose - pricingMethod(d).high); + const prevCloseToLow = (prevBrickClose - pricingMethod(d).low); + const prevOpenToHigh = (prevBrickOpen - pricingMethod(d).high); + const prevOpenToLow = (prevBrickOpen - pricingMethod(d).low); + const priceMovement = Math.min( + Math.abs(prevCloseToHigh), + Math.abs(prevCloseToLow), + Math.abs(prevOpenToHigh), + Math.abs(prevOpenToLow)); + + // @ts-ignore + brick.high = Math.max(brick.high, d.high); + // @ts-ignore + brick.low = Math.min(brick.low, d.low); + + if (!brick.startOfYear) { + brick.startOfYear = d.startOfYear; + if (brick.startOfYear) { + dateMutator(brick, dateAccessor(d)); + // brick.displayDate = d.displayDate; + } + } + + if (!brick.startOfQuarter) { + brick.startOfQuarter = d.startOfQuarter; + if (brick.startOfQuarter && !brick.startOfYear) { + dateMutator(brick, dateAccessor(d)); + // brick.displayDate = d.displayDate; + } + } + + if (!brick.startOfMonth) { + brick.startOfMonth = d.startOfMonth; + if (brick.startOfMonth && !brick.startOfQuarter) { + dateMutator(brick, dateAccessor(d)); + // brick.displayDate = d.displayDate; + } + } + if (!brick.startOfWeek) { + brick.startOfWeek = d.startOfWeek; + if (brick.startOfWeek && !brick.startOfMonth) { + dateMutator(brick, dateAccessor(d)); + // brick.displayDate = d.displayDate; + } + } + + // d.brick = JSON.stringify(brick); + if (brickSize(d)) { + const noOfBricks = Math.floor(priceMovement / brickSize(d)); + + brick.open = (Math.abs(prevCloseToHigh) < Math.abs(prevOpenToHigh) + || Math.abs(prevCloseToLow) < Math.abs(prevOpenToLow)) + ? prevBrickClose + : prevBrickOpen; + + if (noOfBricks >= 1) { + let j = 0; + for (j = 0; j < noOfBricks; j++) { + // @ts-ignore + brick.close = (brick.open < pricingMethod(d).high) + // if brick open is less than current price it means it is green/hollow brick + ? brick.open + brickSize(d) + // @ts-ignore + : brick.open - brickSize(d); + // @ts-ignore + direction = brick.close > brick.open ? 1 : -1; + brick.direction = direction; + brick.to = idx; + brick.toDate = dateAccessor(d); + // brick.diff = brick.open - brick.close; + // brick.atr = d.atr; + brick.fullyFormed = true; + renkoData.push(brick); + + prevBrickClose = brick.close; + prevBrickOpen = brick.open; + + const newBrick = { + high: brick.high, + low: brick.low, + open: brick.close, + startOfYear: false, + startOfMonth: false, + startOfQuarter: false, + startOfWeek: false, + }; + brick = newBrick; + brick.from = idx; + brick.fromDate = dateAccessor(d); + // indexMutator(brick, index + j); + dateMutator(brick, dateAccessor(d)); + brick.volume = (brick.volume || 0) + d.volume; + } + index = index + j - 1; + brick = {}; + } else { + if (idx === rawData.length - 1) { + brick.close = direction > 0 ? pricingMethod(d).high : pricingMethod(d).low; + brick.to = idx; + brick.toDate = dateAccessor(d); + dateMutator(brick, dateAccessor(d)); + brick.fullyFormed = false; + renkoData.push(brick); + } + } + } + + }); + return renkoData; + + } + calculator.options = function (x) { + if (!arguments.length) { + return options; + } + options = { ...defaultOptions, ...x }; + return calculator; + }; + + calculator.dateMutator = function (x) { + if (!arguments.length) { return dateMutator; } + dateMutator = x; + return calculator; + }; + calculator.dateAccessor = function (x) { + if (!arguments.length) { return dateAccessor; } + dateAccessor = x; + return calculator; + }; + + return calculator; +} diff --git a/packages/react-financial-charts/src/calculator/rsi.ts b/packages/react-financial-charts/src/calculator/rsi.ts new file mode 100644 index 000000000..575aafced --- /dev/null +++ b/packages/react-financial-charts/src/calculator/rsi.ts @@ -0,0 +1,100 @@ +/* +https://github.com/ScottLogic/d3fc/blob/master/src/indicator/algorithm/calculator/relativeStrengthIndex.js + +The MIT License (MIT) + +Copyright (c) 2014-2015 Scott Logic Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +import { mean } from "d3-array"; + +import { isDefined, last, path, slidingWindow } from "../utils"; +import { RSI as defaultOptions } from "./defaultOptionsForComputation"; + +export default function () { + + let options = defaultOptions; + + function calculator(data) { + const { windowSize, sourcePath } = options; + + // @ts-ignore + const source = path(sourcePath); + + let prevAvgGain; + let prevAvgLoss; + const rsiAlgorithm = slidingWindow() + .windowSize(windowSize) + // @ts-ignore + .accumulator((values) => { + + const avgGain = isDefined(prevAvgGain) + ? (prevAvgGain * (windowSize - 1) + last(values).gain) / windowSize + : mean(values, (each) => each.gain); + + const avgLoss = isDefined(prevAvgLoss) + ? (prevAvgLoss * (windowSize - 1) + last(values).loss) / windowSize + : mean(values, (each) => each.loss); + + const relativeStrength = avgGain / avgLoss; + const rsi = 100 - (100 / (1 + relativeStrength)); + + prevAvgGain = avgGain; + prevAvgLoss = avgLoss; + + return rsi; + }); + + const gainsAndLossesCalculator = slidingWindow() + .windowSize(2) + // @ts-ignore + .undefinedValue(() => [0, 0]) + .accumulator((tuple) => { + const prev = tuple[0]; + const now = tuple[1]; + const change = source(now) - source(prev); + return { + gain: Math.max(change, 0), + loss: Math.abs(Math.min(change, 0)), + }; + }); + + const gainsAndLosses = gainsAndLossesCalculator(data); + + const rsiData = rsiAlgorithm(gainsAndLosses); + + return rsiData; + } + calculator.undefinedLength = function () { + const { windowSize } = options; + + return windowSize - 1; + }; + calculator.options = function (x) { + if (!arguments.length) { + return options; + } + options = { ...defaultOptions, ...x }; + return calculator; + }; + + return calculator; +} diff --git a/packages/react-financial-charts/src/calculator/sar.ts b/packages/react-financial-charts/src/calculator/sar.ts new file mode 100644 index 000000000..049a6a7c1 --- /dev/null +++ b/packages/react-financial-charts/src/calculator/sar.ts @@ -0,0 +1,113 @@ +import { isDefined, isNotDefined, mappedSlidingWindow } from "../utils"; +import { SAR as defaultOptions } from "./defaultOptionsForComputation"; + +function calc(prev, now) { + const risingSar = prev.risingSar + + prev.af * (prev.risingEp - prev.risingSar); + + const fallingSar = prev.fallingSar + - prev.af * (prev.fallingSar - prev.fallingEp); + + const risingEp = Math.max(prev.risingEp, now.high); + const fallingEp = Math.min(prev.fallingEp, now.low); + + return { + risingSar, + fallingSar, + risingEp, + fallingEp, + }; +} + +export default function () { + + let options = defaultOptions; + + function calculator(data) { + const { accelerationFactor, maxAccelerationFactor } = options; + + const algorithm = mappedSlidingWindow() + .windowSize(2) + // @ts-ignore + .undefinedValue(({ high, low }) => { + return { + risingSar: low, + risingEp: high, + fallingSar: high, + fallingEp: low, + af: accelerationFactor, + }; + }) + .accumulator(([prev, now]) => { + + const { + risingSar, + fallingSar, + risingEp, + fallingEp, + } = calc(prev, now); + + if (isNotDefined(prev.use) + && risingSar > now.low + && fallingSar < now.high) { + return { + risingSar, + fallingSar, + risingEp, + fallingEp, + }; + } + + const use = isDefined(prev.use) + ? prev.use === "rising" + ? risingSar > now.low ? "falling" : "rising" + : fallingSar < now.high ? "rising" : "falling" + : risingSar > now.low + ? "falling" + : "rising"; + + const current = prev.use === use + ? { + af: Math.min(maxAccelerationFactor, prev.af + accelerationFactor), + fallingEp, + risingEp, + fallingSar, + risingSar, + } + : { + af: accelerationFactor, + fallingEp: now.low, + risingEp: now.high, + fallingSar: Math.max(prev.risingEp, now.high), + risingSar: Math.min(prev.fallingEp, now.low), + }; + + const { date, high, low } = now; + return { + date, + high, + low, + ...current, + use, + sar: use === "falling" ? current.fallingSar : current.risingSar, + }; + }); + + const calculatedData = algorithm(data).map((d) => d.sar); + // console.log(calculatedData); + + return calculatedData; + } + calculator.undefinedLength = function () { + return 1; + }; + calculator.options = function (x) { + if (!arguments.length) { + return options; + } + options = { ...defaultOptions, ...x }; + return calculator; + }; + + return calculator; +} diff --git a/packages/react-financial-charts/src/calculator/sma.ts b/packages/react-financial-charts/src/calculator/sma.ts new file mode 100644 index 000000000..40ca46469 --- /dev/null +++ b/packages/react-financial-charts/src/calculator/sma.ts @@ -0,0 +1,34 @@ +import { mean } from "d3-array"; + +import { slidingWindow } from "../utils"; +import { SMA as defaultOptions } from "./defaultOptionsForComputation"; + +export default function () { + + let options = defaultOptions; + + function calculator(data) { + const { windowSize, sourcePath } = options; + + const average = slidingWindow() + .windowSize(windowSize) + // @ts-ignore + .sourcePath(sourcePath) + .accumulator((values) => mean(values)); + + return average(data); + } + calculator.undefinedLength = function () { + const { windowSize } = options; + return windowSize - 1; + }; + calculator.options = function (x) { + if (!arguments.length) { + return options; + } + options = { ...defaultOptions, ...x }; + return calculator; + }; + + return calculator; +} diff --git a/packages/react-financial-charts/src/calculator/smoothedForceIndex.ts b/packages/react-financial-charts/src/calculator/smoothedForceIndex.ts new file mode 100644 index 000000000..0a84f019b --- /dev/null +++ b/packages/react-financial-charts/src/calculator/smoothedForceIndex.ts @@ -0,0 +1,53 @@ +import { zipper } from "../utils"; +import { SmoothedForceIndex as defaultOptions } from "./defaultOptionsForComputation"; +import ema from "./ema"; +import forceIndex from "./forceIndex"; +import sma from "./sma"; + +export default function () { + + const underlyingAlgorithm = forceIndex(); + const merge = zipper() + .combine((force, smoothed) => { + return { force, smoothed }; + }); + + let options = defaultOptions; + function calculator(data) { + const { smoothingType, smoothingWindow } = options; + const { sourcePath, volumePath } = options; + + const algo = underlyingAlgorithm + .options({ sourcePath, volumePath }); + + // @ts-ignore + const force = algo(data); + + const ma = smoothingType === "ema" ? ema() : sma(); + const forceMA = ma + .options({ + windowSize: smoothingWindow, + sourcePath: undefined, + }); + + // @ts-ignore + const smoothed = forceMA(force); + + // @ts-ignore + return merge(force, smoothed); + } + + calculator.undefinedLength = function () { + const { smoothingWindow } = options; + return underlyingAlgorithm.undefinedLength() + smoothingWindow - 1; + }; + calculator.options = function (x) { + if (!arguments.length) { + return options; + } + options = { ...defaultOptions, ...x }; + return calculator; + }; + + return calculator; +} diff --git a/packages/react-financial-charts/src/calculator/sto.ts b/packages/react-financial-charts/src/calculator/sto.ts new file mode 100644 index 000000000..62dd26d33 --- /dev/null +++ b/packages/react-financial-charts/src/calculator/sto.ts @@ -0,0 +1,102 @@ +/* +https://github.com/ScottLogic/d3fc/blob/master/src/indicator/algorithm/calculator/stochasticOscillator.js + +The MIT License (MIT) + +Copyright (c) 2014-2015 Scott Logic Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +import { max, mean, min } from "d3-array"; + +import { last, slidingWindow, zipper } from "../utils"; +import { FullStochasticOscillator as defaultOptions } from "./defaultOptionsForComputation"; + +export default function () { + + let options = defaultOptions; + + let source = (d) => ({ open: d.open, high: d.high, low: d.low, close: d.close }); + + function calculator(data) { + const { windowSize, kWindowSize, dWindowSize } = options; + + const high = (d) => source(d).high; + const low = (d) => source(d).low; + const close = (d) => source(d).close; + + const kWindow = slidingWindow() + .windowSize(windowSize) + // @ts-ignore + .accumulator((values) => { + + const highestHigh = max(values, high); + const lowestLow = min(values, low); + + const currentClose = close(last(values)); + const k = (currentClose - lowestLow) / (highestHigh - lowestLow) * 100; + + return k; + }); + + const kSmoothed = slidingWindow() + .skipInitial(windowSize - 1) + // @ts-ignore + .windowSize(kWindowSize) + .accumulator((values) => mean(values)); + + const dWindow = slidingWindow() + .skipInitial(windowSize - 1 + kWindowSize - 1) + // @ts-ignore + .windowSize(dWindowSize) + .accumulator((values) => mean(values)); + + const stoAlgorithm = zipper() + .combine((K, D) => ({ K, D })); + + const kData = kSmoothed(kWindow(data)); + const dData = dWindow(kData); + + // @ts-ignore + const indicatorData = stoAlgorithm(kData, dData); + + return indicatorData; + } + calculator.undefinedLength = function () { + const { windowSize, kWindowSize, dWindowSize } = options; + return windowSize + kWindowSize + dWindowSize; + }; + calculator.source = function (x) { + if (!arguments.length) { + return source; + } + source = x; + return calculator; + }; + calculator.options = function (x) { + if (!arguments.length) { + return options; + } + options = { ...defaultOptions, ...x }; + return calculator; + }; + + return calculator; +} diff --git a/packages/react-financial-charts/src/calculator/tma.ts b/packages/react-financial-charts/src/calculator/tma.ts new file mode 100644 index 000000000..d16b1e788 --- /dev/null +++ b/packages/react-financial-charts/src/calculator/tma.ts @@ -0,0 +1,79 @@ +/* + TRIMA (Triangular Moving Average). + + The Triangular Moving Average can be used like any other Moving Average, to + obtain a smoother representation of the underlying data. + It is important to note that the Triangular Moving Average is typically much + smoother than other moving averages. + + Triangular moving averages give greater weight to prices at the centre of the + chosen period andi it is calculated as double smoothed SMA (simple moving average). + + Examples: + For TimeSerie={a,b,c,d,e,f...} ('a' is the older price) + + 1st value for TMA 4-Period is: ((1*a)+(2*b)+(2*c)+(1*d)) / 6 + 2nd value for TMA 4-Period is: ((1*b)+(2*c)+(2*d)+(1*e)) / 6 + + 1st value for TMA 5-Period is: ((1*a)+(2*b)+(3*c)+(2*d)+(1*e)) / 9 + 2nd value for TMA 5-Period is: ((1*b)+(2*c)+(3*d)+(2*e)+(1*f)) / 9 + + Using algebra, it can be demonstrated that the TMA is equivalent to + doing a SMA of a SMA. The following explain the rules: + + (1) When the period is even, TMA(x,period)=SMA(SMA(x,period/2),(period/2)+1) + (2) When the period is odd, TMA(x,period)=SMA(SMA(x,(period+1)/2),(period+1)/2) + + In other word: + (1) A period of 4 becomes TMA(x,4) = SMA( SMA( x, 2), 3 ) + (2) A period of 5 becomes TMA(x,5) = SMA( SMA( x, 3), 3 ) + + The SMA of a SMA is the algorithm generally found in books. +*/ + +import { sum } from "d3-array"; + +import { slidingWindow } from "../utils"; +import { TMA as defaultOptions } from "./defaultOptionsForComputation"; + +export default function () { + let options = defaultOptions; + + function calculator(data) { + const { windowSize, sourcePath } = options; + + const n = Math.floor(windowSize / 2); + const weight = (windowSize % 2) === 0 + ? n * (n + 1) + : (n + 1) * (n + 1); + + const triaverage = slidingWindow() + .windowSize(windowSize) + // @ts-ignore + .sourcePath(sourcePath) + .accumulator((values) => { + const total = sum(values, (v, i) => { + return i < n + ? (i + 1) * v + : (windowSize - i) * v; + }); + return total / weight; + }); + + return triaverage(data); + + } + calculator.undefinedLength = function () { + const { windowSize } = options; + return windowSize - 1; + }; + calculator.options = function (x) { + if (!arguments.length) { + return options; + } + options = { ...defaultOptions, ...x }; + return calculator; + }; + + return calculator; +} diff --git a/packages/react-financial-charts/src/calculator/wma.ts b/packages/react-financial-charts/src/calculator/wma.ts new file mode 100644 index 000000000..a8c13a5c3 --- /dev/null +++ b/packages/react-financial-charts/src/calculator/wma.ts @@ -0,0 +1,42 @@ +import { sum } from "d3-array"; + +import { slidingWindow } from "../utils"; +import { WMA as defaultOptions } from "./defaultOptionsForComputation"; + +export default function () { + + let options = defaultOptions; + + function calculator(data) { + const { windowSize, sourcePath } = options; + + const weight = windowSize * (windowSize + 1) / 2; + + const waverage = slidingWindow() + .windowSize(windowSize) + // @ts-ignore + .sourcePath(sourcePath) + .accumulator((values) => { + const total = sum(values, (v, i) => { + return (i + 1) * v; + }); + return total / weight; + }); + + return waverage(data); + } + calculator.undefinedLength = function () { + const { windowSize } = options; + + return windowSize - 1; + }; + calculator.options = function (x) { + if (!arguments.length) { + return options; + } + options = { ...defaultOptions, ...x }; + return calculator; + }; + + return calculator; +} diff --git a/packages/react-financial-charts/src/coordinates/CrossHairCursor.tsx b/packages/react-financial-charts/src/coordinates/CrossHairCursor.tsx new file mode 100644 index 000000000..8a1d4aac2 --- /dev/null +++ b/packages/react-financial-charts/src/coordinates/CrossHairCursor.tsx @@ -0,0 +1,130 @@ +import * as PropTypes from "prop-types"; +import * as React from "react"; +import GenericComponent, { getMouseCanvas } from "../GenericComponent"; + +import { getStrokeDasharray, hexToRGBA, isDefined, isNotDefined, strokeDashTypes } from "../utils"; + +interface CrossHairCursorProps { + readonly className?: string; + readonly opacity?: number; + readonly snapX?: boolean; + readonly stroke?: string; + readonly strokeDasharray?: strokeDashTypes; +} + +const defaultCustomX = (props: CrossHairCursorProps, moreProps) => { + const { xScale, xAccessor, currentItem, mouseXY } = moreProps; + const { snapX } = props; + const x = snapX + ? Math.round(xScale(xAccessor(currentItem))) + : mouseXY[0]; + return x; +}; + +export class CrossHairCursor extends React.Component { + + public static defaultProps = { + stroke: "#000000", + opacity: 0.3, + strokeDasharray: "ShortDash", + snapX: true, + customX: defaultCustomX, + }; + + public static contextTypes = { + margin: PropTypes.object.isRequired, + ratio: PropTypes.number.isRequired, + }; + + public render() { + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + const { className } = this.props; + const lines = this.helper(this.props, moreProps); + + if (lines === undefined) { + return null; + } + + return ( + + {lines.map(({ strokeDasharray, ...rest }, idx) => + )} + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const lines = this.helper(this.props, moreProps); + + if (lines !== undefined && isDefined(lines)) { + + const { margin, ratio } = this.context; + const originX = 0.5 * ratio + margin.left; + const originY = 0.5 * ratio + margin.top; + + ctx.save(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.scale(ratio, ratio); + + ctx.translate(originX, originY); + + lines.forEach((line) => { + const dashArray = getStrokeDasharray(line.strokeDasharray) + .split(",") + .map((d) => +d); + + ctx.strokeStyle = hexToRGBA(line.stroke, line.opacity); + ctx.setLineDash(dashArray); + ctx.beginPath(); + ctx.moveTo(line.x1, line.y1); + ctx.lineTo(line.x2, line.y2); + ctx.stroke(); + }); + + ctx.restore(); + } + } + + private readonly helper = (props, moreProps) => { + const { + mouseXY, currentItem, show, height, width, + } = moreProps; + + const { customX, stroke, opacity, strokeDasharray } = props; + + if (!show || isNotDefined(currentItem)) { + return undefined; + } + + const line1 = { + x1: 0, + x2: width, + y1: mouseXY[1], + y2: mouseXY[1], + stroke, strokeDasharray, opacity, + }; + const x = customX(props, moreProps); + + const line2 = { + x1: x, + x2: x, + y1: 0, + y2: height, + stroke, strokeDasharray, opacity, + }; + return [line1, line2]; + } +} diff --git a/packages/react-financial-charts/src/coordinates/CurrentCoordinate.tsx b/packages/react-financial-charts/src/coordinates/CurrentCoordinate.tsx new file mode 100644 index 000000000..b547a89f1 --- /dev/null +++ b/packages/react-financial-charts/src/coordinates/CurrentCoordinate.tsx @@ -0,0 +1,76 @@ +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getMouseCanvas } from "../GenericComponent"; +import { isNotDefined } from "../utils"; + +interface CurrentCoordinateProps { + readonly className?: string; + readonly fill?: any; + readonly r: number; + readonly yAccessor: (item: any) => number; +} + +export class CurrentCoordinate extends React.Component { + + public static defaultProps = { + r: 3, + className: "react-stockcharts-current-coordinate", + }; + + public render() { + return ( + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const circle = this.helper(this.props, moreProps); + if (!circle) { return null; } + + ctx.fillStyle = circle.fill; + ctx.beginPath(); + ctx.arc(circle.x, circle.y, circle.r, 0, 2 * Math.PI, false); + ctx.fill(); + } + + private readonly renderSVG = (moreProps) => { + const { className } = this.props; + + const circle = this.helper(this.props, moreProps); + if (!circle) { return null; } + + const fillColor = circle.fill instanceof Function ? circle.fill(moreProps.currentItem) : circle.fill; + + return ( + + ); + } + + private readonly helper = (props: CurrentCoordinateProps, moreProps) => { + const { fill, yAccessor, r } = props; + + const { show, xScale, chartConfig: { yScale }, currentItem, xAccessor } = moreProps; + + if (!show || isNotDefined(currentItem)) { + return null; + } + + const xValue = xAccessor(currentItem); + const yValue = yAccessor(currentItem); + + if (isNotDefined(yValue)) { + return null; + } + + const x = Math.round(xScale(xValue)); + const y = Math.round(yScale(yValue)); + + return { x, y, r, fill }; + } +} diff --git a/packages/react-financial-charts/src/coordinates/Cursor.tsx b/packages/react-financial-charts/src/coordinates/Cursor.tsx new file mode 100644 index 000000000..0b6f48512 --- /dev/null +++ b/packages/react-financial-charts/src/coordinates/Cursor.tsx @@ -0,0 +1,284 @@ +import * as PropTypes from "prop-types"; +import * as React from "react"; +import GenericComponent, { getMouseCanvas } from "../GenericComponent"; + +import { + first, + getStrokeDasharray, + hexToRGBA, + isNotDefined, + last, + strokeDashTypes, +} from "../utils"; + +interface CursorProps { + className?: string; + stroke?: string; + strokeDasharray?: strokeDashTypes; + snapX?: boolean; + opacity?: number; + disableYCursor?: boolean; + useXCursorShape?: boolean; + xCursorShapeFill?: string | any; // func + xCursorShapeStroke: string | any; // func + xCursorShapeStrokeDasharray?: strokeDashTypes; + xCursorShapeOpacity?: number; +} + +const defaultCustomSnapX = (props: CursorProps, moreProps) => { + const { xScale, xAccessor, currentItem, mouseXY } = moreProps; + const { snapX } = props; + const x = snapX ? Math.round(xScale(xAccessor(currentItem))) : mouseXY[0]; + return x; +}; + +class Cursor extends React.Component { + + public static defaultProps = { + stroke: "#000000", + opacity: 0.3, + strokeDasharray: "ShortDash", + snapX: true, + customSnapX: defaultCustomSnapX, + disableYCursor: false, + useXCursorShape: false, + xCursorShapeStroke: "#000000", + xCursorShapeOpacity: 0.5, + }; + + public static contextTypes = { + margin: PropTypes.object.isRequired, + ratio: PropTypes.number.isRequired, + // xScale for getting update event upon pan end, this is needed to get past the PureComponent shouldComponentUpdate + // xScale: PropTypes.func.isRequired, + }; + + public render() { + return ( + + ); + } + + private getXCursorShapeStroke(moreProps) { + const { xCursorShapeStroke } = this.props; + const { currentItem } = moreProps; + return xCursorShapeStroke instanceof Function + ? xCursorShapeStroke(currentItem) + : xCursorShapeStroke; + } + + private getXCursorShapeFill(moreProps) { + const { xCursorShapeFill } = this.props; + const { currentItem } = moreProps; + return xCursorShapeFill instanceof Function + ? xCursorShapeFill(currentItem) + : xCursorShapeFill; + } + + private getXCursorShape(moreProps/* , ctx */) { + const { height, xScale, currentItem, plotData } = moreProps; + const { xAccessor } = moreProps; + const xValue = xAccessor(currentItem); + const centerX = xScale(xValue); + const shapeWidth = + Math.abs( + xScale(xAccessor(last(plotData))) - + xScale(xAccessor(first(plotData))), + ) / (plotData.length - 1); + const xPos = centerX - shapeWidth / 2; + + return { height, xPos, shapeWidth }; + } + + private getXYCursor(props, moreProps) { + const { mouseXY, currentItem, show, height, width } = moreProps; + const { + customSnapX, + stroke, + opacity, + strokeDasharray, + disableYCursor, + } = props; + + if (!show || isNotDefined(currentItem)) { + return undefined; + } + + const yCursor = { + x1: 0, + x2: width, + y1: mouseXY[1], + y2: mouseXY[1], + stroke, + strokeDasharray, + opacity, + id: "yCursor", + }; + const x = customSnapX(props, moreProps); + + const xCursor = { + x1: x, + x2: x, + y1: 0, + y2: height, + stroke, + strokeDasharray, + opacity, + id: "xCursor", + }; + + return disableYCursor ? [xCursor] : [yCursor, xCursor]; + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const cursors = this.getXYCursor(this.props, moreProps); + + if (cursors !== undefined) { + const { useXCursorShape } = this.props; + + const { margin, ratio } = this.context; + const originX = 0.5 * ratio + margin.left; + const originY = 0.5 * ratio + margin.top; + + ctx.save(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.scale(ratio, ratio); + + ctx.translate(originX, originY); + + cursors.forEach((line) => { + const dashArray = getStrokeDasharray(line.strokeDasharray) + .split(",") + .map((d) => +d); + const xShapeFill = this.getXCursorShapeFill(moreProps); + + if (useXCursorShape && line.id === "xCursor") { + const { + xCursorShapeOpacity, + xCursorShapeStrokeDasharray, + } = this.props; + const xShape = this.getXCursorShape(moreProps); + + if (xCursorShapeStrokeDasharray != null) { + const xShapeStroke = this.getXCursorShapeStroke( + moreProps, + ); + ctx.strokeStyle = hexToRGBA( + xShapeStroke, + xCursorShapeOpacity, + ); + ctx.setLineDash( + getStrokeDasharray(xCursorShapeStrokeDasharray) + .split(",") + .map((d) => +d), + ); + } + + ctx.beginPath(); + ctx.fillStyle = + xShapeFill != null + ? hexToRGBA(xShapeFill, xCursorShapeOpacity) + : "rgba(0, 0, 0, 0)"; // ="transparent" + + ctx.beginPath(); + xCursorShapeStrokeDasharray == null + ? ctx.fillRect( + xShape.xPos, + 0, + xShape.shapeWidth, + xShape.height, + ) + : ctx.rect( + xShape.xPos, + 0, + xShape.shapeWidth, + xShape.height, + ); + ctx.fill(); + } else { + ctx.strokeStyle = hexToRGBA(line.stroke, line.opacity); + ctx.setLineDash(dashArray); + ctx.beginPath(); + ctx.moveTo(line.x1, line.y1); + ctx.lineTo(line.x2, line.y2); + } + + ctx.stroke(); + }); + + ctx.restore(); + } + } + + private readonly renderSVG = (moreProps) => { + const cursors = this.getXYCursor(this.props, moreProps); + if (cursors === undefined) { + return null; + } + + const { className, useXCursorShape } = this.props; + + return ( + + {cursors.map(({ strokeDasharray, id, ...rest }, idx) => { + if (useXCursorShape && id === "xCursor") { + const { + xCursorShapeOpacity, + xCursorShapeStrokeDasharray, + } = this.props; + const xShape = this.getXCursorShape(moreProps); + const xShapeFill = this.getXCursorShapeFill(moreProps); + const xShapeStroke = this.getXCursorShapeStroke( + moreProps, + ); + return ( + + ); + } + + return ( + + ); + })} + + ); + } +} + +export default Cursor; diff --git a/packages/react-financial-charts/src/coordinates/EdgeCoordinate.tsx b/packages/react-financial-charts/src/coordinates/EdgeCoordinate.tsx new file mode 100644 index 000000000..ba90d40df --- /dev/null +++ b/packages/react-financial-charts/src/coordinates/EdgeCoordinate.tsx @@ -0,0 +1,192 @@ + +import * as React from "react"; + +import { hexToRGBA, isDefined } from "../utils"; + +const helper = (props) => { + const { coordinate: displayCoordinate, show, type, orient, edgeAt, hideLine } = props; + const { fill, opacity, fontFamily, fontSize, textFill, lineStroke, lineOpacity, arrowWidth } = props; + const { rectWidth, rectHeight } = props; + const { x1, y1, x2, y2 } = props; + + if (!show) { return null; } + + let edgeXRect; + let edgeYRect; + let edgeXText; + let edgeYText; + + if (type === "horizontal") { + + edgeXRect = (orient === "right") ? edgeAt + 1 : edgeAt - rectWidth - arrowWidth - 1; + edgeYRect = y1 - (rectHeight / 2); + edgeXText = (orient === "right") ? edgeAt + (rectWidth / 2) + arrowWidth : edgeAt - (rectWidth / 2) - arrowWidth; + edgeYText = y1; + } else { + edgeXRect = x1 - (rectWidth / 2); + edgeYRect = (orient === "bottom") ? edgeAt : edgeAt - rectHeight; + edgeXText = x1; + edgeYText = (orient === "bottom") ? edgeAt + (rectHeight / 2) : edgeAt - (rectHeight / 2); + } + + let coordinateBase; + let coordinate; + const textAnchor = "middle"; + if (isDefined(displayCoordinate)) { + coordinateBase = { + edgeXRect, edgeYRect, rectHeight, rectWidth, fill, opacity, arrowWidth, + }; + coordinate = { + edgeXText, edgeYText, textAnchor, fontFamily, fontSize, textFill, displayCoordinate, + }; + } + + const line = hideLine ? undefined : { + opacity: lineOpacity, stroke: lineStroke, x1, y1, x2, y2, + }; + return { + coordinateBase, coordinate, line, orient, + }; +}; + +interface EdgeCoordinateProps { + className?: string; + type: "vertical" | "horizontal"; + coordinate: any; + x1: number; + y1: number; + x2: number; + y2: number; + orient?: "bottom" | "top" | "left" | "right"; + rectWidth?: number; + hideLine?: boolean; + fill?: string; + opacity?: number; + fontFamily: string; + fontSize: number; +} + +export class EdgeCoordinate extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-edgecoordinate", + orient: "left", + hideLine: false, + fill: "#8a8a8a", + opacity: 1, + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 13, + textFill: "#FFFFFF", + lineStroke: "#000000", + lineOpacity: 0.3, + arrowWidth: 10, + }; + + public static drawOnCanvasStatic = (ctx, props) => { + props = { ...EdgeCoordinate.defaultProps, ...props }; + + const edge = helper(props); + + if (edge === null) { return; } + + if (isDefined(edge.coordinateBase)) { + const { rectWidth, rectHeight, arrowWidth } = edge.coordinateBase; + + ctx.fillStyle = hexToRGBA(edge.coordinateBase.fill, edge.coordinateBase.opacity); + + const x = edge.coordinateBase.edgeXRect; + const y = edge.coordinateBase.edgeYRect; + + ctx.beginPath(); + + if (edge.orient === "right") { + ctx.moveTo(x, y + rectHeight / 2); + ctx.lineTo(x + arrowWidth, y); + ctx.lineTo(x + rectWidth + arrowWidth, y); + ctx.lineTo(x + rectWidth + arrowWidth, y + rectHeight); + ctx.lineTo(x + arrowWidth, y + rectHeight); + ctx.closePath(); + } else if (edge.orient === "left") { + ctx.moveTo(x, y); + ctx.lineTo(x + rectWidth, y); + ctx.lineTo(x + rectWidth + arrowWidth, y + rectHeight / 2); + ctx.lineTo(x + rectWidth, y + rectHeight); + ctx.lineTo(x, y + rectHeight); + ctx.closePath(); + } else { + ctx.rect(x, y, rectWidth, rectHeight); + } + ctx.fill(); + + ctx.font = `${edge.coordinate.fontSize}px ${edge.coordinate.fontFamily}`; + ctx.fillStyle = edge.coordinate.textFill; + ctx.textAlign = edge.coordinate.textAnchor === "middle" ? "center" : edge.coordinate.textAnchor; + ctx.textBaseline = "middle"; + + ctx.fillText(edge.coordinate.displayCoordinate, edge.coordinate.edgeXText, edge.coordinate.edgeYText); + } + + if (edge.line !== undefined) { + ctx.strokeStyle = hexToRGBA(edge.line.stroke, edge.line.opacity); + + ctx.beginPath(); + ctx.moveTo(edge.line.x1, edge.line.y1); + ctx.lineTo(edge.line.x2, edge.line.y2); + ctx.stroke(); + } + } + + public render() { + const { className } = this.props; + + const edge = helper(this.props); + if (edge === null) { + return null; + } + let line; + let coordinateBase; + let coordinate; + + if (edge.line !== undefined) { + line = ; + } + + if (isDefined(edge.coordinateBase)) { + + const { rectWidth, rectHeight, arrowWidth } = edge.coordinateBase; + + const path = edge.orient === "left" + ? `M0,0L0,${rectHeight}L${rectWidth},${rectHeight}L${rectWidth + arrowWidth},10L${rectWidth},0L0,0L0,0` + : `M0,${arrowWidth}L${arrowWidth},${rectHeight}L${rectWidth + arrowWidth},${rectHeight}L${rectWidth + arrowWidth},0L${arrowWidth},0L0,${arrowWidth}`; + + coordinateBase = edge.orient === "left" || edge.orient === "right" + ? + + + : ; + + coordinate = ({edge.coordinate.displayCoordinate}); + } + return ( + + {line} + {coordinateBase} + {coordinate} + + ); + } +} diff --git a/packages/react-financial-charts/src/coordinates/EdgeCoordinateV2.tsx b/packages/react-financial-charts/src/coordinates/EdgeCoordinateV2.tsx new file mode 100644 index 000000000..84cafd6fc --- /dev/null +++ b/packages/react-financial-charts/src/coordinates/EdgeCoordinateV2.tsx @@ -0,0 +1,153 @@ +import * as React from "react"; + +import { hexToRGBA, isDefined } from "../utils"; + +export function renderSVG(props) { + const { className } = props; + + const edge = helper(props); + if (edge === null) { return null; } + let line; + let coordinateBase; + let coordinate; + + if (edge.line !== undefined && isDefined(edge.line)) { + line = ; + } + + if (isDefined(edge.coordinateBase)) { + + const { rectWidth, rectHeight, arrowWidth } = edge.coordinateBase; + + const path = edge.orient === "left" + ? `M0,0L0,${rectHeight}L${rectWidth},${rectHeight}L${rectWidth + arrowWidth},10L${rectWidth},0L0,0L0,0` + : `M0,${arrowWidth}L${arrowWidth},${rectHeight}L${rectWidth + arrowWidth},${rectHeight}L${rectWidth + arrowWidth},0L${arrowWidth},0L0,${arrowWidth}`; + + coordinateBase = edge.orient === "left" || edge.orient === "right" + ? + + + : ; + + coordinate = ({edge.coordinate.displayCoordinate}); + } + return ( + + {line} + {coordinateBase} + {coordinate} + + ); +} + +function helper(props) { + const { coordinate: displayCoordinate, show, type, orient, edgeAt, hideLine } = props; + const { fill, opacity, fontFamily, fontSize, textFill, lineStroke, lineOpacity, arrowWidth } = props; + const { rectWidth, rectHeight } = props; + const { x1, y1, x2, y2, dx } = props; + + if (!show) { + return null; + } + + let edgeXRect; + let edgeYRect; + let edgeXText; + let edgeYText; + + if (type === "horizontal") { + + edgeXRect = dx + ((orient === "right") ? edgeAt + 1 : edgeAt - rectWidth - arrowWidth - 1); + edgeYRect = y1 - (rectHeight / 2); + edgeXText = dx + ((orient === "right") ? edgeAt + (rectWidth / 2) + arrowWidth : edgeAt - (rectWidth / 2) - arrowWidth); + edgeYText = y1; + } else { + edgeXRect = x1 - (rectWidth / 2); + edgeYRect = (orient === "bottom") ? edgeAt : edgeAt - rectHeight; + edgeXText = x1; + edgeYText = (orient === "bottom") ? edgeAt + (rectHeight / 2) : edgeAt - (rectHeight / 2); + } + let coordinateBase; + let coordinate; + const textAnchor = "middle"; + if (isDefined(displayCoordinate)) { + coordinateBase = { + edgeXRect, edgeYRect, rectHeight, rectWidth, fill, opacity, arrowWidth, + }; + coordinate = { + edgeXText, edgeYText, textAnchor, fontFamily, fontSize, textFill, displayCoordinate, + }; + } + + const line = hideLine ? undefined : { + opacity: lineOpacity, stroke: lineStroke, x1, y1, x2, y2, + }; + return { + coordinateBase, coordinate, line, orient, + }; +} + +export function drawOnCanvas(ctx, props) { + const edge = helper(props); + + if (edge === null) { return; } + + if (isDefined(edge.coordinateBase)) { + const { rectWidth, rectHeight, arrowWidth } = edge.coordinateBase; + + ctx.fillStyle = hexToRGBA(edge.coordinateBase.fill, edge.coordinateBase.opacity); + + const x = edge.coordinateBase.edgeXRect; + const y = edge.coordinateBase.edgeYRect; + + ctx.beginPath(); + + if (edge.orient === "right") { + ctx.moveTo(x, y + rectHeight / 2); + ctx.lineTo(x + arrowWidth, y); + ctx.lineTo(x + rectWidth + arrowWidth, y); + ctx.lineTo(x + rectWidth + arrowWidth, y + rectHeight); + ctx.lineTo(x + arrowWidth, y + rectHeight); + ctx.closePath(); + } else if (edge.orient === "left") { + ctx.moveTo(x, y); + ctx.lineTo(x + rectWidth, y); + ctx.lineTo(x + rectWidth + arrowWidth, y + rectHeight / 2); + ctx.lineTo(x + rectWidth, y + rectHeight); + ctx.lineTo(x, y + rectHeight); + ctx.closePath(); + } else { + ctx.rect(x, y, rectWidth, rectHeight); + } + ctx.fill(); + + ctx.font = `${edge.coordinate.fontSize}px ${edge.coordinate.fontFamily}`; + ctx.fillStyle = edge.coordinate.textFill; + ctx.textAlign = edge.coordinate.textAnchor === "middle" ? "center" : edge.coordinate.textAnchor; + ctx.textBaseline = "middle"; + + ctx.fillText(edge.coordinate.displayCoordinate, edge.coordinate.edgeXText, edge.coordinate.edgeYText); + } + if (edge.line !== undefined && isDefined(edge.line)) { + ctx.strokeStyle = hexToRGBA(edge.line.stroke, edge.line.opacity); + + ctx.beginPath(); + ctx.moveTo(edge.line.x1, edge.line.y1); + ctx.lineTo(edge.line.x2, edge.line.y2); + ctx.stroke(); + } +} diff --git a/packages/react-financial-charts/src/coordinates/EdgeCoordinateV3.tsx b/packages/react-financial-charts/src/coordinates/EdgeCoordinateV3.tsx new file mode 100644 index 000000000..35d3693ee --- /dev/null +++ b/packages/react-financial-charts/src/coordinates/EdgeCoordinateV3.tsx @@ -0,0 +1,307 @@ +import * as React from "react"; + +import { getStrokeDasharray, hexToRGBA, isDefined } from "../utils"; + +export function renderSVG(props) { + const { className } = props; + + const edge = helper(props); + if (edge === null) { return null; } + let line; + let coordinateBase; + let coordinate; + + if (edge.line !== undefined && isDefined(edge.line)) { + line = ( + + ); + } + if (isDefined(edge.coordinateBase)) { + const { rectWidth, rectHeight, arrowWidth } = edge.coordinateBase; + + const path = + edge.orient === "left" + ? `M0,0L0,${rectHeight}L${rectWidth},${rectHeight}L${rectWidth + + arrowWidth},10L${rectWidth},0L0,0L0,0` + : `M0,${arrowWidth}L${arrowWidth},${rectHeight}L${rectWidth + + arrowWidth},${rectHeight}L${rectWidth + + arrowWidth},0L${arrowWidth},0L0,${arrowWidth}`; + + coordinateBase = + edge.orient === "left" || edge.orient === "right" ? ( + + + + ) : ( + + ); + + coordinate = ( + + {edge.coordinate.displayCoordinate} + + ); + } + return ( + + {line} + {coordinateBase} + {coordinate} + + ); +} +/* eslint-enable react/prop-types */ + +function helper(props) { + const { + coordinate: displayCoordinate, + show, + type, + orient, + edgeAt, + hideLine, + lineStrokeDasharray, + } = props; + const { + fill, + opacity, + fontFamily, + fontSize, + textFill, + lineStroke, + lineOpacity, + } = props; + const { stroke, strokeOpacity, strokeWidth } = props; + const { arrowWidth, rectWidth, rectHeight, rectRadius } = props; + const { x1, y1, x2, y2, dx } = props; + + if (!show) { return null; } + + let coordinateBase; + let coordinate; + if (isDefined(displayCoordinate)) { + const textAnchor = "middle"; + // TODO: Below it is necessary to implement logic for the possibility of alignment from the right or from the left. + + let edgeXRect; + let edgeYRect; + let edgeXText; + let edgeYText; + + if (type === "horizontal") { + edgeXRect = + dx + (orient === "right" ? edgeAt + 1 : edgeAt - rectWidth - 1); + edgeYRect = y1 - rectHeight / 2 - strokeWidth; + edgeXText = + dx + + (orient === "right" + ? edgeAt + rectWidth / 2 + : edgeAt - rectWidth / 2); + edgeYText = y1; + } else { + const dy = orient === "bottom" ? strokeWidth - 1 : -strokeWidth + 1; + edgeXRect = x1 - rectWidth / 2; + edgeYRect = + (orient === "bottom" ? edgeAt : edgeAt - rectHeight) + dy; + edgeXText = x1; + edgeYText = + (orient === "bottom" + ? edgeAt + rectHeight / 2 + : edgeAt - rectHeight / 2) + dy; + } + + coordinateBase = { + edgeXRect, + edgeYRect, + rectHeight: rectHeight + strokeWidth, + rectWidth, + rectRadius, + fill, + opacity, + arrowWidth, + stroke, + strokeOpacity, + strokeWidth, + }; + coordinate = { + edgeXText, + edgeYText, + textAnchor, + fontFamily, + fontSize, + textFill, + displayCoordinate, + }; + } + + const line = hideLine + ? undefined + : { + opacity: lineOpacity, + stroke: lineStroke, + strokeDasharray: lineStrokeDasharray, + x1, + y1, + x2, + y2, + }; + + return { + coordinateBase, + coordinate, + line, + orient, + }; +} + +export function drawOnCanvas(ctx, props) { + const { fontSize, fontFamily } = props; + + ctx.font = `${fontSize}px ${fontFamily}`; + ctx.textBaseline = "middle"; + const width = Math.round(ctx.measureText(props.coordinate).width + 10); + + const edge = helper({ ...props, rectWidth: width }); + + if (edge === null) { return; } + + if (edge.line !== undefined && isDefined(edge.line)) { + const dashArray = getStrokeDasharray(edge.line.strokeDasharray) + .split(",") + .map((d) => +d); + ctx.setLineDash(dashArray); + ctx.strokeStyle = hexToRGBA(edge.line.stroke, edge.line.opacity); + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.moveTo(edge.line.x1, edge.line.y1); + ctx.lineTo(edge.line.x2, edge.line.y2); + ctx.stroke(); + } + + ctx.setLineDash([]); + if (isDefined(edge.coordinateBase)) { + const { + rectWidth, + rectHeight, + rectRadius, + arrowWidth, + } = edge.coordinateBase; + + ctx.fillStyle = hexToRGBA( + edge.coordinateBase.fill, + edge.coordinateBase.opacity, + ); + if (isDefined(edge.coordinateBase.stroke)) { + ctx.strokeStyle = hexToRGBA( + edge.coordinateBase.stroke, + edge.coordinateBase.strokeOpacity, + ); + ctx.lineWidth = edge.coordinateBase.strokeWidth; + } + + let x = edge.coordinateBase.edgeXRect; + const y = edge.coordinateBase.edgeYRect; + const halfHeight = rectHeight / 2; + + ctx.beginPath(); + if (edge.orient === "right") { + x -= arrowWidth; + ctx.moveTo(x, y + halfHeight); + ctx.lineTo(x + arrowWidth, y); + ctx.lineTo(x + rectWidth + arrowWidth, y); + ctx.lineTo(x + rectWidth + arrowWidth, y + rectHeight); + ctx.lineTo(x + arrowWidth, y + rectHeight); + ctx.closePath(); + } else if (edge.orient === "left") { + // x += arrowWidth; + ctx.moveTo(x, y); + ctx.lineTo(x + rectWidth, y); + ctx.lineTo(x + rectWidth + arrowWidth, y + halfHeight); + ctx.lineTo(x + rectWidth, y + rectHeight); + ctx.lineTo(x, y + rectHeight); + ctx.closePath(); + } else { + // console.error(x, y, rectWidth, rectHeight) + if (rectRadius) { + roundRect(ctx, x, y, rectWidth, rectHeight, 3); + } else { + ctx.rect(x, y, rectWidth, rectHeight); + } + } + ctx.fill(); + + if (isDefined(edge.coordinateBase.stroke)) { + ctx.stroke(); + } + + ctx.fillStyle = edge.coordinate.textFill; + ctx.textAlign = + edge.coordinate.textAnchor === "middle" + ? "center" + : edge.coordinate.textAnchor; + ctx.fillText( + edge.coordinate.displayCoordinate, + edge.coordinate.edgeXText, + edge.coordinate.edgeYText, + ); + } +} + +function roundRect(ctx, x, y, width, height, radius) { + ctx.beginPath(); + ctx.moveTo(x + radius, y); + ctx.lineTo(x + width - radius, y); + ctx.quadraticCurveTo(x + width, y, x + width, y + radius); + ctx.lineTo(x + width, y + height - radius); + ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); + ctx.lineTo(x + radius, y + height); + ctx.quadraticCurveTo(x, y + height, x, y + height - radius); + ctx.lineTo(x, y + radius); + ctx.quadraticCurveTo(x, y, x + radius, y); + ctx.closePath(); +} + +// export default EdgeCoordinate; diff --git a/packages/react-financial-charts/src/coordinates/EdgeIndicator.tsx b/packages/react-financial-charts/src/coordinates/EdgeIndicator.tsx new file mode 100644 index 000000000..698749552 --- /dev/null +++ b/packages/react-financial-charts/src/coordinates/EdgeIndicator.tsx @@ -0,0 +1,136 @@ +import { format } from "d3-format"; +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getAxisCanvas } from "../GenericComponent"; +import { drawOnCanvas, renderSVG } from "./EdgeCoordinateV3"; + +import { first, functor, isDefined, last, noop, strokeDashTypes } from "../utils"; + +interface EdgeIndicatorProps { + yAccessor?: any; // func + type?: "horizontal"; + className?: string; + fill?: string | any; // func + lineStroke?: string | any; // func + textFill?: string | any; // func + itemType: "first" | "last"; + orient?: "left" | "right"; + edgeAt?: "left" | "right"; + displayFormat?: any; // func + rectHeight?: number; + rectWidth?: number; + arrowWidth?: number; + lineStrokeDasharray?: strokeDashTypes; +} + +export class EdgeIndicator extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-edgeindicator", + type: "horizontal", + orient: "left", + edgeAt: "left", + textFill: "#FFFFFF", + displayFormat: format(".2f"), + yAxisPad: 0, + rectHeight: 20, + rectWidth: 50, + arrowWidth: 10, + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 13, + dx: 0, + hideLine: false, + fill: "#8a8a8a", + opacity: 1, + stroke: noop, + strokeOpacity: 1, + strokeWidth: 3, + lineStroke: "#000000", + lineOpacity: 0.3, + lineStrokeDasharray: "ShortDash", + }; + + public render() { + return ; + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const edge = this.helper(this.props, moreProps); + const props = { + ...this.props, + ...edge, + }; + drawOnCanvas(ctx, props); + } + + private readonly renderSVG = (moreProps) => { + const edge = this.helper(this.props, moreProps); + const props = { + ...this.props, + ...edge, + }; + return renderSVG(props); + } + + private readonly helper = (props, moreProps) => { + const { itemType, yAccessor } = props; + const { plotData } = moreProps; + + const item = itemType === "first" + ? first(plotData, yAccessor) + : last(plotData, yAccessor); + + const edge = isDefined(item) + ? this.getEdge(props, moreProps, item) + : null; + + return edge; + } + + private readonly getEdge = (props, moreProps, item) => { + const { type: edgeType, displayFormat, edgeAt, yAxisPad, orient, lineStroke } = props; + + const { yAccessor, fill, textFill, rectHeight, rectWidth, arrowWidth } = props; + const { fontFamily, fontSize } = props; + const { stroke } = props; + + const { xScale, chartConfig: { yScale }, xAccessor, width } = moreProps; + + const yValue = yAccessor(item); + const xValue = xAccessor(item); + + const x1 = Math.round(xScale(xValue)); + const y1 = Math.round(yScale(yValue)); + + const [left, right] = [0, width]; + const edgeX = edgeAt === "left" + ? left - yAxisPad + : right + yAxisPad; + + return { + coordinate: displayFormat(yValue), + show: true, + type: edgeType, + orient, + edgeAt: edgeX, + fill: functor(fill)(item), + lineStroke: functor(lineStroke)(item), + stroke: functor(stroke)(item), + fontFamily, fontSize, + textFill: functor(textFill)(item), + rectHeight, rectWidth, arrowWidth, + x1, + y1, + x2: right, + y2: y1, + }; + } +} diff --git a/packages/react-financial-charts/src/coordinates/MouseCoordinateX.tsx b/packages/react-financial-charts/src/coordinates/MouseCoordinateX.tsx new file mode 100644 index 000000000..90242227e --- /dev/null +++ b/packages/react-financial-charts/src/coordinates/MouseCoordinateX.tsx @@ -0,0 +1,131 @@ +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getMouseCanvas } from "../GenericComponent"; +import { drawOnCanvas, renderSVG } from "./EdgeCoordinateV3"; + +import { isNotDefined } from "../utils"; + +interface MouseCoordinateXProps { + readonly displayFormat: any; // func + readonly yAxisPad?: number; + readonly rectWidth?: number; + readonly rectHeight?: number; + readonly orient?: "bottom" | "top" | "left" | "right"; + readonly at?: "bottom" | "top" | "left" | "right"; + readonly fill?: string; + readonly opacity?: number; + readonly fontFamily?: string; + readonly fontSize?: number; + readonly textFill?: string; + readonly snapX?: boolean; +} + +const defaultCustomX = (props: MouseCoordinateXProps, moreProps) => { + const { xScale, xAccessor, currentItem, mouseXY } = moreProps; + const { snapX } = props; + const x = snapX + ? xScale(xAccessor(currentItem)) + : mouseXY[0]; + + const { displayXAccessor } = moreProps; + const { displayFormat } = props; + const coordinate = snapX + ? displayFormat(displayXAccessor(currentItem)) + : displayFormat(xScale.invert(x)); + return { x, coordinate }; +}; + +export class MouseCoordinateX extends React.Component { + + public static defaultProps = { + yAxisPad: 0, + rectWidth: 80, + rectHeight: 20, + strokeOpacity: 1, + strokeWidth: 1, + orient: "bottom", + at: "bottom", + fill: "#525252", + opacity: 1, + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 13, + textFill: "#FFFFFF", + snapX: true, + customX: defaultCustomX, + }; + + public render() { + return ( + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const props = this.helper(this.props, moreProps); + if (isNotDefined(props)) { return null; } + + drawOnCanvas(ctx, props); + } + + private readonly renderSVG = (moreProps) => { + const props = this.helper(this.props, moreProps); + if (isNotDefined(props)) { return null; } + + return renderSVG(props); + } + + private readonly helper = (props, moreProps) => { + const { show, currentItem } = moreProps; + const { chartConfig: { height } } = moreProps; + + if (isNotDefined(currentItem)) { return null; } + + const { customX } = props; + + const { orient, at } = props; + const { stroke, strokeOpacity, strokeWidth } = props; + const { rectRadius, rectWidth, rectHeight } = props; + const { fill, opacity, fontFamily, fontSize, textFill } = props; + + const edgeAt = (at === "bottom") + ? height + : 0; + + const { + x, + coordinate, + } = customX(props, moreProps); + + const type = "vertical"; + const y1 = 0; + const y2 = height; + const hideLine = true; + + const coordinateProps = { + coordinate, + show, + type, + orient, + edgeAt, + hideLine, + fill, opacity, fontFamily, fontSize, textFill, + stroke, strokeOpacity, strokeWidth, + rectWidth, + rectHeight, + rectRadius, + arrowWidth: 0, + x1: x, + x2: x, + y1, + y2, + }; + return coordinateProps; + } +} diff --git a/packages/react-financial-charts/src/coordinates/MouseCoordinateXV2.tsx b/packages/react-financial-charts/src/coordinates/MouseCoordinateXV2.tsx new file mode 100644 index 000000000..854040e76 --- /dev/null +++ b/packages/react-financial-charts/src/coordinates/MouseCoordinateXV2.tsx @@ -0,0 +1,157 @@ + +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getMouseCanvas } from "../GenericComponent"; + +interface MouseCoordinateXV2Props { + xPosition?: any; // func + drawCoordinate?: any; // func + displayFormat: any; // func + at?: "bottom" | "top"; + orient?: "bottom" | "top"; + text?: { + fontStyle: string, + fontWeight: string, + fontFamily: string, + fontSize: number, + fill: string | any, // func + }; + bg: { + fill: string | any, // func + stroke: string, + strokeWidth: number, + padding: { + left: number, + right: number, + top: number, + bottom: number, + }, + }; + dx?: number; + dy?: number; +} + +export class MouseCoordinateXV2 extends React.Component { + + public static defaultProps = { + xPosition: defaultXPosition, + drawCoordinate: defaultDrawCoordinate, + at: "bottom", + orient: "bottom", + text: { + fontStyle: "", + fontWeight: "", + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 13, + fill: "rgb(35, 35, 35)", + }, + bg: { + fill: "rgb(255, 255, 255)", + stroke: "rgb(35, 35, 35)", + strokeWidth: 1, + padding: { + left: 7, + right: 7, + top: 4, + bottom: 4, + }, + }, + dx: 7, + dy: 7, + }; + + public render() { + return ( + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { show, currentItem } = moreProps; + const { drawCoordinate } = this.props; + + if (show && currentItem != null) { + const shape = getXCoordinateInfo(ctx, this.props, moreProps); + drawCoordinate(ctx, shape, this.props, moreProps); + } + } +} + +function defaultXPosition(props, moreProps) { + const { currentItem, xAccessor } = moreProps; + return xAccessor(currentItem); +} + +function getXCoordinateInfo(ctx, props, moreProps) { + const { xPosition } = props; + const xValue = xPosition(props, moreProps); + const { at, displayFormat } = props; + const { text } = props; + const { xScale, chartConfig: { height } } = moreProps; + ctx.font = `${text.fontStyle} ${text.fontWeight} ${text.fontSize}px ${text.fontFamily}`; + + const t = displayFormat(xValue); + const textWidth = ctx.measureText(t).width; + + const y = at === "bottom" ? height : 0; + const x = Math.round(xScale(xValue)); + + return { + x, + y, + textWidth, + text: t, + }; +} + +function defaultDrawCoordinate( + ctx, + shape, + props, + moreProps, +) { + const { x, y, textWidth, text } = shape; + const { orient, dx, dy } = props; + + const { + bg: { padding, fill, stroke, strokeWidth }, + text: { fontSize, fill: textFill }, + } = props; + + ctx.textAlign = "center"; + + const sign = orient === "top" ? -1 : 1; + const halfWidth = Math.round(textWidth / 2 + padding.right); + const height = Math.round(fontSize + padding.top + padding.bottom); + + ctx.strokeStyle = typeof stroke === "function" ? stroke(moreProps, ctx) : stroke; + ctx.fillStyle = typeof fill === "function" ? fill(moreProps, ctx) : fill; + ctx.lineWidth = typeof strokeWidth === "function" ? strokeWidth(moreProps) : strokeWidth; + + ctx.beginPath(); + + ctx.moveTo(x, y); + ctx.lineTo(x + dx, y + sign * dy); + ctx.lineTo(x + halfWidth, y + sign * dy); + ctx.lineTo(x + halfWidth, y + sign * (dy + height)); + ctx.lineTo(x - halfWidth, y + sign * (dy + height)); + ctx.lineTo(x - halfWidth, y + sign * dy); + ctx.lineTo(x - dx, y + sign * dy); + ctx.closePath(); + ctx.stroke(); + ctx.fill(); + + ctx.beginPath(); + ctx.fillStyle = typeof textFill === "function" ? textFill(moreProps, ctx) : textFill; + + ctx.textBaseline = orient === "top" ? "alphabetic" : "hanging"; + const pad = orient === "top" ? padding.bottom : padding.top; + + ctx.fillText(text, x, y + sign * (dy + pad + 2)); +} diff --git a/packages/react-financial-charts/src/coordinates/MouseCoordinateY.tsx b/packages/react-financial-charts/src/coordinates/MouseCoordinateY.tsx new file mode 100644 index 000000000..303a39346 --- /dev/null +++ b/packages/react-financial-charts/src/coordinates/MouseCoordinateY.tsx @@ -0,0 +1,135 @@ + +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getMouseCanvas } from "../GenericComponent"; +import { drawOnCanvas, renderSVG } from "./EdgeCoordinateV3"; + +import { isNotDefined } from "../utils"; + +interface MouseCoordinateYProps { + displayFormat: any; // func + yAxisPad?: number; + rectWidth?: number; + rectHeight?: number; + orient?: "bottom" | "top" | "left" | "right"; + at?: "bottom" | "top" | "left" | "right"; + dx?: number; + fill?: string; + opacity?: number; + fontFamily?: string; + fontSize?: number; + textFill?: string; +} + +export class MouseCoordinateY extends React.Component { + + public static defaultProps = { + yAxisPad: 0, + rectWidth: 50, + rectHeight: 20, + orient: "left", + at: "left", + dx: 0, + arrowWidth: 10, + fill: "#525252", + opacity: 1, + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 13, + textFill: "#FFFFFF", + strokeOpacity: 1, + strokeWidth: 1, + }; + + public render() { + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + const props = this.helper(this.props, moreProps); + if (isNotDefined(props)) { return null; } + + return renderSVG(props); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const props = this.helper(this.props, moreProps); + if (isNotDefined(props)) { return null; } + + drawOnCanvas(ctx, props); + } + + private readonly helper = (props, moreProps) => { + const { chartId } = moreProps; + const { currentCharts, mouseXY } = moreProps; + + if (isNotDefined(mouseXY)) { return null; } + if (currentCharts.indexOf(chartId) < 0) { return null; } + + const { show } = moreProps; + if (!show) { return null; } + + const y = mouseXY[1]; + const { chartConfig: { yScale } } = moreProps; + const { displayFormat } = props; + + const coordinate = displayFormat(yScale.invert(y)); + + return getYCoordinate(y, coordinate, props, moreProps); + } +} + +export function getYCoordinate(y, displayValue, props, moreProps) { + const { width } = moreProps; + + const { orient, at, rectWidth, rectHeight, dx } = props; + const { fill, opacity, fontFamily, fontSize, textFill, arrowWidth } = props; + const { stroke, strokeOpacity, strokeWidth } = props; + + const x1 = 0; + const x2 = width; + const edgeAt = (at === "right") + ? width + : 0; + + const type = "horizontal"; + const hideLine = true; + + const coordinateProps = { + coordinate: displayValue, + show: true, + type, + orient, + edgeAt, + hideLine, + fill, + opacity, + + fontFamily, + fontSize, + textFill, + + stroke, + strokeOpacity, + strokeWidth, + + rectWidth, + rectHeight, + + arrowWidth, + dx, + x1, + x2, + y1: y, + y2: y, + }; + return coordinateProps; +} diff --git a/packages/react-financial-charts/src/coordinates/PriceCoordinate.tsx b/packages/react-financial-charts/src/coordinates/PriceCoordinate.tsx new file mode 100644 index 000000000..8bfc9f2b5 --- /dev/null +++ b/packages/react-financial-charts/src/coordinates/PriceCoordinate.tsx @@ -0,0 +1,126 @@ +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getAxisCanvas } from "../GenericComponent"; +import { functor, strokeDashTypes } from "../utils"; +import { drawOnCanvas, renderSVG } from "./EdgeCoordinateV3"; + +interface PriceCoordinateProps { + displayFormat: any; // func + yAxisPad?: number; + rectWidth?: number; + rectHeight?: number; + orient?: "bottom" | "top" | "left" | "right"; + at?: "bottom" | "top" | "left" | "right"; + price?: number; + dx?: number; + arrowWidth?: number; + opacity?: number; + lineOpacity?: number; + lineStroke?: string; + fontFamily?: string; + fontSize?: number; + fill?: string | any; // func + strokeDasharray?: strokeDashTypes; + stroke?: string; + strokeOpacity?: number; + strokeWidth?: number; + textFill?: string | any; // func +} + +export class PriceCoordinate extends React.Component { + + public static defaultProps = { + yAxisPad: 0, + rectWidth: 50, + rectHeight: 20, + orient: "left", + at: "left", + price: 0, + dx: 0, + arrowWidth: 0, + fill: "#BAB8b8", + opacity: 1, + lineOpacity: 0.2, + lineStroke: "#000000", + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 13, + textFill: "#FFFFFF", + strokeOpacity: 1, + strokeWidth: 1, + strokeDasharray: "Solid", + }; + + public render() { + return ( + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const props = this.helper(this.props, moreProps); + drawOnCanvas(ctx, props); + } + + private readonly renderSVG = (moreProps) => { + const props = this.helper(this.props, moreProps); + return renderSVG(props); + } + + private readonly helper = (props, moreProps) => { + const { width } = moreProps; + const { chartConfig: { yScale } } = moreProps; + const [lowerYValue, upperYValue] = yScale.domain(); + + const { price, stroke, strokeDasharray, strokeOpacity, strokeWidth } = props; + const { orient, at, rectWidth, rectHeight, displayFormat, dx } = props; + const { fill, opacity, fontFamily, fontSize, textFill, arrowWidth, lineOpacity, lineStroke } = props; + + const x1 = 0; + const x2 = width; + const edgeAt = (at === "right") + ? width + : 0; + + const type = "horizontal"; + + const y = yScale(price); + const show = (price <= upperYValue && price >= lowerYValue); + + const coordinate = displayFormat(yScale.invert(y)); + const hideLine = false; + + const coordinateProps = { + coordinate, + show, + type, + orient, + edgeAt, + hideLine, + lineOpacity, + lineStroke, + lineStrokeDasharray: strokeDasharray, + stroke, + strokeOpacity, + strokeWidth, + fill: functor(fill)(price), + textFill: functor(textFill)(price), + opacity, fontFamily, fontSize, + rectWidth, + rectHeight, + arrowWidth, + dx, + x1, + x2, + y1: y, + y2: y, + }; + return coordinateProps; + } +} diff --git a/packages/react-financial-charts/src/coordinates/index.ts b/packages/react-financial-charts/src/coordinates/index.ts new file mode 100644 index 000000000..b8e14d1c0 --- /dev/null +++ b/packages/react-financial-charts/src/coordinates/index.ts @@ -0,0 +1,8 @@ +export * from "./EdgeIndicator"; +export * from "./CurrentCoordinate"; +export * from "./MouseCoordinateX"; +export { MouseCoordinateXV2 } from "./MouseCoordinateXV2"; + +export { MouseCoordinateY } from "./MouseCoordinateY"; +export * from "./CrossHairCursor"; +export * from "./PriceCoordinate"; diff --git a/packages/react-financial-charts/src/index.ts b/packages/react-financial-charts/src/index.ts new file mode 100644 index 000000000..579ae713a --- /dev/null +++ b/packages/react-financial-charts/src/index.ts @@ -0,0 +1,2 @@ +export { ChartCanvas } from "./ChartCanvas"; +export { Chart } from "./Chart"; diff --git a/packages/react-financial-charts/src/indicator/atr.ts b/packages/react-financial-charts/src/indicator/atr.ts new file mode 100644 index 000000000..f56da489a --- /dev/null +++ b/packages/react-financial-charts/src/indicator/atr.ts @@ -0,0 +1,33 @@ +import { atr } from "../calculator"; +import { merge, rebind } from "../utils"; + +import baseIndicator from "./baseIndicator"; + +const ALGORITHM_TYPE = "ATR"; + +export default function () { + + const base = baseIndicator() + .type(ALGORITHM_TYPE); + + const underlyingAlgorithm = atr(); + + const mergedAlgorithm = merge() + .algorithm(underlyingAlgorithm) + // @ts-ignore + .merge((datum, i) => { datum.atr = i; }); + + const indicator = function (data, options = { merge: true }) { + if (options.merge) { + if (!base.accessor()) { throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); } + return mergedAlgorithm(data); + } + return underlyingAlgorithm(data); + }; + + rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); + rebind(indicator, underlyingAlgorithm, "options"); + rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); + + return indicator; +} diff --git a/packages/react-financial-charts/src/indicator/baseIndicator.ts b/packages/react-financial-charts/src/indicator/baseIndicator.ts new file mode 100644 index 000000000..c606a5745 --- /dev/null +++ b/packages/react-financial-charts/src/indicator/baseIndicator.ts @@ -0,0 +1,49 @@ +import { overlayColors } from "../utils"; + +let i = 0; + +export default function () { + + let id = i++; + let accessor; + let stroke; + let fill; + let echo; + let type; + + // tslint:disable-next-line: no-empty + function baseIndicator() { + } + + baseIndicator.id = function (x) { + if (!arguments.length) { return id; } + id = x; + return baseIndicator; + }; + baseIndicator.accessor = function (x) { + if (!arguments.length) { return accessor; } + accessor = x; + return baseIndicator; + }; + baseIndicator.stroke = function (x) { + if (!arguments.length) { return !stroke ? stroke = overlayColors(id) : stroke; } + stroke = x; + return baseIndicator; + }; + baseIndicator.fill = function (x) { + if (!arguments.length) { return !fill ? fill = overlayColors(id) : fill; } + fill = x; + return baseIndicator; + }; + baseIndicator.echo = function (x) { + if (!arguments.length) { return echo; } + echo = x; + return baseIndicator; + }; + baseIndicator.type = function (x) { + if (!arguments.length) { return type; } + type = x; + return baseIndicator; + }; + return baseIndicator; +} diff --git a/packages/react-financial-charts/src/indicator/bollingerBand.ts b/packages/react-financial-charts/src/indicator/bollingerBand.ts new file mode 100644 index 000000000..cb92ca79d --- /dev/null +++ b/packages/react-financial-charts/src/indicator/bollingerBand.ts @@ -0,0 +1,33 @@ +import { merge, rebind } from "../utils"; + +import { bollingerband } from "../calculator"; +import baseIndicator from "./baseIndicator"; + +const ALGORITHM_TYPE = "BollingerBand"; + +export default function () { + + const base = baseIndicator() + .type(ALGORITHM_TYPE); + + const underlyingAlgorithm = bollingerband(); + + const mergedAlgorithm = merge() + .algorithm(underlyingAlgorithm) + // @ts-ignore + .merge((datum, i) => { datum.bollingerBand = i; }); + + const indicator = function (data, options = { merge: true }) { + if (options.merge) { + if (!base.accessor()) { throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); } + return mergedAlgorithm(data); + } + return underlyingAlgorithm(data); + }; + + rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); + rebind(indicator, underlyingAlgorithm, "options"); + rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); + + return indicator; +} diff --git a/packages/react-financial-charts/src/indicator/change.ts b/packages/react-financial-charts/src/indicator/change.ts new file mode 100644 index 000000000..bd54e35d1 --- /dev/null +++ b/packages/react-financial-charts/src/indicator/change.ts @@ -0,0 +1,35 @@ +import { merge, rebind } from "../utils"; + +import { change } from "../calculator"; + +import baseIndicator from "./baseIndicator"; + +const ALGORITHM_TYPE = "Change"; + +export default function () { + + const base = baseIndicator() + .type(ALGORITHM_TYPE); + + const underlyingAlgorithm = change(); + + const mergedAlgorithm = merge() + .algorithm(underlyingAlgorithm) + // @ts-ignore + .merge((datum, i) => { + datum.absoluteChange = i.absoluteChange; + datum.percentChange = i.percentChange; + }); + + const indicator = function (data, options = { merge: true }) { + if (options.merge) { + return mergedAlgorithm(data); + } + return underlyingAlgorithm(data); + }; + rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); + rebind(indicator, underlyingAlgorithm, "options"); + rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); + + return indicator; +} diff --git a/packages/react-financial-charts/src/indicator/compare.ts b/packages/react-financial-charts/src/indicator/compare.ts new file mode 100644 index 000000000..f019efcff --- /dev/null +++ b/packages/react-financial-charts/src/indicator/compare.ts @@ -0,0 +1,36 @@ + +import { merge, rebind } from "../utils"; + +import { compare } from "../calculator"; + +import baseIndicator from "./baseIndicator"; + +const ALGORITHM_TYPE = "Compare"; + +export default function () { + + const base = baseIndicator() + .type(ALGORITHM_TYPE) + .accessor((d) => d.compare); + + const underlyingAlgorithm = compare(); + + const mergedAlgorithm = merge() + .algorithm(underlyingAlgorithm) + // @ts-ignore + .merge((datum, i) => { datum.compare = i; }); + + const indicator = function (data, options = { merge: true }) { + if (options.merge) { + if (!base.accessor()) { throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); } + return mergedAlgorithm(data); + } + return underlyingAlgorithm(data); + }; + + rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); + rebind(indicator, underlyingAlgorithm, "options"); + rebind(indicator, mergedAlgorithm, "merge"); + + return indicator; +} diff --git a/packages/react-financial-charts/src/indicator/defaultOptionsForAppearance.ts b/packages/react-financial-charts/src/indicator/defaultOptionsForAppearance.ts new file mode 100644 index 000000000..852039a38 --- /dev/null +++ b/packages/react-financial-charts/src/indicator/defaultOptionsForAppearance.ts @@ -0,0 +1,77 @@ +export const themes = { + light: { + BollingerBand: { + stroke: { + top: "#964B00", + middle: "#000000", + bottom: "#964B00", + }, + fill: "#4682B4", + }, + ElderImpulse: { + stroke: { + up: "#6BA583", + down: "#FF0000", + neutral: "#0000FF", + }, + }, + MACD: { + fill: { + divergence: "#4682B4", + }, + stroke: { + macd: "#FF0000", + signal: "#00F300", + }, + }, + FullStochasticOscillator: { + stroke: { + top: "#37a600", + middle: "#b8ab00", + bottom: "#37a600", + dLine: "#17becf", + kLine: "#ff7f0e", + }, + }, + }, + dark: { + BollingerBand: { + stroke: { + top: "#964B00", + middle: "#FF6600", + bottom: "#964B00", + }, + fill: "#4682B4", + }, + ElderImpulse: { + stroke: { + up: "#6BA583", + down: "#FF0000", + neutral: "#0000FF", + }, + }, + MACD: { + fill: { + divergence: "#FF6600", + }, + stroke: { + macd: "#ea2bff", + signal: "#74d400", + }, + }, + FullStochasticOscillator: { + stroke: { + top: "#37a600", + middle: "#b8ab00", + bottom: "#37a600", + dLine: "#ea2bff", + kLine: "#74d400", + }, + }, + }, +}; + +export const BollingerBand = themes.light.BollingerBand; +export const ElderImpulse = themes.light.ElderImpulse; +export const MACD = themes.light.MACD; +export const FullStochasticOscillator = themes.light.FullStochasticOscillator; diff --git a/packages/react-financial-charts/src/indicator/elderImpulse.ts b/packages/react-financial-charts/src/indicator/elderImpulse.ts new file mode 100644 index 000000000..b3f6ec3bf --- /dev/null +++ b/packages/react-financial-charts/src/indicator/elderImpulse.ts @@ -0,0 +1,68 @@ +import { isDefined, isNotDefined, merge, rebind, slidingWindow } from "../utils"; + +import baseIndicator from "./baseIndicator"; +import { ElderImpulse as appearanceOptions } from "./defaultOptionsForAppearance"; + +const ALGORITHM_TYPE = "ElderImpulse"; + +export default function () { + + let macdSource; + let emaSource; + + const base = baseIndicator() + .type(ALGORITHM_TYPE) + .stroke(appearanceOptions.stroke) + .fill(undefined); + + const underlyingAlgorithm = slidingWindow() + .windowSize(2) + // @ts-ignore + .undefinedValue("neutral") + .accumulator(([prev, curr]) => { + if (isNotDefined(macdSource)) { throw new Error(`macdSource not defined for ${ALGORITHM_TYPE} calculator`); } + if (isNotDefined(emaSource)) { throw new Error(`emaSource not defined for ${ALGORITHM_TYPE} calculator`); } + + if (isDefined(macdSource(prev)) && isDefined(emaSource(prev))) { + const prevMACDDivergence = macdSource(prev).divergence; + const currMACDDivergence = macdSource(curr).divergence; + + const prevEMA = emaSource(prev); + const currEMA = emaSource(curr); + + if (currMACDDivergence >= prevMACDDivergence + && currEMA >= prevEMA) { return "up"; } + + if (currMACDDivergence <= prevMACDDivergence + && currEMA <= prevEMA) { return "down"; } + } + return "neutral"; + }); + + const mergedAlgorithm = merge() + .algorithm(underlyingAlgorithm) + // @ts-ignore + .merge((datum, i) => { datum.elderImpulse = i; }); + + const indicator = function (data, options = { merge: true }) { + const newData = options.merge + ? mergedAlgorithm(data) + : underlyingAlgorithm(data); + + return newData; + }; + indicator.macdSource = function (x) { + if (!arguments.length) { return macdSource; } + macdSource = x; + return indicator; + }; + indicator.emaSource = function (x) { + if (!arguments.length) { return emaSource; } + emaSource = x; + return indicator; + }; + rebind(indicator, base, "id", "echo", "type", "stroke"); + rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); + + return indicator; +} diff --git a/packages/react-financial-charts/src/indicator/elderRay.ts b/packages/react-financial-charts/src/indicator/elderRay.ts new file mode 100644 index 000000000..da9555642 --- /dev/null +++ b/packages/react-financial-charts/src/indicator/elderRay.ts @@ -0,0 +1,34 @@ +import { elderRay } from "../calculator"; +import { merge, rebind } from "../utils"; + +import baseIndicator from "./baseIndicator"; + +const ALGORITHM_TYPE = "ElderRay"; + +export default function () { + + const base = baseIndicator() + .type(ALGORITHM_TYPE) + .accessor((d) => d.elderRay); + + const underlyingAlgorithm = elderRay(); + + const mergedAlgorithm = merge() + .algorithm(underlyingAlgorithm) + // @ts-ignore + .merge((datum, i) => { datum.elderRay = i; }); + + const indicator = function (data, options = { merge: true }) { + if (options.merge) { + if (!base.accessor()) { throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); } + return mergedAlgorithm(data); + } + return underlyingAlgorithm(data); + }; + + rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); + rebind(indicator, underlyingAlgorithm, "options"); + rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); + + return indicator; +} diff --git a/packages/react-financial-charts/src/indicator/ema.ts b/packages/react-financial-charts/src/indicator/ema.ts new file mode 100644 index 000000000..66110731e --- /dev/null +++ b/packages/react-financial-charts/src/indicator/ema.ts @@ -0,0 +1,34 @@ +import { merge, rebind } from "../utils"; + +import { ema } from "../calculator"; +import baseIndicator from "./baseIndicator"; + +const ALGORITHM_TYPE = "EMA"; + +export default function () { + + const base = baseIndicator() + .type(ALGORITHM_TYPE) + .accessor((d) => d.ema); + + const underlyingAlgorithm = ema(); + + const mergedAlgorithm = merge() + .algorithm(underlyingAlgorithm) + // @ts-ignore + .merge((datum, i) => { datum.ema = i; }); + + const indicator = function (data, options = { merge: true }) { + if (options.merge) { + if (!base.accessor()) { throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); } + return mergedAlgorithm(data); + } + return underlyingAlgorithm(data); + }; + + rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); + rebind(indicator, underlyingAlgorithm, "options", "undefinedLength"); + rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); + + return indicator; +} diff --git a/packages/react-financial-charts/src/indicator/forceIndex.ts b/packages/react-financial-charts/src/indicator/forceIndex.ts new file mode 100644 index 000000000..0023410c6 --- /dev/null +++ b/packages/react-financial-charts/src/indicator/forceIndex.ts @@ -0,0 +1,35 @@ +import { merge, rebind } from "../utils"; + +import { forceIndex } from "../calculator"; + +import baseIndicator from "./baseIndicator"; + +const ALGORITHM_TYPE = "ForceIndex"; + +export default function () { + + const base = baseIndicator() + .type(ALGORITHM_TYPE) + .accessor((d) => d.forceIndex); + + const underlyingAlgorithm = forceIndex(); + + const mergedAlgorithm = merge() + .algorithm(underlyingAlgorithm) + // @ts-ignore + .merge((datum, i) => { datum.forceIndex = i; }); + + const indicator = function (data, options = { merge: true }) { + if (options.merge) { + if (!base.accessor()) { throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); } + return mergedAlgorithm(data); + } + return underlyingAlgorithm(data); + }; + + rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); + rebind(indicator, underlyingAlgorithm, "options"); + rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); + + return indicator; +} diff --git a/packages/react-financial-charts/src/indicator/heikinAshi.ts b/packages/react-financial-charts/src/indicator/heikinAshi.ts new file mode 100644 index 000000000..dd2c1c748 --- /dev/null +++ b/packages/react-financial-charts/src/indicator/heikinAshi.ts @@ -0,0 +1,35 @@ +import { heikinAshi } from "../calculator"; +import baseIndicator from "./baseIndicator"; + +import { merge, rebind } from "../utils"; + +const ALGORITHM_TYPE = "HeikinAshi"; + +export default function () { + + const base = baseIndicator() + .type(ALGORITHM_TYPE) + .accessor((d) => d.ha); + + const underlyingAlgorithm = heikinAshi(); + + const mergedAlgorithm = merge() + .algorithm(underlyingAlgorithm) + // @ts-ignore + .merge((datum, i) => { + return { ...datum, ...i }; + }); + + const indicator = function (data, options = { merge: true }) { + if (options.merge) { + if (!base.accessor()) { throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); } + return mergedAlgorithm(data); + } + return underlyingAlgorithm(data); + }; + + rebind(indicator, base, "accessor", "stroke", "fill", "echo", "type"); + rebind(indicator, mergedAlgorithm, "merge"); + + return indicator; +} diff --git a/packages/react-financial-charts/src/indicator/index.ts b/packages/react-financial-charts/src/indicator/index.ts new file mode 100644 index 000000000..3b98b7b01 --- /dev/null +++ b/packages/react-financial-charts/src/indicator/index.ts @@ -0,0 +1,27 @@ +export { default as ema } from "./ema"; +export { default as sma } from "./sma"; +export { default as wma } from "./wma"; +export { default as tma } from "./tma"; +export { default as bollingerBand } from "./bollingerBand"; +export { default as heikinAshi } from "./heikinAshi"; +export { default as kagi } from "./kagi"; +export { default as pointAndFigure } from "./pointAndFigure"; +export { default as renko } from "./renko"; +export { default as macd } from "./macd"; +export { default as rsi } from "./rsi"; +export { default as atr } from "./atr"; +export { default as stochasticOscillator } from "./stochasticOscillator"; +export { default as forceIndex } from "./forceIndex"; +export { default as sar } from "./sar"; +export { default as elderRay } from "./elderRay"; +export { default as change } from "./change"; +export { default as elderImpulse } from "./elderImpulse"; +export { default as compare } from "./compare"; + +import * as defaultOptionsForComputation from "../calculator/defaultOptionsForComputation"; +import * as defaultOptionsForAppearance from "./defaultOptionsForAppearance"; + +export { + defaultOptionsForComputation, + defaultOptionsForAppearance +}; diff --git a/packages/react-financial-charts/src/indicator/kagi.ts b/packages/react-financial-charts/src/indicator/kagi.ts new file mode 100644 index 000000000..3fccdf801 --- /dev/null +++ b/packages/react-financial-charts/src/indicator/kagi.ts @@ -0,0 +1,22 @@ +import { rebind } from "../utils"; + +import { kagi } from "../calculator"; +import baseIndicator from "./baseIndicator"; + +const ALGORITHM_TYPE = "Kagi"; + +export default function () { + + const base = baseIndicator() + .type(ALGORITHM_TYPE); + + const underlyingAlgorithm = kagi(); + + const indicator = underlyingAlgorithm; + + rebind(indicator, base, "id", "stroke", "fill", "echo", "type"); + rebind(indicator, underlyingAlgorithm, "dateAccessor", "dateMutator"); + rebind(indicator, underlyingAlgorithm, "options"); + + return indicator; +} diff --git a/packages/react-financial-charts/src/indicator/macd.ts b/packages/react-financial-charts/src/indicator/macd.ts new file mode 100644 index 000000000..33995e182 --- /dev/null +++ b/packages/react-financial-charts/src/indicator/macd.ts @@ -0,0 +1,38 @@ +import { merge, rebind } from "../utils"; + +import { macd } from "../calculator"; + +import baseIndicator from "./baseIndicator"; +import { MACD as appearanceOptions } from "./defaultOptionsForAppearance"; + +const ALGORITHM_TYPE = "MACD"; + +export default function () { + + const base = baseIndicator() + .type(ALGORITHM_TYPE) + .fill(appearanceOptions.fill) + .stroke(appearanceOptions.stroke) + .accessor((d) => d.macd); + + const underlyingAlgorithm = macd(); + + const mergedAlgorithm = merge() + .algorithm(underlyingAlgorithm) + // @ts-ignore + .merge((datum, i) => { datum.macd = i; }); + + const indicator = function (data, options = { merge: true }) { + if (options.merge) { + if (!base.accessor()) { throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); } + return mergedAlgorithm(data); + } + return underlyingAlgorithm(data); + }; + + rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); + rebind(indicator, underlyingAlgorithm, "options", "undefinedLength"); + rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); + + return indicator; +} diff --git a/packages/react-financial-charts/src/indicator/pointAndFigure.ts b/packages/react-financial-charts/src/indicator/pointAndFigure.ts new file mode 100644 index 000000000..673f13956 --- /dev/null +++ b/packages/react-financial-charts/src/indicator/pointAndFigure.ts @@ -0,0 +1,22 @@ +import { rebind } from "../utils"; + +import { pointAndFigure } from "../calculator"; +import baseIndicator from "./baseIndicator"; + +const ALGORITHM_TYPE = "PointAndFigure"; + +export default function () { + + const base = baseIndicator() + .type(ALGORITHM_TYPE); + + const underlyingAlgorithm = pointAndFigure(); + + const indicator = underlyingAlgorithm; + + rebind(indicator, base, "id", "stroke", "fill", "echo", "type"); + rebind(indicator, underlyingAlgorithm, "dateAccessor", "dateMutator"); + rebind(indicator, underlyingAlgorithm, "options"); + + return indicator; +} diff --git a/packages/react-financial-charts/src/indicator/renko.ts b/packages/react-financial-charts/src/indicator/renko.ts new file mode 100644 index 000000000..27208b8f1 --- /dev/null +++ b/packages/react-financial-charts/src/indicator/renko.ts @@ -0,0 +1,22 @@ +import { rebind } from "../utils"; + +import { renko } from "../calculator"; +import baseIndicator from "./baseIndicator"; + +const ALGORITHM_TYPE = "Renko"; + +export default function () { + + const base = baseIndicator() + .type(ALGORITHM_TYPE); + + const underlyingAlgorithm = renko(); + + const indicator = underlyingAlgorithm; + + rebind(indicator, base, "id", "stroke", "fill", "echo", "type"); + rebind(indicator, underlyingAlgorithm, "dateAccessor", "dateMutator"); + rebind(indicator, underlyingAlgorithm, "options"); + + return indicator; +} diff --git a/packages/react-financial-charts/src/indicator/rsi.ts b/packages/react-financial-charts/src/indicator/rsi.ts new file mode 100644 index 000000000..889490892 --- /dev/null +++ b/packages/react-financial-charts/src/indicator/rsi.ts @@ -0,0 +1,34 @@ +import { merge, rebind } from "../utils"; + +import { rsi } from "../calculator"; + +import baseIndicator from "./baseIndicator"; + +const ALGORITHM_TYPE = "RSI"; + +export default function () { + const base = baseIndicator() + .type(ALGORITHM_TYPE) + .accessor((d) => d.rsi); + + const underlyingAlgorithm = rsi(); + + const mergedAlgorithm = merge() + .algorithm(underlyingAlgorithm) + // @ts-ignore + .merge((datum, i) => { datum.rsi = i; }); + + const indicator = function (data, options = { merge: true }) { + if (options.merge) { + if (!base.accessor()) { throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); } + return mergedAlgorithm(data); + } + return underlyingAlgorithm(data); + }; + + rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); + rebind(indicator, underlyingAlgorithm, "options", "undefinedLength"); + rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); + + return indicator; +} diff --git a/packages/react-financial-charts/src/indicator/sar.ts b/packages/react-financial-charts/src/indicator/sar.ts new file mode 100644 index 000000000..f438b80c3 --- /dev/null +++ b/packages/react-financial-charts/src/indicator/sar.ts @@ -0,0 +1,36 @@ +import { merge, rebind } from "../utils"; + +import { sar } from "../calculator"; + +import baseIndicator from "./baseIndicator"; + +const ALGORITHM_TYPE = "SMA"; + +export default function () { + + const base = baseIndicator() + .type(ALGORITHM_TYPE) + .accessor((d) => d.sar); + + const underlyingAlgorithm = sar(); + + const mergedAlgorithm = merge() + .algorithm(underlyingAlgorithm) + // @ts-ignore + .merge((datum, i) => { datum.sar = i; }); + + const indicator = function (data, options = { merge: true }) { + if (options.merge) { + if (!base.accessor()) { throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); } + return mergedAlgorithm(data); + } + return underlyingAlgorithm(data); + }; + + rebind(indicator, base, "id", "accessor", "stroke", "echo", "type"); + rebind(indicator, underlyingAlgorithm, "undefinedLength"); + rebind(indicator, underlyingAlgorithm, "options"); + rebind(indicator, mergedAlgorithm, "merge"); + + return indicator; +} diff --git a/packages/react-financial-charts/src/indicator/sma.ts b/packages/react-financial-charts/src/indicator/sma.ts new file mode 100644 index 000000000..a4b1fda15 --- /dev/null +++ b/packages/react-financial-charts/src/indicator/sma.ts @@ -0,0 +1,34 @@ +import { merge, rebind } from "../utils"; + +import { sma } from "../calculator"; +import baseIndicator from "./baseIndicator"; + +const ALGORITHM_TYPE = "SMA"; + +export default function () { + + const base = baseIndicator() + .type(ALGORITHM_TYPE) + .accessor((d) => d.sma); + + const underlyingAlgorithm = sma(); + + const mergedAlgorithm = merge() + .algorithm(underlyingAlgorithm) + // @ts-ignore + .merge((datum, i) => { datum.sma = i; }); + + const indicator = function (data, options = { merge: true }) { + if (options.merge) { + if (!base.accessor()) { throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); } + return mergedAlgorithm(data); + } + return underlyingAlgorithm(data); + }; + rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); + rebind(indicator, underlyingAlgorithm, "undefinedLength"); + rebind(indicator, underlyingAlgorithm, "options"); + rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); + + return indicator; +} diff --git a/packages/react-financial-charts/src/indicator/stochasticOscillator.ts b/packages/react-financial-charts/src/indicator/stochasticOscillator.ts new file mode 100644 index 000000000..036dc7ef7 --- /dev/null +++ b/packages/react-financial-charts/src/indicator/stochasticOscillator.ts @@ -0,0 +1,33 @@ +import { merge, rebind } from "../utils"; + +import { sto } from "../calculator"; + +import baseIndicator from "./baseIndicator"; + +const ALGORITHM_TYPE = "STO"; + +export default function () { + const base = baseIndicator() + .type(ALGORITHM_TYPE); + + const underlyingAlgorithm = sto(); + + const mergedAlgorithm = merge() + .algorithm(underlyingAlgorithm) + // @ts-ignore + .merge((datum, i) => { datum.sto = i; }); + + const indicator = function (data, options = { merge: true }) { + if (options.merge) { + if (!base.accessor()) { throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); } + return mergedAlgorithm(data); + } + return underlyingAlgorithm(data); + }; + + rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); + rebind(indicator, underlyingAlgorithm, "options", "undefinedLength"); + rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); + + return indicator; +} diff --git a/packages/react-financial-charts/src/indicator/tma.ts b/packages/react-financial-charts/src/indicator/tma.ts new file mode 100644 index 000000000..e7d14575e --- /dev/null +++ b/packages/react-financial-charts/src/indicator/tma.ts @@ -0,0 +1,35 @@ +import { merge, rebind } from "../utils"; + +import { tma } from "../calculator"; + +import baseIndicator from "./baseIndicator"; + +const ALGORITHM_TYPE = "TMA"; + +export default function () { + + const base = baseIndicator() + .type(ALGORITHM_TYPE) + .accessor((d) => d.tma); + + const underlyingAlgorithm = tma(); + + const mergedAlgorithm = merge() + .algorithm(underlyingAlgorithm) + // @ts-ignore + .merge((datum, i) => { datum.tma = i; }); + + const indicator = function (data, options = { merge: true }) { + if (options.merge) { + if (!base.accessor()) { throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); } + return mergedAlgorithm(data); + } + return underlyingAlgorithm(data); + }; + rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); + rebind(indicator, underlyingAlgorithm, "undefinedLength"); + rebind(indicator, underlyingAlgorithm, "options"); + rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); + + return indicator; +} diff --git a/packages/react-financial-charts/src/indicator/wma.ts b/packages/react-financial-charts/src/indicator/wma.ts new file mode 100644 index 000000000..9d531897d --- /dev/null +++ b/packages/react-financial-charts/src/indicator/wma.ts @@ -0,0 +1,36 @@ +import { merge, rebind } from "../utils"; + +import { wma } from "../calculator"; + +import baseIndicator from "./baseIndicator"; + +const ALGORITHM_TYPE = "WMA"; + +export default function () { + + const base = baseIndicator() + .type(ALGORITHM_TYPE) + .accessor((d) => d.wma); + + const underlyingAlgorithm = wma(); + + const mergedAlgorithm = merge() + .algorithm(underlyingAlgorithm) + // @ts-ignore + .merge((datum, i) => { datum.wma = i; }); + + const indicator = function (data, options = { merge: true }) { + if (options.merge) { + if (!base.accessor()) { throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); } + return mergedAlgorithm(data); + } + return underlyingAlgorithm(data); + }; + + rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); + rebind(indicator, underlyingAlgorithm, "undefinedLength"); + rebind(indicator, underlyingAlgorithm, "options"); + rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); + + return indicator; +} diff --git a/packages/react-financial-charts/src/interactive/Brush.tsx b/packages/react-financial-charts/src/interactive/Brush.tsx new file mode 100644 index 000000000..d15376317 --- /dev/null +++ b/packages/react-financial-charts/src/interactive/Brush.tsx @@ -0,0 +1,207 @@ +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getMouseCanvas } from "../GenericComponent"; +import { + getStrokeDasharray, + hexToRGBA, + isDefined, + noop, +} from "../utils"; + +interface BrushProps { + enabled: boolean; + onStart: any; // func + onBrush: any; // func + type?: "1D" | "2D"; + stroke?: string; + fill?: string; + strokeOpacity?: number; + fillOpacity?: number; + interactiveState: object; + strokeDashArray?: string; +} + +interface BrushState { + end?: any; + rect: any | null; + selected?: boolean; + start?: any; + x1y1?: any; +} + +export class Brush extends React.Component { + + public static defaultProps = { + type: "2D", + stroke: "#000000", + fillOpacity: 0.3, + strokeOpacity: 1, + fill: "#3h3h3h", + onBrush: noop, + onStart: noop, + strokeDashArray: "ShortDash", + }; + + private zoomHappening?: boolean; + + constructor(props: BrushProps, context) { + super(props, context); + + this.terminate = this.terminate.bind(this); + this.state = { + rect: null, + }; + } + + public terminate() { + this.zoomHappening = false; + this.setState({ + x1y1: null, + start: null, + end: null, + rect: null, + }); + } + + public render() { + const { enabled } = this.props; + if (!enabled) { + return null; + } + + return ( + + ); + } + + private readonly drawOnCanvas = (ctx) => { + const { rect } = this.state; + if (isDefined(rect)) { + const { x, y, height, width } = rect; + const { stroke, fill, strokeDashArray } = this.props; + const { strokeOpacity, fillOpacity } = this.props; + + const dashArray = getStrokeDasharray(strokeDashArray) + .split(",") + .map((d) => +d); + + ctx.strokeStyle = hexToRGBA(stroke, strokeOpacity); + ctx.fillStyle = hexToRGBA(fill, fillOpacity); + ctx.setLineDash(dashArray); + ctx.beginPath(); + ctx.fillRect(x, y, width, height); + ctx.strokeRect(x, y, width, height); + } + } + + private readonly handleZoomStart = (moreProps) => { + this.zoomHappening = false; + const { + mouseXY: [, mouseY], + currentItem, + chartConfig: { yScale }, + xAccessor, + xScale, + } = moreProps; + + const x1y1 = [ + xScale(xAccessor(currentItem)), + mouseY, + ]; + + this.setState({ + selected: true, + x1y1, + start: { + item: currentItem, + xValue: xAccessor(currentItem), + yValue: yScale.invert(mouseY), + }, + }); + } + + private readonly handleDrawSquare = (moreProps) => { + if (this.state.x1y1 == null) { return; } + + this.zoomHappening = true; + + const { + mouseXY: [, mouseY], + currentItem, + chartConfig: { yScale }, + xAccessor, + xScale, + } = moreProps; + + const [x2, y2] = [ + xScale(xAccessor(currentItem)), + mouseY, + ]; + + const { x1y1: [x1, y1] } = this.state; + + const x = Math.min(x1, x2); + const y = Math.min(y1, y2); + const height = Math.abs(y2 - y1); + const width = Math.abs(x2 - x1); + + this.setState({ + selected: true, + end: { + item: currentItem, + xValue: xAccessor(currentItem), + yValue: yScale.invert(mouseY), + }, + rect: { + x, y, height, width, + }, + }); + } + + private readonly handleZoomComplete = (moreProps) => { + if (this.zoomHappening) { + const { onBrush } = this.props; + const { start, end } = this.state; + onBrush({ start, end }, moreProps); + } + this.setState({ + selected: false, + rect: null, + }); + } + + private readonly renderSVG = () => { + const { rect } = this.state; + if (isDefined(rect)) { + const { x, y, height, width } = rect; + const { stroke, strokeDashArray } = this.props; + const { strokeOpacity, fillOpacity } = this.props; + + const dashArray = getStrokeDasharray(strokeDashArray); + + return ( + + ); + } + } +} diff --git a/packages/react-financial-charts/src/interactive/ClickCallback.tsx b/packages/react-financial-charts/src/interactive/ClickCallback.tsx new file mode 100644 index 000000000..bad4bc82c --- /dev/null +++ b/packages/react-financial-charts/src/interactive/ClickCallback.tsx @@ -0,0 +1,51 @@ +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getMouseCanvas } from "../GenericComponent"; +import { noop } from "../utils"; + +interface ClickCallbackProps { + disablePan: boolean; + onMouseDown?: any; // func + onClick?: any; // func + onDoubleClick?: any; // func + onContextMenu?: any; // func + onMouseMove?: any; // func + onPan?: any; // func + onPanEnd?: any; // func +} + +export class ClickCallback extends React.Component { + + public static defaultProps = { + disablePan: false, + }; + + public render() { + const { + onMouseDown, + onClick, + onDoubleClick, + onContextMenu, + onMouseMove, + onPan, + onPanEnd, + } = this.props; + + return ( + + ); + } +} diff --git a/packages/react-financial-charts/src/interactive/DrawingObjectSelector.tsx b/packages/react-financial-charts/src/interactive/DrawingObjectSelector.tsx new file mode 100644 index 000000000..29bf0f6c5 --- /dev/null +++ b/packages/react-financial-charts/src/interactive/DrawingObjectSelector.tsx @@ -0,0 +1,105 @@ +import * as React from "react"; +import { head, isDefined, mapObject, noop } from "../utils"; +import { getMorePropsForChart, getSelected } from "./utils"; + +import GenericComponent, { getMouseCanvas } from "../GenericComponent"; + +interface DrawingObjectSelectorProps { + readonly getInteractiveNodes: any; // func + readonly onSelect: any; // func + readonly onDoubleClick: any; // func + readonly drawingObjectMap: object; + readonly enabled: boolean; +} + +export class DrawingObjectSelector extends React.Component { + + public static defaultProps = { + enabled: true, + onDoubleClick: noop, + }; + + public render() { + return ( + + ); + } + + private readonly getInteraction = (moreProps) => { + const { getInteractiveNodes, drawingObjectMap } = this.props; + const interactiveNodes = getInteractiveNodes(); + const interactives = mapObject(interactiveNodes, (each) => { + const key = drawingObjectMap[each.type]; + + const valueArray = isDefined(key) + ? each.node.props[key] + : undefined; + + const valuePresent = isDefined(valueArray) + && Array.isArray(valueArray) + && valueArray.length > 0; + if (valuePresent) { + // console.log("Value present for ", each.type, each.chartId); + const morePropsForChart = getMorePropsForChart( + moreProps, each.chartId, + ); + + const objects = each.node.getSelectionState(morePropsForChart); + + return { + type: each.type, + chartId: each.chartId, + objects, + }; + } + return { + type: each.type, + chartId: each.chartId, + objects: [], + }; + }); + + return interactives; + } + + private readonly handleClick = (moreProps, e) => { + e.preventDefault(); + const { onSelect } = this.props; + const { enabled } = this.props; + if (!enabled) { return; } + + const interactives = this.getInteraction(moreProps); + + onSelect(interactives, moreProps); + } + + private readonly handleDoubleClick = (moreProps, e) => { + e.preventDefault(); + const { onDoubleClick } = this.props; + const { enabled } = this.props; + if (!enabled) { return; } + + const interactives = this.getInteraction(moreProps); + const allSelected = getSelected(interactives); + + if (allSelected.length > 0) { + const selected = head(allSelected); + const item = { + type: selected.type, + chartId: selected.chartId, + object: head(selected.objects), + }; + const morePropsForChart = getMorePropsForChart( + moreProps, selected.chartId, + ); + onDoubleClick(item, morePropsForChart); + } + } +} diff --git a/packages/react-financial-charts/src/interactive/EquidistantChannel.tsx b/packages/react-financial-charts/src/interactive/EquidistantChannel.tsx new file mode 100644 index 000000000..11bacfec4 --- /dev/null +++ b/packages/react-financial-charts/src/interactive/EquidistantChannel.tsx @@ -0,0 +1,254 @@ +import * as React from "react"; + +import { isDefined, isNotDefined, noop } from "../utils"; +import { HoverTextNearMouse } from "./components/HoverTextNearMouse"; +import { MouseLocationIndicator } from "./components/MouseLocationIndicator"; +import { getSlope, getYIntercept } from "./components/StraightLine"; +import { + isHoverForInteractiveType, + saveNodeType, + terminate, +} from "./utils"; +import { EachEquidistantChannel } from "./wrapper/EachEquidistantChannel"; + +interface EquidistantChannelProps { + enabled: boolean; + onStart: any; // func + onComplete: any; // func + onSelect: any; // func + currentPositionStroke?: string; + currentPositionStrokeWidth?: number; + currentPositionOpacity?: number; + currentPositionRadius?: number; + hoverText: object; + channels: any[]; + appearance: { + stroke: string; + strokeOpacity: number; + strokeWidth: number; + fill: string; + fillOpacity: number; + edgeStroke: string; + edgeFill: string; + edgeFill2: string; + edgeStrokeWidth: number; + r: number; + }; +} + +interface EquidistantChannelState { + current?: any; + override?: any; +} + +export class EquidistantChannel extends React.Component { + + public static defaultProps = { + onStart: noop, + onComplete: noop, + onSelect: noop, + currentPositionStroke: "#000000", + currentPositionOpacity: 1, + currentPositionStrokeWidth: 3, + currentPositionRadius: 4, + hoverText: { + ...HoverTextNearMouse.defaultProps, + enable: true, + bgHeight: 18, + bgWidth: 120, + text: "Click to select object", + }, + channels: [], + appearance: { + stroke: "#000000", + strokeOpacity: 1, + strokeWidth: 1, + fill: "#8AAFE2", + fillOpacity: 0.7, + edgeStroke: "#000000", + edgeFill: "#FFFFFF", + edgeFill2: "#250B98", + edgeStrokeWidth: 1, + r: 5, + }, + }; + + // @ts-ignore + private terminate: () => void; + private saveNodeType; + // @ts-ignore + private getSelectionState; + private mouseMoved; + + public constructor(props) { + super(props); + + this.terminate = terminate.bind(this); + this.saveNodeType = saveNodeType.bind(this); + + this.getSelectionState = isHoverForInteractiveType("channels") + .bind(this); + + this.state = { + }; + } + + public render() { + const { appearance } = this.props; + const { enabled } = this.props; + const { currentPositionRadius, currentPositionStroke } = this.props; + const { currentPositionOpacity, currentPositionStrokeWidth } = this.props; + const { channels, hoverText } = this.props; + const { current, override } = this.state; + const overrideIndex = isDefined(override) ? override.index : null; + + const tempChannel = isDefined(current) && isDefined(current.endXY) + ? + : null; + + return + {channels.map((each, idx) => { + const eachAppearance = isDefined(each.appearance) + ? { ...appearance, ...each.appearance } + : appearance; + + return ; + })} + {tempChannel} + + ; + } + + private readonly handleDragChannel = (index, newXYValue) => { + this.setState({ + override: { + index, + ...newXYValue, + }, + }); + } + + private readonly handleDragChannelComplete = (moreProps) => { + const { override } = this.state; + const { channels } = this.props; + + if (isDefined(override)) { + const { index, ...rest } = override; + const newChannels = channels + .map((each, idx) => idx === index + ? { ...each, ...rest, selected: true } + : each); + + this.setState({ + override: null, + }, () => { + this.props.onComplete(newChannels, moreProps); + }); + } + } + + private readonly handleStart = (xyValue) => { + const { current } = this.state; + + if (isNotDefined(current) || isNotDefined(current.startXY)) { + this.mouseMoved = false; + this.setState({ + current: { + startXY: xyValue, + endXY: null, + }, + }, () => { + this.props.onStart(); + }); + } + } + + private readonly handleEnd = (xyValue, moreProps, e) => { + const { current } = this.state; + const { channels, appearance } = this.props; + + if (this.mouseMoved + && isDefined(current) + && isDefined(current.startXY) + ) { + + if (isNotDefined(current.dy)) { + this.setState({ + current: { + ...current, + dy: 0, + }, + }); + } else { + const newChannels = [ + ...channels.map((d) => ({ ...d, selected: false })), + { + ...current, selected: true, + appearance, + }, + ]; + + this.setState({ + current: null, + }, () => { + + this.props.onComplete(newChannels, moreProps, e); + }); + } + } + } + + private readonly handleDrawChannel = (xyValue) => { + const { current } = this.state; + + if (isDefined(current) + && isDefined(current.startXY)) { + this.mouseMoved = true; + if (isNotDefined(current.dy)) { + this.setState({ + current: { + startXY: current.startXY, + endXY: xyValue, + }, + }); + } else { + const m = getSlope(current.startXY, current.endXY); + const b = getYIntercept(m, current.endXY); + + // @ts-ignore + const y = m * xyValue[0] + b; + const dy = xyValue[1] - y; + + this.setState({ + current: { + ...current, + dy, + }, + }); + } + } + } +} diff --git a/packages/react-financial-charts/src/interactive/FibonacciRetracement.tsx b/packages/react-financial-charts/src/interactive/FibonacciRetracement.tsx new file mode 100644 index 000000000..4ac7fb06f --- /dev/null +++ b/packages/react-financial-charts/src/interactive/FibonacciRetracement.tsx @@ -0,0 +1,304 @@ +import * as React from "react"; + +import { isDefined, isNotDefined, noop } from "../utils"; +import { HoverTextNearMouse } from "./components/HoverTextNearMouse"; +import { MouseLocationIndicator } from "./components/MouseLocationIndicator"; +import { + isHoverForInteractiveType, + saveNodeType, + terminate, +} from "./utils"; +import { EachFibRetracement } from "./wrapper/EachFibRetracement"; + +interface FibonacciRetracementProps { + enabled: boolean; + width?: number; + onStart?: any; // func + onComplete?: any; // func + onSelect?: any; // func + type: + "EXTEND" | // extends from -Infinity to +Infinity + "RAY" | // extends to +/-Infinity in one direction + "BOUND"; // extends between the set bounds + hoverText: object; + currentPositionStroke?: string; + currentPositionStrokeWidth?: number; + currentPositionOpacity?: number; + currentPositionRadius?: number; + retracements: any[]; + appearance: { + stroke: string; + strokeWidth: number; + strokeOpacity: number; + fontFamily: string; + fontSize: number; + fontFill: string; + edgeStroke: string; + edgeFill: string; + nsEdgeFill: string; + edgeStrokeWidth: number; + r: number; + }; +} + +interface FibonacciRetracementState { + current?: any; + override?: any; +} + +export class FibonacciRetracement extends React.Component { + + public static defaultProps = { + enabled: true, + type: "RAY", + retracements: [], + onStart: noop, + onComplete: noop, + onSelect: noop, + hoverText: { + ...HoverTextNearMouse.defaultProps, + enable: true, + bgHeight: "auto", + bgWidth: "auto", + text: "Click to select object", + selectedText: "", + }, + currentPositionStroke: "#000000", + currentPositionOpacity: 1, + currentPositionStrokeWidth: 3, + currentPositionRadius: 4, + appearance: { + stroke: "#000000", + strokeWidth: 1, + strokeOpacity: 1, + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 11, + fontFill: "#000000", + edgeStroke: "#000000", + edgeFill: "#FFFFFF", + nsEdgeFill: "#000000", + edgeStrokeWidth: 1, + r: 5, + }, + }; + + // @ts-ignore + private getSelectionState; + private mouseMoved; + private saveNodeType; + + // @ts-ignore + private terminate; + + constructor(props) { + super(props); + + this.handleEdge1Drag = this.handleEdge1Drag.bind(this); + this.handleEdge2Drag = this.handleEdge2Drag.bind(this); + + this.terminate = terminate.bind(this); + this.getSelectionState = isHoverForInteractiveType("retracements") + .bind(this); + + this.saveNodeType = saveNodeType.bind(this); + this.state = {}; + } + + public render() { + const { current, override } = this.state; + const { retracements } = this.props; + + const { + appearance, + type, + } = this.props; + const { + currentPositionStroke, + currentPositionOpacity, + currentPositionStrokeWidth, + currentPositionRadius, + } = this.props; + + const { enabled, hoverText } = this.props; + const overrideIndex = isDefined(override) ? override.index : null; + const hoverTextWidthDefault = { + ...FibonacciRetracement.defaultProps.hoverText, + ...hoverText, + }; + + const currentRetracement = isDefined(current) && isDefined(current.x2) + ? + : null; + return ( + + {retracements.map((each, idx) => { + const eachAppearance = isDefined(each.appearance) + ? { ...appearance, ...each.appearance } + : appearance; + + const eachHoverText = isDefined(each.hoverText) + ? { ...hoverTextWidthDefault, ...each.hoverText } + : hoverTextWidthDefault; + + return ( + + ); + })} + {currentRetracement} + + + ); + } + + private readonly handleDrawRetracement = (xyValue) => { + const { current } = this.state; + if (isDefined(current) && isDefined(current.x1)) { + this.mouseMoved = true; + this.setState({ + current: { + ...current, + x2: xyValue[0], + y2: xyValue[1], + }, + }); + } + } + + private readonly handleEdge1Drag = (echo, newXYValue, origXYValue) => { + const { retracements } = this.props; + const { index } = echo; + + const dx = origXYValue.x1Value - newXYValue.x1Value; + + this.setState({ + override: { + index, + x1: retracements[index].x1 - dx, + y1: retracements[index].y1, + x2: retracements[index].x2, + y2: retracements[index].y2, + }, + }); + } + + private readonly handleDrag = (index, xy) => { + this.setState({ + override: { + index, + ...xy, + }, + }); + } + + private readonly handleEdge2Drag = (echo, newXYValue, origXYValue) => { + const { retracements } = this.props; + const { index } = echo; + + const dx = origXYValue.x2Value - newXYValue.x2Value; + + this.setState({ + override: { + index, + x1: retracements[index].x1, + y1: retracements[index].y1, + x2: retracements[index].x2 - dx, + y2: retracements[index].y2, + }, + }); + } + + private readonly handleDragComplete = (moreProps) => { + const { retracements } = this.props; + const { override } = this.state; + if (isDefined(override)) { + const { index, ...rest } = override; + + const newRetracements = retracements.map( + (each, idx) => + (idx === index + ? { ...each, ...rest, selected: true } + : each), + ); + this.setState( + { + override: null, + }, + () => { + this.props.onComplete(newRetracements, moreProps); + }, + ); + } + } + + private readonly handleStart = (xyValue, moreProps) => { + const { current } = this.state; + if (isNotDefined(current) || isNotDefined(current.x1)) { + this.mouseMoved = false; + this.setState( + { + current: { + x1: xyValue[0], + y1: xyValue[1], + x2: null, + y2: null, + }, + }, + () => { + this.props.onStart(moreProps); + }, + ); + } + } + + private readonly handleEnd = (xyValue, moreProps, e) => { + const { retracements, appearance, type } = this.props; + const { current } = this.state; + + if (this.mouseMoved && isDefined(current) && isDefined(current.x1)) { + const newRetracements = retracements.concat({ + ...current, + x2: xyValue[0], + y2: xyValue[1], + selected: true, + appearance, + type, + }); + + this.setState( + { + current: null, + }, + () => { + this.props.onComplete(newRetracements, moreProps, e); + }, + ); + } + } +} diff --git a/packages/react-financial-charts/src/interactive/GannFan.tsx b/packages/react-financial-charts/src/interactive/GannFan.tsx new file mode 100644 index 000000000..f061c4116 --- /dev/null +++ b/packages/react-financial-charts/src/interactive/GannFan.tsx @@ -0,0 +1,237 @@ +import * as React from "react"; + +import { isDefined, isNotDefined, noop } from "../utils"; +import { HoverTextNearMouse } from "./components/HoverTextNearMouse"; +import { MouseLocationIndicator } from "./components/MouseLocationIndicator"; +import { + isHoverForInteractiveType, + saveNodeType, + terminate, +} from "./utils"; +import { EachGannFan } from "./wrapper/EachGannFan"; + +interface GannFanProps { + enabled: boolean; + onStart: any; // func + onComplete: any; // func + onSelect?: any; // func + currentPositionStroke?: string; + currentPositionStrokeWidth?: number; + currentPositionOpacity?: number; + currentPositionRadius?: number; + appearance: { + stroke: string; + strokeOpacity: number; + fillOpacity: number; + strokeWidth: number; + edgeStroke: string; + edgeFill: string; + edgeStrokeWidth: number; + r: number; + fill: string[], + fontFamily: string; + fontSize: number; + fontFill: string; + }; + hoverText: object; + fans: any[]; +} + +interface GannFanState { + current?: any; + override?: any; +} + +export class GannFan extends React.Component { + + public static defaultProps = { + appearance: { + stroke: "#000000", + fillOpacity: 0.2, + strokeOpacity: 1, + strokeWidth: 1, + edgeStroke: "#000000", + edgeFill: "#FFFFFF", + edgeStrokeWidth: 1, + r: 5, + fill: [ + "#e41a1c", + "#377eb8", + "#4daf4a", + "#984ea3", + "#ff7f00", + "#ffff33", + "#a65628", + "#f781bf", + ], + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 12, + fontFill: "#000000", + }, + onStart: noop, + onComplete: noop, + onSelect: noop, + currentPositionStroke: "#000000", + currentPositionOpacity: 1, + currentPositionStrokeWidth: 3, + currentPositionRadius: 4, + hoverText: { + ...HoverTextNearMouse.defaultProps, + enable: true, + bgHeight: 18, + bgWidth: 120, + text: "Click to select object", + }, + fans: [], + }; + + private mouseMoved; + // @ts-ignore + private getSelectionState; + private saveNodeType; + + // @ts-ignore + private terminate; + + public constructor(props) { + super(props); + + this.terminate = terminate.bind(this); + this.saveNodeType = saveNodeType.bind(this); + + this.getSelectionState = isHoverForInteractiveType("fans") + .bind(this); + + this.state = {}; + } + + public render() { + const { enabled, appearance } = this.props; + const { currentPositionRadius, currentPositionStroke } = this.props; + const { currentPositionOpacity, currentPositionStrokeWidth } = this.props; + const { hoverText, fans } = this.props; + const { current, override } = this.state; + const overrideIndex = isDefined(override) ? override.index : null; + + const tempChannel = isDefined(current) && isDefined(current.endXY) + ? + : null; + + return ( + + {fans.map((each, idx) => { + const eachAppearance = isDefined(each.appearance) + ? { ...appearance, ...each.appearance } + : appearance; + + return ; + })} + {tempChannel} + + + ); + } + + private readonly handleEnd = (xyValyue, moreProps, e) => { + const { current } = this.state; + const { fans, appearance } = this.props; + + if (this.mouseMoved + && isDefined(current) + && isDefined(current.startXY) + ) { + const newfans = [ + ...fans.map((d) => ({ ...d, selected: false })), + { ...current, selected: true, appearance }, + ]; + this.setState({ + current: null, + }, () => { + this.props.onComplete(newfans, moreProps, e); + }); + } + } + + private readonly handleStart = (xyValue) => { + const { current } = this.state; + + if (isNotDefined(current) || isNotDefined(current.startXY)) { + this.mouseMoved = false; + + this.setState({ + current: { + startXY: xyValue, + endXY: null, + }, + }, () => { + this.props.onStart(); + }); + } + } + + private readonly handleDrawFan = (xyValue) => { + const { current } = this.state; + + if (isDefined(current) && isDefined(current.startXY)) { + this.mouseMoved = true; + + this.setState({ + current: { + startXY: current.startXY, + endXY: xyValue, + }, + }); + } + } + + private readonly handleDragFanComplete = (moreProps) => { + const { override } = this.state; + const { fans } = this.props; + + if (isDefined(override)) { + const { index, ...rest } = override; + const newfans = fans + .map((each, idx) => idx === index + ? { ...each, ...rest, selected: true } + : each); + this.setState({ + override: null, + }, () => { + this.props.onComplete(newfans, moreProps); + }); + } + } + + private readonly handleDragFan = (index, newXYValue) => { + this.setState({ + override: { + index, + ...newXYValue, + }, + }); + } +} diff --git a/packages/react-financial-charts/src/interactive/InteractiveText.tsx b/packages/react-financial-charts/src/interactive/InteractiveText.tsx new file mode 100644 index 000000000..528cf35bc --- /dev/null +++ b/packages/react-financial-charts/src/interactive/InteractiveText.tsx @@ -0,0 +1,192 @@ +import * as PropTypes from "prop-types"; +import * as React from "react"; + +import { isDefined, noop } from "../utils"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getMouseCanvas } from "../GenericComponent"; +import { HoverTextNearMouse } from "./components/HoverTextNearMouse"; +import { + getValueFromOverride, + isHoverForInteractiveType, + saveNodeType, + terminate, +} from "./utils"; +import { EachText } from "./wrapper/EachText"; + +interface InteractiveTextProps { + onChoosePosition: any; // func + onDragComplete: any; // func + onSelect?: any; // func + defaultText: { + bgFill: string; + bgOpacity: number; + bgStrokeWidth?: number; + bgStroke?: string; + textFill: string; + fontFamily: string; + fontWeight: string; + fontStyle: string; + fontSize: number; + text: string; + }; + hoverText: object; + textList: any[]; + enabled: boolean; +} + +interface InteractiveTextState { + current?: any; + override?: any; +} + +export class InteractiveText extends React.Component { + + public static defaultProps = { + onChoosePosition: noop, + onDragComplete: noop, + onSelect: noop, + defaultText: { + bgFill: "#D3D3D3", + bgOpacity: 1, + bgStrokeWidth: 1, + textFill: "#F10040", + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 12, + fontStyle: "normal", + fontWeight: "normal", + text: "Lorem ipsum...", + }, + hoverText: { + ...HoverTextNearMouse.defaultProps, + enable: true, + bgHeight: "auto", + bgWidth: "auto", + text: "Click to select object", + selectedText: "", + }, + textList: [], + }; + + public static contextTypes = { + subscribe: PropTypes.func.isRequired, + unsubscribe: PropTypes.func.isRequired, + generateSubscriptionId: PropTypes.func.isRequired, + chartId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + }; + + // @ts-ignore + private getSelectionState; + private saveNodeType; + + // @ts-ignore + private terminate; + + constructor(props) { + super(props); + + this.terminate = terminate.bind(this); + + this.saveNodeType = saveNodeType.bind(this); + this.getSelectionState = isHoverForInteractiveType("textList") + .bind(this); + + this.state = {}; + } + + public render() { + const { textList, defaultText, hoverText } = this.props; + const { override } = this.state; + return + {textList.map((each, idx) => { + const defaultHoverText = InteractiveText.defaultProps.hoverText; + const props = { + ...defaultText, + ...each, + hoverText: { + ...defaultHoverText, + ...hoverText, + ...(each.hoverText || {}), + }, + }; + return ; + })} + ; + ; + } + + private readonly handleDraw = (moreProps, e) => { + const { enabled } = this.props; + if (enabled) { + const { + mouseXY: [, mouseY], + chartConfig: { yScale }, + xAccessor, + currentItem, + } = moreProps; + + const xyValue = [xAccessor(currentItem), yScale.invert(mouseY)]; + + const { defaultText, onChoosePosition } = this.props; + + const newText = { + ...defaultText, + position: xyValue, + }; + onChoosePosition(newText, moreProps, e); + } + } + + private readonly handleDragComplete = (moreProps) => { + const { override } = this.state; + if (isDefined(override)) { + const { textList } = this.props; + const newTextList = textList + .map((each, idx) => { + const selected = (idx === override.index); + return selected + ? { + ...each, + position: override.position, + selected, + } + : { + ...each, + selected, + }; + }); + this.setState({ + override: null, + }, () => { + this.props.onDragComplete(newTextList, moreProps); + }); + } + } + + private readonly handleDrag = (index, position) => { + this.setState({ + override: { + index, + position, + }, + }); + } +} diff --git a/packages/react-financial-charts/src/interactive/InteractiveYCoordinate.tsx b/packages/react-financial-charts/src/interactive/InteractiveYCoordinate.tsx new file mode 100644 index 000000000..d894c683b --- /dev/null +++ b/packages/react-financial-charts/src/interactive/InteractiveYCoordinate.tsx @@ -0,0 +1,216 @@ +import * as PropTypes from "prop-types"; +import * as React from "react"; + +import { format } from "d3-format"; +import { isDefined, noop, strokeDashTypes } from "../utils"; + +import { HoverTextNearMouse } from "./components/HoverTextNearMouse"; +import { + getValueFromOverride, + isHoverForInteractiveType, + saveNodeType, + terminate, +} from "./utils"; +import { EachInteractiveYCoordinate } from "./wrapper/EachInteractiveYCoordinate"; + +interface InteractiveYCoordinateProps { + onChoosePosition: any; // func + onDragComplete: any; // func + onSelect?: any; // func + onDelete?: any; // func + defaultPriceCoordinate: { + bgFill: string; + bgOpacity: number; + stroke: string; + strokeDasharray: strokeDashTypes; + strokeOpacity: number; + strokeWidth: number; + textFill: string; + fontFamily: string; + fontWeight: string; + fontStyle: string; + fontSize: number; + text: string; + textBox: { + height: number; + left: number; + padding: { + left: number; + right: number; + }; + closeIcon: { + padding: { + left: number; + right: number; + }; + width: number; + }; + }; + edge: { + stroke: string; + strokeOpacity: number; + strokeWidth: number; + fill: string; + fillOpacity: number; + }; + }; + hoverText: object; + yCoordinateList: any[]; + enabled: boolean; +} + +interface InteractiveYCoordinateState { + current?: any; + override?: any; +} + +export class InteractiveYCoordinate extends React.Component { + + public static defaultProps = { + onChoosePosition: noop, + onDragComplete: noop, + onSelect: noop, + onDelete: noop, + defaultPriceCoordinate: { + bgFill: "#FFFFFF", + bgOpacity: 1, + + stroke: "#6574CD", + strokeOpacity: 1, + strokeDasharray: "ShortDash2", + strokeWidth: 1, + + textFill: "#6574CD", + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 12, + fontStyle: "normal", + fontWeight: "normal", + text: "Alert", + textBox: { + height: 24, + left: 20, + padding: { left: 10, right: 5 }, + closeIcon: { + padding: { left: 5, right: 8 }, + width: 8, + }, + }, + edge: { + stroke: "#6574CD", + strokeOpacity: 1, + strokeWidth: 1, + + fill: "#FFFFFF", + fillOpacity: 1, + orient: "right", + at: "right", + arrowWidth: 10, + dx: 0, + rectWidth: 50, + rectHeight: 20, + displayFormat: format(".2f"), + }, + }, + hoverText: { + ...HoverTextNearMouse.defaultProps, + enable: true, + bgHeight: 18, + bgWidth: 175, + text: "Click and drag the edge circles", + }, + yCoordinateList: [], + }; + + public static contextTypes = { + subscribe: PropTypes.func.isRequired, + unsubscribe: PropTypes.func.isRequired, + generateSubscriptionId: PropTypes.func.isRequired, + chartId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + }; + + // @ts-ignore + private getSelectionState; + private saveNodeType; + + // @ts-ignore + private terminate; + + constructor(props) { + super(props); + + this.terminate = terminate.bind(this); + + this.saveNodeType = saveNodeType.bind(this); + this.getSelectionState = isHoverForInteractiveType("yCoordinateList") + .bind(this); + + this.state = {}; + } + + public render() { + const { yCoordinateList } = this.props; + const { override } = this.state; + return ( + + {yCoordinateList.map((each, idx) => { + const props = each; + return ( + + ); + })} + + ); + } + + private readonly handleDelete = (index, moreProps) => { + const { onDelete, yCoordinateList } = this.props; + + onDelete(yCoordinateList[index], moreProps); + } + + private readonly handleDragComplete = (moreProps) => { + const { override } = this.state; + if (isDefined(override)) { + const { yCoordinateList } = this.props; + const newAlertList = yCoordinateList + .map((each, idx) => { + const selected = (idx === override.index); + return selected + ? { + ...each, + yValue: override.yValue, + selected, + } + : { + ...each, + selected, + }; + }); + const draggedAlert = newAlertList[override.index]; + this.setState({ + override: null, + }, () => { + this.props.onDragComplete(newAlertList, moreProps, draggedAlert); + }); + } + } + + private readonly handleDrag = (index, yValue) => { + this.setState({ + override: { + index, + yValue, + }, + }); + } +} diff --git a/packages/react-financial-charts/src/interactive/StandardDeviationChannel.tsx b/packages/react-financial-charts/src/interactive/StandardDeviationChannel.tsx new file mode 100644 index 000000000..1fbc2570b --- /dev/null +++ b/packages/react-financial-charts/src/interactive/StandardDeviationChannel.tsx @@ -0,0 +1,257 @@ +import * as React from "react"; + +import { isDefined, isNotDefined, noop } from "../utils"; + +import { + getValueFromOverride, + isHoverForInteractiveType, + saveNodeType, + terminate, +} from "./utils"; + +import { HoverTextNearMouse } from "./components/HoverTextNearMouse"; +import { MouseLocationIndicator } from "./components/MouseLocationIndicator"; +import { EachLinearRegressionChannel } from "./wrapper/EachLinearRegressionChannel"; + +interface StandardDeviationChannelProps { + enabled: boolean; + snapTo?: any; // func + onStart?: any; // func + onComplete: any; // func + onSelect?: any; // func + currentPositionStroke?: string; + currentPositionStrokeWidth?: number; + currentPositionOpacity?: number; + currentPositionRadius?: number; + appearance: { + stroke?: string; + strokeOpacity?: number; + strokeWidth?: number; + fill?: string; + fillOpacity?: number; + edgeStrokeWidth?: number; + edgeStroke?: string; + edgeFill?: string; + r?: number; + }; + hoverText: object; + channels: any[]; +} + +interface StandardDeviationChannelState { + current?: any; + override?: any; +} + +export class StandardDeviationChannel extends React.Component { + + public static defaultProps = { + snapTo: (d) => d.close, + appearance: { + stroke: "#000000", + fillOpacity: 0.2, + strokeOpacity: 1, + strokeWidth: 1, + fill: "#8AAFE2", + edgeStrokeWidth: 2, + edgeStroke: "#000000", + edgeFill: "#FFFFFF", + r: 5, + }, + onStart: noop, + onComplete: noop, + onSelect: noop, + currentPositionStroke: "#000000", + currentPositionOpacity: 1, + currentPositionStrokeWidth: 3, + currentPositionRadius: 4, + hoverText: { + ...HoverTextNearMouse.defaultProps, + enable: true, + bgHeight: "auto", + bgWidth: "auto", + text: "Click and drag the edge circles", + selectedText: "", + }, + channels: [], + }; + + // @ts-ignore + private getSelectionState; + private mouseMoved; + private saveNodeType; + + // @ts-ignore + private terminate; + + public constructor(props) { + super(props); + + this.terminate = terminate.bind(this); + this.saveNodeType = saveNodeType.bind(this); + + this.getSelectionState = isHoverForInteractiveType("channels") + .bind(this); + + this.state = {}; + } + + public render() { + const { appearance } = this.props; + const { enabled, snapTo } = this.props; + const { currentPositionRadius, currentPositionStroke } = this.props; + const { currentPositionOpacity, currentPositionStrokeWidth } = this.props; + const { hoverText, channels } = this.props; + const { current, override } = this.state; + + const eachDefaultAppearance = { + ...StandardDeviationChannel.defaultProps.appearance, + ...appearance, + }; + + const hoverTextDefault = { + ...StandardDeviationChannel.defaultProps.hoverText, + ...hoverText, + }; + + const tempLine = isDefined(current) && isDefined(current.end) + ? + : null; + + return + {channels.map((each, idx) => { + const eachAppearance = isDefined(each.appearance) + ? { ...eachDefaultAppearance, ...each.appearance } + : eachDefaultAppearance; + + const eachHoverText = isDefined(each.hoverText) + ? { ...hoverTextDefault, ...each.hoverText } + : hoverTextDefault; + + return ; + })} + {tempLine} + + ; + } + + private handleEnd = (xyValue, moreProps, e) => { + const { current } = this.state; + const { appearance, channels } = this.props; + + if (this.mouseMoved + && isDefined(current) + && isDefined(current.start) + ) { + const newChannels = [ + ...channels.map((d) => ({ ...d, selected: false })), + { + start: current.start, + end: xyValue, + selected: true, + appearance, + }, + ]; + + this.setState({ + current: null, + }, () => { + this.props.onComplete(newChannels, moreProps, e); + }); + } + } + + private readonly handleStart = (xyValue) => { + const { current } = this.state; + + if (isNotDefined(current) || isNotDefined(current.start)) { + this.mouseMoved = false; + + this.setState({ + current: { + start: xyValue, + end: null, + }, + }, () => { + this.props.onStart(); + }); + } + } + + private readonly handleDrawLine = (xyValue) => { + const { current } = this.state; + + if (isDefined(current) && isDefined(current.start)) { + this.mouseMoved = true; + this.setState({ + current: { + start: current.start, + end: xyValue, + }, + }); + } + } + + private readonly handleDragLineComplete = (moreProps) => { + const { override } = this.state; + const { channels } = this.props; + if (isDefined(override)) { + + const newChannels = channels + .map((each, idx) => idx === override.index + ? { + ...each, + start: [override.x1Value, override.y1Value], + end: [override.x2Value, override.y2Value], + selected: true, + } + : each); + this.setState({ + override: null, + }, () => { + this.props.onComplete(newChannels, moreProps); + }); + } + } + + private readonly handleDragLine = (index, newXYValue) => { + this.setState({ + override: { + index, + ...newXYValue, + }, + }); + } +} diff --git a/packages/react-financial-charts/src/interactive/TrendLine.tsx b/packages/react-financial-charts/src/interactive/TrendLine.tsx new file mode 100644 index 000000000..060b08d2e --- /dev/null +++ b/packages/react-financial-charts/src/interactive/TrendLine.tsx @@ -0,0 +1,267 @@ +import * as React from "react"; + +import { isDefined, isNotDefined, noop, strokeDashTypes } from "../utils"; + +import { + getValueFromOverride, + isHoverForInteractiveType, + saveNodeType, + terminate, +} from "./utils"; + +import { HoverTextNearMouse } from "./components/HoverTextNearMouse"; +import { MouseLocationIndicator } from "./components/MouseLocationIndicator"; +import StraightLine from "./components/StraightLine"; +import { EachTrendLine } from "./wrapper/EachTrendLine"; + +interface TrendLineProps { + snap: boolean; + enabled: boolean; + snapTo?: any; // func + shouldDisableSnap: any; // func + onStart: any; // func + onComplete: any; // func + onSelect?: any; // func + currentPositionStroke?: string; + currentPositionStrokeWidth?: number; + currentPositionstrokeOpacity?: number; + currentPositionRadius?: number; + type: + "XLINE" | // extends from -Infinity to +Infinity + "RAY" | // extends to +/-Infinity in one direction + "LINE"; // extends between the set bounds + hoverText: object; + trends: any[]; + appearance: { + stroke: string; + strokeOpacity: number; + strokeWidth: number; + strokeDasharray: strokeDashTypes; + edgeStrokeWidth: number; + edgeFill: string; + edgeStroke: string; + }; +} + +interface TrendLineState { + current?: any; + override?: any; + trends?: any; +} + +export class TrendLine extends React.Component { + + public static defaultProps = { + type: "XLINE", + onStart: noop, + onComplete: noop, + onSelect: noop, + currentPositionStroke: "#000000", + currentPositionstrokeOpacity: 1, + currentPositionStrokeWidth: 3, + currentPositionRadius: 0, + shouldDisableSnap: (e) => (e.button === 2 || e.shiftKey), + hoverText: { + ...HoverTextNearMouse.defaultProps, + enable: true, + bgHeight: "auto", + bgWidth: "auto", + text: "Click to select object", + selectedText: "", + }, + trends: [], + appearance: { + stroke: "#000000", + strokeOpacity: 1, + strokeWidth: 1, + strokeDasharray: "Solid", + edgeStrokeWidth: 1, + edgeFill: "#FFFFFF", + edgeStroke: "#000000", + r: 6, + }, + }; + + // @ts-ignore + private getSelectionState; + private mouseMoved; + private saveNodeType; + // @ts-ignore + private terminate; + + public constructor(props: TrendLineProps) { + super(props); + + this.terminate = terminate.bind(this); + this.saveNodeType = saveNodeType.bind(this); + + this.getSelectionState = isHoverForInteractiveType("trends") + .bind(this); + + this.state = { + }; + } + + public render() { + const { appearance } = this.props; + const { enabled, snap, shouldDisableSnap, snapTo, type } = this.props; + const { currentPositionRadius, currentPositionStroke } = this.props; + const { currentPositionstrokeOpacity, currentPositionStrokeWidth } = this.props; + const { hoverText, trends } = this.props; + const { current, override } = this.state; + + const tempLine = isDefined(current) && isDefined(current.end) + ? + : null; + + return + {trends.map((each, idx) => { + const eachAppearance = isDefined(each.appearance) + ? { ...appearance, ...each.appearance } + : appearance; + + const hoverTextWithDefault = { + ...TrendLine.defaultProps.hoverText, + ...hoverText, + }; + + return ; + })} + {tempLine} + + ; + } + + private readonly handleEnd = (xyValue, moreProps, e) => { + const { current } = this.state; + const { trends, appearance, type } = this.props; + + if (this.mouseMoved + && isDefined(current) + && isDefined(current.start) + ) { + const newTrends = [ + ...trends.map((d) => ({ ...d, selected: false })), + { + start: current.start, + end: xyValue, + selected: true, + appearance, + type, + }, + ]; + this.setState({ + current: null, + trends: newTrends, + }, () => { + this.props.onComplete(newTrends, moreProps, e); + }); + } + } + + private readonly handleStart = (xyValue, moreProps, e) => { + const { current } = this.state; + + if (isNotDefined(current) || isNotDefined(current.start)) { + this.mouseMoved = false; + + this.setState({ + current: { + start: xyValue, + end: null, + }, + }, () => { + this.props.onStart(moreProps, e); + }); + } + } + + private readonly handleDrawLine = (xyValue) => { + const { current } = this.state; + if (isDefined(current) && isDefined(current.start)) { + this.mouseMoved = true; + this.setState({ + current: { + start: current.start, + end: xyValue, + }, + }); + } + } + + private readonly handleDragLineComplete = (moreProps) => { + const { override } = this.state; + if (isDefined(override)) { + const { trends } = this.props; + const newTrends = trends + .map((each, idx) => idx === override.index + ? { + ...each, + start: [override.x1Value, override.y1Value], + end: [override.x2Value, override.y2Value], + selected: true, + } + : { + ...each, + selected: false, + }); + + this.setState({ + override: null, + }, () => { + this.props.onComplete(newTrends, moreProps); + }); + } + } + + private readonly handleDragLine = (index, newXYValue) => { + this.setState({ + override: { + index, + ...newXYValue, + }, + }); + } +} diff --git a/packages/react-financial-charts/src/interactive/components/ChannelWithArea.tsx b/packages/react-financial-charts/src/interactive/components/ChannelWithArea.tsx new file mode 100644 index 000000000..0594a9475 --- /dev/null +++ b/packages/react-financial-charts/src/interactive/components/ChannelWithArea.tsx @@ -0,0 +1,258 @@ +import { path as d3Path } from "d3-path"; +import * as React from "react"; + +import GenericChartComponent from "../../GenericChartComponent"; +import { getMouseCanvas } from "../../GenericComponent"; +import { generateLine, isHovering } from "./StraightLine"; + +import { hexToRGBA, isDefined, isNotDefined, noop } from "../../utils"; + +interface ChannelWithAreaProps { + startXY?: number[]; + endXY?: number[]; + dy?: number; + interactiveCursorClass?: string; + stroke: string; + strokeWidth: number; + fill: string; + fillOpacity: number; + strokeOpacity: number; + type: + "XLINE" | // extends from -Infinity to +Infinity + "RAY" | // extends to +/-Infinity in one direction + "LINE"; // extends between the set bounds + onDragStart: any; // func + onDrag: any; // func + onDragComplete: any; // func + onHover?: any; // func + onUnHover?: any; // func + defaultClassName?: string; + tolerance: number; + selected: boolean; +} + +export class ChannelWithArea extends React.Component { + + public static defaultProps = { + onDragStart: noop, + onDrag: noop, + onDragComplete: noop, + type: "LINE", + strokeWidth: 1, + tolerance: 4, + selected: false, + }; + + public render() { + const { selected, interactiveCursorClass } = this.props; + const { onDragStart, onDrag, onDragComplete, onHover, onUnHover } = this.props; + + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + const { stroke, strokeWidth, fillOpacity, fill, strokeOpacity } = this.props; + const { line1, line2 } = helper(this.props, moreProps); + + if (line1 !== undefined) { + const { x1, y1, x2, y2 } = line1; + const line = line2 !== undefined + ? + : null; + const area = isDefined(line2) + ? + : null; + + return ( + + + {line} + {area} + + ); + } + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { stroke, strokeWidth, fillOpacity, strokeOpacity, fill } = this.props; + const { line1, line2 } = helper(this.props, moreProps); + + if (line1 !== undefined) { + const { x1, y1, x2, y2 } = line1; + + ctx.lineWidth = strokeWidth; + ctx.strokeStyle = hexToRGBA(stroke, strokeOpacity); + + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + + if (line2 !== undefined) { + const { + y1: line2Y1, + y2: line2Y2, + } = line2; + + ctx.beginPath(); + ctx.moveTo(x1, line2Y1); + ctx.lineTo(x2, line2Y2); + ctx.stroke(); + + ctx.fillStyle = hexToRGBA(fill, fillOpacity); + ctx.beginPath(); + ctx.moveTo(x1, y1); + + ctx.lineTo(x2, y2); + ctx.lineTo(x2, line2Y2); + ctx.lineTo(x1, line2Y1); + + ctx.closePath(); + ctx.fill(); + } + } + } + + private readonly isHover = (moreProps) => { + const { tolerance, onHover } = this.props; + + if (isDefined(onHover)) { + + const { line1, line2 } = helper(this.props, moreProps); + + if (line1 !== undefined && line2 !== undefined) { + const { mouseXY, xScale, chartConfig: { yScale } } = moreProps; + + const line1Hovering = isHovering({ + x1Value: line1.x1, + y1Value: line1.y1, + x2Value: line1.x2, + y2Value: line1.y2, + type: "LINE", + mouseXY, + tolerance, + xScale, + yScale, + }); + + const line2Hovering = isHovering({ + x1Value: line2.x1, + y1Value: line2.y1, + x2Value: line2.x2, + y2Value: line2.y2, + type: "LINE", + mouseXY, + tolerance, + xScale, + yScale, + }); + + return line1Hovering || line2Hovering; + } + } + return false; + } +} + +function getPath(line1, line2) { + const ctx = d3Path(); + ctx.moveTo(line1.x1, line1.y1); + ctx.lineTo(line1.x2, line1.y2); + ctx.lineTo(line1.x2, line2.y2); + ctx.lineTo(line1.x1, line2.y1); + + ctx.closePath(); + return ctx.toString(); +} + +function getLines(props: ChannelWithAreaProps, moreProps) { + const { startXY, endXY, dy, type } = props; + const { xScale } = moreProps; + + if (isNotDefined(startXY) || isNotDefined(endXY)) { + return {}; + } + const line1 = generateLine({ + type, + start: startXY, + end: endXY, + xScale, + yScale: undefined, + }); + const line2 = isDefined(dy) + ? { + ...line1, + y1: line1.y1 + dy, + y2: line1.y2 + dy, + } + : undefined; + + return { + line1, + line2, + }; +} + +function helper(props: ChannelWithAreaProps, moreProps) { + const lines = getLines(props, moreProps); + const { xScale, chartConfig: { yScale } } = moreProps; + + const line1 = lines.line1 !== undefined + ? { + x1: xScale(lines.line1.x1), + y1: yScale(lines.line1.y1), + x2: xScale(lines.line1.x2), + y2: yScale(lines.line1.y2), + } + : undefined; + + const line2 = lines.line2 !== undefined + ? { + x1: line1!.x1, + y1: yScale(lines.line2.y1), + x2: line1!.x2, + y2: yScale(lines.line2.y2), + } + : undefined; + + return { + lines, + line1, + line2, + }; +} diff --git a/packages/react-financial-charts/src/interactive/components/ClickableCircle.tsx b/packages/react-financial-charts/src/interactive/components/ClickableCircle.tsx new file mode 100644 index 000000000..110397927 --- /dev/null +++ b/packages/react-financial-charts/src/interactive/components/ClickableCircle.tsx @@ -0,0 +1,124 @@ +import * as React from "react"; + +import GenericChartComponent from "../../GenericChartComponent"; +import { getMouseCanvas } from "../../GenericComponent"; +import { hexToRGBA, isDefined, noop } from "../../utils"; + +interface ClickableCircleProps { + readonly xyProvider?: any; // func + readonly onDragStart: any; // func + readonly onDrag: any; // func + readonly onDragComplete: any; // func + readonly strokeWidth: number; + readonly stroke: string; + readonly fill: string; + readonly r: number; + readonly cx?: number; + readonly cy?: number; + readonly className: string; + readonly show: boolean; + readonly strokeOpacity: number; + readonly fillOpacity: number; + readonly interactiveCursorClass?: string; +} + +export class ClickableCircle extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-interactive-line-edge", + onDragStart: noop, + onDrag: noop, + onDragComplete: noop, + onMove: noop, + show: false, + fillOpacity: 1, + strokeOpacity: 1, + }; + + public render() { + const { interactiveCursorClass } = this.props; + const { show } = this.props; + const { onDragStart, onDrag, onDragComplete } = this.props; + + if (!show) { + return null; + } + + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + const { stroke, strokeWidth, fill } = this.props; + const { fillOpacity, strokeOpacity } = this.props; + const { r } = this.props; + + const [x, y] = this.helper(this.props, moreProps); + + return ( + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { stroke, strokeWidth, fill } = this.props; + const { fillOpacity, strokeOpacity } = this.props; + const { r } = this.props; + + const [x, y] = this.helper(this.props, moreProps); + + ctx.lineWidth = strokeWidth; + ctx.fillStyle = hexToRGBA(fill, fillOpacity); + ctx.strokeStyle = hexToRGBA(stroke, strokeOpacity); + + ctx.beginPath(); + ctx.arc(x, y, r, 0, 2 * Math.PI, false); + ctx.fill(); + ctx.stroke(); + } + + private readonly isHover = (moreProps) => { + const { mouseXY } = moreProps; + const r = this.props.r + 7; + const [x, y] = this.helper(this.props, moreProps); + + const [mx, my] = mouseXY; + const hover = (x - r) < mx && mx < (x + r) + && (y - r) < my && my < (y + r); + + return hover; + } + + private readonly helper = (props, moreProps) => { + const { xyProvider, cx, cy } = props; + + if (isDefined(xyProvider)) { + return xyProvider(moreProps); + } + + const { xScale, chartConfig: { yScale } } = moreProps; + + const x = xScale(cx); + const y = yScale(cy); + return [x, y]; + } +} diff --git a/packages/react-financial-charts/src/interactive/components/ClickableShape.tsx b/packages/react-financial-charts/src/interactive/components/ClickableShape.tsx new file mode 100644 index 000000000..1db5d4acf --- /dev/null +++ b/packages/react-financial-charts/src/interactive/components/ClickableShape.tsx @@ -0,0 +1,126 @@ +import * as React from "react"; + +import GenericChartComponent from "../../GenericChartComponent"; +import { getMouseCanvas } from "../../GenericComponent"; + +import { hexToRGBA } from "../../utils"; +import { isHovering2 } from "./StraightLine"; + +interface ClickableShapeProps { + fontWeight: string; + fontFamily: string; + fontStyle: string; + fontSize: number; + stroke: string; + strokeOpacity: number; + strokeWidth: number; + text: string; + textBox: { + closeIcon: any; + left: number; + padding: any; + }; + hovering?: boolean; + interactiveCursorClass?: string; + show?: boolean; + onHover?: any; // func + onUnHover?: any; // func + onClick?: any; // func + yValue: number; +} + +export class ClickableShape extends React.Component { + + public static defaultProps = { + show: false, + fillOpacity: 1, + strokeOpacity: 1, + strokeWidth: 1, + }; + + private closeIcon; + + public render() { + const { interactiveCursorClass } = this.props; + const { show } = this.props; + const { onHover, onUnHover, onClick } = this.props; + + if (!show) { + return null; + } + + return ( + + ); + } + + private readonly renderSVG = () => { + throw new Error("svg not implemented"); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { stroke, strokeWidth, strokeOpacity, hovering, textBox } = this.props; + + const [x, y] = this.helper(this.props, moreProps, ctx); + + this.closeIcon = { x, y }; + ctx.beginPath(); + + ctx.lineWidth = hovering ? strokeWidth + 1 : strokeWidth; + ctx.strokeStyle = hexToRGBA(stroke, strokeOpacity); + const halfWidth = textBox.closeIcon.width / 2; + ctx.moveTo(x - halfWidth, y - halfWidth); + ctx.lineTo(x + halfWidth, y + halfWidth); + ctx.moveTo(x - halfWidth, y + halfWidth); + ctx.lineTo(x + halfWidth, y - halfWidth); + ctx.stroke(); + } + + private readonly isHover = (moreProps) => { + const { mouseXY } = moreProps; + if (this.closeIcon) { + const { textBox } = this.props; + const { x, y } = this.closeIcon; + const halfWidth = textBox.closeIcon.width / 2; + + const start1 = [x - halfWidth, y - halfWidth]; + const end1 = [x + halfWidth, y + halfWidth]; + const start2 = [x - halfWidth, y + halfWidth]; + const end2 = [x + halfWidth, y - halfWidth]; + + if (isHovering2(start1, end1, mouseXY, 3) || isHovering2(start2, end2, mouseXY, 3)) { + return true; + } + } + return false; + } + + private readonly helper = (props: ClickableShapeProps, moreProps, ctx) => { + const { yValue, text, textBox } = props; + const { fontFamily, fontStyle, fontWeight, fontSize } = props; + ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`; + + const { chartConfig: { yScale } } = moreProps; + + const x = textBox.left + + textBox.padding.left + + ctx.measureText(text).width + + textBox.padding.right + + textBox.closeIcon.padding.left + + textBox.closeIcon.width / 2; + + const y = yScale(yValue); + + return [x, y]; + } +} diff --git a/packages/react-financial-charts/src/interactive/components/GannFan.tsx b/packages/react-financial-charts/src/interactive/components/GannFan.tsx new file mode 100644 index 000000000..16613eb7a --- /dev/null +++ b/packages/react-financial-charts/src/interactive/components/GannFan.tsx @@ -0,0 +1,297 @@ +import { pairs } from "d3-array"; +import { path as d3Path } from "d3-path"; +import * as React from "react"; + +import GenericChartComponent from "../../GenericChartComponent"; +import { getMouseCanvas } from "../../GenericComponent"; +import { generateLine, isHovering2 } from "./StraightLine"; + +import { + hexToRGBA, isDefined, + isNotDefined, noop, +} from "../../utils"; + +interface GannFanProps { + startXY: number[]; + endXY: number[]; + interactiveCursorClass?: string; + stroke: string; + strokeWidth: number; + fill: string[]; + strokeOpacity: number; + fillOpacity: number; + fontFamily: string; + fontSize: number; + fontFill: string; + onDragStart: any; // func + onDrag: any; // func + onDragComplete: any; // func + onHover?: any; // func + onUnHover?: any; // func + defaultClassName?: string; + tolerance: number; + selected: boolean; +} + +export class GannFan extends React.Component { + + public static defaultProps = { + onDragStart: noop, + onDrag: noop, + onDragComplete: noop, + strokeWidth: 1, + tolerance: 4, + selected: false, + }; + + public render() { + const { selected, interactiveCursorClass } = this.props; + const { onDragStart, onDrag, onDragComplete, onHover, onUnHover } = this.props; + + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + const { stroke, strokeWidth, fillOpacity, fill, strokeOpacity } = this.props; + + const lines = this.helper(this.props, moreProps); + const pairsOfLines = pairs(lines); + + return ( + + {lines.map((each, idx) => { + const { x1, y1, x2, y2 } = each; + return ( + + ); + })} + {pairsOfLines.map(([line1, line2], idx) => { + const ctx = d3Path(); + ctx.moveTo(line1.x1, line1.y1); + ctx.lineTo(line1.x2, line1.y2); + ctx.lineTo(line2.x2, line2.y2); + ctx.closePath(); + return ( + + ); + })} + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { + stroke, strokeWidth, strokeOpacity, + fill, fillOpacity, + fontFamily, fontSize, fontFill, + } = this.props; + + const lines = this.helper(this.props, moreProps); + + ctx.lineWidth = strokeWidth; + ctx.strokeStyle = hexToRGBA(stroke, strokeOpacity); + + ctx.font = `${fontSize}px ${fontFamily}`; + ctx.fillStyle = fontFill; + + lines.forEach((line) => { + const { + x1, y1, x2, y2, label, + } = line; + + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + ctx.beginPath(); + ctx.fillText(label.text, label.x, label.y); + }); + const pairsOfLines = pairs(lines); + + pairsOfLines.forEach(([line1, line2], idx) => { + ctx.fillStyle = hexToRGBA(fill[idx], fillOpacity); + + ctx.beginPath(); + ctx.moveTo(line1.x1, line1.y1); + ctx.lineTo(line1.x2, line1.y2); + ctx.lineTo(line2.x2, line2.y2); + ctx.closePath(); + ctx.fill(); + }); + } + + private readonly isHover = (moreProps) => { + const { tolerance, onHover } = this.props; + const { mouseXY } = moreProps; + const [mouseX, mouseY] = mouseXY; + + let hovering = false; + if (isDefined(onHover)) { + + const lines = this.helper(this.props, moreProps); + + // tslint:disable-next-line: prefer-for-of + for (let i = 0; i < lines.length; i++) { + const line1 = lines[i]; + + const left = Math.min(line1.x1, line1.x2); + const right = Math.max(line1.x1, line1.x2); + const top = Math.min(line1.y1, line1.y2); + const bottom = Math.max(line1.y1, line1.y2); + + const isWithinLineBounds = mouseX >= left && mouseX <= right + && mouseY >= top && mouseY <= bottom; + + hovering = isWithinLineBounds + && isHovering2( + [line1.x1, line1.y1], + [line1.x2, line1.y2], + mouseXY, + tolerance); + + if (hovering) { break; } + } + } + return hovering; + } + + private readonly getLineCoordinates = (start, endX, endY, text) => { + const end = [ + endX, + endY, + ]; + return { + start, end, text, + }; + } + + private readonly helper = (props: GannFanProps, moreProps) => { + const { startXY, endXY } = props; + + const { + xScale, + chartConfig: { yScale }, + } = moreProps; + if (isNotDefined(startXY) || isNotDefined(endXY)) { + return []; + } + const [x1, y1] = startXY; + const [x2, y2] = endXY; + + const dx = x2 - x1; + const dy = y2 - y1; + + if (dx !== 0 && dy !== 0) { + const halfY = this.getLineCoordinates( + startXY, + x2, + y1 + dy / 2, + "2/1", + ); + const oneThirdY = this.getLineCoordinates( + startXY, + x2, + y1 + dy / 3, + "3/1", + ); + const oneFourthY = this.getLineCoordinates( + startXY, + x2, + y1 + dy / 4, + "4/1", + ); + const oneEighthY = this.getLineCoordinates( + startXY, + x2, + y1 + dy / 8, + "8/1", + ); + const halfX = this.getLineCoordinates( + startXY, + x1 + dx / 2, + y2, + "1/2", + ); + const oneThirdX = this.getLineCoordinates( + startXY, + x1 + dx / 3, + y2, + "1/3", + ); + const oneFourthX = this.getLineCoordinates( + startXY, + x1 + dx / 4, + y2, + "1/4", + ); + const oneEighthX = this.getLineCoordinates( + startXY, + x1 + dx / 8, + y2, + "1/8", + ); + const lines = [ + oneEighthX, + oneFourthX, + oneThirdX, + halfX, + { start: startXY, end: endXY, text: "1/1" }, + halfY, + oneThirdY, + oneFourthY, + oneEighthY, + ]; + const lineCoods = lines.map((line) => { + // tslint:disable-next-line: no-shadowed-variable + const { x1, y1, x2, y2 } = generateLine({ + type: "RAY", + start: line.start, + end: line.end, + xScale, + yScale, + }); + return { + x1: xScale(x1), + y1: yScale(y1), + x2: xScale(x2), + y2: yScale(y2), + label: { + x: xScale(line.end[0]), + y: yScale(line.end[1]), + text: line.text, + }, + }; + }); + return lineCoods; + } + return []; + } +} diff --git a/packages/react-financial-charts/src/interactive/components/HoverTextNearMouse.tsx b/packages/react-financial-charts/src/interactive/components/HoverTextNearMouse.tsx new file mode 100644 index 000000000..6b3486c3e --- /dev/null +++ b/packages/react-financial-charts/src/interactive/components/HoverTextNearMouse.tsx @@ -0,0 +1,197 @@ +import * as React from "react"; + +import GenericChartComponent from "../../GenericChartComponent"; +import { isDefined } from "../../utils"; + +const PADDING = 10; +const MIN_WIDTH = PADDING; + +interface HoverTextNearMouseProps { + fontFamily: string; + fontSize: number; + fill: string; + text: string; + bgFill: string; + bgOpacity: number; + bgWidth: number | string; + bgHeight: number | string; + show: boolean; +} + +interface HoverTextNearMouseState { + textHeight?: number; + textWidth?: number; +} + +export class HoverTextNearMouse extends React.Component { + + public static defaultProps = { + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 12, + fill: "#000000", + bgFill: "#FA9325", + bgOpacity: 0.5, + }; + + private textNode; + + public constructor(props: HoverTextNearMouseProps) { + super(props); + + this.state = { + textWidth: undefined, + textHeight: undefined, + }; + } + + public componentDidMount() { + this.updateTextSize(); + } + + public componentDidUpdate() { + this.updateTextSize(); + } + + public render() { + const { text } = this.props; + if (text) { + return ( + + ); + } else { + return null; + } + } + + private readonly renderSVG = (moreProps) => { + const { + fontFamily, + fontSize, + fill, + bgFill, + bgOpacity, + } = this.props; + + const textMetaData = helper({ + ...this.props, + bgWidth: this.getBgWidth(), + bgHeight: this.getBgHeight(), + }, moreProps); + + if (textMetaData !== undefined && isDefined(textMetaData)) { + const { rect, text } = textMetaData; + return ( + + + {text.text} + + ); + } + } + + private readonly getBgHeight = () => { + const { bgHeight } = this.props; + const { textHeight } = this.state; + + if (bgHeight !== "auto") { + return bgHeight; + } else if (textHeight !== undefined) { + return textHeight + PADDING; + } else { + return MIN_WIDTH; + } + } + + private readonly getBgWidth = () => { + const { bgWidth } = this.props; + const { textWidth } = this.state; + + if (bgWidth !== "auto") { + return bgWidth; + } else if (textWidth !== undefined) { + return textWidth + PADDING; + } else { + return MIN_WIDTH; + } + } + + private readonly updateTextSize = () => { + const { bgWidth, bgHeight } = this.props; + if (bgWidth === "auto" || bgHeight === "auto") { + const textNode = this.textNode; + if (textNode) { + const { width, height } = textNode.getBBox(); + if (this.state.textWidth !== width || this.state.textHeight !== height) { + this.setState({ + textWidth: width, + textHeight: height, + }); + } + } + } + } + + private readonly saveNode = (node) => { + this.textNode = node; + } +} + +function helper(props, moreProps) { + const { + show, + bgWidth, + bgHeight, + } = props; + + const { + mouseXY, + chartConfig: { height, width }, + show: mouseInsideCanvas, + } = moreProps; + + if (show && mouseInsideCanvas) { + const [x, y] = mouseXY; + + const cx = x < width / 2 + ? x + PADDING + : x - bgWidth - PADDING; + + const cy = y < height / 2 + ? y + PADDING + : y - bgHeight - PADDING; + + const rect = { + x: cx, + y: cy, + width: bgWidth, + height: bgHeight, + }; + + const text = { + text: props.text, + x: cx + PADDING / 2, + y: cy + bgHeight / 2, + }; + + return { + rect, + text, + }; + } +} diff --git a/packages/react-financial-charts/src/interactive/components/InteractiveText.tsx b/packages/react-financial-charts/src/interactive/components/InteractiveText.tsx new file mode 100644 index 000000000..e69ba9f1b --- /dev/null +++ b/packages/react-financial-charts/src/interactive/components/InteractiveText.tsx @@ -0,0 +1,171 @@ +import * as React from "react"; + +import GenericChartComponent from "../../GenericChartComponent"; +import { getMouseCanvas } from "../../GenericComponent"; + +import { hexToRGBA, isDefined, noop } from "../../utils"; + +interface InteractiveTextProps { + bgFill: string; + bgOpacity: number; + bgStrokeWidth: number; + bgStroke: string; + textFill: string; + fontFamily: string; + fontSize: number; + fontWeight: number | string; + fontStyle: string; + text: string; + onDragStart: any; // func + onDrag: any; // func + onDragComplete: any; // func + onHover?: any; // func + onUnHover?: any; // func + position?: any; + defaultClassName?: string; + interactiveCursorClass?: string; + tolerance: number; + selected: boolean; +} + +export class InteractiveText extends React.Component { + + public static defaultProps = { + onDragStart: noop, + onDrag: noop, + onDragComplete: noop, + type: "SD", // standard dev + fontWeight: "normal", // standard dev + tolerance: 4, + selected: false, + }; + + private calculateTextWidth = true; + private textWidth; + + public UNSAFE_componentWillReceiveProps(nextProps) { + this.calculateTextWidth = ( + nextProps.text !== this.props.text + || nextProps.fontStyle !== this.props.fontStyle + || nextProps.fontWeight !== this.props.fontWeight + || nextProps.fontSize !== this.props.fontSize + || nextProps.fontFamily !== this.props.fontFamily + ); + } + + public render() { + const { selected, interactiveCursorClass } = this.props; + const { onHover, onUnHover } = this.props; + const { onDragStart, onDrag, onDragComplete } = this.props; + + return ( + + ); + } + + private readonly isHover = (moreProps) => { + const { onHover } = this.props; + + if ( + isDefined(onHover) + && isDefined(this.textWidth) + && !this.calculateTextWidth + ) { + const { rect } = this.helper(this.props, moreProps, this.textWidth); + const { mouseXY: [x, y] } = moreProps; + + if ( + x >= rect.x + && y >= rect.y + && x <= rect.x + rect.width + && y <= rect.y + rect.height + ) { + return true; + } + } + return false; + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { + bgFill, + bgOpacity, + bgStrokeWidth, + bgStroke, + textFill, + fontFamily, + fontSize, + fontStyle, + fontWeight, + text, + } = this.props; + + if (this.calculateTextWidth) { + ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`; + const { width } = ctx.measureText(text); + this.textWidth = width; + this.calculateTextWidth = false; + } + + const { selected } = this.props; + + const { x, y, rect } = this.helper(this.props, moreProps, this.textWidth); + + ctx.fillStyle = hexToRGBA(bgFill, bgOpacity); + + ctx.beginPath(); + ctx.fillRect(rect.x, rect.y, rect.width, rect.height); + + if (selected) { + ctx.strokeStyle = bgStroke; + ctx.lineWidth = bgStrokeWidth; + ctx.strokeRect(rect.x, rect.y, rect.width, rect.height); + } + + ctx.fillStyle = textFill; + ctx.textBaseline = "middle"; + ctx.textAlign = "center"; + ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`; + + ctx.beginPath(); + ctx.fillText(text, x, y); + } + + private readonly renderSVG = () => { + throw new Error("svg not implemented"); + } + + private readonly helper = (props, moreProps, textWidth) => { + const { position, fontSize } = props; + + const { xScale, chartConfig: { yScale } } = moreProps; + + const [xValue, yValue] = position; + const x = xScale(xValue); + const y = yScale(yValue); + + const rect = { + x: x - textWidth / 2 - fontSize, + y: y - fontSize, + width: textWidth + fontSize * 2, + height: fontSize * 2, + }; + + return { + x, y, rect, + }; + } +} diff --git a/packages/react-financial-charts/src/interactive/components/InteractiveYCoordinate.tsx b/packages/react-financial-charts/src/interactive/components/InteractiveYCoordinate.tsx new file mode 100644 index 000000000..7b16fdc38 --- /dev/null +++ b/packages/react-financial-charts/src/interactive/components/InteractiveYCoordinate.tsx @@ -0,0 +1,212 @@ +import * as React from "react"; + +import { drawOnCanvas } from "../../coordinates/EdgeCoordinateV3"; +import { getYCoordinate } from "../../coordinates/MouseCoordinateY"; +import GenericChartComponent from "../../GenericChartComponent"; +import { getMouseCanvas } from "../../GenericComponent"; +import { getStrokeDasharrayCanvas, hexToRGBA, isDefined, noop } from "../../utils"; + +interface InteractiveYCoordinateProps { + bgFill: string; + bgOpacity: number; + stroke: string; + strokeWidth: number; + strokeOpacity: number; + strokeDasharray: string; + textFill: string; + fontFamily: string; + fontSize: number; + fontWeight: number | string; + fontStyle: string; + text: string; + edge: object; + textBox: { + closeIcon: any; + padding: any; + }; + yValue: number; + onDragStart: any; // func + onDrag: any; // func + onDragComplete: any; // func + onHover?: any; // func + onUnHover?: any; // func + defaultClassName?: string; + interactiveCursorClass?: string; + tolerance: number; + selected: boolean; + hovering: boolean; +} + +export class InteractiveYCoordinate extends React.Component { + + public static defaultProps = { + onDragStart: noop, + onDrag: noop, + onDragComplete: noop, + fontWeight: "normal", // standard dev + strokeWidth: 1, + tolerance: 4, + selected: false, + hovering: false, + }; + + private width; + + public render() { + const { interactiveCursorClass } = this.props; + const { onHover, onUnHover } = this.props; + const { onDragStart, onDrag, onDragComplete } = this.props; + + return ( + + ); + } + + private readonly renderSVG = () => { + throw new Error("svg not implemented"); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { + bgFill, + bgOpacity, + + textFill, + fontFamily, + fontSize, + + fontStyle, + fontWeight, + stroke, + strokeWidth, + strokeOpacity, + strokeDasharray, + text, + textBox, + edge, + } = this.props; + + const { selected, hovering } = this.props; + + const values = helper(this.props, moreProps); + if (values == null) { return; } + + const { x1, x2, y, rect } = values; + + ctx.strokeStyle = hexToRGBA(stroke, strokeOpacity); + + ctx.beginPath(); + if (selected || hovering) { + ctx.lineWidth = strokeWidth + 1; + } else { + ctx.lineWidth = strokeWidth; + } + ctx.textBaseline = "middle"; + ctx.textAlign = "start"; + ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`; + + this.width = textBox.padding.left + + ctx.measureText(text).width + + textBox.padding.right + + textBox.closeIcon.padding.left + + textBox.closeIcon.width + + textBox.closeIcon.padding.right; + + ctx.setLineDash(getStrokeDasharrayCanvas(strokeDasharray)); + ctx.moveTo(x1, y); + ctx.lineTo(rect.x, y); + + ctx.moveTo(rect.x + this.width, y); + ctx.lineTo(x2, y); + ctx.stroke(); + + ctx.setLineDash([]); + + ctx.fillStyle = hexToRGBA(bgFill, bgOpacity); + + ctx.fillRect(rect.x, rect.y, this.width, rect.height); + ctx.strokeRect(rect.x, rect.y, this.width, rect.height); + + ctx.fillStyle = textFill; + + ctx.beginPath(); + ctx.fillText(text, rect.x + 10, y); + const newEdge = { + ...edge, + textFill, + fontFamily, + fontSize, + opacity: bgOpacity, + }; + + // @ts-ignore + const yValue = edge.displayFormat(this.props.yValue); + const yCoord = getYCoordinate(y, yValue, newEdge, moreProps); + drawOnCanvas(ctx, yCoord); + } + + private readonly isHover = (moreProps) => { + const { onHover } = this.props; + + if (isDefined(onHover)) { + const values = helper(this.props, moreProps); + if (values == null) { return false; } + + const { x1, x2, y, rect } = values; + const { mouseXY: [mouseX, mouseY] } = moreProps; + + if ( + mouseX >= rect.x + && mouseX <= rect.x + this.width + && mouseY >= rect.y + && mouseY <= rect.y + rect.height + ) { + return true; + } + if ( + x1 <= mouseX + && x2 >= mouseX + && Math.abs(mouseY - y) < 4 + ) { + return true; + } + } + return false; + } +} + +function helper(props, moreProps) { + const { yValue, textBox } = props; + + const { chartConfig: { width, yScale, height } } = moreProps; + + const y = Math.round(yScale(yValue)); + + if (y >= 0 && y <= height) { + const rect = { + x: textBox.left, + y: y - textBox.height / 2, + height: textBox.height, + }; + return { + x1: 0, + x2: width, + y, + rect, + }; + } +} diff --git a/packages/react-financial-charts/src/interactive/components/LinearRegressionChannelWithArea.tsx b/packages/react-financial-charts/src/interactive/components/LinearRegressionChannelWithArea.tsx new file mode 100644 index 000000000..16f0e4d2c --- /dev/null +++ b/packages/react-financial-charts/src/interactive/components/LinearRegressionChannelWithArea.tsx @@ -0,0 +1,221 @@ +import { deviation, sum } from "d3-array"; +import { path as d3Path } from "d3-path"; +import * as React from "react"; + +import GenericChartComponent from "../../GenericChartComponent"; +import { getMouseCanvas } from "../../GenericComponent"; +import { isHovering2 } from "./StraightLine"; + +import { getClosestItemIndexes, hexToRGBA, isDefined, noop, zipper } from "../../utils"; + +interface LinearRegressionChannelWithAreaProps { + x1Value: any; + x2Value: any; + type: + "SD" | // standard deviation channel + "Raff"; // Raff Regression Channel + interactiveCursorClass?: string; + stroke: string; + strokeWidth: number; + fill: string; + fillOpacity: number; + strokeOpacity: number; + onDragStart: any; // func + onDrag: any; // func + onDragComplete: any; // func + onHover?: any; // func + onUnHover?: any; // func + defaultClassName?: string; + tolerance: number; + selected: boolean; +} + +export class LinearRegressionChannelWithArea extends React.Component { + + public static defaultProps = { + onDragStart: noop, + onDrag: noop, + onDragComplete: noop, + type: "SD", // standard dev + strokeWidth: 1, + tolerance: 4, + selected: false, + }; + + public render() { + const { selected, interactiveCursorClass } = this.props; + const { onHover, onUnHover } = this.props; + + return ; + } + + private readonly renderSVG = (moreProps) => { + const { stroke, strokeWidth, fillOpacity, strokeOpacity, fill } = this.props; + const { x1, y1, x2, y2, dy } = helper(this.props, moreProps); + const line = { + strokeWidth, + stroke, + strokeOpacity, + }; + const ctx = d3Path(); + ctx.moveTo(x1, y1 - dy); + ctx.lineTo(x2, y2 - dy); + ctx.lineTo(x2, y2 + dy); + ctx.lineTo(x1, y1 + dy); + ctx.closePath(); + return ( + + + + + + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { stroke, strokeWidth, fillOpacity, strokeOpacity, fill } = this.props; + const { x1, y1, x2, y2, dy } = helper(this.props, moreProps); + + ctx.lineWidth = strokeWidth; + ctx.strokeStyle = hexToRGBA(stroke, strokeOpacity); + ctx.fillStyle = hexToRGBA(fill, fillOpacity); + + ctx.beginPath(); + ctx.moveTo(x1, y1 - dy); + ctx.lineTo(x2, y2 - dy); + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(x2, y2 + dy); + ctx.lineTo(x1, y1 + dy); + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(x1, y1 - dy); + ctx.lineTo(x2, y2 - dy); + ctx.lineTo(x2, y2 + dy); + ctx.lineTo(x1, y1 + dy); + ctx.closePath(); + ctx.fill(); + + ctx.beginPath(); + ctx.moveTo(x2, y2); + ctx.lineTo(x1, y1); + ctx.stroke(); + } + + private readonly isHover = (moreProps) => { + const { tolerance, onHover } = this.props; + + if (isDefined(onHover)) { + const { mouseXY } = moreProps; + + const { x1, y1, x2, y2, dy } = helper(this.props, moreProps); + const yDiffs = [-dy, 0, dy]; + + const hovering = yDiffs.reduce((result, diff) => result || isHovering2( + [x1, y1 + diff], [x2, y2 + diff], mouseXY, tolerance, + ), false); + return hovering; + } + return false; + } +} + +export function edge1Provider(props) { + return function (moreProps) { + const { x1, y1 } = helper(props, moreProps); + return [x1, y1]; + }; +} + +export function edge2Provider(props) { + return function (moreProps) { + const { x2, y2 } = helper(props, moreProps); + return [x2, y2]; + }; +} + +function helper(props, moreProps) { + const { x1Value, x2Value, type } = props; + + const { xScale, chartConfig: { yScale }, fullData } = moreProps; + const { xAccessor } = moreProps; + + const { left } = getClosestItemIndexes(fullData, x1Value, xAccessor); + const { right } = getClosestItemIndexes(fullData, x2Value, xAccessor); + + const startIndex = Math.min(left, right); + const endIndex = Math.max(left, right) + 1; + + const array = fullData.slice(startIndex, endIndex); + + const xs = array.map((d) => xAccessor(d).valueOf()); + const ys = array.map((d) => d.close); + const n = array.length; + + const combine = zipper() + .combine((x, y) => x * y); + + // @ts-ignore + const xys = combine(xs, ys); + const xSquareds = xs.map((x) => Math.pow(x, 2)); + + const b = (n * sum(xys) - sum(xs) * sum(ys)) / (n * sum(xSquareds) - Math.pow(sum(xs), 2)); + const a = (sum(ys) - b * sum(xs)) / n; + + const newy1 = a + b * x1Value; + const newy2 = a + b * x2Value; + + const x1 = xScale(x1Value); + const y1 = yScale(newy1); + const x2 = xScale(x2Value); + const y2 = yScale(newy2); + + const stdDev = type === "SD" + ? deviation(array, (d) => d.close) + : 0; + + const dy = yScale(newy1 - stdDev) - y1; + + return { + x1, y1, x2, y2, dy, + }; +} diff --git a/packages/react-financial-charts/src/interactive/components/MouseLocationIndicator.tsx b/packages/react-financial-charts/src/interactive/components/MouseLocationIndicator.tsx new file mode 100644 index 000000000..e4fe4543d --- /dev/null +++ b/packages/react-financial-charts/src/interactive/components/MouseLocationIndicator.tsx @@ -0,0 +1,149 @@ +import * as React from "react"; + +import GenericChartComponent from "../../GenericChartComponent"; +import { getMouseCanvas } from "../../GenericComponent"; + +import { functor, getClosestValue, isDefined, noop, shallowEqual } from "../../utils"; +import { getXValue } from "../../utils/ChartDataUtil"; + +interface MouseLocationIndicatorProps { + enabled: boolean; + snap: boolean; + shouldDisableSnap: any; // func; + snapTo?: any; // func; + onMouseMove: any; // func; + onMouseDown: any; // func; + onClick: any; // func; + r?: number; + stroke: string; + strokeWidth: number; + opacity: number; + disablePan: boolean; +} + +export class MouseLocationIndicator extends React.Component { + + public static defaultProps = { + onMouseMove: noop, + onMouseDown: noop, + onClick: noop, + shouldDisableSnap: functor(false), + stroke: "#000000", + strokeWidth: 1, + opacity: 1, + disablePan: true, + }; + + private mutableState; + + constructor(props) { + super(props); + this.renderSVG = this.renderSVG.bind(this); + this.drawOnCanvas = this.drawOnCanvas.bind(this); + + this.handleMousePosChange = this.handleMousePosChange.bind(this); + this.handleMouseDown = this.handleMouseDown.bind(this); + this.handleClick = this.handleClick.bind(this); + this.xy = this.xy.bind(this); + + this.mutableState = {}; + } + + public render() { + const { enabled, disablePan } = this.props; + + return ( + + ); + } + + private readonly xy = (moreProps, e) => { + const { xAccessor, plotData } = moreProps; + const { mouseXY, currentItem, xScale, chartConfig: { yScale } } = moreProps; + const { enabled, snap, shouldDisableSnap, snapTo } = this.props; + + if (enabled && isDefined(currentItem) && isDefined(e)) { + const xValue = snap && !shouldDisableSnap(e) + ? xAccessor(currentItem) + : getXValue(xScale, xAccessor, mouseXY, plotData); + const yValue = snap && !shouldDisableSnap(e) + ? getClosestValue(snapTo(currentItem), yScale.invert(mouseXY[1])) + : yScale.invert(mouseXY[1]); + + const x = xScale(xValue); + const y = yScale(yValue); + + return { xValue, yValue, x, y }; + } + } + + private readonly handleClick = (moreProps, e) => { + const pos = this.xy(moreProps, e); + if (pos !== undefined && isDefined(pos)) { + const { xValue, yValue, x, y } = pos; + this.mutableState = { x, y }; + this.props.onClick([xValue, yValue], moreProps, e); + } + } + + private readonly handleMouseDown = (moreProps, e) => { + const pos = this.xy(moreProps, e); + if (pos !== undefined && isDefined(pos)) { + const { xValue, yValue, x, y } = pos; + this.mutableState = { x, y }; + this.props.onMouseDown([xValue, yValue], moreProps, e); + } + } + + private readonly handleMousePosChange = (moreProps, e) => { + if (!shallowEqual(moreProps.mousXY, moreProps.prevMouseXY)) { + const pos = this.xy(moreProps, e); + if (pos !== undefined && isDefined(pos)) { + const { xValue, yValue, x, y } = pos; + this.mutableState = { x, y }; + this.props.onMouseMove([xValue, yValue], e); + } + } + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { enabled, r, stroke, strokeWidth } = this.props; + const { x, y } = this.mutableState; + const { show } = moreProps; + if (enabled && show && isDefined(x)) { + ctx.lineWidth = strokeWidth; + ctx.strokeStyle = stroke; + ctx.moveTo(x, y); + ctx.beginPath(); + ctx.arc(x, y, r, 0, 2 * Math.PI, false); + ctx.stroke(); + } + } + + private readonly renderSVG = (moreProps) => { + const { enabled, r, stroke, strokeWidth, opacity } = this.props; + const { x, y } = this.mutableState; + const { show } = moreProps; + + return enabled && show && isDefined(x) + ? + : null; + } +} diff --git a/packages/react-financial-charts/src/interactive/components/StraightLine.tsx b/packages/react-financial-charts/src/interactive/components/StraightLine.tsx new file mode 100644 index 000000000..cfa2a90d8 --- /dev/null +++ b/packages/react-financial-charts/src/interactive/components/StraightLine.tsx @@ -0,0 +1,328 @@ +import * as React from "react"; + +import GenericChartComponent from "../../GenericChartComponent"; +import { getMouseCanvas } from "../../GenericComponent"; + +import { + getStrokeDasharray, + hexToRGBA, + isDefined, + noop, + strokeDashTypes, +} from "../../utils"; + +interface StraightLineProps { + x1Value: any; + x2Value: any; + y1Value: any; + y2Value: any; + + interactiveCursorClass?: string; + stroke: string; + strokeWidth: number; + strokeOpacity: number; + strokeDasharray: strokeDashTypes; + type: + "XLINE" | // extends from -Infinity to +Infinity + "RAY" | // extends to +/-Infinity in one direction + "LINE"; // extends between the set bounds + + onEdge1Drag: any; // func + onEdge2Drag: any; // func + onDragStart: any; // func + onDrag: any; // func + onDragComplete: any; // func + onHover?: any; // func + onUnHover?: any; // func + defaultClassName?: string; + r: number; + edgeFill: string; + edgeStroke: string; + edgeStrokeWidth: number; + withEdge: boolean; + children: any; // func + tolerance: number; + selected: boolean; +} + +class StraightLine extends React.Component { + + public static defaultProps = { + onEdge1Drag: noop, + onEdge2Drag: noop, + onDragStart: noop, + onDrag: noop, + onDragComplete: noop, + edgeStrokeWidth: 3, + edgeStroke: "#000000", + edgeFill: "#FFFFFF", + r: 10, + withEdge: false, + strokeWidth: 1, + strokeDasharray: "Solid", + children: noop, + tolerance: 7, + selected: false, + }; + + public render() { + const { selected, interactiveCursorClass } = this.props; + const { onDragStart, onDrag, onDragComplete, onHover, onUnHover } = this.props; + + return ( + + ); + } + + private readonly isHover = (moreProps) => { + const { tolerance, onHover } = this.props; + + if (isDefined(onHover)) { + const { x1Value, x2Value, y1Value, y2Value, type } = this.props; + const { mouseXY, xScale } = moreProps; + const { chartConfig: { yScale } } = moreProps; + + const hovering = isHovering({ + x1Value, y1Value, + x2Value, y2Value, + mouseXY, + type, + tolerance, + xScale, + yScale, + }); + + return hovering; + } + return false; + } + + private readonly renderSVG = (moreProps) => { + const { stroke, strokeWidth, strokeOpacity, strokeDasharray } = this.props; + + const lineWidth = strokeWidth; + + const { x1, y1, x2, y2 } = helper(this.props, moreProps); + return ( + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { stroke, strokeWidth, strokeOpacity, strokeDasharray } = this.props; + const { x1, y1, x2, y2 } = helper(this.props, moreProps); + + ctx.lineWidth = strokeWidth; + ctx.strokeStyle = hexToRGBA(stroke, strokeOpacity); + ctx.setLineDash(getStrokeDasharray(strokeDasharray).split(",")); + + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + } +} + +export function isHovering2(start, end, [mouseX, mouseY], tolerance) { + const m = getSlope(start, end); + + if (m !== undefined && isDefined(m)) { + const b = getYIntercept(m, end); + const y = m * mouseX + b; + return (mouseY < y + tolerance) + && mouseY > (y - tolerance) + && mouseX > Math.min(start[0], end[0]) - tolerance + && mouseX < Math.max(start[0], end[0]) + tolerance; + } else { + return mouseY >= Math.min(start[1], end[1]) + && mouseY <= Math.max(start[1], end[1]) + && mouseX < start[0] + tolerance + && mouseX > start[0] - tolerance; + } +} + +export function isHovering({ + x1Value, y1Value, + x2Value, y2Value, + mouseXY, + type, + tolerance, + xScale, + yScale, +}) { + + const line = generateLine({ + type, + start: [x1Value, y1Value], + end: [x2Value, y2Value], + xScale, + yScale, + }); + + const start = [xScale(line.x1), yScale(line.y1)]; + const end = [xScale(line.x2), yScale(line.y2)]; + + const m = getSlope(start, end); + const [mouseX, mouseY] = mouseXY; + + if (m !== undefined && isDefined(m)) { + const b = getYIntercept(m, end); + const y = m * mouseX + b; + + return mouseY < (y + tolerance) + && mouseY > (y - tolerance) + && mouseX > Math.min(start[0], end[0]) - tolerance + && mouseX < Math.max(start[0], end[0]) + tolerance; + } else { + return mouseY >= Math.min(start[1], end[1]) + && mouseY <= Math.max(start[1], end[1]) + && mouseX < start[0] + tolerance + && mouseX > start[0] - tolerance; + } +} + +function helper(props, moreProps) { + const { x1Value, x2Value, y1Value, y2Value, type } = props; + + const { xScale, chartConfig: { yScale } } = moreProps; + + const modLine = generateLine({ + type, + start: [x1Value, y1Value], + end: [x2Value, y2Value], + xScale, + yScale, + }); + + const x1 = xScale(modLine.x1); + const y1 = yScale(modLine.y1); + const x2 = xScale(modLine.x2); + const y2 = yScale(modLine.y2); + + return { + x1, y1, x2, y2, + }; +} + +export function getSlope(start, end) { + const m /* slope */ = end[0] === start[0] + ? undefined + : (end[1] - start[1]) / (end[0] - start[0]); + return m; +} +export function getYIntercept(m, end) { + const b /* y intercept */ = -1 * m * end[0] + end[1]; + return b; +} + +export function generateLine({ + type, start, end, xScale, yScale, +}) { + const m /* slope */ = getSlope(start, end); + // console.log(end[0] - start[0], m) + const b /* y intercept */ = getYIntercept(m, start); + + switch (type) { + case "XLINE": + return getXLineCoordinates({ + start, end, xScale, yScale, m, b, + }); + case "RAY": + return getRayCoordinates({ + start, end, xScale, yScale, m, b, + }); + default: + case "LINE": + return getLineCoordinates({ + start, end, + }); + } +} + +function getXLineCoordinates({ + start, end, xScale, yScale, m, b, +}) { + const [xBegin, xFinish] = xScale.domain(); + const [yBegin, yFinish] = yScale.domain(); + + if (end[0] === start[0]) { + return { + x1: end[0], y1: yBegin, + x2: end[0], y2: yFinish, + }; + } + const [x1, x2] = end[0] > start[0] + ? [xBegin, xFinish] + : [xFinish, xBegin]; + + return { + x1, y1: m * x1 + b, + x2, y2: m * x2 + b, + }; +} + +function getRayCoordinates({ + start, end, xScale, yScale, m, b, +}) { + const [xBegin, xFinish] = xScale.domain(); + const [yBegin, yFinish] = yScale.domain(); + + const x1 = start[0]; + if (end[0] === start[0]) { + return { + x1, + y1: start[1], + x2: x1, + y2: end[1] > start[1] ? yFinish : yBegin, + }; + } + + const x2 = end[0] > start[0] + ? xFinish + : xBegin; + + return { + x1, y1: m * x1 + b, + x2, y2: m * x2 + b, + }; +} + +function getLineCoordinates({ + start, end, +}) { + + const [x1, y1] = start; + const [x2, y2] = end; + if (end[0] === start[0]) { + return { + x1, + y1: start[1], + x2: x1, + y2: end[1], + }; + } + + return { + x1, y1, + x2, y2, + }; +} + +export default StraightLine; diff --git a/packages/react-financial-charts/src/interactive/components/Text.tsx b/packages/react-financial-charts/src/interactive/components/Text.tsx new file mode 100644 index 000000000..36da57ec8 --- /dev/null +++ b/packages/react-financial-charts/src/interactive/components/Text.tsx @@ -0,0 +1,74 @@ +import * as React from "react"; + +import GenericChartComponent from "../../GenericChartComponent"; +import { getMouseCanvas } from "../../GenericComponent"; + +interface TextProps { + xyProvider: (moreProps: any) => number[]; + fontFamily: string; + fontSize: number; + fill: string; + children: string; + selected: boolean; +} + +export class Text extends React.Component { + + public static defaultProps = { + selected: false, + }; + + public render() { + const { selected } = this.props; + + return ( + + ); + } + + private readonly isHover = () => { + return false; + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { + xyProvider, + fontFamily, + fontSize, + fill, + children, + } = this.props; + const [x, y] = xyProvider(moreProps); + + ctx.font = `${fontSize}px ${fontFamily}`; + ctx.fillStyle = fill; + + ctx.beginPath(); + ctx.fillText(children, x, y); + } + + private readonly renderSVG = (moreProps) => { + const { + xyProvider, + fontFamily, + fontSize, + fill, + children, + } = this.props; + const [x, y] = xyProvider(moreProps); + + return {children}; + } +} diff --git a/packages/react-financial-charts/src/interactive/index.ts b/packages/react-financial-charts/src/interactive/index.ts new file mode 100644 index 000000000..03ea2aa7d --- /dev/null +++ b/packages/react-financial-charts/src/interactive/index.ts @@ -0,0 +1,10 @@ +export { TrendLine } from "./TrendLine"; +export { FibonacciRetracement } from "./FibonacciRetracement"; +export { EquidistantChannel } from "./EquidistantChannel"; +export { StandardDeviationChannel } from "./StandardDeviationChannel"; +export { GannFan } from "./GannFan"; +export { ClickCallback } from "./ClickCallback"; +export { Brush } from "./Brush"; +export { InteractiveText } from "./InteractiveText"; +export { InteractiveYCoordinate } from "./InteractiveYCoordinate"; +export { DrawingObjectSelector } from "./DrawingObjectSelector"; diff --git a/packages/react-financial-charts/src/interactive/utils.ts b/packages/react-financial-charts/src/interactive/utils.ts new file mode 100644 index 000000000..561fd5ca2 --- /dev/null +++ b/packages/react-financial-charts/src/interactive/utils.ts @@ -0,0 +1,103 @@ +import { + find, + isDefined, + isNotDefined, + mapObject, +} from "../utils"; + +export function getValueFromOverride(override, index, key, defaultValue) { + if (isDefined(override) && override.index === index) { + return override[key]; + } + return defaultValue; +} + +export function terminate() { + // @ts-ignore + this.setState({ + current: null, + override: null, + }); +} + +export function saveNodeType(type) { + return (node) => { + // @ts-ignore + if (isNotDefined(node) && isDefined(this.nodes[type])) { + // @ts-ignore + delete this.nodes[type]; + } else { + // @ts-ignore + this.nodes[type] = node; + } + // console.error(this.nodes) + }; +} +export function isHoverForInteractiveType(interactiveType) { + return function (moreProps) { // this has to be function as it is bound to this + + // @ts-ignore + if (isDefined(this.nodes)) { + // @ts-ignore + const selecedNodes = this.nodes + .map((node) => node.isHover(moreProps)); + // @ts-ignore + const interactive = this.props[interactiveType].map((t, idx) => { + return { + ...t, + selected: selecedNodes[idx], + }; + }); + return interactive; + } + }; +} + +export function isHover(moreProps) { + // @ts-ignore + const hovering = mapObject(this.nodes, (node) => node.isHover(moreProps)) + .reduce((a, b) => { + return a || b; + }); + return hovering; +} + +function getMouseXY(moreProps, [ox, oy]) { + if (Array.isArray(moreProps.mouseXY)) { + const { mouseXY: [x, y] } = moreProps; + const mouseXY = [ + x - ox, + y - oy, + ]; + return mouseXY; + } + return moreProps.mouseXY; +} + +export function getMorePropsForChart(moreProps, chartId) { + const { chartConfig: chartConfigList } = moreProps; + const chartConfig = find(chartConfigList, (each) => each.id === chartId); + + const { origin } = chartConfig; + const mouseXY = getMouseXY(moreProps, origin); + return { + ...moreProps, + chartConfig, + mouseXY, + }; +} + +export function getSelected(interactives) { + const selected = interactives + .map((each) => { + const objects = each.objects.filter((obj) => { + return obj.selected; + }); + return { + ...each, + objects, + }; + }) + .filter((each) => each.objects.length > 0); + return selected; +} diff --git a/packages/react-financial-charts/src/interactive/wrapper/EachEquidistantChannel.tsx b/packages/react-financial-charts/src/interactive/wrapper/EachEquidistantChannel.tsx new file mode 100644 index 000000000..62bfc79c5 --- /dev/null +++ b/packages/react-financial-charts/src/interactive/wrapper/EachEquidistantChannel.tsx @@ -0,0 +1,310 @@ +import * as React from "react"; + +import { isDefined, noop } from "../../utils"; +import { getXValue } from "../../utils/ChartDataUtil"; +import { isHover, saveNodeType } from "../utils"; + +import { ChannelWithArea } from "../components/ChannelWithArea"; +import { ClickableCircle } from "../components/ClickableCircle"; +import { HoverTextNearMouse } from "../components/HoverTextNearMouse"; + +interface EachEquidistantChannelProps { + readonly startXY: number[]; + readonly endXY: number[]; + readonly dy?: number; + readonly interactive: boolean; + readonly selected: boolean; + readonly hoverText: { + enable: boolean; + fontFamily: string; + fontSize: number; + fill: string; + text: string; + bgFill: string; + bgOpacity: number; + bgWidth: number | string; + bgHeight: number | string; + }; + readonly appearance: { + stroke: string; + fillOpacity: number; + strokeOpacity: number; + strokeWidth: number; + fill: string; + edgeStroke: string; + edgeFill: string; + edgeFill2: string; + edgeStrokeWidth: number; + r: number; + }; + readonly index?: number; + readonly onDrag: any; // func + readonly onDragComplete: any; // func +} + +interface EachEquidistantChannelState { + hover: boolean; +} + +export class EachEquidistantChannel extends React.Component { + + public static defaultProps = { + yDisplayFormat: (d) => d.toFixed(2), + interactive: true, + selected: false, + onDrag: noop, + onDragComplete: noop, + hoverText: { + enable: false, + }, + }; + + private dragStart; + // @ts-ignore + private isHover; + private saveNodeType; + + constructor(props: EachEquidistantChannelProps) { + super(props); + + this.isHover = isHover.bind(this); + this.saveNodeType = saveNodeType.bind(this); + + this.state = { + hover: false, + }; + } + + public render() { + const { startXY, endXY, dy } = this.props; + const { interactive, hoverText, appearance } = this.props; + const { + edgeFill, edgeFill2, + stroke, strokeWidth, strokeOpacity, + fill, fillOpacity, + } = appearance; + const { selected } = this.props; + const { onDragComplete } = this.props; + const { hover } = this.state; + const { enable: hoverTextEnabled, ...restHoverTextProps } = hoverText; + + const hoverHandler = interactive + ? { onHover: this.handleHover, onUnHover: this.handleHover } + : {}; + + const line1Edge = isDefined(startXY) && isDefined(endXY) + ? + {this.getEdgeCircle({ + xy: startXY, + dragHandler: this.handleLine1Edge1Drag, + cursor: "react-stockcharts-move-cursor", + fill: edgeFill, + edge: "line1edge1", + })} + {this.getEdgeCircle({ + xy: endXY, + dragHandler: this.handleLine1Edge2Drag, + cursor: "react-stockcharts-move-cursor", + fill: edgeFill, + edge: "line1edge2", + })} + + : null; + const line2Edge = dy !== undefined && isDefined(dy) + ? + {this.getEdgeCircle({ + xy: [startXY[0], startXY[1] + dy], + dragHandler: this.handleChannelHeightChange, + cursor: "react-stockcharts-ns-resize-cursor", + fill: edgeFill2, + edge: "line2edge1", + })} + {this.getEdgeCircle({ + xy: [endXY[0], endXY[1] + dy], + dragHandler: this.handleChannelHeightChange, + cursor: "react-stockcharts-ns-resize-cursor", + fill: edgeFill2, + edge: "line2edge2", + })} + + : null; + + return + + {line1Edge} + {line2Edge} + + ; + } + + private readonly getEdgeCircle = ({ xy, dragHandler, cursor, fill, edge }) => { + const { hover } = this.state; + const { appearance } = this.props; + const { edgeStroke, edgeStrokeWidth, r } = appearance; + const { selected } = this.props; + const { onDragComplete } = this.props; + + return ; + } + + private readonly handleChannelHeightChange = (moreProps) => { + const { index, onDrag } = this.props; + + const { + startXY, endXY, + } = this.dragStart; + + const { chartConfig: { yScale } } = moreProps; + const { startPos, mouseXY } = moreProps; + + const y2 = yScale(endXY[1]); + + const dy = startPos[1] - mouseXY[1]; + + const newY2Value = yScale.invert(y2 - dy); + + const newDy = newY2Value - endXY[1] + this.dragStart.dy; + + onDrag(index, { + startXY, + endXY, + dy: newDy, + }); + } + + private readonly handleLine1Edge2Drag = (moreProps) => { + const { index, onDrag } = this.props; + const { + endXY, + } = this.dragStart; + + const { + startPos, mouseXY, xAccessor, + xScale, fullData, + chartConfig: { yScale }, + } = moreProps; + + const dx = startPos[0] - mouseXY[0]; + const dy = startPos[1] - mouseXY[1]; + + const x1 = xScale(endXY[0]); + const y1 = yScale(endXY[1]); + + const newX1Value = getXValue(xScale, xAccessor, [x1 - dx, y1 - dy], fullData); + const newY1Value = yScale.invert(y1 - dy); + + onDrag(index, { + startXY: this.dragStart.startXY, + endXY: [newX1Value, newY1Value], + dy: this.dragStart.dy, + }); + } + + private readonly handleLine1Edge1Drag = (moreProps) => { + const { index, onDrag } = this.props; + const { + startXY, + } = this.dragStart; + + const { + startPos, mouseXY, xAccessor, + xScale, fullData, + chartConfig: { yScale }, + } = moreProps; + + const dx = startPos[0] - mouseXY[0]; + const dy = startPos[1] - mouseXY[1]; + + const x1 = xScale(startXY[0]); + const y1 = yScale(startXY[1]); + + const newX1Value = getXValue(xScale, xAccessor, [x1 - dx, y1 - dy], fullData); + const newY1Value = yScale.invert(y1 - dy); + + onDrag(index, { + startXY: [newX1Value, newY1Value], + endXY: this.dragStart.endXY, + dy: this.dragStart.dy, + }); + } + + private readonly handleChannelDrag = (moreProps) => { + const { index, onDrag } = this.props; + + const { + startXY, endXY, + } = this.dragStart; + + const { xScale, chartConfig: { yScale }, xAccessor, fullData } = moreProps; + const { startPos, mouseXY } = moreProps; + + const x1 = xScale(startXY[0]); + const y1 = yScale(startXY[1]); + const x2 = xScale(endXY[0]); + const y2 = yScale(endXY[1]); + + const dx = startPos[0] - mouseXY[0]; + const dy = startPos[1] - mouseXY[1]; + + const newX1Value = getXValue(xScale, xAccessor, [x1 - dx, y1 - dy], fullData); + const newY1Value = yScale.invert(y1 - dy); + const newX2Value = getXValue(xScale, xAccessor, [x2 - dx, y2 - dy], fullData); + const newY2Value = yScale.invert(y2 - dy); + + // const newDy = newY2Value - endXY[1] + this.dragStart.dy; + + onDrag(index, { + startXY: [newX1Value, newY1Value], + endXY: [newX2Value, newY2Value], + dy: this.dragStart.dy, + }); + } + + private readonly handleDragStart = () => { + const { + startXY, endXY, dy, + } = this.props; + + this.dragStart = { + startXY, endXY, dy, + }; + } + + private readonly handleHover = (moreProps) => { + if (this.state.hover !== moreProps.hovering) { + this.setState({ + hover: moreProps.hovering, + }); + } + } +} diff --git a/packages/react-financial-charts/src/interactive/wrapper/EachFibRetracement.tsx b/packages/react-financial-charts/src/interactive/wrapper/EachFibRetracement.tsx new file mode 100644 index 000000000..369475841 --- /dev/null +++ b/packages/react-financial-charts/src/interactive/wrapper/EachFibRetracement.tsx @@ -0,0 +1,354 @@ +import * as React from "react"; + +import { head, last, noop } from "../../utils"; +import { getXValue } from "../../utils/ChartDataUtil"; +import { isHover, saveNodeType } from "../utils"; + +import { ClickableCircle } from "../components/ClickableCircle"; +import { HoverTextNearMouse } from "../components/HoverTextNearMouse"; +import StraightLine, { generateLine } from "../components/StraightLine"; +import { Text } from "../components/Text"; +import { getNewXY } from "./EachTrendLine"; + +interface EachFibRetracementProps { + x1: any; + x2: any; + y1: number; + y2: number; + yDisplayFormat: any; // func + type: string; + selected: boolean; + appearance: { + stroke: string; + strokeWidth: number; + strokeOpacity: number; + fontFamily: string; + fontSize: number; + fontFill: string; + edgeStroke: string; + edgeFill: string; + nsEdgeFill: string; + edgeStrokeWidth: number; + r: number; + }; + interactive: boolean; + hoverText: { + enable: boolean; + fontFamily: string; + fontSize: number; + fill: string; + text: string; + bgFill: string; + bgOpacity: number; + bgWidth: number | string; + bgHeight: number | string; + selectedText: string; + }; + index?: number; + onDrag: any; // func + onDragComplete: any; // func +} + +interface EachFibRetracementState { + hover: boolean; +} + +export class EachFibRetracement extends React.Component { + + public static defaultProps = { + yDisplayFormat: (d) => d.toFixed(2), + interactive: true, + appearance: { + stroke: "#000000", + strokeWidth: 1, + strokeOpacity: 1, + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 10, + fontFill: "#000000", + edgeStroke: "#000000", + edgeFill: "#FFFFFF", + nsEdgeFill: "#000000", + edgeStrokeWidth: 1, + r: 5, + }, + selected: false, + onDrag: noop, + onDragComplete: noop, + hoverText: { + enable: false, + }, + }; + + private dragStart; + // @ts-ignore + private isHover; + private saveNodeType; + + public constructor(props) { + super(props); + + this.isHover = isHover.bind(this); + this.saveNodeType = saveNodeType.bind(this); + + this.state = { + hover: false, + }; + } + + public render() { + const { x1, x2, y1, y2 } = this.props; + const { interactive, yDisplayFormat, type, appearance } = this.props; + const { stroke, strokeWidth, strokeOpacity } = appearance; + const { fontFamily, fontSize, fontFill } = appearance; + const { edgeStroke, edgeFill, nsEdgeFill, edgeStrokeWidth, r } = appearance; + const { hoverText, selected } = this.props; + const { hover } = this.state; + const { onDragComplete } = this.props; + const lines = helper({ x1, x2, y1, y2 }); + + const { + enable: hoverTextEnabled, + selectedText: hoverTextSelected, + text: hoverTextUnselected, + ...restHoverTextProps + } = hoverText; + + const lineType = type === "EXTEND" ? "XLINE" : type === "BOUND" ? "LINE" : "RAY"; + const dir = head(lines).y1 > last(lines).y1 ? 3 : -1.3; + + return ( + + {lines.map((line, j) => { + const text = `${yDisplayFormat(line.y)} (${line.percent.toFixed(2)}%)`; + + const xyProvider = ({ xScale, chartConfig }) => { + const { yScale } = chartConfig; + const { x1: lineX1, y1: lineY1, x2: lineX2 } = generateLine({ + type: lineType, + start: [line.x1, line.y], + end: [line.x2, line.y], + xScale, + yScale, + }); + + const x = xScale(Math.min(lineX1, lineX2)) + 10; + const y = yScale(lineY1) + dir * 4; + return [x, y]; + }; + + const firstOrLast = (j === 0) || (j === lines.length - 1); + + const interactiveCursorClass = firstOrLast + ? "react-stockcharts-ns-resize-cursor" + : "react-stockcharts-move-cursor"; + + const interactiveEdgeCursorClass = firstOrLast + ? "react-stockcharts-ns-resize-cursor" + : "react-stockcharts-ew-resize-cursor"; + + const dragHandler = j === 0 + ? this.handleLineNSResizeTop + : j === lines.length - 1 + ? this.handleLineNSResizeBottom + : this.handleLineMove; + + const edge1DragHandler = j === 0 + ? this.handleLineNSResizeTop + : j === lines.length - 1 + ? this.handleLineNSResizeBottom + : this.handleEdge1Drag; + const edge2DragHandler = j === 0 + ? this.handleLineNSResizeTop + : j === lines.length - 1 + ? this.handleLineNSResizeBottom + : this.handleEdge2Drag; + + const hoverHandler = interactive + ? { onHover: this.handleHover, onUnHover: this.handleHover } + : {}; + return + + {text} + + + ; + })} + + + ); + } + + private readonly handleEdge2Drag = (moreProps) => { + const { index, onDrag } = this.props; + const { + x1, y1, y2, + } = this.props; + + const [x2] = getNewXY(moreProps); + + onDrag(index, { + x1, + y1, + x2, + y2, + }); + } + + private readonly handleEdge1Drag = (moreProps) => { + const { index, onDrag } = this.props; + const { + y1, x2, y2, + } = this.props; + + const [x1] = getNewXY(moreProps); + + onDrag(index, { + x1, + y1, + x2, + y2, + }); + } + + private readonly handleLineNSResizeBottom = (moreProps) => { + const { index, onDrag } = this.props; + const { + x1, y1, x2, + } = this.props; + + const [, y2] = getNewXY(moreProps); + + onDrag(index, { + x1, + y1, + x2, + y2, + }); + } + + private readonly handleLineNSResizeTop = (moreProps) => { + const { index, onDrag } = this.props; + const { + x1, x2, y2, + } = this.props; + + const [, y1] = getNewXY(moreProps); + + onDrag(index, { + x1, + y1, + x2, + y2, + }); + } + + private readonly handleLineMove = (moreProps) => { + const { index, onDrag } = this.props; + + const { + x1: x1Value, y1: y1Value, x2: x2Value, y2: y2Value, + } = this.dragStart; + + const { xScale, chartConfig: { yScale }, xAccessor, fullData } = moreProps; + const { startPos, mouseXY } = moreProps; + + const x1 = xScale(x1Value); + const y1 = yScale(y1Value); + const x2 = xScale(x2Value); + const y2 = yScale(y2Value); + + const dx = startPos[0] - mouseXY[0]; + const dy = startPos[1] - mouseXY[1]; + + const newX1Value = getXValue(xScale, xAccessor, [x1 - dx, y1 - dy], fullData); + const newY1Value = yScale.invert(y1 - dy); + const newX2Value = getXValue(xScale, xAccessor, [x2 - dx, y2 - dy], fullData); + const newY2Value = yScale.invert(y2 - dy); + + onDrag(index, { + x1: newX1Value, + y1: newY1Value, + x2: newX2Value, + y2: newY2Value, + }); + } + + private readonly handleLineDragStart = () => { + const { + x1, y1, x2, y2, + } = this.props; + + this.dragStart = { + x1, y1, x2, y2, + }; + } + + private readonly handleHover = (moreProps) => { + if (this.state.hover !== moreProps.hovering) { + this.setState({ + hover: moreProps.hovering, + }); + } + } +} + +function helper({ x1, y1, x2, y2 }) { + const dy = y2 - y1; + const retracements = [100, 61.8, 50, 38.2, 23.6, 0] + .map((each) => ({ + percent: each, + x1, + x2, + y: (y2 - (each / 100) * dy), + })); + + return retracements; +} diff --git a/packages/react-financial-charts/src/interactive/wrapper/EachGannFan.tsx b/packages/react-financial-charts/src/interactive/wrapper/EachGannFan.tsx new file mode 100644 index 000000000..98108103e --- /dev/null +++ b/packages/react-financial-charts/src/interactive/wrapper/EachGannFan.tsx @@ -0,0 +1,300 @@ +import * as React from "react"; + +import { isDefined, noop } from "../../utils"; +import { getXValue } from "../../utils/ChartDataUtil"; +import { isHover, saveNodeType } from "../utils"; + +import { ClickableCircle } from "../components/ClickableCircle"; +import { GannFan } from "../components/GannFan"; +import { HoverTextNearMouse } from "../components/HoverTextNearMouse"; + +interface EachGannFanProps { + startXY: number[]; + endXY: number[]; + dy?: number; + interactive: boolean; + selected: boolean; + appearance: { + stroke: string; + strokeOpacity: number; + fillOpacity: number; + strokeWidth: number; + edgeStroke: string; + edgeFill: string; + edgeStrokeWidth: number; + r: number; + fill: string[]; + fontFamily: string; + fontSize: number; + fontFill: string; + }; + hoverText: { + enable: boolean; + fontFamily: string; + fontSize: number; + fill: string; + text: string; + bgFill: string; + bgOpacity: number; + bgWidth: number | string; + bgHeight: number | string; + selectedText: string; + }; + index?: number; + onDrag: any; // func + onDragComplete: any; // func +} + +interface EachGannFanState { + hover: boolean; +} + +export class EachGannFan extends React.Component { + + public static defaultProps = { + yDisplayFormat: (d) => d.toFixed(2), + interactive: true, + selected: false, + appearance: { + stroke: "#000000", + fillOpacity: 0.2, + strokeOpacity: 1, + strokeWidth: 1, + edgeStroke: "#000000", + edgeFill: "#FFFFFF", + edgeStrokeWidth: 1, + r: 5, + fill: [ + "#1f77b4", + "#ff7f0e", + "#2ca02c", + "#d62728", + "#9467bd", + "#8c564b", + "#e377c2", + "#7f7f7f", + ], + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 10, + fontFill: "#000000", + }, + onDrag: noop, + onDragComplete: noop, + hoverText: { + ...HoverTextNearMouse.defaultProps, + enable: true, + bgHeight: 18, + bgWidth: 120, + text: "Click to select object", + }, + }; + + // @ts-ignore + private isHover; + private dragStart; + private saveNodeType; + + constructor(props) { + super(props); + + this.isHover = isHover.bind(this); + this.saveNodeType = saveNodeType.bind(this); + + this.state = { + hover: false, + }; + } + + public render() { + const { startXY, endXY } = this.props; + const { interactive, appearance } = this.props; + const { + edgeFill, + stroke, strokeWidth, strokeOpacity, + fill, fillOpacity, + } = appearance; + const { fontFamily, fontSize, fontFill } = appearance; + const { hoverText, selected } = this.props; + const { onDragComplete } = this.props; + const { hover } = this.state; + const { enable: hoverTextEnabled, ...restHoverTextProps } = hoverText; + + const hoverHandler = interactive + ? { onHover: this.handleHover, onUnHover: this.handleHover } + : {}; + + const line1Edge = isDefined(startXY) && isDefined(endXY) + ? + {this.getEdgeCircle({ + xy: startXY, + dragHandler: this.handleLine1Edge1Drag, + cursor: "react-stockcharts-move-cursor", + fill: edgeFill, + edge: "edge1", + })} + {this.getEdgeCircle({ + xy: endXY, + dragHandler: this.handleLine1Edge2Drag, + cursor: "react-stockcharts-move-cursor", + fill: edgeFill, + edge: "edge2", + })} + + : null; + + return ( + + + {line1Edge} + + + ); + } + + private readonly getEdgeCircle = ({ xy, dragHandler, cursor, fill, edge }) => { + const { hover } = this.state; + const { selected, appearance } = this.props; + const { edgeStroke, edgeStrokeWidth, r } = appearance; + const { onDragComplete } = this.props; + + return ; + } + + private readonly handleLine1Edge2Drag = (moreProps) => { + const { index, onDrag } = this.props; + const { + endXY, + } = this.dragStart; + + const { + startPos, mouseXY, xAccessor, + xScale, fullData, + chartConfig: { yScale }, + } = moreProps; + + const dx = startPos[0] - mouseXY[0]; + const dy = startPos[1] - mouseXY[1]; + + const x1 = xScale(endXY[0]); + const y1 = yScale(endXY[1]); + + const newX1Value = getXValue(xScale, xAccessor, [x1 - dx, y1 - dy], fullData); + const newY1Value = yScale.invert(y1 - dy); + + onDrag(index, { + startXY: this.dragStart.startXY, + endXY: [newX1Value, newY1Value], + dy: this.dragStart.dy, + }); + } + + private readonly handleLine1Edge1Drag = (moreProps) => { + const { index, onDrag } = this.props; + const { + startXY, + } = this.dragStart; + + const { + startPos, mouseXY, xAccessor, + xScale, fullData, + chartConfig: { yScale }, + } = moreProps; + + const dx = startPos[0] - mouseXY[0]; + const dy = startPos[1] - mouseXY[1]; + + const x1 = xScale(startXY[0]); + const y1 = yScale(startXY[1]); + + const newX1Value = getXValue(xScale, xAccessor, [x1 - dx, y1 - dy], fullData); + const newY1Value = yScale.invert(y1 - dy); + + onDrag(index, { + startXY: [newX1Value, newY1Value], + endXY: this.dragStart.endXY, + dy: this.dragStart.dy, + }); + } + + private readonly handleFanDrag = (moreProps) => { + const { index, onDrag } = this.props; + + const { + startXY, endXY, + } = this.dragStart; + + const { xScale, chartConfig: { yScale }, xAccessor, fullData } = moreProps; + const { startPos, mouseXY } = moreProps; + + const x1 = xScale(startXY[0]); + const y1 = yScale(startXY[1]); + const x2 = xScale(endXY[0]); + const y2 = yScale(endXY[1]); + + const dx = startPos[0] - mouseXY[0]; + const dy = startPos[1] - mouseXY[1]; + + const newX1Value = getXValue(xScale, xAccessor, [x1 - dx, y1 - dy], fullData); + const newY1Value = yScale.invert(y1 - dy); + const newX2Value = getXValue(xScale, xAccessor, [x2 - dx, y2 - dy], fullData); + const newY2Value = yScale.invert(y2 - dy); + + // const newDy = newY2Value - endXY[1] + this.dragStart.dy; + + onDrag(index, { + startXY: [newX1Value, newY1Value], + endXY: [newX2Value, newY2Value], + dy: this.dragStart.dy, + }); + } + + private readonly handleDragStart = () => { + const { + startXY, endXY, dy, + } = this.props; + + this.dragStart = { + startXY, endXY, dy, + }; + } + + private readonly handleHover = (moreProps) => { + if (this.state.hover !== moreProps.hovering) { + this.setState({ + hover: moreProps.hovering, + }); + } + } +} diff --git a/packages/react-financial-charts/src/interactive/wrapper/EachInteractiveYCoordinate.tsx b/packages/react-financial-charts/src/interactive/wrapper/EachInteractiveYCoordinate.tsx new file mode 100644 index 000000000..f4ab1800d --- /dev/null +++ b/packages/react-financial-charts/src/interactive/wrapper/EachInteractiveYCoordinate.tsx @@ -0,0 +1,203 @@ +import * as React from "react"; + +import { noop } from "../../utils"; +import { isHover, saveNodeType } from "../utils"; + +import { ClickableShape } from "../components/ClickableShape"; +import { InteractiveYCoordinate } from "../components/InteractiveYCoordinate"; + +interface EachInteractiveYCoordinateProps { + index?: number; + draggable: boolean; + yValue: number; + bgFill: string; + bgOpacity: number; + stroke: string; + strokeWidth: number; + strokeOpacity: number; + strokeDasharray: string; + textFill: string; + fontWeight: string; + fontFamily: string; + fontStyle: string; + fontSize: number; + text: string; + selected: boolean; + edge: object; + textBox: { + closeIcon: any; + left: number; + padding: any; + }; + onDrag: any; // func + onDragComplete: any; // func + onDelete: any; // func +} + +interface EachInteractiveYCoordinateState { + closeIconHover: boolean; + hover: boolean; +} + +export class EachInteractiveYCoordinate extends React.Component { + + public static defaultProps = { + onDrag: noop, + onDragComplete: noop, + strokeWidth: 1, + opacity: 1, + selected: false, + fill: "#FFFFFF", + draggable: false, + }; + + private dragStartPosition; + // @ts-ignore + private isHover; + private saveNodeType; + + public constructor(props) { + super(props); + + this.isHover = isHover.bind(this); + this.saveNodeType = saveNodeType.bind(this); + + this.state = { + hover: false, + closeIconHover: false, + }; + } + + public render() { + const { + yValue, + bgFill, + bgOpacity, + textFill, + fontFamily, + fontSize, + fontWeight, + fontStyle, + text, + // hoverText, + selected, + onDragComplete, + stroke, + strokeOpacity, + strokeDasharray, + strokeWidth, + edge, + textBox, + draggable, + } = this.props; + + const { hover, closeIconHover } = this.state; + + const hoverHandler = { + onHover: this.handleHover, + onUnHover: this.handleHover, + }; + + const dragProps = draggable + ? { + onDragStart: this.handleDragStart, + onDrag: this.handleDrag, + onDragComplete, + } + : {}; + return ( + + + + + ); + } + + private readonly handleCloseIconHover = (moreProps) => { + if (this.state.closeIconHover !== moreProps.hovering) { + this.setState({ + closeIconHover: moreProps.hovering, + }); + } + } + + private readonly handleHover = (moreProps) => { + if (this.state.hover !== moreProps.hovering) { + this.setState({ + hover: moreProps.hovering, + closeIconHover: moreProps.hovering ? this.state.closeIconHover : false, + }); + } + } + + private readonly handleDelete = (moreProps) => { + const { index, onDelete } = this.props; + onDelete(index, moreProps); + } + + private readonly handleDrag = (moreProps) => { + const { index, onDrag } = this.props; + const { + mouseXY: [, mouseY], + chartConfig: { yScale }, + } = moreProps; + + const { dy } = this.dragStartPosition; + + const newYValue = yScale.invert(mouseY - dy); + + onDrag(index, newYValue); + } + + private readonly handleDragStart = (moreProps) => { + const { + yValue, + } = this.props; + const { mouseXY } = moreProps; + const { chartConfig: { yScale } } = moreProps; + const [, mouseY] = mouseXY; + + const dy = mouseY - yScale(yValue); + + this.dragStartPosition = { + yValue, dy, + }; + } +} diff --git a/packages/react-financial-charts/src/interactive/wrapper/EachLinearRegressionChannel.tsx b/packages/react-financial-charts/src/interactive/wrapper/EachLinearRegressionChannel.tsx new file mode 100644 index 000000000..8b815081c --- /dev/null +++ b/packages/react-financial-charts/src/interactive/wrapper/EachLinearRegressionChannel.tsx @@ -0,0 +1,221 @@ +import * as React from "react"; + +import { noop } from "../../utils"; +import { getCurrentItem } from "../../utils/ChartDataUtil"; +import { isHover, saveNodeType } from "../utils"; + +import { HoverTextNearMouse } from "../components/HoverTextNearMouse"; +import { + edge1Provider, + edge2Provider, + LinearRegressionChannelWithArea, +} from "../components/LinearRegressionChannelWithArea"; + +import { ClickableCircle } from "../components/ClickableCircle"; + +interface EachLinearRegressionChannelProps { + defaultClassName?: string; + x1Value: any; + x2Value: any; + index?: number; + appearance: { + stroke: string; + fillOpacity: number; + strokeOpacity: number; + strokeWidth: number; + fill: string; + edgeStrokeWidth: number; + edgeStroke: string; + edgeFill: string; + r: number; + }; + edgeInteractiveCursor?: string; + onDrag: any; // func + onDragComplete: any; // func + snapTo?: any; // func + interactive: boolean; + selected: boolean; + hoverText: { + enable: boolean; + fontFamily: string; + fontSize: number; + fill: string; + text: string; + bgFill: string; + bgOpacity: number; + bgWidth: number | string; + bgHeight: number | string; + selectedText: string; + }; +} + +interface EachLinearRegressionChannelState { + hover: boolean; +} + +export class EachLinearRegressionChannel extends React.Component { + + public static defaultProps = { + onDrag: noop, + onDragComplete: noop, + appearance: { + stroke: "#000000", + fillOpacity: 0.7, + strokeOpacity: 1, + strokeWidth: 1, + fill: "#8AAFE2", + edgeStrokeWidth: 2, + edgeStroke: "#000000", + edgeFill: "#FFFFFF", + r: 5, + }, + interactive: true, + selected: false, + hoverText: { + ...HoverTextNearMouse.defaultProps, + enable: true, + bgHeight: 18, + bgWidth: 175, + text: "Click and drag the edge circles", + }, + }; + + // @ts-ignore + private isHover; + private saveNodeType; + + constructor(props) { + super(props); + + this.isHover = isHover.bind(this); + this.saveNodeType = saveNodeType.bind(this); + + this.state = { + hover: false, + }; + } + + public render() { + const { + x1Value, + x2Value, + appearance, + edgeInteractiveCursor, + hoverText, + interactive, + selected, + onDragComplete, + } = this.props; + const { + stroke, + strokeWidth, + strokeOpacity, + fill, + fillOpacity, + r, + edgeStrokeWidth, + edgeFill, + edgeStroke, + } = appearance; + const { hover } = this.state; + + const hoverHandler = interactive + ? { onHover: this.handleHover, onUnHover: this.handleHover } + : {}; + + const { + enable: hoverTextEnabled, + selectedText: hoverTextSelected, + text: hoverTextUnselected, + ...restHoverTextProps + } = hoverText; + + return + + + + + ; + } + + private readonly handleHover = (moreProps) => { + if (this.state.hover !== moreProps.hovering) { + this.setState({ + hover: moreProps.hovering, + }); + } + } + + private readonly handleEdge2Drag = (moreProps) => { + const { index, onDrag, snapTo } = this.props; + const { + x1Value, + } = this.props; + + const [x2Value] = getNewXY(moreProps, snapTo); + + onDrag(index, { + x1Value, + x2Value, + }); + } + + private readonly handleEdge1Drag = (moreProps) => { + const { index, onDrag, snapTo } = this.props; + const { + x2Value, + } = this.props; + + const [x1Value] = getNewXY(moreProps, snapTo); + + onDrag(index, { + x1Value, + x2Value, + }); + } +} + +export function getNewXY(moreProps, snapTo) { + const { xScale, xAccessor, plotData, mouseXY } = moreProps; + + const currentItem = getCurrentItem(xScale, xAccessor, mouseXY, plotData); + const x = xAccessor(currentItem); + const y = snapTo(currentItem); + + return [x, y]; +} diff --git a/packages/react-financial-charts/src/interactive/wrapper/EachText.tsx b/packages/react-financial-charts/src/interactive/wrapper/EachText.tsx new file mode 100644 index 000000000..376b106b1 --- /dev/null +++ b/packages/react-financial-charts/src/interactive/wrapper/EachText.tsx @@ -0,0 +1,193 @@ +import * as React from "react"; + +import { noop } from "../../utils"; +import { getXValue } from "../../utils/ChartDataUtil"; +import { isHover, saveNodeType } from "../utils"; + +import { HoverTextNearMouse } from "../components/HoverTextNearMouse"; +import { InteractiveText } from "../components/InteractiveText"; + +interface EachTextProps { + index?: number; + position?: any; + bgFill: string; + bgOpacity: number; + bgStrokeWidth: number; + bgStroke?: string; + textFill: string; + fontWeight: string; + fontFamily: string; + fontStyle: string; + fontSize: number; + text: string; + selected: boolean; + onDrag: any; // func + onDragComplete: any; // func + hoverText: { + enable: boolean; + fontFamily: string; + fontSize: number; + fill: string; + text: string; + selectedText: string; + bgFill: string; + bgOpacity: number; + bgWidth: number | string; + bgHeight: number | string; + }; +} + +interface EachTextState { + hover: boolean; +} + +export class EachText extends React.Component { + + public static defaultProps = { + onDrag: noop, + onDragComplete: noop, + bgOpacity: 1, + bgStrokeWidth: 1, + selected: false, + fill: "#8AAFE2", + hoverText: { + ...HoverTextNearMouse.defaultProps, + enable: true, + bgHeight: "auto", + bgWidth: "auto", + text: "Click to select object", + }, + }; + + private dragStartPosition; + // @ts-ignore + private isHover; + private saveNodeType; + + constructor(props) { + super(props); + + this.handleHover = this.handleHover.bind(this); + + this.handleDragStart = this.handleDragStart.bind(this); + this.handleDrag = this.handleDrag.bind(this); + + this.isHover = isHover.bind(this); + this.saveNodeType = saveNodeType.bind(this); + + this.state = { + hover: false, + }; + } + + public render() { + const { + position, + bgFill, + bgOpacity, + bgStroke, + bgStrokeWidth, + textFill, + fontFamily, + fontSize, + fontWeight, + fontStyle, + text, + hoverText, + selected, + onDragComplete, + } = this.props; + const { hover } = this.state; + + const hoverHandler = { + onHover: this.handleHover, + onUnHover: this.handleHover, + }; + + const { + enable: hoverTextEnabled, + selectedText: hoverTextSelected, + text: hoverTextUnselected, + ...restHoverTextProps + } = hoverText; + + return ( + + + + + ); + } + + private readonly handleHover = (moreProps) => { + if (this.state.hover !== moreProps.hovering) { + this.setState({ + hover: moreProps.hovering, + }); + } + } + + private readonly handleDrag = (moreProps) => { + const { index, onDrag } = this.props; + const { + mouseXY: [, mouseY], + chartConfig: { yScale }, + xAccessor, + mouseXY, + plotData, + xScale, + } = moreProps; + + const { dx, dy } = this.dragStartPosition; + const xValue = xScale.invert( + xScale(getXValue(xScale, xAccessor, mouseXY, plotData)) - dx, + ); + // xScale.invert(xScale(xAccessor(currentItem)) - dx); + const xyValue = [ + xValue, + yScale.invert(mouseY - dy), + ]; + + onDrag(index, xyValue); + } + + private readonly handleDragStart = (moreProps) => { + const { + position, + } = this.props; + const { mouseXY } = moreProps; + const { chartConfig: { yScale }, xScale } = moreProps; + const [mouseX, mouseY] = mouseXY; + + const [textCX, textCY] = position; + const dx = mouseX - xScale(textCX); + const dy = mouseY - yScale(textCY); + + this.dragStartPosition = { + position, dx, dy, + }; + } +} diff --git a/packages/react-financial-charts/src/interactive/wrapper/EachTrendLine.tsx b/packages/react-financial-charts/src/interactive/wrapper/EachTrendLine.tsx new file mode 100644 index 000000000..1061ffd68 --- /dev/null +++ b/packages/react-financial-charts/src/interactive/wrapper/EachTrendLine.tsx @@ -0,0 +1,298 @@ +import * as React from "react"; + +import { ascending as d3Ascending } from "d3-array"; +import { noop, strokeDashTypes } from "../../utils"; +import { getXValue } from "../../utils/ChartDataUtil"; +import { isHover, saveNodeType } from "../utils"; + +import { ClickableCircle } from "../components/ClickableCircle"; +import { HoverTextNearMouse } from "../components/HoverTextNearMouse"; +import StraightLine from "../components/StraightLine"; + +interface EachTrendLineProps { + x1Value: any; + x2Value: any; + y1Value: any; + y2Value: any; + index?: number; + type: "XLINE" | // extends from -Infinity to +Infinity + "RAY" | // extends to +/-Infinity in one direction + "LINE"; // extends between the set bounds + onDrag: any; // func + onEdge1Drag: any; // func + onEdge2Drag: any; // func + onDragComplete: any; // func + onSelect: any; // func + onUnSelect: any; // func + r: number; + strokeOpacity: number; + defaultClassName?: string; + selected?: boolean; + stroke: string; + strokeWidth: number; + strokeDasharray: strokeDashTypes; + edgeStrokeWidth: number; + edgeStroke: string; + edgeInteractiveCursor: string; + lineInteractiveCursor: string; + edgeFill: string; + hoverText: { + enable: boolean; + fontFamily: string; + fontSize: number; + fill: string; + text: string; + selectedText: string; + bgFill: string; + bgOpacity: number; + bgWidth: number | string; + bgHeight: number | string; + }; +} + +interface EachTrendLineState { + anchor?: string; + hover?: any; +} + +export class EachTrendLine extends React.Component { + + public static defaultProps = { + onDrag: noop, + onEdge1Drag: noop, + onEdge2Drag: noop, + onDragComplete: noop, + onSelect: noop, + onUnSelect: noop, + selected: false, + edgeStroke: "#000000", + edgeFill: "#FFFFFF", + edgeStrokeWidth: 2, + r: 5, + strokeWidth: 1, + strokeOpacity: 1, + strokeDasharray: "Solid", + hoverText: { + enable: false, + }, + }; + + private dragStart; + // @ts-ignore + private isHover; + private saveNodeType; + + constructor(props) { + super(props); + + this.isHover = isHover.bind(this); + this.saveNodeType = saveNodeType.bind(this); + + this.state = { + hover: false, + }; + } + + public render() { + const { + x1Value, + y1Value, + x2Value, + y2Value, + type, + stroke, + strokeWidth, + strokeOpacity, + strokeDasharray, + r, + edgeStrokeWidth, + edgeFill, + edgeStroke, + edgeInteractiveCursor, + lineInteractiveCursor, + hoverText, + selected, + + onDragComplete, + } = this.props; + + const { + enable: hoverTextEnabled, + selectedText: hoverTextSelected, + text: hoverTextUnselected, + ...restHoverTextProps + } = hoverText; + + const { hover, anchor } = this.state; + + return + + + + + ; + } + + private readonly handleHover = (moreProps) => { + if (this.state.hover !== moreProps.hovering) { + this.setState({ + hover: moreProps.hovering, + }); + } + } + + private readonly handleEdge2Drag = (moreProps) => { + const { index, onDrag } = this.props; + const { + x1Value, y1Value, + } = this.props; + + const [x2Value, y2Value] = getNewXY(moreProps); + + onDrag(index, { + x1Value, + y1Value, + x2Value, + y2Value, + }); + } + + private readonly handleEdge1Drag = (moreProps) => { + const { index, onDrag } = this.props; + const { + x2Value, y2Value, + } = this.props; + + const [x1Value, y1Value] = getNewXY(moreProps); + + onDrag(index, { + x1Value, + y1Value, + x2Value, + y2Value, + }); + } + + private readonly handleDragComplete = (...rest) => { + this.setState({ + anchor: undefined, + }); + this.props.onDragComplete(...rest); + } + + private readonly handleEdge2DragStart = () => { + this.setState({ + anchor: "edge1", + }); + } + + private readonly handleEdge1DragStart = () => { + this.setState({ + anchor: "edge2", + }); + } + + private readonly handleLineDrag = (moreProps) => { + const { index, onDrag } = this.props; + + const { + x1Value, y1Value, + x2Value, y2Value, + } = this.dragStart; + + const { xScale, chartConfig: { yScale }, xAccessor, fullData } = moreProps; + const { startPos, mouseXY } = moreProps; + + const x1 = xScale(x1Value); + const y1 = yScale(y1Value); + const x2 = xScale(x2Value); + const y2 = yScale(y2Value); + + const dx = startPos[0] - mouseXY[0]; + const dy = startPos[1] - mouseXY[1]; + + const newX1Value = getXValue(xScale, xAccessor, [x1 - dx, y1 - dy], fullData); + const newY1Value = yScale.invert(y1 - dy); + const newX2Value = getXValue(xScale, xAccessor, [x2 - dx, y2 - dy], fullData); + const newY2Value = yScale.invert(y2 - dy); + + onDrag(index, { + x1Value: newX1Value, + y1Value: newY1Value, + x2Value: newX2Value, + y2Value: newY2Value, + }); + } + + private readonly handleLineDragStart = () => { + const { + x1Value, y1Value, + x2Value, y2Value, + } = this.props; + + this.dragStart = { + x1Value, y1Value, + x2Value, y2Value, + }; + } +} + +export function getNewXY(moreProps) { + const { xScale, chartConfig: { yScale }, xAccessor, plotData, mouseXY } = moreProps; + const mouseY = mouseXY[1]; + + const x = getXValue(xScale, xAccessor, mouseXY, plotData); + + const [small, big] = yScale.domain().slice().sort(d3Ascending); + const y = yScale.invert(mouseY); + const newY = Math.min(Math.max(y, small), big); + + return [x, newY]; +} diff --git a/packages/react-financial-charts/src/scale/discontinuousTimeScaleProvider.ts b/packages/react-financial-charts/src/scale/discontinuousTimeScaleProvider.ts new file mode 100644 index 000000000..a7f6d09d2 --- /dev/null +++ b/packages/react-financial-charts/src/scale/discontinuousTimeScaleProvider.ts @@ -0,0 +1,225 @@ +import { timeFormat, timeFormatDefaultLocale } from "d3-time-format"; + +import { identity, isNotDefined, slidingWindow, zipper } from "../utils"; +import financeDiscontinuousScale from "./financeDiscontinuousScale"; +import { defaultFormatters, levelDefinition } from "./levels"; + +function evaluateLevel(d, date, i, formatters) { + return levelDefinition + .map((eachLevel, idx) => { + return { + level: levelDefinition.length - idx - 1, + + // @ts-ignore + format: formatters[eachLevel(d, date, i)], + }; + }) + .find((l) => !!l.format); +} + +const discontinuousIndexCalculator = slidingWindow() + .windowSize(2) + // @ts-ignore + .undefinedValue((d, idx, { initialIndex, formatters }) => { + const i = initialIndex; + const row = { + date: d.getTime(), + startOf30Seconds: false, + startOfMinute: false, + startOf5Minutes: false, + startOf15Minutes: false, + startOf30Minutes: false, + startOfHour: false, + startOfEighthOfADay: false, + startOfQuarterDay: false, + startOfHalfDay: false, + startOfDay: true, + startOfWeek: false, + startOfMonth: false, + startOfQuarter: false, + startOfYear: false, + }; + const level = evaluateLevel(row, d, i, formatters); + return { ...row, index: i, ...level }; + }); + +const discontinuousIndexCalculatorLocalTime = discontinuousIndexCalculator + .accumulator(([prevDate, nowDate], i, idx, { initialIndex, formatters }) => { + const startOf30Seconds = nowDate.getSeconds() % 30 === 0; + + const startOfMinute = nowDate.getMinutes() !== prevDate.getMinutes(); + const startOf5Minutes = startOfMinute && nowDate.getMinutes() % 5 <= prevDate.getMinutes() % 5; + const startOf15Minutes = startOfMinute && nowDate.getMinutes() % 15 <= prevDate.getMinutes() % 15; + const startOf30Minutes = startOfMinute && nowDate.getMinutes() % 30 <= prevDate.getMinutes() % 30; + + const startOfHour = nowDate.getHours() !== prevDate.getHours(); + + const startOfEighthOfADay = startOfHour && nowDate.getHours() % 3 === 0; + const startOfQuarterDay = startOfHour && nowDate.getHours() % 6 === 0; + const startOfHalfDay = startOfHour && nowDate.getHours() % 12 === 0; + + const startOfDay = nowDate.getDay() !== prevDate.getDay(); + // According to ISO calendar + // Sunday = 0, Monday = 1, ... Saturday = 6 + // day of week of today < day of week of yesterday then today is start of week + const startOfWeek = nowDate.getDay() < prevDate.getDay(); + // month of today != month of yesterday then today is start of month + const startOfMonth = nowDate.getMonth() !== prevDate.getMonth(); + // if start of month and month % 3 === 0 then it is start of quarter + const startOfQuarter = startOfMonth && (nowDate.getMonth() % 3 <= prevDate.getMonth() % 3); + // year of today != year of yesterday then today is start of year + const startOfYear = nowDate.getFullYear() !== prevDate.getFullYear(); + + const row = { + date: nowDate.getTime(), + startOf30Seconds, + startOfMinute, + startOf5Minutes, + startOf15Minutes, + startOf30Minutes, + startOfHour, + startOfEighthOfADay, + startOfQuarterDay, + startOfHalfDay, + startOfDay, + startOfWeek, + startOfMonth, + startOfQuarter, + startOfYear, + }; + const level = evaluateLevel(row, nowDate, i, formatters); + + return { ...row, index: i + initialIndex, ...level }; + }); + +function doStuff(realDateAccessor, inputDateAccessor, initialIndex, formatters) { + return function (data) { + const dateAccessor = realDateAccessor(inputDateAccessor); + const calculate = discontinuousIndexCalculatorLocalTime + .source(dateAccessor) + .misc({ initialIndex, formatters }); + + const index = calculate(data).map((each) => { + const { format } = each; + return { + // ...each, + index: each.index, + level: each.level, + date: new Date(each.date), + format: timeFormat(format), + }; + }); + + return { index }; + }; +} + +export function discontinuousTimeScaleProviderBuilder() { + let initialIndex = 0; + let realDateAccessor = identity; + let inputDateAccessor = (d) => d.date; + let indexAccessor = (d) => d.idx; + let indexMutator = (d, idx) => ({ ...d, idx }); + let withIndex; + + let currentFormatters = defaultFormatters; + + const discontinuousTimeScaleProvider = function (data) { + + let index = withIndex; + + if (isNotDefined(index)) { + const response = doStuff( + realDateAccessor, + inputDateAccessor, + initialIndex, + currentFormatters, + )(data); + + index = response.index; + } + + const inputIndex = index; + + // @ts-ignore + const xScale = financeDiscontinuousScale( + inputIndex, + ); + + const mergedData = zipper() + .combine(indexMutator); + + // @ts-ignore + const finalData = mergedData(data, inputIndex); + + return { + data: finalData, + xScale, + xAccessor: (d) => d && indexAccessor(d).index, + displayXAccessor: realDateAccessor(inputDateAccessor), + }; + }; + + discontinuousTimeScaleProvider.initialIndex = function (x) { + if (!arguments.length) { + return initialIndex; + } + initialIndex = x; + return discontinuousTimeScaleProvider; + }; + discontinuousTimeScaleProvider.inputDateAccessor = function (x) { + if (!arguments.length) { + return inputDateAccessor; + } + inputDateAccessor = x; + return discontinuousTimeScaleProvider; + }; + discontinuousTimeScaleProvider.indexAccessor = function (x) { + if (!arguments.length) { + return indexAccessor; + } + indexAccessor = x; + return discontinuousTimeScaleProvider; + }; + discontinuousTimeScaleProvider.indexMutator = function (x) { + if (!arguments.length) { + return indexMutator; + } + indexMutator = x; + return discontinuousTimeScaleProvider; + }; + discontinuousTimeScaleProvider.withIndex = function (x) { + if (!arguments.length) { + return withIndex; + } + withIndex = x; + return discontinuousTimeScaleProvider; + }; + discontinuousTimeScaleProvider.utc = function () { + realDateAccessor = (dateAccessor) => (d) => { + const date = dateAccessor(d); + // The getTimezoneOffset() method returns the time-zone offset from UTC, in minutes, for the current locale. + const offsetInMillis = date.getTimezoneOffset() * 60 * 1000; + return new Date(date.getTime() + offsetInMillis); + }; + return discontinuousTimeScaleProvider; + }; + discontinuousTimeScaleProvider.setLocale = function (locale, formatters = null) { + if (locale) { + timeFormatDefaultLocale(locale); + } + if (formatters) { + // @ts-ignore + currentFormatters = formatters; + } + return discontinuousTimeScaleProvider; + }; + + discontinuousTimeScaleProvider.indexCalculator = function () { + return doStuff(realDateAccessor, inputDateAccessor, initialIndex, currentFormatters); + }; + + return discontinuousTimeScaleProvider; +} + +export default discontinuousTimeScaleProviderBuilder(); diff --git a/packages/react-financial-charts/src/scale/evaluator.ts b/packages/react-financial-charts/src/scale/evaluator.ts new file mode 100644 index 000000000..d8f1dfadb --- /dev/null +++ b/packages/react-financial-charts/src/scale/evaluator.ts @@ -0,0 +1,171 @@ +import { + getClosestItemIndexes, + getLogger, + head, + isDefined, + isNotDefined, + last, +} from "../utils"; + +const log = getLogger("evaluator"); + +function getNewEnd(fallbackEnd, xAccessor, initialXScale, start) { + const { + lastItem, lastItemX, + } = fallbackEnd; + const lastItemXValue = xAccessor(lastItem); + const [rangeStart, rangeEnd] = initialXScale.range(); + + const newEnd = (rangeEnd - rangeStart) / (lastItemX - rangeStart) * (lastItemXValue - start) + start; + return newEnd; +} + +function extentsWrapper(useWholeData, clamp, pointsPerPxThreshold, minPointsPerPxThreshold, flipXScale) { + function filterData( + data, inputDomain, xAccessor, initialXScale, + // @ts-ignore + { currentPlotData, currentDomain, fallbackStart, fallbackEnd } = {}, + ) { + if (useWholeData) { + return { plotData: data, domain: inputDomain }; + } + + let left = head(inputDomain); + let right = last(inputDomain); + let clampedDomain = inputDomain; + + let filteredData = getFilteredResponse(data, left, right, xAccessor); + + if (filteredData.length === 1 && isDefined(fallbackStart)) { + left = fallbackStart; + right = getNewEnd(fallbackEnd, xAccessor, initialXScale, left); + + clampedDomain = [ + left, + right, + ]; + filteredData = getFilteredResponse(data, left, right, xAccessor); + } + + if (typeof clamp === "function") { + clampedDomain = clamp(clampedDomain, [xAccessor(head(data)), xAccessor(last(data))]); + } else { + if (clamp === "left" || clamp === "both" || clamp === true) { + clampedDomain = [ + Math.max(left, xAccessor(head(data))), + clampedDomain[1], + ]; + } + + if (clamp === "right" || clamp === "both" || clamp === true) { + clampedDomain = [ + clampedDomain[0], + Math.min(right, xAccessor(last(data))), + ]; + } + } + + if (clampedDomain !== inputDomain) { + filteredData = getFilteredResponse(data, clampedDomain[0], clampedDomain[1], xAccessor); + } + + const realInputDomain = clampedDomain; + // [xAccessor(head(filteredData)), xAccessor(last(filteredData))]; + + const xScale = initialXScale.copy().domain(realInputDomain); + + let width = Math.floor(xScale(xAccessor(last(filteredData))) + - xScale(xAccessor(head(filteredData)))); + + // prevent negative width when flipXScale + if (flipXScale && width < 0) { + width = width * -1; + } + + let plotData; + let domain; + + const chartWidth = last(xScale.range()) - head(xScale.range()); + + // @ts-ignore + log(`Trying to show ${filteredData.length} points in ${width}px,` + + ` I can show up to ${showMaxThreshold(width, pointsPerPxThreshold) - 1} points in that width. ` + + `Also FYI the entire chart width is ${chartWidth}px and pointsPerPxThreshold is ${pointsPerPxThreshold}`); + + if (canShowTheseManyPeriods(width, filteredData.length, pointsPerPxThreshold, minPointsPerPxThreshold)) { + plotData = filteredData; + domain = realInputDomain; + + // @ts-ignore + log("AND IT WORKED"); + } else { + if (chartWidth > showMaxThreshold(width, pointsPerPxThreshold) && isDefined(fallbackEnd)) { + plotData = filteredData; + const newEnd = getNewEnd(fallbackEnd, xAccessor, initialXScale, head(realInputDomain)); + domain = [ + head(realInputDomain), + newEnd, + ]; + + const newXScale = xScale.copy().domain(domain); + const newWidth = Math.floor(newXScale(xAccessor(last(plotData))) + - newXScale(xAccessor(head(plotData)))); + + // @ts-ignore + log(`and ouch, that is too much, so instead showing ${plotData.length} in ${newWidth}px`); + } else { + plotData = currentPlotData || filteredData.slice(filteredData.length - showMax(width, pointsPerPxThreshold)); + domain = currentDomain || [xAccessor(head(plotData)), xAccessor(last(plotData))]; + + const newXScale = xScale.copy().domain(domain); + const newWidth = Math.floor(newXScale(xAccessor(last(plotData))) + - newXScale(xAccessor(head(plotData)))); + + // @ts-ignore + log(`and ouch, that is too much, so instead showing ${plotData.length} in ${newWidth}px`); + } + } + return { plotData, domain }; + } + return { filterData }; +} + +function canShowTheseManyPeriods(width, arrayLength, maxThreshold, minThreshold) { + return arrayLength > showMinThreshold(width, minThreshold) && arrayLength < showMaxThreshold(width, maxThreshold); +} + +function showMinThreshold(width, threshold) { + return Math.max(1, Math.ceil(width * threshold)); +} + +function showMaxThreshold(width, threshold) { + return Math.floor(width * threshold); +} + +function showMax(width, threshold) { + return Math.floor(showMaxThreshold(width, threshold) * 0.97); +} + +function getFilteredResponse(data, left, right, xAccessor) { + const newLeftIndex = getClosestItemIndexes(data, left, xAccessor).right; + const newRightIndex = getClosestItemIndexes(data, right, xAccessor).left; + + const filteredData = data.slice(newLeftIndex, newRightIndex + 1); + // console.log(right, newRightIndex, dataForInterval.length); + + return filteredData; +} + +export default function ({ + xScale, useWholeData, clamp, + pointsPerPxThreshold, minPointsPerPxThreshold, + flipXScale, +}) { + return extentsWrapper( + useWholeData || isNotDefined(xScale.invert), + clamp, + pointsPerPxThreshold, + minPointsPerPxThreshold, + flipXScale, + ); +} diff --git a/packages/react-financial-charts/src/scale/financeDiscontinuousScale.ts b/packages/react-financial-charts/src/scale/financeDiscontinuousScale.ts new file mode 100644 index 000000000..7aad1219e --- /dev/null +++ b/packages/react-financial-charts/src/scale/financeDiscontinuousScale.ts @@ -0,0 +1,148 @@ +import { ascending } from "d3-array"; +import { map, set } from "d3-collection"; +import { scaleLinear } from "d3-scale"; + +import { head, isDefined, isNotDefined, last } from "../utils"; +import { levelDefinition } from "./levels"; + +const MAX_LEVEL = levelDefinition.length - 1; + +export default function financeDiscontinuousScale( + index, + futureProvider, + backingLinearScale = scaleLinear(), +) { + + if (isNotDefined(index)) { + throw new Error("Use the discontinuousTimeScaleProvider to create financeDiscontinuousScale"); + } + + function scale(x) { + return backingLinearScale(x); + } + scale.invert = function (x) { + const inverted = backingLinearScale.invert(x); + return Math.round(inverted * 10000) / 10000; + }; + scale.domain = function (x) { + if (!arguments.length) { return backingLinearScale.domain(); } + backingLinearScale.domain(x); + return scale; + }; + scale.range = function (x) { + if (!arguments.length) { return backingLinearScale.range(); } + backingLinearScale.range(x); + return scale; + }; + scale.rangeRound = function (x) { + return backingLinearScale.range(x); + }; + scale.clamp = function (x) { + if (!arguments.length) { return backingLinearScale.clamp(); } + backingLinearScale.clamp(x); + return scale; + }; + scale.interpolate = function (x) { + if (!arguments.length) { return backingLinearScale.interpolate(); } + backingLinearScale.interpolate(x); + return scale; + }; + scale.ticks = function (m, flexTicks) { + const backingTicks = backingLinearScale.ticks(m); + const ticksMap = map(); + + const [domainStart, domainEnd] = backingLinearScale.domain(); + + const start = Math.max(Math.ceil(domainStart), head(index).index) + Math.abs(head(index).index); + const end = Math.min(Math.floor(domainEnd), last(index).index) + Math.abs(head(index).index); + + if (Math.floor(domainEnd) > end) { + // console.log(end, domainEnd, index); + } + + const desiredTickCount = Math.ceil((end - start) / (domainEnd - domainStart) * backingTicks.length); + + for (let i = MAX_LEVEL; i >= 0; i--) { + const ticksAtLevel = ticksMap.get(i); + const temp = isNotDefined(ticksAtLevel) + ? [] + : ticksAtLevel.slice(); + + for (let j = start; j <= end; j++) { + if (index[j].level === i) { + temp.push(index[j]); + } + } + + ticksMap.set(i, temp); + } + + let unsortedTicks = []; + for (let i = MAX_LEVEL; i >= 0; i--) { + if ((ticksMap.get(i).length + unsortedTicks.length) > desiredTickCount * 1.5) { break; } + unsortedTicks = unsortedTicks.concat(ticksMap.get(i).map((d) => d.index)); + } + + const ticks = unsortedTicks.sort(ascending); + + // console.log(backingTicks.length, desiredTickCount, ticks, ticksMap); + + if (!flexTicks && end - start > ticks.length) { + const ticksSet = set(ticks); + + const d = Math.abs(head(index).index); + + // ignore ticks within this distance + const distance = Math.ceil( + (backingTicks.length > 0 + ? (last(backingTicks) - head(backingTicks)) / (backingTicks.length) / 4 + : 1) * 1.5); + + for (let i = 0; i < ticks.length - 1; i++) { + for (let j = i + 1; j < ticks.length; j++) { + if (ticks[j] - ticks[i] <= distance) { + ticksSet.remove(index[ticks[i] + d].level >= index[ticks[j] + d].level ? ticks[j] : ticks[i]); + } + } + } + + const tickValues = ticksSet + .values() + .map((i) => parseInt(i, 10)); + + // console.log(ticks.length, tickValues, level); + // console.log(ticks, tickValues, distance); + + return tickValues; + } + + return ticks; + }; + scale.tickFormat = function () { + return function (x) { + const d = Math.abs(head(index).index); + const { format, date } = index[Math.floor(x + d)]; + return format(date); + }; + }; + scale.value = function (x) { + const d = Math.abs(head(index).index); + if (isDefined(index[Math.floor(x + d)])) { + const { date } = index[Math.floor(x + d)]; + return date; + } + }; + scale.nice = function (m) { + backingLinearScale.nice(m); + return scale; + }; + scale.index = function (x) { + if (!arguments.length) { return index; } + index = x; + return scale; + }; + scale.copy = function () { + return financeDiscontinuousScale(index, futureProvider, backingLinearScale.copy()); + }; + return scale; +} diff --git a/packages/react-financial-charts/src/scale/index.ts b/packages/react-financial-charts/src/scale/index.ts new file mode 100644 index 000000000..010d12a42 --- /dev/null +++ b/packages/react-financial-charts/src/scale/index.ts @@ -0,0 +1,9 @@ +export { + default as discontinuousTimeScaleProvider, + discontinuousTimeScaleProviderBuilder, +} from "./discontinuousTimeScaleProvider"; +export { default as financeDiscontinuousScale } from "./financeDiscontinuousScale"; + +export function defaultScaleProvider(xScale) { + return (data, xAccessor) => ({ data, xScale, xAccessor, displayXAccessor: xAccessor }); +} diff --git a/packages/react-financial-charts/src/scale/levels.ts b/packages/react-financial-charts/src/scale/levels.ts new file mode 100644 index 000000000..d8a648abd --- /dev/null +++ b/packages/react-financial-charts/src/scale/levels.ts @@ -0,0 +1,36 @@ +export const defaultFormatters = { + yearFormat: "%Y", + quarterFormat: "%b %Y", + monthFormat: "%b", + weekFormat: "%d %b", + dayFormat: "%a %d", + hourFormat: "%_I %p", + minuteFormat: "%I:%M %p", + secondFormat: "%I:%M:%S %p", + milliSecondFormat: "%L", +}; + +export const levelDefinition = [ + /* eslint-disable no-unused-vars */ + /* 19 */(d, date, i) => d.startOfYear && date.getFullYear() % 12 === 0 && "yearFormat", + /* 18 */(d, date, i) => d.startOfYear && date.getFullYear() % 4 === 0 && "yearFormat", + /* 17 */(d, date, i) => d.startOfYear && date.getFullYear() % 2 === 0 && "yearFormat", + /* 16 */(d, date, i) => d.startOfYear && "yearFormat", + /* 15 */(d, date, i) => d.startOfQuarter && "quarterFormat", + /* 14 */(d, date, i) => d.startOfMonth && "monthFormat", + /* 13 */(d, date, i) => d.startOfWeek && "weekFormat", + /* 12 */(d, date, i) => d.startOfDay && i % 2 === 0 && "dayFormat", + /* 11 */(d, date, i) => d.startOfDay && "dayFormat", + /* 10 */(d, date, i) => d.startOfHalfDay && "hourFormat", // 12h + /* 9 */(d, date, i) => d.startOfQuarterDay && "hourFormat", // 6h + /* 8 */(d, date, i) => d.startOfEighthOfADay && "hourFormat", // 3h + /* 7 */(d, date, i) => d.startOfHour && date.getHours() % 2 === 0 && "hourFormat", // 2h -- REMOVE THIS + /* 6 */(d, date, i) => d.startOfHour && "hourFormat", // 1h + /* 5 */(d, date, i) => d.startOf30Minutes && "minuteFormat", + /* 4 */(d, date, i) => d.startOf15Minutes && "minuteFormat", + /* 3 */(d, date, i) => d.startOf5Minutes && "minuteFormat", + /* 2 */(d, date, i) => d.startOfMinute && "minuteFormat", + /* 1 */(d, date, i) => d.startOf30Seconds && "secondFormat", + /* 0 */(d, date, i) => "secondFormat", + /* eslint-enable no-unused-vars */ +]; diff --git a/packages/react-financial-charts/src/series/AlternatingFillAreaSeries.tsx b/packages/react-financial-charts/src/series/AlternatingFillAreaSeries.tsx new file mode 100644 index 000000000..7d851599f --- /dev/null +++ b/packages/react-financial-charts/src/series/AlternatingFillAreaSeries.tsx @@ -0,0 +1,199 @@ +import * as React from "react"; + +import { strokeDashTypes } from "../utils"; +import { AreaSeries } from "./AreaSeries"; +import { SVGComponent } from "./SVGComponent"; + +interface AlternatingFillAreaSeriesProps { + stroke: { + top: string; + bottom: string; + }; + strokeWidth: { + top: number; + bottom: number; + }; + strokeOpacity: { + top: number; + bottom: number; + }; + fill: { + top: string; + bottom: string; + }; + fillOpacity: { + top: number, + bottom: number, + }; + strokeDasharray: { + top: strokeDashTypes; + bottom: strokeDashTypes; + }; + className?: string; + yAccessor: any; // func; + baseAt: number; + interpolation?: any; // func +} + +export class AlternatingFillAreaSeries extends React.Component { + + public static defaultProps = { + stroke: { + top: "#38C172", + bottom: "#E3342F", + }, + strokeWidth: { + top: 2, + bottom: 2, + }, + strokeOpacity: { + top: 1, + bottom: 1, + }, + fill: { + top: "#A2F5BF", + bottom: "#EF5753", + }, + fillOpacity: { + top: 0.5, + bottom: 0.5, + }, + strokeDasharray: { + top: "Solid", + bottom: "Solid", + }, + className: "react-stockcharts-alternating-area", + }; + + private clipPathId1: string; + private clipPathId2: string; + + public constructor(props) { + super(props); + this.renderClip = this.renderClip.bind(this); + this.topClip = this.topClip.bind(this); + this.bottomClip = this.bottomClip.bind(this); + this.baseAt = this.baseAt.bind(this); + + const id1 = String(Math.round(Math.random() * 10000 * 10000)); + this.clipPathId1 = `alternating-area-clip-${id1}`; + + const id2 = String(Math.round(Math.random() * 10000 * 10000)); + this.clipPathId2 = `alternating-area-clip-${id2}`; + } + + public topClip(ctx, moreProps) { + const { chartConfig } = moreProps; + const { baseAt } = this.props; + const { yScale, width } = chartConfig; + + ctx.beginPath(); + ctx.rect( + 0, + 0, + width, + yScale(baseAt), + ); + ctx.clip(); + } + + public bottomClip(ctx, moreProps) { + const { chartConfig } = moreProps; + const { baseAt } = this.props; + const { yScale, width, height } = chartConfig; + + ctx.beginPath(); + ctx.rect( + 0, + yScale(baseAt), + width, + height - yScale(baseAt), + ); + ctx.clip(); + } + + public renderClip(moreProps) { + const { chartConfig } = moreProps; + const { baseAt } = this.props; + const { yScale, width, height } = chartConfig; + + return ( + + + + + + + + + ); + } + + public baseAt(yScale) { + return yScale(this.props.baseAt); + } + + public render() { + const { className, yAccessor, interpolation } = this.props; + const { + stroke, + strokeWidth, + strokeOpacity, + strokeDasharray, + + fill, + fillOpacity, + } = this.props; + + const style1 = { clipPath: `url(#${this.clipPathId1})` }; + const style2 = { clipPath: `url(#${this.clipPathId2})` }; + + return ( + + + {this.renderClip} + + + + + ); + } +} diff --git a/packages/react-financial-charts/src/series/AreaOnlySeries.tsx b/packages/react-financial-charts/src/series/AreaOnlySeries.tsx new file mode 100644 index 000000000..27adf8577 --- /dev/null +++ b/packages/react-financial-charts/src/series/AreaOnlySeries.tsx @@ -0,0 +1,112 @@ +import { area as d3Area } from "d3-shape"; +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getAxisCanvas } from "../GenericComponent"; + +import { first, functor, hexToRGBA, isDefined } from "../utils"; + +interface AreaOnlySeriesProps { + className?: string; + yAccessor: any; // func + stroke?: string; + fill?: string; + opacity?: number; + defined?: any; // func + base?: number | any; // func + interpolation?: any; // func + canvasClip?: any; // func + style?: React.CSSProperties; + canvasGradient?: any; // func +} + +export class AreaOnlySeries extends React.Component { + + public static defaultProps = { + className: "line", + fill: "none", + opacity: 1, + defined: (d) => !isNaN(d), + base: (yScale /* , d, moreProps */) => first(yScale.range()), + }; + + public render() { + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + const { yAccessor, defined, base, style } = this.props; + const { stroke, fill, className = "line", opacity, interpolation } = this.props; + + const { xScale, chartConfig: { yScale }, plotData, xAccessor } = moreProps; + + const newBase = functor(base); + const areaSeries = d3Area() + .defined((d) => defined(yAccessor(d))) + .x((d) => Math.round(xScale(xAccessor(d)))) + .y0((d) => newBase(yScale, d, moreProps)) + .y1((d) => Math.round(yScale(yAccessor(d)))); + + if (isDefined(interpolation)) { + areaSeries.curve(interpolation); + } + + const data = areaSeries(plotData); + const newClassName = className.concat(isDefined(stroke) ? "" : " line-stroke"); + return ( + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { yAccessor, defined, base, canvasGradient } = this.props; + const { fill, stroke, opacity, interpolation, canvasClip } = this.props; + + const { xScale, chartConfig: { yScale }, plotData, xAccessor } = moreProps; + + if (canvasClip) { + ctx.save(); + canvasClip(ctx, moreProps); + } + + if (canvasGradient != null) { + ctx.fillStyle = canvasGradient(moreProps, ctx); + } else { + ctx.fillStyle = hexToRGBA(fill, opacity); + } + ctx.strokeStyle = stroke; + + ctx.beginPath(); + const newBase = functor(base); + const areaSeries = d3Area() + .defined((d) => defined(yAccessor(d))) + .x((d) => Math.round(xScale(xAccessor(d)))) + .y0((d) => newBase(yScale, d, moreProps)) + .y1((d) => Math.round(yScale(yAccessor(d)))) + .context(ctx); + + if (isDefined(interpolation)) { + areaSeries.curve(interpolation); + } + areaSeries(plotData); + ctx.fill(); + + if (canvasClip) { + ctx.restore(); + } + } +} diff --git a/packages/react-financial-charts/src/series/AreaSeries.tsx b/packages/react-financial-charts/src/series/AreaSeries.tsx new file mode 100644 index 000000000..a53da80cc --- /dev/null +++ b/packages/react-financial-charts/src/series/AreaSeries.tsx @@ -0,0 +1,80 @@ +import * as React from "react"; + +import { strokeDashTypes } from "../utils"; +import { AreaOnlySeries } from "./AreaOnlySeries"; +import { LineSeries } from "./LineSeries"; + +interface AreaSeriesProps { + stroke?: string; + strokeWidth?: number; + canvasGradient?: any; // func + fill: string; + strokeOpacity: number; + opacity: number; + className?: string; + yAccessor: any; // func + baseAt?: any; // func + interpolation?: any; // func + canvasClip?: any; // func + style: React.CSSProperties; + strokeDasharray: strokeDashTypes; +} + +export class AreaSeries extends React.Component { + + public static defaultProps = { + stroke: "#4682B4", + strokeWidth: 1, + strokeOpacity: 1, + strokeDasharray: "Solid", + opacity: 0.5, + fill: "#4682B4", + className: "react-stockcharts-area", + }; + + public render() { + const { + baseAt, + className, + opacity, + stroke, + strokeWidth, + strokeOpacity, + strokeDasharray, + canvasGradient, + fill, + interpolation, + style, + canvasClip, + yAccessor, + } = this.props; + + return ( + + + + + ); + } +} diff --git a/packages/react-financial-charts/src/series/BarSeries.tsx b/packages/react-financial-charts/src/series/BarSeries.tsx new file mode 100644 index 000000000..c8a68075c --- /dev/null +++ b/packages/react-financial-charts/src/series/BarSeries.tsx @@ -0,0 +1,124 @@ +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getAxisCanvas } from "../GenericComponent"; + +import { + drawOnCanvas2, + drawOnCanvasHelper, + getBarsSVG2, + identityStack, + StackedBarSeries, + svgHelper, +} from "./StackedBarSeries"; + +import { functor, isDefined } from "../utils"; + +interface BarSeriesProps { + baseAt?: number | any; // func + stroke?: boolean; + width?: number | any; // func + yAccessor: any; // func + opacity?: number; + fill?: number | any; // func + className?: number | any; // func + clip?: boolean; + swapScales?: boolean; +} + +export class BarSeries extends React.Component { + + public static defaultProps = StackedBarSeries.defaultProps; + + public render() { + const { clip } = this.props; + + return ( + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + if (this.props.swapScales) { + const { xAccessor } = moreProps; + drawOnCanvasHelper(ctx, this.props, moreProps, xAccessor, identityStack); + } else { + const bars = getBars(this.props, moreProps); + drawOnCanvas2(this.props, ctx, bars); + } + + } + + private readonly renderSVG = (moreProps) => { + if (this.props.swapScales) { + const { xAccessor } = moreProps; + return {svgHelper(this.props, moreProps, xAccessor, identityStack)}; + } else { + const bars = getBars(this.props, moreProps); + return {getBarsSVG2(this.props, bars)}; + } + } +} + +/* + Initially, this program was using StackedBarSeries.getBars + to benefit from code reuse and having a single place that + contains the logic for drawing all types of bar charts + simple, grouped, horizontal, but turnes out + making it highly cuztimizable also made it slow for the + most simple case, a regular bar chart. + This function contains just the necessary logic + to create bars +*/ +function getBars(props, moreProps) { + const { baseAt, fill, stroke, yAccessor } = props; + const { xScale, xAccessor, plotData, chartConfig: { yScale } } = moreProps; + + const getFill = functor(fill); + const getBase = functor(baseAt); + + const widthFunctor = functor(props.width); + + const width = widthFunctor(props, { + xScale, + xAccessor, + plotData, + }); + + const offset = Math.floor(0.5 * width); + + const bars = plotData + .filter((d) => isDefined(yAccessor(d))) + .map((d) => { + const yValue = yAccessor(d); + let y = yScale(yValue); + + const x = Math.round(xScale(xAccessor(d))) - offset; + let h = getBase(xScale, yScale, d) - yScale(yValue); + + if (h < 0) { + y = y + h; + h = -h; + } + + return { + // type: "line" + x, + y: Math.round(y), + height: Math.round(h), + width: offset * 2, + fill: getFill(d, 0), + stroke: stroke ? getFill(d, 0) : "none", + }; + }); + + return bars; +} diff --git a/packages/react-financial-charts/src/series/BollingerSeries.tsx b/packages/react-financial-charts/src/series/BollingerSeries.tsx new file mode 100644 index 000000000..df9ddb593 --- /dev/null +++ b/packages/react-financial-charts/src/series/BollingerSeries.tsx @@ -0,0 +1,75 @@ +import * as React from "react"; + +import { AreaOnlySeries } from "./AreaOnlySeries"; +import { LineSeries } from "./LineSeries"; + +interface BollingerSeriesProps { + yAccessor: any; // func + className?: string; + areaClassName?: string; + opacity?: number; + type?: string; + stroke: { + top: string, + middle: string, + bottom: string, + }; + fill: string; +} + +export class BollingerSeries extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-bollinger-band-series", + areaClassName: "react-stockcharts-bollinger-band-series-area", + opacity: 0.2, + }; + + public render() { + const { areaClassName, className, opacity } = this.props; + const { stroke, fill } = this.props; + + return ( + + + + + + + ); + } + + private readonly yAccessorForScalledBottom = (scale, d) => { + const { yAccessor } = this.props; + return scale(yAccessor(d) && yAccessor(d).bottom); + } + + private readonly yAccessorForBottom = (d) => { + const { yAccessor } = this.props; + return yAccessor(d) && yAccessor(d).bottom; + } + + private readonly yAccessorForMiddle = (d) => { + const { yAccessor } = this.props; + return yAccessor(d) && yAccessor(d).middle; + } + + private readonly yAccessorForTop = (d) => { + const { yAccessor } = this.props; + return yAccessor(d) && yAccessor(d).top; + } +} diff --git a/packages/react-financial-charts/src/series/CandlestickSeries.tsx b/packages/react-financial-charts/src/series/CandlestickSeries.tsx new file mode 100644 index 000000000..5a3d3de52 --- /dev/null +++ b/packages/react-financial-charts/src/series/CandlestickSeries.tsx @@ -0,0 +1,237 @@ +import { nest } from "d3-collection"; +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getAxisCanvas } from "../GenericComponent"; + +import { + functor, head, hexToRGBA, isDefined, plotDataLengthBarWidth, +} from "../utils"; + +interface CandlestickSeriesProps { + readonly className?: string; + readonly wickClassName?: string; + readonly candleClassName?: string; + readonly candleStrokeWidth?: number | string; + readonly widthRatio?: number; + readonly width?: number | any; // func + readonly classNames?: string | any; // func + readonly fill?: string | any; // func + readonly stroke?: string | any; // func + readonly wickStroke?: string | any; // func + readonly yAccessor?: any; // func + readonly clip?: boolean; + readonly opacity?: number; +} + +export class CandlestickSeries extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-candlestick", + wickClassName: "react-stockcharts-candlestick-wick", + candleClassName: "react-stockcharts-candlestick-candle", + yAccessor: (d) => ({ open: d.open, high: d.high, low: d.low, close: d.close }), + classNames: (d) => d.close > d.open ? "up" : "down", + width: plotDataLengthBarWidth, + wickStroke: "#000000", + fill: (d) => d.close > d.open ? "#6BA583" : "#FF0000", + stroke: "#000000", + candleStrokeWidth: 0.5, + widthRatio: 0.8, + opacity: 0.5, + clip: true, + }; + + public render() { + const { clip } = this.props; + + return ( + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + drawOnCanvas(ctx, this.props, moreProps); + } + + private readonly renderSVG = (moreProps) => { + const { className, wickClassName, candleClassName } = this.props; + const { xScale, chartConfig: { yScale }, plotData, xAccessor } = moreProps; + + const candleData = getCandleData(this.props, xAccessor, xScale, yScale, plotData); + + return + + {getWicksSVG(candleData)} + + + {getCandlesSVG(this.props, candleData)} + + ; + } +} + +function getWicksSVG(candleData) { + + const wicks = candleData + .map((each, idx) => { + const d = each.wick; + return ; + }); + + return wicks; +} + +function getCandlesSVG(props: CandlestickSeriesProps, candleData) { + + const { opacity, candleStrokeWidth } = props; + + const candles = candleData.map((d, idx) => { + if (d.width <= 1) { + return ( + + ); + } else if (d.height === 0) { + return ( + + ); + } + return ( + + ); + }); + return candles; +} + +function drawOnCanvas(ctx, props: CandlestickSeriesProps, moreProps) { + const { opacity, candleStrokeWidth } = props; + const { xScale, chartConfig: { yScale }, plotData, xAccessor } = moreProps; + + const candleData = getCandleData(props, xAccessor, xScale, yScale, plotData); + + const wickNest = nest() + .key((d) => d.wick.stroke) + .entries(candleData); + + wickNest.forEach((outer) => { + const { key, values } = outer; + ctx.strokeStyle = key; + ctx.fillStyle = key; + values.forEach((each) => { + + const d = each.wick; + + ctx.fillRect(d.x - 0.5, d.y1, 1, d.y2 - d.y1); + ctx.fillRect(d.x - 0.5, d.y3, 1, d.y4 - d.y3); + }); + }); + + const candleNest = nest() + .key((d) => d.stroke) + .key((d) => d.fill) + .entries(candleData); + + candleNest.forEach((outer) => { + const { key: strokeKey, values: strokeValues } = outer; + if (strokeKey !== "none") { + ctx.strokeStyle = strokeKey; + ctx.lineWidth = candleStrokeWidth; + } + strokeValues.forEach((inner) => { + const { key, values } = inner; + const fillStyle = head(values).width <= 1 + ? key + : hexToRGBA(key, opacity); + ctx.fillStyle = fillStyle; + + values.forEach((d) => { + if (d.width <= 1) { + ctx.fillRect(d.x - 0.5, d.y, 1, d.height); + } else if (d.height === 0) { + ctx.fillRect(d.x, d.y - 0.5, d.width, 1); + } else { + ctx.fillRect(d.x, d.y, d.width, d.height); + if (strokeKey !== "none") { + ctx.strokeRect(d.x, d.y, d.width, d.height); + } + } + }); + }); + }); +} + +function getCandleData(props: CandlestickSeriesProps, xAccessor, xScale, yScale, plotData) { + + const { wickStroke: wickStrokeProp } = props; + const wickStroke = functor(wickStrokeProp); + + const { classNames, fill: fillProp, stroke: strokeProp, yAccessor } = props; + const className = functor(classNames); + + const fill = functor(fillProp); + const stroke = functor(strokeProp); + + const widthFunctor = functor(props.width); + const width = widthFunctor(props, { + xScale, + xAccessor, + plotData, + }); + + const trueOffset = 0.5 * width; + const offset = trueOffset > 0.7 + ? Math.round(trueOffset) + : Math.floor(trueOffset); + + const candles: any[] = []; + + // tslint:disable-next-line: prefer-for-of + for (let i = 0; i < plotData.length; i++) { + const d = plotData[i]; + if (isDefined(yAccessor(d).close)) { + const x = Math.round(xScale(xAccessor(d))); + + const ohlc = yAccessor(d); + const y = Math.round(yScale(Math.max(ohlc.open, ohlc.close))); + const height = Math.round(Math.abs(yScale(ohlc.open) - yScale(ohlc.close))); + + candles.push({ + // type: "line" + x: x - offset, + y, + wick: { + stroke: wickStroke(ohlc), + x, + y1: Math.round(yScale(ohlc.high)), + y2: y, + y3: y + height, // Math.round(yScale(Math.min(ohlc.open, ohlc.close))), + y4: Math.round(yScale(ohlc.low)), + }, + height, + width: offset * 2, + className: className(ohlc), + fill: fill(ohlc), + stroke: stroke(ohlc), + direction: (ohlc.close - ohlc.open), + }); + } + } + + return candles; +} diff --git a/packages/react-financial-charts/src/series/CircleMarker.tsx b/packages/react-financial-charts/src/series/CircleMarker.tsx new file mode 100644 index 000000000..586d3ef5e --- /dev/null +++ b/packages/react-financial-charts/src/series/CircleMarker.tsx @@ -0,0 +1,70 @@ +import * as React from "react"; + +import { functor, hexToRGBA } from "../utils"; + +interface CircleProps { + stroke?: string; + fill: string; + opacity: number; + point: { + x: number, + y: number, + datum: any, + }; + className?: string; + strokeWidth?: number; + r: number | any; // func +} + +export class Circle extends React.Component { + + public static defaultProps = { + stroke: "#4682B4", + strokeWidth: 1, + opacity: 0.5, + fill: "#4682B4", + className: "react-stockcharts-marker-circle", + }; + + public static drawOnCanvas = (props, point, ctx) => { + + const { stroke, fill, opacity, strokeWidth } = props; + + ctx.strokeStyle = stroke; + ctx.lineWidth = strokeWidth; + + if (fill !== "none") { + ctx.fillStyle = hexToRGBA(fill, opacity); + } + + Circle.drawOnCanvasWithNoStateChange(props, point, ctx); + } + + public static drawOnCanvasWithNoStateChange = (props, point, ctx) => { + + const { r } = props; + const radius = functor(r)(point.datum); + + ctx.moveTo(point.x, point.y); + ctx.beginPath(); + ctx.arc(point.x, point.y, radius, 0, 2 * Math.PI, false); + ctx.stroke(); + ctx.fill(); + } + + public render() { + const { className, stroke, strokeWidth, opacity, fill, point, r } = this.props; + const radius = functor(r)(point.datum); + return ( + + ); + } +} diff --git a/packages/react-financial-charts/src/series/ElderRaySeries.tsx b/packages/react-financial-charts/src/series/ElderRaySeries.tsx new file mode 100644 index 000000000..71bfec5f1 --- /dev/null +++ b/packages/react-financial-charts/src/series/ElderRaySeries.tsx @@ -0,0 +1,93 @@ +import * as React from "react"; + +import OverlayBarSeries from "./OverlayBarSeries"; +import { StraightLine } from "./StraightLine"; + +interface ElderRaySeriesProps { + className?: string; + yAccessor: any; // func + opacity?: number; + stroke?: boolean; + bullPowerFill?: string; + bearPowerFill?: string; + straightLineStroke?: string; + straightLineOpacity?: number; + widthRatio?: number; + clip: boolean; +} + +export class ElderRaySeries extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-elderray-series", + straightLineStroke: "#000000", + straightLineOpacity: 0.3, + opacity: 0.5, + stroke: true, + bullPowerFill: "#6BA583", + bearPowerFill: "#FF0000", + widthRatio: 0.8, + clip: true, + }; + + public render() { + const { className, opacity, stroke, + straightLineStroke, + straightLineOpacity, + widthRatio, + } = this.props; + const { clip } = this.props; + + return ( + + + + + ); + } + + private readonly yAccessorBullTop = (d) => { + const { yAccessor } = this.props; + return yAccessor(d) && (yAccessor(d).bullPower > 0 ? yAccessor(d).bullPower : undefined); + } + + private readonly yAccessorBearTop = (d) => { + const { yAccessor } = this.props; + return yAccessor(d) && (yAccessor(d).bearPower > 0 ? yAccessor(d).bearPower : undefined); + } + + private readonly yAccessorBullBottom = (d) => { + const { yAccessor } = this.props; + return yAccessor(d) && (yAccessor(d).bullPower < 0 ? 0 : undefined); + } + + private readonly yAccessorBearBottom = (d) => { + const { yAccessor } = this.props; + return yAccessor(d) && (yAccessor(d).bullPower < 0 + || yAccessor(d).bullPower * yAccessor(d).bearPower < 0 // bullPower is +ve and bearPower is -ve + ? Math.min(0, yAccessor(d).bullPower) : undefined); + } + + private readonly yAccessorForBarBase = (xScale, yScale, d) => { + const { yAccessor } = this.props; + const y = yAccessor(d) && Math.min(yAccessor(d).bearPower, 0); + return yScale(y); + } + + private readonly fillForEachBar = (d, yAccessorNumber) => { + const { bullPowerFill, bearPowerFill } = this.props; + return yAccessorNumber % 2 === 0 ? bullPowerFill : bearPowerFill; + } +} diff --git a/packages/react-financial-charts/src/series/GroupedBarSeries.tsx b/packages/react-financial-charts/src/series/GroupedBarSeries.tsx new file mode 100644 index 000000000..6e4d41cee --- /dev/null +++ b/packages/react-financial-charts/src/series/GroupedBarSeries.tsx @@ -0,0 +1,62 @@ +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getAxisCanvas } from "../GenericComponent"; +import { drawOnCanvasHelper, identityStack, StackedBarSeries, svgHelper } from "./StackedBarSeries"; + +interface GroupedBarSeriesProps { + baseAt: number | any; // func + direction: "up" | "down"; + stroke: boolean; + widthRatio: number; + opacity: number; + fill: string | any; // func + className: string | any; // func + yAccessor: any[]; // func +} + +export class GroupedBarSeries extends React.Component { + + public static defaultProps = { + ...StackedBarSeries.defaultProps, + widthRatio: 0.8, + spaceBetweenBar: 5, + }; + + public render() { + return ( + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { xAccessor } = moreProps; + + drawOnCanvasHelper(ctx, this.props, moreProps, xAccessor, identityStack, this.postProcessor); + } + + private readonly renderSVG = (moreProps) => { + const { xAccessor } = moreProps; + + return ( + + {svgHelper(this.props, moreProps, xAccessor, identityStack, this.postProcessor)} + + ); + } + + private readonly postProcessor = (array) => { + return array.map((each) => { + return { + ...each, + x: each.x + each.offset - each.groupOffset, + width: each.groupWidth, + }; + }); + } +} diff --git a/packages/react-financial-charts/src/series/KagiSeries.tsx b/packages/react-financial-charts/src/series/KagiSeries.tsx new file mode 100644 index 000000000..448bb5f79 --- /dev/null +++ b/packages/react-financial-charts/src/series/KagiSeries.tsx @@ -0,0 +1,178 @@ +import { curveStepBefore, line } from "d3-shape"; +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getAxisCanvas } from "../GenericComponent"; +import { isDefined, isNotDefined } from "../utils"; + +interface KagiSeriesProps { + readonly className?: string; + readonly stroke?: { + yang: string; + yin: string; + }; + readonly fill?: { + yang: string; + yin: string; + }; + readonly strokeWidth: number; +} + +export class KagiSeries extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-kagi", + strokeWidth: 2, + stroke: { + yang: "#6BA583", + yin: "#E60000", + }, + fill: { + yang: "none", + yin: "none", + }, + currentValueStroke: "#000000", + }; + + public render() { + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + const { xAccessor, xScale, chartConfig: { yScale }, plotData } = moreProps; + + const { + className, + stroke = KagiSeries.defaultProps.stroke, + fill = KagiSeries.defaultProps.fill, + strokeWidth, + } = this.props; + + const paths = helper(plotData, xAccessor) + .map((each, i) => { + const dataSeries = line() + .x((item) => xScale(item[0])) + .y((item) => yScale(item[1])) + .curve(curveStepBefore); + + dataSeries(each.plot); + + return ( + + ); + }); + return ( + + {paths} + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { xAccessor } = moreProps; + + drawOnCanvas(ctx, this.props, moreProps, xAccessor); + } +} + +function drawOnCanvas(ctx, props, moreProps, xAccessor) { + const { stroke, strokeWidth, currentValueStroke } = props; + const { xScale, chartConfig: { yScale }, plotData } = moreProps; + + const paths = helper(plotData, xAccessor); + + let begin = true; + + paths.forEach((each) => { + ctx.strokeStyle = stroke[each.type]; + ctx.lineWidth = strokeWidth; + + ctx.beginPath(); + let prevX; + each.plot.forEach((d) => { + const [x1, y] = [xScale(d[0]), yScale(d[1])]; + if (begin) { + ctx.moveTo(x1, y); + begin = false; + } else { + if (isDefined(prevX)) { + ctx.lineTo(prevX, y); + } + ctx.lineTo(x1, y); + } + prevX = x1; + }); + ctx.stroke(); + }); + const lastPlot = paths[paths.length - 1].plot; + const last = lastPlot[lastPlot.length - 1]; + ctx.beginPath(); + ctx.lineWidth = 1; + + const [x, y1, y2] = [xScale(last[0]), yScale(last[2]), yScale(last[3])]; + ctx.moveTo(x, y1); + ctx.lineTo(x + 10, y1); + ctx.stroke(); + + ctx.beginPath(); + ctx.strokeStyle = currentValueStroke; + ctx.moveTo(x - 10, y2); + ctx.lineTo(x, y2); + ctx.stroke(); +} + +function helper(plotData, xAccessor) { + const kagiLine: any[] = []; + let kagi: { + added?: boolean; + plot?: any; + type?: any; + } = {}; + let d = plotData[0]; + let idx = xAccessor(d); + + // tslint:disable-next-line: prefer-for-of + for (let i = 0; i < plotData.length; i++) { + d = plotData[i]; + + if (isNotDefined(d.close)) { continue; } + if (isNotDefined(kagi.type)) { kagi.type = d.startAs; } + if (isNotDefined(kagi.plot)) { kagi.plot = []; } + + idx = xAccessor(d); + kagi.plot.push([idx, d.open]); + + if (isDefined(d.changeTo)) { + kagi.plot.push([idx, d.changePoint]); + kagi.added = true; + kagiLine.push(kagi); + + kagi = { + type: d.changeTo, + plot: [], + added: false, + }; + kagi.plot.push([idx, d.changePoint]); + } + } + + if (!kagi.added) { + kagi.plot.push([idx, d.close, d.current, d.reverseAt]); + kagiLine.push(kagi); + } + + return kagiLine; +} diff --git a/packages/react-financial-charts/src/series/LineSeries.tsx b/packages/react-financial-charts/src/series/LineSeries.tsx new file mode 100644 index 000000000..f70cca625 --- /dev/null +++ b/packages/react-financial-charts/src/series/LineSeries.tsx @@ -0,0 +1,208 @@ + +import { line as d3Line } from "d3-shape"; +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getAxisCanvas, getMouseCanvas } from "../GenericComponent"; + +import { + getClosestItemIndexes, + getStrokeDasharray, + hexToRGBA, + isDefined, + strokeDashTypes, +} from "../utils"; + +interface LineSeriesProps { + className?: string; + strokeWidth?: number; + strokeOpacity?: number; + stroke?: string; + hoverStrokeWidth?: number; + fill?: string; + defined?: any; // func + hoverTolerance?: number; + strokeDasharray?: strokeDashTypes; + highlightOnHover?: boolean; + onClick?: any; // func + onDoubleClick?: any; // func + onHover?: any; // func + onUnHover?: any; // func + onContextMenu?: any; // func + yAccessor?: any; // func + connectNulls?: boolean; + interpolation?: any; // func + canvasClip?: any; // func + style?: React.CSSProperties; +} + +export class LineSeries extends React.Component { + + public static defaultProps = { + className: "line ", + strokeWidth: 1, + strokeOpacity: 1, + hoverStrokeWidth: 4, + fill: "none", + stroke: "#4682B4", + strokeDasharray: "Solid", + defined: (d) => !isNaN(d), + hoverTolerance: 6, + highlightOnHover: false, + connectNulls: false, + }; + + public render() { + const { highlightOnHover, onHover, onUnHover } = this.props; + const hoverProps = (highlightOnHover || onHover || onUnHover) + ? { + isHover: this.isHover, + drawOn: ["mousemove", "pan"], + canvasToDraw: getMouseCanvas, + } + : { + drawOn: ["pan"], + canvasToDraw: getAxisCanvas, + }; + + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + const { yAccessor, stroke, strokeOpacity, strokeWidth, hoverStrokeWidth, defined, strokeDasharray } = this.props; + const { connectNulls } = this.props; + const { interpolation, style } = this.props; + const { xAccessor, chartConfig } = moreProps; + + const { xScale, plotData, hovering } = moreProps; + + const { yScale } = chartConfig; + const dataSeries = d3Line() + .x((d) => Math.round(xScale(xAccessor(d)))) + .y((d) => Math.round(yScale(yAccessor(d)))); + + if (isDefined(interpolation)) { + dataSeries.curve(interpolation); + } + if (!connectNulls) { + dataSeries.defined((d) => defined(yAccessor(d))); + } + + const data = dataSeries(plotData); + + const { fill, className } = this.props; + + return ( + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { + yAccessor, + stroke, + strokeOpacity, + strokeWidth, + hoverStrokeWidth, + defined, + strokeDasharray, + interpolation, + canvasClip, + } = this.props; + + const { connectNulls } = this.props; + + const { xAccessor } = moreProps; + const { xScale, chartConfig: { yScale }, plotData, hovering } = moreProps; + + if (canvasClip) { + ctx.save(); + canvasClip(ctx, moreProps); + } + + ctx.lineWidth = hovering ? hoverStrokeWidth : strokeWidth; + + ctx.strokeStyle = hexToRGBA(stroke, strokeOpacity); + ctx.setLineDash(getStrokeDasharray(strokeDasharray).split(",")); + + const dataSeries = d3Line() + .x((d) => Math.round(xScale(xAccessor(d)))) + .y((d) => Math.round(yScale(yAccessor(d)))); + + if (isDefined(interpolation)) { + dataSeries.curve(interpolation); + } + if (!connectNulls) { + dataSeries.defined((d) => defined(yAccessor(d))); + } + + ctx.beginPath(); + dataSeries.context(ctx)(plotData); + ctx.stroke(); + + if (canvasClip) { + ctx.restore(); + } + } + + private readonly isHover = (moreProps) => { + const { highlightOnHover, yAccessor, hoverTolerance = LineSeries.defaultProps.hoverTolerance } = this.props; + + if (!highlightOnHover) { return false; } + + const { mouseXY, currentItem, xScale, plotData } = moreProps; + const { chartConfig: { yScale, origin } } = moreProps; + + const { xAccessor } = moreProps; + + const [x, y] = mouseXY; + const radius = hoverTolerance; + + const { left, right } = getClosestItemIndexes(plotData, xScale.invert(x), xAccessor); + if (left === right) { + const cy = yScale(yAccessor(currentItem)) + origin[1]; + const cx = xScale(xAccessor(currentItem)) + origin[0]; + + const hovering1 = Math.pow(x - cx, 2) + Math.pow(y - cy, 2) < Math.pow(radius, 2); + + return hovering1; + } else { + const l = plotData[left]; + const r = plotData[right]; + const x1 = xScale(xAccessor(l)) + origin[0]; + const y1 = yScale(yAccessor(l)) + origin[1]; + const x2 = xScale(xAccessor(r)) + origin[0]; + const y2 = yScale(yAccessor(r)) + origin[1]; + + // y = m * x + b + const m /* slope */ = (y2 - y1) / (x2 - x1); + const b /* y intercept */ = -1 * m * x1 + y1; + + const desiredY = Math.round(m * x + b); + + const hovering2 = y >= desiredY - radius && y <= desiredY + radius; + + return hovering2; + } + } +} diff --git a/packages/react-financial-charts/src/series/MACDSeries.tsx b/packages/react-financial-charts/src/series/MACDSeries.tsx new file mode 100644 index 000000000..50c418e2b --- /dev/null +++ b/packages/react-financial-charts/src/series/MACDSeries.tsx @@ -0,0 +1,92 @@ +import * as React from "react"; + +import { BarSeries } from "./BarSeries"; +import { LineSeries } from "./LineSeries"; +import { StraightLine } from "./StraightLine"; + +interface MACDSeriesProps { + className?: string; + yAccessor?: any; // func + opacity?: number; + divergenceStroke?: boolean; + zeroLineStroke?: string; + zeroLineOpacity?: number; + clip: boolean; + stroke: { + macd: string; + signal: string; + }; + fill: { + divergence: string | any; // func + }; + widthRatio?: number; + width?: number | any; // func +} + +export class MACDSeries extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-macd-series", + zeroLineStroke: "#000000", + zeroLineOpacity: 0.3, + opacity: 0.6, + divergenceStroke: false, + clip: true, + widthRatio: 0.5, + width: BarSeries.defaultProps.width, + }; + + public render() { + const { className, opacity, divergenceStroke, widthRatio, width } = this.props; + const { stroke, fill } = this.props; + + const { clip } = this.props; + const { zeroLineStroke, zeroLineOpacity } = this.props; + + return ( + + + + + + + ); + } + + private readonly yAccessorForDivergenceBase = (xScale, yScale) => { + return yScale(0); + } + + private readonly yAccessorForDivergence = (d) => { + const { yAccessor } = this.props; + return yAccessor(d) && yAccessor(d).divergence; + } + + private readonly yAccessorForSignal = (d) => { + const { yAccessor } = this.props; + return yAccessor(d) && yAccessor(d).signal; + } + + private readonly yAccessorForMACD = (d) => { + const { yAccessor } = this.props; + return yAccessor(d) && yAccessor(d).macd; + } +} diff --git a/packages/react-financial-charts/src/series/OHLCSeries.tsx b/packages/react-financial-charts/src/series/OHLCSeries.tsx new file mode 100644 index 000000000..f0a7223d4 --- /dev/null +++ b/packages/react-financial-charts/src/series/OHLCSeries.tsx @@ -0,0 +1,124 @@ +import { nest } from "d3-collection"; +import * as React from "react"; +import GenericChartComponent from "../GenericChartComponent"; +import { getAxisCanvas } from "../GenericComponent"; + +import { functor, isDefined } from "../utils"; + +interface OHLCSeriesProps { + className?: string; + classNames: string | any; // func + stroke: string | any; // func + yAccessor: any; // func + clip: boolean; +} + +export class OHLCSeries extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-ohlc", + yAccessor: (d) => ({ open: d.open, high: d.high, low: d.low, close: d.close }), + classNames: (d) => isDefined(d.absoluteChange) ? (d.absoluteChange > 0 ? "up" : "down") : "firstbar", + stroke: (d) => isDefined(d.absoluteChange) ? (d.absoluteChange > 0 ? "#6BA583" : "#FF0000") : "#000000", + clip: true, + }; + + public render() { + const { clip } = this.props; + + return ; + } + + private readonly renderSVG = (moreProps) => { + const { className, yAccessor } = this.props; + const { xAccessor } = moreProps; + const { xScale, chartConfig: { yScale }, plotData } = moreProps; + + const barData = getOHLCBars(this.props, xAccessor, yAccessor, xScale, yScale, plotData); + + const { strokeWidth, bars } = barData; + + return + {bars.map((d, idx) => )} + ; + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { yAccessor } = this.props; + const { xAccessor } = moreProps; + const { xScale, chartConfig: { yScale }, plotData } = moreProps; + + const barData = getOHLCBars(this.props, xAccessor, yAccessor, xScale, yScale, plotData); + drawOnCanvas(ctx, barData); + } +} + +function drawOnCanvas(ctx, barData) { + + const { strokeWidth, bars } = barData; + + const wickNest = nest() + .key((d) => d.stroke) + .entries(bars); + + ctx.lineWidth = strokeWidth; + + wickNest.forEach((outer) => { + const { key, values } = outer; + ctx.strokeStyle = key; + values.forEach((d) => { + ctx.beginPath(); + ctx.moveTo(d.x, d.y1); + ctx.lineTo(d.x, d.y2); + + ctx.moveTo(d.openX1, d.openY); + ctx.lineTo(d.openX2, d.openY); + + ctx.moveTo(d.closeX1, d.closeY); + ctx.lineTo(d.closeX2, d.closeY); + + ctx.stroke(); + }); + }); +} + +function getOHLCBars(props, xAccessor, yAccessor, xScale, yScale, plotData) { + const { classNames: classNamesProp, stroke: strokeProp } = props; + + const strokeFunc = functor(strokeProp); + const classNameFunc = functor(classNamesProp); + + const width = xScale(xAccessor(plotData[plotData.length - 1])) + - xScale(xAccessor(plotData[0])); + + const barWidth = Math.max(1, Math.round(width / (plotData.length - 1) / 2) - 1.5); + const strokeWidth = Math.min(barWidth, 6); + + const bars = plotData + .filter((d) => isDefined(yAccessor(d).close)) + .map((d) => { + const ohlc = yAccessor(d); + const x = Math.round(xScale(xAccessor(d))); + const y1 = yScale(ohlc.high); + const y2 = yScale(ohlc.low); + const openX1 = x - barWidth; + const openX2 = x + strokeWidth / 2; + const openY = yScale(ohlc.open); + const closeX1 = x - strokeWidth / 2; + const closeX2 = x + barWidth; + const closeY = yScale(ohlc.close); + const className = classNameFunc(d); + const stroke = strokeFunc(d); + + return { x, y1, y2, openX1, openX2, openY, closeX1, closeX2, closeY, stroke, className }; + }); + return { barWidth, strokeWidth, bars }; +} diff --git a/packages/react-financial-charts/src/series/OverlayBarSeries.tsx b/packages/react-financial-charts/src/series/OverlayBarSeries.tsx new file mode 100644 index 000000000..b426d5fd2 --- /dev/null +++ b/packages/react-financial-charts/src/series/OverlayBarSeries.tsx @@ -0,0 +1,123 @@ +import { merge } from "d3-array"; + +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getAxisCanvas } from "../GenericComponent"; + +import { first, functor, isDefined, isNotDefined, plotDataLengthBarWidth } from "../utils"; +import { drawOnCanvas2, getBarsSVG2 } from "./StackedBarSeries"; + +interface OverlayBarSeriesProps { + baseAt: number | any; // func + direction: "up" | "down"; + stroke: boolean; + widthRatio: number; + opacity: number; + fill: string | any; // func + className: string | any; // func + xAccessor?: any; // func + yAccessor?: any[]; // func + xScale?: any; // func + yScale?: any; // func + plotData?: number[]; + clip: boolean; +} + +export class OverlayBarSeries extends React.Component { + + public static defaultProps = { + baseAt: (xScale, yScale/* , d*/) => first(yScale.range()), + direction: "up", + className: "bar", + stroke: false, + fill: "#4682B4", + opacity: 1, + widthRatio: 0.5, + width: plotDataLengthBarWidth, + clip: true, + }; + + public render() { + const { clip } = this.props; + + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + const { yAccessor } = this.props; + + const bars = getBars(this.props, moreProps, yAccessor); + return ( + + {getBarsSVG2(this.props, bars)} + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { yAccessor } = this.props; + const bars = getBars(this.props, moreProps, yAccessor); + + drawOnCanvas2(this.props, ctx, bars); + } +} + +function getBars(props, moreProps, yAccessor) { + const { xScale, xAccessor, chartConfig: { yScale }, plotData } = moreProps; + const { baseAt, className, fill, stroke } = props; + + const getClassName = functor(className); + const getFill = functor(fill); + const getBase = functor(baseAt); + const widthFunctor = functor(props.width); + + const width = widthFunctor(props, moreProps); + const offset = Math.floor(0.5 * width); + + const bars = plotData + .map((d) => { + const innerBars = yAccessor.map((eachYAccessor, i) => { + const yValue = eachYAccessor(d); + if (isNotDefined(yValue)) { return undefined; } + + const xValue = xAccessor(d); + const x = Math.round(xScale(xValue)) - offset; + const y = yScale(yValue); + return { + width: offset * 2, + x, + y, + className: getClassName(d, i), + stroke: stroke ? getFill(d, i) : "none", + fill: getFill(d, i), + i, + }; + }).filter((yValue) => isDefined(yValue)); + + let b = getBase(xScale, yScale, d); + let h; + for (let i = innerBars.length - 1; i >= 0; i--) { + h = b - innerBars[i].y; + if (h < 0) { + innerBars[i].y = b; + h = -1 * h; + } + innerBars[i].height = h; + b = innerBars[i].y; + } + return innerBars; + }); + + return merge(bars); +} + +export default OverlayBarSeries; diff --git a/packages/react-financial-charts/src/series/PointAndFigureSeries.tsx b/packages/react-financial-charts/src/series/PointAndFigureSeries.tsx new file mode 100644 index 000000000..4fac1a28e --- /dev/null +++ b/packages/react-financial-charts/src/series/PointAndFigureSeries.tsx @@ -0,0 +1,172 @@ +import * as React from "react"; +import GenericChartComponent from "../GenericChartComponent"; +import { getAxisCanvas } from "../GenericComponent"; + +import { isDefined, isNotDefined } from "../utils"; + +interface PointAndFigureSeriesProps { + className?: string; + strokeWidth: number; + stroke: { + up: string, + down: string, + }; + fill: { + up: string, + down: string, + }; + clip: boolean; +} + +export class PointAndFigureSeries extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-point-and-figure", + strokeWidth: 1, + stroke: { + up: "#6BA583", + down: "#FF0000", + }, + fill: { + up: "none", + down: "none", + }, + clip: true, + }; + + public render() { + const { clip } = this.props; + + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + const { xAccessor } = moreProps; + const { xScale, chartConfig: { yScale }, plotData } = moreProps; + + const { stroke, fill, strokeWidth, className } = this.props; + + const columns = getColumns(xScale, xAccessor, yScale, plotData); + + return ( + + {columns.map((col, idx) => ( + + {col.boxes.map((box, i) => { + if (col.direction > 0) { + return ( + + + + + ); + } + return ( + + ); + })} + + ))} + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { xAccessor } = moreProps; + const { xScale, chartConfig: { yScale }, plotData } = moreProps; + const columns = getColumns(xScale, xAccessor, yScale, plotData); + + drawOnCanvas(ctx, this.props, columns); + } +} + +function drawOnCanvas(ctx, props, columns) { + const { stroke, fill, strokeWidth } = props; + + ctx.lineWidth = strokeWidth; + + columns.forEach((col) => { + const [offsetX, offsetY] = col.offset; + col.boxes.forEach((box) => { + if (col.direction > 0) { + ctx.fillStyle = fill.up; + ctx.strokeStyle = stroke.up; + + ctx.beginPath(); + + ctx.moveTo(offsetX, offsetY + box.open); + ctx.lineTo(offsetX + box.columnWidth, offsetY + box.close); + ctx.moveTo(offsetX, offsetY + box.close); + ctx.lineTo(offsetX + box.columnWidth, offsetY + box.open); + + ctx.stroke(); + } else { + ctx.fillStyle = fill.down; + ctx.strokeStyle = stroke.down; + + ctx.beginPath(); + + const [x, y] = [offsetX + box.columnWidth / 2, offsetY + box.open + box.boxHeight / 2]; + const [rx, ry] = [box.columnWidth / 2, box.boxHeight / 2]; + + ctx.ellipse(x, y, rx, ry, 0, 0, 2 * Math.PI); + ctx.stroke(); + } + }); + }); + + ctx.stroke(); +} + +function getColumns(xScale, xAccessor, yScale, plotData) { + + const width = xScale(xAccessor(plotData[plotData.length - 1])) + - xScale(xAccessor(plotData[0])); + + const columnWidth = (width / (plotData.length - 1)); + + let anyBox; + let j = 0; + while (isNotDefined(anyBox)) { + if (isDefined(plotData[j].close)) { + anyBox = plotData[j].boxes[0]; + } else { + break; + } + j++; + } + + const boxHeight = Math.abs(yScale(anyBox.open) - yScale(anyBox.close)); + + const columns = plotData + .filter((d) => isDefined(d.close)) + .map((d) => { + const boxes = d.boxes.map((box) => ({ + columnWidth, + boxHeight, + open: yScale(box.open), + close: yScale(box.close), + })); + + const xOffset = (xScale(xAccessor(d)) - (columnWidth / 2)); + return { + boxes, + direction: d.direction, + offset: [xOffset, 0], + }; + }); + return columns; +} diff --git a/packages/react-financial-charts/src/series/RSISeries.tsx b/packages/react-financial-charts/src/series/RSISeries.tsx new file mode 100644 index 000000000..16be85f77 --- /dev/null +++ b/packages/react-financial-charts/src/series/RSISeries.tsx @@ -0,0 +1,215 @@ +import * as React from "react"; +import { + strokeDashTypes, +} from "../utils"; +import { LineSeries } from "./LineSeries"; +import { StraightLine } from "./StraightLine"; +import { SVGComponent } from "./SVGComponent"; + +interface RSISeriesProps { + className?: string; + yAccessor: any; // func + stroke: { + line: strokeDashTypes; + top: string; + middle: string; + bottom: string; + outsideThreshold: string; + insideThreshold: string; + }; + opacity: { + top: number; + middle: number; + bottom: number; + }; + strokeDasharray: { + line: strokeDashTypes; + top: strokeDashTypes; + middle: strokeDashTypes; + bottom: strokeDashTypes; + }; + strokeWidth: { + outsideThreshold: number; + insideThreshold: number; + top: number; + middle: number; + bottom: number; + }; + overSold: number; + middle: number; + overBought: number; +} + +export class RSISeries extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-rsi-series", + stroke: { + line: "#000000", + top: "#B8C2CC", + middle: "#8795A1", + bottom: "#B8C2CC", + outsideThreshold: "#b300b3", + insideThreshold: "#ffccff", + }, + opacity: { + top: 1, + middle: 1, + bottom: 1, + }, + strokeDasharray: { + line: "Solid", + top: "ShortDash", + middle: "ShortDash", + bottom: "ShortDash", + }, + strokeWidth: { + outsideThreshold: 1, + insideThreshold: 1, + top: 1, + middle: 1, + bottom: 1, + }, + overSold: 70, + middle: 50, + overBought: 30, + }; + + private clipPathId1: string; + private clipPathId2: string; + + constructor(props) { + super(props); + + const id1 = String(Math.round(Math.random() * 10000 * 10000)); + this.clipPathId1 = `rsi-clip-${id1}`; + + const id2 = String(Math.round(Math.random() * 10000 * 10000)); + this.clipPathId2 = `rsi-clip-${id2}`; + } + + public render() { + const { className, stroke, opacity, strokeDasharray, strokeWidth } = this.props; + const { yAccessor } = this.props; + const { overSold, middle, overBought } = this.props; + + const style1 = { clipPath: `url(#${this.clipPathId1})` }; + const style2 = { clipPath: `url(#${this.clipPathId2})` }; + + return ( + + + {this.renderClip} + + + + + + + + ); + } + + private readonly renderClip = (moreProps) => { + const { chartConfig } = moreProps; + const { overSold, overBought } = this.props; + const { yScale, width, height } = chartConfig; + + return ( + + + + + + + + + + ); + } + + private readonly mainClip = (ctx, moreProps) => { + const { chartConfig } = moreProps; + const { overSold, overBought } = this.props; + const { yScale, width, height } = chartConfig; + + ctx.beginPath(); + ctx.rect( + 0, + 0, + width, + yScale(overSold), + ); + ctx.rect( + 0, + yScale(overBought), + width, + height - yScale(overBought), + ); + ctx.clip(); + } + + private readonly topAndBottomClip = (ctx, moreProps) => { + const { chartConfig } = moreProps; + const { overSold, overBought } = this.props; + const { yScale, width } = chartConfig; + + ctx.beginPath(); + ctx.rect( + 0, + yScale(overSold), + width, + yScale(overBought) - yScale(overSold), + ); + ctx.clip(); + } +} diff --git a/packages/react-financial-charts/src/series/RenkoSeries.tsx b/packages/react-financial-charts/src/series/RenkoSeries.tsx new file mode 100644 index 000000000..60857d9ab --- /dev/null +++ b/packages/react-financial-charts/src/series/RenkoSeries.tsx @@ -0,0 +1,134 @@ +import * as React from "react"; +import GenericChartComponent from "../GenericChartComponent"; +import { getAxisCanvas } from "../GenericComponent"; + +import { isDefined } from "../utils"; + +interface RenkoSeriesProps { + classNames?: { + up: string, + down: string, + }; + stroke: { + up: string, + down: string, + }; + fill: { + up: string, + down: string, + partial: string, + }; + yAccessor: any; // func + clip: boolean; +} + +export class RenkoSeries extends React.Component { + + public static defaultProps = { + classNames: { + up: "up", + down: "down", + }, + stroke: { + up: "none", + down: "none", + }, + fill: { + up: "#6BA583", + down: "#E60000", + partial: "#4682B4", + }, + yAccessor: (d) => ({ open: d.open, high: d.high, low: d.low, close: d.close }), + clip: true, + }; + + public render() { + const { clip } = this.props; + + return ; + } + + private readonly renderSVG = (moreProps) => { + const { xAccessor } = moreProps; + const { xScale, chartConfig: { yScale }, plotData } = moreProps; + + const { yAccessor } = this.props; + + const candles = getRenko(this.props, plotData, xScale, xAccessor, yScale, yAccessor) + .map((each, idx) => ()); + + return ( + + + {candles} + + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { xAccessor } = moreProps; + const { xScale, chartConfig: { yScale }, plotData } = moreProps; + + const { yAccessor } = this.props; + + const candles = getRenko(this.props, plotData, xScale, xAccessor, yScale, yAccessor); + + drawOnCanvas(ctx, candles); + } +} + +function drawOnCanvas(ctx, renko) { + renko.forEach((d) => { + ctx.beginPath(); + + ctx.strokeStyle = d.stroke; + ctx.fillStyle = d.fill; + + ctx.rect(d.x, d.y, d.width, d.height); + ctx.closePath(); + ctx.fill(); + }); +} + +function getRenko(props, plotData, xScale, xAccessor, yScale, yAccessor) { + const { classNames, fill } = props; + const width = xScale(xAccessor(plotData[plotData.length - 1])) + - xScale(xAccessor(plotData[0])); + + const candleWidth = (width / (plotData.length - 1)); + const candles = plotData + .filter((d) => isDefined(yAccessor(d).close)) + .map((d) => { + const ohlc = yAccessor(d); + const x = xScale(xAccessor(d)) - 0.5 * candleWidth; + const y = yScale(Math.max(ohlc.open, ohlc.close)); + const height = Math.abs(yScale(ohlc.open) - yScale(ohlc.close)); + const className = (ohlc.open <= ohlc.close) ? classNames.up : classNames.down; + + const svgfill = d.fullyFormed + ? (ohlc.open <= ohlc.close ? fill.up : fill.down) + : fill.partial; + + return { + className, + fill: svgfill, + x, + y, + height, + width: candleWidth, + }; + }); + return candles; +} diff --git a/packages/react-financial-charts/src/series/SARSeries.tsx b/packages/react-financial-charts/src/series/SARSeries.tsx new file mode 100644 index 000000000..6c40ca27a --- /dev/null +++ b/packages/react-financial-charts/src/series/SARSeries.tsx @@ -0,0 +1,114 @@ +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getAxisCanvas, getMouseCanvas } from "../GenericComponent"; + +import { first, hexToRGBA, isDefined, last } from "../utils"; + +interface SARSeriesProps { + className?: string; + fill: { + falling: string; + rising: string; + }; + yAccessor: any; // func + opacity: number; + onClick?: any; // func + onDoubleClick?: any; // func + onContextMenu?: any; // func + highlightOnHover?: boolean; +} + +export class SARSeries extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-sar", + fill: { + falling: "#4682B4", + rising: "#15EC2E", + }, + highlightOnHover: true, + opacity: 0.2, + }; + + public render() { + const { highlightOnHover } = this.props; + + const hoverProps = highlightOnHover + ? { + isHover: this.isHover, + drawOn: ["mousemove", "pan"], + canvasToDraw: getMouseCanvas, + } + : { + drawOn: ["pan"], + canvasToDraw: getAxisCanvas, + }; + + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + const { className, yAccessor } = this.props; + const { xAccessor, plotData, xScale, chartConfig: { yScale } } = moreProps; + + return + {plotData + .filter((each) => isDefined(yAccessor(each))) + .map((each, idx) => { + return ( + + ); + })} + ; + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { yAccessor, fill, opacity } = this.props; + const { xAccessor, plotData, xScale, chartConfig: { yScale }, hovering } = moreProps; + + const width = xScale(xAccessor(last(plotData))) - xScale(xAccessor(first(plotData))); + + const d = (width / plotData.length) * 0.5 / 2; + const radius = Math.min(2, Math.max(0.5, d)) + (hovering ? 2 : 0); + + plotData.forEach((each) => { + const centerX = xScale(xAccessor(each)); + const centerY = yScale(yAccessor(each)); + const color = yAccessor(each) > each.close + ? fill.falling + : fill.rising; + + ctx.fillStyle = hexToRGBA(color, opacity); + ctx.strokeStyle = color; + + ctx.beginPath(); + ctx.ellipse(centerX, centerY, radius, radius, 0, 0, 2 * Math.PI); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + }); + } + + private readonly isHover = (moreProps) => { + const { mouseXY, currentItem, chartConfig: { yScale } } = moreProps; + const { yAccessor } = this.props; + const y = mouseXY[1]; + const currentY = yScale(yAccessor(currentItem)); + return y < currentY + 5 && y > currentY - 5; + } +} diff --git a/packages/react-financial-charts/src/series/SVGComponent.tsx b/packages/react-financial-charts/src/series/SVGComponent.tsx new file mode 100644 index 000000000..afe36a0c0 --- /dev/null +++ b/packages/react-financial-charts/src/series/SVGComponent.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; + +export class SVGComponent extends React.Component { + public render() { + const { children } = this.props; + return ( + + ); + } +} diff --git a/packages/react-financial-charts/src/series/ScatterSeries.tsx b/packages/react-financial-charts/src/series/ScatterSeries.tsx new file mode 100644 index 000000000..7b440b804 --- /dev/null +++ b/packages/react-financial-charts/src/series/ScatterSeries.tsx @@ -0,0 +1,109 @@ +import { nest as d3Nest } from "d3-collection"; +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getAxisCanvas } from "../GenericComponent"; + +import { functor, hexToRGBA } from "../utils"; + +interface ScatterSeriesProps { + className?: string; + yAccessor: any; // func + marker?: any; // func + markerProvider?: any; // func + markerProps?: object; +} + +export class ScatterSeries extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-scatter", + }; + + public render() { + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + const { className, markerProps } = this.props; + const { xAccessor } = moreProps; + + const points = this.helper(this.props, moreProps, xAccessor); + + return + {points.map((point, idx) => { + const { marker: Marker } = point; + return ; + })} + ; + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { xAccessor } = moreProps; + + const points = this.helper(this.props, moreProps, xAccessor); + + this.drawOnCanvasHelper(ctx, this.props, points); + } + + private readonly helper = (props, moreProps, xAccessor) => { + const { yAccessor, markerProvider, markerProps } = props; + let { marker: Marker } = props; + const { xScale, chartConfig: { yScale }, plotData } = moreProps; + + if (!(markerProvider || Marker)) { throw new Error("required prop, either marker or markerProvider missing"); } + + return plotData.map((d) => { + + if (markerProvider) { Marker = markerProvider(d); } + + const mProps = { ...Marker.defaultProps, ...markerProps }; + + const fill = functor(mProps.fill); + const stroke = functor(mProps.stroke); + + return { + x: xScale(xAccessor(d)), + y: yScale(yAccessor(d)), + fill: hexToRGBA(fill(d), mProps.opacity), + stroke: stroke(d), + datum: d, + marker: Marker, + }; + }); + } + + private readonly drawOnCanvasHelper = (ctx, props, points) => { + + const { markerProps } = props; + + const nest = d3Nest() + .key((d) => d.fill) + .key((d) => d.stroke) + .entries(points); + + nest.forEach((fillGroup) => { + const { key: fillKey, values: fillValues } = fillGroup; + + if (fillKey !== "none") { + ctx.fillStyle = fillKey; + } + + fillValues.forEach((strokeGroup) => { + const { values: strokeValues } = strokeGroup; + + strokeValues.forEach((point) => { + const { marker } = point; + marker.drawOnCanvas({ ...marker.defaultProps, ...markerProps, fill: fillKey }, point, ctx); + }); + }); + }); + } +} diff --git a/packages/react-financial-charts/src/series/SquareMarker.tsx b/packages/react-financial-charts/src/series/SquareMarker.tsx new file mode 100644 index 000000000..166b70d67 --- /dev/null +++ b/packages/react-financial-charts/src/series/SquareMarker.tsx @@ -0,0 +1,72 @@ +import * as React from "react"; +import { functor, hexToRGBA } from "../utils"; + +interface SquareProps { + stroke?: string; + fill: string; + opacity: number; + point: { + x: number, + y: number, + datum: any, + }; + className?: string; + strokeWidth?: number; + width: number | any; // func +} + +export class Square extends React.Component { + + public static defaultProps = { + stroke: "#4682B4", + strokeWidth: 1, + opacity: 0.5, + fill: "#4682B4", + className: "react-stockcharts-marker-rect", + }; + + public static drawOnCanvas = (props, point, ctx) => { + const { stroke, fill, opacity, strokeWidth } = props; + ctx.strokeStyle = stroke; + ctx.lineWidth = strokeWidth; + if (fill !== "none") { + ctx.fillStyle = hexToRGBA(fill, opacity); + } + Square.drawOnCanvasWithNoStateChange(props, point, ctx); + } + + public static drawOnCanvasWithNoStateChange = (props, point, ctx) => { + const { width } = props; + const w = functor(width)(point.datum); + const x = point.x - (w / 2); + const y = point.y - (w / 2); + ctx.beginPath(); + ctx.rect(x, y, w, w); + ctx.stroke(); + ctx.fill(); + } + + public render() { + const { + className, stroke, strokeWidth, + opacity, fill, point, width, + } = this.props; + const w = functor(width)(point.datum); + const x = point.x - (w / 2); + const y = point.y - (w / 2); + + return ( + + ); + } +} diff --git a/packages/react-financial-charts/src/series/StackedBarSeries.tsx b/packages/react-financial-charts/src/series/StackedBarSeries.tsx new file mode 100644 index 000000000..9789e8483 --- /dev/null +++ b/packages/react-financial-charts/src/series/StackedBarSeries.tsx @@ -0,0 +1,270 @@ +import { merge } from "d3-array"; +import { nest as d3Nest } from "d3-collection"; +import { stack as d3Stack } from "d3-shape"; +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getAxisCanvas } from "../GenericComponent"; + +import { functor, head, hexToRGBA, identity, plotDataLengthBarWidth } from "../utils"; + +interface StackedBarSeriesProps { + readonly baseAt: number | any; // func + readonly direction: "up" | "down"; + readonly stroke: boolean; + readonly width: number | any; // func + readonly opacity: number; + readonly fill: string | any; // func + readonly className: string | any; // func + readonly clip: boolean; +} + +export class StackedBarSeries extends React.Component { + + public static defaultProps = { + baseAt: (xScale, yScale/* , d*/) => head(yScale.range()), + direction: "up", + className: "bar", + stroke: true, + fill: "#4682B4", + opacity: 0.5, + width: plotDataLengthBarWidth, + widthRatio: 0.8, + clip: true, + swapScales: false, + }; + + public render() { + const { clip } = this.props; + + return ( + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { xAccessor } = moreProps; + + drawOnCanvasHelper(ctx, this.props, moreProps, xAccessor, d3Stack); + } + + private readonly renderSVG = (moreProps) => { + const { xAccessor } = moreProps; + + return {svgHelper(this.props, moreProps, xAccessor, d3Stack)}; + } +} + +export function identityStack() { + let keys = []; + function stack(data) { + const response = keys.map((key, i) => { + const arrays = data.map((d) => { + const array = [0, d[key]]; + + // @ts-ignore + array.data = d; + return array; + }); + arrays.key = key; + arrays.index = i; + return arrays; + }); + return response; + } + stack.keys = function (x) { + if (!arguments.length) { + return keys; + } + keys = x; + return stack; + }; + return stack; +} + +export function drawOnCanvasHelper(ctx, props, moreProps, xAccessor, stackFn, defaultPostAction = identity, postRotateAction = rotateXY) { + const { xScale, chartConfig: { yScale }, plotData } = moreProps; + + const bars = doStuff(props, xAccessor, plotData, xScale, yScale, stackFn, postRotateAction, defaultPostAction); + + drawOnCanvas2(props, ctx, bars); +} + +function convertToArray(item) { + return Array.isArray(item) ? item : [item]; +} + +export function svgHelper(props, moreProps, xAccessor, stackFn, defaultPostAction = identity, postRotateAction = rotateXY) { + const { xScale, chartConfig: { yScale }, plotData } = moreProps; + const bars = doStuff(props, xAccessor, plotData, xScale, yScale, stackFn, postRotateAction, defaultPostAction); + return getBarsSVG2(props, bars); +} + +function doStuff(props, xAccessor, plotData, xScale, yScale, stackFn, postRotateAction, defaultPostAction) { + const { yAccessor, swapScales } = props; + + const modifiedYAccessor = swapScales ? convertToArray(props.xAccessor) : convertToArray(yAccessor); + const modifiedXAccessor = swapScales ? yAccessor : xAccessor; + + const modifiedXScale = swapScales ? yScale : xScale; + const modifiedYScale = swapScales ? xScale : yScale; + + const postProcessor = swapScales ? postRotateAction : defaultPostAction; + + const bars = getBars(props, modifiedXAccessor, modifiedYAccessor, modifiedXScale, modifiedYScale, plotData, stackFn, postProcessor); + + return bars; +} + +export const rotateXY = (array) => array.map((each) => { + return { + ...each, + x: each.y, + y: each.x, + height: each.width, + width: each.height, + }; +}); + +export function getBarsSVG2(props, bars) { + /* eslint-disable react/prop-types */ + const { opacity } = props; + /* eslint-enable react/prop-types */ + + return bars.map((d, idx) => { + if (d.width <= 1) { + return ; + } + return ; + }); +} + +export function drawOnCanvas2(props, ctx, bars) { + const { stroke } = props; + + const nest = d3Nest() + .key((d) => d.fill) + .entries(bars); + + nest.forEach((outer) => { + const { key, values } = outer; + if (head(values).width > 1) { + ctx.strokeStyle = key; + } + const fillStyle = head(values).width <= 1 + ? key + : hexToRGBA(key, props.opacity); + ctx.fillStyle = fillStyle; + + values.forEach((d) => { + if (d.width <= 1) { + ctx.fillRect(d.x - 0.5, d.y, 1, d.height); + } else { + ctx.fillRect(d.x, d.y, d.width, d.height); + if (stroke) { ctx.strokeRect(d.x, d.y, d.width, d.height); } + } + + }); + }); +} + +export function getBars(props, xAccessor, yAccessor, xScale, yScale, plotData, stack = identityStack, after = identity) { + const { baseAt, className, fill, stroke, spaceBetweenBar = 0 } = props; + + const getClassName = functor(className); + const getFill = functor(fill); + const getBase = functor(baseAt); + + const widthFunctor = functor(props.width); + const width = widthFunctor(props, { + xScale, + xAccessor, + plotData, + }); + + const barWidth = Math.round(width); + + const eachBarWidth = (barWidth - spaceBetweenBar * (yAccessor.length - 1)) / yAccessor.length; + + const offset = (barWidth === 1 ? 0 : 0.5 * width); + + const ds = plotData + .map((each) => { + const d = { + appearance: { + }, + x: xAccessor(each), + }; + yAccessor.forEach((eachYAccessor, i) => { + const key = `y${i}`; + d[key] = eachYAccessor(each); + const appearance = { + className: getClassName(each, i), + stroke: stroke ? getFill(each, i) : "none", + fill: getFill(each, i), + }; + d.appearance[key] = appearance; + }); + return d; + }); + + const keys = yAccessor.map((_, i) => `y${i}`); + + // @ts-ignore + const data = stack().keys(keys)(ds); + + const newData = data.map((each, i) => { + const key = each.key; + return each.map((d) => { + const array = [d[0], d[1]]; + + // @ts-ignore + array.data = { + x: d.data.x, + i, + appearance: d.data.appearance[key], + }; + return array; + }); + }); + + const bars = merge(newData) + .map((d) => { + let y = yScale(d[1]); + let h = getBase(xScale, yScale, d.data) - yScale(d[1] - d[0]); + if (h < 0) { + y = y + h; + h = -h; + } + + return { + ...d.data.appearance, + x: Math.round(xScale(d.data.x) - width / 2), + y, + groupOffset: Math.round(offset - (d.data.i > 0 ? (eachBarWidth + spaceBetweenBar) * d.data.i : 0)), + groupWidth: Math.round(eachBarWidth), + offset: Math.round(offset), + height: h, + width: barWidth, + }; + }) + .filter((bar) => !isNaN(bar.y)); + + return after(bars); +} diff --git a/packages/react-financial-charts/src/series/StochasticSeries.tsx b/packages/react-financial-charts/src/series/StochasticSeries.tsx new file mode 100644 index 000000000..f1363d4e7 --- /dev/null +++ b/packages/react-financial-charts/src/series/StochasticSeries.tsx @@ -0,0 +1,79 @@ +import * as React from "react"; + +import { LineSeries } from "./LineSeries"; +import { StraightLine } from "./StraightLine"; + +interface StochasticSeriesProps { + className?: string; + yAccessor: any; // func + stroke: { + top: string; + middle: string; + bottom: string; + dLine: string; + kLine: string; + }; + overSold: number; + middle: number; + overBought: number; + refLineOpacity: number; +} + +export class StochasticSeries extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-stochastic-series", + stroke: { + top: "#964B00", + middle: "#000000", + bottom: "#964B00", + dLine: "#EA2BFF", + kLine: "#74D400", + }, + overSold: 80, + middle: 50, + overBought: 20, + refLineOpacity: 0.3, + }; + + public render() { + const { className, stroke, refLineOpacity } = this.props; + const { overSold, middle, overBought } = this.props; + return ( + + + + + + + + ); + } + + private readonly yAccessorForK = (d) => { + const { yAccessor } = this.props; + + return yAccessor(d) && yAccessor(d).K; + } + + private readonly yAccessorForD = (d) => { + const { yAccessor } = this.props; + + return yAccessor(d) && yAccessor(d).D; + } +} diff --git a/packages/react-financial-charts/src/series/StraightLine.tsx b/packages/react-financial-charts/src/series/StraightLine.tsx new file mode 100644 index 000000000..a6fc0258b --- /dev/null +++ b/packages/react-financial-charts/src/series/StraightLine.tsx @@ -0,0 +1,86 @@ +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getAxisCanvas } from "../GenericComponent"; +import { getStrokeDasharray, hexToRGBA, strokeDashTypes } from "../utils"; + +interface StraightLineProps { + className?: string; + type?: "vertical" | "horizontal"; + stroke?: string; + strokeWidth?: number; + strokeDasharray?: strokeDashTypes; + opacity: number; + yValue?: number; + xValue?: number; +} + +export class StraightLine extends React.Component { + + public static defaultProps = { + className: "line", + type: "horizontal", + stroke: "#000000", + opacity: 0.5, + strokeWidth: 1, + strokeDasharray: "Solid", + }; + + public render() { + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + const { width, height } = moreProps; + const { xScale, chartConfig: { yScale } } = moreProps; + + const { className } = this.props; + const { type, stroke, strokeWidth, opacity, strokeDasharray } = this.props; + const { yValue, xValue } = this.props; + + const lineCoordinates = this.getLineCoordinates(type, xScale, yScale, xValue, yValue, width, height); + + return ( + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { type, stroke, strokeWidth, opacity, strokeDasharray } = this.props; + const { yValue, xValue } = this.props; + const { xScale } = moreProps; + const { chartConfig: { yScale, width, height } } = moreProps; + + ctx.beginPath(); + + ctx.strokeStyle = hexToRGBA(stroke, opacity); + ctx.lineWidth = strokeWidth; + + const { x1, y1, x2, y2 } = this.getLineCoordinates(type, xScale, yScale, xValue, yValue, width, height); + + ctx.setLineDash(getStrokeDasharray(strokeDasharray).split(",")); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + } + + private readonly getLineCoordinates = (type, xScale, yScale, xValue, yValue, width, height) => { + return type === "horizontal" + ? { x1: 0, y1: Math.round(yScale(yValue)), x2: width, y2: Math.round(yScale(yValue)) } + : { x1: Math.round(xScale(xValue)), y1: 0, x2: Math.round(xScale(xValue)), y2: height }; + } +} diff --git a/packages/react-financial-charts/src/series/TriangleMarker.tsx b/packages/react-financial-charts/src/series/TriangleMarker.tsx new file mode 100644 index 000000000..d62a735a8 --- /dev/null +++ b/packages/react-financial-charts/src/series/TriangleMarker.tsx @@ -0,0 +1,145 @@ +import * as React from "react"; +import { functor, hexToRGBA } from "../utils"; + +interface TriangleProps { + direction: "top" | "bottom" | "left" | "right" | "hide"; + stroke: string | any; // func + fill: string | any; + opacity: number; + point: { + x: number, + y: number, + datum: any, + }; + className?: string; + strokeWidth?: number; + width: number | any; // func +} + +export class Triangle extends React.Component { + + public static defaultProps = { + direction: "top", + stroke: "#4682B4", + strokeWidth: 1, + opacity: 0.5, + fill: "#4682B4", + className: "react-stockcharts-marker-triangle", + }; + + public static drawOnCanvas = (props, point, ctx) => { + const { stroke, fill, opacity, strokeWidth } = props; + ctx.strokeStyle = stroke; + ctx.lineWidth = strokeWidth; + if (fill !== "none") { + ctx.fillStyle = hexToRGBA(fill, opacity); + } + Triangle.drawOnCanvasWithNoStateChange(props, point, ctx); + } + + public static drawOnCanvasWithNoStateChange = (props, point, ctx) => { + + const { width } = props; + const w = functor(width)(point.datum); + const { x, y } = point; + const { innerOpposite, innerHypotenuse } = getTrianglePoints(w); + const rotationDeg = getRotationInDegrees(props, point); + + ctx.beginPath(); + ctx.moveTo(x, y - innerHypotenuse); + ctx.lineTo(x + (w / 2), y + innerOpposite); + ctx.lineTo(x - (w / 2), y + innerOpposite); + ctx.stroke(); + + // TODO: rotation does not work + // example: https://gist.github.com/geoffb/6392450 + if (rotationDeg !== null && rotationDeg !== 0) { + ctx.save(); + ctx.translate(x, y); + ctx.rotate(rotationDeg * Math.PI / 180); // 45 degrees + ctx.fill(); + ctx.restore(); + } + ctx.fill(); + } + + public render() { + + const { + className, strokeWidth, + opacity, point, width, + } = this.props; + + const rotation = getRotationInDegrees(this.props, point); + if (rotation == null) { + return null; + } + + const fillColor = getFillColor(this.props); + const strokeColor = getStrokeColor(this.props); + + const w = functor(width)(point.datum); + const { x, y } = point; + const { innerOpposite, innerHypotenuse } = getTrianglePoints(w); + const points = ` + ${x} ${y - innerHypotenuse}, + ${x + (w / 2)} ${y + innerOpposite}, + ${x - (w / 2)} ${y + innerOpposite} + `; + + return ( + + ); + } +} + +function getTrianglePoints(width) { + const innerHypotenuse = (width / 2) * (1 / Math.cos(30 * Math.PI / 180)); + const innerOpposite = (width / 2) * (1 / Math.tan(60 * Math.PI / 180)); + return { + innerOpposite, + innerHypotenuse, + }; +} + +function getFillColor(props) { + const { fill, point } = props; + + return fill instanceof Function ? fill(point.datum) : fill; +} + +function getStrokeColor(props) { + const { stroke, point } = props; + + return stroke instanceof Function ? stroke(point.datum) : stroke; +} + +function getRotationInDegrees(props, point) { + const { direction } = props; + const directionVal = direction instanceof Function ? direction(point.datum) : direction; + if (directionVal === "hide") { + return null; + } + + let rotate = 0; + switch (directionVal) { + case "bottom": + rotate = 180; + break; + case "left": + rotate = -90; + break; + case "right": + rotate = 90; + break; + } + return rotate; +} diff --git a/packages/react-financial-charts/src/series/VolumeProfileSeries.tsx b/packages/react-financial-charts/src/series/VolumeProfileSeries.tsx new file mode 100644 index 000000000..5fc3c29cb --- /dev/null +++ b/packages/react-financial-charts/src/series/VolumeProfileSeries.tsx @@ -0,0 +1,237 @@ +import { ascending, descending, histogram as d3Histogram, max, merge, sum, zip } from "d3-array"; +import { nest } from "d3-collection"; +import { scaleLinear } from "d3-scale"; +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { getAxisCanvas } from "../GenericComponent"; + +import { accumulatingWindow, functor, head, hexToRGBA, identity, last } from "../utils"; + +interface VolumeProfileSeriesProps { + className?: string; + opacity?: number; + showSessionBackground?: boolean; + sessionBackGround?: string; + sessionBackGroundOpacity?: number; +} + +export class VolumeProfileSeries extends React.Component { + + public static defaultProps = { + className: "line ", + bins: 20, + opacity: 0.5, + maxProfileWidthPercent: 50, + fill: ({ type }) => type === "up" ? "#6BA583" : "#FF0000", + stroke: "#FFFFFF", + showSessionBackground: false, + sessionBackGround: "#4682B4", + sessionBackGroundOpacity: 0.3, + source: (d) => d.close, + volume: (d) => d.volume, + absoluteChange: (d) => d.absoluteChange, + bySession: false, + sessionStart: ({ d, i, plotData }) => i > 0 && plotData[i - 1].date.getMonth() !== d.date.getMonth(), + orient: "left", + partialStartOK: true, + partialEndOK: true, + }; + + public render() { + return ( + + ); + } + + private readonly drawOnCanvas = (ctx, moreProps) => { + const { xAccessor, width } = moreProps; + const { rects, sessionBg } = this.helper(this.props, moreProps, xAccessor, width); + + this.drawOnCanvasContext(ctx, this.props, rects, sessionBg); + } + + private readonly renderSVG = (moreProps) => { + const { className, opacity } = this.props; + const { showSessionBackground, sessionBackGround, sessionBackGroundOpacity } = this.props; + + const { xAccessor, width } = moreProps; + const { rects, sessionBg } = this.helper(this.props, moreProps, xAccessor, width); + + const sessionBgSvg = showSessionBackground + ? sessionBg.map((d, idx) => ) + : null; + + return ( + + {sessionBgSvg} + {rects.map((d, i) => + + + )} + + ); + } + + private readonly drawOnCanvasContext = (ctx, props, rects, sessionBg) => { + const { opacity, sessionBackGround, sessionBackGroundOpacity, showSessionBackground } = props; + + if (showSessionBackground) { + ctx.fillStyle = hexToRGBA(sessionBackGround, sessionBackGroundOpacity); + + sessionBg.forEach((each) => { + const { x, y, height, width } = each; + + ctx.beginPath(); + ctx.rect(x, y, width, height); + ctx.closePath(); + ctx.fill(); + }); + } + + rects.forEach((each) => { + const { x, y, height, w1, w2, stroke1, stroke2, fill1, fill2 } = each; + + if (w1 > 0) { + ctx.fillStyle = hexToRGBA(fill1, opacity); + if (stroke1 !== "none") { ctx.strokeStyle = stroke1; } + + ctx.beginPath(); + ctx.rect(x, y, w1, height); + ctx.closePath(); + ctx.fill(); + + if (stroke1 !== "none") { ctx.stroke(); } + } + + if (w2 > 0) { + ctx.fillStyle = hexToRGBA(fill2, opacity); + if (stroke2 !== "none") { ctx.strokeStyle = stroke2; } + + ctx.beginPath(); + ctx.rect(x + w1, y, w2, height); + ctx.closePath(); + ctx.fill(); + + if (stroke2 !== "none") { ctx.stroke(); } + } + }); + } + + private readonly helper = (props, moreProps, xAccessor, width) => { + const { xScale: realXScale, chartConfig: { yScale }, plotData } = moreProps; + + const { sessionStart, bySession, partialStartOK, partialEndOK } = props; + + const { bins, maxProfileWidthPercent, source, volume, absoluteChange, orient, fill, stroke } = props; + + const sessionBuilder = accumulatingWindow() + .discardTillStart(!partialStartOK) + // @ts-ignore + .discardTillEnd(!partialEndOK) + .accumulateTill((d, i) => { + return sessionStart({ d, i, ...moreProps }); + }) + .accumulator(identity); + + const dx = plotData.length > 1 ? realXScale(xAccessor(plotData[1])) - realXScale(xAccessor(head(plotData))) : 0; + + const sessions = bySession ? sessionBuilder(plotData) : [plotData]; + + const allRects = sessions.map((session) => { + + const begin = bySession ? realXScale(xAccessor(head(session))) : 0; + const finish = bySession ? realXScale(xAccessor(last(session))) : width; + const sessionWidth = finish - begin + dx; + + const histogram2 = d3Histogram() + .value(source) + .thresholds(bins); + + const rollup = nest() + .key((d) => d.direction) + .sortKeys(orient === "right" ? descending : ascending) + .rollup((leaves) => sum(leaves, (d) => d.volume)); + + const values = histogram2(session); + + const volumeInBins = values + .map((arr) => arr.map((d) => { + return absoluteChange(d) > 0 ? { direction: "up", volume: volume(d) } : { direction: "down", volume: volume(d) }; + })) + .map((arr) => rollup.entries(arr)); + + const volumeValues = volumeInBins + .map((each) => sum(each.map((d) => d.value))); + + const base = (xScaleD) => head(xScaleD.range()); + + const [start, end] = orient === "right" + ? [begin, begin + sessionWidth * maxProfileWidthPercent / 100] + : [finish, finish - sessionWidth * (100 - maxProfileWidthPercent) / 100]; + + const xScale = scaleLinear() + .domain([0, max(volumeValues)]) + .range([start, end]); + + const totalVolumes = volumeInBins.map((volumes) => { + + const totalVolume = sum(volumes, (d) => d.value); + const totalVolumeX = xScale(totalVolume); + const widthLocal = base(xScale) - totalVolumeX; + const x = widthLocal < 0 ? totalVolumeX + widthLocal : totalVolumeX; + + const ws = volumes.map((d) => { + return { + type: d.key, + width: d.value * Math.abs(widthLocal) / totalVolume, + }; + }); + + return { x, ws, totalVolumeX }; + }); + + const rects = zip(values, totalVolumes) + .map(([d, { x, ws }]) => { + const w1 = ws[0] || { type: "up", width: 0 }; + const w2 = ws[1] || { type: "down", width: 0 }; + + return { + y: yScale(d.x1), + height: yScale(d.x1) - yScale(d.x0), + x, + width, + w1: w1.width, + w2: w2.width, + stroke1: functor(stroke)(w1), + stroke2: functor(stroke)(w2), + fill1: functor(fill)(w1), + fill2: functor(fill)(w2), + }; + }); + + const sessionBg = { + x: begin, + y: last(rects).y, + height: head(rects).y - last(rects).y + head(rects).height, + width: sessionWidth, + }; + + return { rects, sessionBg }; + }); + + return { + rects: merge(allRects.map((d) => d.rects)), + sessionBg: allRects.map((d) => d.sessionBg), + }; + } +} diff --git a/packages/react-financial-charts/src/series/index.ts b/packages/react-financial-charts/src/series/index.ts new file mode 100644 index 000000000..db18712ca --- /dev/null +++ b/packages/react-financial-charts/src/series/index.ts @@ -0,0 +1,24 @@ +export * from "./AreaSeries"; +export * from "./AlternatingFillAreaSeries"; +export * from "./AreaOnlySeries"; +export * from "./CircleMarker"; +export * from "./TriangleMarker"; +export * from "./SquareMarker"; +export * from "./LineSeries"; +export * from "./CandlestickSeries"; +export * from "./OHLCSeries"; +export * from "./BarSeries"; +export { StackedBarSeries } from "./StackedBarSeries"; +export * from "./GroupedBarSeries"; +export * from "./KagiSeries"; +export * from "./PointAndFigureSeries"; +export * from "./RenkoSeries"; +export * from "./MACDSeries"; +export * from "./BollingerSeries"; +export * from "./RSISeries"; +export * from "./StochasticSeries"; +export * from "./ElderRaySeries"; +export * from "./VolumeProfileSeries"; +export * from "./ScatterSeries"; +export * from "./StraightLine"; +export * from "./SARSeries"; diff --git a/packages/react-financial-charts/src/tooltip/BollingerBandTooltip.tsx b/packages/react-financial-charts/src/tooltip/BollingerBandTooltip.tsx new file mode 100644 index 000000000..2432f0072 --- /dev/null +++ b/packages/react-financial-charts/src/tooltip/BollingerBandTooltip.tsx @@ -0,0 +1,87 @@ +import { format } from "d3-format"; +import * as React from "react"; +import GenericChartComponent from "../GenericChartComponent"; +import { default as defaultDisplayValuesFor } from "./displayValuesFor"; + +import { functor, isDefined } from "../utils"; +import { ToolTipText } from "./ToolTipText"; +import { ToolTipTSpanLabel } from "./ToolTipTSpanLabel"; + +interface BollingerBandTooltipProps { + className?: string; + yAccessor: any; // func + displayValuesFor?: any; // func + displayFormat: any; // func + origin: number[]; + onClick?: any; // func + options: { + sourcePath: string; + windowSize: number; + multiplier: number; + movingAverageType: string; + }; + textFill?: string; + labelFill?: string; + fontFamily?: string; + fontSize?: number; +} + +export class BollingerBandTooltip extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-tooltip react-stockcharts-bollingerband-tooltip", + displayFormat: format(".2f"), + displayValuesFor: defaultDisplayValuesFor, + origin: [0, 10], + }; + + public render() { + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + const { onClick, displayFormat, yAccessor, options, textFill, labelFill } = this.props; + const { displayValuesFor } = this.props; + + const { chartConfig: { width, height } } = moreProps; + + const currentItem = displayValuesFor(this.props, moreProps); + + let top; + let middle; + let bottom; + top = middle = bottom = "n/a"; + + if (isDefined(currentItem) + && isDefined(yAccessor(currentItem))) { + const item = yAccessor(currentItem); + top = displayFormat(item.top); + middle = displayFormat(item.middle); + bottom = displayFormat(item.bottom); + } + + const { origin: originProp } = this.props; + const origin = functor(originProp); + const [x, y] = origin(width, height); + + const { sourcePath, windowSize, multiplier, movingAverageType } = options; + const tooltipLabel = `BB(${sourcePath}, ${windowSize}, ${multiplier}, ${movingAverageType}): `; + const tooltipValue = `${top}, ${middle}, ${bottom}`; + return ( + + + {tooltipLabel} + {tooltipValue} + + + ); + } +} diff --git a/packages/react-financial-charts/src/tooltip/GroupTooltip.tsx b/packages/react-financial-charts/src/tooltip/GroupTooltip.tsx new file mode 100644 index 000000000..e8489db27 --- /dev/null +++ b/packages/react-financial-charts/src/tooltip/GroupTooltip.tsx @@ -0,0 +1,261 @@ +import { format } from "d3-format"; +import * as React from "react"; +import GenericChartComponent from "../GenericChartComponent"; +import { default as defaultDisplayValuesFor } from "./displayValuesFor"; +import { ToolTipText } from "./ToolTipText"; +import { ToolTipTSpanLabel } from "./ToolTipTSpanLabel"; + +type layouts = + "horizontal" | + "horizontalRows" | + "horizontalInline" | + "vertical" | + "verticalRows"; + +interface SingleTooltipProps { + origin: number[]; + yLabel: string; + yValue: string; + onClick?: ((details: any, event: React.MouseEvent) => void); + fontFamily?: string; + labelFill: string; + valueFill: string; + fontSize?: number; + withShape: boolean; + forChart: number | string; + options: any; + layout: layouts; +} + +export class SingleTooltip extends React.Component { + + public static defaultProps = { + labelFill: "#4682B4", + valueFill: "#000000", + withShape: false, + }; + + /* + * Renders the value next to the label. + */ + public renderValueNextToLabel() { + const { origin, yLabel, yValue, labelFill, valueFill, withShape, fontSize, fontFamily } = this.props; + + return ( + + {withShape ? : null} + + {yLabel}: + {yValue} + + + ); + } + + /* + * Renders the value beneath the label. + */ + public renderValueBeneathToLabel() { + const { origin, yLabel, yValue, labelFill, valueFill, withShape, fontSize, fontFamily } = this.props; + + return ( + + {withShape ? : null} + + {yLabel} + {yValue} + + + ); + } + + /* + * Renders the value next to the label. + * The parent component must have a "text"-element. + */ + public renderInline() { + const { yLabel, yValue, labelFill, valueFill, fontSize, fontFamily } = this.props; + + return ( + + {yLabel}:  + {yValue}   + + ); + } + + public render() { + + const { layout } = this.props; + let comp: JSX.Element | null = null; + + switch (layout) { + case "horizontal": + comp = this.renderValueNextToLabel(); + break; + case "horizontalRows": + comp = this.renderValueBeneathToLabel(); + break; + case "horizontalInline": + comp = this.renderInline(); + break; + case "vertical": + comp = this.renderValueNextToLabel(); + break; + case "verticalRows": + comp = this.renderValueBeneathToLabel(); + break; + default: + comp = this.renderValueNextToLabel(); + } + + return comp; + } + + private readonly handleClick = (e: React.MouseEvent) => { + const { onClick, forChart, options } = this.props; + + if (onClick !== undefined) { + onClick({ chartId: forChart, ...options }, e); + } + } +} + +interface GroupTooltipProps { + className?: string; + layout: layouts; + position: "topRight" | "bottomLeft" | "bottomRight"; + displayFormat: any; // func + origin: number[]; + displayValuesFor: any; // func + onClick?: ((event: React.MouseEvent) => void); + fontFamily?: string; + fontSize?: number; + width?: number; // "width" only be used, if layout is "horizontal" or "horizontalRows". + verticalSize?: number; // "verticalSize" only be used, if layout is "vertical", "verticalRows". + options: Array<{ + labelFill?: string; + yLabel: string | any; // func + yAccessor: any; + valueFill?: string; + withShape?: boolean; + }>; + yAccessor: any; // func + labelFill?: string; + valueFill?: string; + withShape: boolean; // "withShape" is ignored, if layout is "horizontalInline" or "vertical". +} + +// tslint:disable-next-line: max-classes-per-file +export class GroupTooltip extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-tooltip react-stockcharts-group-tooltip", + layout: "horizontal", + displayFormat: format(".2f"), + displayValuesFor: defaultDisplayValuesFor, + origin: [0, 0], + width: 60, + verticalSize: 13, + }; + + public getPosition(moreProps) { + const { position } = this.props; + const { height, width } = moreProps.chartConfig; + + const dx = 20; + const dy = 40; + let textAnchor: string | undefined; + let xyPos: Array | null = null; + + if (position !== undefined) { + switch (position) { + case "topRight": + xyPos = [width - dx, null]; + textAnchor = "end"; + break; + case "bottomLeft": + xyPos = [null, height - dy]; + break; + case "bottomRight": + xyPos = [width - dx, height - dy]; + textAnchor = "end"; + break; + default: + xyPos = [null, null]; + } + } else { + xyPos = [null, null]; + } + + return { xyPos, textAnchor }; + } + + public render() { + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + + const { displayValuesFor } = this.props; + const { chartId } = moreProps; + + const { className, onClick, width = 60, verticalSize = 13, fontFamily, fontSize, layout } = this.props; + const { origin, displayFormat, options } = this.props; + const currentItem = displayValuesFor(this.props, moreProps); + const { xyPos, textAnchor } = this.getPosition(moreProps); + + const xPos = xyPos != null && xyPos[0] != null ? xyPos[0] : origin[0]; + const yPos = xyPos != null && xyPos[1] != null ? xyPos[1] : origin[1]; + + const singleTooltip = options.map((each, idx) => { + + const yValue = currentItem && each.yAccessor(currentItem); + const yDisplayValue = yValue ? displayFormat(yValue) : "n/a"; + + const orig = () => { + if (layout === "horizontal" || layout === "horizontalRows") { + return [width * idx, 0]; + } + if (layout === "vertical") { + return [0, verticalSize * idx]; + } + if (layout === "verticalRows") { + return [0, verticalSize * 2.3 * idx]; + } + return [0, 0]; + }; + + return ; + }); + + return ( + + {layout === "horizontalInline" + ? {singleTooltip} + : singleTooltip + } + + ); + } +} diff --git a/packages/react-financial-charts/src/tooltip/HoverTooltip.tsx b/packages/react-financial-charts/src/tooltip/HoverTooltip.tsx new file mode 100644 index 000000000..560153b94 --- /dev/null +++ b/packages/react-financial-charts/src/tooltip/HoverTooltip.tsx @@ -0,0 +1,283 @@ +import { sum } from "d3-array"; +import * as PropTypes from "prop-types"; +import * as React from "react"; +import GenericComponent from "../GenericComponent"; + +import { first, hexToRGBA, isDefined, isNotDefined, last } from "../utils"; + +interface HoverTooltipProps { + chartId?: number | string; + yAccessor?: any; // func + tooltipSVG?: any; // func + backgroundShapeSVG?: any; // func + bgwidth?: number; + bgheight?: number; + bgFill: string; + bgOpacity: number; + tooltipContent: any; // func + origin: number[] | any; // func + fontFamily?: string; + fontSize?: number; +} + +class HoverTooltip extends React.Component { + + public static defaultProps = { + tooltipSVG, + tooltipCanvas, + origin, + fill: "#D4E2FD", + bgFill: "#D4E2FD", + bgOpacity: 0.5, + stroke: "#9B9BFF", + fontFill: "#000000", + opacity: 0.8, + backgroundShapeSVG, + backgroundShapeCanvas, + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 12, + }; + + public static contextTypes = { + margin: PropTypes.object.isRequired, + ratio: PropTypes.number.isRequired, + }; + + public render() { + return ; + } + + private readonly renderSVG = (moreProps) => { + + const pointer = helper(this.props, moreProps); + + if (pointer === undefined) { + return null; + } + + const { bgFill, bgOpacity, backgroundShapeSVG, tooltipSVG } = this.props; + const { bgheight, bgwidth } = this.props; + const { height } = moreProps; + + const { x, y, content, centerX, pointWidth, bgSize } = pointer; + + const bgShape = isDefined(bgwidth) && isDefined(bgheight) + ? { width: bgwidth, height: bgheight } + : bgSize; + + return ( + + + + {backgroundShapeSVG(this.props, bgShape)} + {tooltipSVG(this.props, content)} + + + ); + } + private readonly drawOnCanvas = (ctx, moreProps) => { + const pointer = helper(this.props, moreProps, ctx); + const { height } = moreProps; + + if (isNotDefined(pointer)) { + return null; + } + + drawOnCanvas(ctx, this.props, this.context, pointer, height); + } +} + +const PADDING = 5; +const X = 10; +const Y = 10; + +function backgroundShapeSVG({ fill, stroke, opacity }, { height, width }) { + return ; +} + +function tooltipSVG({ fontFamily, fontSize, fontFill }, content) { + const tspans: any[] = []; + const startY = Y + fontSize * 0.9; + + for (let i = 0; i < content.y.length; i++) { + const y = content.y[i]; + const textY = startY + (fontSize * (i + 1)); + + tspans.push({y.label}); + tspans.push(: ); + tspans.push({y.value}); + } + return + {content.x} + {tspans} + ; +} + +function backgroundShapeCanvas(props, { width, height }, ctx) { + const { fill, stroke, opacity } = props; + + ctx.fillStyle = hexToRGBA(fill, opacity); + ctx.strokeStyle = stroke; + ctx.beginPath(); + ctx.rect(0, 0, width, height); + ctx.fill(); + ctx.stroke(); +} + +function tooltipCanvas({ fontFamily, fontSize, fontFill }, content, ctx) { + const startY = Y + fontSize * 0.9; + ctx.font = `${fontSize}px ${fontFamily}`; + ctx.fillStyle = fontFill; + ctx.textAlign = "left"; + ctx.fillText(content.x, X, startY); + + for (let i = 0; i < content.y.length; i++) { + const y = content.y[i]; + const textY = startY + (fontSize * (i + 1)); + ctx.fillStyle = y.stroke || fontFill; + ctx.fillText(y.label, X, textY); + + ctx.fillStyle = fontFill; + ctx.fillText(": " + y.value, X + ctx.measureText(y.label).width, textY); + } +} + +function drawOnCanvas(ctx, props, context, pointer, height) { + + const { margin, ratio } = context; + const { bgFill, bgOpacity } = props; + const { backgroundShapeCanvas, tooltipCanvas } = props; + + const originX = 0.5 * ratio + margin.left; + const originY = 0.5 * ratio + margin.top; + + ctx.save(); + + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.scale(ratio, ratio); + + ctx.translate(originX, originY); + + const { x, y, content, centerX, pointWidth, bgSize } = pointer; + + ctx.fillStyle = hexToRGBA(bgFill, bgOpacity); + ctx.beginPath(); + ctx.rect(centerX - pointWidth / 2, 0, pointWidth, height); + ctx.fill(); + + ctx.translate(x, y); + backgroundShapeCanvas(props, bgSize, ctx); + tooltipCanvas(props, content, ctx); + + ctx.restore(); +} + +function calculateTooltipSize({ fontFamily, fontSize, fontFill }, content, ctx) { + if (isNotDefined(ctx)) { + const canvas = document.createElement("canvas"); + ctx = canvas.getContext("2d"); + } + + ctx.font = `${fontSize}px ${fontFamily}`; + ctx.fillStyle = fontFill; + ctx.textAlign = "left"; + + const measureText = (str) => ({ + width: ctx.measureText(str).width, + height: fontSize, + }); + + const { width, height } = content.y + .map(({ label, value }) => measureText(`${label}: ${value}`)) + // Sum all y and x sizes (begin with x label size) + .reduce((res, size) => sumSizes(res, size), measureText(String(content.x))) + ; + + return { + width: width + 2 * X, + height: height + 2 * Y, + }; +} + +function sumSizes(...sizes) { + return { + width: Math.max(...sizes.map((size) => size.width)), + height: sum(sizes, (d) => d.height), + }; +} + +function normalizeX(x, bgSize, pointWidth, width) { + // return x - bgSize.width - pointWidth / 2 - PADDING * 2 < 0 + return x < width / 2 + ? x + pointWidth / 2 + PADDING + : x - bgSize.width - pointWidth / 2 - PADDING; +} + +function normalizeY(y, bgSize) { + return y - bgSize.height <= 0 + ? y + PADDING + : y - bgSize.height - PADDING; +} + +function origin(props, moreProps, bgSize, pointWidth) { + const { chartId, yAccessor } = props; + const { mouseXY, xAccessor, currentItem, xScale, chartConfig, width } = moreProps; + + // @ts-ignore + let y = last(mouseXY); + + const xValue = xAccessor(currentItem); + let x = Math.round(xScale(xValue)); + + if (isDefined(chartId) && isDefined(yAccessor) + && isDefined(chartConfig) && isDefined(chartConfig.findIndex)) { + const yValue = yAccessor(currentItem); + const chartIndex = chartConfig.findIndex(x => x.id === chartId); + + y = Math.round(chartConfig[chartIndex].yScale(yValue)); + } + + x = normalizeX(x, bgSize, pointWidth, width); + y = normalizeY(y, bgSize); + + return [x, y]; +} + +function helper(props, moreProps, ctx?) { + const { show, xScale, currentItem, plotData } = moreProps; + const { origin, tooltipContent } = props; + const { xAccessor, displayXAccessor } = moreProps; + + if (!show || isNotDefined(currentItem)) { return; } + + const xValue = xAccessor(currentItem); + + if (!show || isNotDefined(xValue)) { return; } + + const content = tooltipContent({ currentItem, xAccessor: displayXAccessor }); + const centerX = xScale(xValue); + // @ts-ignore + const pointWidth = Math.abs(xScale(xAccessor(last(plotData))) - xScale(xAccessor(first(plotData)))) / (plotData.length - 1); + + const bgSize = calculateTooltipSize(props, content, ctx); + + const [x, y] = origin(props, moreProps, bgSize, pointWidth); + + return { x, y, content, centerX, pointWidth, bgSize }; +} + +export default HoverTooltip; diff --git a/packages/react-financial-charts/src/tooltip/MACDTooltip.tsx b/packages/react-financial-charts/src/tooltip/MACDTooltip.tsx new file mode 100644 index 000000000..ed49013b3 --- /dev/null +++ b/packages/react-financial-charts/src/tooltip/MACDTooltip.tsx @@ -0,0 +1,94 @@ +import { format } from "d3-format"; +import * as React from "react"; + +import GenericChartComponent from "../GenericChartComponent"; +import { default as defaultDisplayValuesFor } from "./displayValuesFor"; + +import { functor } from "../utils"; +import { ToolTipText } from "./ToolTipText"; +import { ToolTipTSpanLabel } from "./ToolTipTSpanLabel"; + +interface MACDTooltipProps { + origin: number[] | any; // func + className?: string; + fontFamily?: string; + fontSize?: number; + labelFill?: string; + yAccessor: any; // func + options: { + slow: number; + fast: number; + signal: number; + }; + appearance: { + stroke: { + macd: string, + signal: string, + }, + fill: { + divergence: string, + }, + }; + displayFormat: any; // func + displayValuesFor: any; // func + onClick?: any; // func +} + +export class MACDTooltip extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-tooltip", + displayFormat: format(".2f"), + displayValuesFor: defaultDisplayValuesFor, + origin: [0, 0], + }; + + public render() { + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + const { onClick, fontFamily, fontSize, displayFormat, className } = this.props; + const { yAccessor, options, appearance, labelFill } = this.props; + const { displayValuesFor } = this.props; + + const { chartConfig: { width, height } } = moreProps; + + const currentItem = displayValuesFor(this.props, moreProps); + const macdValue = currentItem && yAccessor(currentItem); + + const macd = (macdValue && macdValue.macd && displayFormat(macdValue.macd)) || "n/a"; + const signal = (macdValue && macdValue.signal && displayFormat(macdValue.signal)) || "n/a"; + const divergence = (macdValue && macdValue.divergence && displayFormat(macdValue.divergence)) || "n/a"; + + const { origin: originProp } = this.props; + const origin = functor(originProp); + const [x, y] = origin(width, height); + + return ( + + + MACD ( + {options.slow} + , + {options.fast} + ): + {macd} + Signal ( + {options.signal} + ): + {signal} + Divergence: + {divergence} + + + ); + } +} diff --git a/packages/react-financial-charts/src/tooltip/MovingAverageTooltip.tsx b/packages/react-financial-charts/src/tooltip/MovingAverageTooltip.tsx new file mode 100644 index 000000000..cdd7b3f7f --- /dev/null +++ b/packages/react-financial-charts/src/tooltip/MovingAverageTooltip.tsx @@ -0,0 +1,146 @@ + +import { format } from "d3-format"; +import * as React from "react"; +import GenericChartComponent from "../GenericChartComponent"; +import { default as defaultDisplayValuesFor } from "./displayValuesFor"; + +import { ToolTipText } from "./ToolTipText"; +import { ToolTipTSpanLabel } from "./ToolTipTSpanLabel"; + +import { functor } from "../utils"; + +interface SingleMAToolTipProps { + origin: number[]; + color: string; + displayName: string; + value: string; + onClick?: ((details: any, event: React.MouseEvent) => void); + fontFamily?: string; + textFill?: string; + labelFill?: string; + fontSize?: number; + forChart: number | string; + options: any; +} + +export class SingleMAToolTip extends React.Component { + + public render() { + const { textFill, labelFill } = this.props; + const translate = "translate(" + this.props.origin[0] + ", " + this.props.origin[1] + ")"; + return ( + + + + {this.props.displayName} + {this.props.value} + + + + ); + } + + private readonly onClick = (event: React.MouseEvent) => { + const { onClick, forChart, options } = this.props; + + if (onClick !== undefined) { + onClick({ chartId: forChart, ...options }, event); + } + } +} + +interface MovingAverageTooltipProps { + className?: string; + displayFormat: any; // func + origin: number[]; + displayValuesFor?: any; // func + onClick?: ((event: React.MouseEvent) => void); + textFill?: string; + labelFill?: string; + fontFamily?: string; + fontSize?: number; + width?: number; + options: Array<{ + yAccessor: any; // func + type: string; + stroke: string; + windowSize: number; + echo: any; + }>; +} + +// tslint:disable-next-line: max-classes-per-file +export class MovingAverageTooltip extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-tooltip react-stockcharts-moving-average-tooltip", + displayFormat: format(".2f"), + displayValuesFor: defaultDisplayValuesFor, + origin: [0, 10], + width: 65, + }; + + public render() { + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + const { displayValuesFor } = this.props; + + const { chartId } = moreProps; + const { chartConfig } = moreProps; + + const { className, onClick, width = 65, fontFamily, fontSize, textFill, labelFill } = this.props; + const { origin: originProp, displayFormat, options } = this.props; + const { chartConfig: { height } } = moreProps; + + const currentItem = displayValuesFor(this.props, moreProps); + const config = chartConfig; + + const origin = functor(originProp); + const [x, y] = origin(width, height); + const [ox, oy] = config.origin; + + return ( + + {options + .map((each, idx) => { + const yValue = currentItem && each.yAccessor(currentItem); + + const tooltipLabel = `${each.type} (${each.windowSize})`; + const yDisplayValue = yValue ? displayFormat(yValue) : "n/a"; + return ( + + ); + })} + + ); + } +} diff --git a/packages/react-financial-charts/src/tooltip/OHLCTooltip.tsx b/packages/react-financial-charts/src/tooltip/OHLCTooltip.tsx new file mode 100644 index 000000000..a6c54057f --- /dev/null +++ b/packages/react-financial-charts/src/tooltip/OHLCTooltip.tsx @@ -0,0 +1,183 @@ + +import { format } from "d3-format"; +import { timeFormat } from "d3-time-format"; +import * as React from "react"; +import GenericChartComponent from "../GenericChartComponent"; +import { default as defaultDisplayValuesFor } from "./displayValuesFor"; + +import { functor, isDefined } from "../utils"; +import { ToolTipText } from "./ToolTipText"; +import { ToolTipTSpanLabel } from "./ToolTipTSpanLabel"; + +const displayTextsDefault = { + d: "Date: ", + o: " O: ", + h: " H: ", + l: " L: ", + c: " C: ", + v: " Vol: ", + na: "n/a", +}; + +const defaultDisplay = (props, _, itemsToDisplay) => { + + const { + className, + textFill, + labelFill, + onClick, + fontFamily, + fontSize, + displayTexts, + } = props; + + const { + displayDate, + open, + high, + low, + close, + volume, + x, + y, + } = itemsToDisplay; + + return ( + + + + {displayTexts.d} + + {displayDate} + {displayTexts.o} + {open} + {displayTexts.h} + {high} + {displayTexts.l} + {low} + {displayTexts.c} + {close} + {displayTexts.v} + {volume} + + + ); +}; + +interface OHLCTooltipProps { + className?: string; + accessor?: any; // func + xDisplayFormat?: any; // func + children?: any; // func + volumeFormat?: any; // func + percentFormat?: any; // func + ohlcFormat?: any; // func + origin?: number[] | any; // func + fontFamily?: string; + fontSize?: number; + onClick?: ((event: React.MouseEvent) => void); + displayValuesFor?: any; // func + textFill?: string; + labelFill?: string; + displayTexts?: any; +} + +export class OHLCTooltip extends React.Component { + + public static defaultProps = { + accessor: (d) => { + return { + date: d.date, + open: d.open, + high: d.high, + low: d.low, + close: d.close, + volume: d.volume, + }; + }, + xDisplayFormat: timeFormat("%Y-%m-%d"), + volumeFormat: format(".4s"), + percentFormat: format(".2%"), + ohlcFormat: format(".2f"), + displayValuesFor: defaultDisplayValuesFor, + origin: [0, 0], + children: defaultDisplay, + displayTexts: displayTextsDefault, + }; + + public render() { + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + const { displayValuesFor } = this.props; + const { + xDisplayFormat, + accessor, + volumeFormat, + ohlcFormat, + percentFormat, + displayTexts, + } = this.props; + + const { chartConfig: { width, height } } = moreProps; + const { displayXAccessor } = moreProps; + + const currentItem = displayValuesFor(this.props, moreProps); + + let displayDate; + let open; + let high; + let low; + let close; + let volume; + let percent; + displayDate = open = high = low = close = volume = percent = displayTexts.na; + + if (isDefined(currentItem) && isDefined(accessor(currentItem))) { + const item = accessor(currentItem); + volume = isDefined(item.volume) ? volumeFormat(item.volume) : displayTexts.na; + + displayDate = xDisplayFormat(displayXAccessor(item)); + open = ohlcFormat(item.open); + high = ohlcFormat(item.high); + low = ohlcFormat(item.low); + close = ohlcFormat(item.close); + percent = percentFormat((item.close - item.open) / item.open); + } + + const { origin: originProp } = this.props; + const origin = functor(originProp); + const [x, y] = origin(width, height); + + const itemsToDisplay = { + displayDate, + open, + high, + low, + close, + percent, + volume, + x, + y, + }; + return this.props.children(this.props, moreProps, itemsToDisplay); + } +} diff --git a/packages/react-financial-charts/src/tooltip/RSITooltip.tsx b/packages/react-financial-charts/src/tooltip/RSITooltip.tsx new file mode 100644 index 000000000..884e86e93 --- /dev/null +++ b/packages/react-financial-charts/src/tooltip/RSITooltip.tsx @@ -0,0 +1,74 @@ +import { format } from "d3-format"; +import * as React from "react"; +import GenericChartComponent from "../GenericChartComponent"; +import { default as defaultDisplayValuesFor } from "./displayValuesFor"; + +import { functor, isDefined } from "../utils"; +import { ToolTipText } from "./ToolTipText"; +import { ToolTipTSpanLabel } from "./ToolTipTSpanLabel"; + +interface RSITooltipProps { + origin: number[] | any; // func + options: { + windowSize: number; + }; + className?: string; + fontFamily?: string; + fontSize?: number; + onClick?: ((event: React.MouseEvent) => void); + yAccessor: any; // func + displayFormat: any; // func + displayValuesFor?: any; // func + textFill?: string; + labelFill?: string; +} + +export class RSITooltip extends React.Component { + + public static defaultProps = { + displayFormat: format(".2f"), + displayValuesFor: defaultDisplayValuesFor, + origin: [0, 0], + className: "react-stockcharts-tooltip", + }; + + public render() { + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + const { onClick, fontFamily, fontSize, yAccessor, displayFormat, className } = this.props; + const { options, labelFill, textFill } = this.props; + const { displayValuesFor } = this.props; + + const { chartConfig: { width, height } } = moreProps; + + const currentItem = displayValuesFor(this.props, moreProps); + const rsi = isDefined(currentItem) && yAccessor(currentItem); + const value = (rsi && displayFormat(rsi)) || "n/a"; + + const { origin: originProp } = this.props; + const origin = functor(originProp); + const [x, y] = origin(width, height); + + const tooltipLabel = `RSI (${options.windowSize}): `; + return ( + + + {tooltipLabel} + {value} + + + ); + } +} diff --git a/packages/react-financial-charts/src/tooltip/SingleValueTooltip.tsx b/packages/react-financial-charts/src/tooltip/SingleValueTooltip.tsx new file mode 100644 index 000000000..6c668a874 --- /dev/null +++ b/packages/react-financial-charts/src/tooltip/SingleValueTooltip.tsx @@ -0,0 +1,86 @@ + +import { format } from "d3-format"; +import * as React from "react"; +import GenericChartComponent from "../GenericChartComponent"; +import { default as defaultDisplayValuesFor } from "./displayValuesFor"; + +import { functor, identity, isDefined, noop } from "../utils"; +import { ToolTipText } from "./ToolTipText"; +import { ToolTipTSpanLabel } from "./ToolTipTSpanLabel"; + +interface SingleValueTooltipProps { + xDisplayFormat?: any; // func + yDisplayFormat: any; // func + xLabel?: string; + yLabel: string; + labelFill?: string; + valueFill?: string; + origin?: number[] | any; // func + className?: string; + fontFamily?: string; + fontSize?: number; + onClick?: ((event: React.MouseEvent) => void); + displayValuesFor?: any; // func + xAccessor?: any; // func + yAccessor?: any; // func +} + +export class SingleValueTooltip extends React.Component { + + public static defaultProps = { + origin: [0, 0], + labelFill: "#4682B4", + valueFill: "#000000", + yDisplayFormat: format(".2f"), + displayValuesFor: defaultDisplayValuesFor, + xAccessor: noop, + yAccessor: identity, + className: "react-stockcharts-tooltip", + }; + + public render() { + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + + const { onClick, fontFamily, fontSize, labelFill, valueFill, className } = this.props; + const { xDisplayFormat, yDisplayFormat, xLabel, yLabel, xAccessor, yAccessor } = this.props; + const { displayValuesFor } = this.props; + + const { chartConfig: { width, height } } = moreProps; + const currentItem = displayValuesFor(this.props, moreProps); + + const xDisplayValue = isDefined(currentItem) && isDefined(xAccessor(currentItem)) ? xDisplayFormat(xAccessor(currentItem)) : "n/a"; + const yDisplayValue = isDefined(currentItem) && isDefined(yAccessor(currentItem)) ? yDisplayFormat(yAccessor(currentItem)) : "n/a"; + + const { origin: originProp } = this.props; + const origin = functor(originProp); + const [x, y] = origin(width, height); + + return ( + + + {xLabel ? {`${xLabel}: `} : null} + {xLabel ? {`${xDisplayValue} `} : null} + + {`${yLabel}: `} + + + {yDisplayValue} + + + + ); + } +} diff --git a/packages/react-financial-charts/src/tooltip/StochasticTooltip.tsx b/packages/react-financial-charts/src/tooltip/StochasticTooltip.tsx new file mode 100644 index 000000000..763b040ec --- /dev/null +++ b/packages/react-financial-charts/src/tooltip/StochasticTooltip.tsx @@ -0,0 +1,87 @@ +import { format } from "d3-format"; +import * as React from "react"; +import GenericChartComponent from "../GenericChartComponent"; +import { default as defaultDisplayValuesFor } from "./displayValuesFor"; + +import { functor } from "../utils"; + +import { ToolTipText } from "./ToolTipText"; +import { ToolTipTSpanLabel } from "./ToolTipTSpanLabel"; + +interface StochasticTooltipProps { + origin: number[] | any; // func + className?: string; + fontFamily?: string; + fontSize?: number; + labelFill?: string; + onClick?: any; // func + yAccessor: any; // func + options: { + windowSize: number; + kWindowSize: number; + dWindowSize: number; + }; + appearance: { + stroke: { + dLine: string; + kLine: string; + }, + }; + displayFormat: any; // func + displayValuesFor?: any; // func + label: string; +} + +export class StochasticTooltip extends React.Component { + + public static defaultProps = { + displayFormat: format(".2f"), + displayValuesFor: defaultDisplayValuesFor, + origin: [0, 0], + className: "react-stockcharts-tooltip", + label: "STO", + }; + + public render() { + return ( + + ); + } + + private readonly renderSVG = (moreProps) => { + const { onClick, fontFamily, fontSize, yAccessor, displayFormat, label } = this.props; + const { className, options, appearance, labelFill } = this.props; + const { displayValuesFor } = this.props; + const { chartConfig: { width, height } } = moreProps; + + const currentItem = displayValuesFor(this.props, moreProps); + const { stroke } = appearance; + const stochastic = currentItem && yAccessor(currentItem); + + const K = (stochastic && stochastic.K && displayFormat(stochastic.K)) || "n/a"; + const D = (stochastic && stochastic.D && displayFormat(stochastic.D)) || "n/a"; + + const { origin: originProp } = this.props; + const origin = functor(originProp); + const [x, y] = origin(width, height); + + return ( + + + {`${label} %K(`} + {`${options.windowSize}, ${options.kWindowSize}`} + ): + {K} + %D ( + {options.dWindowSize} + ): + {D} + + + ); + } +} diff --git a/packages/react-financial-charts/src/tooltip/ToolTipTSpanLabel.tsx b/packages/react-financial-charts/src/tooltip/ToolTipTSpanLabel.tsx new file mode 100644 index 000000000..a31227b2e --- /dev/null +++ b/packages/react-financial-charts/src/tooltip/ToolTipTSpanLabel.tsx @@ -0,0 +1,24 @@ +import * as React from "react"; + +interface ToolTipTSpanLabelProps extends React.SVGProps { + readonly fill: string | undefined; +} + +export class ToolTipTSpanLabel extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-tooltip-label", + fill: "#4682B4", + }; + + public render() { + + const { children, ...rest } = this.props; + + return ( + + {children} + + ); + } +} diff --git a/packages/react-financial-charts/src/tooltip/ToolTipText.tsx b/packages/react-financial-charts/src/tooltip/ToolTipText.tsx new file mode 100644 index 000000000..5699ce942 --- /dev/null +++ b/packages/react-financial-charts/src/tooltip/ToolTipText.tsx @@ -0,0 +1,27 @@ +import * as React from "react"; + +interface ToolTipTextProps extends React.SVGProps { +} + +export class ToolTipText extends React.Component { + + public static defaultProps = { + className: "react-stockcharts-tooltip", + fontFamily: "Helvetica Neue, Helvetica, Arial, sans-serif", + fontSize: 11, + }; + + public render() { + + const { children, fontFamily, fontSize, ...rest } = this.props; + + return ( + + {children} + + ); + } +} diff --git a/packages/react-financial-charts/src/tooltip/displayValuesFor.ts b/packages/react-financial-charts/src/tooltip/displayValuesFor.ts new file mode 100644 index 000000000..c06b73689 --- /dev/null +++ b/packages/react-financial-charts/src/tooltip/displayValuesFor.ts @@ -0,0 +1,4 @@ +export default function displayValuesFor(props, moreProps) { + const { currentItem } = moreProps; + return currentItem; +} diff --git a/packages/react-financial-charts/src/tooltip/index.ts b/packages/react-financial-charts/src/tooltip/index.ts new file mode 100644 index 000000000..43291452b --- /dev/null +++ b/packages/react-financial-charts/src/tooltip/index.ts @@ -0,0 +1,11 @@ +export * from "./MACDTooltip"; +export * from "./OHLCTooltip"; +export * from "./SingleValueTooltip"; +export * from "./MovingAverageTooltip"; +export * from "./BollingerBandTooltip"; +export * from "./RSITooltip"; +export * from "./StochasticTooltip"; +export { default as HoverTooltip } from "./HoverTooltip"; +export * from "./ToolTipText"; +export * from "./ToolTipTSpanLabel"; +export * from "./GroupTooltip"; diff --git a/packages/react-financial-charts/src/utils/ChartDataUtil.ts b/packages/react-financial-charts/src/utils/ChartDataUtil.ts new file mode 100644 index 000000000..1ca4f692a --- /dev/null +++ b/packages/react-financial-charts/src/utils/ChartDataUtil.ts @@ -0,0 +1,266 @@ + +import { extent } from "d3-array"; +import { set } from "d3-collection"; +import flattenDeep from "lodash.flattendeep"; +import * as React from "react"; + +import { Chart } from "../Chart"; + +import { + find, + functor, + getClosestItem, + isDefined, + isNotDefined, + isObject, + last, + mapObject, + shallowEqual, + zipper, +} from "./index"; + +export function getChartOrigin(origin, contextWidth, contextHeight) { + const originCoordinates = typeof origin === "function" + ? origin(contextWidth, contextHeight) + : origin; + return originCoordinates; +} + +export function getDimensions({ width, height }, chartProps) { + + const chartHeight = (chartProps.height || height); + + return { + availableHeight: height, + width, + height: chartHeight, + }; +} + +function values(func) { + return (d) => { + const obj = func(d); + if (isObject(obj)) { + return mapObject(obj); + } + return obj; + }; +} + +function isArraySize2AndNumber(yExtentsProp) { + if (Array.isArray(yExtentsProp) && yExtentsProp.length === 2) { + const [a, b] = yExtentsProp; + return (typeof a === "number" && typeof b === "number"); + } + return false; +} + +export function getNewChartConfig(innerDimension, children, existingChartConfig = []) { + return React.Children.map(children, (each) => { + if (each && each.type.toString() === Chart.toString()) { + const chartProps = { + ...Chart.defaultProps, + ...each.props, + }; + const { + id, + origin, + padding, + yExtents: yExtentsProp, + yScale: yScaleProp, + flipYScale, + yExtentsCalculator, + } = chartProps; + + const yScale = yScaleProp.copy(); + const { + width, height, availableHeight, + } = getDimensions(innerDimension, chartProps); + + const { yPan } = chartProps; + let { yPanEnabled } = chartProps; + const yExtents = isDefined(yExtentsProp) + ? (Array.isArray(yExtentsProp) ? yExtentsProp : [yExtentsProp]).map(functor) + : undefined; + + const prevChartConfig = find(existingChartConfig, (d) => d.id === id); + + if (isArraySize2AndNumber(yExtentsProp)) { + if ( + isDefined(prevChartConfig) + && prevChartConfig.yPan + && prevChartConfig.yPanEnabled + && yPan + && yPanEnabled + && shallowEqual(prevChartConfig.originalYExtentsProp, yExtentsProp) + ) { + // console.log(prevChartConfig.originalYExtentsProp, yExtentsProp) + // console.log(prevChartConfig.yScale.domain()) + yScale.domain(prevChartConfig.yScale.domain()); + } else { + const [a, b] = yExtentsProp; + yScale.domain([a, b]); + } + } else if (isDefined(prevChartConfig) && prevChartConfig.yPanEnabled) { + if (isArraySize2AndNumber(prevChartConfig.originalYExtentsProp)) { + // do nothing + } else { + yScale.domain(prevChartConfig.yScale.domain()); + yPanEnabled = true; + } + } + + return { + id, + origin: functor(origin)(width, availableHeight), + padding, + originalYExtentsProp: yExtentsProp, + yExtents, + yExtentsCalculator, + flipYScale, + // yScale: setRange(yScale.copy(), height, padding, flipYScale), + yScale, + yPan, + yPanEnabled, + // mouseCoordinates, + width, + height, + }; + } + return undefined; + }).filter((each) => isDefined(each)); +} +export function getCurrentCharts(chartConfig, mouseXY) { + const currentCharts = chartConfig.filter((eachConfig) => { + const top = eachConfig.origin[1]; + const bottom = top + eachConfig.height; + return (mouseXY[1] > top && mouseXY[1] < bottom); + }).map((config) => config.id); + + return currentCharts; +} + +function setRange(scale, height, padding, flipYScale) { + if (scale.rangeRoundPoints || isNotDefined(scale.invert)) { + if (isNaN(padding)) { throw new Error("padding has to be a number for ordinal scale"); } + if (scale.rangeRoundPoints) { scale.rangeRoundPoints(flipYScale ? [0, height] : [height, 0], padding); } + if (scale.rangeRound) { scale.range(flipYScale ? [0, height] : [height, 0]).padding(padding); } + } else { + const { top, bottom } = isNaN(padding) + ? padding + : { top: padding, bottom: padding }; + + scale.range(flipYScale ? [top, height - bottom] : [height - bottom, top]); + } + return scale; +} + +function yDomainFromYExtents(yExtents, yScale, plotData) { + const yValues = yExtents.map((eachExtent) => + plotData.map(values(eachExtent))); + + const allYValues = flattenDeep(yValues); + // console.log(allYValues) + const realYDomain = (yScale.invert) + ? extent(allYValues) + : set(allYValues).values(); + + return realYDomain; +} + +export function getChartConfigWithUpdatedYScales( + chartConfig, + { plotData, xAccessor, displayXAccessor, fullData }, + xDomain, + dy, + chartsToPan, +) { + const yDomains = chartConfig + .map(({ yExtentsCalculator, yExtents, yScale }) => { + + const realYDomain = isDefined(yExtentsCalculator) + ? yExtentsCalculator({ plotData, xDomain, xAccessor, displayXAccessor, fullData }) + : yDomainFromYExtents(yExtents, yScale, plotData); + + // console.log("yScale.domain() ->", yScale.domain()) + + const yDomainDY = isDefined(dy) + ? yScale.range().map((each) => each - dy).map(yScale.invert) + : yScale.domain(); + return { + realYDomain, + yDomainDY, + prevYDomain: yScale.domain(), + }; + }); + + const combine = zipper() + .combine((config, { realYDomain, yDomainDY, prevYDomain }) => { + const { id, padding, height, yScale, yPan, flipYScale, yPanEnabled = false } = config; + + const another = isDefined(chartsToPan) + ? chartsToPan.indexOf(id) > -1 + : true; + const domain = yPan && yPanEnabled + ? another ? yDomainDY : prevYDomain + : realYDomain; + + // console.log(id, yPan, yPanEnabled, another); + // console.log(domain, realYDomain, prevYDomain); + const newYScale = setRange( + yScale.copy().domain(domain), height, padding, flipYScale, + ); + return { + ...config, + yScale: newYScale, + realYDomain, + }; + // return { ...config, yScale: yScale.copy().domain(domain).range([height - padding, padding]) }; + }); + + // @ts-ignore + const updatedChartConfig = combine(chartConfig, yDomains); + // console.error(yDomains, dy, chartsToPan, updatedChartConfig.map(d => d.yScale.domain())); + // console.log(updatedChartConfig.map(d => ({ id: d.id, domain: d.yScale.domain() }))) + + return updatedChartConfig; +} + +export function getCurrentItem(xScale, xAccessor, mouseXY, plotData) { + let xValue; + let item; + if (xScale.invert) { + xValue = xScale.invert(mouseXY[0]); + item = getClosestItem(plotData, xValue, xAccessor); + } else { + const dr = xScale + .range() + .map((d, idx) => ({ x: Math.abs(d - mouseXY[0]), idx })) + .reduce((a, b) => a.x < b.x ? a : b); + + item = isDefined(dr) ? plotData[dr.idx] : plotData[0]; + } + return item; +} + +export function getXValue(xScale, xAccessor, mouseXY, plotData) { + + let xValue; + let item; + if (xScale.invert) { + xValue = xScale.invert(mouseXY[0]); + if (xValue > xAccessor(last(plotData)) && xScale.value) { + return Math.round(xValue); + } else { + item = getClosestItem(plotData, xValue, xAccessor); + } + } else { + const dr = xScale + .range() + .map((d, idx) => ({ x: Math.abs(d - mouseXY[0]), idx })) + .reduce((a, b) => a.x < b.x ? a : b); + + item = isDefined(dr) ? plotData[dr.idx] : plotData[0]; + } + return xAccessor(item); +} diff --git a/packages/react-financial-charts/src/utils/PureComponent.tsx b/packages/react-financial-charts/src/utils/PureComponent.tsx new file mode 100644 index 000000000..65546f1de --- /dev/null +++ b/packages/react-financial-charts/src/utils/PureComponent.tsx @@ -0,0 +1,10 @@ +import * as React from "react"; +import shallowEqual from "./shallowEqual"; + +export class PureComponent extends React.Component { + public shouldComponentUpdate(nextProps, nextState, nextContext) { + return !shallowEqual(this.props, nextProps) + || !shallowEqual(this.state, nextState) + || !shallowEqual(this.context, nextContext); + } +} diff --git a/packages/react-financial-charts/src/utils/accumulatingWindow.ts b/packages/react-financial-charts/src/utils/accumulatingWindow.ts new file mode 100644 index 000000000..a618f66ad --- /dev/null +++ b/packages/react-financial-charts/src/utils/accumulatingWindow.ts @@ -0,0 +1,104 @@ +/* + +Taken from https://github.com/ScottLogic/d3fc/blob/master/src/indicator/algorithm/calculator/slidingWindow.js + +The MIT License (MIT) + +Copyright (c) 2014-2015 Scott Logic Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +import identity from "./identity"; +import { functor } from "./index"; +import noop from "./noop"; + +export default function () { + + let accumulateTill = functor(false); + let accumulator = noop; + let value = identity; + let discardTillStart = false; + let discardTillEnd = false; + + const accumulatingWindow = function (data) { + let accumulatedWindow: any[] | undefined = discardTillStart ? undefined : []; + const response: any[] = []; + let accumulatorIdx = 0; + let i = 0; + for (i = 0; i < data.length; i++) { + const d = data[i]; + if (accumulateTill(d, i, (accumulatedWindow || []))) { + if (accumulatedWindow && accumulatedWindow.length > 0) { + // @ts-ignore + response.push(accumulator(accumulatedWindow, i, accumulatorIdx++)); + } + + accumulatedWindow = [value(d)]; + } else if (accumulatedWindow) { + accumulatedWindow.push(value(d)); + } + } + + if (!discardTillEnd) { + // @ts-ignore + response.push(accumulator(accumulatedWindow, i, accumulatorIdx)); + } + + return response; + }; + + accumulatingWindow.accumulateTill = function (x) { + if (!arguments.length) { + return accumulateTill; + } + accumulateTill = functor(x); + return accumulatingWindow; + }; + accumulatingWindow.accumulator = function (x) { + if (!arguments.length) { + return accumulator; + } + accumulator = x; + return accumulatingWindow; + }; + accumulatingWindow.value = function (x) { + if (!arguments.length) { + return value; + } + value = x; + return accumulatingWindow; + }; + accumulatingWindow.discardTillStart = function (x) { + if (!arguments.length) { + return discardTillStart; + } + discardTillStart = x; + return accumulatingWindow; + }; + accumulatingWindow.discardTillEnd = function (x) { + if (!arguments.length) { + return discardTillEnd; + } + discardTillEnd = x; + return accumulatingWindow; + }; + return accumulatingWindow; +} diff --git a/packages/react-financial-charts/src/utils/barWidth.ts b/packages/react-financial-charts/src/utils/barWidth.ts new file mode 100644 index 000000000..d6879bd40 --- /dev/null +++ b/packages/react-financial-charts/src/utils/barWidth.ts @@ -0,0 +1,40 @@ +import { head } from "."; + +/** + * Bar width is based on the amount of items in the plot data and the distance between the first and last of those + * items. + * @param props the props passed to the series. + * @param moreProps an object holding the xScale, xAccessor and plotData. + * @return {number} the bar width. + */ +export const plotDataLengthBarWidth = (props: { widthRatio: number }, moreProps: { xScale: any; }): number => { + const { widthRatio } = props; + const { xScale } = moreProps; + + const [l, r] = xScale.range(); + + const totalWidth = Math.abs(r - l); + if (xScale.invert != null) { + const [dl, dr] = xScale.domain(); + const width = totalWidth / Math.abs(dl - dr); + return width * widthRatio; + } else { + const width = totalWidth / xScale.domain().length; + return width * widthRatio; + } +}; + +/** + * Generates a width function that calculates the bar width based on the given time interval. + * @param interval a d3-time time interval. + * @return {Function} the width function. + */ +export const timeIntervalBarWidth = (interval) => { + return function (props: { widthRatio: number }, moreProps: { xScale: any; xAccessor: any; plotData: any }) { + const { widthRatio } = props; + const { xScale, xAccessor, plotData } = moreProps; + + const first = xAccessor(head(plotData)); + return Math.abs(xScale(interval.offset(first, 1)) - xScale(first)) * widthRatio; + }; +}; diff --git a/packages/react-financial-charts/src/utils/identity.ts b/packages/react-financial-charts/src/utils/identity.ts new file mode 100644 index 000000000..d9aabe12a --- /dev/null +++ b/packages/react-financial-charts/src/utils/identity.ts @@ -0,0 +1 @@ +export default (d) => d; diff --git a/packages/react-financial-charts/src/utils/index.ts b/packages/react-financial-charts/src/utils/index.ts new file mode 100644 index 000000000..0e8db023f --- /dev/null +++ b/packages/react-financial-charts/src/utils/index.ts @@ -0,0 +1,311 @@ +import { bisector } from "d3-array"; +import { scaleOrdinal, schemeCategory10 } from "d3-scale"; +import identity from "./identity"; +import noop from "./noop"; + +export { default as rebind } from "./rebind"; +export { default as zipper } from "./zipper"; +export { default as merge } from "./merge"; +export { default as slidingWindow } from "./slidingWindow"; +export { default as identity } from "./identity"; +export { default as noop } from "./noop"; +export { default as shallowEqual } from "./shallowEqual"; +export { default as mappedSlidingWindow } from "./mappedSlidingWindow"; +export { default as accumulatingWindow } from "./accumulatingWindow"; + +export * from "./barWidth"; +export * from "./strokeDasharray"; + +export function getLogger(prefix) { + let logger = noop; + if (process.env.NODE_ENV !== "production") { + logger = require("debug")("react-financial-charts:" + prefix); + } + return logger; +} + +export function sign(x) { + // @ts-ignore + return (x > 0) - (x < 0); +} + +export const yes = () => true; + +export function path(loc = []) { + const key = Array.isArray(loc) ? loc : [loc]; + const length = key.length; + + return function (obj, defaultValue?) { + if (length === 0) { return isDefined(obj) ? obj : defaultValue; } + + let index = 0; + while (obj != null && index < length) { + obj = obj[key[index++]]; + } + return (index === length) ? obj : defaultValue; + }; +} + +export function functor(v) { + return typeof v === "function" ? v : () => v; +} + +export function createVerticalLinearGradient(stops) { + return function (moreProps, ctx) { + const { chartConfig: { height } } = moreProps; + + const grd = ctx.createLinearGradient(0, height, 0, 0); + stops.forEach((each) => { + grd.addColorStop(each.stop, each.color); + }); + + return grd; + }; +} + +export function getClosestItemIndexes2(array, value, accessor) { + let left = bisector(accessor).left(array, value); + left = Math.max(left - 1, 0); + let right = Math.min(left + 1, array.length - 1); + + const item = accessor(array[left]); + if (item >= value && item <= value) { right = left; } + + return { left, right }; +} + +export function getClosestValue(inputValue, currentValue) { + const values = isArray(inputValue) ? inputValue : [inputValue]; + + const diff = values + .map((each) => each - currentValue) + .reduce((diff1, diff2) => Math.abs(diff1) < Math.abs(diff2) ? diff1 : diff2); + return currentValue + diff; +} + +// @ts-ignore +export function find(list, predicate, context = this) { + for (let i = 0; i < list.length; ++i) { + if (predicate.call(context, list[i], i, list)) { + return list[i]; + } + } + return undefined; +} + +export function d3Window(node) { + const d3win = node + && (node.ownerDocument && node.ownerDocument.defaultView + || node.document && node + || node.defaultView); + return d3win; +} + +export const MOUSEENTER = "mouseenter.interaction"; +export const MOUSELEAVE = "mouseleave.interaction"; +export const MOUSEMOVE = "mousemove.pan"; +export const MOUSEUP = "mouseup.pan"; +export const TOUCHMOVE = "touchmove.pan"; +export const TOUCHEND = "touchend.pan touchcancel.pan"; + +export function getTouchProps(touch) { + if (!touch) { return {}; } + return { + pageX: touch.pageX, + pageY: touch.pageY, + clientX: touch.clientX, + clientY: touch.clientY, + }; +} + +export function getClosestItemIndexes(array, value, accessor) { + let lo = 0; + let hi = array.length - 1; + while (hi - lo > 1) { + const mid = Math.round((lo + hi) / 2); + if (accessor(array[mid]) <= value) { + lo = mid; + } else { + hi = mid; + } + } + // for Date object === does not work, so using the <= in combination with >= + // the same code works for both dates and numbers + if (accessor(array[lo]).valueOf() === value.valueOf()) { hi = lo; } + if (accessor(array[hi]).valueOf() === value.valueOf()) { lo = hi; } + + if (accessor(array[lo]) < value && accessor(array[hi]) < value) { lo = hi; } + if (accessor(array[lo]) > value && accessor(array[hi]) > value) { hi = lo; } + + return { left: lo, right: hi }; +} + +export function getClosestItem(array, value, accessor) { + const { left, right } = getClosestItemIndexes(array, value, accessor); + + if (left === right) { + return array[left]; + } + + const closest = (Math.abs(accessor(array[left]) - value) < Math.abs(accessor(array[right]) - value)) + ? array[left] + : array[right]; + + return closest; +} + +export const overlayColors = scaleOrdinal(schemeCategory10); + +export function head(array, accessor?) { + if (accessor && array) { + let value; + // tslint:disable-next-line: prefer-for-of + for (let i = 0; i < array.length; i++) { + value = array[i]; + if (isDefined(accessor(value))) { + return value; + } + } + return undefined; + } + return array ? array[0] : undefined; +} + +export function tail(array, accessor) { + if (accessor && array) { + return array.map(accessor).slice(1); + } + return array ? array.slice(1) : undefined; +} + +export const first = head; + +export function last(array, accessor?) { + if (accessor && array) { + let value; + for (let i = array.length - 1; i >= 0; i--) { + value = array[i]; + if (isDefined(accessor(value))) { return value; } + } + return undefined; + } + const length = array ? array.length : 0; + return length ? array[length - 1] : undefined; +} + +export const isDefined = (d: T) => { + return d !== null && d !== undefined; +}; + +export function isNotDefined(d) { + return !isDefined(d); +} + +export function isObject(d) { + return isDefined(d) && typeof d === "object" && !Array.isArray(d); +} + +export const isArray = Array.isArray; + +export function touchPosition(touch, e) { + const container = e.target; + const rect = container.getBoundingClientRect(); + const x = touch.clientX - rect.left - container.clientLeft; + const y = touch.clientY - rect.top - container.clientTop; + const xy = [Math.round(x), Math.round(y)]; + return xy; +} + +export function mousePosition(e: React.MouseEvent, defaultRect?) { + const container = e.currentTarget; + const rect = defaultRect || container.getBoundingClientRect(); + const x = e.clientX - rect.left - container.clientLeft; + const y = e.clientY - rect.top - container.clientTop; + const xy = [Math.round(x), Math.round(y)]; + return xy; +} + +export function clearCanvas(canvasList, ratio) { + canvasList.forEach((each) => { + each.setTransform(1, 0, 0, 1, 0, 0); + each.clearRect(-1, -1, each.canvas.width + 2, each.canvas.height + 2); + each.scale(ratio, ratio); + }); +} + +export function capitalizeFirst(str) { + return str.charAt(0).toUpperCase() + str.substring(1); +} + +export function hexToRGBA(inputHex, opacity) { + const hex = inputHex.replace("#", ""); + if (inputHex.indexOf("#") > -1 && (hex.length === 3 || hex.length === 6)) { + + const multiplier = (hex.length === 3) ? 1 : 2; + + const r = parseInt(hex.substring(0, 1 * multiplier), 16); + const g = parseInt(hex.substring(1 * multiplier, 2 * multiplier), 16); + const b = parseInt(hex.substring(2 * multiplier, 3 * multiplier), 16); + + const result = `rgba(${r}, ${g}, ${b}, ${opacity})`; + + return result; + } + return inputHex; +} + +export function toObject(array, iteratee = identity) { + return array.reduce((returnObj, a) => { + const [key, value] = iteratee(a); + return { + ...returnObj, + [key]: value, + }; + }, {}); +} + +// copied from https://github.com/lodash/lodash/blob/master/mapValue.js +export function mapValue(object, iteratee) { + object = Object(object); + // eslint-disable-next-line prefer-const + const result = {}; + + Object.keys(object).forEach((key) => { + const mappedValue = iteratee(object[key], key, object); + + if (isDefined(mappedValue)) { + result[key] = mappedValue; + } + }); + return result; +} + +// copied from https://github.com/lodash/lodash/blob/master/mapObject.js +export function mapObject(object = {}, iteratee = identity) { + const props = Object.keys(object); + + // eslint-disable-next-line prefer-const + const result = new Array(props.length); + + props.forEach((key, index) => { + // @ts-ignore + result[index] = iteratee(object[key], key, object); + }); + return result; +} + +export function replaceAtIndex(array, index, value) { + if (isDefined(array) && array.length > index) { + return array.slice(0, index) + .concat(value) + .concat(array.slice(index + 1)); + } + return array; +} + +// copied from https://github.com/lodash/lodash/blob/master/forOwn.js +export function forOwn(obj, iteratee) { + const object = Object(obj); + Object.keys(object) + .forEach((key) => iteratee(object[key], key, object)); +} diff --git a/packages/react-financial-charts/src/utils/mappedSlidingWindow.ts b/packages/react-financial-charts/src/utils/mappedSlidingWindow.ts new file mode 100644 index 000000000..2438baf9e --- /dev/null +++ b/packages/react-financial-charts/src/utils/mappedSlidingWindow.ts @@ -0,0 +1,82 @@ + +import identity from "./identity"; +import { functor } from "./index"; +import noop from "./noop"; + +export default function () { + + let undefinedValue; + let windowSize = 10; + let accumulator = noop; + let source = identity; + let skipInitial = 0; + + const mappedSlidingWindow = function (data) { + // @ts-ignore + const size = functor(windowSize).apply(this, arguments); + const windowData: any[] = []; + let accumulatorIdx = 0; + const undef = functor(undefinedValue); + const result: any[] = []; + data.forEach(function (d, i) { + let mapped; + if (i < (skipInitial + size - 1)) { + mapped = undef(d, i); + result.push(mapped); + windowData.push(mapped); + return; + } + if (i >= (skipInitial + size)) { + windowData.shift(); + } + // @ts-ignore + windowData.push(source(d, i)); + + // @ts-ignore + mapped = accumulator(windowData, i, accumulatorIdx++); + result.push(mapped); + windowData.pop(); + windowData.push(mapped); + return; + }); + return result; + }; + + mappedSlidingWindow.undefinedValue = function (x) { + if (!arguments.length) { + return undefinedValue; + } + undefinedValue = x; + return mappedSlidingWindow; + }; + mappedSlidingWindow.windowSize = function (x) { + if (!arguments.length) { + return windowSize; + } + windowSize = x; + return mappedSlidingWindow; + }; + mappedSlidingWindow.accumulator = function (x) { + if (!arguments.length) { + return accumulator; + } + accumulator = x; + return mappedSlidingWindow; + }; + mappedSlidingWindow.skipInitial = function (x) { + if (!arguments.length) { + return skipInitial; + } + skipInitial = x; + return mappedSlidingWindow; + }; + mappedSlidingWindow.source = function (x) { + if (!arguments.length) { + return source; + } + source = x; + return mappedSlidingWindow; + }; + + return mappedSlidingWindow; +} diff --git a/packages/react-financial-charts/src/utils/merge.ts b/packages/react-financial-charts/src/utils/merge.ts new file mode 100644 index 000000000..ceea13a84 --- /dev/null +++ b/packages/react-financial-charts/src/utils/merge.ts @@ -0,0 +1,79 @@ +/* +https://github.com/ScottLogic/d3fc/blob/master/src/indicator/algorithm/merge.js + +The MIT License (MIT) + +Copyright (c) 2014-2015 Scott Logic Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +import identity from "./identity"; +import noop from "./noop"; +import zipper from "./zipper"; + +import { isNotDefined } from "./index"; + +// applies an algorithm to an array, merging the result back into +// the source array using the given merge function. +export default function () { + + let algorithm = identity; + let skipUndefined = true; + let merge = noop; + + function mergeCompute(data) { + const zip = zipper() + .combine((datum, indicator) => { + const result = (skipUndefined && isNotDefined(indicator)) + ? datum + // @ts-ignore + : merge(datum, indicator); + return isNotDefined(result) ? datum : result; + }); + + // @ts-ignore + return zip(data, algorithm(data)); + } + + mergeCompute.algorithm = function (x) { + if (!arguments.length) { + return algorithm; + } + algorithm = x; + return mergeCompute; + }; + + mergeCompute.merge = function (x) { + if (!arguments.length) { + return merge; + } + merge = x; + return mergeCompute; + }; + mergeCompute.skipUndefined = function (x) { + if (!arguments.length) { + return skipUndefined; + } + skipUndefined = x; + return mergeCompute; + }; + + return mergeCompute; +} diff --git a/packages/react-financial-charts/src/utils/noop.ts b/packages/react-financial-charts/src/utils/noop.ts new file mode 100644 index 000000000..c39d398e9 --- /dev/null +++ b/packages/react-financial-charts/src/utils/noop.ts @@ -0,0 +1,2 @@ +// tslint:disable-next-line: no-empty +export default () => { }; diff --git a/packages/react-financial-charts/src/utils/rebind.ts b/packages/react-financial-charts/src/utils/rebind.ts new file mode 100644 index 000000000..8f9cacbb3 --- /dev/null +++ b/packages/react-financial-charts/src/utils/rebind.ts @@ -0,0 +1,19 @@ +// copied from https://github.com/d3fc/d3fc-rebind/blob/master/src/rebind.js + +function createReboundMethod(target, source, name) { + const method = source[name]; + if (typeof method !== "function") { + throw new Error(`Attempt to rebind ${name} which isn't a function on the source object`); + } + return (...args) => { + const value = method.apply(source, args); + return value === source ? target : value; + }; +} + +export default function rebind(target, source, ...names) { + for (const name of names) { + target[name] = createReboundMethod(target, source, name); + } + return target; +} diff --git a/packages/react-financial-charts/src/utils/shallowEqual.ts b/packages/react-financial-charts/src/utils/shallowEqual.ts new file mode 100644 index 000000000..871490324 --- /dev/null +++ b/packages/react-financial-charts/src/utils/shallowEqual.ts @@ -0,0 +1,58 @@ +// https://github.com/jonschlinkert/is-equal-shallow/ + +/* +The MIT License (MIT) + +Copyright (c) 2015, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +function isDate(date) { + return Object.prototype.toString.call(date) === "[object Date]"; +} + +function isEqual(val1, val2) { + return (isDate(val1) && isDate(val2)) + ? val1.getTime() === val2.getTime() + : val1 === val2; +} + +export default function shallowEqual(a, b) { + if (!a && !b) { return true; } + if (!a && b || a && !b) { return false; } + + let numKeysA = 0; + let numKeysB = 0; + let key; + + // tslint:disable: forin + for (key in b) { + numKeysB++; + if ((b.hasOwnProperty(key) && !a.hasOwnProperty(key)) || !isEqual(a[key], b[key])) { + return false; + } + } + + for (key in a) { + numKeysA++; + } + + return numKeysA === numKeysB; +} diff --git a/packages/react-financial-charts/src/utils/slidingWindow.ts b/packages/react-financial-charts/src/utils/slidingWindow.ts new file mode 100644 index 000000000..17d323336 --- /dev/null +++ b/packages/react-financial-charts/src/utils/slidingWindow.ts @@ -0,0 +1,117 @@ +/* + +Taken from https://github.com/ScottLogic/d3fc/blob/master/src/indicator/algorithm/calculator/slidingWindow.js + +The MIT License (MIT) + +Copyright (c) 2014-2015 Scott Logic Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +import { functor, path } from "./index"; +import noop from "./noop"; + +export default function () { + + let undefinedValue; + let windowSize = 10; + let accumulator = noop; + let sourcePath; + let source; + let skipInitial = 0; + let misc; + + const slidingWindow = function (data) { + const sourceFunction = source || path(sourcePath); + + // @ts-ignore + const size = functor(windowSize).apply(this, arguments); + const windowData = data.slice(skipInitial, size + skipInitial).map(sourceFunction); + let accumulatorIdx = 0; + const undef = functor(undefinedValue); + return data.map(function (d, i) { + // console.log(d, i); + if (i < (skipInitial + size - 1)) { + return undef(sourceFunction(d), i, misc); + } + if (i >= (skipInitial + size)) { + // Treat windowData as FIFO rolling buffer + windowData.shift(); + windowData.push(sourceFunction(d, i)); + } + + // @ts-ignore + return accumulator(windowData, i, accumulatorIdx++, misc); + }); + }; + + slidingWindow.undefinedValue = function (...args) { + if (!args.length) { + return undefinedValue; + } + undefinedValue = args; + return slidingWindow; + }; + slidingWindow.windowSize = function (x) { + if (!arguments.length) { + return windowSize; + } + windowSize = x; + return slidingWindow; + }; + slidingWindow.misc = function (x) { + if (!arguments.length) { + return misc; + } + misc = x; + return slidingWindow; + }; + slidingWindow.accumulator = function (x) { + if (!arguments.length) { + return accumulator; + } + accumulator = x; + return slidingWindow; + }; + slidingWindow.skipInitial = function (x) { + if (!arguments.length) { + return skipInitial; + } + skipInitial = x; + return slidingWindow; + }; + slidingWindow.sourcePath = function (x) { + if (!arguments.length) { + return sourcePath; + } + sourcePath = x; + return slidingWindow; + }; + slidingWindow.source = function (x) { + if (!arguments.length) { + return source; + } + source = x; + return slidingWindow; + }; + + return slidingWindow; +} diff --git a/packages/react-financial-charts/src/utils/strokeDasharray.ts b/packages/react-financial-charts/src/utils/strokeDasharray.ts new file mode 100644 index 000000000..e770a6d7f --- /dev/null +++ b/packages/react-financial-charts/src/utils/strokeDasharray.ts @@ -0,0 +1,51 @@ +export type strokeDashTypes = + "Solid" | + "ShortDash" | + "ShortDash2" | + "ShortDot" | + "ShortDashDot" | + "ShortDashDotDot" | + "Dot" | + "Dash" | + "LongDash" | + "DashDot" | + "LongDashDot" | + "LongDashDotDot"; + +export function getStrokeDasharrayCanvas(type) { + const a = getStrokeDasharray(type).split(","); + + if (a.length === 1) { return []; } + + return a.map((d) => Number(d)); + +} +export const getStrokeDasharray = (type) => { + switch (type) { + default: + case "Solid": + return "none"; + case "ShortDash": + return "6, 2"; + case "ShortDash2": + return "6, 3"; + case "ShortDot": + return "2, 2"; + case "ShortDashDot": + return "6, 2, 2, 2"; + case "ShortDashDotDot": + return "6, 2, 2, 2, 2, 2"; + case "Dot": + return "2, 6"; + case "Dash": + return "8, 6"; + case "LongDash": + return "16, 6"; + case "DashDot": + return "8, 6, 2, 6"; + case "LongDashDot": + return "16, 6, 2, 6"; + case "LongDashDotDot": + return "16, 6, 2, 6, 2, 6"; + } +}; diff --git a/packages/react-financial-charts/src/utils/zipper.ts b/packages/react-financial-charts/src/utils/zipper.ts new file mode 100644 index 000000000..8d1d7e569 --- /dev/null +++ b/packages/react-financial-charts/src/utils/zipper.ts @@ -0,0 +1,39 @@ +/* an extension to d3.zip so we call a function instead of an array */ + +import { min } from "d3-array"; + +import identity from "./identity"; + +export default function zipper() { + let combine = identity; + + function zip() { + const n = arguments.length; + if (!n) { return []; } + const m = min(arguments, d3_zipLength); + + let i; + const zips = new Array(m); + for (i = -1; ++i < m;) { + // tslint:disable-next-line: no-shadowed-variable + for (let j = -1, zip = zips[i] = new Array(n); ++j < n;) { + zip[j] = arguments[j][i]; + } + + // @ts-ignore + zips[i] = combine.apply(this, zips[i]); + } + return zips; + } + function d3_zipLength(d) { + return d.length; + } + zip.combine = function (x) { + if (!arguments.length) { + return combine; + } + combine = x; + return zip; + }; + return zip; +} diff --git a/packages/react-financial-charts/src/utils/zoomBehavior.ts b/packages/react-financial-charts/src/utils/zoomBehavior.ts new file mode 100644 index 000000000..6ec77a5f1 --- /dev/null +++ b/packages/react-financial-charts/src/utils/zoomBehavior.ts @@ -0,0 +1,40 @@ +import { + getCurrentItem, +} from "./ChartDataUtil"; + +import { + last, +} from "./index"; + +export function mouseBasedZoomAnchor({ + xScale, + xAccessor, + mouseXY, + plotData, + fullData, +}) { + const currentItem = getCurrentItem(xScale, xAccessor, mouseXY, plotData); + return xAccessor(currentItem); +} + +export function lastVisibleItemBasedZoomAnchor({ + xScale, + xAccessor, + mouseXY, + plotData, + fullData, +}) { + const lastItem = last(plotData); + return xAccessor(lastItem); +} + +export function rightDomainBasedZoomAnchor({ + xScale, + xAccessor, + mouseXY, + plotData, + fullData, +}) { + const [, end] = xScale.domain(); + return end; +} diff --git a/packages/react-financial-charts/tsconfig.json b/packages/react-financial-charts/tsconfig.json new file mode 100644 index 000000000..ba5a25607 --- /dev/null +++ b/packages/react-financial-charts/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "declaration": true, + "experimentalDecorators": true, + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "module": "esnext", + "moduleResolution": "node", + "noImplicitAny": false, + "noImplicitThis": true, + "noUnusedLocals": true, + "outDir": "lib", + "removeComments": true, + "sourceMap": true, + "strict": true, + "target": "es2017" + }, + "include": [ + "src" + ], + "exclude": [ + "lib", + "**/__tests__/**" + ] +} diff --git a/packages/react-financial-charts/tslint.yaml b/packages/react-financial-charts/tslint.yaml new file mode 100644 index 000000000..7325d2a01 --- /dev/null +++ b/packages/react-financial-charts/tslint.yaml @@ -0,0 +1,10 @@ +--- +extends: "tslint:recommended" +rules: + completed-docs: false + interface-name: false + object-literal-sort-keys: false + only-arrow-functions: false + space-before-function-paren: false + max-line-length: + options: [140]