From 8eb497812e411adf6ed98bcb8c54bda3da848e77 Mon Sep 17 00:00:00 2001 From: SiriusWhi Date: Sat, 26 Jun 2021 16:37:11 +0200 Subject: [PATCH] kambani extension --- .editorconfig | 13 + .gitignore | 45 + LICENSE | 21 + README.md | 298 + angular.json | 110 + examples/demo-voting-app/README.md | 19 + examples/demo-voting-app/index.html | 70 + examples/demo-voting-app/js/index.js | 329 + package-lock.json | 10095 ++++++++++++++++ package.json | 76 + src/app/app-routing.module.ts | 44 + src/app/app.component.html | 22 + src/app/app.component.scss | 21 + src/app/app.component.ts | 131 + src/app/app.module.ts | 85 + .../approval-requests.component.html | 25 + .../approval-requests.component.scss | 0 .../approval-requests.component.ts | 85 + src/app/components/base.component.ts | 10 + .../backup/backup.dialog.component.html | 11 + .../backup/backup.dialog.component.scss | 30 + .../dialogs/backup/backup.dialog.component.ts | 14 + src/app/components/dialogs/dialogs.module.ts | 22 + src/app/components/dialogs/index.ts | 9 + .../password/password.dialog.component.html | 25 + .../password/password.dialog.component.scss | 27 + .../password/password.dialog.component.ts | 27 + .../remove-vault.dialog.component.html | 12 + .../remove-vault.dialog.component.scss | 30 + .../remove-vault.dialog.component.ts | 19 + .../did/action/action.component.html | 16 + .../did/action/action.component.scss | 76 + .../components/did/action/action.component.ts | 39 + .../did/did-keys/did-keys.component.html | 178 + .../did/did-keys/did-keys.component.scss | 104 + .../did/did-keys/did-keys.component.ts | 222 + src/app/components/did/did.module.ts | 49 + src/app/components/did/did.routing.ts | 20 + src/app/components/did/index.ts | 25 + .../manage-dids/manage-dids.component.html | 95 + .../manage-dids/manage-dids.component.scss | 225 + .../did/manage-dids/manage-dids.component.ts | 266 + .../management-keys.component.html | 203 + .../management-keys.component.scss | 100 + .../management-keys.component.ts | 192 + .../preview-did/preview-did.component.html | 172 + .../preview-did/preview-did.component.scss | 222 + .../did/preview-did/preview-did.component.ts | 307 + .../did/services/services.component.html | 121 + .../did/services/services.component.scss | 78 + .../did/services/services.component.ts | 133 + .../did/stepper/stepper.component.html | 3 + .../did/stepper/stepper.component.scss | 24 + .../did/stepper/stepper.component.ts | 40 + .../did/summary/summary.component.html | 17 + .../did/summary/summary.component.scss | 20 + .../did/summary/summary.component.ts | 125 + .../did-key-form/did-key-form.component.html | 72 + .../did-key-form/did-key-form.component.scss | 4 + .../did-key-form/did-key-form.component.ts | 195 + .../management-key-form.component.html | 90 + .../management-key-form.component.scss | 0 .../management-key-form.component.ts | 163 + .../service-form/service-form.component.html | 74 + .../service-form/service-form.component.scss | 0 .../service-form/service-form.component.ts | 102 + .../factom-addresses.module.ts | 30 + .../factom-addresses.routing.ts | 16 + .../import-address.component.html | 54 + .../import-address.component.scss | 0 .../import-address.component.ts | 133 + src/app/components/factom-addresses/index.ts | 7 + .../manage-addresses.component.html | 185 + .../manage-addresses.component.scss | 132 + .../manage-addresses.component.ts | 253 + src/app/components/home/home.component.html | 52 + src/app/components/home/home.component.scss | 66 + src/app/components/home/home.component.ts | 119 + .../keys/import-key/import-key.component.html | 52 + .../keys/import-key/import-key.component.scss | 0 .../keys/import-key/import-key.component.ts | 113 + src/app/components/keys/index.ts | 7 + src/app/components/keys/keys.module.ts | 30 + src/app/components/keys/keys.routing.ts | 16 + .../manage-keys/manage-keys.component.html | 72 + .../manage-keys/manage-keys.component.scss | 132 + .../keys/manage-keys/manage-keys.component.ts | 154 + .../confirm-modal.component.html | 12 + .../confirm-modal.component.scss | 30 + .../confirm-modal/confirm-modal.component.ts | 13 + .../create-advanced-info-modal.component.html | 24 + .../create-advanced-info-modal.component.scss | 42 + .../create-advanced-info-modal.component.ts | 11 + .../create-basic-info-modal.component.html | 21 + .../create-basic-info-modal.component.scss | 42 + .../create-basic-info-modal.component.ts | 11 + src/app/components/modals/index.ts | 13 + src/app/components/modals/modals.module.ts | 24 + .../private-key-address-modal.component.html | 30 + .../private-key-address-modal.component.scss | 69 + .../private-key-address-modal.component.ts | 54 + .../remove-confirm-modal.component.html | 12 + .../remove-confirm-modal.component.scss | 30 + .../remove-confirm-modal.component.ts | 18 + .../components/navbar/navbar.component.html | 45 + .../components/navbar/navbar.component.scss | 144 + src/app/components/navbar/navbar.component.ts | 57 + .../settings/settings.component.html | 175 + .../settings/settings.component.scss | 29 + .../components/settings/settings.component.ts | 128 + .../components/signer/signer.component.html | 111 + .../components/signer/signer.component.scss | 14 + src/app/components/signer/signer.component.ts | 461 + .../create-vault/create-vault.component.html | 46 + .../create-vault/create-vault.component.scss | 61 + .../create-vault/create-vault.component.ts | 50 + src/app/components/vault/index.ts | 9 + .../restore-vault.component.html | 31 + .../restore-vault.component.scss | 0 .../restore-vault/restore-vault.component.ts | 126 + .../vault-backup/vault-backup.component.html | 20 + .../vault-backup/vault-backup.component.scss | 0 .../vault-backup/vault-backup.component.ts | 48 + src/app/components/vault/vault.module.ts | 22 + src/app/components/vault/vault.routing.ts | 19 + src/app/core/enums/action-routes.ts | 21 + src/app/core/enums/action-type.ts | 4 + src/app/core/enums/chrome-message-type.ts | 17 + src/app/core/enums/create-routes.ts | 6 + src/app/core/enums/entry-type.ts | 5 + src/app/core/enums/factom-address-type.ts | 5 + src/app/core/enums/key-type.ts | 3 + src/app/core/enums/modal-size-types.ts | 7 + src/app/core/enums/purpose-type.ts | 4 + src/app/core/enums/request-key-type.ts | 8 + src/app/core/enums/request-type.ts | 4 + src/app/core/enums/shared-routes.ts | 4 + src/app/core/enums/signature-type.ts | 5 + src/app/core/enums/transaction-type.ts | 5 + src/app/core/guards/create-action.guard.ts | 34 + src/app/core/guards/guards.module.ts | 16 + src/app/core/guards/vault.guard.ts | 32 + .../core/interceptors/error.interceptor.ts | 34 + .../core/interfaces/dialogs/modal-options.ts | 18 + src/app/core/interfaces/dialogs/modal.ts | 6 + src/app/core/interfaces/did-document.ts | 10 + src/app/core/interfaces/did-key-entry.ts | 9 + .../core/interfaces/management-key-entry.ts | 9 + src/app/core/interfaces/revoke-model.ts | 3 + src/app/core/interfaces/service-entry.ts | 6 + .../core/interfaces/update-entry-document.ts | 17 + src/app/core/models/backup-result.model.ts | 7 + src/app/core/models/component-key.model.ts | 18 + .../core/models/component-service.model.ts | 8 + src/app/core/models/did-key.model.ts | 17 + src/app/core/models/key-pair.model.ts | 5 + src/app/core/models/key.model.ts | 8 + src/app/core/models/management-key.model.ts | 16 + src/app/core/models/restore-result.model.ts | 7 + src/app/core/models/result.model.ts | 6 + src/app/core/models/service.model.ts | 7 + src/app/core/models/signature-data.model.ts | 8 + src/app/core/models/signature-result.model.ts | 7 + src/app/core/models/update-did.model.ts | 28 + .../core/services/dialogs/dialogs.service.ts | 55 + src/app/core/services/did/did.service.ts | 375 + src/app/core/services/index.ts | 15 + src/app/core/services/keys/keys.service.ts | 112 + src/app/core/services/services.module.ts | 14 + .../core/services/signing/signing.service.ts | 316 + src/app/core/services/vault/vault.service.ts | 973 ++ .../services/workflow/workflow.service.ts | 53 + src/app/core/store/app.reducers.ts | 12 + src/app/core/store/app.state.ts | 9 + .../store/create-did/create-did.actions.ts | 69 + .../store/create-did/create-did.reducers.ts | 92 + .../core/store/create-did/create-did.state.ts | 9 + .../store/update-did/update-did.actions.ts | 86 + .../store/update-did/update-did.reducers.ts | 190 + .../core/store/update-did/update-did.state.ts | 6 + .../core/store/workflow/workflow.actions.ts | 28 + .../core/store/workflow/workflow.reducers.ts | 30 + src/app/core/store/workflow/workflow.state.ts | 5 + src/app/core/utils/alias.validator.ts | 72 + src/app/core/utils/customValidators.ts | 52 + src/app/core/utils/helpers.ts | 212 + src/app/core/utils/priority.max.validator.ts | 21 + src/app/core/utils/priority.min.validator.ts | 21 + src/app/core/utils/tooltip.messages.ts | 24 + src/assets/.gitkeep | 0 src/assets/bootstrap.min.css | 12 + src/assets/close.svg | 4 + src/assets/crypto-auth.png | Bin 0 -> 102894 bytes src/assets/factom-protocol.png | Bin 0 -> 19365 bytes src/assets/factomatic-logo-transparent.png | Bin 0 -> 66164 bytes src/assets/kambani-logo.png | Bin 0 -> 43662 bytes src/assets/next-page.svg | 6 + src/background.js | 325 + src/browserslist | 11 + src/contentScript.js | 156 + src/environments/environment.prod.ts | 10 + src/environments/environment.staging.ts | 10 + src/environments/environment.ts | 23 + src/index.html | 13 + src/main.ts | 12 + src/manifest.json | 37 + src/polyfills.ts | 89 + src/styles.scss | 207 + src/tsconfig.app.json | 14 + src/tsconfig.spec.json | 18 + src/tslint.json | 17 + tsconfig.json | 22 + tslint.json | 131 + webpack.config.js | 5 + 214 files changed, 23579 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 angular.json create mode 100644 examples/demo-voting-app/README.md create mode 100644 examples/demo-voting-app/index.html create mode 100644 examples/demo-voting-app/js/index.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/app/app-routing.module.ts create mode 100644 src/app/app.component.html create mode 100644 src/app/app.component.scss create mode 100644 src/app/app.component.ts create mode 100644 src/app/app.module.ts create mode 100644 src/app/components/approval-requests/approval-requests.component.html create mode 100644 src/app/components/approval-requests/approval-requests.component.scss create mode 100644 src/app/components/approval-requests/approval-requests.component.ts create mode 100644 src/app/components/base.component.ts create mode 100644 src/app/components/dialogs/backup/backup.dialog.component.html create mode 100644 src/app/components/dialogs/backup/backup.dialog.component.scss create mode 100644 src/app/components/dialogs/backup/backup.dialog.component.ts create mode 100644 src/app/components/dialogs/dialogs.module.ts create mode 100644 src/app/components/dialogs/index.ts create mode 100644 src/app/components/dialogs/password/password.dialog.component.html create mode 100644 src/app/components/dialogs/password/password.dialog.component.scss create mode 100644 src/app/components/dialogs/password/password.dialog.component.ts create mode 100644 src/app/components/dialogs/remove-vault/remove-vault.dialog.component.html create mode 100644 src/app/components/dialogs/remove-vault/remove-vault.dialog.component.scss create mode 100644 src/app/components/dialogs/remove-vault/remove-vault.dialog.component.ts create mode 100644 src/app/components/did/action/action.component.html create mode 100644 src/app/components/did/action/action.component.scss create mode 100644 src/app/components/did/action/action.component.ts create mode 100644 src/app/components/did/did-keys/did-keys.component.html create mode 100644 src/app/components/did/did-keys/did-keys.component.scss create mode 100644 src/app/components/did/did-keys/did-keys.component.ts create mode 100644 src/app/components/did/did.module.ts create mode 100644 src/app/components/did/did.routing.ts create mode 100644 src/app/components/did/index.ts create mode 100644 src/app/components/did/manage-dids/manage-dids.component.html create mode 100644 src/app/components/did/manage-dids/manage-dids.component.scss create mode 100644 src/app/components/did/manage-dids/manage-dids.component.ts create mode 100644 src/app/components/did/management-keys/management-keys.component.html create mode 100644 src/app/components/did/management-keys/management-keys.component.scss create mode 100644 src/app/components/did/management-keys/management-keys.component.ts create mode 100644 src/app/components/did/preview-did/preview-did.component.html create mode 100644 src/app/components/did/preview-did/preview-did.component.scss create mode 100644 src/app/components/did/preview-did/preview-did.component.ts create mode 100644 src/app/components/did/services/services.component.html create mode 100644 src/app/components/did/services/services.component.scss create mode 100644 src/app/components/did/services/services.component.ts create mode 100644 src/app/components/did/stepper/stepper.component.html create mode 100644 src/app/components/did/stepper/stepper.component.scss create mode 100644 src/app/components/did/stepper/stepper.component.ts create mode 100644 src/app/components/did/summary/summary.component.html create mode 100644 src/app/components/did/summary/summary.component.scss create mode 100644 src/app/components/did/summary/summary.component.ts create mode 100644 src/app/components/did/update-did/did-key-form/did-key-form.component.html create mode 100644 src/app/components/did/update-did/did-key-form/did-key-form.component.scss create mode 100644 src/app/components/did/update-did/did-key-form/did-key-form.component.ts create mode 100644 src/app/components/did/update-did/management-key-form/management-key-form.component.html create mode 100644 src/app/components/did/update-did/management-key-form/management-key-form.component.scss create mode 100644 src/app/components/did/update-did/management-key-form/management-key-form.component.ts create mode 100644 src/app/components/did/update-did/service-form/service-form.component.html create mode 100644 src/app/components/did/update-did/service-form/service-form.component.scss create mode 100644 src/app/components/did/update-did/service-form/service-form.component.ts create mode 100644 src/app/components/factom-addresses/factom-addresses.module.ts create mode 100644 src/app/components/factom-addresses/factom-addresses.routing.ts create mode 100644 src/app/components/factom-addresses/import-address/import-address.component.html create mode 100644 src/app/components/factom-addresses/import-address/import-address.component.scss create mode 100644 src/app/components/factom-addresses/import-address/import-address.component.ts create mode 100644 src/app/components/factom-addresses/index.ts create mode 100644 src/app/components/factom-addresses/manage-addresses/manage-addresses.component.html create mode 100644 src/app/components/factom-addresses/manage-addresses/manage-addresses.component.scss create mode 100644 src/app/components/factom-addresses/manage-addresses/manage-addresses.component.ts create mode 100644 src/app/components/home/home.component.html create mode 100644 src/app/components/home/home.component.scss create mode 100644 src/app/components/home/home.component.ts create mode 100644 src/app/components/keys/import-key/import-key.component.html create mode 100644 src/app/components/keys/import-key/import-key.component.scss create mode 100644 src/app/components/keys/import-key/import-key.component.ts create mode 100644 src/app/components/keys/index.ts create mode 100644 src/app/components/keys/keys.module.ts create mode 100644 src/app/components/keys/keys.routing.ts create mode 100644 src/app/components/keys/manage-keys/manage-keys.component.html create mode 100644 src/app/components/keys/manage-keys/manage-keys.component.scss create mode 100644 src/app/components/keys/manage-keys/manage-keys.component.ts create mode 100644 src/app/components/modals/confirm-modal/confirm-modal.component.html create mode 100644 src/app/components/modals/confirm-modal/confirm-modal.component.scss create mode 100644 src/app/components/modals/confirm-modal/confirm-modal.component.ts create mode 100644 src/app/components/modals/create-advanced-info-modal/create-advanced-info-modal.component.html create mode 100644 src/app/components/modals/create-advanced-info-modal/create-advanced-info-modal.component.scss create mode 100644 src/app/components/modals/create-advanced-info-modal/create-advanced-info-modal.component.ts create mode 100644 src/app/components/modals/create-basic-info-modal/create-basic-info-modal.component.html create mode 100644 src/app/components/modals/create-basic-info-modal/create-basic-info-modal.component.scss create mode 100644 src/app/components/modals/create-basic-info-modal/create-basic-info-modal.component.ts create mode 100644 src/app/components/modals/index.ts create mode 100644 src/app/components/modals/modals.module.ts create mode 100644 src/app/components/modals/private-key-address-modal/private-key-address-modal.component.html create mode 100644 src/app/components/modals/private-key-address-modal/private-key-address-modal.component.scss create mode 100644 src/app/components/modals/private-key-address-modal/private-key-address-modal.component.ts create mode 100644 src/app/components/modals/remove-confirm-modal/remove-confirm-modal.component.html create mode 100644 src/app/components/modals/remove-confirm-modal/remove-confirm-modal.component.scss create mode 100644 src/app/components/modals/remove-confirm-modal/remove-confirm-modal.component.ts create mode 100644 src/app/components/navbar/navbar.component.html create mode 100644 src/app/components/navbar/navbar.component.scss create mode 100644 src/app/components/navbar/navbar.component.ts create mode 100644 src/app/components/settings/settings.component.html create mode 100644 src/app/components/settings/settings.component.scss create mode 100644 src/app/components/settings/settings.component.ts create mode 100644 src/app/components/signer/signer.component.html create mode 100644 src/app/components/signer/signer.component.scss create mode 100644 src/app/components/signer/signer.component.ts create mode 100644 src/app/components/vault/create-vault/create-vault.component.html create mode 100644 src/app/components/vault/create-vault/create-vault.component.scss create mode 100644 src/app/components/vault/create-vault/create-vault.component.ts create mode 100644 src/app/components/vault/index.ts create mode 100644 src/app/components/vault/restore-vault/restore-vault.component.html create mode 100644 src/app/components/vault/restore-vault/restore-vault.component.scss create mode 100644 src/app/components/vault/restore-vault/restore-vault.component.ts create mode 100644 src/app/components/vault/vault-backup/vault-backup.component.html create mode 100644 src/app/components/vault/vault-backup/vault-backup.component.scss create mode 100644 src/app/components/vault/vault-backup/vault-backup.component.ts create mode 100644 src/app/components/vault/vault.module.ts create mode 100644 src/app/components/vault/vault.routing.ts create mode 100644 src/app/core/enums/action-routes.ts create mode 100644 src/app/core/enums/action-type.ts create mode 100644 src/app/core/enums/chrome-message-type.ts create mode 100644 src/app/core/enums/create-routes.ts create mode 100644 src/app/core/enums/entry-type.ts create mode 100644 src/app/core/enums/factom-address-type.ts create mode 100644 src/app/core/enums/key-type.ts create mode 100644 src/app/core/enums/modal-size-types.ts create mode 100644 src/app/core/enums/purpose-type.ts create mode 100644 src/app/core/enums/request-key-type.ts create mode 100644 src/app/core/enums/request-type.ts create mode 100644 src/app/core/enums/shared-routes.ts create mode 100644 src/app/core/enums/signature-type.ts create mode 100644 src/app/core/enums/transaction-type.ts create mode 100644 src/app/core/guards/create-action.guard.ts create mode 100644 src/app/core/guards/guards.module.ts create mode 100644 src/app/core/guards/vault.guard.ts create mode 100644 src/app/core/interceptors/error.interceptor.ts create mode 100644 src/app/core/interfaces/dialogs/modal-options.ts create mode 100644 src/app/core/interfaces/dialogs/modal.ts create mode 100644 src/app/core/interfaces/did-document.ts create mode 100644 src/app/core/interfaces/did-key-entry.ts create mode 100644 src/app/core/interfaces/management-key-entry.ts create mode 100644 src/app/core/interfaces/revoke-model.ts create mode 100644 src/app/core/interfaces/service-entry.ts create mode 100644 src/app/core/interfaces/update-entry-document.ts create mode 100644 src/app/core/models/backup-result.model.ts create mode 100644 src/app/core/models/component-key.model.ts create mode 100644 src/app/core/models/component-service.model.ts create mode 100644 src/app/core/models/did-key.model.ts create mode 100644 src/app/core/models/key-pair.model.ts create mode 100644 src/app/core/models/key.model.ts create mode 100644 src/app/core/models/management-key.model.ts create mode 100644 src/app/core/models/restore-result.model.ts create mode 100644 src/app/core/models/result.model.ts create mode 100644 src/app/core/models/service.model.ts create mode 100644 src/app/core/models/signature-data.model.ts create mode 100644 src/app/core/models/signature-result.model.ts create mode 100644 src/app/core/models/update-did.model.ts create mode 100644 src/app/core/services/dialogs/dialogs.service.ts create mode 100644 src/app/core/services/did/did.service.ts create mode 100644 src/app/core/services/index.ts create mode 100644 src/app/core/services/keys/keys.service.ts create mode 100644 src/app/core/services/services.module.ts create mode 100644 src/app/core/services/signing/signing.service.ts create mode 100644 src/app/core/services/vault/vault.service.ts create mode 100644 src/app/core/services/workflow/workflow.service.ts create mode 100644 src/app/core/store/app.reducers.ts create mode 100644 src/app/core/store/app.state.ts create mode 100644 src/app/core/store/create-did/create-did.actions.ts create mode 100644 src/app/core/store/create-did/create-did.reducers.ts create mode 100644 src/app/core/store/create-did/create-did.state.ts create mode 100644 src/app/core/store/update-did/update-did.actions.ts create mode 100644 src/app/core/store/update-did/update-did.reducers.ts create mode 100644 src/app/core/store/update-did/update-did.state.ts create mode 100644 src/app/core/store/workflow/workflow.actions.ts create mode 100644 src/app/core/store/workflow/workflow.reducers.ts create mode 100644 src/app/core/store/workflow/workflow.state.ts create mode 100644 src/app/core/utils/alias.validator.ts create mode 100644 src/app/core/utils/customValidators.ts create mode 100644 src/app/core/utils/helpers.ts create mode 100644 src/app/core/utils/priority.max.validator.ts create mode 100644 src/app/core/utils/priority.min.validator.ts create mode 100644 src/app/core/utils/tooltip.messages.ts create mode 100644 src/assets/.gitkeep create mode 100644 src/assets/bootstrap.min.css create mode 100644 src/assets/close.svg create mode 100644 src/assets/crypto-auth.png create mode 100644 src/assets/factom-protocol.png create mode 100644 src/assets/factomatic-logo-transparent.png create mode 100644 src/assets/kambani-logo.png create mode 100644 src/assets/next-page.svg create mode 100644 src/background.js create mode 100644 src/browserslist create mode 100644 src/contentScript.js create mode 100644 src/environments/environment.prod.ts create mode 100644 src/environments/environment.staging.ts create mode 100644 src/environments/environment.ts create mode 100644 src/index.html create mode 100644 src/main.ts create mode 100644 src/manifest.json create mode 100644 src/polyfills.ts create mode 100644 src/styles.scss create mode 100644 src/tsconfig.app.json create mode 100644 src/tsconfig.spec.json create mode 100644 src/tslint.json create mode 100644 tsconfig.json create mode 100644 tslint.json create mode 100644 webpack.config.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e89330a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..60f7fdc --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +/dist +/tmp +/out-tsc + +# dependencies +/node_modules + +# profiling files +chrome-profiler-events.json +speed-measure-plugin.json + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace +*.swp +*.swo + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +yarn-error.log +testem.log +/typings + +# System Files +.DS_Store +Thumbs.db diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..419f710 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Factomatic LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..be9915f --- /dev/null +++ b/README.md @@ -0,0 +1,298 @@ +# Kambani + +Kambani is a Chrome extension for cryptographic keys and digital identities management, tailored to the [Factom blockchain](https://www.factomprotocol.org/). + +The main feature of the extension is the ability for existing web sites to easily integrate with Kambani and send arbitrary +signing requests for review and approval. This flexibility can be used as a building block in a wide variety of applications, +such as: + +* passwordless authentication and authorization +* initiation of blockchain transactions (e.g. for web wallets) +* encrypted P2P communication +* on-chain voting + +The extension provides the following additional functionality: + +* creation of encrypted vaults for storing private keys, using the AES-GCM encryption algorithm +* support for creation and update of digital identities based on [W3C DID](https://github.com/bi-foundation/FIS/blob/feature/DID/FIS/DID.md) +* support for importing and creating FCT and EC keys +* ECDSA digital signatures over the `secp256k1` (a.k.a. Bitcoin) elliptic curve +* EdDSA digital signatures over the `edwards25519` curve, as specified in [RFC8032](https://tools.ietf.org/html/rfc8032) +* RSA digital signatures +* paper backups for vaults + +## Build +Run `ng build --aot` to build the project. The build artifacts will be stored in the `dist/` directory. + +Run `ng build --aot --watch` to build the project for development. Any changes you make to the source code should be +automatically reflected in your location version of Kambani. + +Run `ng build --prod` for a production build. The build artifacts will be stored in the `dist/` directory. + +## Local installation of the extension +To install the extension locally: + +1. Checkout the repository +1. Run `npm install` +1. Run `ng build --prod` or `ng build --aot --watch` (for a development build) from the project root directory +1. Open a Chrome browser and go to the special URL `chrome://extensions` +1. Make sure Developer Mode is switched on in the top-right corner +1. Click on the `Load Unpacked` link in the top-left corner +1. Choose the `dist/kambani` folder in the project root directory + +The plugin should now be visible in your Chrome browser and should be listed on the `chrome://extensions` page + +## Integration into existing websites +Kambani is designed to allow easy communication with existing websites. This is achieved by using [DOM CustomEvents](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent). +The rest of this document assumes that you are familiar with `CustomEvents`, in particular the way in which data can be passed +inside those events, so please take a minute to read through the docs if you are not. + +The basic workflow for websites looking to integrate with Kambani is as follows: + +* the website dispatches a `SigningRequest` `CustomEvent`, which contains the data that needs to be reviewed and signed by the user +* the user receives a notification of the incoming request(s) +* the user reviews the request(s) and either signs or cancels it(them) +* Kambani dispatches a `SigningResponse` `CustomEvent`, which can be intercepted by the website. The `SigningResponse` contains the + signature from the user, together with additional metadata such as the public key and the type of signature, or an indication that + the request has not been approved + +To make it easier for application developers, the extension provides both generic data signing requests, in which an arbitrary +JSON object can be sent to the user, as well as application specific requests such as [PegNet](https://pegnet.org/) transfer, +conversion and burning requests. + +### Example application +If you prefer to look at an example application, which integrates with Kambani, instead of reading through the rest of the +documentation, you can find a demo in the `examples/demo-voting-app` folder. + +The application provides a working demonstration of obtaining the user's FCT & EC addresses, as well as multiple examples of +signing requests with FCT, EC and DID keys. Check the associated README for more information. + +### Detecting Kambani +A website can detected if Kambani is installed in the user's browser by dispatching an `IsKambaniInstalled` `CustomEvent` and +listening for a `KambaniInstalled` event fired by Kambani in response. + +Example code: +```javascript + window.addEventListener("KambaniInstalled", event => { + console.log("Kambani is installed") + }) + window.dispatchEvent(new CustomEvent("IsKambaniInstalled")) +``` + +### FCT addresses request +A website can request access to the users public FCT addresses. This can be accomplished by dispatching a `GetFCTAddresses` +`CustomEvent` and listening for an `FCTAddresses` event fired by Kambani in response, following the user's approval. + +Example code for requesting the user's FCT addresses: +```javascript + const fctAddressesEvent = new CustomEvent("GetFCTAddresses"); + window.dispatchEvent(fctAddressesEvent); +``` + +Example code for listening for a response from Kambani: +```javascript + window.addEventListener("FCTAddresses", event => { + if (event.detail.success) { + const fctAddresses = event.detail.fctAddresses; + + if (fctAddresses.length > 0) { + console.log(JSON.stringify(fctAddresses, null, 2)); + } + } else { + console.log("GetFCTAddresses request not approved"); + } + }); +``` + +Note that if the user does not grant access to their FCT addresses, the `event.detail.success` value will be set to `false`. + +If the user does grant access, subsequent requests from the same domain can obtain the FCT addresses without requiring explicit +approval from the user, unless the user manually revokes the access from their settings inside Kambani. + +If access to the FCT addresses is granted, this also exposes the website to an `FCTAddressesChanged` event, which is used to +notify the website of any additions or removals of FCT addresses inside Kambani (more on this in the following sections). + +### EC addresses request +Similarly to FCT addresses, websites can request access to the EC addresses of the user. + +Example code for requesting the user's EC addresses: +```javascript + const ecAddressesEvent = new CustomEvent("GetECAddresses"); + window.dispatchEvent(ecAddressesEvent); +``` + +Example code for listening for a response from Kambani: +```javascript + window.addEventListener("ECAddresses", event => { + if (event.detail.success) { + const ecAddresses = event.detail.ecAddresses; + + if (ecAddresses.length > 0) { + console.log(JSON.stringify(ecAddresses, null, 2)); + } + } else { + console.log("GetECAddresses request not approved"); + } + }); +``` + +The same disclaimers about subsequent access and changes to EC addresses apply as for FCT addresses. + +### Changes to FCT or EC addresses +Once a given domain is granted access to the user's FCT and EC addresses, websites under this domain can listen for changes in +the user's addresses in the background in order to have an up-to-date state. This is accomplished by registering event listeners +for the `FCTAddressesChanged` and `ECAddressesChanged` events. + +Example code for listening to EC addresses changes: +```javascript + window.addEventListener("ECAddressesChanged", event => { + console.log(event.detail); + if (event.detail.removed) { + const removedEcAddresses = event.detail.removed.map(addr => JSON.stringify(addr)); + console.log(removedEcAddresses); + } + + if (event.detail.added) { + for (const addedAddress of event.detail.added) { + console.log(addedAddress); + } + } + }); +``` + +Identical code can be used to monitor for changes in FCT addresses using the `FCTAddressesChanged` event. A complete working example +is available in the `examples/demo-voting-app`. + +### Data signing request +The most flexible type of signing request supported by Kambani is a "data" signing request. Websites can request a signature of +any JSON object by using this type of request. The signature is produced over the output bytes of SHA-256 of the stringified +JSON object. + +In order to send a data signing request, the website needs to dispatch a `SigningRequest` event containing a `detail` object with +the following schema: + +``` +{ + "requestId": unique identifier (int/string, required), + "requestType": "data" (required), + "requestInfo": { + "data": JSON object to sign (required), + "keyType": one of "fct", "ec", "didKey" or "managementKey" (required), + "keyIdentifier": string (optional), + "did": string (optional) + }, +} +``` + +The semantics of the fields in the above schema is: + +* `requestId`: should be a unique identifier for this request, which can be used for bookkeeping purposes in the website +* `requestType`: must be "data" and signifies to Kambani how this request should be treated +* `requestInfo.keyType`: what type of key must be used by the user to sign the message. Kambani will filter the keys +stored in the encrypted vault based on the provided `keyType` and will not allow signature from a different `keyType` +* `requestInfo.keyIdentifier`: the website has the ability to request a signature from a specific key. The user will only +be able to sign with the exact requested key. If a `keyType` of `fct` or `ec` is specified, the identifier should be +the public FCT or EC key. If a `didKey` or `managementKey` is specified, the `keyIdentifier` value should be the key +identifier of the DID or management key (see the [DID spec](https://github.com/bi-foundation/FIS/blob/feature/DID/FIS/DID.md)). +If there is no value specified for `keyIdentifier`, then the user will be able to choose with which key to sign from all +available keys of the given `keyType` +* `requestInfo.did`: must be a valid DID, as given in the [DID spec](https://github.com/bi-foundation/FIS/blob/feature/DID/FIS/DID.md). +This field can be set to request a signature from a specific DID and it must be used only if the `keyType` is `didKey` or +`managementKey`. The field is required if one of those `keyType`s is used AND a `keyIdentifier` is used as well + +### PegNet requests +Kambani supports several types of PegNet specific requests to ease development of websites integrating with PegNet, such as +web wallets, trading platforms, e-shops, content platforms, etc. + +To ensure the security of users, the PegNet requests initiated from a website contain only metadata, as opposed to the +transactions themselves. The building of the raw transaction bytes that are signed by the user is done inside Kambani. +Since the extension is fully open-sourced and anyone can audit the code, this provides strong guarantees that users +cannot be tricked into sending funds to the wrong address by a malicious website, for example. + +#### FCT burning +To initiate a request for an FCT to pFCT burn, a website must send a `SigningRequest` `CustomEvent` with the following +schema of the `detail` object: + +``` +{ + "requestId": unique identifier (int/string, required), + "requestType": "pegnet", + "requestInfo": { + "txType": "burn", + "inputAddress": "FA..." (required), + "inputAmount": integer (required), + } +} +``` + +In case of a successfully signed transaction, the `SigningResponse` event contains a `transaction` field, which has the +raw signed transaction bytes ready to be submitted to the `factoid-submit` API endpoint of `factomd` without any modification. + +#### PegNet transfer +To initiate a request for a PegNet transfer, a website must send a `SigningRequest` `CustomEvent` with the following +data in the `detail` object: + +``` +{ + "requestId": unique identifier (int/string, required), + "requestType": "pegnet", + "requestInfo": { + "txType": "transfer", + "inputAddress": "FA..." (required), + "inputAsset": string (required), + "inputAmount": integer (required) + "outputAddress": "FA..." (required), + "txMetadata": JSON object (optional), + } +} +``` + +In case of a successfully signed transaction, the `SigningResponse` event contains an `entry` field, which is an array of +two values: the first being the `extIDs` for the transfer entry and the second being the entry content. + +Note that: + +* the entry is not signed by an EC key and so is **not paid for by the user**. Websites using the PegNet transfer request +type are currently expected to pay for those entries themselves, before broadcasting the entry to the Factom blockchain. +We plan to modify this request type to allow websites to request payment from the user in a future version of Kambani +* the `txMetadata` field can be used to record optional metadata for the conversion entry. The JSON object passed to this field +will be put directly inside the `metadata` field of the PegNet transfer entry. + +#### PegNet conversion +To initiate a request for a PegNet conversion, a website must send a `SigningRequest` `CustomEvent` with the following +schema of the `detail` object: + +``` +{ + "requestId": unique identifier (int/string, required), + "requestType": "pegnet", + "requestInfo": { + "txType": "conversion", + "inputAddress": "FA..." (required), + "inputAsset": string (required), + "inputAmount": integer (required) + "outputAsset": string (required), + "txMetadata": JSON object (optional), + } +} +``` + +In case of a successfully signed transaction, the `SigningResponse` event contains an `entry` field, which is an array of +two values: the first being the `extIDs` for the conversion entry and the second being the entry content. + +The same disclaimers for the payment of the entry and the `txMetadata` as for PegNet transfer transactions apply. + +## Security and Privacy +We treat the security and privacy of Kambani users with utmost care. + +All private keys in Kambani are stored encrypted in-memory and at-rest using state-of-the-art AES-GCM encryption, with a strong +passphrase. + +Decryption of the private keys is only done when signing an incoming message, or when importing them from an encrypted file, +thus greatly limiting the time window during which any sensitive cryptographic material is exposed in plaintext to a potential attacker. + +As of the latest version of the extension, to protect the privacy of users, any requests for accessing their FCT or EC +public keys need to be explicitly approved. This is done to prevent websites from silently accessing the public keys and +checking the user's FCT balance (and subsequently using the person's assumed net worth to modify the web site content, +such as prices on an e-shop, e.g.). In addition to this, preventing access to the user's addresses, removes the possibility +of tracking the user across different websites. diff --git a/angular.json b/angular.json new file mode 100644 index 0000000..f065347 --- /dev/null +++ b/angular.json @@ -0,0 +1,110 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "kambani": { + "root": "", + "sourceRoot": "src", + "projectType": "application", + "prefix": "app", + "schematics": { + "@schematics/angular:component": { + "styleext": "scss" + } + }, + "architect": { + "build": { + "builder": "@angular-builders/custom-webpack:browser", + "options": { + "customWebpackConfig": { + "path": "./webpack.config.js" + }, + "outputPath": "dist/kambani", + "index": "src/index.html", + "main": "src/main.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "src/tsconfig.app.json", + "assets": [ + "src/assets", + "src/manifest.json", + "src/background.js", + "src/contentScript.js" + ], + "styles": [ + "node_modules/@fortawesome/fontawesome-free/scss/fontawesome.scss", + "node_modules/@fortawesome/fontawesome-free/scss/solid.scss", + "node_modules/@fortawesome/fontawesome-free/scss/regular.scss", + "node_modules/@fortawesome/fontawesome-free/scss/brands.scss", + "src/assets/bootstrap.min.css", + "node_modules/ngx-toastr/toastr.css", + "node_modules/highlight.js/styles/solarized-dark.css", + "src/styles.scss" + ], + "scripts": [ + "node_modules/jquery/dist/jquery.min.js", + "node_modules/popper.js/dist/umd/popper.min.js", + "node_modules/bootstrap/dist/js/bootstrap.min.js" + ] + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "extractCss": true, + "namedChunks": false, + "aot": true, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "2mb", + "maximumError": "5mb" + } + ] + } + } + }, + "serve": { + "builder": "@angular-builders/dev-server:generic", + "options": { + "browserTarget": "kambani:build" + }, + "configurations": { + "production": { + "browserTarget": "kambani:build:production" + } + } + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "kambani:build" + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "src/tsconfig.app.json", + "src/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**" + ] + } + } + } + } + }, + "defaultProject": "kambani" +} diff --git a/examples/demo-voting-app/README.md b/examples/demo-voting-app/README.md new file mode 100644 index 0000000..22d0552 --- /dev/null +++ b/examples/demo-voting-app/README.md @@ -0,0 +1,19 @@ +# Demo voting application + +This application provides a complete example of integrating with the Kambani extension by simulating a vote for the +current best football player. + +The features demonstrated in this application are: + +* accessing the FCT & EC keys stored in Kambani +* monitoring changes to the FCT & EC keys +* sending a data signing request to Kambani, which should be signed by an FCT, EC or DID key + +The logic of the application is in `js/index.js` and this file can be modified to experiment with different request types +and their behavior. Check the comments in the file for more explanations. + +The HTML page is designed to show changes to FCT & EC addresses as well as the responses returned by Kambani to the incoming +signature requests. + +To run the application **you need a `localhost` server**. If you have Python3 installed, you can run: `python -m http.server` +from the project root directory and access the application at `https://localhost:8000`. diff --git a/examples/demo-voting-app/index.html b/examples/demo-voting-app/index.html new file mode 100644 index 0000000..bb1131c --- /dev/null +++ b/examples/demo-voting-app/index.html @@ -0,0 +1,70 @@ + + + + + + + Demo Voting App + + + + +
+
+
+

Demo Voting DApp

+
+
+

Who is the best football player at the moment ?

+
+
+ + +
+
+ + +
+
+ + +
+ + + + +
+
+
+
+ + +
+
+
+
+ + +
+ +
+ + +
+
+
+
+ + + + + diff --git a/examples/demo-voting-app/js/index.js b/examples/demo-voting-app/js/index.js new file mode 100644 index 0000000..d3ac98a --- /dev/null +++ b/examples/demo-voting-app/js/index.js @@ -0,0 +1,329 @@ +new Vue({ + el: "#app", + mounted: function () { + /* + Listens for a SigningResponse after dispatching a SigningRequest event + */ + window.addEventListener("SigningResponse", event => { + console.log(event.detail); + this.voteSignature = JSON.stringify(event.detail, null, 2); + }); + + /* + Listens for a response after dispatching a GetFCTAddresses event + */ + window.addEventListener("FCTAddresses", event => { + console.log(event.detail); + if (event.detail.success) { + this.fctAddresses = event.detail.fctAddresses; + + if (this.fctAddresses.length > 0) { + this.fctAddressesStringified = JSON.stringify(this.fctAddresses, null, 2); + } + } else { + console.log("GetFCTAddresses request not approved"); + } + }); + + /* + Listens for a response after dispatching a GetECAddresses event + */ + window.addEventListener("ECAddresses", event => { + console.log(event.detail); + if (event.detail.success) { + this.ecAddresses = event.detail.ecAddresses; + + if (this.ecAddresses.length > 0) { + this.ecAddressesStringified = JSON.stringify(this.ecAddresses, null, 2); + } + } else { + console.log("GetECAddresses request not approved"); + } + }); + + /* + Listens for a response after dispatching a GetBlockSigningKeys event + */ + window.addEventListener("BlockSigningKeys", event => { + console.log(event.detail); + if (event.detail.success) { + console.log(event.detail.blockSigningKeys); + } else { + console.log("GetBlockSigningKeys request not approved"); + } + }); + + /* + Listens for a response after dispatching a GetPegnetAddresses event + */ + window.addEventListener("PegnetAddresses", event => { + console.log(event.detail); + }); + + /* + Listens for changes to the FCT addresses and updates them + */ + window.addEventListener("FCTAddressesChanged", event => { + console.log(event.detail); + if (event.detail.removed) { + const removedFctAddresses = event.detail.removed.map(addr => JSON.stringify(addr)); + this.fctAddresses = this.fctAddresses.filter(addr => !removedFctAddresses.includes(JSON.stringify(addr))); + } + + if (event.detail.added) { + for (const addedAddress of event.detail.added) { + this.fctAddresses.push(addedAddress); + } + } + + if (this.fctAddresses.length > 0) { + this.fctAddressesStringified = JSON.stringify(this.fctAddresses, null, 2); + } else { + this.fctAddressesStringified = "You currently have no FCT addresses or you haven't granted Kambani access to them"; + } + }); + + /* + Listens for changes to the EC addresses and updates them + */ + window.addEventListener("ECAddressesChanged", event => { + console.log(event.detail); + if (event.detail.removed) { + const removedEcAddresses = event.detail.removed.map(addr => JSON.stringify(addr)); + this.ecAddresses = this.ecAddresses.filter(addr => !removedEcAddresses.includes(JSON.stringify(addr))); + } + + if (event.detail.added) { + for (const addedAddress of event.detail.added) { + this.ecAddresses.push(addedAddress); + } + } + + if (this.ecAddresses.length > 0) { + this.ecAddressesStringified = JSON.stringify(this.ecAddresses, null, 2); + } else { + this.ecAddressesStringified = "You currently have no EC addresses or you haven't granted Kambani access to them" + } + }); + + /* + Listens for changes to the EtherLink addresses + */ + window.addEventListener("EtherLinkAddressesChanged", event => { + console.log(event.detail); + }); + + /* + Listens for changes to the Block Signing keys + */ + window.addEventListener("BlockSigningKeysChanged", event => { + console.log(event.detail); + }); + + /* + Dispatches GetFCTAddresses, GetECAddresses, GetPegnetAddresses and GetBlockSigningKeys events + in order to obtain all addresses and keys stored in the extension + */ + const fctAddressesEvent = new CustomEvent("GetFCTAddresses"); + window.dispatchEvent(fctAddressesEvent); + + const ecAddressesEvent = new CustomEvent("GetECAddresses"); + window.dispatchEvent(ecAddressesEvent); + + const pegAddressesEvent = new CustomEvent("GetPegnetAddresses"); + window.dispatchEvent(pegAddressesEvent); + + const blockSigningKeysEvent = new CustomEvent("GetBlockSigningKeys"); + window.dispatchEvent(blockSigningKeysEvent); + }, + data: { + selected: "Leo Messi", + voteSignature: undefined, + fctAddresses: [], + ecAddresses: [], + fctAddressesStringified: "You currently have no FCT addresses or you haven't granted Kambani access to them", + ecAddressesStringified: "You currently have no EC addresses or you haven't granted Kambani access to them" + }, + methods: { + signWithFCTAddress: function () { + this.voteSignature = undefined; + + const fctRequestWithoutSpecifiedKey = { + requestId: 18, + requestType: "data", + requestInfo: { + keyType: "fct", + data: { + purpose: "Ballon D'or Voting", + winner: this.selected + } + } + }; + + /* + Before dispatching this request please put for keyIdentifier a valid FCT address + which you have generated or imported in your Kambani extension + */ + const fctRequestWithSpecifiedKey = { + requestId: 19, + requestType: "data", + requestInfo: { + keyType: "fct", + data: { + purpose: "Ballon D'or Voting", + winner: this.selected + }, + keyIdentifier: "FA3YYXQX68BF6jUMqp63gSu5j5AmDoztstXoR8jUNpMxBJmw2rz5" + } + }; + + const event = new CustomEvent("SigningRequest", { + detail: fctRequestWithoutSpecifiedKey + }); + + window.dispatchEvent(event); + }, + signWithECAddress: function () { + /* + If there are no EC addresses in Kambani, an EC request without a + specified key will be dispatched. Kambani will display the signing + request to the user and will notify them of the lack of EC addresses, + suggesting to create a new or import an existing EC address. + + Otherwise, the first (oldest by creation time) EC address will be used + as keyIdentifier. + */ + + let ecRequest; + this.voteSignature = undefined; + + if (this.ecAddresses.length > 0) { + const firstECAddressObject = this.ecAddresses[0]; + const firstECAddress = Object.keys(firstECAddressObject)[0]; + const firstECAddressNickname = firstECAddressObject[firstECAddress]; + + ecRequest = { + requestId: 21, + requestType: "data", + requestInfo: { + keyType: "ec", + data: { + purpose: "Ballon D'or Voting", + winner: this.selected + }, + keyIdentifier: firstECAddress + } + }; + } else { + ecRequest = { + requestId: 20, + requestType: "data", + requestInfo: { + keyType: "ec", + data: { + purpose: "Ballon D'or Voting", + winner: this.selected + } + } + }; + } + + const event = new CustomEvent("SigningRequest", { + detail: ecRequest + }); + + window.dispatchEvent(event); + }, + signWithDID: function () { + this.voteSignature = undefined; + + /* + If there are no DIDs in Kambani, Kambani will display the signing + request to the user and will notify them of the lack of DIDs, + suggesting to create a new DID. + + Otherwise, the user will be able to choose the DID and DIDKey with + which to sign the request. + */ + const didRequestWithoutSpecifiedDID = { + requestId: 22, + requestType: "data", + requestInfo: { + keyType: "didKey", + data: { + purpose: "Ballon D'or Voting", + winner: this.selected + } + } + }; + + /* + Before dispatching this request, please put a valid DID and keyIdentifier + which you have created inside Kambani. If this is not done, Kambani + will automatically return an unsuccessful SigningResponse. + + Since the DID and DIDKey are explicitly specified in the request, the + user will not be able to choose a different DID or DIDKey with which to + sign the request. They would either have to sign it with the given + DID/key combination or cancel the request. + */ + const didRequestWithSpecifiedDIDAndKeyIdentifier = { + requestId: 23, + requestType: "data", + requestInfo: { + keyType: "didKey", + data: { + purpose: "Ballon D'or Voting", + winner: this.selected + }, + did: "did:factom:134709a8d2ab4ca0454a10f7d16007127b9359a849b762ed65855cc6286e2bac", + keyIdentifier: "my-signing-key" + } + }; + + const event = new CustomEvent("SigningRequest", { + detail: didRequestWithoutSpecifiedDID + }); + + window.dispatchEvent(event); + }, + signWithBlockSigningKey: function () { + this.voteSignature = undefined; + + const blockSigningKeyRequestWithoutSpecifiedKey = { + requestId: 24, + requestType: "data", + requestInfo: { + keyType: "blockSigningKey", + data: { + purpose: "Ballon D'or Voting", + winner: this.selected + } + } + }; + + /* + Before dispatching this request please put for keyIdentifier a valid Block Signing key + which you have imported in your Kambani extension + */ + const blockSigningKeyRequestWithSpecifiedKey = { + requestId: 25, + requestType: "data", + requestInfo: { + keyType: "blockSigningKey", + data: { + purpose: "Ballon D'or Voting", + winner: this.selected + }, + keyIdentifier: "4zvwRjXUKGfvwnParsHAS3HuSVzV5cA4McphgmoCtajS" + } + }; + + const event = new CustomEvent("SigningRequest", { + detail: blockSigningKeyRequestWithoutSpecifiedKey + }); + + window.dispatchEvent(event); + } + } +}); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..675213b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,10095 @@ +{ + "name": "kambani", + "version": "0.1.3", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@angular-builders/custom-webpack": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/@angular-builders/custom-webpack/-/custom-webpack-7.5.2.tgz", + "integrity": "sha512-QvH/4cr8RYC7+A67/ASCoizrFdnMDhs05ZZ+3wrkWlj+nbKI9eKTJIdfBsDm0qP+zEx1d9Hr6oz6CXNNwuMA9g==", + "dev": true, + "requires": { + "lodash": "^4.17.10" + } + }, + "@angular-builders/dev-server": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@angular-builders/dev-server/-/dev-server-7.3.1.tgz", + "integrity": "sha512-rFr0NyFcwTb4RkkboYQN5JeR9ZraOkfUrQYljMSe/O01MM3SJvE8LYJbsyMwGtp71Rc8T6JrpdxaNEeYCV/4PA==", + "dev": true + }, + "@angular-devkit/architect": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.11.4.tgz", + "integrity": "sha512-2zi6S9tPlk52vyqN67IvFoeNgd0uxtrPlwl3TdvJ3wrH7sYGJnkQ+EzAE7cKUGWAV989BbNtx2YxhRDHnN21Fg==", + "dev": true, + "requires": { + "@angular-devkit/core": "7.1.4", + "rxjs": "6.3.3" + } + }, + "@angular-devkit/build-angular": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.13.9.tgz", + "integrity": "sha512-onh07LhdxotDFjja0KKsDWNCwgpM/ymuRr5h0e+vT4AgklP2Uioz1CpzVOgxPIKkdVdGR9QgDinVsWAmY90J8g==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.13.9", + "@angular-devkit/build-optimizer": "0.13.9", + "@angular-devkit/build-webpack": "0.13.9", + "@angular-devkit/core": "7.3.9", + "@ngtools/webpack": "7.3.9", + "ajv": "6.9.1", + "autoprefixer": "9.4.6", + "circular-dependency-plugin": "5.0.2", + "clean-css": "4.2.1", + "copy-webpack-plugin": "4.6.0", + "file-loader": "3.0.1", + "glob": "7.1.3", + "istanbul-instrumenter-loader": "3.0.1", + "karma-source-map-support": "1.3.0", + "less": "3.9.0", + "less-loader": "4.1.0", + "license-webpack-plugin": "2.1.0", + "loader-utils": "1.2.3", + "mini-css-extract-plugin": "0.5.0", + "minimatch": "3.0.4", + "node-sass": "4.12.0", + "open": "6.0.0", + "parse5": "4.0.0", + "postcss": "7.0.14", + "postcss-import": "12.0.1", + "postcss-loader": "3.0.0", + "raw-loader": "1.0.0", + "rxjs": "6.3.3", + "sass-loader": "7.1.0", + "semver": "5.6.0", + "source-map-loader": "0.2.4", + "source-map-support": "0.5.10", + "speed-measure-webpack-plugin": "1.3.1", + "stats-webpack-plugin": "0.7.0", + "style-loader": "0.23.1", + "stylus": "0.54.5", + "stylus-loader": "3.0.2", + "terser-webpack-plugin": "1.2.2", + "tree-kill": "1.2.1", + "webpack": "4.29.0", + "webpack-dev-middleware": "3.5.1", + "webpack-dev-server": "3.1.14", + "webpack-merge": "4.2.1", + "webpack-sources": "1.3.0", + "webpack-subresource-integrity": "1.1.0-rc.6" + }, + "dependencies": { + "@angular-devkit/architect": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.13.9.tgz", + "integrity": "sha512-EAFtCs9dsGhpMRC45PoYsrkiExpWz9Ax15qXfzwdDRacz5DmdOVt+QpkLW1beUOwiyj/bhFyj23eaONK2RTn/w==", + "dev": true, + "requires": { + "@angular-devkit/core": "7.3.9", + "rxjs": "6.3.3" + } + }, + "@angular-devkit/core": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.9.tgz", + "integrity": "sha512-SaxD+nKFW3iCBKsxNR7+66J30EexW/y7tm8m5AvUH+GwSAgIj0ZYmRUzFEPggcaLVA4WnE/YWqIXZMJW5dT7gw==", + "dev": true, + "requires": { + "ajv": "6.9.1", + "chokidar": "2.0.4", + "fast-json-stable-stringify": "2.0.0", + "rxjs": "6.3.3", + "source-map": "0.7.3" + } + }, + "ajv": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", + "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", + "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" + } + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + }, + "source-map-support": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz", + "integrity": "sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + } + } + }, + "@angular-devkit/build-optimizer": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.13.9.tgz", + "integrity": "sha512-GQtCntthQHSBv5l1ZY5p00JOECb/WcE1qUBo5kFjp84z0fszDkhOy52M1kcWCX4PFzJaY4DKk58hbUE/2UN0jw==", + "dev": true, + "requires": { + "loader-utils": "1.2.3", + "source-map": "0.5.6", + "typescript": "3.2.4", + "webpack-sources": "1.3.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "dev": true + }, + "typescript": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.4.tgz", + "integrity": "sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==", + "dev": true + } + } + }, + "@angular-devkit/build-webpack": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.13.9.tgz", + "integrity": "sha512-6ypu6pzNmQxzATF4rTWEhGSl5hyGQ8a/3aCZF/ux+XGc3d4hi2HW+NWlDm1UEna6ZjNtgEPlgfP4q8BKrjRmfA==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.13.9", + "@angular-devkit/core": "7.3.9", + "rxjs": "6.3.3" + }, + "dependencies": { + "@angular-devkit/architect": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.13.9.tgz", + "integrity": "sha512-EAFtCs9dsGhpMRC45PoYsrkiExpWz9Ax15qXfzwdDRacz5DmdOVt+QpkLW1beUOwiyj/bhFyj23eaONK2RTn/w==", + "dev": true, + "requires": { + "@angular-devkit/core": "7.3.9", + "rxjs": "6.3.3" + } + }, + "@angular-devkit/core": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.9.tgz", + "integrity": "sha512-SaxD+nKFW3iCBKsxNR7+66J30EexW/y7tm8m5AvUH+GwSAgIj0ZYmRUzFEPggcaLVA4WnE/YWqIXZMJW5dT7gw==", + "dev": true, + "requires": { + "ajv": "6.9.1", + "chokidar": "2.0.4", + "fast-json-stable-stringify": "2.0.0", + "rxjs": "6.3.3", + "source-map": "0.7.3" + } + }, + "ajv": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", + "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", + "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" + } + } + } + }, + "@angular-devkit/core": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.1.4.tgz", + "integrity": "sha512-3cBVHjSQjMyE/mIyOX82ekdybNRQlN+kUfmdZS6oVW9aV48vdxcVbEGdl8t1H4enMf89u8kXiAAET9jFaqWopg==", + "dev": true, + "requires": { + "ajv": "6.5.3", + "chokidar": "2.0.4", + "fast-json-stable-stringify": "2.0.0", + "rxjs": "6.3.3", + "source-map": "0.7.3" + } + }, + "@angular-devkit/schematics": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-7.1.4.tgz", + "integrity": "sha512-+rn3ppcC3grsi9vV2uUIYh/5mUBEJ+JRCKW11BJoUqLMeu8W7h+vbVonyfwJXsk3FSTf9ZY0C7F7UqggRS3cWw==", + "dev": true, + "requires": { + "@angular-devkit/core": "7.1.4", + "rxjs": "6.3.3" + } + }, + "@angular/animations": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-7.1.4.tgz", + "integrity": "sha512-877LZ83scksJtflVz97CUWlSsZnxduBxPD+ls5OTrTT/bq3muzHCm8rgTO7S8fBwwrEVXLorvMAlhDPpMg5Swg==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/cli": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-7.1.4.tgz", + "integrity": "sha512-SruaZsmyq3+ymMPeMJSzhytvgtvzyzb1q58pUYX+vZjff2aMYOo0TVxJALwTOPIABICTqUTZmujbLG9uxVgxFA==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.11.4", + "@angular-devkit/core": "7.1.4", + "@angular-devkit/schematics": "7.1.4", + "@schematics/angular": "7.1.4", + "@schematics/update": "0.11.4", + "inquirer": "6.2.0", + "opn": "5.3.0", + "semver": "5.5.1", + "symbol-observable": "1.2.0" + } + }, + "@angular/common": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.1.4.tgz", + "integrity": "sha512-oQPCilcf1H/OXmt4z6PfGoCSb1YPRBAXGs/KRBARm3tYan2r5CmV0BFwpWXWQrEMt8YQqqLiBQUQ64d8+VFm8Q==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/compiler": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.1.4.tgz", + "integrity": "sha512-AvYXtjEJ27Rhv4c27DXNEa58Lit63jdydzbz7VuyFhNU+FwDUK2DC4gZe0nWZsf7HUniJezVRFkECDCZQeSKCQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/compiler-cli": { + "version": "7.2.15", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.2.15.tgz", + "integrity": "sha512-+AsfyKawmj/sa+m4Pz8VSRFbCfx/3IOjAuuEjhopbyr154YpPDSu8NTbcwzq3yfbVcPwK4/4exmbQzpsndaCTg==", + "dev": true, + "requires": { + "canonical-path": "1.0.0", + "chokidar": "^2.1.1", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.7.2", + "magic-string": "^0.25.0", + "minimist": "^1.2.0", + "reflect-metadata": "^0.1.2", + "shelljs": "^0.8.1", + "source-map": "^0.6.1", + "tslib": "^1.9.0", + "yargs": "9.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 + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "chokidar": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz", + "integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==", + "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" + } + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "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 + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "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 + }, + "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" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "upath": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", + "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==", + "dev": true + }, + "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 + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yargs": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-9.0.1.tgz", + "integrity": "sha1-UqzCP+7Kw0BCB47njAwAf1CF20w=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "read-pkg-up": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^7.0.0" + } + }, + "yargs-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", + "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "@angular/core": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.1.4.tgz", + "integrity": "sha512-36uWLrmmlzf8JKaq2A5F2tPQEHvFSsbTQWOT559Drp1tzM2uSA7PysNHv5TXUshDn5i54S2EQFm4bj2YPp4Hzg==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/forms": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.1.4.tgz", + "integrity": "sha512-YB2lDRe7aMsaO5ZlbeFZGH+uTQOcpotFDKCmTckpefRJ7id6TlUSBYdYRH80qOQfPJGpsnqQvLZkL24UMasKtQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/language-service": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-7.1.4.tgz", + "integrity": "sha512-Pvrk3W3+6XfrmpCRcTumfyplv6AQJXKfDdPFSbhdpHJlpdcQRo6TckA85Yln5/CXZSAiPaZeiejQt2OogrIRLg==", + "dev": true + }, + "@angular/platform-browser": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.1.4.tgz", + "integrity": "sha512-lIFBKo6Uqty7qYDI9T8quFCUzUpGBgpzvDe14aAHFwZCft9rMS1J7PB1F26/dy2RBQE8tUyN2zp2xZQnYAhMcA==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/platform-browser-dynamic": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.1.4.tgz", + "integrity": "sha512-LtFd6XIz98BKjxrCRbaz2y0XSmVQSTzrvpAyNzKnzHAMn+4XpIpnzyV3Y6DeHolIBwLjFHFzGKMBwOHOwME4RQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/router": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-7.1.4.tgz", + "integrity": "sha512-5VVVcRsmuKrIWPnh5zF1ExXmIpCx2tlisJ7YTS2FFDXnqrZ9i2QgaxyJuyZE+Btg3t7LPF4tkhRQpjauNiHJYA==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@fortawesome/fontawesome-free": { + "version": "5.11.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.11.2.tgz", + "integrity": "sha512-XiUPoS79r1G7PcpnNtq85TJ7inJWe0v+b5oZJZKb0pGHNIV6+UiNeQWiFGmuQ0aj7GEhnD/v9iqxIsjuRKtEnQ==" + }, + "@ng-bootstrap/ng-bootstrap": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-4.0.4.tgz", + "integrity": "sha512-77wXARMgUA0VdOAnctOlmdkeTRLESTrFpxHY8jy48FKy7DOogcq+Fs0QCBiufi5YSu8m+rmlKtoeZ8Yg9YEsbQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@ngrx/store": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@ngrx/store/-/store-8.5.1.tgz", + "integrity": "sha512-KIQ9Ooro0tl3+ohTpSXvvVUlo40TTLWAWFx2Ys8pI1Yqr39O9da3PNWecLA/2q4YmFf1bO072lYmcBTp1+Shvg==" + }, + "@ngtools/webpack": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-7.3.9.tgz", + "integrity": "sha512-+ROpqfCXLdQwfP+UNDLk4p959ZrocpStkdd2Iy9CeOJ8yDkityqpstTwQC3oHzzu/95BiyZ0hrHbM6AsPPIvJg==", + "dev": true, + "requires": { + "@angular-devkit/core": "7.3.9", + "enhanced-resolve": "4.1.0", + "rxjs": "6.3.3", + "tree-kill": "1.2.1", + "webpack-sources": "1.3.0" + }, + "dependencies": { + "@angular-devkit/core": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.9.tgz", + "integrity": "sha512-SaxD+nKFW3iCBKsxNR7+66J30EexW/y7tm8m5AvUH+GwSAgIj0ZYmRUzFEPggcaLVA4WnE/YWqIXZMJW5dT7gw==", + "dev": true, + "requires": { + "ajv": "6.9.1", + "chokidar": "2.0.4", + "fast-json-stable-stringify": "2.0.0", + "rxjs": "6.3.3", + "source-map": "0.7.3" + } + }, + "ajv": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", + "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", + "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" + } + } + } + }, + "@schematics/angular": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-7.1.4.tgz", + "integrity": "sha512-4QVSmvQtOELek+FDq+k2ROeH9YrRrPJ6jWK179+qOruKSd4uTgEti/jy+fS0rfr52kDSGdDhz7XTh/QvQB89fg==", + "dev": true, + "requires": { + "@angular-devkit/core": "7.1.4", + "@angular-devkit/schematics": "7.1.4", + "typescript": "3.1.6" + } + }, + "@schematics/update": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.11.4.tgz", + "integrity": "sha512-InfsMJtdWwoqCPmtlJeXBwRPPgIXUzpzIkCCcayRe9gy6PkPUIlOjfgEZ4Mqm/HR46lqsI8xwZfUK7SLV//a2g==", + "dev": true, + "requires": { + "@angular-devkit/core": "7.1.4", + "@angular-devkit/schematics": "7.1.4", + "@yarnpkg/lockfile": "1.1.0", + "ini": "1.3.5", + "pacote": "9.1.1", + "rxjs": "6.3.3", + "semver": "5.5.1", + "semver-intersect": "1.4.0" + } + }, + "@types/chrome": { + "version": "0.0.81", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.81.tgz", + "integrity": "sha512-NKkQGMJSppFwUwMbEbYcq8/p/ACVHmQCUpWaqJVdHn81tU3by+YXnxw86KFa0tUSLamu35wtzFaMyY4TFaMceQ==", + "requires": { + "@types/filesystem": "*" + } + }, + "@types/filesystem": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.29.tgz", + "integrity": "sha512-85/1KfRedmfPGsbK8YzeaQUyV1FQAvMPMTuWFQ5EkLd2w7szhNO96bk3Rh/SKmOfd9co2rCLf0Voy4o7ECBOvw==", + "requires": { + "@types/filewriter": "*" + } + }, + "@types/filewriter": { + "version": "0.0.28", + "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.28.tgz", + "integrity": "sha1-wFTor02d11205jq8dviFFocU1LM=" + }, + "@types/jasmine": { + "version": "2.8.16", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.16.tgz", + "integrity": "sha512-056oRlBBp7MDzr+HoU5su099s/s7wjZ3KcHxLfv+Byqb9MwdLUvsfLgw1VS97hsh3ddxSPyQu+olHMnoVTUY6g==", + "dev": true + }, + "@types/jasminewd2": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.8.tgz", + "integrity": "sha512-d9p31r7Nxk0ZH0U39PTH0hiDlJ+qNVGjlt1ucOoTUptxb2v+Y5VMnsxfwN+i3hK4yQnqBi3FMmoMFcd1JHDxdg==", + "dev": true, + "requires": { + "@types/jasmine": "*" + } + }, + "@types/node": { + "version": "8.10.56", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.56.tgz", + "integrity": "sha512-5yWs9hy3UWdandOgvmmPCNJ3jI5/o8syatQWOmiAO/9/PptOQ+0O2ANKHltFhE4MGCt/QiVkoxQFUbeha9Yf4w==", + "dev": true + }, + "@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/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" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@webassemblyjs/ast": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz", + "integrity": "sha512-ZEzy4vjvTzScC+SH8RBssQUawpaInUdMTYwYYLh54/s8TuT0gBLuyUnppKsVyZEi876VmmStKsUs28UxPgdvrA==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.7.11", + "@webassemblyjs/helper-wasm-bytecode": "1.7.11", + "@webassemblyjs/wast-parser": "1.7.11" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.11.tgz", + "integrity": "sha512-zY8dSNyYcgzNRNT666/zOoAyImshm3ycKdoLsyDw/Bwo6+/uktb7p4xyApuef1dwEBo/U/SYQzbGBvV+nru2Xg==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.11.tgz", + "integrity": "sha512-7r1qXLmiglC+wPNkGuXCvkmalyEstKVwcueZRP2GNC2PAvxbLYwLLPr14rcdJaE4UtHxQKfFkuDFuv91ipqvXg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.11.tgz", + "integrity": "sha512-MynuervdylPPh3ix+mKZloTcL06P8tenNH3sx6s0qE8SLR6DdwnfgA7Hc9NSYeob2jrW5Vql6GVlsQzKQCa13w==", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.11.tgz", + "integrity": "sha512-T8ESC9KMXFTXA5urJcyor5cn6qWeZ4/zLPyWeEXZ03hj/x9weSokGNkVCdnhSabKGYWxElSdgJ+sFa9G/RdHNw==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.7.11" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.11.tgz", + "integrity": "sha512-nsAQWNP1+8Z6tkzdYlXT0kxfa2Z1tRTARd8wYnc/e3Zv3VydVVnaeePgqUzFrpkGUyhUUxOl5ML7f1NuT+gC0A==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.11.tgz", + "integrity": "sha512-JxfD5DX8Ygq4PvXDucq0M+sbUFA7BJAv/GGl9ITovqE+idGX+J3QSzJYz+LwQmL7fC3Rs+utvWoJxDb6pmC0qg==", + "dev": true + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.11.tgz", + "integrity": "sha512-cMXeVS9rhoXsI9LLL4tJxBgVD/KMOKXuFqYb5oCJ/opScWpkCMEz9EJtkonaNcnLv2R3K5jIeS4TRj/drde1JQ==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.11.tgz", + "integrity": "sha512-8ZRY5iZbZdtNFE5UFunB8mmBEAbSI3guwbrsCl4fWdfRiAcvqQpeqd5KHhSWLL5wuxo53zcaGZDBU64qgn4I4Q==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/helper-buffer": "1.7.11", + "@webassemblyjs/helper-wasm-bytecode": "1.7.11", + "@webassemblyjs/wasm-gen": "1.7.11" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.7.11.tgz", + "integrity": "sha512-Mmqx/cS68K1tSrvRLtaV/Lp3NZWzXtOHUW2IvDvl2sihAwJh4ACE0eL6A8FvMyDG9abes3saB6dMimLOs+HMoQ==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.7.11.tgz", + "integrity": "sha512-vuGmgZjjp3zjcerQg+JA+tGOncOnJLWVkt8Aze5eWQLwTQGNgVLcyOTqgSCxWTR4J42ijHbBxnuRaL1Rv7XMdw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.1" + } + }, + "@webassemblyjs/utf8": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.7.11.tgz", + "integrity": "sha512-C6GFkc7aErQIAH+BMrIdVSmW+6HSe20wg57HEC1uqJP8E/xpMjXqQUxkQw07MhNDSDcGpxI9G5JSNOQCqJk4sA==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.11.tgz", + "integrity": "sha512-FUd97guNGsCZQgeTPKdgxJhBXkUbMTY6hFPf2Y4OedXd48H97J+sOY2Ltaq6WGVpIH8o/TGOVNiVz/SbpEMJGg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/helper-buffer": "1.7.11", + "@webassemblyjs/helper-wasm-bytecode": "1.7.11", + "@webassemblyjs/helper-wasm-section": "1.7.11", + "@webassemblyjs/wasm-gen": "1.7.11", + "@webassemblyjs/wasm-opt": "1.7.11", + "@webassemblyjs/wasm-parser": "1.7.11", + "@webassemblyjs/wast-printer": "1.7.11" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.11.tgz", + "integrity": "sha512-U/KDYp7fgAZX5KPfq4NOupK/BmhDc5Kjy2GIqstMhvvdJRcER/kUsMThpWeRP8BMn4LXaKhSTggIJPOeYHwISA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/helper-wasm-bytecode": "1.7.11", + "@webassemblyjs/ieee754": "1.7.11", + "@webassemblyjs/leb128": "1.7.11", + "@webassemblyjs/utf8": "1.7.11" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.11.tgz", + "integrity": "sha512-XynkOwQyiRidh0GLua7SkeHvAPXQV/RxsUeERILmAInZegApOUAIJfRuPYe2F7RcjOC9tW3Cb9juPvAC/sCqvg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/helper-buffer": "1.7.11", + "@webassemblyjs/wasm-gen": "1.7.11", + "@webassemblyjs/wasm-parser": "1.7.11" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.11.tgz", + "integrity": "sha512-6lmXRTrrZjYD8Ng8xRyvyXQJYUQKYSXhJqXOBLw24rdiXsHAOlvw5PhesjdcaMadU/pyPQOJ5dHreMjBxwnQKg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/helper-api-error": "1.7.11", + "@webassemblyjs/helper-wasm-bytecode": "1.7.11", + "@webassemblyjs/ieee754": "1.7.11", + "@webassemblyjs/leb128": "1.7.11", + "@webassemblyjs/utf8": "1.7.11" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.7.11.tgz", + "integrity": "sha512-lEyVCg2np15tS+dm7+JJTNhNWq9yTZvi3qEhAIIOaofcYlUp0UR5/tVqOwa/gXYr3gjwSZqw+/lS9dscyLelbQ==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/floating-point-hex-parser": "1.7.11", + "@webassemblyjs/helper-api-error": "1.7.11", + "@webassemblyjs/helper-code-frame": "1.7.11", + "@webassemblyjs/helper-fsm": "1.7.11", + "@xtuc/long": "4.2.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.7.11.tgz", + "integrity": "sha512-m5vkAsuJ32QpkdkDOUPGSltrg8Cuk3KBx4YrmAGQwCZPRdUHXxG4phIOuuycLemHFr74sWL9Wthqss4fzdzSwg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/wast-parser": "1.7.11", + "@xtuc/long": "4.2.1" + } + }, + "@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.1", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.1.tgz", + "integrity": "sha512-FZdkNBDqBRHKQ2MEbSC17xnPFOhZxeJ2YGSfr2BKf3sujG49Qe3bB+rGCwQfIaA7WHnGeGkSijX4FuBCdrzW/g==", + "dev": true + }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "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" + } + }, + "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, + "optional": 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": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", + "dev": true + }, + "acorn-dynamic-import": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz", + "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==", + "dev": true + }, + "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" + } + }, + "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.5.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", + "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", + "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.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz", + "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==", + "dev": true + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "angular": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/angular/-/angular-1.8.0.tgz", + "integrity": "sha512-VdaMx+Qk0Skla7B5gw77a8hzlcOakwF8mjlW13DpIWIDlfqwAbSSLfd8N/qZnzEmQF4jC4iofInd3gE7vL8ZZg==" + }, + "angular-autofocus-fix": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/angular-autofocus-fix/-/angular-autofocus-fix-0.1.2.tgz", + "integrity": "sha512-P5l24by1q6CSaRW/yrjJis56SuECQwXGbwFOfrk6URn3ir+uDrabykF1vjQwKYPrhF2W/JdBfXeMwuKnQ94Bmg==" + }, + "angular-bootstrap-md": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/angular-bootstrap-md/-/angular-bootstrap-md-7.5.4.tgz", + "integrity": "sha512-LBVqSswd49hJ3YNTkXzwM0Cn1+9SQ2kVYQxX36ZjxaKNeDQKaUk3V2Rb/y8krhDJrPJh37pDzRmlCDObg37EIA==", + "requires": { + "tslib": "^1.9.0" + } + }, + "angular-highlight-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/angular-highlight-js/-/angular-highlight-js-2.0.1.tgz", + "integrity": "sha512-DZSPnIE+Zps1FUSCe2e7rFhyeZBABK42rsf+veg4TKMW61PoVZpoti6C2BgEK2ArlFaleQp0RgVrl/P3sbWhXQ==" + }, + "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=" + }, + "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" + } + }, + "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" + } + }, + "app-root-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.1.0.tgz", + "integrity": "sha1-mL9lmTJ+zqGZMJhm6BQDaP0uZGo=", + "dev": true + }, + "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, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "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-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, + "optional": true + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "array-union": { + "version": "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, + "optional": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "optional": 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, + "optional": 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 + }, + "async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "dev": true, + "requires": { + "lodash": "^4.17.11" + } + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "dev": true + }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", + "dev": true, + "optional": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true, + "optional": 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 + }, + "autoprefixer": { + "version": "9.4.6", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.6.tgz", + "integrity": "sha512-Yp51mevbOEdxDUy5WjiKtpQaecqYq9OqZSL04rSoCiry7Tc5I9FEyo3bfxiTJc1DfHeKwSFCUYbBAiOQ2VGfiw==", + "dev": true, + "requires": { + "browserslist": "^4.4.1", + "caniuse-lite": "^1.0.30000929", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.13", + "postcss-value-parser": "^3.3.1" + } + }, + "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, + "optional": true + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true, + "optional": true + }, + "axios": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", + "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", + "requires": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + } + }, + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" + } + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "requires": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "requires": { + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helpers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "requires": { + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "requires": { + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "requires": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", + "requires": { + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "requires": { + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", + "requires": { + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" + }, + "dependencies": { + "regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + } + } + }, + "babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "requires": { + "regenerator-transform": "^0.10.0" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-preset-es2015": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", + "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", + "requires": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.24.1", + "babel-plugin-transform-es2015-classes": "^6.24.1", + "babel-plugin-transform-es2015-computed-properties": "^6.24.1", + "babel-plugin-transform-es2015-destructuring": "^6.22.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", + "babel-plugin-transform-es2015-for-of": "^6.22.0", + "babel-plugin-transform-es2015-function-name": "^6.24.1", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-umd": "^6.24.1", + "babel-plugin-transform-es2015-object-super": "^6.24.1", + "babel-plugin-transform-es2015-parameters": "^6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", + "babel-plugin-transform-regenerator": "^6.24.1" + } + }, + "babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "requires": { + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "requires": { + "source-map": "^0.5.6" + } + } + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babelify": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz", + "integrity": "sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU=", + "requires": { + "babel-core": "^6.0.14", + "object-assign": "^4.0.0" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "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" + } + } + } + }, + "base-58": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/base-58/-/base-58-0.0.1.tgz", + "integrity": "sha1-hdPnAlEHVmGTM4j4MdHri49jFOM=" + }, + "base-x": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.5.tgz", + "integrity": "sha512-C3picSgzPSLE+jW3tcBzJoGwitOtazb5B+5YmAxZm2ybmTi9LNgAtDO/jjVEBZwHoXmDBZ9m/IELj3elJVRBcA==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" + }, + "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, + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + }, + "dependencies": { + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "optional": 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.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.0.tgz", + "integrity": "sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw==", + "dev": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "dev": true, + "optional": true, + "requires": { + "inherits": "~2.0.0" + } + }, + "bluebird": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", + "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==" + }, + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "dev": true, + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + } + }, + "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" + } + }, + "bootstrap": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.3.1.tgz", + "integrity": "sha512-rXqOmH1VilAt2DyPzluTi2blhk17bO7ef+zLLPlWvG494pDxcM234pJ8wTc/6R40UWizAIIMgxjvxZg5kmsbag==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "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=" + }, + "browser-passworder": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/browser-passworder/-/browser-passworder-2.0.3.tgz", + "integrity": "sha1-b90gguUWoXbtvLPc7gt/n85PeRc=", + "requires": { + "browserify-unibabel": "^3.0.0" + } + }, + "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-unibabel": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/browserify-unibabel/-/browserify-unibabel-3.0.0.tgz", + "integrity": "sha1-WmuPD3BM44jTkn30czfiWDD3Hdo=" + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.5.6.tgz", + "integrity": "sha512-o/hPOtbU9oX507lIqon+UvPYqpx3mHc8cV3QemSBTXwkG8gSQSK6UKvXcE/DcleU3+A59XTUHyCvZ5qGy8xVAg==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000963", + "electron-to-chromium": "^1.3.127", + "node-releases": "^1.1.17" + } + }, + "bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", + "requires": { + "base-x": "^3.0.2" + } + }, + "buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "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 + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "cacache": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + } + }, + "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" + } + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true, + "optional": 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, + "optional": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "caniuse-lite": { + "version": "1.0.30000967", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000967.tgz", + "integrity": "sha512-rUBIbap+VJfxTzrM4akJ00lkvVb5/n5v3EGXfWzSH5zT8aJmGzjA8HWhJ4U6kCpzxozUSnB+yvAYDRPY6mRpgQ==", + "dev": true + }, + "canonical-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz", + "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true, + "optional": 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" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "chart.js": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.3.tgz", + "integrity": "sha512-+2jlOobSk52c1VU6fzkh3UwqHMdSlgH1xFv9FKMqHiNCpXsGPQa/+81AFa+i3jZ253Mq9aAycPwDjnn1XbRNNw==", + "requires": { + "chartjs-color": "^2.1.0", + "moment": "^2.10.2" + } + }, + "chartjs-color": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz", + "integrity": "sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==", + "requires": { + "chartjs-color-string": "^0.6.0", + "color-convert": "^1.9.3" + } + }, + "chartjs-color-string": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz", + "integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==", + "requires": { + "color-name": "^1.0.0" + } + }, + "chokidar": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", + "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.0", + "braces": "^2.3.0", + "fsevents": "^1.2.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "lodash.debounce": "^4.0.8", + "normalize-path": "^2.1.1", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0", + "upath": "^1.0.5" + } + }, + "chownr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz", + "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "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" + } + }, + "circular-dependency-plugin": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.0.2.tgz", + "integrity": "sha512-oC7/DVAyfcY3UWKm0sN/oVoDedQDQiw/vIiAnuTWTpE5s0zWf7l3WY417Xw/Fbi/QbAjctAkxgMiS9P0s3zkmA==", + "dev": true + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "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" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "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": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, + "clone-deep": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-2.0.2.tgz", + "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==", + "dev": true, + "requires": { + "for-own": "^1.0.0", + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.0", + "shallow-clone": "^1.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "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 + }, + "codelyzer": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-4.5.0.tgz", + "integrity": "sha512-oO6vCkjqsVrEsmh58oNlnJkRXuA30hF8cdNAQV9DytEalDwyOFRvHMnlKFzmOStNerOmPGZU9GAHnBo4tGvtiQ==", + "dev": true, + "requires": { + "app-root-path": "^2.1.0", + "css-selector-tokenizer": "^0.7.0", + "cssauron": "^1.4.0", + "semver-dsl": "^1.0.1", + "source-map": "^0.5.7", + "sprintf-js": "^1.1.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 + }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "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==", + "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=" + }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true + }, + "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, + "optional": 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 + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "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" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "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, + "optional": 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.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", + "dev": true + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.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==", + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "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" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-webpack-plugin": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz", + "integrity": "sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==", + "dev": true, + "requires": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "globby": "^7.1.1", + "is-glob": "^4.0.0", + "loader-utils": "^1.1.0", + "minimatch": "^3.0.4", + "p-limit": "^1.0.0", + "serialize-javascript": "^1.4.0" + } + }, + "core-js": { + "version": "2.6.10", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.10.tgz", + "integrity": "sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==" + }, + "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=" + }, + "cosmiconfig": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-4.0.0.tgz", + "integrity": "sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ==", + "dev": true, + "requires": { + "is-directory": "^0.3.1", + "js-yaml": "^3.9.0", + "parse-json": "^4.0.0", + "require-from-string": "^2.0.1" + }, + "dependencies": { + "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" + } + } + } + }, + "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": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "dev": true, + "optional": true, + "requires": { + "lru-cache": "^4.0.1", + "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-parse": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.7.0.tgz", + "integrity": "sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs=", + "dev": true + }, + "css-selector-tokenizer": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz", + "integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==", + "dev": true, + "requires": { + "cssesc": "^0.1.0", + "fastparse": "^1.1.1", + "regexpu-core": "^1.0.0" + } + }, + "cssauron": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", + "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", + "dev": true, + "requires": { + "through": "X.X.X" + } + }, + "cssesc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", + "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", + "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, + "optional": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "cyclist": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", + "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "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 + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", + "dev": true + }, + "default-gateway": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-2.7.2.tgz", + "integrity": "sha512-lAc4i9QJR0YHSDFdzeBQKfZ1SRDG3hsJNEkrpcZa8QhBfidLAilT60BDEIVUUGqosFp425KOgB3uYqcnQrWafQ==", + "dev": true, + "requires": { + "execa": "^0.10.0", + "ip-regex": "^2.1.0" + } + }, + "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": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", + "dev": true, + "requires": { + "globby": "^6.1.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "p-map": "^1.1.1", + "pify": "^3.0.0", + "rimraf": "^2.2.8" + }, + "dependencies": { + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + } + } + }, + "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, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true, + "optional": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "dependency-graph": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.7.2.tgz", + "integrity": "sha512-KqtH4/EZdtdfWX0p6MGP9jljvxSY6msy/pRUD4jgNwVpv3v1QmNLlsB3LDSSUg79BRVSn7jI1QPRtArGABovAQ==", + "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-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "requires": { + "repeating": "^2.0.0" + } + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", + "dev": true + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "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" + } + }, + "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 + }, + "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" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "optional": 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 + }, + "electron-to-chromium": { + "version": "1.3.133", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.133.tgz", + "integrity": "sha512-lyoC8aoqbbDqsprb6aPdt9n3DpOZZzdz/T4IZKsR0/dkZIxnJVUjjcpOSwA66jPRIOyDAamCTAUqweU05kKNSg==", + "dev": true + }, + "elliptic": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "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" + } + }, + "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" + } + }, + "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" + } + }, + "es6-promise": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz", + "integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==", + "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=" + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "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.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "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" + } + }, + "execa": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", + "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "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.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "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 + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "optional": 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.0.3", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", + "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "dependencies": { + "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" + } + } + } + }, + "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, + "optional": true + }, + "factom": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/factom/-/factom-1.2.7.tgz", + "integrity": "sha512-N26YCh7STKQz1ub0yCfM6pkS95I6hld/sHsVIsXWoDk8CDejr2WM+50AY8ZtX1BnsZnxd25sLDIaMfGJR5Gsuw==", + "requires": { + "axios": "^0.19.0", + "base-58": "0.0.1", + "bluebird": "^3.7.1", + "hash.js": "^1.1.7", + "long": "^4.0.0", + "retry": "^0.12.0", + "tweetnacl": "^1.0.1" + }, + "dependencies": { + "bluebird": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", + "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==" + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" + } + } + }, + "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-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 + }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "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" + } + }, + "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": "3.0.1", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz", + "integrity": "sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==", + "dev": true, + "requires": { + "loader-utils": "^1.0.2", + "schema-utils": "^1.0.0" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "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.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + } + }, + "find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "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" + } + }, + "follow-redirects": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz", + "integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==", + "dev": true, + "requires": { + "debug": "^3.2.6" + }, + "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" + } + }, + "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 + } + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true, + "optional": 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, + "optional": 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" + } + }, + "fs-minipass": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "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" + } + }, + "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.7", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", + "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.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": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "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.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.3", + "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.5", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.2.0", + "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.6.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 + } + } + }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "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" + } + }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true, + "optional": true, + "requires": { + "globule": "^1.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": "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 + }, + "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, + "optional": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "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, + "optional": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "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": "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" + } + } + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==" + }, + "globby": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + }, + "globule": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", + "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", + "dev": true, + "optional": true, + "requires": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "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 + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true, + "optional": 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, + "optional": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "dev": true, + "optional": 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" + } + } + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "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-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true, + "optional": 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==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "highlight.js": { + "version": "9.15.6", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.6.tgz", + "integrity": "sha512-zozTAWM1D6sozHo8kqhfYgsac+B+q0PmsjXeyDrYIHHcBN0zTVT66+s2GW1GZv7DbyaROdLXKdabwS/WqPyIdQ==" + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" + } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "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" + } + }, + "html-entities": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", + "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.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" + } + }, + "http-parser-js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz", + "integrity": "sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==", + "dev": true + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "dependencies": { + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + } + } + }, + "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" + } + } + } + }, + "http-proxy-middleware": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", + "dev": true, + "requires": { + "http-proxy": "^1.16.2", + "is-glob": "^4.0.0", + "lodash": "^4.17.5", + "micromatch": "^3.1.9" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "optional": 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.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "dev": true, + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "dependencies": { + "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" + } + }, + "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" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "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.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==" + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "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" + } + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": true + }, + "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-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" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "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-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 + }, + "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" + } + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "in-publish": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", + "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", + "dev": true, + "optional": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "optional": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "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.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "inquirer": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz", + "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.0", + "figures": "^2.0.0", + "lodash": "^4.17.10", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.1.0", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.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 + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "internal-ip": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-3.0.1.tgz", + "integrity": "sha512-NXXgESC2nNVtU+pqmC9e6R8B1GpKxzsAQhffvh5AL79qKnodd+L7tnEQmTiUAVngqLalPbSqRA7XGIEL5nCd0Q==", + "dev": true, + "requires": { + "default-gateway": "^2.6.0", + "ipaddr.js": "^1.5.2" + } + }, + "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==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "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-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-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-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "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=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "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" + } + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "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-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "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-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true, + "optional": 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, + "optional": 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=" + }, + "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, + "optional": true + }, + "istanbul-instrumenter-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.1.tgz", + "integrity": "sha512-a5SPObZgS0jB/ixaKSMdn6n/gXSrK2S6q/UfRJBT3e6gQmVjwZROTODQsYW5ZNwOu78hG62Y3fWlebaVOL0C+w==", + "dev": true, + "requires": { + "convert-source-map": "^1.5.0", + "istanbul-lib-instrument": "^1.7.3", + "loader-utils": "^1.1.0", + "schema-utils": "^0.3.0" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "schema-utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", + "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", + "dev": true, + "requires": { + "ajv": "^5.0.0" + } + } + } + }, + "istanbul-lib-coverage": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz", + "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz", + "integrity": "sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==", + "dev": true, + "requires": { + "babel-generator": "^6.18.0", + "babel-template": "^6.16.0", + "babel-traverse": "^6.18.0", + "babel-types": "^6.18.0", + "babylon": "^6.18.0", + "istanbul-lib-coverage": "^1.2.1", + "semver": "^5.3.0" + } + }, + "jasmine-core": { + "version": "2.99.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz", + "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=", + "dev": true + }, + "jasmine-spec-reporter": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz", + "integrity": "sha512-FZBoZu7VE5nR7Nilzy+Np8KuVIOxF4oXDPDknehCYBDE080EnlPu0afdZNmpGDBRCUBv3mj5qgqCRmk6W/K8vg==", + "dev": true, + "requires": { + "colors": "1.1.2" + } + }, + "jquery": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.0.tgz", + "integrity": "sha512-Xb7SVYMvygPxbFMpTFQiHh1J7HClEaThguL15N/Gg37Lri/qKyhRGZYzHRyLH8Stq3Aow0LsHO2O2ci86fCrNQ==" + }, + "js-base64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", + "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==", + "dev": true, + "optional": true + }, + "js-sha256": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", + "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" + }, + "js-sha512": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.8.0.tgz", + "integrity": "sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ==" + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + }, + "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" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, + "optional": true + }, + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=" + }, + "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, + "optional": 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, + "optional": true + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" + }, + "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, + "optional": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "karma-source-map-support": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.3.0.tgz", + "integrity": "sha512-HcPqdAusNez/ywa+biN4EphGz62MmQyPggUsDfsHqa7tSe4jdsxgvTKuDfIazjL+IOxpVWyT7Pr4dhAV+sxX5Q==", + "dev": true, + "requires": { + "source-map-support": "^0.5.5" + } + }, + "keccak": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-1.4.0.tgz", + "integrity": "sha512-eZVaCpblK5formjPjeTBik7TAg+pqnDrMHIffSvi9Lh7PQgM1+hSzakUeZFCk9DVVG0dacZJuaz2ntwlzZUIBw==", + "requires": { + "bindings": "^1.2.1", + "inherits": "^2.0.3", + "nan": "^2.2.1", + "safe-buffer": "^5.1.0" + } + }, + "keccak256": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/keccak256/-/keccak256-1.0.0.tgz", + "integrity": "sha512-8qv2vJdQk+Aa2tFXo8zYodm+6DgXqUOqvNJhj1p1V2pxQJT1oNKxNF+zWfhtKXNLZdLvyxjB/dvd9GwcvTHSQQ==", + "requires": { + "bn.js": "^4.11.8", + "keccak": "^1.4.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 + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "less": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/less/-/less-3.9.0.tgz", + "integrity": "sha512-31CmtPEZraNUtuUREYjSqRkeETFdyEHSEPAGq4erDlUXtda7pzNmctdljdIagSb589d/qXGWiiP31R5JVf+v0w==", + "dev": true, + "requires": { + "clone": "^2.1.2", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "mime": "^1.4.1", + "mkdirp": "^0.5.0", + "promise": "^7.1.1", + "request": "^2.83.0", + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "less-loader": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-4.1.0.tgz", + "integrity": "sha512-KNTsgCE9tMOM70+ddxp9yyt9iHqgmSs0yTZc5XH5Wo+g80RWRIYNqE58QJKm/yMud5wZEvz50ugRDuzVIkyahg==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "loader-utils": "^1.1.0", + "pify": "^3.0.0" + } + }, + "license-webpack-plugin": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.1.0.tgz", + "integrity": "sha512-vDiBeMWxjE9n6TabQ9J4FH8urFdsRK0Nvxn1cit9biCiR9aq1zBR0X2BlAkEiIG6qPamLeU0GzvIgLkrFc398A==", + "dev": true, + "requires": { + "@types/webpack-sources": "^0.1.5", + "webpack-sources": "^1.2.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, + "optional": 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" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "optional": 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" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" + }, + "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.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, + "lodash.tail": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", + "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=", + "dev": true + }, + "loglevel": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", + "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", + "dev": true + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "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, + "optional": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "magic-string": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.3.tgz", + "integrity": "sha512-6QK0OpF/phMz0Q2AxILkX2mFhi7m+WMwTRg0LQKq/WBB0cDP4rYH3Wp4/d3OTXlrPLVJT/RFqj8tFeAR4nk8AA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "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" + } + }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "dev": true + }, + "make-fetch-happen": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-4.0.1.tgz", + "integrity": "sha512-7R5ivfy9ilRJ1EMKIOziwrns9fGeAD4bAha8EB7BIiBBLHm2KeTUGCrICFt2rbHfzheTLynv50GnNTK1zDTrcQ==", + "dev": true, + "requires": { + "agentkeepalive": "^3.4.1", + "cacache": "^11.0.1", + "http-cache-semantics": "^3.8.1", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.1", + "lru-cache": "^4.1.2", + "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" + }, + "dependencies": { + "cacache": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", + "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", + "dev": true, + "requires": { + "bluebird": "^3.5.3", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "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.2", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + }, + "dependencies": { + "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" + } + } + } + }, + "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" + } + }, + "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" + } + }, + "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" + } + }, + "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 + } + } + }, + "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": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true, + "optional": 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" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "optional": 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" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true, + "optional": true + } + } + }, + "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 + }, + "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, + "optional": 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.5.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz", + "integrity": "sha512-IuaLjruM0vMKhUUT51fQdQzBYTX49dLj8w68ALEAe2A4iYNpIC4eMac67mt3NzycvjOlf07/kYxJDc0RTl1Wqw==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.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==" + }, + "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=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "minipass": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "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 + } + } + }, + "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": "2.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", + "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", + "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": "^2.0.1", + "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" + } + } + } + }, + "mixin-object": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", + "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", + "dev": true, + "requires": { + "for-in": "^0.1.3", + "is-extendable": "^0.1.1" + }, + "dependencies": { + "for-in": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", + "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=", + "dev": true + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + }, + "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" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "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 + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "nan": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", + "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==" + }, + "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" + } + }, + "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 + }, + "ng-bootstrap": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ng-bootstrap/-/ng-bootstrap-1.6.3.tgz", + "integrity": "sha1-1B/UIVTAWTQiy4PEc6OCiqdSW/U=", + "requires": { + "moment": "2.18.1" + }, + "dependencies": { + "moment": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", + "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=" + } + } + }, + "ng-click-outside": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/ng-click-outside/-/ng-click-outside-5.1.1.tgz", + "integrity": "sha512-ZGBjbbah8i3iYtSefFgvcVV9eQ1va+T+r2r2FrnUFP8izu2BmVviCIE7IhS3UfyjuavDDh+nbRl3uziN1lfBZA==" + }, + "ngrx-store-logger": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/ngrx-store-logger/-/ngrx-store-logger-0.2.4.tgz", + "integrity": "sha512-caF0xmjCBApn3u6LWLcXJ3Bsfl106C84cdUsK2+aIJ/s5qg0Gk4Wlq8hKBK5vJafTqJMu9Q1t77WfSHFoNZe7Q==" + }, + "ngx-device-detector": { + "version": "1.3.19", + "resolved": "https://registry.npmjs.org/ngx-device-detector/-/ngx-device-detector-1.3.19.tgz", + "integrity": "sha512-ZbYXufMm5l6a6ucJbcIDwcyyYJlbMhXi/11UdjsjS/zc7yaqlQoMuFHWxkmsuCvCuibx3TyWnuNkpNB9YesmjA==", + "requires": { + "tslib": "^1.9.0" + } + }, + "ngx-spinner": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ngx-spinner/-/ngx-spinner-7.0.0.tgz", + "integrity": "sha512-njAJ8emEgI70hhP4f+/LIsuiQLrY8nvV/l8xBDbuQJwGmSz2TjJnIVCz641IZXgUkKQEqK2FAsjYcUX/52eesw==", + "requires": { + "tslib": "^1.9.0" + } + }, + "ngx-toastr": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-9.1.2.tgz", + "integrity": "sha512-XhK3xA73ueF+YZawtqKs6+FQ1OMdx+EzCPihNTMSSXDthglfcgzDzia/OMSGjhx0h7/AK4N/BAbFau9L5RqVDg==", + "requires": { + "tslib": "^1.9.0" + } + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-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.8.5", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.5.tgz", + "integrity": "sha512-vFMQIWt+J/7FLNyKouZ9TazT74PRV3wgv9UT4cRjC8BffxFbKXkgIWR42URCPSnHm/QDz6BOlb2Q0U4+VQT67Q==" + }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "dev": true, + "optional": true, + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true, + "optional": true + } + } + }, + "node-libs-browser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz", + "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.0", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "0.0.4" + }, + "dependencies": { + "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" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "node-releases": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.19.tgz", + "integrity": "sha512-SH/B4WwovHbulIALsQllAVwqZZD1kPmKCqrhGfR29dXjLAVZMHvBjD3S6nL9D/J9QkmZ1R92/0wCMDKXUUvyyA==", + "dev": true, + "requires": { + "semver": "^5.3.0" + } + }, + "node-sass": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz", + "integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==", + "dev": true, + "optional": true, + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash": "^4.17.11", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.13.2", + "node-gyp": "^3.8.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "^2.2.4", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "optional": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "optional": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "nan": { + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", + "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", + "dev": true, + "optional": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "optional": true + } + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "optional": 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" + }, + "dependencies": { + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "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-package-arg": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.0.tgz", + "integrity": "sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.6.0", + "osenv": "^0.1.5", + "semver": "^5.5.0", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz", + "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", + "dev": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-2.2.3.tgz", + "integrity": "sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "npm-package-arg": "^6.0.0", + "semver": "^5.4.1" + } + }, + "npm-registry-fetch": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-3.9.0.tgz", + "integrity": "sha512-srwmt8YhNajAoSAaDWndmZgx89lJwIZ1GWxOuckH4Coek4uHv5S+o/l9FLQe/awA+JwTnj4FJHldxhlXdZEBmw==", + "dev": true, + "requires": { + "JSONStream": "^1.3.4", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.4.1", + "lru-cache": "^4.1.3", + "make-fetch-happen": "^4.0.1", + "npm-package-arg": "^6.1.0" + } + }, + "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, + "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" + } + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "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=" + }, + "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, + "optional": 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-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.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" + } + }, + "obs-store": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/obs-store/-/obs-store-3.0.2.tgz", + "integrity": "sha512-GzBr7KM2TYWoJSlF3sVo1cMIOeyxgXpEdegXLZyYONRpunFHsBdKwOba0ki17kN2stLaEwTNolJChGHafqM7Fw==", + "requires": { + "babel-preset-es2015": "^6.22.0", + "babelify": "^7.3.0", + "readable-stream": "^2.2.2", + "through2": "^2.0.3", + "xtend": "^4.0.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "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" + } + }, + "open": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.0.0.tgz", + "integrity": "sha512-/yb5mVZBz7mHLySMiSj2DcLtMBbFPJk5JBKEkHVZFxZAPzeg3L026O0T+lbdz1B2nyDnkClRSwRQJdeVUIF7zw==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "opn": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", + "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dev": true, + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "optional": true, + "requires": { + "lcid": "^1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "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-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": "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-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "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 + }, + "pacote": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.1.1.tgz", + "integrity": "sha512-f28Rq5ozzKAA9YwIKw61/ipwAatUZseYmVssDbHHaexF0wRIVotapVEZPAjOT7Eu3LYVqEp0NVpNizoAnYBUaA==", + "dev": true, + "requires": { + "bluebird": "^3.5.2", + "cacache": "^11.2.0", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.1.0", + "glob": "^7.1.3", + "lru-cache": "^4.1.3", + "make-fetch-happen": "^4.0.1", + "minimatch": "^3.0.4", + "minipass": "^2.3.5", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "normalize-package-data": "^2.4.0", + "npm-package-arg": "^6.1.0", + "npm-packlist": "^1.1.12", + "npm-pick-manifest": "^2.1.0", + "npm-registry-fetch": "^3.8.0", + "osenv": "^0.1.5", + "promise-inflight": "^1.0.1", + "promise-retry": "^1.1.1", + "protoduck": "^5.0.1", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.2", + "semver": "^5.6.0", + "ssri": "^6.0.1", + "tar": "^4.4.6", + "unique-filename": "^1.1.1", + "which": "^1.3.1" + }, + "dependencies": { + "cacache": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", + "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", + "dev": true, + "requires": { + "bluebird": "^3.5.3", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "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.2", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + }, + "dependencies": { + "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" + } + } + } + }, + "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" + } + }, + "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" + } + }, + "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" + } + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "tar": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", + "dev": 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" + } + }, + "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 + } + } + }, + "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.1.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", + "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "dev": true, + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "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-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" + } + }, + "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.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "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=" + }, + "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" + } + }, + "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, + "optional": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "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" + } + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "popper.js": { + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.7.tgz", + "integrity": "sha512-4q1hNvoUre/8srWsH7hnoSJ5xVmIL4qgz+s4qf2TnJIMyZFUFMGH+9vE7mXynAlHSZ/NdTmmow86muD0myUkVQ==" + }, + "portfinder": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz", + "integrity": "sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==", + "dev": true, + "requires": { + "async": "^1.5.2", + "debug": "^2.2.0", + "mkdirp": "0.5.x" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "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.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz", + "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-import": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.1.tgz", + "integrity": "sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw==", + "dev": true, + "requires": { + "postcss": "^7.0.1", + "postcss-value-parser": "^3.2.3", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-load-config": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.0.0.tgz", + "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==", + "dev": true, + "requires": { + "cosmiconfig": "^4.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" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==" + }, + "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.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "optional": true, + "requires": { + "asap": "~2.0.3" + } + }, + "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" + } + }, + "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 + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "psl": { + "version": "1.1.31", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", + "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", + "dev": true, + "optional": 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": "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" + } + }, + "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" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "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 + }, + "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.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "dev": true, + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "raw-loader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-1.0.0.tgz", + "integrity": "sha512-Uqy5AqELpytJTRxYT4fhltcKPj0TyaEpzJDcGz7DFJi+pQOOi3GjR/DOdxTkTsF+NzhnldIoG6TORaBlInUuqA==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^1.0.0" + } + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "dev": true, + "requires": { + "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 + } + } + }, + "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, + "optional": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "dependencies": { + "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, + "optional": 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, + "optional": true + } + } + }, + "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, + "optional": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "optional": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "optional": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "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" + } + }, + "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" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "optional": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==" + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "requires": { + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" + } + }, + "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" + } + }, + "regexpu-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=" + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + } + } + }, + "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 + }, + "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=", + "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, + "optional": 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" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "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 + }, + "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.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "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-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": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "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" + } + }, + "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.3.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", + "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", + "requires": { + "tslib": "^1.9.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==" + }, + "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 + }, + "sass-graph": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", + "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", + "dev": true, + "optional": true, + "requires": { + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^7.0.0" + } + }, + "sass-loader": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.1.0.tgz", + "integrity": "sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==", + "dev": true, + "requires": { + "clone-deep": "^2.0.1", + "loader-utils": "^1.0.1", + "lodash.tail": "^4.1.1", + "neo-async": "^2.5.0", + "pify": "^3.0.0", + "semver": "^5.5.0" + } + }, + "sax": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz", + "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=", + "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" + } + }, + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "dev": true, + "optional": true, + "requires": { + "js-base64": "^2.1.8", + "source-map": "^0.4.2" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "optional": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "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" + }, + "dependencies": { + "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 + } + } + }, + "semver": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", + "dev": true + }, + "semver-dsl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz", + "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", + "dev": true, + "requires": { + "semver": "^5.3.0" + } + }, + "semver-intersect": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz", + "integrity": "sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ==", + "dev": true, + "requires": { + "semver": "^5.0.0" + } + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "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.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "dependencies": { + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.7.0.tgz", + "integrity": "sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA==", + "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" + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "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.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "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" + } + }, + "shallow-clone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-1.0.0.tgz", + "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==", + "dev": true, + "requires": { + "is-extendable": "^0.1.1", + "kind-of": "^5.0.0", + "mixin-object": "^2.0.1" + }, + "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 + } + } + }, + "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 + }, + "shelljs": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz", + "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=" + }, + "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": { + "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" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "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 + } + } + }, + "socks": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.3.tgz", + "integrity": "sha512-+2r83WaRT3PXYoO/1z+RDEBE7Z2f9YcdQnJ0K/ncXXbV5gJ6wYfNAebYFYiiUjM6E4JyXnPY8cimwyvFYHVUUA==", + "dev": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "4.0.2" + } + }, + "socks-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz", + "integrity": "sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw==", + "dev": true, + "requires": { + "agent-base": "~4.2.0", + "socks": "~2.2.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.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + }, + "source-map-loader": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.4.tgz", + "integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==", + "dev": true, + "requires": { + "async": "^2.5.0", + "loader-utils": "^1.1.0" + } + }, + "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.9", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", + "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "sourcemap-codec": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz", + "integrity": "sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg==", + "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.3", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", + "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==", + "dev": true + }, + "spdy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.0.tgz", + "integrity": "sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==", + "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" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "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 + } + } + }, + "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" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "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 + }, + "readable-stream": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", + "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "speed-measure-webpack-plugin": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.1.tgz", + "integrity": "sha512-qVIkJvbtS9j/UeZumbdfz0vg+QfG/zxonAjzefZrqzkr7xOncLVXkeGbTpzd1gjCBM4PmVNkWlkeTVhgskAGSQ==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "optional": 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" + }, + "dependencies": { + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "optional": true + } + } + }, + "ssri": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", + "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.1" + } + }, + "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" + } + } + } + }, + "stats-webpack-plugin": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/stats-webpack-plugin/-/stats-webpack-plugin-0.7.0.tgz", + "integrity": "sha512-NT0YGhwuQ0EOX+uPhhUcI6/+1Sq/pMzNuSCBVT4GbFl/ac6I/JZefBcjlECNfAb1t3GOx5dEj1Z7x0cAxeeVLQ==", + "dev": true, + "requires": { + "lodash": "^4.17.4" + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true + }, + "stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "dev": true, + "optional": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "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" + } + }, + "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 + }, + "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" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "optional": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "optional": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "style-loader": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz", + "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^1.0.0" + } + }, + "stylus": { + "version": "0.54.5", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.5.tgz", + "integrity": "sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk=", + "dev": true, + "requires": { + "css-parse": "1.7.x", + "debug": "*", + "glob": "7.0.x", + "mkdirp": "0.5.x", + "sax": "0.5.x", + "source-map": "0.1.x" + }, + "dependencies": { + "glob": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", + "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "stylus-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.2.tgz", + "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==", + "dev": true, + "requires": { + "loader-utils": "^1.0.2", + "lodash.clonedeep": "^4.5.0", + "when": "~3.6.x" + } + }, + "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" + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true + }, + "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": "2.2.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", + "dev": true, + "optional": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.12", + "inherits": "2" + } + }, + "terser": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz", + "integrity": "sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==", + "dev": true, + "requires": { + "commander": "^2.19.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.10" + }, + "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 + }, + "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-support": { + "version": "0.5.12", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", + "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, + "terser-webpack-plugin": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.2.tgz", + "integrity": "sha512-1DMkTk286BzmfylAvLXwpJrI7dWa5BnFmscV/2dCr8+c56egFcbaeFAl7+sujAjdmpLam21XRdhA4oifLyiWWg==", + "dev": true, + "requires": { + "cacache": "^11.0.2", + "find-cache-dir": "^2.0.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "terser": "^3.16.1", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + }, + "dependencies": { + "cacache": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", + "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", + "dev": true, + "requires": { + "bluebird": "^3.5.3", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "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.2", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "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" + } + }, + "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" + } + }, + "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" + } + }, + "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" + } + }, + "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" + } + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "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-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 + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "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" + } + }, + "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" + } + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "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 + }, + "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" + } + }, + "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 + } + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "thunky": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.3.tgz", + "integrity": "sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==", + "dev": true + }, + "timers-browserify": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", + "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "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" + } + }, + "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": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" + }, + "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" + } + }, + "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, + "optional": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true, + "optional": true + } + } + }, + "tree-kill": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz", + "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==", + "dev": true + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true, + "optional": 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=" + }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.2" + } + }, + "ts-node": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", + "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "dev": true, + "requires": { + "arrify": "^1.0.0", + "buffer-from": "^1.1.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.6", + "yn": "^2.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + }, + "tslint": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.11.0.tgz", + "integrity": "sha1-mPMMAurjzecAYgHkwzywi0hYHu0=", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.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.7.0", + "minimatch": "^3.0.4", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.27.2" + }, + "dependencies": { + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "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, + "optional": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.1.tgz", + "integrity": "sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A==" + }, + "tweetnacl-util": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.0.tgz", + "integrity": "sha1-RXbBzuXi1j0gf+5S8boCgZSAvHU=" + }, + "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 + }, + "types": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/types/-/types-0.1.1.tgz", + "integrity": "sha1-hgxoWdETZik/g12Mla68+VApg44=" + }, + "typescript": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.6.tgz", + "integrity": "sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==", + "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" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz", + "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "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.1.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", + "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "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" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "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.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "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, + "optional": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true, + "requires": { + "indexof": "0.0.1" + } + }, + "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" + } + }, + "webpack": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.29.0.tgz", + "integrity": "sha512-pxdGG0keDBtamE1mNvT5zyBdx+7wkh6mh7uzMOo/uRQ/fhsdj5FXkh/j5mapzs060forql1oXqXN9HJGju+y7w==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/helper-module-context": "1.7.11", + "@webassemblyjs/wasm-edit": "1.7.11", + "@webassemblyjs/wasm-parser": "1.7.11", + "acorn": "^6.0.5", + "acorn-dynamic-import": "^4.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "chrome-trace-event": "^1.0.0", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.0", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "micromatch": "^3.1.8", + "mkdirp": "~0.5.0", + "neo-async": "^2.5.0", + "node-libs-browser": "^2.0.0", + "schema-utils": "^0.4.4", + "tapable": "^1.1.0", + "terser-webpack-plugin": "^1.1.0", + "watchpack": "^1.5.0", + "webpack-sources": "^1.3.0" + }, + "dependencies": { + "schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "webpack-core": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz", + "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=", + "dev": true, + "requires": { + "source-list-map": "~0.1.7", + "source-map": "~0.4.1" + }, + "dependencies": { + "source-list-map": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", + "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=", + "dev": true + }, + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "webpack-dev-middleware": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.5.1.tgz", + "integrity": "sha512-4dwCh/AyMOYAybggUr8fiCkRnjVDp+Cqlr9c+aaNB3GJYgRGYQWJ1YX/WAKUNA9dPNHZ6QSN2lYDKqjKSI8Vqw==", + "dev": true, + "requires": { + "memory-fs": "~0.4.1", + "mime": "^2.3.1", + "range-parser": "^1.0.3", + "webpack-log": "^2.0.0" + }, + "dependencies": { + "mime": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.2.tgz", + "integrity": "sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg==", + "dev": true + } + } + }, + "webpack-dev-server": { + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.14.tgz", + "integrity": "sha512-mGXDgz5SlTxcF3hUpfC8hrQ11yhAttuUQWf1Wmb+6zo3x6rb7b9mIfuQvAPLdfDRCGRGvakBWHdHOa0I9p/EVQ==", + "dev": true, + "requires": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.0.0", + "compression": "^1.5.2", + "connect-history-api-fallback": "^1.3.0", + "debug": "^3.1.0", + "del": "^3.0.0", + "express": "^4.16.2", + "html-entities": "^1.2.0", + "http-proxy-middleware": "~0.18.0", + "import-local": "^2.0.0", + "internal-ip": "^3.0.1", + "ip": "^1.1.5", + "killable": "^1.0.0", + "loglevel": "^1.4.1", + "opn": "^5.1.0", + "portfinder": "^1.0.9", + "schema-utils": "^1.0.0", + "selfsigned": "^1.9.1", + "semver": "^5.6.0", + "serve-index": "^1.7.2", + "sockjs": "0.3.19", + "sockjs-client": "1.3.0", + "spdy": "^4.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^5.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "3.4.0", + "webpack-log": "^2.0.0", + "yargs": "12.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "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" + } + }, + "decamelize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", + "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", + "dev": true, + "requires": { + "xregexp": "4.0.0" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "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" + } + }, + "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" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "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" + } + }, + "mime": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.2.tgz", + "integrity": "sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg==", + "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 + }, + "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" + } + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "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-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 + }, + "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" + } + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "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" + } + } + } + }, + "webpack-dev-middleware": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.4.0.tgz", + "integrity": "sha512-Q9Iyc0X9dP9bAsYskAVJ/hmIZZQwf/3Sy4xCAZgL5cUkjZmUZLt4l5HpbST/Pdgjn3u6pE7u5OdGd1apgzRujA==", + "dev": true, + "requires": { + "memory-fs": "~0.4.1", + "mime": "^2.3.1", + "range-parser": "^1.0.3", + "webpack-log": "^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 + }, + "yargs": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz", + "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^2.0.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": "^10.1.0" + } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.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-merge": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.1.tgz", + "integrity": "sha512-4p8WQyS98bUJcCvFMbdGZyZmsKuWjWVnVHnAS3FFg0HDaRVrPbkivx2RYCre8UiemD67RsiFFLfn4JhLAin8Vw==", + "dev": true, + "requires": { + "lodash": "^4.17.5" + } + }, + "webpack-sources": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz", + "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "webpack-subresource-integrity": { + "version": "1.1.0-rc.6", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.1.0-rc.6.tgz", + "integrity": "sha512-Az7y8xTniNhaA0620AV1KPwWOqawurVVDzQSpPAeR5RwNbL91GoBSJAAo9cfd+GiFHwsS5bbHepBw1e6Hzxy4w==", + "dev": true, + "requires": { + "webpack-core": "^0.6.8" + } + }, + "websocket-driver": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", + "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", + "dev": true, + "requires": { + "http-parser-js": ">=0.4.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true + }, + "when": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz", + "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=", + "dev": true + }, + "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": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true, + "optional": 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, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "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": "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" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xregexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", + "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "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": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true, + "optional": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true, + "optional": true + } + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "^3.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true, + "optional": true + } + } + }, + "yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true + }, + "zone.js": { + "version": "0.8.29", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.29.tgz", + "integrity": "sha512-mla2acNCMkWXBD+c+yeUrBUrzOxYMNFdQ6FGfigGGtEVBPJx07BQeJekjt9DmH1FtZek4E9rE1eRR9qQpxACOQ==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..5404187 --- /dev/null +++ b/package.json @@ -0,0 +1,76 @@ +{ + "name": "kambani", + "version": "0.1.3", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "test": "ng test", + "lint": "ng lint", + "e2e": "ng e2e" + }, + "private": true, + "dependencies": { + "@angular/animations": "~7.1.0", + "@angular/common": "~7.1.0", + "@angular/compiler": "~7.1.0", + "@angular/core": "~7.1.0", + "@angular/forms": "~7.1.0", + "@angular/platform-browser": "~7.1.0", + "@angular/platform-browser-dynamic": "~7.1.0", + "@angular/router": "~7.1.0", + "@fortawesome/fontawesome-free": "^5.11.2", + "@ng-bootstrap/ng-bootstrap": "4.0.4", + "@ngrx/store": "^8.5.1", + "@types/chrome": "0.0.81", + "angular": "^1.8.0", + "angular-autofocus-fix": "^0.1.2", + "angular-bootstrap-md": "^7.5.4", + "angular-highlight-js": "2.0.1", + "bootstrap": "^4.3.1", + "browser-passworder": "2.0.3", + "bs58": "4.0.1", + "buffer": "5.2.1", + "chart.js": "^2.9.3", + "core-js": "^2.6.10", + "elliptic": "6.5.3", + "factom": "1.2.7", + "highlight.js": "9.15.6", + "jquery": "^3.5.0", + "js-sha256": "^0.9.0", + "js-sha512": "^0.8.0", + "keccak256": "^1.0.0", + "ng-bootstrap": "^1.6.3", + "ng-click-outside": "^5.1.1", + "ngrx-store-logger": "^0.2.4", + "ngx-device-detector": "^1.3.19", + "ngx-spinner": "7.0.0", + "ngx-toastr": "9.1.2", + "node-forge": "0.8.5", + "obs-store": "3.0.2", + "popper.js": "1.14.7", + "rxjs": "~6.3.3", + "tslib": "^1.10.0", + "tweetnacl": "1.0.1", + "tweetnacl-util": "0.15.0", + "types": "^0.1.1", + "zone.js": "~0.8.26" + }, + "devDependencies": { + "@angular-builders/custom-webpack": "^7.5.2", + "@angular-builders/dev-server": "^7.3.1", + "@angular-devkit/build-angular": "^0.13.9", + "@angular/cli": "~7.1.4", + "@angular/compiler-cli": "^7.2.15", + "@angular/language-service": "~7.1.0", + "@types/jasmine": "~2.8.8", + "@types/jasminewd2": "^2.0.8", + "@types/node": "^8.10.56", + "codelyzer": "~4.5.0", + "jasmine-core": "~2.99.1", + "jasmine-spec-reporter": "~4.2.1", + "ts-node": "~7.0.0", + "tslint": "~5.11.0", + "typescript": "~3.1.6" + } +} diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts new file mode 100644 index 0000000..09efd29 --- /dev/null +++ b/src/app/app-routing.module.ts @@ -0,0 +1,44 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +import { ActionComponent } from './components/did/action/action.component'; +import { ApprovalRequestsComponent } from './components/approval-requests/approval-requests.component'; +import { CreateActionGuard } from './core/guards/create-action.guard'; +import { DidKeyFormComponent } from './components/did/update-did/did-key-form/did-key-form.component'; +import { HomeComponent } from './components/home/home.component'; +import { ManageDidsComponent } from './components/did/manage-dids/manage-dids.component'; +import { ManagementKeyFormComponent } from './components/did/update-did/management-key-form/management-key-form.component'; +import { PreviewDidComponent } from './components/did/preview-did/preview-did.component'; +import { ServiceFormComponent } from './components/did/update-did/service-form/service-form.component'; +import { SettingsComponent } from './components/settings/settings.component'; +import { SignerComponent } from './components/signer/signer.component'; +import { VaultGuard } from './core/guards/vault.guard'; + +const routes: Routes = [ + { path: '', pathMatch: 'full', redirectTo: 'home' }, + { path: 'home', component: HomeComponent, canActivate: [VaultGuard] }, + { path: 'signer', component: SignerComponent, canActivate: [VaultGuard] }, + { path: 'vault', loadChildren: './components/vault/vault.module#VaultModule' }, + { path: 'dids/manage', component: ManageDidsComponent, canActivate: [VaultGuard], children: [ + { path: 'action', component: ActionComponent }, + { path: 'create', loadChildren: './components/did/did.module#DIDModule', canActivate: [ CreateActionGuard ] } + ] }, + { path: 'dids/preview/:id', component: PreviewDidComponent, canActivate: [VaultGuard], children: [ + { path: 'create-management-key', component: ManagementKeyFormComponent }, + { path: 'update-management-key/:id', component: ManagementKeyFormComponent }, + { path: 'create-did-key', component: DidKeyFormComponent }, + { path: 'update-did-key/:id', component: DidKeyFormComponent }, + { path: 'create-service', component: ServiceFormComponent }, + ] }, + { path: 'factom', loadChildren: './components/factom-addresses/factom-addresses.module#FactomAddressesModule' }, + { path: 'keys', loadChildren: './components/keys/keys.module#KeysModule' }, + { path: 'approve', component: ApprovalRequestsComponent, canActivate: [VaultGuard] }, + { path: 'settings', component: SettingsComponent, canActivate: [VaultGuard] }, + { path: '**', component: HomeComponent, canActivate: [VaultGuard] } +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule] +}) +export class AppRoutingModule { } diff --git a/src/app/app.component.html b/src/app/app.component.html new file mode 100644 index 0000000..03f335f --- /dev/null +++ b/src/app/app.component.html @@ -0,0 +1,22 @@ + + +

Loading...

+
+
+ +
+ diff --git a/src/app/app.component.scss b/src/app/app.component.scss new file mode 100644 index 0000000..96d56fa --- /dev/null +++ b/src/app/app.component.scss @@ -0,0 +1,21 @@ +@import url('https://fonts.googleapis.com/css?family=Montserrat:500,600,700,800&display=swap'); + +#main-footer { + text-align: center; + .powered-by { + text-transform: uppercase; + letter-spacing: 1px; + color: #353535; + font-size: 17px; + font-weight: 700; + } + .powered-by-logos { + img { + max-width: 49%; + width: 200px; + &:first-child { + margin-right: 2%; + } + } + } +} \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts new file mode 100644 index 0000000..d30ec15 --- /dev/null +++ b/src/app/app.component.ts @@ -0,0 +1,131 @@ +import { Component, OnInit, NgZone } from '@angular/core'; +import { HostListener } from "@angular/core"; +import { Router } from '@angular/router'; +import { Store, select } from '@ngrx/store'; +import { ToastrService } from 'ngx-toastr'; + +import { AppState } from './core/store/app.state'; +import { BackupDialogComponent } from './components/dialogs/backup/backup.dialog.component'; +import { BackupResultModel } from './core/models/backup-result.model'; +import { ChromeMessageType } from './core/enums/chrome-message-type'; +import { DialogsService } from './core/services/dialogs/dialogs.service'; +import { downloadFile, postProcessEncryptedBackupFile, generateBackupFileName } from './core/utils/helpers'; +import { ModalSizeTypes } from './core/enums/modal-size-types'; +import { PasswordDialogComponent } from './components/dialogs/password/password.dialog.component'; +import { VaultService } from './core/services/vault/vault.service'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.scss'] +}) +export class AppComponent implements OnInit { + private pendingChanges: boolean; + + constructor( + private dialogsService: DialogsService, + public vaultService: VaultService, + public router: Router, + private store: Store, + private toastr: ToastrService, + private zone: NgZone) { } + + ngOnInit() { + try { + chrome.runtime.sendMessage({type: ChromeMessageType.CheckRequests}, (checkRequestsResponse) => { + if (checkRequestsResponse.restoreVaultRequested) { + this.zone.run(() => { + this.router.navigate(['/vault/restore']); + }); + } else if (checkRequestsResponse.manageDidsRequested) { + this.zone.run(() => { + this.router.navigate(['/dids/manage']); + }); + } else if (checkRequestsResponse.manageFactomAddressesRequested) { + this.zone.run(() => { + this.router.navigate(['/factom/addresses/manage']); + }); + } else if (checkRequestsResponse.manageKeysRequested) { + this.zone.run(() => { + this.router.navigate(['/keys/manage']); + }); + } else if (checkRequestsResponse.approvalRequests) { + this.zone.run(() => { + this.router.navigate(['approve']); + }); + } else if (checkRequestsResponse.settingsRequested) { + this.zone.run(() => { + this.router.navigate(['settings']); + }); + } else { + chrome.runtime.sendMessage({type: ChromeMessageType.PendingSigningRequestsCount}, (pendingRequestsResponse) => { + this.zone.run(() => { + const pendingRequests = pendingRequestsResponse.pendingSigningRequestsCount; + chrome.browserAction.getBadgeText({}, function(result) { + if (parseInt(result) !== pendingRequests) { + chrome.browserAction.setBadgeText({text: pendingRequests.toString()}); + } + }); + + if (pendingRequests > 0) { + this.router.navigate(['signer']); + } + }); + }); + } + }); + } + catch(err){ + console.error("This app should run as a Chrome extension."); + throw(err); + } + + if (this.vaultService.vaultExists()) { + this.vaultService.updateSignedRequestsData(); + if (this.vaultService.upgradeStorageVersion()) { + this.dialogsService.open(BackupDialogComponent, ModalSizeTypes.ExtraExtraLarge, undefined) + .subscribe(() => { + this.backupVault(); + }); + } + } + + this.store + .pipe(select(state => state.updateDID)) + .subscribe(updateDIDState => { + if (updateDIDState.didsWithPendingChanges.length > 0) { + this.pendingChanges = true; + } else { + this.pendingChanges = false; + } + }); + } + + @HostListener('window:beforeunload', ['$event']) + unloadNotification($event: any) { + if (this.pendingChanges) { + $event.returnValue = true; + } + } + + private backupVault() { + const dialogMessage = 'Enter your vault password to encrypt the backup file'; + + this.dialogsService.open(PasswordDialogComponent, ModalSizeTypes.ExtraExtraLarge, dialogMessage) + .subscribe((vaultPassword: string) => { + if (vaultPassword) { + this.vaultService.getEncryptedState(vaultPassword) + .subscribe((backupResult: BackupResultModel) => { + if (backupResult.success) { + const backupFile = postProcessEncryptedBackupFile(backupResult.backup); + const backupFileName = generateBackupFileName(); + downloadFile(backupFile, backupFileName); + this.toastr.success(backupResult.message); + } else { + this.toastr.error(backupResult.message); + } + }); + } + }); + } +} diff --git a/src/app/app.module.ts b/src/app/app.module.ts new file mode 100644 index 0000000..db7fb65 --- /dev/null +++ b/src/app/app.module.ts @@ -0,0 +1,85 @@ +import { ActionReducer, StoreModule, MetaReducer } from '@ngrx/store'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { BrowserModule } from '@angular/platform-browser'; +import { DeviceDetectorModule } from 'ngx-device-detector'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { HighlightJsModule, HIGHLIGHT_JS } from 'angular-highlight-js'; +import hljs from 'highlight.js/lib/highlight'; +import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; +import json from 'highlight.js/lib/languages/json'; +import { MDBBootstrapModule } from 'angular-bootstrap-md'; +import { NgModule } from '@angular/core'; +import { NgxSpinnerModule } from 'ngx-spinner'; +import { storeLogger } from 'ngrx-store-logger'; +import { ToastrModule } from 'ngx-toastr'; + +import { AppComponent } from './app.component'; +import { appReducers } from './core/store/app.reducers'; +import { AppRoutingModule } from './app-routing.module'; +import { ApprovalRequestsComponent } from './components/approval-requests/approval-requests.component'; +import { AppState } from './core/store/app.state'; +import { DialogsModule } from './components/dialogs/dialogs.module'; +import { environment } from '../environments/environment'; +import { ErrorInterceptor } from './core/interceptors/error.interceptor'; +import { GuardsModule } from './core/guards/guards.module'; +import { HomeComponent } from './components/home/home.component'; +import { ModalsModule } from './components/modals/modals.module'; +import { NavbarComponent } from './components/navbar/navbar.component'; +import { ServicesModule } from './core/services/services.module'; +import { SettingsComponent } from './components/settings/settings.component'; +import { SignerComponent } from './components/signer/signer.component'; +import { VaultModule } from './components/vault/vault.module'; + +hljs.registerLanguage('json', json); + +export function highlightJsFactory() { + return hljs; +} + +export function logger(reducer: ActionReducer): any { + return storeLogger()(reducer); +} + +export const metaReducers: MetaReducer[] = environment.production || environment.staging ? [] : [logger]; + +@NgModule({ + declarations: [ + AppComponent, + ApprovalRequestsComponent, + HomeComponent, + NavbarComponent, + SettingsComponent, + SignerComponent + ], + imports: [ + AppRoutingModule, + BrowserAnimationsModule, + BrowserModule, + DeviceDetectorModule.forRoot(), + DialogsModule, + FormsModule, + GuardsModule, + HighlightJsModule.forRoot({ + provide: HIGHLIGHT_JS, + useFactory: highlightJsFactory + }), + HttpClientModule, + MDBBootstrapModule.forRoot(), + ModalsModule, + NgxSpinnerModule, + ReactiveFormsModule, + ServicesModule, + StoreModule.forRoot(appReducers, { metaReducers }), + ToastrModule.forRoot(), + VaultModule + ], + providers: [ + { + provide: HTTP_INTERCEPTORS, + useClass: ErrorInterceptor, + multi: true + } + ], + bootstrap: [AppComponent] +}) +export class AppModule { } diff --git a/src/app/components/approval-requests/approval-requests.component.html b/src/app/components/approval-requests/approval-requests.component.html new file mode 100644 index 0000000..75eb3fa --- /dev/null +++ b/src/app/components/approval-requests/approval-requests.component.html @@ -0,0 +1,25 @@ +
+
+
+
+

+ Approval Requests + {{requestsCount}} +

+
+
+

Received from:

+

{{from}}

+
+

{{from}} would like to use your {{displayName}}.

+
+
+ + +
+
+
+
+
+
+
\ No newline at end of file diff --git a/src/app/components/approval-requests/approval-requests.component.scss b/src/app/components/approval-requests/approval-requests.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/components/approval-requests/approval-requests.component.ts b/src/app/components/approval-requests/approval-requests.component.ts new file mode 100644 index 0000000..7effa0e --- /dev/null +++ b/src/app/components/approval-requests/approval-requests.component.ts @@ -0,0 +1,85 @@ +import { Component, OnInit, NgZone } from '@angular/core'; +import { Router } from '@angular/router'; + +import { ChromeMessageType } from 'src/app/core/enums/chrome-message-type'; +import { ToastrService } from 'ngx-toastr'; +import { VaultService } from 'src/app/core/services/vault/vault.service'; + +@Component({ + selector: 'app-approval-requests', + templateUrl: './approval-requests.component.html', + styleUrls: ['./approval-requests.component.scss'] +}) +export class ApprovalRequestsComponent implements OnInit { + public type: string; + public from: string; + public displayName: string; + public requestsCount: number; + + constructor( + private router: Router, + private toastr: ToastrService, + private vaultService: VaultService, + private zone: NgZone) { } + + ngOnInit() { + this.getApprovalRequestsCount(); + this.getApprovalRequest(); + } + + sendRequestResponse(approved: boolean) { + if (approved) { + if (this.type === 'Pegnet') { + this.vaultService.addWhitelistedDomain('FCT', this.from); + this.vaultService.addWhitelistedDomain('EtherLink', this.from); + } else { + this.vaultService.addWhitelistedDomain(this.type, this.from); + } + + this.toastr.success('Request approved!', null, {timeOut: 1000}); + } else { + this.toastr.info('Request cancelled!', null, {timeOut: 1000}); + } + + chrome.runtime.sendMessage({type: ChromeMessageType.SendApprovalRequestResponse, success: approved}, (response) => { + this.zone.run(() => { + this.requestsCount = response.approvalRequestsCount; + this.getApprovalRequest(); + }); + }); + } + + private getApprovalRequest() { + chrome.runtime.sendMessage({type: ChromeMessageType.GetApprovalRequest}, (response) => { + this.zone.run(() => { + if (response.success) { + const request = response.approvalRequest; + this.type = request.type; + this.from = request.from; + + switch (this.type) { + case 'Pegnet': + this.displayName = 'FCT and EtherLink addresses'; + break; + case 'BlockSigningKey': + this.displayName = 'Block Signing keys'; + break; + default: + this.displayName = this.type + ' addresses' + break; + } + } else { + this.router.navigate(['home']); + } + }); + }); + } + + private getApprovalRequestsCount() { + chrome.runtime.sendMessage({type: ChromeMessageType.ApprovalRequestsCount}, (response) => { + this.zone.run(() => { + this.requestsCount = response.approvalRequestsCount; + }); + }); + } +} diff --git a/src/app/components/base.component.ts b/src/app/components/base.component.ts new file mode 100644 index 0000000..0d25ce3 --- /dev/null +++ b/src/app/components/base.component.ts @@ -0,0 +1,10 @@ +import { OnDestroy } from '@angular/core'; +import { Subscription } from 'rxjs'; + +export abstract class BaseComponent implements OnDestroy { + public subscriptions: Subscription[] = []; + + public ngOnDestroy() { + this.subscriptions.forEach(el => el.unsubscribe()); + } +} diff --git a/src/app/components/dialogs/backup/backup.dialog.component.html b/src/app/components/dialogs/backup/backup.dialog.component.html new file mode 100644 index 0000000..841c6c9 --- /dev/null +++ b/src/app/components/dialogs/backup/backup.dialog.component.html @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/src/app/components/dialogs/backup/backup.dialog.component.scss b/src/app/components/dialogs/backup/backup.dialog.component.scss new file mode 100644 index 0000000..cadbd93 --- /dev/null +++ b/src/app/components/dialogs/backup/backup.dialog.component.scss @@ -0,0 +1,30 @@ +.modal-header { + min-height: 16.43px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; + background-color: red; + -webkit-border-top-left-radius: 5px; + -webkit-border-top-right-radius: 5px; + -moz-border-radius-topleft: 5px; + -moz-border-radius-topright: 5px; + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} +.modal-header h4 { + margin: 0; + font-size: 17px; +} +.modal-title{ + color: white; +} +.modal-content { + -webkit-box-shadow: 0 5px 20px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 5px 20px 0 rgba(0, 0, 0, 0.1); + border: none; + border-radius: 5px; +} +button:focus { + outline: none !important; + border: none !important; + box-shadow: none !important; +} \ No newline at end of file diff --git a/src/app/components/dialogs/backup/backup.dialog.component.ts b/src/app/components/dialogs/backup/backup.dialog.component.ts new file mode 100644 index 0000000..0dae5d5 --- /dev/null +++ b/src/app/components/dialogs/backup/backup.dialog.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +@Component({ + templateUrl: './backup.dialog.component.html', + styleUrls: ['./backup.dialog.component.scss'] +}) +export class BackupDialogComponent { + constructor(public activeModal: NgbActiveModal) { } + + public onConfirm() { + return this.activeModal.close('confirm'); + } +} \ No newline at end of file diff --git a/src/app/components/dialogs/dialogs.module.ts b/src/app/components/dialogs/dialogs.module.ts new file mode 100644 index 0000000..0829920 --- /dev/null +++ b/src/app/components/dialogs/dialogs.module.ts @@ -0,0 +1,22 @@ +import { CommonModule } from '@angular/common'; +import { MDBBootstrapModule } from 'angular-bootstrap-md'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgModule } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; + +import { dialogComponents } from '.'; + +@NgModule({ + imports: [ + CommonModule, + MDBBootstrapModule.forRoot(), + NgbModule, + ReactiveFormsModule + ], + providers: [], + declarations: [...dialogComponents], + exports: [...dialogComponents], + entryComponents: [...dialogComponents] +}) + +export class DialogsModule { } diff --git a/src/app/components/dialogs/index.ts b/src/app/components/dialogs/index.ts new file mode 100644 index 0000000..8c30ab2 --- /dev/null +++ b/src/app/components/dialogs/index.ts @@ -0,0 +1,9 @@ +import { BackupDialogComponent } from './backup/backup.dialog.component'; +import { RemoveVaultDialogComponent } from './remove-vault/remove-vault.dialog.component'; +import { PasswordDialogComponent } from './password/password.dialog.component'; + +export const dialogComponents = [ + BackupDialogComponent, + RemoveVaultDialogComponent, + PasswordDialogComponent +]; diff --git a/src/app/components/dialogs/password/password.dialog.component.html b/src/app/components/dialogs/password/password.dialog.component.html new file mode 100644 index 0000000..5306b51 --- /dev/null +++ b/src/app/components/dialogs/password/password.dialog.component.html @@ -0,0 +1,25 @@ + diff --git a/src/app/components/dialogs/password/password.dialog.component.scss b/src/app/components/dialogs/password/password.dialog.component.scss new file mode 100644 index 0000000..12532e0 --- /dev/null +++ b/src/app/components/dialogs/password/password.dialog.component.scss @@ -0,0 +1,27 @@ +.modal-header { + min-height: 16.43px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; + background-color: #F8F8F8; + -webkit-border-top-left-radius: 5px; + -webkit-border-top-right-radius: 5px; + -moz-border-radius-topleft: 5px; + -moz-border-radius-topright: 5px; + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} +.modal-header h4 { + margin: 0; + font-size: 17px; +} +.modal-content { + -webkit-box-shadow: 0 5px 20px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 5px 20px 0 rgba(0, 0, 0, 0.1); + border: none; + border-radius: 5px; +} +button:focus { + outline: none !important; + border: none !important; + box-shadow: none !important; +} \ No newline at end of file diff --git a/src/app/components/dialogs/password/password.dialog.component.ts b/src/app/components/dialogs/password/password.dialog.component.ts new file mode 100644 index 0000000..1f71c51 --- /dev/null +++ b/src/app/components/dialogs/password/password.dialog.component.ts @@ -0,0 +1,27 @@ +import { Component, Input } from '@angular/core'; +import { FormBuilder, Validators } from '@angular/forms'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +@Component({ + templateUrl: './password.dialog.component.html', + styleUrls: ['./password.dialog.component.scss'] +}) + +export class PasswordDialogComponent { + @Input() public message: string; + public passwordForm; + + constructor(public activeModal: NgbActiveModal, private formBuilder: FormBuilder) { + this.passwordForm = this.formBuilder.group({ + password: ['', [Validators.required]] + }); + } + + public onConfirm() { + return this.activeModal.close(this.passwordForm.value.password); + } + + public onCancel() { + return this.activeModal.close(''); + } +} diff --git a/src/app/components/dialogs/remove-vault/remove-vault.dialog.component.html b/src/app/components/dialogs/remove-vault/remove-vault.dialog.component.html new file mode 100644 index 0000000..695349f --- /dev/null +++ b/src/app/components/dialogs/remove-vault/remove-vault.dialog.component.html @@ -0,0 +1,12 @@ + diff --git a/src/app/components/dialogs/remove-vault/remove-vault.dialog.component.scss b/src/app/components/dialogs/remove-vault/remove-vault.dialog.component.scss new file mode 100644 index 0000000..cadbd93 --- /dev/null +++ b/src/app/components/dialogs/remove-vault/remove-vault.dialog.component.scss @@ -0,0 +1,30 @@ +.modal-header { + min-height: 16.43px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; + background-color: red; + -webkit-border-top-left-radius: 5px; + -webkit-border-top-right-radius: 5px; + -moz-border-radius-topleft: 5px; + -moz-border-radius-topright: 5px; + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} +.modal-header h4 { + margin: 0; + font-size: 17px; +} +.modal-title{ + color: white; +} +.modal-content { + -webkit-box-shadow: 0 5px 20px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 5px 20px 0 rgba(0, 0, 0, 0.1); + border: none; + border-radius: 5px; +} +button:focus { + outline: none !important; + border: none !important; + box-shadow: none !important; +} \ No newline at end of file diff --git a/src/app/components/dialogs/remove-vault/remove-vault.dialog.component.ts b/src/app/components/dialogs/remove-vault/remove-vault.dialog.component.ts new file mode 100644 index 0000000..ae83860 --- /dev/null +++ b/src/app/components/dialogs/remove-vault/remove-vault.dialog.component.ts @@ -0,0 +1,19 @@ +import { Component } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +@Component({ + templateUrl: './remove-vault.dialog.component.html', + styleUrls: ['./remove-vault.dialog.component.scss'] +}) + +export class RemoveVaultDialogComponent { + constructor(public activeModal: NgbActiveModal) { } + + public onConfirm() { + return this.activeModal.close('confirm'); + } + + public onCancel() { + return this.activeModal.close('cancel'); + } +} diff --git a/src/app/components/did/action/action.component.html b/src/app/components/did/action/action.component.html new file mode 100644 index 0000000..317bd0c --- /dev/null +++ b/src/app/components/did/action/action.component.html @@ -0,0 +1,16 @@ +
+
+

Choose an action

+
+ + + +
+
+
diff --git a/src/app/components/did/action/action.component.scss b/src/app/components/did/action/action.component.scss new file mode 100644 index 0000000..94689c9 --- /dev/null +++ b/src/app/components/did/action/action.component.scss @@ -0,0 +1,76 @@ +$mainColor: #2F5BE7; +h1 { + font-size: 16px !important; + text-transform: uppercase; + font-weight: 600; + /* text-align: center; */ + border-bottom: 3px solid #fbfbfb; + padding-bottom: 8px; +} +.steps-content { + h3 { + text-align: center; + font-size: 15px !important; + text-transform: uppercase; + font-weight: 600; + margin: 35px 0px; + } + .choose-action { + label { + display: block; + width: 100%; + padding: 10px 10px; + border: 3px solid $mainColor; + border-radius: 4px; + text-decoration: none; + position: relative; + transition: all 0.3s ease; + padding-left: 31px; + &::before { + width: 13px; + height: 13px; + border: 2px solid #353535; + position: absolute; + border-radius: 50%; + top: 13px; + left: 10px; + content: ""; + display: inline-block; + } + &:hover { + background: $mainColor; + color: #fff; + &::before { + border-color: #fff; + } + } + &.active { + background: $mainColor; + color: #fff; + &::before { + border-color: #fff; + } + &::after { + width: 5px; + height: 5px; + border-radius: 50%; + content: ""; + display: inline-block; + background: #fff; + top: 17px; + left: 14px; + position: absolute; + } + } + input { + opacity: 0; + position: absolute; + } + } + .next-step { + bottom: 20px; + position: absolute; + width: calc(100% - 30px); + } + } +} diff --git a/src/app/components/did/action/action.component.ts b/src/app/components/did/action/action.component.ts new file mode 100644 index 0000000..440be38 --- /dev/null +++ b/src/app/components/did/action/action.component.ts @@ -0,0 +1,39 @@ +import { Component, OnInit } from '@angular/core'; +import { Store } from '@ngrx/store'; + +import { ActionType } from 'src/app/core/enums/action-type'; +import { AppState } from 'src/app/core/store/app.state'; +import { ClearCreateDIDState } from 'src/app/core/store/create-did/create-did.actions'; +import { ClearWorkflowState, SelectAction } from 'src/app/core/store/workflow/workflow.actions'; +import { KeysService } from 'src/app/core/services/keys/keys.service'; +import { WorkflowService } from 'src/app/core/services/workflow/workflow.service'; + +@Component({ + selector: 'app-action', + templateUrl: './action.component.html', + styleUrls: ['./action.component.scss'] +}) +export class ActionComponent implements OnInit { + public actionType = ActionType.CreateBasic; + public infoModals = { }; + + constructor( + private keysService: KeysService, + private store: Store, + private workflowService: WorkflowService) { } + + ngOnInit() { + this.store.dispatch(new ClearWorkflowState()); + this.store.dispatch(new ClearCreateDIDState()); + } + + goToNext() { + this.store.dispatch(new SelectAction(this.actionType)); + + if (this.actionType === ActionType.CreateBasic) { + this.keysService.autoGenerateKeys(); + } + + this.workflowService.moveToNextStep(); + } +} diff --git a/src/app/components/did/did-keys/did-keys.component.html b/src/app/components/did/did-keys/did-keys.component.html new file mode 100644 index 0000000..32477dd --- /dev/null +++ b/src/app/components/did/did-keys/did-keys.component.html @@ -0,0 +1,178 @@ +
+

Create Signing keys

+ + +
+ diff --git a/src/app/components/did/did-keys/did-keys.component.scss b/src/app/components/did/did-keys/did-keys.component.scss new file mode 100644 index 0000000..7f883a4 --- /dev/null +++ b/src/app/components/did/did-keys/did-keys.component.scss @@ -0,0 +1,104 @@ +.disabled { + pointer-events:none; + background: #eee; +} + +.invalid-alert { + width: 100%; + margin-top: 0.25rem; + font-size: 80%; + color: #dc3545; +} + +.invalid-border { + border-color: #dc3545; + padding-right: calc(1.5em + 0.75rem); + background-repeat: no-repeat; + background-position: center right calc(0.375em + 0.1875rem); + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E"); +} + +.checkbox-inline { + display: inline; + margin-left: 15px; +} + +$mainColor: #2F5BE7; +.steps-content { + h3 { + text-align: center; + font-size: 15px !important; + text-transform: uppercase; + font-weight: 600; + margin: 35px 0px; + } + .collapsible { + padding: 10px; + border: 2px solid $mainColor; + border-radius: 4px; + h3 { + font-size: 15px; + text-transform: uppercase; + margin-top: 0px; + text-align: left; + position: relative; + cursor: pointer; + margin-bottom: 0px; + span { + width: calc(100% - 80px); + text-overflow: ellipsis; + overflow: hidden; + display: inline-block; + } + &:not(.withoutPlus){ + &::before { + position: absolute; + top: 10px; + right: 10px; + background: $mainColor; + width: 14px; + height: 2px; + content: ""; + display: inline-block; + } + &::after { + position: absolute; + top: 4px; + right: 16.5px; + background: $mainColor; + width: 2px; + height: 14px; + content: ""; + display: inline-block; + transition: all 0.3s ease; + } + } + &.collapsed { + &::after { + transform: rotateZ(90deg); + opacity: 0; + } + } + } + } +} +.button-footer { + bottom: 0px; + position: absolute; + width: 100%; + left: 0px; + padding: 10px 15px; + -webkit-box-shadow: 0px -4px 5px 0px rgba(179,179,179,1); + -moz-box-shadow: 0px -4px 5px 0px rgba(179,179,179,1); + box-shadow: 0px -4px 5px 0px rgba(179,179,179,1); + background: #fff; +} +.delete-button { + i { + font-size: 12px; + margin-top: -5px; + display: inline-block; + vertical-align: middle; + } +} diff --git a/src/app/components/did/did-keys/did-keys.component.ts b/src/app/components/did/did-keys/did-keys.component.ts new file mode 100644 index 0000000..7150970 --- /dev/null +++ b/src/app/components/did/did-keys/did-keys.component.ts @@ -0,0 +1,222 @@ +import { CollapseComponent } from 'angular-bootstrap-md'; +import { Component, OnInit, AfterViewInit, ViewChildren, ChangeDetectorRef } from '@angular/core'; +import { FormBuilder, FormGroup, Validators, FormArray, FormControl, ValidatorFn } from '@angular/forms'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { Store, select } from '@ngrx/store'; +import { Subscription } from 'rxjs'; + +import { ActionType } from 'src/app/core/enums/action-type'; +import { AddDIDKey, RemoveDIDKey, UpdateDIDKey } from 'src/app/core/store/create-did/create-did.actions'; +import { AppState } from 'src/app/core/store/app.state'; +import { BaseComponent } from 'src/app/components/base.component'; +import { ComponentKeyModel } from 'src/app/core/models/component-key.model'; +import CustomValidators from 'src/app/core/utils/customValidators'; +import { DidKeyModel } from 'src/app/core/models/did-key.model'; +import { DIDService } from 'src/app/core/services/did/did.service'; +import { KeysService } from 'src/app/core/services/keys/keys.service'; +import { ManagementKeyModel } from 'src/app/core/models/management-key.model'; +import { PurposeType } from 'src/app/core/enums/purpose-type'; +import { RemoveConfirmModalComponent } from '../../modals/remove-confirm-modal/remove-confirm-modal.component'; +import { SignatureType } from 'src/app/core/enums/signature-type'; +import { WorkflowService } from 'src/app/core/services/workflow/workflow.service'; + +const UP_POSITION = 'up'; +const DOWN_POSITION = 'down'; + +@Component({ + selector: 'app-did-keys', + templateUrl: './did-keys.component.html', + styleUrls: ['./did-keys.component.scss'] +}) +export class DidKeysComponent extends BaseComponent implements OnInit, AfterViewInit { + @ViewChildren(CollapseComponent) collapses: CollapseComponent[]; + private subscription: Subscription; + private didId: string; + public actionType = ActionType; + public availablePurposes = [ + {name: 'Public Key', value: PurposeType.PublicKey}, + {name: 'Authentication Key', value: PurposeType.AuthenticationKey} + ]; + public keyForm: FormGroup; + public didKeys: DidKeyModel[] = []; + public componentKeys: ComponentKeyModel[] = []; + public managementKeys: ManagementKeyModel[] = []; + public continueButtonText: string; + + constructor( + private cd: ChangeDetectorRef, + private fb: FormBuilder, + private modalService: NgbModal, + private store: Store, + private keysService: KeysService, + private didService: DIDService, + private workflowService: WorkflowService) { + super(); + } + + ngOnInit() { + this.subscription = this.store + .pipe(select(state => state.createDID)) + .subscribe(createDIDState => { + this.componentKeys = createDIDState.didKeys + .map(key => new ComponentKeyModel(Object.assign({}, key), DOWN_POSITION, true)); + + this.didKeys = createDIDState.didKeys; + this.managementKeys = createDIDState.managementKeys; + this.continueButtonText = this.componentKeys.length > 0 ? 'Next' : 'Skip'; + }); + + this.subscriptions.push(this.subscription); + + this.didId = this.didService.getId(); + this.createForm(); + } + + ngAfterViewInit() { + setTimeout(() => { + this.collapses.forEach((collapse: CollapseComponent, index) => { + if (index === this.collapses.length - 1) { + collapse.toggle(); + } + }); + }); + } + + createForm() { + this.keyForm = this.fb.group({ + type: [SignatureType.EdDSA, [Validators.required]], + purposes: new FormArray([ + new FormControl(false), + new FormControl(false) + ], this.validateCheckboxes()), + controller: [this.didId, [Validators.required]], + alias: ['', [ + Validators.required, + CustomValidators.uniqueKeyAlias( + this.managementKeys, + this.componentKeys.map(key => key.keyModel) as DidKeyModel[] + ) + ]], + priorityRequirement: [null, [Validators.min(0), Validators.max(100)]] + }); + + this.cd.detectChanges(); + } + + generateKey() { + if (this.keyForm.invalid) { + return; + } + + const selectedPurposes = this.keyForm.value.purposes + .map((selected, i) => selected ? this.availablePurposes[i].value : null) + .filter(p => p !== null); + + this.keysService.generateKeyPair(this.type.value) + .subscribe(keyPair => { + const generatedKey = new DidKeyModel( + this.alias.value, + selectedPurposes, + this.type.value, + this.controller.value, + keyPair.publicKey, + keyPair.privateKey, + this.priorityRequirement.value + ); + + this.store.dispatch(new AddDIDKey(generatedKey)); + this.createForm(); + }); + } + + removeKey(key: DidKeyModel, event) { + event.stopPropagation(); + const confirmRef = this.modalService.open(RemoveConfirmModalComponent); + confirmRef.componentInstance.objectType = 'key'; + confirmRef.result.then((result) => { + this.store.dispatch(new RemoveDIDKey(key)); + this.createForm(); + }).catch((error) => { + }); + } + + toggleKey(keyModel) { + const didKey = this.componentKeys.find(k => k.keyModel === keyModel); + didKey.iconPosition = didKey.iconPosition === DOWN_POSITION ? UP_POSITION : DOWN_POSITION; + } + + edit(componentKey: ComponentKeyModel) { + componentKey.disabled = false; + } + + confirm(componentKey: ComponentKeyModel) { + componentKey.disabled = true; + const updatedKey = componentKey.keyModel as DidKeyModel; + const originalKey = this.didKeys.find(k => k.publicKey === updatedKey.publicKey); + + let purposeUpdated = false; + const updatedKeyPurposes = componentKey.purposes + .filter(p => p.checked) + .map(p => p.value); + + if (JSON.stringify(updatedKeyPurposes) !== JSON.stringify(originalKey.purpose)) { + purposeUpdated = true; + componentKey.keyModel['purpose'] = updatedKeyPurposes; + } + + if (this.isKeyUpdated(updatedKey, originalKey) || purposeUpdated) { + this.store.dispatch(new UpdateDIDKey(updatedKey)); + this.cd.detectChanges(); + } + } + + goToNext() { + this.workflowService.moveToNextStep(); + } + + goToPrevious() { + this.workflowService.moveToPreviousStep(); + } + + get type() { + return this.keyForm.get('type'); + } + + get alias() { + return this.keyForm.get('alias'); + } + + get controller() { + return this.keyForm.get('controller'); + } + + get purposes() { + return this.keyForm.get('purposes'); + } + + get priorityRequirement() { + return this.keyForm.get('priorityRequirement'); + } + + private validateCheckboxes() { + const validator: ValidatorFn = (formArray: FormArray) => { + const totalSelected = formArray.controls + .map(control => control.value) + .reduce((prev, next) => next ? prev + next : prev, 0); + + return totalSelected > 0 ? null : { required: true }; + }; + + return validator; + } + + private isKeyUpdated(updatedKey: DidKeyModel, originalKey: DidKeyModel) { + if (updatedKey.alias !== originalKey.alias + || updatedKey.controller !== originalKey.controller + || updatedKey.priorityRequirement !== originalKey.priorityRequirement) { + return true; + } + + return false; + } +} diff --git a/src/app/components/did/did.module.ts b/src/app/components/did/did.module.ts new file mode 100644 index 0000000..9118601 --- /dev/null +++ b/src/app/components/did/did.module.ts @@ -0,0 +1,49 @@ +import { AutofocusModule } from 'angular-autofocus-fix'; +import { CommonModule } from '@angular/common'; +import { ClickOutsideModule } from 'ng-click-outside'; +import { FormsModule, ReactiveFormsModule} from '@angular/forms'; +import { HighlightJsModule, HIGHLIGHT_JS } from 'angular-highlight-js'; +import hljs from 'highlight.js/lib/highlight'; +import json from 'highlight.js/lib/languages/json'; +import { MDBBootstrapModule } from 'angular-bootstrap-md'; +import {NgbModule} from '@ng-bootstrap/ng-bootstrap'; +import { NgModule } from '@angular/core'; + +import { AliasValidator } from 'src/app/core/utils/alias.validator'; +import { didComponents } from '.'; +import { DIDRoutingModule } from './did.routing'; +import { PriorityMaxValidator } from 'src/app/core/utils/priority.max.validator'; +import { PriorityMinValidator } from 'src/app/core/utils/priority.min.validator'; + +hljs.registerLanguage('json', json); + +export function highlightJsFactory() { + return hljs; +} + +@NgModule({ + declarations: [ + AliasValidator, + PriorityMaxValidator, + PriorityMinValidator, + ...didComponents + ], + imports: [ + AutofocusModule, + CommonModule, + ClickOutsideModule, + DIDRoutingModule, + FormsModule, + HighlightJsModule.forRoot({ + provide: HIGHLIGHT_JS, + useFactory: highlightJsFactory + }), + MDBBootstrapModule.forRoot(), + NgbModule, + ReactiveFormsModule + ], + exports: [ + ...didComponents + ] +}) +export class DIDModule { } diff --git a/src/app/components/did/did.routing.ts b/src/app/components/did/did.routing.ts new file mode 100644 index 0000000..0240546 --- /dev/null +++ b/src/app/components/did/did.routing.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { DidKeysComponent } from './did-keys/did-keys.component'; +import { ManagementKeysComponent } from './management-keys/management-keys.component'; +import { ServicesComponent } from './services/services.component'; +import { SummaryComponent } from './summary/summary.component'; + +const didRoutes: Routes = [ + { path: 'keys/did', component: DidKeysComponent }, + { path: 'keys/management', component: ManagementKeysComponent }, + { path: 'services', component: ServicesComponent }, + { path: 'summary', component: SummaryComponent } +]; + +@NgModule({ + imports: [RouterModule.forChild(didRoutes)], + exports: [RouterModule] +}) +export class DIDRoutingModule { } diff --git a/src/app/components/did/index.ts b/src/app/components/did/index.ts new file mode 100644 index 0000000..8f55c10 --- /dev/null +++ b/src/app/components/did/index.ts @@ -0,0 +1,25 @@ +import { ActionComponent } from './action/action.component'; +import { DidKeyFormComponent } from './update-did/did-key-form/did-key-form.component'; +import { DidKeysComponent } from './did-keys/did-keys.component'; +import { ManageDidsComponent } from './manage-dids/manage-dids.component'; +import { ManagementKeyFormComponent } from './update-did/management-key-form/management-key-form.component'; +import { ManagementKeysComponent } from './management-keys/management-keys.component'; +import { PreviewDidComponent } from './preview-did/preview-did.component'; +import { ServiceFormComponent } from './update-did/service-form/service-form.component'; +import { ServicesComponent } from './services/services.component'; +import { StepperComponent } from './stepper/stepper.component'; +import { SummaryComponent } from './summary/summary.component'; + +export const didComponents = [ + ActionComponent, + DidKeyFormComponent, + DidKeysComponent, + ManageDidsComponent, + ManagementKeyFormComponent, + ManagementKeysComponent, + PreviewDidComponent, + ServiceFormComponent, + ServicesComponent, + StepperComponent, + SummaryComponent +]; diff --git a/src/app/components/did/manage-dids/manage-dids.component.html b/src/app/components/did/manage-dids/manage-dids.component.html new file mode 100644 index 0000000..08aace6 --- /dev/null +++ b/src/app/components/did/manage-dids/manage-dids.component.html @@ -0,0 +1,95 @@ +
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + +
IdentityNicknameMGMT KeysSigning KeysServicesActions
+ + {{didId}} + + + + {{allDIDsPublicInfo[didId].nickname}} +
+
+ +
+
+
+
{{allDIDsPublicInfo[didId].didDocument.managementKey ? allDIDsPublicInfo[didId].didDocument.managementKey.length : 0}}
+
+
{{allDIDsPublicInfo[didId].didDocument.didKey ? allDIDsPublicInfo[didId].didDocument.didKey.length : 0}}
+
+
{{allDIDsPublicInfo[didId].didDocument.service ? allDIDsPublicInfo[didId].didDocument.service.length : 0}}
+
+ + + +
+
+
+
+
+ Prev + Next +
+
+
+ {{currentStartIndex + 1}} - {{currentStartIndex + pageSize < didIds.length ? currentStartIndex + pageSize : didIds.length}} from {{didIds.length}} +
+
+
+
+

You have not created any Identities yet

+
+
+

Identity not found

+
+
+
+
+ +
+
+ + +
+
+
+
+
+
diff --git a/src/app/components/did/manage-dids/manage-dids.component.scss b/src/app/components/did/manage-dids/manage-dids.component.scss new file mode 100644 index 0000000..3d3c9c8 --- /dev/null +++ b/src/app/components/did/manage-dids/manage-dids.component.scss @@ -0,0 +1,225 @@ +$mainColor: #2F5BE7; +.table-holder { + margin: 20px 0px; + table { + width: 100%; + white-space: nowrap; + thead { + th { + color: #253992; + text-transform: uppercase; + font-size: 12px; + border-top: 0px; + border-bottom-width: 1px; + } + } + tbody { + tr { + td:first-child { + span { + width: 165px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; + font-weight: 700; + } + } + .counter-label { + display: inline-block; + width: 20px; + line-height: 20px; + text-align: center; + background: $mainColor; + color: #fff; + border-radius: 50%; + // transition: box-shadow 0.3s ease; + font-size: 12px; + // &:hover { + // text-decoration: none; + // box-shadow: 0 0 0 3px rgba(47, 91, 231, 0.5); + // } + } + td:last-child { + button:not(:last-child) { + margin-right: 3px; + } + } + } + } + } + .table-striped tbody tr:nth-of-type(odd) { + background-color: #fdfdfd; + } + .did-link { + color: #333; + font-family: monospace, monospace; + font-size: 16px; + } +} +.paginator { + a { + display: inline-block; + background: #E0E8F3; + padding: 7.5px 15px; + text-transform: uppercase; + color: $mainColor; + font-weight: 600; + border-radius: 4px; + transition: all 0.3s ease; + text-decoration: none!important; + &:not(.disabled):hover { + background: $mainColor; + color: #fff; + } + &.disabled { + color: #495463; + cursor: not-allowed; + pointer-events: none; + } + &:first-child { + margin-right: 10px; + } + } +} +a { + cursor: pointer; +} +.formScreen { + width: 90%; + height: 100%; + max-width: 400px; + position: fixed; + top: 0; + right: -480px; + z-index: 99; + transition: right 0.3s ease; + + .closeForm { + position: absolute; + left: 0px; + width: 80px; + height: 80px; + background: #fff; + display: block; + top: 50%; + margin: -40px 0px 0px -80px; + z-index: 1; + -webkit-border-top-left-radius: 10px; + -webkit-border-bottom-left-radius: 10px; + -moz-border-radius-topleft: 10px; + -moz-border-radius-bottomleft: 10px; + border-top-left-radius: 10px; + border-bottom-left-radius: 10px; + &::before { + content: ""; + display: block; + width: 40px; + height: 40px; + background: url("/assets/next-page.svg"); + background-size: 40px 40px; + margin: 20px 20px; + } + } + @media only screen and (max-width: 768px){ + max-width: calc(100% - 40px); + .closeForm { + width: 40px; + height: 40px; + margin-left: -40px; + &::before { + width: 30px; + height: 30px; + background-size: 30px 30px; + margin: 5px; + } + } + } + .formContent { + position: absolute; + background: #fff; + top: 0; + right: 0; + width: 100%; + height: 100%; + .scrollArea { + padding: 15px; + h1 { + font-size: 16px !important; + text-transform: uppercase; + font-weight: 600; + /* text-align: center; */ + border-bottom: 3px solid #fbfbfb; + padding-bottom: 8px; + } + height: 100%; + overflow-y: scroll; + padding-bottom: 70px; + + } + } + &::before { + content: ""; + display: block; + background: rgba(0,0,0, 0.7); + position: fixed; + top: 0; + right: -100%; + width: 100%; + height: 100%; + transition: right 0.3s ease; + } + &.active { + right: 0; + &::before { + right: 0; + } + } +} +.header-search { + width: 300px; + input { + width: calc(300px - 43px); + float: left; + height: 40px; + } + button { + float: right; + } +} +.copy-didid { + position: relative; + bottom: 5px; + margin-left: 15px; + cursor: pointer; + &::before { + content: "Copied!"; + padding: 5px 15px; + color: #fff; + background: #4caf50; + position: absolute; + top: -4px; + left: 50%; + transform: translateX(-50%) translateY(-100%); + border-radius: 4px; + display: none; + } + &::after { + width: 0; + height: 0; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-top: 7px solid #4caf50; + position: absolute; + top: -6px; + left: 50%; + transform: translateX(-50%); + content: ""; + display: none; + } + &.clicked { + &::before, &::after { + display: inline-block; + } + } +} diff --git a/src/app/components/did/manage-dids/manage-dids.component.ts b/src/app/components/did/manage-dids/manage-dids.component.ts new file mode 100644 index 0000000..6c21dcb --- /dev/null +++ b/src/app/components/did/manage-dids/manage-dids.component.ts @@ -0,0 +1,266 @@ +import { Component, OnInit } from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgxSpinnerService } from 'ngx-spinner'; +import { Router } from '@angular/router'; +import { Store, select } from '@ngrx/store'; +import { Subscription } from 'rxjs'; +import { ToastrService } from 'ngx-toastr'; + +import { AppState } from 'src/app/core/store/app.state'; +import { BackupResultModel } from 'src/app/core/models/backup-result.model'; +import { BaseComponent } from '../../base.component'; +import { ChromeMessageType } from 'src/app/core/enums/chrome-message-type'; +import { ClearCreateDIDState } from 'src/app/core/store/create-did/create-did.actions'; +import { ClearWorkflowState } from 'src/app/core/store/workflow/workflow.actions'; +import { ConfirmModalComponent } from '../../modals/confirm-modal/confirm-modal.component'; +import { CompleteDIDUpdate } from 'src/app/core/store/update-did/update-did.actions'; +import { CreateDIDState } from 'src/app/core/store/create-did/create-did.state'; +import { DialogsService } from 'src/app/core/services/dialogs/dialogs.service'; +import { DIDDocument } from 'src/app/core/interfaces/did-document'; +import { DIDService } from 'src/app/core/services/did/did.service'; +import { downloadFile } from 'src/app/core/utils/helpers'; +import { EntryType } from 'src/app/core/enums/entry-type'; +import { ModalSizeTypes } from 'src/app/core/enums/modal-size-types'; +import { PasswordDialogComponent } from '../../dialogs/password/password.dialog.component'; +import { ResultModel } from 'src/app/core/models/result.model'; +import { SignatureResultModel } from 'src/app/core/models/signature-result.model'; +import { SignatureType } from 'src/app/core/enums/signature-type'; +import { SigningService } from 'src/app/core/services/signing/signing.service'; +import { VaultService } from 'src/app/core/services/vault/vault.service'; + +@Component({ + selector: 'app-manage-dids', + templateUrl: './manage-dids.component.html', + styleUrls: ['./manage-dids.component.scss'] +}) +export class ManageDidsComponent extends BaseComponent implements OnInit { + private subscription: Subscription; + private createDIDState: CreateDIDState; + public didIds: string[] = []; + public displayedDidIds: string[] = []; + public allDIDsPublicInfo: object; + public formScreenOpen: boolean = false; + public pageSize: number = 6; + public didEditNickname: boolean[] = []; + public currentPage: number = 1; + public currentStartIndex = 0; + + constructor( + private dialogsService: DialogsService, + private didService: DIDService, + private modalService: NgbModal, + private router: Router, + private signingService: SigningService, + private spinner: NgxSpinnerService, + private store: Store, + private toastr: ToastrService, + private vaultService: VaultService) { + super(); + } + + ngOnInit() { + chrome.tabs && chrome.tabs.getCurrent(function(tab) { + if (tab === undefined) { + chrome.runtime.sendMessage({type: ChromeMessageType.ManageDidsRequest}, (response) => { + if (response.success) { + const popup_url = chrome.runtime.getURL('index.html'); + chrome.tabs.create({'url': popup_url}); + } + }); + } else { + chrome.runtime.sendMessage({type: ChromeMessageType.CheckRequests}, (response) => { + if (response.manageDidsRequested) { + chrome.runtime.sendMessage({type: ChromeMessageType.NewTabOpen}); + } + }); + } + }); + + this.getDIDsInfo(); + + this.subscription = this.store + .pipe(select(state => state)) + .subscribe(state => { + this.createDIDState = state.createDID; + + if (state.workflow.closeFormScreen) { + this.clearState(); + } + }); + + this.subscriptions.push(this.subscription); + } + + backupDid(didId: string) { + const dialogMessage = 'Enter your vault password to open the vault and encrypt your Identity'; + + this.dialogsService.open(PasswordDialogComponent, ModalSizeTypes.ExtraExtraLarge, dialogMessage) + .subscribe((vaultPassword: string) => { + if (vaultPassword) { + this.vaultService + .backupSingleDIDFromVault(didId, vaultPassword) + .subscribe((result: BackupResultModel) => { + if (result.success) { + const date = new Date(); + const didBackupFile = this.postProcessDidBackupFile(result.backup, didId); + downloadFile(didBackupFile, `paper-did-UTC--${date.toISOString()}.txt`); + } else { + this.toastr.error(result.message); + } + }); + } + }); + } + + previewDid(didId: string) { + this.didService.loadDIDForUpdate(didId); + this.router.navigate([`dids/preview/${didId}`]); + } + + removeDid(didId: string) { + const didDocument = this.allDIDsPublicInfo[didId].didDocument as DIDDocument; + const signingKey = didDocument.managementKey.filter(k => k.priority == 0)[0]; + const signingKeyType = signingKey.type.split('VerificationKey')[0] as SignatureType; + const deactivateEntry = ""; + const dialogMessage = 'Enter your vault password to sign the entry and delete the Identity'; + + this.dialogsService.open(PasswordDialogComponent, ModalSizeTypes.ExtraExtraLarge, dialogMessage) + .subscribe((vaultPassword: string) => { + if (vaultPassword) { + this.spinner.show(); + this.signingService + .signDIDEntry(signingKey.id, signingKeyType, deactivateEntry, EntryType.DeactivateDIDEntry, vaultPassword) + .subscribe((result: SignatureResultModel) => { + if (result.success) { + this.didService + .recordEntryOnChain( + EntryType.DeactivateDIDEntry, + deactivateEntry, + signingKey.id, + result.signatureBase64) + .subscribe((recordResult: any) => { + if (recordResult.error) { + this.spinner.hide(); + this.toastr.error(recordResult.message); + } else { + this.vaultService + .removeDIDFromVault( + didId, + vaultPassword) + .subscribe((result: ResultModel) => { + this.spinner.hide(); + + if (result.success) { + this.getDIDsInfo(); + this.store.dispatch(new CompleteDIDUpdate(didId)); + this.toastr.success(result.message); + } else { + /** + * this should never happen + */ + this.toastr.error('A problem occurred! Please, try again'); + } + }); + } + }); + } else { + this.spinner.hide(); + this.toastr.error(result.message); + } + }); + } + }); + } + + editNickname(didId: string, nickname: string) { + this.vaultService.updateDIDNickname(didId, nickname); + this.allDIDsPublicInfo[didId].nickname = nickname; + this.didEditNickname[didId] = false; + } + + closeFormScreen() { + if (this.createDIDState.managementKeys.length > 0 + || this.createDIDState.didKeys.length > 0 + || this.createDIDState.services.length > 0) { + const confirmRef = this.modalService.open(ConfirmModalComponent); + confirmRef.result.then((result) => { + this.clearState(); + this.store.dispatch(new ClearCreateDIDState()); + }).catch((error) => { + }); + } else { + this.clearState(); + } + } + + searchChange(event) { + const searchTerm = event.target.value; + this.didIds = []; + for (const didId in this.allDIDsPublicInfo) { + if (this.allDIDsPublicInfo[didId].nickname.includes(searchTerm)) { + this.didIds.push(didId); + } + } + + this.currentStartIndex = 0; + this.currentPage = 1; + this.displayedDidIds = this.didIds.slice(this.currentStartIndex, this.currentStartIndex + this.pageSize); + } + + changePage (page) { + this.currentPage = page; + this.currentStartIndex = (this.currentPage - 1) * this.pageSize; + this.displayedDidIds = this.didIds.slice(this.currentStartIndex, this.currentStartIndex + this.pageSize); + } + + anyDID() { + return Object.keys(this.allDIDsPublicInfo).length > 0; + } + + copyDIDId(didId: string, element) { + const selBox = document.createElement('textarea'); + selBox.style.position = 'fixed'; + selBox.style.left = '0'; + selBox.style.top = '0'; + selBox.style.opacity = '0'; + selBox.value = didId; + document.body.appendChild(selBox); + selBox.focus(); + selBox.select(); + document.execCommand('copy'); + document.body.removeChild(selBox); + + element.classList.add('clicked'); + setTimeout(() => {element.classList.remove('clicked')},2000); + } + + private clearState() { + this.formScreenOpen = false; + this.getDIDsInfo(); + this.didService.clearData(); + this.store.dispatch(new ClearWorkflowState()); + this.router.navigate(['dids/manage']); + } + + private getDIDsInfo() { + this.allDIDsPublicInfo = this.vaultService.getAllDIDsPublicInfo(); + this.didIds = Object.keys(this.allDIDsPublicInfo); + this.displayedDidIds = this.didIds.slice(this.currentStartIndex, this.currentStartIndex + this.pageSize); + } + + private postProcessDidBackupFile(encryptedFile: string, didId: string) { + const parsedFile = JSON.parse(encryptedFile); + const newKeysFile: any = { }; + + newKeysFile.data = parsedFile.data; + newKeysFile.encryptionAlgo = { + name: 'AES-GCM', + iv: parsedFile.iv, + salt: parsedFile.salt, + tagLength: 128 + }; + newKeysFile.did = didId; + + return JSON.stringify(newKeysFile, null, 2); + } +} diff --git a/src/app/components/did/management-keys/management-keys.component.html b/src/app/components/did/management-keys/management-keys.component.html new file mode 100644 index 0000000..7987c81 --- /dev/null +++ b/src/app/components/did/management-keys/management-keys.component.html @@ -0,0 +1,203 @@ +
+

Create management keys

+ + +
+ + diff --git a/src/app/components/did/management-keys/management-keys.component.scss b/src/app/components/did/management-keys/management-keys.component.scss new file mode 100644 index 0000000..cbaaa5e --- /dev/null +++ b/src/app/components/did/management-keys/management-keys.component.scss @@ -0,0 +1,100 @@ +.disabled { + pointer-events:none; + background: #eee; +} + +.invalid-alert { + width: 100%; + margin-top: 0.25rem; + font-size: 80%; + color: #dc3545; +} + +.invalid-border { + border-color: #dc3545; + padding-right: calc(1.5em + 0.75rem); + background-repeat: no-repeat; + background-position: center right calc(0.375em + 0.1875rem); + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E"); +} + +$mainColor: #2F5BE7; +.steps-content { + h3 { + text-align: center; + font-size: 15px !important; + text-transform: uppercase; + font-weight: 600; + margin: 35px 0px; + } + .collapsible { + padding: 10px; + border: 2px solid $mainColor; + border-radius: 4px; + h3 { + font-size: 15px; + text-transform: uppercase; + margin-top: 0px; + text-align: left; + position: relative; + overflow: hidden; + margin-bottom: 0px; + cursor: pointer; + span { + width: calc(100% - 80px); + text-overflow: ellipsis; + overflow: hidden; + display: inline-block; + } + &:not(.withoutPlus){ + &::before { + position: absolute; + top: 10px; + right: 10px; + background: $mainColor; + width: 14px; + height: 2px; + content: ""; + display: inline-block; + } + &::after { + position: absolute; + top: 4px; + right: 16.5px; + background: $mainColor; + width: 2px; + height: 14px; + content: ""; + display: inline-block; + transition: all 0.3s ease; + } + } + &.collapsed { + &::after { + transform: rotateZ(90deg); + opacity: 0; + } + } + } + } +} +.button-footer { + bottom: 0px; + position: absolute; + width: 100%; + left: 0px; + padding: 10px 15px; + -webkit-box-shadow: 0px -4px 5px 0px rgba(179,179,179,1); + -moz-box-shadow: 0px -4px 5px 0px rgba(179,179,179,1); + box-shadow: 0px -4px 5px 0px rgba(179,179,179,1); + background: #fff; +} +.delete-button { + i { + font-size: 12px; + margin-top: -5px; + display: inline-block; + vertical-align: middle; + } +} diff --git a/src/app/components/did/management-keys/management-keys.component.ts b/src/app/components/did/management-keys/management-keys.component.ts new file mode 100644 index 0000000..a1812c7 --- /dev/null +++ b/src/app/components/did/management-keys/management-keys.component.ts @@ -0,0 +1,192 @@ +import { CollapseComponent } from 'angular-bootstrap-md'; +import { Component, OnInit, AfterViewInit, ViewChildren, ViewEncapsulation, ChangeDetectorRef } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { Store, select } from '@ngrx/store'; +import { Subscription } from 'rxjs'; +import { ToastrService } from 'ngx-toastr'; + +import { ActionType } from 'src/app/core/enums/action-type'; +import { AddManagementKey, RemoveManagementKey, UpdateManagementKey } from 'src/app/core/store/create-did/create-did.actions'; +import { AppState } from 'src/app/core/store/app.state'; +import { BaseComponent } from 'src/app/components/base.component'; +import { ComponentKeyModel } from 'src/app/core/models/component-key.model'; +import CustomValidators from 'src/app/core/utils/customValidators'; +import { DidKeyModel } from 'src/app/core/models/did-key.model'; +import { DIDService } from 'src/app/core/services/did/did.service'; +import { KeysService } from 'src/app/core/services/keys/keys.service'; +import { ManagementKeyModel } from 'src/app/core/models/management-key.model'; +import { RemoveConfirmModalComponent } from 'src/app/components/modals/remove-confirm-modal/remove-confirm-modal.component'; +import { SignatureType } from 'src/app/core/enums/signature-type'; +import { TooltipMessages } from 'src/app/core/utils/tooltip.messages'; +import { WorkflowService } from 'src/app/core/services/workflow/workflow.service'; + +const UP_POSITION = 'up'; +const DOWN_POSITION = 'down'; + +@Component({ + selector: 'app-management-keys', + templateUrl: './management-keys.component.html', + styleUrls: ['./management-keys.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class ManagementKeysComponent extends BaseComponent implements OnInit, AfterViewInit { + @ViewChildren(CollapseComponent) collapses: CollapseComponent[]; + private subscription: Subscription; + private didId: string; + private managementKeys: ManagementKeyModel[] = []; + private didKeys: DidKeyModel[] = []; + public componentKeys: ComponentKeyModel[] = []; + public keyForm: FormGroup; + public actionType = ActionType; + public aliasTooltipMessage = TooltipMessages.AliasTooltip; + public controllerTooltipMessage = TooltipMessages.ControllerTooltip; + public signatureTypeTooltipMessage = TooltipMessages.SignatureTypeTooltip; + + constructor( + private cd: ChangeDetectorRef, + private fb: FormBuilder, + private modalService: NgbModal, + private store: Store, + private keysService: KeysService, + private didService: DIDService, + private toastr: ToastrService, + private workflowService: WorkflowService) { + super(); + } + + ngOnInit() { + this.subscription = this.store + .pipe(select(state => state.createDID)) + .subscribe(createDIDState => { + this.componentKeys = createDIDState.managementKeys.map(key => new ComponentKeyModel(Object.assign({}, key), DOWN_POSITION, true)); + this.managementKeys = createDIDState.managementKeys; + this.didKeys = createDIDState.didKeys; + }); + + this.subscriptions.push(this.subscription); + + this.didId = this.didService.getId(); + this.createForm(); + } + + ngAfterViewInit() { + setTimeout(() => { + this.collapses.forEach((collapse: CollapseComponent, index) => { + if (index === this.collapses.length - 1) { + collapse.toggle(); + } + }); + }); + } + + createForm() { + this.keyForm = this.fb.group({ + type: [SignatureType.EdDSA, [Validators.required]], + controller: [this.didId, [Validators.required]], + alias: ['', [Validators.required, + CustomValidators.uniqueKeyAlias(this.componentKeys.map(key => key.keyModel) as ManagementKeyModel[], this.didKeys)]], + priority: ['', [Validators.required, Validators.min(0), Validators.max(100)]], + priorityRequirement: [null, [Validators.min(0), Validators.max(100)]] + }); + + this.cd.detectChanges(); + } + + generateKey() { + if (this.keyForm.invalid) { + return; + } + + this.keysService.generateKeyPair(this.type.value) + .subscribe(keyPair => { + const generatedKey = new ManagementKeyModel( + this.alias.value, + this.priority.value, + this.type.value, + this.controller.value, + keyPair.publicKey, + keyPair.privateKey, + this.priorityRequirement.value + ); + + this.store.dispatch(new AddManagementKey(generatedKey)); + this.createForm(); + }); + } + + removeKey(key: ManagementKeyModel, event) { + event.stopPropagation(); + const confirmRef = this.modalService.open(RemoveConfirmModalComponent); + confirmRef.componentInstance.objectType = 'key'; + confirmRef.result.then((result) => { + this.store.dispatch(new RemoveManagementKey(key)); + this.createForm(); + }).catch((error) => { + }); + } + + toggleKey(keyModel) { + const managementKey = this.componentKeys.find(k => k.keyModel === keyModel); + managementKey.iconPosition = managementKey.iconPosition === DOWN_POSITION ? UP_POSITION : DOWN_POSITION; + } + + edit(componentKey: ComponentKeyModel) { + componentKey.disabled = false; + } + + confirm(componentKey: ComponentKeyModel) { + componentKey.disabled = true; + const updatedKey = componentKey.keyModel as ManagementKeyModel; + const originalKey = this.managementKeys.find(k => k.publicKey === updatedKey.publicKey); + + if (this.isKeyUpdated(updatedKey, originalKey)) { + this.store.dispatch(new UpdateManagementKey(updatedKey)); + this.cd.detectChanges(); + } + } + + goToNext() { + if (!this.managementKeys.find(mk => mk.priority === 0)) { + this.toastr.warning('Warning! You must have at least one Management key created at priority 0 before continuing.'); + return; + } + + this.workflowService.moveToNextStep(); + } + + goToPrevious() { + this.workflowService.moveToPreviousStep(); + } + + get type() { + return this.keyForm.get('type'); + } + + get alias() { + return this.keyForm.get('alias'); + } + + get controller() { + return this.keyForm.get('controller'); + } + + get priority() { + return this.keyForm.get('priority'); + } + + get priorityRequirement() { + return this.keyForm.get('priorityRequirement'); + } + + private isKeyUpdated(updatedKey: ManagementKeyModel, originalKey: ManagementKeyModel) { + if (updatedKey.alias !== originalKey.alias + || updatedKey.controller !== originalKey.controller + || updatedKey.priority !== originalKey.priority + || updatedKey.priorityRequirement !== originalKey.priorityRequirement) { + return true; + } + + return false; + } +} diff --git a/src/app/components/did/preview-did/preview-did.component.html b/src/app/components/did/preview-did/preview-did.component.html new file mode 100644 index 0000000..6916d13 --- /dev/null +++ b/src/app/components/did/preview-did/preview-did.component.html @@ -0,0 +1,172 @@ +
+
+
+
+
+
+

+ {{didId}} +

+
+
+
Nickname
+
{{nickname}}
+
+
+
+ +
+ + +
+
+
+
+
+ +
+
+ +
+
+
+
+
+ + +
+
diff --git a/src/app/components/did/preview-did/preview-did.component.scss b/src/app/components/did/preview-did/preview-did.component.scss new file mode 100644 index 0000000..da16c4b --- /dev/null +++ b/src/app/components/did/preview-did/preview-did.component.scss @@ -0,0 +1,222 @@ +$mainColor: #2F5BE7; +.page-content { + .head { + font-size: 15px!important; + color: $mainColor; + font-weight: 700; + margin-bottom: 20px; + word-break: break-all; + } + .action-btn { + font-weight: 600; + border-width: 2px; + line-height: 35px; + clear: both; + text-transform: uppercase; + margin-bottom: 5px; + &:not(:last-child){ + margin-right: 3px; + } + &.add-new { + color: #fff; + } + } + .info-box { + .info-row { + font-size: 15px; + margin-bottom: 10px; + position: relative; + .label { + color: #356590; + } + .value { + font-weight: 600; + color: #353535; + } + .buttons { + position: absolute; + top: 0; + right: 0; + opacity: 0; + transition: opacity 0.3s ease; + z-index: 3; + a:not(:last-child) { + margin-right: 3px; + } + button:not(:last-child) { + margin-right: 3px; + } + a { + width: 30px; + height: 30px; + line-height: 30px; + padding: 0px; + } + } + } + &:hover { + .buttons { + opacity: 1!important; + } + } + } + .info-tables { + margin: 20px 0px; + .nav-link { + color: $mainColor; + &.active { + background: $mainColor; + color: #fff; + } + } + } + .table-holder { + overflow-x: scroll; + table { + thead { + background-color: #f1f5fa; + tr { + th { + color: #50649c; + font-weight: 600; + border-bottom-width: 1px; + } + } + } + tbody { + tr { + td:last-child { + .btn:not(:last-child) { + margin-right: 5px; + } + } + } + } + } + } + .service-row, .key-row { + .info-box { + border: 2px solid $mainColor; + border-radius: 5px; + padding: 10px; + margin-bottom: 10px; + .info-row:not(.alias-row) { + border-top: 1px dashed #e1e1e1; + padding-top: 5px; + margin-top: 5px; + } + @media only screen and (min-width: 768px){ + transition: all 0.3s ease; + &:hover { + background: #f5f7ff; + } + } + // @media only screen and (max-width: 575px){ + // margin-bottom: + // } + } + } +} +.formScreen { + width: 90%; + height: 100%; + max-width: 400px; + position: fixed; + top: 0; + right: -480px; + z-index: 99; + transition: right 0.3s ease; + + .closeForm { + position: absolute; + left: 0px; + width: 80px; + height: 80px; + background: #fff; + display: block; + top: 50%; + margin: -40px 0px 0px -80px; + z-index: 1; + -webkit-border-top-left-radius: 10px; + -webkit-border-bottom-left-radius: 10px; + -moz-border-radius-topleft: 10px; + -moz-border-radius-bottomleft: 10px; + border-top-left-radius: 10px; + border-bottom-left-radius: 10px; + &::before { + content: ""; + display: block; + width: 40px; + height: 40px; + background: url("/assets/next-page.svg"); + background-size: 40px 40px; + margin: 20px 20px; + } + } + @media only screen and (max-width: 768px){ + max-width: calc(100% - 40px); + .closeForm { + width: 40px; + height: 40px; + margin-left: -40px; + &::before { + width: 30px; + height: 30px; + background-size: 30px 30px; + margin: 5px; + } + } + } + .formContent { + position: absolute; + background: #fff; + top: 0; + right: 0; + width: 100%; + height: 100%; + .scrollArea { + padding: 15px; + h1 { + font-size: 16px !important; + text-transform: uppercase; + font-weight: 600; + /* text-align: center; */ + border-bottom: 3px solid #fbfbfb; + padding-bottom: 8px; + } + height: 100%; + overflow-y: scroll; + padding-bottom: 70px; + + } + } + &::before { + content: ""; + display: block; + background: rgba(0,0,0, 0.7); + position: fixed; + top: 0; + right: -100%; + width: 100%; + height: 100%; + transition: right 0.3s ease; + } + &.active { + right: 0; + &::before { + right: 0; + } + } +} +.fixed-bottom-button { + background: #fff; + position: fixed; + bottom: 0; + left: 0; + width: 100%; + z-index: 3; + padding: 15px 0px; + -webkit-box-shadow: 0px -4px 5px 0px rgba(179,179,179,1); + -moz-box-shadow: 0px -4px 5px 0px rgba(179,179,179,1); + box-shadow: 0px -4px 5px 0px rgba(179,179,179,1); +} diff --git a/src/app/components/did/preview-did/preview-did.component.ts b/src/app/components/did/preview-did/preview-did.component.ts new file mode 100644 index 0000000..b78da03 --- /dev/null +++ b/src/app/components/did/preview-did/preview-did.component.ts @@ -0,0 +1,307 @@ +import { ActivatedRoute, Router } from '@angular/router'; +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgxSpinnerService } from 'ngx-spinner'; +import { Store, select } from '@ngrx/store'; +import { Subscription } from 'rxjs'; +import { ToastrService } from 'ngx-toastr'; + +import * as updateDIDActions from 'src/app/core/store/update-did/update-did.actions'; +import { AppState } from 'src/app/core/store/app.state'; +import { BackupResultModel } from 'src/app/core/models/backup-result.model'; +import { BaseComponent } from 'src/app/components/base.component'; +import { DialogsService } from 'src/app/core/services/dialogs/dialogs.service'; +import { DidKeyModel } from 'src/app/core/models/did-key.model'; +import { DIDService } from 'src/app/core/services/did/did.service'; +import { downloadFile, minifyPublicKey, minifyDid } from 'src/app/core/utils/helpers'; +import { EntryType } from 'src/app/core/enums/entry-type'; +import { ManagementKeyModel } from 'src/app/core/models/management-key.model'; +import { ModalSizeTypes } from 'src/app/core/enums/modal-size-types'; +import { PasswordDialogComponent } from 'src/app/components/dialogs/password/password.dialog.component'; +import { PurposeType } from 'src/app/core/enums/purpose-type'; +import { RemoveConfirmModalComponent } from 'src/app/components/modals/remove-confirm-modal/remove-confirm-modal.component'; +import { ResultModel } from 'src/app/core/models/result.model'; +import { ServiceModel } from 'src/app/core/models/service.model'; +import { SharedRoutes } from 'src/app/core/enums/shared-routes'; +import { SignatureResultModel } from 'src/app/core/models/signature-result.model'; +import { SignatureType } from 'src/app/core/enums/signature-type'; +import { SigningService } from 'src/app/core/services/signing/signing.service'; +import { UpdateEntryDocument } from 'src/app/core/interfaces/update-entry-document'; +import { VaultService } from 'src/app/core/services/vault/vault.service'; +import { WorkflowService } from 'src/app/core/services/workflow/workflow.service'; + +@Component({ + selector: 'app-preview-did', + templateUrl: './preview-did.component.html', + styleUrls: ['./preview-did.component.scss'] +}) +export class PreviewDidComponent extends BaseComponent implements OnInit, OnDestroy { + private subscription: Subscription; + private originalManagementKeys: ManagementKeyModel[]; + public MGMTKeysMode = 1; + public DIDKeysMode = 2; + public ServicesMode = 3; + public minKey = minifyPublicKey; + public minDid = minifyDid; + public didId: string; + public nickname: string; + public managementKeys: ManagementKeyModel[]; + public didKeys: DidKeyModel[]; + public services: ServiceModel[]; + public formScreenOpen: boolean = false; + public pendingChanges: boolean = false; + public currentMode: number; + + constructor( + private route: ActivatedRoute, + private router: Router, + private dialogsService: DialogsService, + private didService: DIDService, + private modalService: NgbModal, + private signingService: SigningService, + private spinner: NgxSpinnerService, + private store: Store, + private toastr: ToastrService, + private vaultService: VaultService, + private workflowService: WorkflowService ) { + super(); + } + + ngOnInit() { + this.didId = this.route.snapshot.paramMap.get('id'); + this.didService.loadDIDForUpdate(this.didId); + const didPublicInfo = this.vaultService.getDIDPublicInfo(this.didId); + this.nickname = didPublicInfo.nickname; + this.currentMode = this.MGMTKeysMode; + + this.workflowService.closeFormEvent.subscribe(() => { + this.closeFormScreen(); + }); + + this.subscription = this.store + .pipe(select(state => state.updateDID)) + .subscribe(updateDIDState => { + const didUpdateModel = updateDIDState.dids.find(d => d.didId === this.didId); + if (didUpdateModel) { + this.managementKeys = didUpdateModel.managementKeys; + this.originalManagementKeys = didUpdateModel.originalManagementKeys; + this.didKeys = didUpdateModel.didKeys; + this.services = didUpdateModel.services; + + if (JSON.stringify(didUpdateModel.originalManagementKeys) !== JSON.stringify(didUpdateModel.managementKeys) + || JSON.stringify(didUpdateModel.originalDidKeys) !== JSON.stringify(didUpdateModel.didKeys) + || JSON.stringify(didUpdateModel.originalServices) !== JSON.stringify(didUpdateModel.services)) { + this.pendingChanges = true; + } else { + this.pendingChanges = false; + } + } + }); + + this.subscriptions.push(this.subscription); + } + + ngOnDestroy() { + this.didService.clearData(); + } + + saveChanges() { + if (this.pendingChanges) { + if (this.managementKeys.filter(mk => mk.priority == 0).length == 0) { + this.toastr.error("You must have at least one Management key at priority 0 before continuing", null, {timeOut: 5000}); + return; + } + + const entry = this.didService.generateEntry(EntryType.UpdateDIDEntry); + const signingKey = this.originalManagementKeys.filter(k => k.priority == 0)[0]; + const signingKeyId = `${this.didId}#${signingKey.alias}`; + const dialogMessage = 'Enter your vault password to sign the entry and update any key(s)'; + + this.dialogsService.open(PasswordDialogComponent, ModalSizeTypes.ExtraExtraLarge, dialogMessage) + .subscribe((vaultPassword: string) => { + if (vaultPassword) { + this.spinner.show(); + this.signingService + .signDIDEntry(signingKeyId, signingKey.type as SignatureType, entry, EntryType.UpdateDIDEntry, vaultPassword) + .subscribe((result: SignatureResultModel) => { + if (result.success) { + this.didService + .recordEntryOnChain( + EntryType.UpdateDIDEntry, + entry, + signingKeyId, + result.signatureBase64) + .subscribe((recordResult: any) => { + if (recordResult.error) { + this.spinner.hide(); + this.toastr.error(recordResult.message); + } else { + this.vaultService + .saveDIDChangesToVault( + this.didId, + entry as UpdateEntryDocument, + this.managementKeys, + this.didKeys, + vaultPassword) + .subscribe((result: ResultModel) => { + this.spinner.hide(); + + if (result.success) { + this.store.dispatch(new updateDIDActions.CompleteDIDUpdate(this.didId)); + this.toastr.success('Identity updated successfully'); + this.router.navigate([SharedRoutes.ManageDids]); + } else { + /** + * this should never happen + */ + this.toastr.error('A problem occurred! Please, try again'); + } + }); + } + }); + } else { + this.spinner.hide(); + this.toastr.error(result.message); + } + }); + } + }); + } + } + + cancelChanges() { + this.store.dispatch(new updateDIDActions.CancelChanges(this.didId)); + } + + closeFormScreen() { + this.formScreenOpen = false; + } + + removeManagementKey(key: ManagementKeyModel) { + const confirmRef = this.modalService.open(RemoveConfirmModalComponent); + confirmRef.componentInstance.objectType = 'key'; + confirmRef.result.then((result) => { + this.store.dispatch(new updateDIDActions.RemoveManagementKey(this.didId, key)); + }).catch((error) => { + }); + } + + removeDIDKey(key: DidKeyModel) { + const confirmRef = this.modalService.open(RemoveConfirmModalComponent); + confirmRef.componentInstance.objectType = 'key'; + confirmRef.result.then((result) => { + this.store.dispatch(new updateDIDActions.RemoveDIDKey(this.didId, key)); + }).catch((error) => { + }); + } + + removeService(service: ServiceModel) { + const confirmRef = this.modalService.open(RemoveConfirmModalComponent); + confirmRef.componentInstance.objectType = 'service'; + confirmRef.result.then((result) => { + this.store.dispatch(new updateDIDActions.RemoveService(this.didId, service)); + }).catch((error) => { + }); + } + + displayPurposes(purposes: any[]): string { + if (purposes.includes(PurposeType.PublicKey) && purposes.includes(PurposeType.AuthenticationKey)) { + return 'Public Key, Authentication Key'; + } else if (purposes.includes(PurposeType.PublicKey)) { + return 'Public Key'; + } else if (purposes.includes(PurposeType.AuthenticationKey)) { + return 'Authentication Key'; + } + } + + backupDid(didId: string) { + const dialogMessage = 'Enter your vault password to open the vault and encrypt your Identity'; + + this.dialogsService.open(PasswordDialogComponent, ModalSizeTypes.ExtraExtraLarge, dialogMessage) + .subscribe((vaultPassword: string) => { + if (vaultPassword) { + this.vaultService + .backupSingleDIDFromVault(didId, vaultPassword) + .subscribe((result: BackupResultModel) => { + if (result.success) { + const date = new Date(); + const didBackupFile = this.postProcessDidBackupFile(result.backup, didId); + downloadFile(didBackupFile, `paper-did-UTC--${date.toISOString()}.txt`); + } else { + this.toastr.error(result.message); + } + }); + } + }); + } + + removeDid(didId: string) { + const signingKey = this.originalManagementKeys.filter(k => k.priority == 0)[0]; + const signingKeyId = `${this.didId}#${signingKey.alias}`; + const deactivateEntry = ""; + const dialogMessage = 'Enter your vault password to sign the entry and delete the Identity'; + + this.dialogsService.open(PasswordDialogComponent, ModalSizeTypes.ExtraExtraLarge, dialogMessage) + .subscribe((vaultPassword: string) => { + if (vaultPassword) { + this.spinner.show(); + this.signingService + .signDIDEntry(signingKeyId, signingKey.type as SignatureType, deactivateEntry, EntryType.DeactivateDIDEntry, vaultPassword) + .subscribe((result: SignatureResultModel) => { + if (result.success) { + this.didService + .recordEntryOnChain( + EntryType.DeactivateDIDEntry, + deactivateEntry, + signingKeyId, + result.signatureBase64) + .subscribe((recordResult: any) => { + if (recordResult.error) { + this.spinner.hide(); + this.toastr.error(recordResult.message); + } else { + this.vaultService + .removeDIDFromVault( + didId, + vaultPassword) + .subscribe((result: ResultModel) => { + this.spinner.hide(); + + if (result.success) { + this.store.dispatch(new updateDIDActions.CompleteDIDUpdate(didId)); + this.toastr.success(result.message); + this.router.navigate([SharedRoutes.ManageDids]); + } else { + /** + * this should never happen + */ + this.toastr.error('A problem occurred! Please, try again'); + } + }); + } + }); + } else { + this.spinner.hide(); + this.toastr.error(result.message); + } + }); + } + }); + } + + private postProcessDidBackupFile(encryptedFile: string, didId: string) { + const parsedFile = JSON.parse(encryptedFile); + const newKeysFile: any = { }; + + newKeysFile.data = parsedFile.data; + newKeysFile.encryptionAlgo = { + name: 'AES-GCM', + iv: parsedFile.iv, + salt: parsedFile.salt, + tagLength: 128 + }; + newKeysFile.did = didId; + + return JSON.stringify(newKeysFile, null, 2); + } +} diff --git a/src/app/components/did/services/services.component.html b/src/app/components/did/services/services.component.html new file mode 100644 index 0000000..9f957fe --- /dev/null +++ b/src/app/components/did/services/services.component.html @@ -0,0 +1,121 @@ +
+

+ Add services + {{headerTooltipMessage}} {{headerBoldPartTooltipMessage}} + +

+ + +
+ diff --git a/src/app/components/did/services/services.component.scss b/src/app/components/did/services/services.component.scss new file mode 100644 index 0000000..fc130dc --- /dev/null +++ b/src/app/components/did/services/services.component.scss @@ -0,0 +1,78 @@ +$mainColor: #2F5BE7; +.steps-content { + h3 { + text-align: center; + font-size: 15px !important; + text-transform: uppercase; + font-weight: 600; + margin: 35px 0px; + } + .collapsible { + padding: 10px; + border: 2px solid $mainColor; + border-radius: 4px; + h3 { + font-size: 15px; + text-transform: uppercase; + margin-top: 0px; + text-align: left; + position: relative; + cursor: pointer; + margin-bottom: 0px; + span { + width: calc(100% - 80px); + text-overflow: ellipsis; + overflow: hidden; + display: inline-block; + } + &:not(.withoutPlus){ + &::before { + position: absolute; + top: 10px; + right: 10px; + background: $mainColor; + width: 14px; + height: 2px; + content: ""; + display: inline-block; + } + &::after { + position: absolute; + top: 4px; + right: 16.5px; + background: $mainColor; + width: 2px; + height: 14px; + content: ""; + display: inline-block; + transition: all 0.3s ease; + } + } + &.collapsed { + &::after { + transform: rotateZ(90deg); + opacity: 0; + } + } + } + } +} +.button-footer { + bottom: 0px; + position: absolute; + width: 100%; + left: 0px; + padding: 10px 15px; + -webkit-box-shadow: 0px -4px 5px 0px rgba(179,179,179,1); + -moz-box-shadow: 0px -4px 5px 0px rgba(179,179,179,1); + box-shadow: 0px -4px 5px 0px rgba(179,179,179,1); + background: #fff; +} +.delete-button { + i { + font-size: 12px; + margin-top: -5px; + display: inline-block; + vertical-align: middle; + } +} diff --git a/src/app/components/did/services/services.component.ts b/src/app/components/did/services/services.component.ts new file mode 100644 index 0000000..fe5269b --- /dev/null +++ b/src/app/components/did/services/services.component.ts @@ -0,0 +1,133 @@ +import { CollapseComponent } from 'angular-bootstrap-md'; +import { Component, OnInit, AfterViewInit, ViewChildren } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { Store, select } from '@ngrx/store'; + +import { ActionType } from 'src/app/core/enums/action-type'; +import { AddService, RemoveService } from 'src/app/core/store/create-did/create-did.actions'; +import { AppState } from 'src/app/core/store/app.state'; +import { BaseComponent } from 'src/app/components/base.component'; +import { ComponentServiceModel } from 'src/app/core/models/component-service.model'; +import CustomValidators from 'src/app/core/utils/customValidators'; +import { RemoveConfirmModalComponent } from '../../modals/remove-confirm-modal/remove-confirm-modal.component'; +import { ServiceModel } from 'src/app/core/models/service.model'; +import { Subscription } from 'rxjs'; +import { TooltipMessages } from 'src/app/core/utils/tooltip.messages'; +import { WorkflowService } from 'src/app/core/services/workflow/workflow.service'; + +const UP_POSITION = 'up'; +const DOWN_POSITION = 'down'; + +@Component({ + selector: 'app-services', + templateUrl: './services.component.html', + styleUrls: ['./services.component.scss'] +}) +export class ServicesComponent extends BaseComponent implements OnInit, AfterViewInit { + @ViewChildren(CollapseComponent) collapses: CollapseComponent[]; + private subscription: Subscription; + public services: ComponentServiceModel[] = []; + public serviceForm: FormGroup; + public actionType = ActionType; + public headerTooltipMessage = TooltipMessages.ServicesHeaderTooltip; + public headerBoldPartTooltipMessage = TooltipMessages.ServicesHeaderBoldPartTooltip; + public typeTooltipMessage = TooltipMessages.ServiceTypeTooltip; + public endpointTooltipMessage = TooltipMessages.ServiceEndpointTooltip; + public continueButtonText: string; + + constructor( + private fb: FormBuilder, + private modalService: NgbModal, + private store: Store, + private workflowService: WorkflowService) { + super(); + } + + ngOnInit() { + this.subscription = this.store + .pipe(select(state => state)) + .subscribe(state => { + this.services = state.createDID.services.map(service => new ComponentServiceModel(service, DOWN_POSITION)); + this.continueButtonText = this.services.length > 0 ? 'Next' : 'Skip'; + }); + + this.subscriptions.push(this.subscription); + this.createForm(); + } + + ngAfterViewInit() { + setTimeout(() => { + this.collapses.forEach((collapse: CollapseComponent, index) => { + if (index === this.collapses.length - 1) { + collapse.toggle(); + } + }); + }); + } + + createForm() { + this.serviceForm = this.fb.group({ + type: ['', [Validators.required]], + endpoint: ['', [Validators.required]], + alias: ['', [Validators.required, CustomValidators.uniqueServiceAlias(this.services.map(s => s.serviceModel))]], + priorityRequirement: [null, [Validators.min(0), Validators.max(100)]] + }); + } + + addService() { + if (this.serviceForm.invalid) { + return; + } + + const service = new ServiceModel( + this.type.value, + this.endpoint.value, + this.alias.value, + this.priorityRequirement.value + ); + + this.store.dispatch(new AddService(service)); + this.createForm(); + } + + removeService(service: ServiceModel, event) { + event.stopPropagation(); + const confirmRef = this.modalService.open(RemoveConfirmModalComponent); + confirmRef.componentInstance.objectType = 'service'; + confirmRef.result.then((result) => { + this.store.dispatch(new RemoveService(service)); + this.createForm(); + }).catch((error) => { + }); + } + + toggleService(serviceModel) { + const service = this.services.find(s => s.serviceModel === serviceModel); + service.iconPosition = service.iconPosition === DOWN_POSITION ? UP_POSITION : DOWN_POSITION; + } + + goToNext() { + this.workflowService.moveToNextStep(); + } + + goToPrevious() { + this.workflowService.moveToPreviousStep(); + } + + get type () { + return this.serviceForm.get('type'); + } + + get alias () { + return this.serviceForm.get('alias'); + } + + get endpoint () { + return this.serviceForm.get('endpoint'); + } + + get priorityRequirement() { + return this.serviceForm.get('priorityRequirement'); + } +} diff --git a/src/app/components/did/stepper/stepper.component.html b/src/app/components/did/stepper/stepper.component.html new file mode 100644 index 0000000..41f70b4 --- /dev/null +++ b/src/app/components/did/stepper/stepper.component.html @@ -0,0 +1,3 @@ + diff --git a/src/app/components/did/stepper/stepper.component.scss b/src/app/components/did/stepper/stepper.component.scss new file mode 100644 index 0000000..4431e33 --- /dev/null +++ b/src/app/components/did/stepper/stepper.component.scss @@ -0,0 +1,24 @@ +$mainColor: #2F5BE7; +.steper { + overflow: hidden; + text-align: center; + margin: 20px 0px; + a { + width: 30px; + height: 30px; + display: inline-block; + &:not(:last-child){ + margin-right: 10px; + } + text-decoration: none; + border: 2px solid $mainColor; + border-radius: 4px; + line-height: 28px; + text-align: center; + font-weight: 600; + &.active { + background: $mainColor; + color: #fff; + } + } +} \ No newline at end of file diff --git a/src/app/components/did/stepper/stepper.component.ts b/src/app/components/did/stepper/stepper.component.ts new file mode 100644 index 0000000..e824dec --- /dev/null +++ b/src/app/components/did/stepper/stepper.component.ts @@ -0,0 +1,40 @@ +import { Component, OnInit } from '@angular/core'; +import { Store, select } from '@ngrx/store'; +import { Subscription } from 'rxjs'; + +import { ActionType } from 'src/app/core/enums/action-type'; +import { AppState } from 'src/app/core/store/app.state'; +import { BaseComponent } from '../../base.component'; + +@Component({ + selector: 'app-stepper', + templateUrl: './stepper.component.html', + styleUrls: ['./stepper.component.scss'] +}) +export class StepperComponent extends BaseComponent implements OnInit { + private subscription: Subscription; + public currentStepIndex: number; + public totalNumberOfSteps: number = 3; + public isVisible: boolean = false; + + constructor(private store: Store) { + super(); + } + + ngOnInit() { + this.subscription = this.store + .pipe(select(state => state.workflow)) + .subscribe(workflow => { + this.currentStepIndex = workflow.currentStepIndex; + if (workflow.selectedAction === ActionType.CreateAdvanced + && this.currentStepIndex > 0 + && this.currentStepIndex <= this.totalNumberOfSteps) { + this.isVisible = true; + } else { + this.isVisible = false; + } + }); + + this.subscriptions.push(this.subscription); + } +} diff --git a/src/app/components/did/summary/summary.component.html b/src/app/components/did/summary/summary.component.html new file mode 100644 index 0000000..aa5bd00 --- /dev/null +++ b/src/app/components/did/summary/summary.component.html @@ -0,0 +1,17 @@ +
+

Identity preview

+ +
+
+
+ +
diff --git a/src/app/components/did/summary/summary.component.scss b/src/app/components/did/summary/summary.component.scss new file mode 100644 index 0000000..b0dd57f --- /dev/null +++ b/src/app/components/did/summary/summary.component.scss @@ -0,0 +1,20 @@ +.steps-content { + h3 { + text-align: center; + font-size: 15px !important; + text-transform: uppercase; + font-weight: 600; + margin: 35px 0px; + } +} +.button-footer { + bottom: 0px; + position: absolute; + width: 100%; + left: 0px; + padding: 10px 15px; + -webkit-box-shadow: 0px -4px 5px 0px rgba(179,179,179,1); + -moz-box-shadow: 0px -4px 5px 0px rgba(179,179,179,1); + box-shadow: 0px -4px 5px 0px rgba(179,179,179,1); + background: #fff; +} diff --git a/src/app/components/did/summary/summary.component.ts b/src/app/components/did/summary/summary.component.ts new file mode 100644 index 0000000..8ecca82 --- /dev/null +++ b/src/app/components/did/summary/summary.component.ts @@ -0,0 +1,125 @@ +import { Component, OnInit } from '@angular/core'; +import { NgxSpinnerService } from 'ngx-spinner'; +import { Router } from '@angular/router'; +import { Store, select } from '@ngrx/store'; +import { Subscription } from 'rxjs'; +import { ToastrService } from 'ngx-toastr'; + +import { ActionType } from 'src/app/core/enums/action-type'; +import { AppState } from 'src/app/core/store/app.state'; +import { BaseComponent } from 'src/app/components/base.component'; +import { ClearCreateDIDState } from 'src/app/core/store/create-did/create-did.actions'; +import { CloseFormScreen } from 'src/app/core/store/workflow/workflow.actions'; +import { DIDDocument } from 'src/app/core/interfaces/did-document'; +import { DidKeyModel } from 'src/app/core/models/did-key.model'; +import { DIDService } from 'src/app/core/services/did/did.service'; +import { DialogsService } from 'src/app/core/services/dialogs/dialogs.service'; +import { EntryType } from 'src/app/core/enums/entry-type'; +import { ManagementKeyModel } from 'src/app/core/models/management-key.model'; +import { ModalSizeTypes } from 'src/app/core/enums/modal-size-types'; +import { PasswordDialogComponent } from 'src/app/components/dialogs/password/password.dialog.component'; +import { ResultModel } from 'src/app/core/models/result.model'; +import { SharedRoutes } from 'src/app/core/enums/shared-routes'; +import { UpdateEntryDocument } from 'src/app/core/interfaces/update-entry-document'; +import { VaultService } from 'src/app/core/services/vault/vault.service'; +import { WorkflowService } from 'src/app/core/services/workflow/workflow.service'; + +@Component({ + selector: 'app-summary', + templateUrl: './summary.component.html', + styleUrls: ['./summary.component.scss'] +}) +export class SummaryComponent extends BaseComponent implements OnInit { + private subscription: Subscription; + private didId: string; + private didKeys: DidKeyModel[]; + private managementKeys: ManagementKeyModel[]; + public actionType = ActionType; + public entry: DIDDocument | UpdateEntryDocument; + public entryPretified: string; + public selectedAction: string; + + constructor( + private dialogsService: DialogsService, + private didService: DIDService, + private router: Router, + private spinner: NgxSpinnerService, + private store: Store, + private toastr: ToastrService, + private vaultService: VaultService, + private workflowService: WorkflowService) { + super(); + } + + ngOnInit() { + this.didId = this.didService.getId(); + this.selectedAction = this.workflowService.getSelectedAction(); + + this.subscription = this.store + .pipe(select(state => state)) + .subscribe(state => { + this.managementKeys = state.createDID.managementKeys; + this.didKeys = state.createDID.didKeys; + }); + + this.subscriptions.push(this.subscription); + + this.entry = this.didService.generateEntry(EntryType.CreateDIDEntry); + this.entryPretified = JSON.stringify(this.entry, null, 2); + } + + recordOnChain() { + const dialogMessage = 'Enter your vault password to save your Digital Identity'; + + this.dialogsService.open(PasswordDialogComponent, ModalSizeTypes.ExtraExtraLarge, dialogMessage) + .subscribe((vaultPassword: string) => { + if (vaultPassword) { + this.spinner.show(); + this.vaultService + .canDecryptVault(vaultPassword) + .subscribe((result: ResultModel) => { + if (result.success) { + this.didService + .recordEntryOnChain(EntryType.CreateDIDEntry, this.entry) + .subscribe((recordResult: any) => { + if (recordResult.error) { + this.spinner.hide(); + this.toastr.error(recordResult.message); + } else { + this.vaultService + .saveDIDToVault( + this.didId, + this.entry as DIDDocument, + this.managementKeys, + this.didKeys, + vaultPassword) + .subscribe((result: ResultModel) => { + this.spinner.hide(); + + if (result.success) { + this.store.dispatch(new ClearCreateDIDState()); + this.store.dispatch(new CloseFormScreen()); + this.toastr.success('You have successfully created a new Digital Identity'); + } else { + /** + * this should never happen + */ + this.toastr.error('A problem occurred! Please, try to create a new Digital Identity'); + this.router.navigate([SharedRoutes.ManageDids]); + } + }); + } + }); + } else { + this.spinner.hide(); + this.toastr.error(result.message); + } + }); + } + }); + } + + goToPrevious() { + this.workflowService.moveToPreviousStep(); + } +} diff --git a/src/app/components/did/update-did/did-key-form/did-key-form.component.html b/src/app/components/did/update-did/did-key-form/did-key-form.component.html new file mode 100644 index 0000000..373caab --- /dev/null +++ b/src/app/components/did/update-did/did-key-form/did-key-form.component.html @@ -0,0 +1,72 @@ +
+
+ +
+ +
+
+ +
+ + +
+ +
+ + +
+
Key controller is required!
+
Key controller must be a valid Identity!
+
+
+ +
+ + +
+
Alias is required!
+
Alias must not be more than 32 characters long and must contain only lower-case letters, digits and hyphens!
+
You have already used this alias!
+
+
+ +
+ + +
+
Priority requirement must be a positive number!
+
Priority requirement must not be bigger than 100!
+
+
+ +
+ + +
+
diff --git a/src/app/components/did/update-did/did-key-form/did-key-form.component.scss b/src/app/components/did/update-did/did-key-form/did-key-form.component.scss new file mode 100644 index 0000000..5e5958b --- /dev/null +++ b/src/app/components/did/update-did/did-key-form/did-key-form.component.scss @@ -0,0 +1,4 @@ +.checkbox-inline { + display: inline; + margin-left: 15px; +} \ No newline at end of file diff --git a/src/app/components/did/update-did/did-key-form/did-key-form.component.ts b/src/app/components/did/update-did/did-key-form/did-key-form.component.ts new file mode 100644 index 0000000..11eaf04 --- /dev/null +++ b/src/app/components/did/update-did/did-key-form/did-key-form.component.ts @@ -0,0 +1,195 @@ +import { ActivatedRoute, Router } from '@angular/router'; +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators, FormArray, FormControl, ValidatorFn } from '@angular/forms'; +import { Store, select } from '@ngrx/store'; +import { Subscription } from 'rxjs'; + +import { AddDIDKey, UpdateDIDKey } from 'src/app/core/store/update-did/update-did.actions'; +import { AppState } from 'src/app/core/store/app.state'; +import { BaseComponent } from 'src/app/components/base.component'; +import CustomValidators from 'src/app/core/utils/customValidators'; +import { DidKeyModel } from 'src/app/core/models/did-key.model'; +import { KeysService } from 'src/app/core/services/keys/keys.service'; +import { ManagementKeyModel } from 'src/app/core/models/management-key.model'; +import { PurposeType } from 'src/app/core/enums/purpose-type'; +import { SignatureType } from 'src/app/core/enums/signature-type'; +import { TooltipMessages } from 'src/app/core/utils/tooltip.messages'; +import { WorkflowService } from 'src/app/core/services/workflow/workflow.service'; + +@Component({ + selector: 'app-did-key-form', + templateUrl: './did-key-form.component.html', + styleUrls: ['./did-key-form.component.scss'] +}) +export class DidKeyFormComponent extends BaseComponent implements OnInit { + private subscription: Subscription; + private purposesFormArray: FormArray; + public aliasTooltipMessage = TooltipMessages.AliasTooltip; + public controllerTooltipMessage = TooltipMessages.ControllerTooltip; + public signatureTypeTooltipMessage = TooltipMessages.SignatureTypeTooltip; + public availablePurposes = [ + {name: 'Public Key', value: PurposeType.PublicKey}, + {name: 'Authentication Key', value: PurposeType.AuthenticationKey} + ]; + public CreateMode = 1; + public UpdateMode = 2; + public didId: string; + public keyForm: FormGroup; + public managementKeys: ManagementKeyModel[]; + public didKeys: DidKeyModel[]; + public keyModel: DidKeyModel; + public mode: number; + public formScreenOpen: boolean = true; + + constructor ( + private fb: FormBuilder, + private route: ActivatedRoute, + private router: Router, + private store: Store, + private keysService: KeysService, + private workflowService: WorkflowService) { + super(); + } + + ngOnInit() { + this.didId = this.route.parent.snapshot.params.id; + + this.subscription = this.store + .pipe(select(state => state.updateDID)) + .subscribe(updateDIDState => { + const didUpdateModel = updateDIDState.dids.find(d => d.didId == this.didId); + this.managementKeys = didUpdateModel.managementKeys; + this.didKeys = didUpdateModel.didKeys; + }); + + this.subscriptions.push(this.subscription); + + if (this.route.snapshot.params.id) { + const keyAlias = this.route.snapshot.params.id; + this.keyModel = this.didKeys.find(k => k.alias === keyAlias); + this.mode = this.UpdateMode; + this.purposesFormArray = new FormArray([ + new FormControl(this.keyModel.purpose.includes(PurposeType.PublicKey)), + new FormControl(this.keyModel.purpose.includes(PurposeType.AuthenticationKey)) + ], this.validateCheckboxes()); + + } else { + this.keyModel = new DidKeyModel('', [], SignatureType.EdDSA, this.didId, '', '', null); + this.mode = this.CreateMode; + this.purposesFormArray = new FormArray([new FormControl(false), new FormControl(false)], this.validateCheckboxes()); + } + + this.createForm(); + } + + createForm() { + this.keyForm = this.fb.group({ + type: [this.keyModel.type, [Validators.required]], + purposes: this.purposesFormArray, + controller: [this.keyModel.controller, [Validators.required]], + alias: [this.keyModel.alias, [ + Validators.required, + CustomValidators.uniqueKeyAlias(this.managementKeys, this.didKeys, this.keyModel.alias) + ]], + priorityRequirement: [this.keyModel.priorityRequirement, [Validators.min(0), Validators.max(100)]] + }); + + if (this.mode == this.UpdateMode) { + this.keyForm.get('type').disable(); + } + } + + saveForm() { + if (this.keyForm.invalid) { + return; + } + + this.mode == this.CreateMode + ? this.generateKey() + : this.editKey(); + + this.router.navigate([`dids/preview/${this.didId}`]); + this.workflowService.closeUpdateForm(); + } + + private generateKey() { + const selectedPurposes = this.keyForm.value.purposes + .map((selected, i) => selected ? this.availablePurposes[i].value : null) + .filter(p => p !== null); + + this.keysService.generateKeyPair(this.type.value) + .subscribe(keyPair => { + const generatedKey = new DidKeyModel( + this.alias.value, + selectedPurposes, + this.type.value, + this.controller.value, + keyPair.publicKey, + keyPair.privateKey, + this.priorityRequirement.value + ); + + this.store.dispatch(new AddDIDKey(this.didId, generatedKey)); + }); + } + + private editKey(){ + if (this.isKeyUpdated()) { + let updatedKeyModel = Object.assign({}, this.keyModel); + + const selectedPurposes = this.keyForm.value.purposes + .map((selected, i) => selected ? this.availablePurposes[i].value : null) + .filter(p => p !== null); + + updatedKeyModel.alias = this.alias.value; + updatedKeyModel.purpose = selectedPurposes; + updatedKeyModel.controller = this.controller.value; + updatedKeyModel.priorityRequirement = this.priorityRequirement.value; + + this.store.dispatch(new UpdateDIDKey(this.didId, updatedKeyModel)); + } + } + + private validateCheckboxes() { + const validator: ValidatorFn = (formArray: FormArray) => { + const totalSelected = formArray.controls + .map(control => control.value) + .reduce((prev, next) => next ? prev + next : prev, 0); + + return totalSelected > 0 ? null : { required: true }; + }; + + return validator; + } + + private isKeyUpdated() { + if (this.alias.value !== this.keyModel.alias + || this.purposes.value !== this.keyModel.purpose[0] + || this.controller.value !== this.keyModel.controller + || this.priorityRequirement.value !== this.keyModel.priorityRequirement) { + return true; + } + + return false; + } + + get type() { + return this.keyForm.get('type'); + } + + get alias() { + return this.keyForm.get('alias'); + } + + get controller() { + return this.keyForm.get('controller'); + } + + get purposes() { + return this.keyForm.get('purposes'); + } + + get priorityRequirement() { + return this.keyForm.get('priorityRequirement'); + } +} diff --git a/src/app/components/did/update-did/management-key-form/management-key-form.component.html b/src/app/components/did/update-did/management-key-form/management-key-form.component.html new file mode 100644 index 0000000..eec27d1 --- /dev/null +++ b/src/app/components/did/update-did/management-key-form/management-key-form.component.html @@ -0,0 +1,90 @@ +
+
+ + +
+ +
+ + +
+
Key controller is required!
+
Key controller must be a valid Identity!
+
+
+ +
+ + +
+
Alias is required!
+
Alias must not be more than 32 characters long and must contain only lower-case letters, digits and hyphens!
+
You have already used this alias!
+
+
+ +
+ + +
+
Priority is required!
+
Priority must be a positive number!
+
Priority must not be bigger than 100!
+
+
+ +
+ + +
+
Priority requirement must be a positive number!
+
Priority requirement must not be bigger than 100!
+
+
+ +
+ + +
+
diff --git a/src/app/components/did/update-did/management-key-form/management-key-form.component.scss b/src/app/components/did/update-did/management-key-form/management-key-form.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/components/did/update-did/management-key-form/management-key-form.component.ts b/src/app/components/did/update-did/management-key-form/management-key-form.component.ts new file mode 100644 index 0000000..c865592 --- /dev/null +++ b/src/app/components/did/update-did/management-key-form/management-key-form.component.ts @@ -0,0 +1,163 @@ +import { ActivatedRoute, Router } from '@angular/router'; +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Store, select } from '@ngrx/store'; +import { Subscription } from 'rxjs'; + +import { AddManagementKey, UpdateManagementKey } from 'src/app/core/store/update-did/update-did.actions'; +import { AppState } from 'src/app/core/store/app.state'; +import { BaseComponent } from 'src/app/components/base.component'; +import CustomValidators from 'src/app/core/utils/customValidators'; +import { DidKeyModel } from 'src/app/core/models/did-key.model'; +import { KeysService } from 'src/app/core/services/keys/keys.service'; +import { ManagementKeyModel } from 'src/app/core/models/management-key.model'; +import { SignatureType } from 'src/app/core/enums/signature-type'; +import { TooltipMessages } from 'src/app/core/utils/tooltip.messages'; +import { WorkflowService } from 'src/app/core/services/workflow/workflow.service'; + +@Component({ + selector: 'app-management-key-form', + templateUrl: './management-key-form.component.html', + styleUrls: ['./management-key-form.component.scss'] +}) +export class ManagementKeyFormComponent extends BaseComponent implements OnInit { + private subscription: Subscription; + public aliasTooltipMessage = TooltipMessages.AliasTooltip; + public controllerTooltipMessage = TooltipMessages.ControllerTooltip; + public signatureTypeTooltipMessage = TooltipMessages.SignatureTypeTooltip; + public CreateMode = 1; + public UpdateMode = 2; + public didId: string; + public keyForm: FormGroup; + public managementKeys: ManagementKeyModel[]; + public didKeys: DidKeyModel[]; + public keyModel: ManagementKeyModel; + public mode: number; + public formScreenOpen: boolean = true; + + constructor( + private fb: FormBuilder, + private route: ActivatedRoute, + private router: Router, + private store: Store, + private keysService: KeysService, + private workflowService: WorkflowService) { + super(); + } + + ngOnInit() { + this.didId = this.route.parent.snapshot.params.id; + + this.subscription = this.store + .pipe(select(state => state.updateDID)) + .subscribe(updateDIDState => { + const didUpdateModel = updateDIDState.dids.find(d => d.didId == this.didId); + this.managementKeys = didUpdateModel.managementKeys; + this.didKeys = didUpdateModel.didKeys; + }); + + this.subscriptions.push(this.subscription); + + if (this.route.snapshot.params.id) { + const keyAlias = this.route.snapshot.params.id; + this.keyModel = this.managementKeys.find(k => k.alias === keyAlias); + this.mode = this.UpdateMode; + } else { + this.keyModel = new ManagementKeyModel('', null, SignatureType.EdDSA, this.didId, '', '', null); + this.mode = this.CreateMode; + } + + this.createForm(); + } + + createForm() { + this.keyForm = this.fb.group({ + type: [this.keyModel.type, [Validators.required]], + controller: [this.keyModel.controller, [Validators.required]], + alias: [this.keyModel.alias, [ + Validators.required, + CustomValidators.uniqueKeyAlias(this.managementKeys, this.didKeys, this.keyModel.alias) + ]], + priority: [this.keyModel.priority, [Validators.required, Validators.min(0), Validators.max(100)]], + priorityRequirement: [this.keyModel.priorityRequirement, [Validators.min(0), Validators.max(100)]] + }); + + if (this.mode == this.UpdateMode) { + this.keyForm.get('type').disable(); + } + } + + saveForm() { + if (this.keyForm.invalid) { + return; + } + + this.mode == this.CreateMode + ? this.generateKey() + : this.editKey(); + + this.router.navigate([`dids/preview/${this.didId}`]); + this.workflowService.closeUpdateForm(); + } + + private generateKey() { + this.keysService.generateKeyPair(this.type.value) + .subscribe(keyPair => { + const generatedKey = new ManagementKeyModel( + this.alias.value, + this.priority.value, + this.type.value, + this.controller.value, + keyPair.publicKey, + keyPair.privateKey, + this.priorityRequirement.value + ); + + this.store.dispatch(new AddManagementKey(this.didId, generatedKey)); + }); + } + + private editKey() { + if (this.isKeyUpdated()) { + let updatedKeyModel = Object.assign({}, this.keyModel); + + updatedKeyModel.alias = this.alias.value; + updatedKeyModel.controller = this.controller.value; + updatedKeyModel.priority = this.priority.value; + updatedKeyModel.priorityRequirement = this.priorityRequirement.value; + + this.store.dispatch(new UpdateManagementKey(this.didId, updatedKeyModel)); + } + } + + private isKeyUpdated() { + if (this.alias.value !== this.keyModel.alias + || this.controller.value !== this.keyModel.controller + || this.priority.value !== this.keyModel.priority + || this.priorityRequirement.value !== this.keyModel.priorityRequirement) { + return true; + } + + return false; + } + + get type() { + return this.keyForm.get('type'); + } + + get alias() { + return this.keyForm.get('alias'); + } + + get controller() { + return this.keyForm.get('controller'); + } + + get priority() { + return this.keyForm.get('priority'); + } + + get priorityRequirement() { + return this.keyForm.get('priorityRequirement'); + } +} diff --git a/src/app/components/did/update-did/service-form/service-form.component.html b/src/app/components/did/update-did/service-form/service-form.component.html new file mode 100644 index 0000000..4906c6c --- /dev/null +++ b/src/app/components/did/update-did/service-form/service-form.component.html @@ -0,0 +1,74 @@ +
+
+ + +
+
Service type is required!
+
+
+ +
+ + +
+
Service endpoint is required!
+
Service endpoint must be a valid URL address starting with http:// or https://
+
+
+ +
+ + +
+
Alias is required!
+
Alias must not be more than 32 characters long and must contain only lower-case letters, digits and hyphens!
+
You have already used this alias!
+
+
+ +
+ + +
+
Priority requirement must be a positive number!
+
Priority requirement must not be bigger than 100!
+
+
+ +
+ +
+
diff --git a/src/app/components/did/update-did/service-form/service-form.component.scss b/src/app/components/did/update-did/service-form/service-form.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/components/did/update-did/service-form/service-form.component.ts b/src/app/components/did/update-did/service-form/service-form.component.ts new file mode 100644 index 0000000..ed0ea04 --- /dev/null +++ b/src/app/components/did/update-did/service-form/service-form.component.ts @@ -0,0 +1,102 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Router, ActivatedRoute } from '@angular/router'; +import { Store, select } from '@ngrx/store'; +import { Subscription } from 'rxjs'; + +import { AddService } from 'src/app/core/store/update-did/update-did.actions'; +import { AppState } from 'src/app/core/store/app.state'; +import { BaseComponent } from 'src/app/components/base.component'; +import CustomValidators from 'src/app/core/utils/customValidators'; +import { ServiceModel } from 'src/app/core/models/service.model'; +import { TooltipMessages } from 'src/app/core/utils/tooltip.messages'; +import { WorkflowService } from 'src/app/core/services/workflow/workflow.service'; + +@Component({ + selector: 'app-service-form', + templateUrl: './service-form.component.html', + styleUrls: ['./service-form.component.scss'] +}) +export class ServiceFormComponent extends BaseComponent implements OnInit { + private subscription: Subscription; + public aliasTooltipMessage = TooltipMessages.AliasTooltip; + public controllerTooltipMessage = TooltipMessages.ControllerTooltip; + public signatureTypeTooltipMessage = TooltipMessages.SignatureTypeTooltip; + public headerTooltipMessage = TooltipMessages.ServicesHeaderTooltip; + public headerBoldPartTooltipMessage = TooltipMessages.ServicesHeaderBoldPartTooltip; + public typeTooltipMessage = TooltipMessages.ServiceTypeTooltip; + public endpointTooltipMessage = TooltipMessages.ServiceEndpointTooltip; + public didId: string; + public formScreenOpen: boolean = true; + public serviceForm: FormGroup; + public services: ServiceModel[]; + + constructor( + private fb: FormBuilder, + private route: ActivatedRoute, + private router: Router, + private store: Store, + private workflowService: WorkflowService ) { + super(); + } + + ngOnInit() { + this.didId = this.route.parent.snapshot.params.id; + + this.subscription = this.store + .pipe(select(state => state.updateDID)) + .subscribe(updateDIDState => { + const didUpdateModel = updateDIDState.dids.find(d => d.didId === this.didId); + this.services = didUpdateModel.services; + }); + + this.subscriptions.push(this.subscription); + this.createForm(); + } + + createForm() { + this.serviceForm = this.fb.group({ + type: ['', [Validators.required]], + endpoint: ['', [Validators.required]], + alias: ['', [ + Validators.required, + CustomValidators.uniqueServiceAlias(this.services) + ]], + priorityRequirement: [null, [Validators.min(0), Validators.max(100)]] + }); + } + + addService() { + if (this.serviceForm.invalid) { + return; + } + + const service = new ServiceModel( + this.type.value, + this.endpoint.value, + this.alias.value, + this.priorityRequirement.value + ); + + this.store.dispatch(new AddService(this.didId, service)); + + this.router.navigate([`dids/preview/${this.didId}`]); + this.workflowService.closeUpdateForm(); + } + + get type() { + return this.serviceForm.get('type'); + } + + get alias() { + return this.serviceForm.get('alias'); + } + + get endpoint() { + return this.serviceForm.get('endpoint'); + } + + get priorityRequirement() { + return this.serviceForm.get('priorityRequirement'); + } +} diff --git a/src/app/components/factom-addresses/factom-addresses.module.ts b/src/app/components/factom-addresses/factom-addresses.module.ts new file mode 100644 index 0000000..a927add --- /dev/null +++ b/src/app/components/factom-addresses/factom-addresses.module.ts @@ -0,0 +1,30 @@ +import { AutofocusModule } from 'angular-autofocus-fix'; +import { CommonModule } from '@angular/common'; +import { ClickOutsideModule } from 'ng-click-outside'; +import { FormsModule, ReactiveFormsModule} from '@angular/forms'; +import { MDBBootstrapModule } from 'angular-bootstrap-md'; +import {NgbModule} from '@ng-bootstrap/ng-bootstrap'; +import { NgModule } from '@angular/core'; + +import { factomAddressesComponents } from '.'; +import { FactomAddressesRoutingModule } from './factom-addresses.routing'; + +@NgModule({ + declarations: [ + ...factomAddressesComponents + ], + imports: [ + AutofocusModule, + CommonModule, + ClickOutsideModule, + FactomAddressesRoutingModule, + FormsModule, + MDBBootstrapModule.forRoot(), + NgbModule, + ReactiveFormsModule + ], + exports: [ + ...factomAddressesComponents + ] +}) +export class FactomAddressesModule { } \ No newline at end of file diff --git a/src/app/components/factom-addresses/factom-addresses.routing.ts b/src/app/components/factom-addresses/factom-addresses.routing.ts new file mode 100644 index 0000000..04fc6b1 --- /dev/null +++ b/src/app/components/factom-addresses/factom-addresses.routing.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { ImportAddressComponent } from './import-address/import-address.component'; +import { ManageAddressesComponent } from './manage-addresses/manage-addresses.component'; + +const factomAddressesRoutes: Routes = [ + { path: 'addresses/import/:type', component: ImportAddressComponent }, + { path: 'addresses/manage', component: ManageAddressesComponent }, +]; + +@NgModule({ + imports: [RouterModule.forChild(factomAddressesRoutes)], + exports: [RouterModule] +}) +export class FactomAddressesRoutingModule { } \ No newline at end of file diff --git a/src/app/components/factom-addresses/import-address/import-address.component.html b/src/app/components/factom-addresses/import-address/import-address.component.html new file mode 100644 index 0000000..5fd809d --- /dev/null +++ b/src/app/components/factom-addresses/import-address/import-address.component.html @@ -0,0 +1,54 @@ +
+
+
+
+

+ Import Address +

+
+
+
+
+ + +
+
+ + +
+
Nickname is required!
+
+
+
+ + +
+
Private address is required!
+
+
+
+ + +
+
+
+
+
+
+
+
diff --git a/src/app/components/factom-addresses/import-address/import-address.component.scss b/src/app/components/factom-addresses/import-address/import-address.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/components/factom-addresses/import-address/import-address.component.ts b/src/app/components/factom-addresses/import-address/import-address.component.ts new file mode 100644 index 0000000..a5d3f6f --- /dev/null +++ b/src/app/components/factom-addresses/import-address/import-address.component.ts @@ -0,0 +1,133 @@ +declare const Buffer; +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { NgxSpinnerService } from 'ngx-spinner'; +import { Router, ActivatedRoute } from '@angular/router'; +import { ToastrService } from 'ngx-toastr'; +import * as elliptic from 'elliptic'; +import { isValidPrivateFctAddress, isValidPrivateEcAddress, getPublicAddress } from 'factom'; + +import { DialogsService } from 'src/app/core/services/dialogs/dialogs.service'; +import { FactomAddressType } from 'src/app/core/enums/factom-address-type'; +import { ModalSizeTypes } from 'src/app/core/enums/modal-size-types'; +import { PasswordDialogComponent } from '../../dialogs/password/password.dialog.component'; +import { VaultService } from 'src/app/core/services/vault/vault.service'; + +@Component({ + selector: 'app-import-address', + templateUrl: './import-address.component.html', + styleUrls: ['./import-address.component.scss'] +}) +export class ImportAddressComponent implements OnInit { + public privateAddressForm: FormGroup; + private selectedType: string; + + constructor( + private dialogsService: DialogsService, + private fb: FormBuilder, + private route: ActivatedRoute, + private router: Router, + private spinner: NgxSpinnerService, + private toastr: ToastrService, + private vaultService: VaultService ) { } + + ngOnInit() { + this.selectedType = this.route.snapshot.paramMap.get('type'); + if (this.selectedType != FactomAddressType.FCT && this.selectedType != FactomAddressType.EC && + this.selectedType != FactomAddressType.EtherLink) { + this.selectedType = FactomAddressType.FCT; + } + + this.createPrivateAddressForm(); + } + + createPrivateAddressForm() { + this.privateAddressForm = this.fb.group({ + type: [this.selectedType, Validators.required], + nickname: ['', Validators.required], + privateAddress: ['', [Validators.required]] + }); + } + + importPrivateAddress() { + if (this.privateAddressForm.invalid) { + this.toastr.error('Invalid form! All fields are required'); + return; + } else if (![FactomAddressType.FCT, FactomAddressType.EC, FactomAddressType.EtherLink].includes(this.type.value)) { + this.toastr.error('Invalid address type'); + return; + } else if (this.type.value == FactomAddressType.FCT && !isValidPrivateFctAddress(this.privateAddress.value)) { + this.toastr.error('Invalid FCT private address'); + return; + } else if (this.type.value == FactomAddressType.EtherLink && !this.isValidPrivateEtherLinkAddress(this.privateAddress.value)) { + this.toastr.error('Invalid EtherLink private address'); + return; + } else if (this.type.value == FactomAddressType.EC && !isValidPrivateEcAddress(this.privateAddress.value)) { + this.toastr.error('Invalid EC private address'); + return; + } + + let publicAddress; + + if (this.type.value == FactomAddressType.EtherLink) { + const curve = elliptic.ec('secp256k1'); + if (this.privateAddress.value.startsWith('0x')) { + // Remove the leading 0x from the private key and remove the 04 prefix + // (signifying an uncompressed encoding) from the corresponding public key. + publicAddress = curve.keyFromPrivate(this.privateAddress.value.slice(2)).getPublic('hex').slice(2); + } else { + publicAddress = curve.keyFromPrivate(this.privateAddress.value).getPublic('hex').slice(2); + } + } else { + publicAddress = getPublicAddress(this.privateAddress.value); + } + + const dialogMessage = 'Enter your vault password to import the address'; + this.dialogsService.open(PasswordDialogComponent, ModalSizeTypes.ExtraExtraLarge, dialogMessage) + .subscribe((vaultPassword: string) => { + if (vaultPassword) { + this.spinner.show(); + this.vaultService + .importFactomAddress( + this.type.value, + publicAddress, + this.privateAddress.value, + vaultPassword, + this.nickname.value) + .subscribe(result => { + this.spinner.hide(); + if (result.success) { + this.toastr.success(result.message); + this.router.navigate(['factom/addresses/manage'], { queryParams: { page: this.type.value } }); + } else { + this.toastr.error(result.message); + } + }); + + this.createPrivateAddressForm(); + } + }); + } + + goBack() { + this.router.navigate(['factom/addresses/manage'], { queryParams: { page: this.selectedType } }); + } + + get type() { + return this.privateAddressForm.get('type'); + } + + get nickname() { + return this.privateAddressForm.get('nickname'); + } + + get privateAddress () { + return this.privateAddressForm.get('privateAddress'); + } + + private isValidPrivateEtherLinkAddress(address: string) { + // The private EtherLink address must be a valid ECDSA private key (32 bytes hex string), + // optionally with a leading 0x + return /^(0x)?[0-9a-fA-F]{64}$/.test(address); + } +} diff --git a/src/app/components/factom-addresses/index.ts b/src/app/components/factom-addresses/index.ts new file mode 100644 index 0000000..490b7e1 --- /dev/null +++ b/src/app/components/factom-addresses/index.ts @@ -0,0 +1,7 @@ +import { ImportAddressComponent } from './import-address/import-address.component'; +import { ManageAddressesComponent } from './manage-addresses/manage-addresses.component'; + +export const factomAddressesComponents = [ + ImportAddressComponent, + ManageAddressesComponent +]; \ No newline at end of file diff --git a/src/app/components/factom-addresses/manage-addresses/manage-addresses.component.html b/src/app/components/factom-addresses/manage-addresses/manage-addresses.component.html new file mode 100644 index 0000000..4ba491c --- /dev/null +++ b/src/app/components/factom-addresses/manage-addresses/manage-addresses.component.html @@ -0,0 +1,185 @@ +
+
+
+
+ + + +
+
+
+
diff --git a/src/app/components/factom-addresses/manage-addresses/manage-addresses.component.scss b/src/app/components/factom-addresses/manage-addresses/manage-addresses.component.scss new file mode 100644 index 0000000..9c67891 --- /dev/null +++ b/src/app/components/factom-addresses/manage-addresses/manage-addresses.component.scss @@ -0,0 +1,132 @@ +$mainColor: #2F5BE7; +.table-holder { + margin: 20px 0px; + table { + width: 100%; + white-space: nowrap; + thead { + th { + color: #253992; + text-transform: uppercase; + font-size: 12px; + border-top: 0px; + border-bottom-width: 1px; + vertical-align: middle; + } + } + tbody { + tr { + td { + vertical-align: middle; + } + td:first-child { + span { + white-space: nowrap; + display: inline-block; + font-weight: 700; + } + } + .counter-label { + display: inline-block; + width: 20px; + line-height: 20px; + text-align: center; + background: $mainColor; + color: #fff; + border-radius: 50%; + // transition: box-shadow 0.3s ease; + font-size: 12px; + // &:hover { + // text-decoration: none; + // box-shadow: 0 0 0 3px rgba(47, 91, 231, 0.5); + // } + } + td:last-child { + button:not(:last-child) { + margin-right: 3px; + } + } + } + .public-address { + font-family: monospace, monospace; + font-size: 14px; + } + } + } + .table-striped tbody tr:nth-of-type(odd) { + background-color: #fdfdfd; + } +} +.paginator { + a { + display: inline-block; + background: #E0E8F3; + padding: 7.5px 15px; + text-transform: uppercase; + color: $mainColor; + font-weight: 600; + border-radius: 4px; + transition: all 0.3s ease; + text-decoration: none!important; + &:not(.disabled):hover { + background: $mainColor; + color: #fff; + } + &.disabled { + color: #495463; + cursor: not-allowed; + pointer-events: none; + } + &:first-child { + margin-right: 10px; + } + } +} +a { + cursor: pointer; +} +.info-tables { + margin: 20px 0px; + .nav-link { + color: $mainColor; + &.active { + background: $mainColor; + color: #fff; + } + } +} +.copy-address { + position: relative; + margin-left: 15px; + cursor: pointer; + &::before { + content: "Copied!"; + padding: 5px 15px; + color: #fff; + background: #4caf50; + position: absolute; + top: -4px; + left: 50%; + transform: translateX(-50%) translateY(-100%); + border-radius: 4px; + display: none; + } + &::after { + width: 0; + height: 0; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-top: 7px solid #4caf50; + position: absolute; + top: -6px; + left: 50%; + transform: translateX(-50%); + content: ""; + display: none; + } + &.clicked { + &::before, &::after { + display: inline-block; + } + } +} diff --git a/src/app/components/factom-addresses/manage-addresses/manage-addresses.component.ts b/src/app/components/factom-addresses/manage-addresses/manage-addresses.component.ts new file mode 100644 index 0000000..625b65f --- /dev/null +++ b/src/app/components/factom-addresses/manage-addresses/manage-addresses.component.ts @@ -0,0 +1,253 @@ +declare const Buffer; +import { ActivatedRoute } from '@angular/router'; +import { Component, OnInit } from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgxSpinnerService } from 'ngx-spinner'; +import { ToastrService } from 'ngx-toastr'; +import { generateRandomFctAddress, generateRandomEcAddress } from 'factom'; + +import { BaseComponent } from '../../base.component'; +import { ChromeMessageType } from 'src/app/core/enums/chrome-message-type'; +import { DialogsService } from 'src/app/core/services/dialogs/dialogs.service'; +import { FactomAddressType } from 'src/app/core/enums/factom-address-type'; +import { ModalSizeTypes } from 'src/app/core/enums/modal-size-types'; +import { PasswordDialogComponent } from '../../dialogs/password/password.dialog.component'; +import { PrivateKeyAddressModalComponent } from '../../modals/private-key-address-modal/private-key-address-modal.component'; +import { VaultService } from 'src/app/core/services/vault/vault.service'; +import { + convertECDSAPublicKeyToEthereumAddress, + convertECDSAPublicKeyToEtherLinkAddress, + generateRandomEtherLinkKeyPair, + minifyAddress +} from 'src/app/core/utils/helpers'; + +@Component({ + selector: 'app-manage-addresses', + templateUrl: './manage-addresses.component.html', + styleUrls: ['./manage-addresses.component.scss'] +}) +export class ManageAddressesComponent extends BaseComponent implements OnInit { + public fctAddressesInfo = {}; + public ecAddressesInfo = {}; + public etherLinkAddressesInfo = {}; + public fctAddresses: string[] = []; + public ecAddresses: string[] = []; + public etherLinkAddresses: string[] = []; + public displayedFCTAddresses: string[] = []; + public displayedECAddresses: string[] = []; + public displayedEtherLinkAddresses: Object[] = []; + public selectedAddressType: FactomAddressType = FactomAddressType.FCT; + public FactomAddressType = FactomAddressType; + public editAddressNickname: boolean[] = []; + public pageSize: number = 6; + public currentPage: number = 1; + public currentStartIndex = 0; + public minifyAddress = minifyAddress; + + constructor( + private dialogsService: DialogsService, + private modalService: NgbModal, + private route: ActivatedRoute, + private spinner: NgxSpinnerService, + private toastr: ToastrService, + private vaultService: VaultService ) { + super(); + } + + ngOnInit() { + chrome.tabs && chrome.tabs.getCurrent(function(tab) { + if (tab === undefined) { + chrome.runtime.sendMessage({type: ChromeMessageType.ManageFactomAddressesRequest}, (response) => { + if (response.success) { + const popup_url = chrome.runtime.getURL('index.html'); + chrome.tabs.create({'url': popup_url}); + } + }); + } else { + chrome.runtime.sendMessage({type: ChromeMessageType.CheckRequests}, (response) => { + if (response.manageFactomAddressesRequested) { + chrome.runtime.sendMessage({type: ChromeMessageType.NewTabOpen}); + } + }); + } + }); + + this.getAddressesInfo(); + + const subscription = this.route.queryParams + .subscribe(params => { + if (Object.keys(params).length > 0) { + this.selectedAddressType = params['page']; + } + }); + + this.subscriptions.push(subscription); + } + + changeSelectedAddressType(addressType: FactomAddressType) { + this.selectedAddressType = addressType; + this.currentPage = 1; + this.currentStartIndex = 0; + this.displayedFCTAddresses = this.fctAddresses.slice(this.currentStartIndex, this.currentStartIndex + this.pageSize); + this.displayedECAddresses = this.ecAddresses.slice(this.currentStartIndex, this.currentStartIndex + this.pageSize); + this.displayedEtherLinkAddresses = this.etherLinkAddresses + .slice(this.currentStartIndex, this.currentStartIndex + this.pageSize) + .map(this.convertECDSAPublicKeyToAddresses, this); + } + + editNickname(publicAddress: string, type: FactomAddressType, nickname: string) { + if (nickname.length > 0) { + this.vaultService.updateFactomAddressNickname(publicAddress, type, nickname); + + if (type == FactomAddressType.FCT) { + this.fctAddressesInfo[publicAddress] = nickname; + } else if (type == FactomAddressType.EC) { + this.ecAddressesInfo[publicAddress] = nickname; + } else { + this.etherLinkAddressesInfo[publicAddress] = nickname; + } + } + + this.editAddressNickname[publicAddress] = false; + } + + generateAddressPair() { + const addressPair = (function(addressType) { + switch(addressType) { + case FactomAddressType.FCT: + return generateRandomFctAddress(); + case FactomAddressType.EC: + return generateRandomEcAddress(); + case FactomAddressType.EtherLink: + return generateRandomEtherLinkKeyPair(); + } + })(this.selectedAddressType); + + const dialogMessage = `Enter your vault password to import the generated ${this.selectedAddressType} address`; + this.dialogsService.open(PasswordDialogComponent, ModalSizeTypes.ExtraExtraLarge, dialogMessage) + .subscribe((vaultPassword: string) => { + if (vaultPassword) { + this.spinner.show(); + this.vaultService + .importFactomAddress( + this.selectedAddressType, + addressPair.public, + addressPair.private, + vaultPassword) + .subscribe(result => { + this.spinner.hide(); + if (result.success) { + this.toastr.success(result.message); + this.getAddressesInfo(); + } else { + this.toastr.error(result.message); + } + }); + } + }); + } + + viewPrivateAddress(publicAddress: string) { + const dialogMessage = 'Enter your vault password to view the secret key'; + this.dialogsService.open(PasswordDialogComponent, ModalSizeTypes.Medium, dialogMessage) + .subscribe((vaultPassword: string) => { + if (vaultPassword) { + this.spinner.show(); + this.vaultService + .getPrivateKeyOrAddress(publicAddress, vaultPassword) + .subscribe(result => { + this.spinner.hide(); + if (result.success && result.message) { + const confirmRef = this.modalService.open(PrivateKeyAddressModalComponent); + confirmRef.componentInstance.publicKeyOrAddress = publicAddress; + confirmRef.componentInstance.privateKeyOrAddress = result.message; + confirmRef.componentInstance.isEtherLinkAddress = publicAddress.length == 128; + confirmRef.result + .then((result) => {}) + .catch((error) => {}); + } else if (!result.success) { + this.toastr.error(result.message); + } + }); + } + }); + } + + removeAddress(publicAddress: string, type: FactomAddressType) { + const dialogMessage = 'Warning! Any funds that you have in this address will be irrevocably lost, if you have not backed up your private key. If you want to continue, enter your vault password to remove the FCT/EC address'; + this.dialogsService.open(PasswordDialogComponent, ModalSizeTypes.ExtraExtraLarge, dialogMessage) + .subscribe((vaultPassword: string) => { + if (vaultPassword) { + this.spinner.show(); + this.vaultService + .removeFactomAddress(publicAddress, type, vaultPassword) + .subscribe(result => { + this.spinner.hide(); + if (result.success) { + this.toastr.success(result.message); + this.getAddressesInfo(); + } else { + this.toastr.error(result.message); + } + }); + } + }); + } + + copyAddress(address: string, element) { + const selBox = document.createElement('textarea'); + selBox.style.position = 'fixed'; + selBox.style.left = '0'; + selBox.style.top = '0'; + selBox.style.opacity = '0'; + selBox.value = address; + document.body.appendChild(selBox); + selBox.focus(); + selBox.select(); + document.execCommand('copy'); + document.body.removeChild(selBox); + + element.classList.add('clicked'); + setTimeout(() => {element.classList.remove('clicked')},2000); + } + + changePage(page) { + this.currentPage = page; + this.currentStartIndex = (this.currentPage - 1) * this.pageSize; + + if (this.selectedAddressType == FactomAddressType.FCT) { + this.displayedFCTAddresses = this.fctAddresses.slice(this.currentStartIndex, this.currentStartIndex + this.pageSize); + } else if (this.selectedAddressType == FactomAddressType.EC) { + this.displayedECAddresses = this.ecAddresses.slice(this.currentStartIndex, this.currentStartIndex + this.pageSize); + } else { + this.displayedEtherLinkAddresses = this.etherLinkAddresses + .slice(this.currentStartIndex, this.currentStartIndex + this.pageSize) + .map(this.convertECDSAPublicKeyToAddresses, this); + } + } + + private getAddressesInfo() { + this.fctAddressesInfo = this.vaultService.getFCTAddressesPublicInfo(); + this.ecAddressesInfo = this.vaultService.getECAddressesPublicInfo(); + this.etherLinkAddressesInfo = this.vaultService.getEtherLinkAddressesPublicInfo(); + + this.fctAddresses = Object.keys(this.fctAddressesInfo); + this.ecAddresses = Object.keys(this.ecAddressesInfo); + this.etherLinkAddresses = Object.keys(this.etherLinkAddressesInfo); + + this.displayedFCTAddresses = this.fctAddresses.slice(this.currentStartIndex, this.currentStartIndex + this.pageSize); + this.displayedECAddresses = this.ecAddresses.slice(this.currentStartIndex, this.currentStartIndex + this.pageSize); + this.displayedEtherLinkAddresses = this.etherLinkAddresses + .slice(this.currentStartIndex, this.currentStartIndex + this.pageSize) + .map(this.convertECDSAPublicKeyToAddresses, this); + } + + private convertECDSAPublicKeyToAddresses(publicKey) { + return { + publicKey, + 'ethereum': convertECDSAPublicKeyToEthereumAddress(publicKey), + 'factom': convertECDSAPublicKeyToEtherLinkAddress(publicKey) + } + } + +} diff --git a/src/app/components/home/home.component.html b/src/app/components/home/home.component.html new file mode 100644 index 0000000..6d6f9fb --- /dev/null +++ b/src/app/components/home/home.component.html @@ -0,0 +1,52 @@ +
+
+
+
+
+
+
+ +
+
+ Identities + {{didsCount}} +
+
+
+
+
+
+
+ FCT +
+
+ FCT Addresses + {{fctAddressesCount}} +
+
+
+
+
+
+
+ EC +
+
+ EC Addresses + {{ecAddressesCount}} +
+
+
+
+
+
+ +
+
+
+
+
diff --git a/src/app/components/home/home.component.scss b/src/app/components/home/home.component.scss new file mode 100644 index 0000000..5f32d6f --- /dev/null +++ b/src/app/components/home/home.component.scss @@ -0,0 +1,66 @@ +.card-holder { + margin-top: 20px; + .card-item { + background-image: linear-gradient(45deg, #1c65c9 0%, #2c80ff 100%); + color: #fff; + padding: 20px; + border-radius: 3px; + @media only screen and (max-width: 575px){ + margin-bottom: 10px; + } + .icon-holder { + height: 50px; + width: 50px; + border-radius: 50%; + background: rgba(255,255,255,0.2); + margin-right: 15px; + text-align: center; + line-height: 61px; + float: left; + &.txt { + line-height: 50px; + font-weight: 700; + font-size: 18px; + } + } + .text-holder { + float: left; + font-size: 17px; + .title { + display: block; + letter-spacing: 1px; + text-transform: uppercase; + } + .number { + font-weight: bold; + display: block; + font-size: 23px; + } + } + @media only screen and (min-width: 576px) and (max-width: 767px){ + padding: 10px; + .icon-holder { + width: 35px; + height: 35px; + line-height: 37px; + margin-right: 11px; + .fa-2x { + font-size: 1.2em; + } + } + .text-holder { + font-size: 12px; + .number { + font-size: 17px; + } + } + } + } +} +.graph-panel { + padding-bottom: 0px; + .graph-holder { + width: 100%; + height: 250px; + } +} diff --git a/src/app/components/home/home.component.ts b/src/app/components/home/home.component.ts new file mode 100644 index 0000000..15aee35 --- /dev/null +++ b/src/app/components/home/home.component.ts @@ -0,0 +1,119 @@ +import { Chart } from 'chart.js'; +import { Component, OnInit, ViewChild, ElementRef } from '@angular/core'; + +import { VaultService } from 'src/app/core/services/vault/vault.service'; + +@Component({ + selector: 'app-home', + templateUrl: './home.component.html', + styleUrls: ['./home.component.scss'] +}) +export class HomeComponent implements OnInit { + public didsCount: number; + public fctAddressesCount: number; + public ecAddressesCount: number; + private signedRequestsData: number[]; + private labels: string[] = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + + constructor(private vaultService: VaultService) { } + + ngOnInit() { + this.didsCount = this.vaultService.getDIDsCount(); + this.fctAddressesCount = Object.keys(this.vaultService.getFCTAddressesPublicInfo()).length; + this.ecAddressesCount = Object.keys(this.vaultService.getECAddressesPublicInfo()).length; + this.signedRequestsData = this.vaultService.getSignedRequestsData(); + } + + @ViewChild('signedRequests') chart: ElementRef; + + ngAfterViewInit() { + const today = new Date().getDay(); + const labels = this.labels.slice(today + 1, this.labels.length).concat(this.labels.slice(0, today + 1)); + + this.createChart(labels, this.signedRequestsData); + } + + createChart(labels: string[], data: number[]) { + const H = this.chart.nativeElement.getContext('2d'); + + var gradientStroke1 = H.createLinearGradient(0, 0, 0, 200); + gradientStroke1.addColorStop(0, 'rgb(37, 57, 146, 0.9)'); + var myChart = new Chart( H, { + type: 'line', + data: { + labels: labels, + type: 'line', + datasets: [ { + label: 'Requests', + data: data, + backgroundColor: '#2371E1', + borderColor: '#2371E1', + pointBackgroundColor:'#fff', + pointHoverBackgroundColor:gradientStroke1, + pointBorderColor :gradientStroke1, + pointHoverBorderColor :gradientStroke1, + pointBorderWidth :0, + pointRadius :0, + pointHoverRadius :0, + borderWidth: 2 + }, ] + }, + options: { + maintainAspectRatio: false, + legend: { + display: false + }, + responsive: true, + tooltips: { + mode: 'index', + titleFontSize: 12, + titleFontColor: 'rgba(225,225,225,0.9)', + bodyFontColor: 'rgba(225,225,225,0.9)', + backgroundColor: 'rgba(0,0,0,0.7)', + cornerRadius: 3, + intersect: false, + }, + scales: { + xAxes: [ { + gridLines: { + color: '#e1e1e1', + zeroLineColor: '#e1e1e1' + }, + ticks: { + fontSize: 2, + fontColor: '#e1e1e1' + } + } ], + yAxes: [ { + // display:false, + ticks: { + suggestedMin: 0, // minimum will be 0, unless there is a lower value. + userCallback: function(label, index, labels) { + // when the floored value is the same as the value we have a whole number + if (Math.floor(label) === label) { + return label; + } + + }, + // OR // + beginAtZero: true // minimum value will be 0. + } + } ] + }, + title: { + display: false, + }, + elements: { + line: { + borderWidth: 1 + }, + point: { + radius: 4, + hitRadius: 10, + hoverRadius: 4 + } + } + } + }); + } +} diff --git a/src/app/components/keys/import-key/import-key.component.html b/src/app/components/keys/import-key/import-key.component.html new file mode 100644 index 0000000..ce962c9 --- /dev/null +++ b/src/app/components/keys/import-key/import-key.component.html @@ -0,0 +1,52 @@ +
+
+
+
+

+ Import Key +

+
+
+
+
+ + +
+
+ + +
+
Nickname is required!
+
+
+
+ + +
+
Private key is required!
+
+
+
+ + +
+
+
+
+
+
+
+
diff --git a/src/app/components/keys/import-key/import-key.component.scss b/src/app/components/keys/import-key/import-key.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/components/keys/import-key/import-key.component.ts b/src/app/components/keys/import-key/import-key.component.ts new file mode 100644 index 0000000..25d5ad2 --- /dev/null +++ b/src/app/components/keys/import-key/import-key.component.ts @@ -0,0 +1,113 @@ +declare const Buffer; +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { NgxSpinnerService } from 'ngx-spinner'; +import { Router } from '@angular/router'; +import { ToastrService } from 'ngx-toastr'; + +import { DialogsService } from 'src/app/core/services/dialogs/dialogs.service'; +import { KeyType } from 'src/app/core/enums/key-type'; +import { KeysService } from 'src/app/core/services/keys/keys.service'; +import { ModalSizeTypes } from 'src/app/core/enums/modal-size-types'; +import { PasswordDialogComponent } from '../../dialogs/password/password.dialog.component'; +import { VaultService } from 'src/app/core/services/vault/vault.service'; +import { SignatureType } from 'src/app/core/enums/signature-type'; + +@Component({ + selector: 'app-import-key', + templateUrl: './import-key.component.html', + styleUrls: ['./import-key.component.scss'] +}) +export class ImportKeyComponent implements OnInit { + public privateKeyForm: FormGroup; + private selectedType: string; + + constructor( + private dialogsService: DialogsService, + private fb: FormBuilder, + private router: Router, + private spinner: NgxSpinnerService, + private toastr: ToastrService, + private keysService: KeysService, + private vaultService: VaultService ) { } + + ngOnInit() { + this.selectedType = KeyType.BlockSigningKey; + this.createPrivateKeyForm(); + } + + createPrivateKeyForm() { + this.privateKeyForm = this.fb.group({ + type: [this.selectedType, Validators.required], + nickname: ['', Validators.required], + privateKey: ['', [Validators.required]] + }); + } + + importPrivateKey() { + if (this.privateKeyForm.invalid) { + this.toastr.error('Invalid form! All fields are required'); + return; + } else if (![KeyType.BlockSigningKey].includes(this.type.value)) { + this.toastr.error('Invalid key type'); + return; + } else if (this.type.value == KeyType.BlockSigningKey && !this.isValidPrivateBlockSigningKey(this.privateKey.value)) { + this.toastr.error('Invalid Block Signing private key'); + return; + } + + let privateKey = this.privateKey.value.startsWith('0x') + ? this.privateKey.value.slice(2) + : this.privateKey.value; + + let publicKey = this.keysService.getPublicKeyFromPrivate(SignatureType.EdDSA, Buffer.from(privateKey, 'hex')); + + const dialogMessage = 'Enter your vault password to import the key'; + this.dialogsService.open(PasswordDialogComponent, ModalSizeTypes.ExtraExtraLarge, dialogMessage) + .subscribe((vaultPassword: string) => { + if (vaultPassword) { + this.spinner.show(); + this.vaultService + .importKey( + this.type.value, + publicKey, + privateKey, + vaultPassword, + this.nickname.value) + .subscribe(result => { + this.spinner.hide(); + if (result.success) { + this.toastr.success(result.message); + this.router.navigate(['keys/manage']); + } else { + this.toastr.error(result.message); + } + }); + + this.createPrivateKeyForm(); + } + }); + } + + goBack() { + this.router.navigate(['keys/manage']); + } + + get type() { + return this.privateKeyForm.get('type'); + } + + get nickname() { + return this.privateKeyForm.get('nickname'); + } + + get privateKey () { + return this.privateKeyForm.get('privateKey'); + } + + private isValidPrivateBlockSigningKey(privateKey: string) { + // The private Block Signing key must be a valid Ed25519 private key (32 bytes hex string), + // optionally with a leading 0x + return /^(0x)?[0-9a-fA-F]{64}$/.test(privateKey); + } +} diff --git a/src/app/components/keys/index.ts b/src/app/components/keys/index.ts new file mode 100644 index 0000000..c4d7af8 --- /dev/null +++ b/src/app/components/keys/index.ts @@ -0,0 +1,7 @@ +import { ImportKeyComponent } from './import-key/import-key.component'; +import { ManageKeysComponent } from './manage-keys/manage-keys.component'; + +export const keysComponents = [ + ImportKeyComponent, + ManageKeysComponent +]; \ No newline at end of file diff --git a/src/app/components/keys/keys.module.ts b/src/app/components/keys/keys.module.ts new file mode 100644 index 0000000..b66d745 --- /dev/null +++ b/src/app/components/keys/keys.module.ts @@ -0,0 +1,30 @@ +import { AutofocusModule } from 'angular-autofocus-fix'; +import { CommonModule } from '@angular/common'; +import { ClickOutsideModule } from 'ng-click-outside'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MDBBootstrapModule } from 'angular-bootstrap-md'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgModule } from '@angular/core'; + +import { keysComponents } from '.'; +import { KeysRoutingModule } from './keys.routing'; + +@NgModule({ + declarations: [ + ...keysComponents + ], + imports: [ + AutofocusModule, + CommonModule, + ClickOutsideModule, + KeysRoutingModule, + FormsModule, + MDBBootstrapModule.forRoot(), + NgbModule, + ReactiveFormsModule + ], + exports: [ + ...keysComponents + ] +}) +export class KeysModule { } \ No newline at end of file diff --git a/src/app/components/keys/keys.routing.ts b/src/app/components/keys/keys.routing.ts new file mode 100644 index 0000000..b0720b0 --- /dev/null +++ b/src/app/components/keys/keys.routing.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { ImportKeyComponent } from './import-key/import-key.component'; +import { ManageKeysComponent } from './manage-keys/manage-keys.component'; + +const keysRoutes: Routes = [ + { path: 'import', component: ImportKeyComponent }, + { path: 'manage', component: ManageKeysComponent }, +]; + +@NgModule({ + imports: [RouterModule.forChild(keysRoutes)], + exports: [RouterModule] +}) +export class KeysRoutingModule { } \ No newline at end of file diff --git a/src/app/components/keys/manage-keys/manage-keys.component.html b/src/app/components/keys/manage-keys/manage-keys.component.html new file mode 100644 index 0000000..8d31316 --- /dev/null +++ b/src/app/components/keys/manage-keys/manage-keys.component.html @@ -0,0 +1,72 @@ +
+
+
+
+ + + +
+
+
+
diff --git a/src/app/components/keys/manage-keys/manage-keys.component.scss b/src/app/components/keys/manage-keys/manage-keys.component.scss new file mode 100644 index 0000000..d0742ac --- /dev/null +++ b/src/app/components/keys/manage-keys/manage-keys.component.scss @@ -0,0 +1,132 @@ +$mainColor: #2F5BE7; +.table-holder { + margin: 20px 0px; + table { + width: 100%; + white-space: nowrap; + thead { + th { + color: #253992; + text-transform: uppercase; + font-size: 12px; + border-top: 0px; + border-bottom-width: 1px; + vertical-align: middle; + } + } + tbody { + tr { + td { + vertical-align: middle; + } + td:first-child { + span { + white-space: nowrap; + display: inline-block; + font-weight: 700; + } + } + .counter-label { + display: inline-block; + width: 20px; + line-height: 20px; + text-align: center; + background: $mainColor; + color: #fff; + border-radius: 50%; + // transition: box-shadow 0.3s ease; + font-size: 12px; + // &:hover { + // text-decoration: none; + // box-shadow: 0 0 0 3px rgba(47, 91, 231, 0.5); + // } + } + td:last-child { + button:not(:last-child) { + margin-right: 3px; + } + } + } + .public-key { + font-family: monospace, monospace; + font-size: 14px; + } + } + } + .table-striped tbody tr:nth-of-type(odd) { + background-color: #fdfdfd; + } +} +.paginator { + a { + display: inline-block; + background: #E0E8F3; + padding: 7.5px 15px; + text-transform: uppercase; + color: $mainColor; + font-weight: 600; + border-radius: 4px; + transition: all 0.3s ease; + text-decoration: none!important; + &:not(.disabled):hover { + background: $mainColor; + color: #fff; + } + &.disabled { + color: #495463; + cursor: not-allowed; + pointer-events: none; + } + &:first-child { + margin-right: 10px; + } + } +} +a { + cursor: pointer; +} +.info-tables { + margin: 20px 0px; + .nav-link { + color: $mainColor; + &.active { + background: $mainColor; + color: #fff; + } + } +} +.copy-key { + position: relative; + margin-left: 15px; + cursor: pointer; + &::before { + content: "Copied!"; + padding: 5px 15px; + color: #fff; + background: #4caf50; + position: absolute; + top: -4px; + left: 50%; + transform: translateX(-50%) translateY(-100%); + border-radius: 4px; + display: none; + } + &::after { + width: 0; + height: 0; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-top: 7px solid #4caf50; + position: absolute; + top: -6px; + left: 50%; + transform: translateX(-50%); + content: ""; + display: none; + } + &.clicked { + &::before, &::after { + display: inline-block; + } + } +} diff --git a/src/app/components/keys/manage-keys/manage-keys.component.ts b/src/app/components/keys/manage-keys/manage-keys.component.ts new file mode 100644 index 0000000..97ba7ab --- /dev/null +++ b/src/app/components/keys/manage-keys/manage-keys.component.ts @@ -0,0 +1,154 @@ +declare const Buffer; +import { Component, OnInit } from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgxSpinnerService } from 'ngx-spinner'; +import { ToastrService } from 'ngx-toastr'; + +import { BaseComponent } from '../../base.component'; +import { ChromeMessageType } from 'src/app/core/enums/chrome-message-type'; +import { DialogsService } from 'src/app/core/services/dialogs/dialogs.service'; +import { KeyType } from 'src/app/core/enums/key-type'; +import { ModalSizeTypes } from 'src/app/core/enums/modal-size-types'; +import { PasswordDialogComponent } from '../../dialogs/password/password.dialog.component'; +import { PrivateKeyAddressModalComponent } from '../../modals/private-key-address-modal/private-key-address-modal.component'; +import { VaultService } from 'src/app/core/services/vault/vault.service'; + +@Component({ + selector: 'app-manage-keys', + templateUrl: './manage-keys.component.html', + styleUrls: ['./manage-keys.component.scss'] +}) +export class ManageKeysComponent extends BaseComponent implements OnInit { + public blockSigningKeysInfo = {}; + public blockSigningKeys: string[] = []; + public displayedBlockSigningKeys: string[] = []; + public selectedKeyType: KeyType = KeyType.BlockSigningKey; + public KeyType = KeyType; + public editKeyNickname: boolean[] = []; + public pageSize: number = 6; + public currentPage: number = 1; + public currentStartIndex = 0; + + constructor( + private dialogsService: DialogsService, + private modalService: NgbModal, + private spinner: NgxSpinnerService, + private toastr: ToastrService, + private vaultService: VaultService ) { + super(); + } + + ngOnInit() { + chrome.tabs && chrome.tabs.getCurrent(function(tab) { + if (tab === undefined) { + chrome.runtime.sendMessage({type: ChromeMessageType.ManageKeysRequest}, (response) => { + if (response.success) { + const popup_url = chrome.runtime.getURL('index.html'); + chrome.tabs.create({'url': popup_url}); + } + }); + } else { + chrome.runtime.sendMessage({type: ChromeMessageType.CheckRequests}, (response) => { + if (response.manageKeysRequested) { + chrome.runtime.sendMessage({type: ChromeMessageType.NewTabOpen}); + } + }); + } + }); + + this.getKeysInfo(); + } + + changeSelectedKeyType(keyType: KeyType) { + this.selectedKeyType = keyType; + this.currentPage = 1; + this.currentStartIndex = 0; + this.displayedBlockSigningKeys = this.blockSigningKeys.slice(this.currentStartIndex, this.currentStartIndex + this.pageSize); + } + + editNickname(keyType: KeyType, publicKey: string, nickname: string) { + if (nickname.length > 0) { + this.vaultService.updateKeyNickname(keyType, publicKey, nickname); + this.blockSigningKeysInfo[publicKey] = nickname; + } + + this.editKeyNickname[publicKey] = false; + } + + viewPrivateKey(publicKey: string) { + const dialogMessage = 'Enter your vault password to view the secret key'; + this.dialogsService.open(PasswordDialogComponent, ModalSizeTypes.Medium, dialogMessage) + .subscribe((vaultPassword: string) => { + if (vaultPassword) { + this.spinner.show(); + this.vaultService + .getPrivateKeyOrAddress(publicKey, vaultPassword) + .subscribe(result => { + this.spinner.hide(); + if (result.success && result.message) { + const confirmRef = this.modalService.open(PrivateKeyAddressModalComponent); + confirmRef.componentInstance.publicKeyOrAddress = publicKey; + confirmRef.componentInstance.privateKeyOrAddress = result.message; + confirmRef.componentInstance.isKey = true; + confirmRef.result + .then((result) => {}) + .catch((error) => {}); + } else if (!result.success) { + this.toastr.error(result.message); + } + }); + } + }); + } + + removeKey(keyType: KeyType, publicKey: string) { + const dialogMessage = 'Warning! You are about to delete your Block Signing key. If you want to continue, enter your vault password'; + this.dialogsService.open(PasswordDialogComponent, ModalSizeTypes.ExtraExtraLarge, dialogMessage) + .subscribe((vaultPassword: string) => { + if (vaultPassword) { + this.spinner.show(); + this.vaultService + .removeKey(keyType, publicKey, vaultPassword) + .subscribe(result => { + this.spinner.hide(); + if (result.success) { + this.toastr.success(result.message); + this.getKeysInfo(); + } else { + this.toastr.error(result.message); + } + }); + } + }); + } + + copyKey(key: string, element) { + const selBox = document.createElement('textarea'); + selBox.style.position = 'fixed'; + selBox.style.left = '0'; + selBox.style.top = '0'; + selBox.style.opacity = '0'; + selBox.value = key; + document.body.appendChild(selBox); + selBox.focus(); + selBox.select(); + document.execCommand('copy'); + document.body.removeChild(selBox); + + element.classList.add('clicked'); + setTimeout(() => {element.classList.remove('clicked')},2000); + } + + changePage(page) { + this.currentPage = page; + this.currentStartIndex = (this.currentPage - 1) * this.pageSize; + + this.displayedBlockSigningKeys = this.blockSigningKeys.slice(this.currentStartIndex, this.currentStartIndex + this.pageSize); + } + + private getKeysInfo() { + this.blockSigningKeysInfo = this.vaultService.getBlockSigningKeysPublicInfo(); + this.blockSigningKeys = Object.keys(this.blockSigningKeysInfo); + this.displayedBlockSigningKeys= this.blockSigningKeys.slice(this.currentStartIndex, this.currentStartIndex + this.pageSize); + } +} diff --git a/src/app/components/modals/confirm-modal/confirm-modal.component.html b/src/app/components/modals/confirm-modal/confirm-modal.component.html new file mode 100644 index 0000000..6a16d00 --- /dev/null +++ b/src/app/components/modals/confirm-modal/confirm-modal.component.html @@ -0,0 +1,12 @@ + diff --git a/src/app/components/modals/confirm-modal/confirm-modal.component.scss b/src/app/components/modals/confirm-modal/confirm-modal.component.scss new file mode 100644 index 0000000..cadbd93 --- /dev/null +++ b/src/app/components/modals/confirm-modal/confirm-modal.component.scss @@ -0,0 +1,30 @@ +.modal-header { + min-height: 16.43px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; + background-color: red; + -webkit-border-top-left-radius: 5px; + -webkit-border-top-right-radius: 5px; + -moz-border-radius-topleft: 5px; + -moz-border-radius-topright: 5px; + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} +.modal-header h4 { + margin: 0; + font-size: 17px; +} +.modal-title{ + color: white; +} +.modal-content { + -webkit-box-shadow: 0 5px 20px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 5px 20px 0 rgba(0, 0, 0, 0.1); + border: none; + border-radius: 5px; +} +button:focus { + outline: none !important; + border: none !important; + box-shadow: none !important; +} \ No newline at end of file diff --git a/src/app/components/modals/confirm-modal/confirm-modal.component.ts b/src/app/components/modals/confirm-modal/confirm-modal.component.ts new file mode 100644 index 0000000..c64b222 --- /dev/null +++ b/src/app/components/modals/confirm-modal/confirm-modal.component.ts @@ -0,0 +1,13 @@ +import { Component } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +@Component({ + selector: 'app-confirm-modal', + templateUrl: './confirm-modal.component.html', + styleUrls: ['./confirm-modal.component.scss'] +}) +export class ConfirmModalComponent { + constructor( + public activeModal: NgbActiveModal, + ) { } +} \ No newline at end of file diff --git a/src/app/components/modals/create-advanced-info-modal/create-advanced-info-modal.component.html b/src/app/components/modals/create-advanced-info-modal/create-advanced-info-modal.component.html new file mode 100644 index 0000000..70e6e65 --- /dev/null +++ b/src/app/components/modals/create-advanced-info-modal/create-advanced-info-modal.component.html @@ -0,0 +1,24 @@ + diff --git a/src/app/components/modals/create-advanced-info-modal/create-advanced-info-modal.component.scss b/src/app/components/modals/create-advanced-info-modal/create-advanced-info-modal.component.scss new file mode 100644 index 0000000..669717a --- /dev/null +++ b/src/app/components/modals/create-advanced-info-modal/create-advanced-info-modal.component.scss @@ -0,0 +1,42 @@ +.modal-header { + min-height: 16.43px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; + background-color: #F8F8F8; + -webkit-border-top-left-radius: 5px; + -webkit-border-top-right-radius: 5px; + -moz-border-radius-topleft: 5px; + -moz-border-radius-topright: 5px; + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} +.modal-header h4 { + margin: 0; + font-size: 17px; +} + +.modal-content { + -webkit-box-shadow: 0 5px 20px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 5px 20px 0 rgba(0, 0, 0, 0.1); + border: none; + border-radius: 5px; +} +.close { + width: 30px; + height: 30px; + background: #fff; + border-radius: 5px; + opacity: 1; + position: absolute; + top: 0px; + line-height: 29px; + right: 3px; + color: #2f5be7!important; + opacity: 1!important; + padding: 0px; +} +.close:focus { + outline: none !important; + border: none !important; + box-shadow: none !important; +} \ No newline at end of file diff --git a/src/app/components/modals/create-advanced-info-modal/create-advanced-info-modal.component.ts b/src/app/components/modals/create-advanced-info-modal/create-advanced-info-modal.component.ts new file mode 100644 index 0000000..663cb0b --- /dev/null +++ b/src/app/components/modals/create-advanced-info-modal/create-advanced-info-modal.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +@Component({ + selector: 'app-create-advanced-info-modal', + templateUrl: './create-advanced-info-modal.component.html', + styleUrls: ['./create-advanced-info-modal.component.scss'] +}) +export class CreateAdvancedInfoModalComponent { + constructor(public activeModal: NgbActiveModal) { } +} diff --git a/src/app/components/modals/create-basic-info-modal/create-basic-info-modal.component.html b/src/app/components/modals/create-basic-info-modal/create-basic-info-modal.component.html new file mode 100644 index 0000000..23c4daa --- /dev/null +++ b/src/app/components/modals/create-basic-info-modal/create-basic-info-modal.component.html @@ -0,0 +1,21 @@ + diff --git a/src/app/components/modals/create-basic-info-modal/create-basic-info-modal.component.scss b/src/app/components/modals/create-basic-info-modal/create-basic-info-modal.component.scss new file mode 100644 index 0000000..11e08c5 --- /dev/null +++ b/src/app/components/modals/create-basic-info-modal/create-basic-info-modal.component.scss @@ -0,0 +1,42 @@ +.modal-header { + min-height: 16.43px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; + background-color: #F8F8F8; + -webkit-border-top-left-radius: 5px; + -webkit-border-top-right-radius: 5px; + -moz-border-radius-topleft: 5px; + -moz-border-radius-topright: 5px; + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} +.modal-header h4 { + margin: 0; + font-size: 17px; +} + +.modal-content { + -webkit-box-shadow: 0 5px 20px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 5px 20px 0 rgba(0, 0, 0, 0.1); + border: none; + border-radius: 5px; +} +.close { + width: 30px; + height: 30px; + background: #fff; + border-radius: 5px; + opacity: 1; + position: absolute; + top: 0px; + line-height: 29px; + right: 3px; + color: #2f5be7!important; + opacity: 1!important; + padding: 0px; +} +.close:focus { + outline: none !important; + border: none !important; + box-shadow: none !important; +} diff --git a/src/app/components/modals/create-basic-info-modal/create-basic-info-modal.component.ts b/src/app/components/modals/create-basic-info-modal/create-basic-info-modal.component.ts new file mode 100644 index 0000000..54167ca --- /dev/null +++ b/src/app/components/modals/create-basic-info-modal/create-basic-info-modal.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +@Component({ + selector: 'app-create-basic-info-modal', + templateUrl: './create-basic-info-modal.component.html', + styleUrls: ['./create-basic-info-modal.component.scss'] +}) +export class CreateBasicInfoModalComponent { + constructor(public activeModal: NgbActiveModal) { } +} diff --git a/src/app/components/modals/index.ts b/src/app/components/modals/index.ts new file mode 100644 index 0000000..37fc3f3 --- /dev/null +++ b/src/app/components/modals/index.ts @@ -0,0 +1,13 @@ +import { ConfirmModalComponent } from './confirm-modal/confirm-modal.component'; +import { CreateAdvancedInfoModalComponent } from './create-advanced-info-modal/create-advanced-info-modal.component'; +import { CreateBasicInfoModalComponent } from './create-basic-info-modal/create-basic-info-modal.component'; +import { PrivateKeyAddressModalComponent } from './private-key-address-modal/private-key-address-modal.component'; +import { RemoveConfirmModalComponent } from './remove-confirm-modal/remove-confirm-modal.component'; + +export const modalComponents = [ + ConfirmModalComponent, + CreateAdvancedInfoModalComponent, + CreateBasicInfoModalComponent, + PrivateKeyAddressModalComponent, + RemoveConfirmModalComponent +]; diff --git a/src/app/components/modals/modals.module.ts b/src/app/components/modals/modals.module.ts new file mode 100644 index 0000000..c0f95c9 --- /dev/null +++ b/src/app/components/modals/modals.module.ts @@ -0,0 +1,24 @@ +import { CommonModule } from '@angular/common'; +import { MDBBootstrapModule } from 'angular-bootstrap-md'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgModule } from '@angular/core'; + +import { modalComponents } from '.'; + +@NgModule({ + declarations: [ + ...modalComponents + ], + imports: [ + CommonModule, + MDBBootstrapModule.forRoot(), + NgbModule + ], + exports: [ + ...modalComponents + ], + entryComponents: [ + ...modalComponents + ] +}) +export class ModalsModule { } diff --git a/src/app/components/modals/private-key-address-modal/private-key-address-modal.component.html b/src/app/components/modals/private-key-address-modal/private-key-address-modal.component.html new file mode 100644 index 0000000..b1fa844 --- /dev/null +++ b/src/app/components/modals/private-key-address-modal/private-key-address-modal.component.html @@ -0,0 +1,30 @@ + diff --git a/src/app/components/modals/private-key-address-modal/private-key-address-modal.component.scss b/src/app/components/modals/private-key-address-modal/private-key-address-modal.component.scss new file mode 100644 index 0000000..3f44a58 --- /dev/null +++ b/src/app/components/modals/private-key-address-modal/private-key-address-modal.component.scss @@ -0,0 +1,69 @@ +.modal-header { + min-height: 16.43px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; + background-color: #F8F8F8; + -webkit-border-top-left-radius: 5px; + -webkit-border-top-right-radius: 5px; + -moz-border-radius-topleft: 5px; + -moz-border-radius-topright: 5px; + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} +.modal-header h4 { + margin: 0; + font-size: 17px; +} +.modal-content { + -webkit-box-shadow: 0 5px 20px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 5px 20px 0 rgba(0, 0, 0, 0.1); + border: none; + border-radius: 5px; +} +.address { + font-family: monospace, monospace; + font-size: 15px; + display: block; +} +button:focus { + outline: none !important; + border: none !important; + box-shadow: none !important; +} +.copy-address { + position: relative; + position: relative; + bottom: 2px; + margin-left: 10px; + cursor: pointer; + &::before { + content: "Copied!"; + padding: 5px 15px; + color: #fff; + background: #4caf50; + position: absolute; + top: -4px; + left: 50%; + transform: translateX(-50%) translateY(-100%); + border-radius: 4px; + display: none; + } + &::after { + width: 0; + height: 0; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-top: 7px solid #4caf50; + position: absolute; + top: -6px; + left: 50%; + transform: translateX(-50%); + content: ""; + display: none; + } + &.clicked { + &::before, &::after { + display: inline-block; + } + } +} diff --git a/src/app/components/modals/private-key-address-modal/private-key-address-modal.component.ts b/src/app/components/modals/private-key-address-modal/private-key-address-modal.component.ts new file mode 100644 index 0000000..4c936ef --- /dev/null +++ b/src/app/components/modals/private-key-address-modal/private-key-address-modal.component.ts @@ -0,0 +1,54 @@ +import { Component, Input } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { + convertECDSAPublicKeyToEthereumAddress, + convertECDSAPublicKeyToEtherLinkAddress, +} from 'src/app/core/utils/helpers'; + +@Component({ + selector: 'app-private-key-address-modal', + templateUrl: './private-key-address-modal.component.html', + styleUrls: ['./private-key-address-modal.component.scss'] +}) +export class PrivateKeyAddressModalComponent { + @Input() public publicKeyOrAddress: string; + @Input() public privateKeyOrAddress: string; + @Input() public isKey: boolean = false; + @Input() public isEtherLinkAddress: boolean = false; + public convertToEthereumAddress = convertECDSAPublicKeyToEthereumAddress; + public convertToEtherLinkAddress = convertECDSAPublicKeyToEtherLinkAddress; + public publicElCopyTitle: string; + public privateElCopyTitle: string; + + constructor( + public activeModal: NgbActiveModal, + ) { + this.publicElCopyTitle = 'Click to copy public ' + this.isKey + ? 'key' + : 'address'; + this.privateElCopyTitle = 'Click to copy secret ' + this.isKey + ? 'key' + : 'address'; + } + + copy(keyOrAddress: string, element) { + const selBox = document.createElement('textarea'); + selBox.style.position = 'fixed'; + selBox.style.left = '0'; + selBox.style.top = '0'; + selBox.style.opacity = '0'; + selBox.value = keyOrAddress; + document.body.appendChild(selBox); + selBox.focus(); + selBox.select(); + document.execCommand('copy'); + document.body.removeChild(selBox); + + element.classList.add('clicked'); + setTimeout(() => {element.classList.remove('clicked')},2000); + } + + minifyHex(key: string) { + return '0x' + key.slice(0, 30) + '...' + key.slice(-16); + } +} diff --git a/src/app/components/modals/remove-confirm-modal/remove-confirm-modal.component.html b/src/app/components/modals/remove-confirm-modal/remove-confirm-modal.component.html new file mode 100644 index 0000000..ac67b1a --- /dev/null +++ b/src/app/components/modals/remove-confirm-modal/remove-confirm-modal.component.html @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/src/app/components/modals/remove-confirm-modal/remove-confirm-modal.component.scss b/src/app/components/modals/remove-confirm-modal/remove-confirm-modal.component.scss new file mode 100644 index 0000000..cadbd93 --- /dev/null +++ b/src/app/components/modals/remove-confirm-modal/remove-confirm-modal.component.scss @@ -0,0 +1,30 @@ +.modal-header { + min-height: 16.43px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; + background-color: red; + -webkit-border-top-left-radius: 5px; + -webkit-border-top-right-radius: 5px; + -moz-border-radius-topleft: 5px; + -moz-border-radius-topright: 5px; + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} +.modal-header h4 { + margin: 0; + font-size: 17px; +} +.modal-title{ + color: white; +} +.modal-content { + -webkit-box-shadow: 0 5px 20px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 5px 20px 0 rgba(0, 0, 0, 0.1); + border: none; + border-radius: 5px; +} +button:focus { + outline: none !important; + border: none !important; + box-shadow: none !important; +} \ No newline at end of file diff --git a/src/app/components/modals/remove-confirm-modal/remove-confirm-modal.component.ts b/src/app/components/modals/remove-confirm-modal/remove-confirm-modal.component.ts new file mode 100644 index 0000000..9ae673b --- /dev/null +++ b/src/app/components/modals/remove-confirm-modal/remove-confirm-modal.component.ts @@ -0,0 +1,18 @@ +import { Component, Input } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +import { capitalize } from 'src/app/core/utils/helpers'; + +@Component({ + selector: 'app-remove-confirm-modal', + templateUrl: './remove-confirm-modal.component.html', + styleUrls: ['./remove-confirm-modal.component.scss'] +}) +export class RemoveConfirmModalComponent { + @Input() public objectType: string; + public capitalize = capitalize; + + constructor( + public activeModal: NgbActiveModal, + ) { } +} diff --git a/src/app/components/navbar/navbar.component.html b/src/app/components/navbar/navbar.component.html new file mode 100644 index 0000000..cf8628e --- /dev/null +++ b/src/app/components/navbar/navbar.component.html @@ -0,0 +1,45 @@ +
+
+
+
+
Kambanibeta
+
+ +
+
+
+
+ +
diff --git a/src/app/components/navbar/navbar.component.scss b/src/app/components/navbar/navbar.component.scss new file mode 100644 index 0000000..1c3ae1e --- /dev/null +++ b/src/app/components/navbar/navbar.component.scss @@ -0,0 +1,144 @@ +$mainColor: #253992; +#main-header { + .mobile-menu-toggle { + display: none; + cursor: pointer; + } + .logo-section { + background: $mainColor; + padding: 15px 0px; + color: #fff; + .brand-name { + font-size: 27px; + font-weight: 800; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } + } + .navbar { + background: #fff; + box-shadow: 0px 4px 35px 0px rgba(0,0,0,0.1); + padding: 0px 0px; + ul{ + overflow: hidden; + width: 100%; + margin-bottom: 0px; + position: relative; + li { + float: left; + &:not(:last-child){ + margin-right: 15px; + @media only screen and (min-width: 768px){ + margin-right: 25px; + } + } + a { + color: $mainColor; + display: inline-block; + padding: 10px 0px; + cursor: pointer; + position: relative; + &::before { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 1px; + background: $mainColor; + opacity: 0; + display: block; + content: ""; + transition: opacity 0.3s ease; + } + &:hover { + &::before { + opacity: 1; + } + } + } + a.active { + border-bottom: 1px solid $mainColor; + } + } + } + } +} +@media only screen and (max-width: 767px){ + #main-header { + .mobile-menu-toggle { + position: relative; + display: inline-block; + width: 40px; + height: 24px; + float : right; + margin-top: 7px; + margin-right: 15px; + @media only screen and (min-width: 768px){ + display: none; + } + span, span:after, span:before { + position: absolute; + width: 40px; + height: 4px; + transition-timing-function: ease; + transition-duration: .15s; + transition-property: transform; + border-radius: 4px; + background-color: #fff; + display: block; + content: ""; + } + span:before { + top: 10px; + transition-timing-function: ease; + transition-duration: .15s; + transition-property: transform,opacity; + } + span:after { + top: 20px; + } + &.active { + span { + transform: translate3d(0,10px,0) rotate(45deg); + &::before { + transform: rotate(-45deg) translate3d(-5.71429px,-6px,0); + opacity: 0; + } + &::after { + transform: translate3d(0,-20px,0) rotate(-90deg); + } + } + } + } + .navbar { + height: 0px; + overflow: hidden; + transition: height 0.3s ease; + position: fixed; + top: 70px; + left: -100%; + width: 100%; + height: calc(100% - 70px); + z-index: 999; + text-align: center; + display: block; + padding-top: 100px; + transition: left 0.3s ease; + &.active { + left: 0px; + } + ul { + li { + float: none; + } + } + &.active { + height: 100%; + } + } + } +} diff --git a/src/app/components/navbar/navbar.component.ts b/src/app/components/navbar/navbar.component.ts new file mode 100644 index 0000000..c6de76d --- /dev/null +++ b/src/app/components/navbar/navbar.component.ts @@ -0,0 +1,57 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; + +import { RemoveVaultDialogComponent } from '../dialogs/remove-vault/remove-vault.dialog.component'; +import { DialogsService } from 'src/app/core/services/dialogs/dialogs.service'; +import { ModalSizeTypes } from 'src/app/core/enums/modal-size-types'; +import { SigningService } from 'src/app/core/services/signing/signing.service'; +import { VaultService } from 'src/app/core/services/vault/vault.service'; + +@Component({ + selector: 'app-navbar', + templateUrl: './navbar.component.html', + styleUrls: ['./navbar.component.scss'] +}) +export class NavbarComponent implements OnInit { + public pendingRequestsCount = 0; + public openMobileNav: boolean; + + constructor( + private dialogsService: DialogsService, + private router: Router, + private signingService: SigningService, + private vaultService: VaultService) { } + + ngOnInit() { + + chrome.browserAction && chrome.browserAction.getBadgeText({}, (result) => { + this.pendingRequestsCount = parseInt(result, 10); + }); + + this.signingService.change.subscribe(pendingRequestsCount => { + this.pendingRequestsCount = pendingRequestsCount; + }); + } + + toggleNavigation() { + this.openMobileNav = !this.openMobileNav; + } + + removeVault() { + this.dialogsService.open(RemoveVaultDialogComponent, ModalSizeTypes.ExtraExtraLarge, undefined) + .subscribe((response: string) => { + if (response === 'confirm') { + this.vaultService.removeVault(); + this.router.navigate(['/vault/create']); + } + }); + } + + checkIsActive(route: string) { + if (this.router.url.startsWith(route)) { + return true; + } + + return false; + } +} diff --git a/src/app/components/settings/settings.component.html b/src/app/components/settings/settings.component.html new file mode 100644 index 0000000..881c666 --- /dev/null +++ b/src/app/components/settings/settings.component.html @@ -0,0 +1,175 @@ +
+
+
+
+

+ Settings +

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

You have not whitelisted any domains yet

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

You have not whitelisted any domains yet

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

You have not whitelisted any domains yet

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

You have not whitelisted any domains yet

+
+
+
+
+
+
+
+
+
+
+ + +
+
Old Password is required!
+
+
+
+ + +
+
New Password is required!
+
+ Your new password must be at least 18 characters long. Hint: Use 4-5 easy to remember words in sequence (e.g. TorReallyLovesMeatballs) +
+
+
+
+ + +
+
Confirm password is required!
+
Passwords do not match!
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/src/app/components/settings/settings.component.scss b/src/app/components/settings/settings.component.scss new file mode 100644 index 0000000..c31c225 --- /dev/null +++ b/src/app/components/settings/settings.component.scss @@ -0,0 +1,29 @@ +i { + cursor: pointer; +} + +.card-header { + text-align: center; + cursor: pointer; +} + +ul.nav li a { + color: black !important; +} + +ul.nav li a.active { + color: #2F5BE7 !important; +} + +form { + width: 100%; +} + +.fa-times { + color: red; +} + +.list-group-item { + text-align: left; + font-size: small; +} \ No newline at end of file diff --git a/src/app/components/settings/settings.component.ts b/src/app/components/settings/settings.component.ts new file mode 100644 index 0000000..67b0219 --- /dev/null +++ b/src/app/components/settings/settings.component.ts @@ -0,0 +1,128 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, Validators } from '@angular/forms'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgxSpinnerService } from 'ngx-spinner'; +import { Router } from '@angular/router'; +import { ToastrService } from 'ngx-toastr'; + +import { ChromeMessageType } from 'src/app/core/enums/chrome-message-type'; +import CustomValidators from 'src/app/core/utils/customValidators'; +import { FactomAddressType } from 'src/app/core/enums/factom-address-type'; +import { KeyType } from 'src/app/core/enums/key-type'; +import { ResultModel } from 'src/app/core/models/result.model'; +import { VaultService } from 'src/app/core/services/vault/vault.service'; +import { RemoveConfirmModalComponent } from '../modals/remove-confirm-modal/remove-confirm-modal.component'; + +@Component({ + selector: 'app-settings', + templateUrl: './settings.component.html', + styleUrls: ['./settings.component.scss'] +}) +export class SettingsComponent implements OnInit { + public factomAddressType = FactomAddressType; + public keyType = KeyType; + public selectedTab: string = 'domains'; + public changePasswordForm; + public fctAddressesRequestWhitelistedDomains: []; + public ecAddressesRequestWhitelistedDomains: []; + public etherLinkAddressesRequestWhitelistedDomains: []; + public blockSigningKeysRequestWhitelistedDomains: []; + public fctRequestExpanded: boolean = true; + public ecRequestExpanded: boolean = true; + public etherLinkRequestExpanded: boolean = true; + public blockSigningKeyRequestExpanded: boolean = true; + + constructor( + private fb: FormBuilder, + private modalService: NgbModal, + private router: Router, + private spinner: NgxSpinnerService, + private toastr: ToastrService, + private vaultService: VaultService) { } + + ngOnInit() { + chrome.tabs && chrome.tabs.getCurrent(function(tab) { + if (tab === undefined) { + chrome.runtime.sendMessage({type: ChromeMessageType.SettingsRequest}, (response) => { + if (response.success) { + const popup_url = chrome.runtime.getURL('index.html'); + chrome.tabs.create({'url': popup_url}); + } + }); + } else { + chrome.runtime.sendMessage({type: ChromeMessageType.CheckRequests}, (response) => { + if (response.settingsRequested) { + chrome.runtime.sendMessage({type: ChromeMessageType.NewTabOpen}); + } + }); + } + }); + + this.getWhitelistedDomains(); + this.buildChangePasswordForm(); + } + + selectTab(tab: string) { + this.selectedTab = tab; + } + + checkIsActive(tab: string) { + return this.selectedTab === tab; + } + + removeDomain(requestType: string, domain: string) { + const confirmRef = this.modalService.open(RemoveConfirmModalComponent); + confirmRef.componentInstance.objectType = 'domain'; + confirmRef.result.then((result) => { + this.vaultService.removeWhitelistedDomain(requestType, domain); + this.getWhitelistedDomains(); + }).catch((error) => { + }); + } + + changePassword() { + if (this.changePasswordForm.invalid) { + return; + } + + this.spinner.show(); + this.vaultService + .changeVaultPassword(this.oldPassword.value, this.newPassword.value) + .subscribe((result: ResultModel) => { + this.spinner.hide(); + if (result.success) { + this.toastr.success(result.message); + this.router.navigate(['home']); + } else { + this.toastr.error(result.message); + } + }) + } + + private buildChangePasswordForm() { + this.changePasswordForm = this.fb.group({ + oldPassword: ['', [Validators.required]], + password: ['', [Validators.required, Validators.minLength(18)]], + confirmPassword: ['', [Validators.required]] + }, { validator: CustomValidators.passwordsDoMatch.bind(this)}); + } + + private getWhitelistedDomains() { + this.fctAddressesRequestWhitelistedDomains = this.vaultService.getFCTAddressesRequestWhitelistedDomains(); + this.ecAddressesRequestWhitelistedDomains = this.vaultService.getECAddressesRequestWhitelistedDomains(); + this.etherLinkAddressesRequestWhitelistedDomains = this.vaultService.getEtherLinkAddressesRequestWhitelistedDomains(); + this.blockSigningKeysRequestWhitelistedDomains = this.vaultService.getBlockSigningKeysRequestWhitelistedDomains(); + } + + get oldPassword () { + return this.changePasswordForm.get('oldPassword'); + } + + get newPassword () { + return this.changePasswordForm.get('password'); + } + + get confirmPassword() { + return this.changePasswordForm.get('confirmPassword'); + } +} \ No newline at end of file diff --git a/src/app/components/signer/signer.component.html b/src/app/components/signer/signer.component.html new file mode 100644 index 0000000..56bc7d5 --- /dev/null +++ b/src/app/components/signer/signer.component.html @@ -0,0 +1,111 @@ +
+
+
+
+

+ Signing Requests + {{pendingRequestsCount}} +

+
+
+

Received from:

+

{{from}}

+
+ +
+
+
+
+ +
+
FCT Address
+
{{minifyAddress(selectedFactomAddress)}}
+
Amount
+
{{toHumanReadable(inputAmount)}} FCT
+
+
+
+ +
+
Address
+
{{minifyAddress(selectedFactomAddress)}}
+
Input Asset
+
{{inputAsset}}
+
Input Amount
+
{{toHumanReadable(inputAmount)}}
+
Output Asset
+
{{outputAsset}}
+
+
+
+ +
+
From
+
{{minifyAddress(selectedFactomAddress)}}
+
To
+
{{minifyAddress(outputAddress)}}
+
Asset
+
{{inputAsset}}
+
Amount
+
{{toHumanReadable(inputAmount)}}
+
+
+
+
+
+ + +
+
+ + +
+
+

You need to create an Identity in order to sign the data.

+
+
+

You need to create an Identity with a Signing Key in order to sign the data.

+
+
+
+
+ + +
+
+

You need to import or generate an {{requestKeyType.toUpperCase()}} address in order to sign the data.

+
+
+
+
+ + +
+
+

You need to import a Block Signing key in order to sign the data.

+
+
+
+ + + +
+
+
+

You do not have any pending requests

+
+
+
+
+
+
diff --git a/src/app/components/signer/signer.component.scss b/src/app/components/signer/signer.component.scss new file mode 100644 index 0000000..8996199 --- /dev/null +++ b/src/app/components/signer/signer.component.scss @@ -0,0 +1,14 @@ +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #ccc; + border-radius: 4px; + max-height: 16em; +} \ No newline at end of file diff --git a/src/app/components/signer/signer.component.ts b/src/app/components/signer/signer.component.ts new file mode 100644 index 0000000..11a5d90 --- /dev/null +++ b/src/app/components/signer/signer.component.ts @@ -0,0 +1,461 @@ +import { Component, OnInit, NgZone } from '@angular/core'; +import { NgxSpinnerService } from 'ngx-spinner'; +import { ToastrService } from 'ngx-toastr'; +import { Transaction } from 'factom'; +import { sha512 } from 'factom/src/util.js'; + +import { ChromeMessageType } from 'src/app/core/enums/chrome-message-type'; +import { DialogsService } from 'src/app/core/services/dialogs/dialogs.service'; +import { DidKeyEntryModel } from 'src/app/core/interfaces/did-key-entry'; +import { ManagementKeyEntryModel } from 'src/app/core/interfaces/management-key-entry'; +import { minifyAddress } from 'src/app/core/utils/helpers'; +import { ModalSizeTypes } from 'src/app/core/enums/modal-size-types'; +import { PasswordDialogComponent } from 'src/app/components/dialogs/password/password.dialog.component'; +import { RequestKeyType } from 'src/app/core/enums/request-key-type'; +import { RequestType } from 'src/app/core/enums/request-type'; +import { ResultModel } from 'src/app/core/models/result.model'; +import { SignatureDataModel } from 'src/app/core/models/signature-data.model'; +import { SigningService } from 'src/app/core/services/signing/signing.service'; +import { TransactionType } from 'src/app/core/enums/transaction-type'; +import { VaultService } from 'src/app/core/services/vault/vault.service'; + +@Component({ + selector: 'app-signer', + templateUrl: './signer.component.html', + styleUrls: ['./signer.component.scss'] +}) +export class SignerComponent implements OnInit { + public RequestKeyType = RequestKeyType; + public RequestType = RequestType; + public TransactionType = TransactionType; + public pendingRequestsCount: number; + public request: any; + public from: string; + public dataToSign: string; + public requestKeyType: string; + public requestType: string; + public txType: string; + public inputAmount: number; + public inputAsset: string; + public outputAsset: string; + public outputAddress: string; + public txMetadata: any; + public allDIDsPublicInfo = {}; + public fctAddressesPublicInfo = {}; + public ecAddressesPublicInfo = {}; + public blockSigningKeysPublicInfo = {}; + public allDIDIds: string[] = []; + public didIdsWithDIDKeys: string[] = []; + public availableDIDIds: string[] = []; + public fctAddresses: string[] = []; + public ecAddresses: string[] = []; + public blockSigningKeys: string[] = []; + public availableFactomAddresses: string[] = []; + public availableFactomAddressesPublicInfo = {}; + public availableDIDKeys: any[]; + public selectedDIDId: string; + public didIdSpecified: boolean; + public selectedDIDKeyId: string; + public didKeySpecified: boolean; + public selectedFactomAddress: string; + public factomAddressSpecified: boolean; + public selectedKey: string; + public keySpecified: boolean; + public minifyAddress = minifyAddress; + + constructor( + private dialogsService: DialogsService, + private signingService: SigningService, + private vaultService: VaultService, + private toastr: ToastrService, + private spinner: NgxSpinnerService, + private zone: NgZone) { } + + ngOnInit() { + this.getAllAvailableSigningKeys(); + this.getPendingRequestsCount(); + this.getSigningRequest(); + } + + onSelectDIDChange(selectedDIDId: string) { + this.selectedDIDId = selectedDIDId; + this.availableDIDKeys = this.getDIDKeys(this.selectedDIDId); + this.selectedDIDKeyId = this.availableDIDKeys[0].id; + } + + onSelectDIDKeyChange(selectedDIDKeyId: string) { + this.selectedDIDKeyId = selectedDIDKeyId; + } + + onSelectAddressChange(selectedFactomAddress: string) { + this.selectedFactomAddress = selectedFactomAddress; + } + + onSelectKeyChange(selectedKey: string) { + this.selectedKey = selectedKey; + } + + signData() { + let dialogMessage = 'Enter your vault password to sign the '; + dialogMessage += this.requestType == RequestType.Data + ? 'data' + : 'transaction'; + + this.dialogsService.open(PasswordDialogComponent, ModalSizeTypes.ExtraExtraLarge, dialogMessage) + .subscribe((vaultPassword: string) => { + if (vaultPassword) { + this.spinner.show(); + + if (this.requestType == RequestType.Data) { + const data = this.request.requestInfo.data; + + const dataToSign = typeof data == "string" + ? data + : JSON.stringify(data); + + let signingKeyOrAddress; + if ([RequestKeyType.ManagementKey, RequestKeyType.DIDKey].includes(this.requestKeyType as RequestKeyType)) { + signingKeyOrAddress = this.availableDIDKeys.find(dk => dk.id == this.selectedDIDKeyId); + } else if ([RequestKeyType.FCT, RequestKeyType.EC].includes(this.requestKeyType as RequestKeyType)) { + signingKeyOrAddress = this.selectedFactomAddress; + } else { + signingKeyOrAddress = this.selectedKey; + } + + this.signDataRequest(dataToSign, signingKeyOrAddress, vaultPassword); + } else { + this.signPegNetRequest(this.selectedFactomAddress, vaultPassword); + } + } + }); + } + + cancelSigning(message?: string) { + if (message) { + this.toastr.error(message, null, {timeOut: 5000}); + } else { + this.toastr.info('Signing request cancelled!', null, {timeOut: 1000}); + } + + this.cancelSigningRequest(); + this.getPendingRequestsCount(); + this.getSigningRequest(); + } + + skipSigning() { + this.skipSigningRequest(); + this.getPendingRequestsCount(); + this.getSigningRequest(); + } + + toHumanReadable(amount: number) { + return amount / Math.pow(10, 8); + } + + private signDataRequest(dataToSign: string, signingKeyOrAddress: any, vaultPassword: string) { + this.signingService + .signData(dataToSign, this.requestKeyType, signingKeyOrAddress, vaultPassword) + .subscribe((signatureData: SignatureDataModel) => { + if (signatureData) { + chrome.runtime.sendMessage({type: ChromeMessageType.SendSigningRequestResponse, data: { + requestId: this.request.requestId, + ...signatureData + }}); + + this.displaySuccessMessageAndUpdateState('Data successfully signed!'); + } else { + this.spinner.hide(); + this.toastr.error('Incorrect vault password'); + } + }); + } + + private signPegNetRequest(fctPublicAddress: string, vaultPassword: string) { + if (this.txType == TransactionType.Burn) { + this.vaultService + .getPrivateKeyOrAddress(fctPublicAddress, vaultPassword) + .subscribe((result: ResultModel) => { + if (result.success) { + const fctPrivateAddress = result.message; + const tx = Transaction + .builder() + .input(fctPrivateAddress, this.inputAmount) + .output('EC2BURNFCT2PEGNETooo1oooo1oooo1oooo1oooo1oooo19wthin', 0) + .build(); + + chrome.runtime.sendMessage({type: ChromeMessageType.SendSigningRequestResponse, data: { + requestId: this.request.requestId, + transaction: tx.marshalBinary().toString('hex') + }}); + + this.displaySuccessMessageAndUpdateState('Transaction successfully signed!'); + } else { + this.spinner.hide(); + this.toastr.error(result.message); + } + }); + + } else { + const unixSeconds = Math.round(new Date().getTime() / 1000).toString(); + const entryContent = this.txType == TransactionType.Transfer + ? this.buildTransferEntry() + : this.buildConversionEntry(); + + const dataToSign = sha512(Buffer.concat([ + Buffer.from('0'), + Buffer.from(unixSeconds), + Buffer.from('cffce0f409ebba4ed236d49d89c70e4bd1f1367d86402a3363366683265a242d', 'hex'), + Buffer.from(entryContent) + ])); + + this.signingService + .signPegNetTransaction(dataToSign, fctPublicAddress, vaultPassword) + .subscribe((signatureData: SignatureDataModel) => { + if (signatureData) { + const rcd = Buffer.concat([Buffer.from([1]), signatureData.publicKey]).toString('hex'); + const signature = signatureData.signature.toString('hex'); + const extIds = [unixSeconds, rcd, signature]; + + chrome.runtime.sendMessage({type: ChromeMessageType.SendSigningRequestResponse, data: { + requestId: this.request.requestId, + entry: [extIds, entryContent] + }}); + + this.displaySuccessMessageAndUpdateState('Transaction successfully signed!'); + } else { + this.spinner.hide(); + this.toastr.error('Incorrect vault password'); + } + }); + } + } + + private buildConversionEntry() { + return JSON.stringify({ + version: 1, + transactions: [{ + input: { + address: this.selectedFactomAddress, + amount: this.inputAmount, + type: this.inputAsset + }, + conversion: this.outputAsset, + metadata: this.txMetadata + } + ], + }); + } + + private buildTransferEntry() { + return JSON.stringify({ + version: 1, + transactions: [{ + input: { + address: this.selectedFactomAddress, + amount: this.inputAmount, + type: this.inputAsset + }, + transfers: [{ + address: this.outputAddress, + amount: this.inputAmount + }], + metadata: this.txMetadata + } + ], + }); + } + + private displaySuccessMessageAndUpdateState(message: string) { + this.spinner.hide(); + this.toastr.success(message, null, {timeOut: 1000}); + this.clearRequestData(); + this.getPendingRequestsCount(); + this.getSigningRequest(); + } + + private getAllAvailableSigningKeys() { + this.getDIDIds(); + this.fctAddressesPublicInfo = this.vaultService.getFCTAddressesPublicInfo(); + this.fctAddresses = Object.keys(this.fctAddressesPublicInfo); + this.ecAddressesPublicInfo = this.vaultService.getECAddressesPublicInfo(); + this.ecAddresses = Object.keys(this.ecAddressesPublicInfo); + this.blockSigningKeysPublicInfo = this.vaultService.getBlockSigningKeysPublicInfo(); + this.blockSigningKeys = Object.keys(this.blockSigningKeysPublicInfo); + } + + private getDIDIds() { + this.allDIDsPublicInfo = this.vaultService.getAllDIDsPublicInfo(); + this.allDIDIds = Object.keys(this.allDIDsPublicInfo); + + for (const didId in this.allDIDsPublicInfo) { + const didDocument = this.allDIDsPublicInfo[didId].didDocument; + if (didDocument.didKey && didDocument.didKey.length > 0) { + this.didIdsWithDIDKeys.push(didId); + } + } + } + + private getDIDKeys(didId: string): DidKeyEntryModel[] | ManagementKeyEntryModel[] { + if (this.requestKeyType == RequestKeyType.DIDKey) { + return this.allDIDsPublicInfo[didId].didDocument.didKey; + } + + return this.allDIDsPublicInfo[didId].didDocument.managementKey; + } + + private getSigningRequest() { + chrome.runtime.sendMessage({type: ChromeMessageType.GetSigningRequest}, (response) => { + this.zone.run(() => { + if (response.success) { + this.from = response.signingRequest.from; + this.request = response.signingRequest.content; + this.requestType = this.request.requestType; + this.txType = this.request.requestInfo.txType; + + this.requestKeyType = this.requestType == RequestType.Data + ? this.request.requestInfo.keyType + : RequestKeyType.FCT; + + if ((this.requestKeyType == RequestKeyType.DIDKey && this.didIdsWithDIDKeys.length > 0) + || (this.requestKeyType == RequestKeyType.ManagementKey && this.allDIDIds.length > 0)) { + this.availableDIDIds = this.requestKeyType == RequestKeyType.DIDKey + ? this.didIdsWithDIDKeys + : this.allDIDIds; + this.selectedDIDId = this.availableDIDIds[0]; + this.availableDIDKeys = this.getDIDKeys(this.selectedDIDId); + this.selectedDIDKeyId = this.availableDIDKeys[0].id; + + const did = this.request.requestInfo.did; + if (did) { + if (this.availableDIDIds.includes(did)) { + this.selectedDIDId = did; + this.availableDIDKeys = this.getDIDKeys(this.selectedDIDId); + this.selectedDIDKeyId = this.availableDIDKeys[0].id; + this.didIdSpecified = true; + + const selectedKeyAlias = this.request.requestInfo.keyIdentifier; + if (selectedKeyAlias) { + const selectedKey = this.availableDIDKeys.find(k => k.id.split('#')[1] == selectedKeyAlias); + if (selectedKey) { + this.selectedDIDKeyId = selectedKey.id; + this.didKeySpecified = true; + } else { + const keyType = this.requestKeyType == RequestKeyType.DIDKey + ? 'Signing Key' + : 'Management Key'; + this.cancelSigning(`The ${keyType} requested for signing does not exist!`); + return; + } + } + } else { + if (this.requestKeyType == RequestKeyType.DIDKey + && this.allDIDIds.includes(did)) { + this.cancelSigning('The Identity requested for signing does not have any Signing keys!'); + } else { + this.cancelSigning('The Identity requested for signing does not exist!'); + } + + return; + } + } + + } else if ((this.requestKeyType == RequestKeyType.FCT && this.fctAddresses.length > 0) + || (this.requestKeyType == RequestKeyType.EC && this.ecAddresses.length > 0)) { + this.availableFactomAddresses = this.requestKeyType == RequestKeyType.FCT + ? this.fctAddresses + : this.ecAddresses; + + this.availableFactomAddressesPublicInfo = this.requestKeyType == RequestKeyType.FCT + ? this.fctAddressesPublicInfo + : this.ecAddressesPublicInfo; + + this.selectedFactomAddress = this.availableFactomAddresses[0]; + + const selectedAddress = this.requestType == RequestType.Data + ? this.request.requestInfo.keyIdentifier + : this.request.requestInfo.inputAddress; + + if (selectedAddress) { + if (this.availableFactomAddresses.includes(selectedAddress)) { + this.selectedFactomAddress = selectedAddress; + this.factomAddressSpecified = true; + } else { + this.cancelSigning(`The ${this.requestKeyType.toUpperCase()} Address requested for signing does not exist!`); + return; + } + } + } else if (this.requestKeyType == RequestKeyType.BlockSigningKey && this.blockSigningKeys.length > 0) { + this.selectedKey = this.blockSigningKeys[0]; + const selectedKey = this.request.requestInfo.keyIdentifier; + + if (selectedKey) { + if (this.blockSigningKeys.includes(selectedKey)) { + this.selectedKey = selectedKey; + this.keySpecified = true; + } else { + this.cancelSigning('The Block Signing key requested for signing does not exist!'); + return; + } + } + } + + if (this.requestType == RequestType.Data) { + this.dataToSign = JSON.stringify(this.request.requestInfo.data, null, 2); + } else { + this.inputAmount = this.request.requestInfo.inputAmount; + this.inputAsset = this.request.requestInfo.inputAsset; + this.outputAsset = this.request.requestInfo.outputAsset; + this.outputAddress = this.request.requestInfo.outputAddress; + this.txMetadata = this.request.requestInfo.txMetadata; + } + } + }); + }); + } + + private getPendingRequestsCount() { + chrome.runtime.sendMessage({type: ChromeMessageType.PendingSigningRequestsCount}, (response) => { + this.zone.run(() => { + this.pendingRequestsCount = response.pendingSigningRequestsCount; + this.signingService.updatePendingRequestsCount(this.pendingRequestsCount); + }); + }); + } + + private cancelSigningRequest() { + chrome.runtime.sendMessage({type: ChromeMessageType.CancelSigningRequest, data: {requestId: this.request.requestId}}); + this.clearRequestData(); + } + + private skipSigningRequest() { + this.toastr.info('Signing request skipped!', null, {timeOut: 1000}); + chrome.runtime.sendMessage({type: ChromeMessageType.SkipSigningRequest}); + this.clearRequestData(); + } + + private clearRequestData() { + this.request = undefined; + this.dataToSign = undefined; + this.from = undefined; + this.requestType = undefined; + this.requestKeyType = undefined; + this.txType = undefined; + this.inputAmount = undefined; + this.inputAsset = undefined; + this.outputAsset = undefined; + this.outputAddress = undefined; + this.txMetadata = undefined; + this.selectedDIDId = undefined; + this.selectedDIDKeyId = undefined; + this.availableDIDIds = undefined; + this.availableDIDKeys = undefined; + this.selectedFactomAddress = undefined; + this.selectedKey = undefined; + this.availableFactomAddresses = undefined; + this.availableFactomAddressesPublicInfo = undefined; + this.factomAddressSpecified = false; + this.didKeySpecified = false; + this.didIdSpecified = false; + this.keySpecified = false; + } +} diff --git a/src/app/components/vault/create-vault/create-vault.component.html b/src/app/components/vault/create-vault/create-vault.component.html new file mode 100644 index 0000000..d1e3d2a --- /dev/null +++ b/src/app/components/vault/create-vault/create-vault.component.html @@ -0,0 +1,46 @@ +
+
+
+
+

+ Create new vault +

+
+
+
+ + +
+
Password is required!
+
+ Your password must be at least 18 characters long. Hint: Use 4-5 easy to remember words in sequence (e.g. TorReallyLovesMeatballs) +
+
+
+
+ + +
+
Confirm password is required!
+
Passwords do not match!
+
+
+ +
+ +
+
+
+
diff --git a/src/app/components/vault/create-vault/create-vault.component.scss b/src/app/components/vault/create-vault/create-vault.component.scss new file mode 100644 index 0000000..6945852 --- /dev/null +++ b/src/app/components/vault/create-vault/create-vault.component.scss @@ -0,0 +1,61 @@ +#create-vault { + .auth-side { + width: 100%; + min-height: 100vh; + @media only screen and (min-width: 768px){ + width: 50%; + float: left; + display: -webkit-flex; + -webkit-align-items: center; + display: flex; + align-items: center; + } + + .form-container { + width: 90%; + max-width: 400px; + margin: 0 auto; + .heading { + margin: 0 0 30px 0; + h1 { + font-size: 23px; + font-weight: 700; + } + } + .btn-primary { + width: 100%; + display: block; + } + } + @media only screen and (max-width: 768px){ + background-image: url("/assets/crypto-auth.png"); + background-size: cover; + background-position: 50% 50%; + .form-container { + border-radius: 9px; + background: #fff; + padding: 10px; + position: absolute; + top: 50%; + left: 5%; + transform: translateY(-50%); + max-width: none; + } + } + } + .background-side { + min-height: 100vh; + display: none; + @media only screen and (min-width: 768px){ + width: 50%; + float: left; + background-image: url("/assets/crypto-auth.png"); + background-size: cover; + background-position: 50% 50%; + display: block; + } + } + .form-control.is-invalid{ + background-image: none; + } +} diff --git a/src/app/components/vault/create-vault/create-vault.component.ts b/src/app/components/vault/create-vault/create-vault.component.ts new file mode 100644 index 0000000..f742689 --- /dev/null +++ b/src/app/components/vault/create-vault/create-vault.component.ts @@ -0,0 +1,50 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; +import { ToastrService } from 'ngx-toastr'; + +import CustomValidators from 'src/app/core/utils/customValidators'; +import { VaultService } from 'src/app/core/services/vault/vault.service'; + +@Component({ + selector: 'app-create-vault', + templateUrl: './create-vault.component.html', + styleUrls: ['./create-vault.component.scss'] +}) +export class CreateVaultComponent implements OnInit { + public createVaultForm; + + constructor( + private fb: FormBuilder, + private router: Router, + private toastr: ToastrService, + private vaultService: VaultService) { } + + ngOnInit() { + this.createVaultForm = this.fb.group({ + password: ['', [Validators.required, Validators.minLength(18)]], + confirmPassword: ['', [Validators.required]] + }, { validator: CustomValidators.passwordsDoMatch.bind(this)}); + } + + createVault() { + if (this.createVaultForm.invalid) { + return; + } + + this.vaultService + .createNewVault(this.password.value) + .subscribe(() => { + this.toastr.success('New vault created'); + this.router.navigate(['home']); + }); + } + + get password () { + return this.createVaultForm.get('password'); + } + + get confirmPassword() { + return this.createVaultForm.get('confirmPassword'); + } +} diff --git a/src/app/components/vault/index.ts b/src/app/components/vault/index.ts new file mode 100644 index 0000000..8489508 --- /dev/null +++ b/src/app/components/vault/index.ts @@ -0,0 +1,9 @@ +import { CreateVaultComponent } from './create-vault/create-vault.component'; +import { RestoreVaultComponent } from './restore-vault/restore-vault.component'; +import { VaultBackupComponent } from './vault-backup/vault-backup.component'; + +export const vaultComponents = [ + CreateVaultComponent, + RestoreVaultComponent, + VaultBackupComponent +]; diff --git a/src/app/components/vault/restore-vault/restore-vault.component.html b/src/app/components/vault/restore-vault/restore-vault.component.html new file mode 100644 index 0000000..2493188 --- /dev/null +++ b/src/app/components/vault/restore-vault/restore-vault.component.html @@ -0,0 +1,31 @@ +
+

+ Restore Vault +

+
+
+

Select Your Backup File

+
+ +
+
+
+ +
+ +
+
Password is required!
+
+
+
+
+ + +
+
+
diff --git a/src/app/components/vault/restore-vault/restore-vault.component.scss b/src/app/components/vault/restore-vault/restore-vault.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/components/vault/restore-vault/restore-vault.component.ts b/src/app/components/vault/restore-vault/restore-vault.component.ts new file mode 100644 index 0000000..aa70916 --- /dev/null +++ b/src/app/components/vault/restore-vault/restore-vault.component.ts @@ -0,0 +1,126 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, Validators } from '@angular/forms'; +import { NgxSpinnerService } from 'ngx-spinner'; +import { Router } from '@angular/router'; +import { ToastrService } from 'ngx-toastr'; + +import { BackupDialogComponent } from '../../dialogs/backup/backup.dialog.component'; +import { BackupResultModel } from 'src/app/core/models/backup-result.model'; +import { ChromeMessageType } from 'src/app/core/enums/chrome-message-type'; +import { DialogsService } from 'src/app/core/services/dialogs/dialogs.service'; +import { downloadFile, preProcessEncryptedBackupFile, postProcessEncryptedBackupFile, generateBackupFileName } from 'src/app/core/utils/helpers'; +import { ModalSizeTypes } from 'src/app/core/enums/modal-size-types'; +import { PasswordDialogComponent } from '../../dialogs/password/password.dialog.component'; +import { RestoreResultModel } from 'src/app/core/models/restore-result.model'; +import { VaultService } from 'src/app/core/services/vault/vault.service'; + +@Component({ + selector: 'app-restore-vault', + templateUrl: './restore-vault.component.html', + styleUrls: ['./restore-vault.component.scss'] +}) +export class RestoreVaultComponent implements OnInit { + public backupPasswordForm; + public file: string; + + constructor( + private fb: FormBuilder, + private dialogsService: DialogsService, + private vaultService: VaultService, + private spinner: NgxSpinnerService, + private toastr: ToastrService, + private router: Router) { } + + ngOnInit() { + chrome.tabs.getCurrent(function(tab) { + if (tab === undefined) { + chrome.runtime.getPlatformInfo(function(info) { + if (info.os !== 'win') { + chrome.runtime.sendMessage({type: ChromeMessageType.RestoreVaultRequest}, (response) => { + if (response.success) { + const popup_url = chrome.runtime.getURL('index.html'); + chrome.tabs.create({'url': popup_url}); + } + }); + } + }); + } else { + chrome.runtime.sendMessage({type: ChromeMessageType.CheckRequests}, (response) => { + if (response.restoreVaultRequested) { + chrome.runtime.sendMessage({type: ChromeMessageType.NewTabOpen}); + } + }); + } + }); + + this.backupPasswordForm = this.fb.group({ + password: ['', [Validators.required]] + }); + } + + importVault() { + if (this.backupPasswordForm.invalid || !this.file) { + return; + } + + this.spinner.show(); + const backupFile = preProcessEncryptedBackupFile(this.file); + this.vaultService + .restoreVault(backupFile, this.password.value) + .subscribe((result: RestoreResultModel) => { + if (result.success) { + this.spinner.hide(); + if (result.versionUpgraded) { + this.dialogsService.open(BackupDialogComponent, ModalSizeTypes.ExtraExtraLarge, undefined) + .subscribe(() => { + this.backupVault(); + }); + } else { + this.toastr.success(result.message); + } + + this.router.navigate(['home']); + } else { + this.spinner.hide(); + this.toastr.error(result.message); + this.backupPasswordForm.reset(); + } + }); + } + + get password () { + return this.backupPasswordForm.get('password'); + } + + handleFileInput(files) { + if (files && files.length > 0) { + const fileReader = new FileReader(); + fileReader.onload = () => { + this.file = fileReader.result.toString(); + }; + + fileReader.readAsText(files[0]); + } + } + + private backupVault() { + const dialogMessage = 'Enter your vault password to encrypt the backup file'; + + this.dialogsService.open(PasswordDialogComponent, ModalSizeTypes.ExtraExtraLarge, dialogMessage) + .subscribe((vaultPassword: string) => { + if (vaultPassword) { + this.vaultService.getEncryptedState(vaultPassword) + .subscribe((backupResult: BackupResultModel) =>{ + if (backupResult.success) { + const backupFile = postProcessEncryptedBackupFile(backupResult.backup); + const backupFileName = generateBackupFileName(); + downloadFile(backupFile, backupFileName); + this.toastr.success(backupResult.message); + } else { + this.toastr.error(backupResult.message); + } + }); + } + }); + } +} diff --git a/src/app/components/vault/vault-backup/vault-backup.component.html b/src/app/components/vault/vault-backup/vault-backup.component.html new file mode 100644 index 0000000..8807e2b --- /dev/null +++ b/src/app/components/vault/vault-backup/vault-backup.component.html @@ -0,0 +1,20 @@ +
+
+
+
+

+ Vault Backup +

+
+
+

Download your vault backup file. You can use the file to restore your vault later or import it on a different computer.

+ +
+
+

You have to create at least one Identity or Address before backing up your vault

+
+
+
+
+
+
diff --git a/src/app/components/vault/vault-backup/vault-backup.component.scss b/src/app/components/vault/vault-backup/vault-backup.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/components/vault/vault-backup/vault-backup.component.ts b/src/app/components/vault/vault-backup/vault-backup.component.ts new file mode 100644 index 0000000..f0e1de0 --- /dev/null +++ b/src/app/components/vault/vault-backup/vault-backup.component.ts @@ -0,0 +1,48 @@ +import { Component, OnInit } from '@angular/core'; +import { ToastrService } from 'ngx-toastr'; + +import { BackupResultModel } from 'src/app/core/models/backup-result.model'; +import { DialogsService } from 'src/app/core/services/dialogs/dialogs.service'; +import { downloadFile, postProcessEncryptedBackupFile, generateBackupFileName } from 'src/app/core/utils/helpers'; +import { ModalSizeTypes } from 'src/app/core/enums/modal-size-types'; +import { PasswordDialogComponent } from '../../dialogs/password/password.dialog.component'; +import { VaultService } from 'src/app/core/services/vault/vault.service'; + +@Component({ + selector: 'app-vault-backup', + templateUrl: './vault-backup.component.html', + styleUrls: ['./vault-backup.component.scss'] +}) +export class VaultBackupComponent implements OnInit { + public anyDIDsOrAddresses: boolean; + + constructor( + private dialogsService: DialogsService, + private toastr: ToastrService, + private vaultService: VaultService) { } + + ngOnInit() { + this.anyDIDsOrAddresses = this.vaultService.anyDIDsOrAddresses(); + } + + backupVault() { + const dialogMessage = 'Enter your vault password to encrypt the backup file'; + + this.dialogsService.open(PasswordDialogComponent, ModalSizeTypes.ExtraExtraLarge, dialogMessage) + .subscribe((vaultPassword: string) => { + if (vaultPassword) { + this.vaultService.getEncryptedState(vaultPassword) + .subscribe((backupResult: BackupResultModel) =>{ + if (backupResult.success) { + const backupFile = postProcessEncryptedBackupFile(backupResult.backup); + const backupFileName = generateBackupFileName(); + downloadFile(backupFile, backupFileName); + this.toastr.success(backupResult.message); + } else { + this.toastr.error(backupResult.message); + } + }); + } + }); + } +} diff --git a/src/app/components/vault/vault.module.ts b/src/app/components/vault/vault.module.ts new file mode 100644 index 0000000..158654b --- /dev/null +++ b/src/app/components/vault/vault.module.ts @@ -0,0 +1,22 @@ +import { CommonModule } from '@angular/common'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { NgModule } from '@angular/core'; + +import { vaultComponents } from '.'; +import { VaultRoutingModule } from './vault.routing'; + +@NgModule({ + declarations: [ + ...vaultComponents + ], + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + VaultRoutingModule + ], + exports: [ + ...vaultComponents + ] +}) +export class VaultModule { } diff --git a/src/app/components/vault/vault.routing.ts b/src/app/components/vault/vault.routing.ts new file mode 100644 index 0000000..94e401f --- /dev/null +++ b/src/app/components/vault/vault.routing.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { CreateVaultComponent } from './create-vault/create-vault.component'; +import { RestoreVaultComponent } from './restore-vault/restore-vault.component'; +import { VaultBackupComponent } from './vault-backup/vault-backup.component'; +import { VaultGuard } from '../../core/guards/vault.guard'; + +const carsRoutes: Routes = [ + { path: 'backup', component: VaultBackupComponent, canActivate: [VaultGuard] }, + { path: 'create', component: CreateVaultComponent }, + { path: 'restore', component: RestoreVaultComponent } +]; + +@NgModule({ + imports: [RouterModule.forChild(carsRoutes)], + exports: [RouterModule] +}) +export class VaultRoutingModule { } diff --git a/src/app/core/enums/action-routes.ts b/src/app/core/enums/action-routes.ts new file mode 100644 index 0000000..9e2767a --- /dev/null +++ b/src/app/core/enums/action-routes.ts @@ -0,0 +1,21 @@ +import { ActionType } from './action-type'; +import { CreateRoutes } from './create-routes'; +import { SharedRoutes } from './shared-routes'; + +const actionRoutes = { + [ActionType.CreateAdvanced]: [ + SharedRoutes.Action, + CreateRoutes.ManagementKeys, + CreateRoutes.DidKeys, + CreateRoutes.Services, + CreateRoutes.Summary + ], + [ActionType.CreateBasic]: [ + SharedRoutes.Action, + CreateRoutes.Summary + ] +}; + +export { + actionRoutes +}; diff --git a/src/app/core/enums/action-type.ts b/src/app/core/enums/action-type.ts new file mode 100644 index 0000000..8906911 --- /dev/null +++ b/src/app/core/enums/action-type.ts @@ -0,0 +1,4 @@ +export enum ActionType { + CreateAdvanced = 'create-advanced', + CreateBasic = 'create-basic' +} diff --git a/src/app/core/enums/chrome-message-type.ts b/src/app/core/enums/chrome-message-type.ts new file mode 100644 index 0000000..7ad8b6e --- /dev/null +++ b/src/app/core/enums/chrome-message-type.ts @@ -0,0 +1,17 @@ +export enum ChromeMessageType { + PendingSigningRequestsCount = 'pendingSigningRequestsCount', + GetSigningRequest = 'getSigningRequest', + CancelSigningRequest = 'cancelSigningRequest', + SkipSigningRequest = 'skipSigningRequest', + SendSigningRequestResponse = 'sendSigningRequestResponse', + RestoreVaultRequest = 'restoreVaultRequest', + ManageDidsRequest = 'manageDidsRequest', + ManageFactomAddressesRequest = 'manageFactomAddressesRequest', + ManageKeysRequest = 'manageKeysRequest', + SettingsRequest = 'settingsRequest', + CheckRequests = 'checkRequests', + NewTabOpen = 'newTabOpen', + ApprovalRequestsCount = 'approvalRequestsCount', + GetApprovalRequest = 'getApprovalRequest', + SendApprovalRequestResponse = 'sendApprovalRequestResponse' +} diff --git a/src/app/core/enums/create-routes.ts b/src/app/core/enums/create-routes.ts new file mode 100644 index 0000000..df5de5f --- /dev/null +++ b/src/app/core/enums/create-routes.ts @@ -0,0 +1,6 @@ +export enum CreateRoutes { + ManagementKeys = '/dids/manage/create/keys/management', + DidKeys = '/dids/manage/create/keys/did', + Services = '/dids/manage/create/services', + Summary = '/dids/manage/create/summary' +} diff --git a/src/app/core/enums/entry-type.ts b/src/app/core/enums/entry-type.ts new file mode 100644 index 0000000..aaec7c9 --- /dev/null +++ b/src/app/core/enums/entry-type.ts @@ -0,0 +1,5 @@ +export enum EntryType { + CreateDIDEntry = 'DIDManagement', + UpdateDIDEntry = 'DIDUpdate', + DeactivateDIDEntry = 'DIDDeactivation' +} diff --git a/src/app/core/enums/factom-address-type.ts b/src/app/core/enums/factom-address-type.ts new file mode 100644 index 0000000..c857e87 --- /dev/null +++ b/src/app/core/enums/factom-address-type.ts @@ -0,0 +1,5 @@ +export enum FactomAddressType { + FCT = 'FCT', + EC = 'EC', + EtherLink = 'EtherLink' +} diff --git a/src/app/core/enums/key-type.ts b/src/app/core/enums/key-type.ts new file mode 100644 index 0000000..db493c7 --- /dev/null +++ b/src/app/core/enums/key-type.ts @@ -0,0 +1,3 @@ +export enum KeyType { + BlockSigningKey = 'BlockSigningKey', +} \ No newline at end of file diff --git a/src/app/core/enums/modal-size-types.ts b/src/app/core/enums/modal-size-types.ts new file mode 100644 index 0000000..2060658 --- /dev/null +++ b/src/app/core/enums/modal-size-types.ts @@ -0,0 +1,7 @@ +export enum ModalSizeTypes { + Small = 'sm', + Medium = 'md', + Large = 'lg', + ExtraLarge = 'xl', + ExtraExtraLarge = 'xxl' +} diff --git a/src/app/core/enums/purpose-type.ts b/src/app/core/enums/purpose-type.ts new file mode 100644 index 0000000..3624eff --- /dev/null +++ b/src/app/core/enums/purpose-type.ts @@ -0,0 +1,4 @@ +export enum PurposeType { + PublicKey = 'publicKey', + AuthenticationKey = 'authentication' +} \ No newline at end of file diff --git a/src/app/core/enums/request-key-type.ts b/src/app/core/enums/request-key-type.ts new file mode 100644 index 0000000..ede1183 --- /dev/null +++ b/src/app/core/enums/request-key-type.ts @@ -0,0 +1,8 @@ +export enum RequestKeyType { + DIDKey = 'didKey', + ManagementKey = 'managementKey', + FCT = 'fct', + EC = 'ec', + EtherLink = 'etherLink', + BlockSigningKey = 'blockSigningKey' +} \ No newline at end of file diff --git a/src/app/core/enums/request-type.ts b/src/app/core/enums/request-type.ts new file mode 100644 index 0000000..999e4ff --- /dev/null +++ b/src/app/core/enums/request-type.ts @@ -0,0 +1,4 @@ +export enum RequestType { + Data = 'data', + Pegnet = 'pegnet' +} \ No newline at end of file diff --git a/src/app/core/enums/shared-routes.ts b/src/app/core/enums/shared-routes.ts new file mode 100644 index 0000000..4bbd18d --- /dev/null +++ b/src/app/core/enums/shared-routes.ts @@ -0,0 +1,4 @@ +export enum SharedRoutes { + Action = '/dids/manage/action', + ManageDids = '/dids/manage' +} diff --git a/src/app/core/enums/signature-type.ts b/src/app/core/enums/signature-type.ts new file mode 100644 index 0000000..36b0993 --- /dev/null +++ b/src/app/core/enums/signature-type.ts @@ -0,0 +1,5 @@ +export enum SignatureType { + EdDSA = 'Ed25519', + ECDSA = 'ECDSASecp256k1', + RSA = 'RSA' +} diff --git a/src/app/core/enums/transaction-type.ts b/src/app/core/enums/transaction-type.ts new file mode 100644 index 0000000..d7cdf3c --- /dev/null +++ b/src/app/core/enums/transaction-type.ts @@ -0,0 +1,5 @@ +export enum TransactionType { + Conversion = 'conversion', + Transfer = 'transfer', + Burn = 'burn' +} \ No newline at end of file diff --git a/src/app/core/guards/create-action.guard.ts b/src/app/core/guards/create-action.guard.ts new file mode 100644 index 0000000..091f9c7 --- /dev/null +++ b/src/app/core/guards/create-action.guard.ts @@ -0,0 +1,34 @@ +import { + CanActivate, + ActivatedRouteSnapshot, + RouterStateSnapshot, + Router +} from '@angular/router'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; + +import { ActionType } from '../enums/action-type'; +import { SharedRoutes } from '../enums/shared-routes'; +import { WorkflowService } from '../services/workflow/workflow.service'; + +@Injectable({ + providedIn: 'root' +}) +export class CreateActionGuard implements CanActivate { + constructor( + private router: Router, + private workflowService: WorkflowService) { } + + canActivate( + next: ActivatedRouteSnapshot, + state: RouterStateSnapshot ): Observable | Promise | boolean { + + const selectedAction = this.workflowService.getSelectedAction(); + if (selectedAction === ActionType.CreateAdvanced || selectedAction === ActionType.CreateBasic) { + return true; + } + + this.router.navigate([SharedRoutes.ManageDids]); + return false; + } +} diff --git a/src/app/core/guards/guards.module.ts b/src/app/core/guards/guards.module.ts new file mode 100644 index 0000000..7158c4c --- /dev/null +++ b/src/app/core/guards/guards.module.ts @@ -0,0 +1,16 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; + +import { CreateActionGuard } from './create-action.guard'; +import { VaultGuard } from './vault.guard'; + +@NgModule({ + providers: [ + CreateActionGuard, + VaultGuard + ], + imports: [ + CommonModule + ] +}) +export class GuardsModule { } diff --git a/src/app/core/guards/vault.guard.ts b/src/app/core/guards/vault.guard.ts new file mode 100644 index 0000000..2443b36 --- /dev/null +++ b/src/app/core/guards/vault.guard.ts @@ -0,0 +1,32 @@ +import { + CanActivate, + ActivatedRouteSnapshot, + RouterStateSnapshot, + Router +} from '@angular/router'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; + +import { VaultService } from '../services/vault/vault.service'; + +@Injectable({ + providedIn: 'root' +}) +export class VaultGuard implements CanActivate { + + constructor( + private router: Router, + private vaultService: VaultService) { } + + canActivate( + next: ActivatedRouteSnapshot, + state: RouterStateSnapshot ): Observable | Promise | boolean { + + if (this.vaultService.vaultExists()) { + return true; + } + + this.router.navigate(['/vault/create']); + return false; + } +} diff --git a/src/app/core/interceptors/error.interceptor.ts b/src/app/core/interceptors/error.interceptor.ts new file mode 100644 index 0000000..7578b2a --- /dev/null +++ b/src/app/core/interceptors/error.interceptor.ts @@ -0,0 +1,34 @@ +import { catchError, retryWhen, take, concat, delay } from 'rxjs/operators'; +import { + HttpRequest, + HttpHandler, + HttpEvent, + HttpInterceptor, + HttpErrorResponse +} from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { NgxSpinnerService } from 'ngx-spinner'; +import { Observable, throwError } from 'rxjs'; +import { ToastrService } from 'ngx-toastr'; + +@Injectable() +export class ErrorInterceptor implements HttpInterceptor { + constructor ( + private toastr: ToastrService, + private spinner: NgxSpinnerService ) { } + + intercept(req: HttpRequest, next: HttpHandler): Observable> { + return next + .handle(req) + .pipe( + retryWhen(errors => errors.pipe(delay(1000), take(3), concat(throwError(errors)))), + catchError((err: HttpErrorResponse) => { + this.spinner.hide(); + + const errorMessage = 'A problem occurred while publishing your data. Please, try again!'; + this.toastr.error(errorMessage, 'Warning!'); + + return throwError(err); + })); + } +} diff --git a/src/app/core/interfaces/dialogs/modal-options.ts b/src/app/core/interfaces/dialogs/modal-options.ts new file mode 100644 index 0000000..d98150e --- /dev/null +++ b/src/app/core/interfaces/dialogs/modal-options.ts @@ -0,0 +1,18 @@ +import { NgbModalOptions } from '@ng-bootstrap/ng-bootstrap'; + +export class ModalOptions implements NgbModalOptions { + // alternatively, specify 'static' for a backdrop which doesn't close the modal on click. + backdrop?: boolean | 'static'; + + // an element to which to attach newly opened modal windows. + container?: string; + + // whether to close the modal when escape key is pressed (true by default). + keyboard?: boolean; + + // size of a new modal window. Use the Enumeration DialogSizeTypes + size?: any; + + // custom class to append to the modal window + windowClass?: string; +} diff --git a/src/app/core/interfaces/dialogs/modal.ts b/src/app/core/interfaces/dialogs/modal.ts new file mode 100644 index 0000000..cedf732 --- /dev/null +++ b/src/app/core/interfaces/dialogs/modal.ts @@ -0,0 +1,6 @@ +import { ModalSizeTypes } from '../../enums/modal-size-types'; + +export interface Modal { + component: any; + size: ModalSizeTypes; +} diff --git a/src/app/core/interfaces/did-document.ts b/src/app/core/interfaces/did-document.ts new file mode 100644 index 0000000..2d6472e --- /dev/null +++ b/src/app/core/interfaces/did-document.ts @@ -0,0 +1,10 @@ +import { DidKeyEntryModel } from './did-key-entry'; +import { ManagementKeyEntryModel } from './management-key-entry'; +import { ServiceEntryModel } from './service-entry'; + +export interface DIDDocument { + didMethodVersion: string; + managementKey: ManagementKeyEntryModel[]; + didKey?: DidKeyEntryModel[]; + service?: ServiceEntryModel[]; +} diff --git a/src/app/core/interfaces/did-key-entry.ts b/src/app/core/interfaces/did-key-entry.ts new file mode 100644 index 0000000..50b61d1 --- /dev/null +++ b/src/app/core/interfaces/did-key-entry.ts @@ -0,0 +1,9 @@ +export interface DidKeyEntryModel { + id: string, + type: string, + controller: string, + publicKeyBase58?: string, + publicKeyPem?: string, + purpose?: [], + priorityRequirement?: number +} \ No newline at end of file diff --git a/src/app/core/interfaces/management-key-entry.ts b/src/app/core/interfaces/management-key-entry.ts new file mode 100644 index 0000000..8b1d095 --- /dev/null +++ b/src/app/core/interfaces/management-key-entry.ts @@ -0,0 +1,9 @@ +export interface ManagementKeyEntryModel { + id: string, + type: string, + controller: string, + publicKeyBase58?: string, + publicKeyPem?: string, + priority?: number, + priorityRequirement?: number +} \ No newline at end of file diff --git a/src/app/core/interfaces/revoke-model.ts b/src/app/core/interfaces/revoke-model.ts new file mode 100644 index 0000000..ae09137 --- /dev/null +++ b/src/app/core/interfaces/revoke-model.ts @@ -0,0 +1,3 @@ +export interface RevokeModel { + id: string +} \ No newline at end of file diff --git a/src/app/core/interfaces/service-entry.ts b/src/app/core/interfaces/service-entry.ts new file mode 100644 index 0000000..7391115 --- /dev/null +++ b/src/app/core/interfaces/service-entry.ts @@ -0,0 +1,6 @@ +export interface ServiceEntryModel { + id: string, + type: string, + serviceEndpoint: string, + priorityRequirement?: number +} \ No newline at end of file diff --git a/src/app/core/interfaces/update-entry-document.ts b/src/app/core/interfaces/update-entry-document.ts new file mode 100644 index 0000000..832a7c2 --- /dev/null +++ b/src/app/core/interfaces/update-entry-document.ts @@ -0,0 +1,17 @@ +import { DidKeyEntryModel } from './did-key-entry'; +import { ManagementKeyEntryModel } from './management-key-entry'; +import { ServiceEntryModel } from './service-entry'; +import { RevokeModel } from './revoke-model'; + +export interface UpdateEntryDocument { + revoke?: { + managementKey?: RevokeModel[], + didKey?: RevokeModel[], + service?: RevokeModel[] + }, + add?: { + managementKey?: ManagementKeyEntryModel[], + didKey?: DidKeyEntryModel[], + service?: ServiceEntryModel[] + } +} \ No newline at end of file diff --git a/src/app/core/models/backup-result.model.ts b/src/app/core/models/backup-result.model.ts new file mode 100644 index 0000000..7b2896f --- /dev/null +++ b/src/app/core/models/backup-result.model.ts @@ -0,0 +1,7 @@ +export class BackupResultModel { + constructor( + public success: boolean, + public message: string, + public backup?: string + ) { } +} \ No newline at end of file diff --git a/src/app/core/models/component-key.model.ts b/src/app/core/models/component-key.model.ts new file mode 100644 index 0000000..a8c3182 --- /dev/null +++ b/src/app/core/models/component-key.model.ts @@ -0,0 +1,18 @@ +import { DidKeyModel } from './did-key.model'; +import { ManagementKeyModel } from './management-key.model'; +import { PurposeType } from '../enums/purpose-type'; + +export class ComponentKeyModel { + constructor( + public keyModel: ManagementKeyModel | DidKeyModel, + public iconPosition: string, + public disabled: boolean, + public purposes?: any[]) { + if (keyModel['purpose']) { + this.purposes = [ + { name: 'Public Key', value: PurposeType.PublicKey, checked: keyModel['purpose'].includes(PurposeType.PublicKey) }, + { name: 'Authentication Key', value: PurposeType.AuthenticationKey, checked: keyModel['purpose'].includes(PurposeType.AuthenticationKey) } + ] + } + } +} diff --git a/src/app/core/models/component-service.model.ts b/src/app/core/models/component-service.model.ts new file mode 100644 index 0000000..b91cbb8 --- /dev/null +++ b/src/app/core/models/component-service.model.ts @@ -0,0 +1,8 @@ +import { ServiceModel } from './service.model'; + +export class ComponentServiceModel { + constructor( + public serviceModel: ServiceModel, + public iconPosition: string) { + } +} diff --git a/src/app/core/models/did-key.model.ts b/src/app/core/models/did-key.model.ts new file mode 100644 index 0000000..de24a6f --- /dev/null +++ b/src/app/core/models/did-key.model.ts @@ -0,0 +1,17 @@ +import { KeyModel } from './key.model'; +import { PurposeType } from '../enums/purpose-type'; + +export class DidKeyModel extends KeyModel { + constructor( + public alias: string, + public purpose: PurposeType[], + public type: string, + public controller: string, + public publicKey: string, + public privateKey?: string, + public priorityRequirement?: number) { + super(alias, type, controller, publicKey, privateKey); + this.purpose = purpose; + this.priorityRequirement = priorityRequirement; + } +} \ No newline at end of file diff --git a/src/app/core/models/key-pair.model.ts b/src/app/core/models/key-pair.model.ts new file mode 100644 index 0000000..adbaa5f --- /dev/null +++ b/src/app/core/models/key-pair.model.ts @@ -0,0 +1,5 @@ +export class KeyPairModel { + constructor( + public publicKey: string, + public privateKey: string) {} +} diff --git a/src/app/core/models/key.model.ts b/src/app/core/models/key.model.ts new file mode 100644 index 0000000..cfbd201 --- /dev/null +++ b/src/app/core/models/key.model.ts @@ -0,0 +1,8 @@ +export class KeyModel { + constructor( + public alias: string, + public type: string, + public controller: string, + public publicKey: string, + public privateKey?: string) {} +} diff --git a/src/app/core/models/management-key.model.ts b/src/app/core/models/management-key.model.ts new file mode 100644 index 0000000..b728938 --- /dev/null +++ b/src/app/core/models/management-key.model.ts @@ -0,0 +1,16 @@ +import { KeyModel } from './key.model'; + +export class ManagementKeyModel extends KeyModel { + constructor( + public alias: string, + public priority: number, + public type: string, + public controller: string, + public publicKey: string, + public privateKey?: string, + public priorityRequirement?: number) { + super(alias, type, controller, publicKey, privateKey); + this.priority = priority; + this.priorityRequirement = priorityRequirement; + } +} \ No newline at end of file diff --git a/src/app/core/models/restore-result.model.ts b/src/app/core/models/restore-result.model.ts new file mode 100644 index 0000000..117002a --- /dev/null +++ b/src/app/core/models/restore-result.model.ts @@ -0,0 +1,7 @@ +export class RestoreResultModel { + constructor( + public success: boolean, + public versionUpgraded: boolean, + public message?: string + ) { } +} \ No newline at end of file diff --git a/src/app/core/models/result.model.ts b/src/app/core/models/result.model.ts new file mode 100644 index 0000000..a31f962 --- /dev/null +++ b/src/app/core/models/result.model.ts @@ -0,0 +1,6 @@ +export class ResultModel { + constructor( + public success: boolean, + public message: string + ) { } +} \ No newline at end of file diff --git a/src/app/core/models/service.model.ts b/src/app/core/models/service.model.ts new file mode 100644 index 0000000..6d15c48 --- /dev/null +++ b/src/app/core/models/service.model.ts @@ -0,0 +1,7 @@ +export class ServiceModel { + constructor( + public type: string, + public endpoint: string, + public alias: string, + public priorityRequirement?: number) {} +} diff --git a/src/app/core/models/signature-data.model.ts b/src/app/core/models/signature-data.model.ts new file mode 100644 index 0000000..6af8aaf --- /dev/null +++ b/src/app/core/models/signature-data.model.ts @@ -0,0 +1,8 @@ +export class SignatureDataModel { + constructor( + public message: any, + public publicKey: any, + public signature: any, + public signatureType: string + ) { } +} diff --git a/src/app/core/models/signature-result.model.ts b/src/app/core/models/signature-result.model.ts new file mode 100644 index 0000000..7d4ec9c --- /dev/null +++ b/src/app/core/models/signature-result.model.ts @@ -0,0 +1,7 @@ +export class SignatureResultModel { + constructor( + public success: boolean, + public message: string, + public signatureBase64?: string + ) { } +} \ No newline at end of file diff --git a/src/app/core/models/update-did.model.ts b/src/app/core/models/update-did.model.ts new file mode 100644 index 0000000..e4b4d8a --- /dev/null +++ b/src/app/core/models/update-did.model.ts @@ -0,0 +1,28 @@ +import { DidKeyModel } from './did-key.model'; +import { ManagementKeyModel } from './management-key.model'; +import { ServiceModel } from './service.model'; + +export class UpdateDIDModel { + didId: string; + managementKeys: ManagementKeyModel[]; + didKeys: DidKeyModel[]; + services: ServiceModel[]; + originalManagementKeys: ManagementKeyModel[]; + originalDidKeys: DidKeyModel[]; + originalServices: ServiceModel[]; + + constructor( + didId: string, + managementKeys: ManagementKeyModel[], + didKeys: DidKeyModel[], + services: ServiceModel[] + ) { + this.didId = didId; + this.managementKeys = managementKeys; + this.didKeys = didKeys; + this.services = services; + this.originalManagementKeys = managementKeys; + this.originalDidKeys = didKeys; + this.originalServices = services; + } +} \ No newline at end of file diff --git a/src/app/core/services/dialogs/dialogs.service.ts b/src/app/core/services/dialogs/dialogs.service.ts new file mode 100644 index 0000000..9f3a852 --- /dev/null +++ b/src/app/core/services/dialogs/dialogs.service.ts @@ -0,0 +1,55 @@ +import { Injectable } from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { from } from 'rxjs'; + +import { ModalOptions } from '../../interfaces/dialogs/modal-options'; +import { ModalSizeTypes } from '../../enums/modal-size-types'; + +@Injectable() +export class DialogsService { + constructor(private modal: NgbModal) { } + + open(component: any, modalSize: ModalSizeTypes, message: string) { + const modalOptions = this.createModalOptions(modalSize); + modalOptions.keyboard = false; + modalOptions.backdrop = 'static'; + + const modalRef = this.modal.open(component, modalOptions); + modalRef.componentInstance.message = message; + + return from(modalRef.result); + } + + private createModalOptions(size: string): ModalOptions { + const modalOptions = new ModalOptions(); + + switch (size) { + case ModalSizeTypes.Small: { + modalOptions.size = ModalSizeTypes.Small; + modalOptions.windowClass = 'modal-sm'; + break; + } + case ModalSizeTypes.Medium: { + modalOptions.size = ModalSizeTypes.Large; + modalOptions.windowClass = 'modal-md'; + break; + } + case ModalSizeTypes.ExtraLarge: { + modalOptions.size = ModalSizeTypes.Large; + modalOptions.windowClass = 'modal-xl'; + break; + } + case ModalSizeTypes.ExtraExtraLarge: { + modalOptions.size = ModalSizeTypes.Large; + modalOptions.windowClass = 'modal-xxl'; + break; + } + default: { + modalOptions.size = size; + break; + } + } + + return modalOptions; + } +} diff --git a/src/app/core/services/did/did.service.ts b/src/app/core/services/did/did.service.ts new file mode 100644 index 0000000..d873360 --- /dev/null +++ b/src/app/core/services/did/did.service.ts @@ -0,0 +1,375 @@ +declare const Buffer; +import * as nacl from 'tweetnacl/nacl-fast'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable, of } from 'rxjs'; +import { Store, select } from '@ngrx/store'; + +import { AppState } from '../../store/app.state'; +import { CreateDIDState } from '../../store/create-did/create-did.state'; +import { DIDDocument } from '../../interfaces/did-document'; +import { DidKeyEntryModel } from '../../interfaces/did-key-entry'; +import { DidKeyModel } from '../../models/did-key.model'; +import { EntryType } from '../../enums/entry-type'; +import { environment } from 'src/environments/environment'; +import { InitializeDIDUpdate } from '../../store/update-did/update-did.actions'; +import { ManagementKeyEntryModel } from '../../interfaces/management-key-entry'; +import { ManagementKeyModel } from '../../models/management-key.model'; +import { RevokeModel } from '../../interfaces/revoke-model'; +import { ServiceEntryModel } from '../../interfaces/service-entry'; +import { ServiceModel } from '../../models/service.model'; +import { SignatureType } from '../../enums/signature-type'; +import { toHexString, calculateChainId } from '../../utils/helpers'; +import { UpdateDIDModel } from '../../models/update-did.model'; +import { UpdateDIDState } from '../../store/update-did/update-did.state'; +import { UpdateEntryDocument } from '../../interfaces/update-entry-document'; +import { VaultService } from '../vault/vault.service'; + +@Injectable() +export class DIDService { + private createDIDState: CreateDIDState; + private updateDIDState: UpdateDIDState; + private id: string; + private nonce: string; + private VerificationKeySuffix = 'VerificationKey'; + private apiUrl = environment.apiUrl; + private didMethodSpecVersion = environment.didMethodSpecVersion; + private entrySchemaVersion = environment.entrySchemaVersion; + + constructor( + private http: HttpClient, + private store: Store, + private vaultService: VaultService) { + this.store + .pipe(select(state => state)) + .subscribe(state => { + this.createDIDState = state.createDID; + this.updateDIDState = state.updateDID; + }); + } + + getId(): string { + if (!this.id) { + return this.generateId(); + } + + return this.id; + } + + generateEntry(entryType: string): DIDDocument | UpdateEntryDocument { + if (entryType === EntryType.UpdateDIDEntry) { + return this.generateUpdateEntry(); + } + + return this.generateDocument(); + } + + recordEntryOnChain(entryType: EntryType, entry: any, managementKeyId?: string, signature?: string): Observable { + const extIds = entryType == EntryType.CreateDIDEntry + ? [entryType, this.entrySchemaVersion, this.nonce] + : [entryType, this.entrySchemaVersion, managementKeyId, signature]; + + const entrySize = this.calculateEntrySize(extIds, JSON.stringify(entry)); + if (entrySize > environment.entrySizeLimit) { + return of({error: true, message: 'You have exceeded the entry size limit!'}); + } + + const data = JSON.stringify([extIds, entry]); + + return this.recordEntry(this.apiUrl, data); + } + + loadDIDForUpdate(didId: string): void { + this.id = didId; + + if (!this.updateDIDState.dids.find(d => d.didId === didId)) { + const didDocument: DIDDocument = this.vaultService.getDIDPublicInfo(didId).didDocument; + this.parseDocument(didDocument); + } + } + + revokeSigningKey(managementKeyId: string, updateEntry: UpdateEntryDocument): UpdateEntryDocument { + let revokedManagementKeys: RevokeModel[] = []; + if (updateEntry.revoke && updateEntry.revoke.managementKey) { + revokedManagementKeys = updateEntry.revoke.managementKey; + } + + if (!revokedManagementKeys.some(rm => rm.id === managementKeyId)) { + revokedManagementKeys.push({ id: managementKeyId }); + + let revokeObject = {}; + if (updateEntry.revoke) { + revokeObject = updateEntry.revoke; + } + + revokeObject['managementKey'] = revokedManagementKeys; + updateEntry.revoke = revokeObject; + } + + return updateEntry; + } + + clearData(): void { + this.id = undefined; + this.nonce = undefined; + } + + private recordEntry(apiUrl: string, data: string) { + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + + return this.http.post(apiUrl, data, httpOptions); + } + + private generateDocument(): DIDDocument { + const managementKeys = this.createDIDState.managementKeys.map(k => (this.buildManagementKeyEntryObject(k))); + + const didDocument: DIDDocument = { + 'didMethodVersion': this.didMethodSpecVersion, + 'managementKey': managementKeys + }; + + const didKeys = this.createDIDState.didKeys.map(k => (this.buildDidKeyEntryObject(k))); + if (didKeys.length > 0) { + didDocument.didKey = didKeys; + } + + const services = this.createDIDState.services.map(s => (this.buildServiceEntryObject(s))); + if (services.length > 0) { + didDocument.service = services; + } + + return didDocument; + } + + private generateUpdateEntry(): UpdateEntryDocument { + const didUpdateModel: UpdateDIDModel = this.updateDIDState.dids.find(d => d.didId === this.id); + + const newManagementKeys = this.getNew(didUpdateModel.originalManagementKeys, didUpdateModel.managementKeys); + const newDidKeys = this.getNew(didUpdateModel.originalDidKeys, didUpdateModel.didKeys); + const newServices = this.getNew(didUpdateModel.originalServices, didUpdateModel.services); + const revokedManagementKeys = this.getRevoked(didUpdateModel.originalManagementKeys, didUpdateModel.managementKeys); + const revokedDidKeys = this.getRevoked(didUpdateModel.originalDidKeys, didUpdateModel.didKeys); + const revokedServices = this.getRevoked(didUpdateModel.originalServices, didUpdateModel.services); + + const updateEntry: UpdateEntryDocument = {}; + + if (newManagementKeys.length > 0 || newDidKeys.length > 0 || newServices.length > 0) { + updateEntry['add'] = this.constructAddObject( + newManagementKeys as ManagementKeyModel[], + newDidKeys as DidKeyModel[], + newServices as ServiceModel[] + ); + } + + if (revokedManagementKeys.length > 0 || revokedDidKeys.length > 0 || revokedServices.length > 0) { + updateEntry['revoke'] = this.constructRevokeObject(revokedManagementKeys, revokedDidKeys, revokedServices); + } + + return updateEntry; + } + + private buildManagementKeyEntryObject(key: ManagementKeyModel): ManagementKeyEntryModel { + let keyEntryObject = this.buildKeyEntryObject(key); + keyEntryObject['priority'] = key.priority; + + if (key.priorityRequirement !== null) { + keyEntryObject['priorityRequirement'] = key.priorityRequirement; + } + + return keyEntryObject; + } + + private buildDidKeyEntryObject(key: DidKeyModel): DidKeyEntryModel { + let keyEntryObject = this.buildKeyEntryObject(key); + keyEntryObject['purpose'] = key.purpose; + + if (key.priorityRequirement !== null) { + keyEntryObject['priorityRequirement'] = key.priorityRequirement; + } + + return keyEntryObject; + } + + private buildKeyEntryObject(key: ManagementKeyModel | DidKeyModel): ManagementKeyEntryModel | DidKeyEntryModel { + const publicKeyProperty = key.type == SignatureType.RSA ? 'publicKeyPem' : 'publicKeyBase58'; + + const keyEntryObject = { + id: `${this.id}#${key.alias}`, + type: `${key.type}${this.VerificationKeySuffix}`, + controller: key.controller, + [publicKeyProperty]: key.publicKey + }; + + return keyEntryObject; + } + + private buildServiceEntryObject(service: ServiceModel): ServiceEntryModel { + let serviceEntryObject = { + id: `${this.id}#${service.alias}`, + type: service.type, + serviceEndpoint: service.endpoint + }; + + if (service.priorityRequirement !== null) { + serviceEntryObject['priorityRequirement'] = service.priorityRequirement; + } + + return serviceEntryObject; + } + + private getNew(original: any[], current: any[]) { + const _new = []; + const originalStrArray = original.map(e => JSON.stringify(e)); + + current.forEach(obj => { + if (!originalStrArray.includes(JSON.stringify(obj))) { + _new.push(obj); + } + }); + + return _new; + } + + private getRevoked(original: any[], current: any[]): RevokeModel[] { + const revoked: RevokeModel[] = []; + const currentStrArray = current.map(e => JSON.stringify(e)); + + original.forEach(obj => { + if (!currentStrArray.includes(JSON.stringify(obj))) { + revoked.push({ id: `${this.id}#${obj.alias}` }); + } + }); + + return revoked; + } + + private constructAddObject(newManagementKeys: ManagementKeyModel[], newDidKeys: DidKeyModel[], newServices: ServiceModel[]): {} { + const add = {}; + + if (newManagementKeys.length > 0) { + add['managementKey'] = newManagementKeys.map(k => (this.buildManagementKeyEntryObject(k))); + } + + if (newDidKeys.length > 0) { + add['didKey'] = newDidKeys.map(k => (this.buildDidKeyEntryObject(k))); + } + + if (newServices.length > 0) { + add['service'] = newServices.map(s => (this.buildServiceEntryObject(s))); + } + + return add; + } + + private constructRevokeObject(revokedManagementKeys: RevokeModel[], revokedDidKeys: RevokeModel[], revokedServices: RevokeModel[]): {} { + const revoke = {}; + + if (revokedManagementKeys.length > 0) { + revoke['managementKey'] = revokedManagementKeys; + } + + if (revokedDidKeys.length > 0) { + revoke['didKey'] = revokedDidKeys; + } + + if (revokedServices.length > 0) { + revoke['service'] = revokedServices; + } + + return revoke; + } + + private generateId(): string { + this.nonce = toHexString(nacl.randomBytes(32)); + + const chainId = calculateChainId([EntryType.CreateDIDEntry, this.entrySchemaVersion, this.nonce]); + this.id = `did:factom:${chainId}`; + return this.id; + } + + private calculateEntrySize(extIds: string[], content: string): number { + let totalEntrySize = 0; + const fixedHeaderSize = 35; + totalEntrySize += fixedHeaderSize + 2 * extIds.length; + + totalEntrySize += extIds.reduce((accumulator, currentExtId) => { + if (this.isHexadecimal(currentExtId)) { + return accumulator + currentExtId.length / 2; + } + + return accumulator + this.getBinarySize(currentExtId); + }, 0); + + totalEntrySize += this.getBinarySize(content); + + return totalEntrySize; + } + + private getBinarySize(string): number { + return Buffer.byteLength(string, 'utf8'); + } + + private isHexadecimal(str: string) { + const regexp = /^[0-9a-fA-F]+$/; + + if (regexp.test(str)) { + return true; + } + + return false; + } + + private parseDocument(didDocument: DIDDocument): void { + const managementKeys = this.extractManagementKeys(didDocument.managementKey); + + let didKeys = []; + if (didDocument.didKey) { + didKeys = this.extractDidKeys(didDocument.didKey); + } + + let services = []; + if (didDocument.service) { + services = this.extractServices(didDocument.service); + } + + const updateDIDModel = new UpdateDIDModel(this.id, managementKeys, didKeys, services); + this.store.dispatch(new InitializeDIDUpdate(updateDIDModel)); + } + + private extractManagementKeys(documentManagementKeys: ManagementKeyEntryModel[]): ManagementKeyModel[] { + return documentManagementKeys.map(k => new ManagementKeyModel( + k.id.split('#')[1], + k.priority, + k.type.split(this.VerificationKeySuffix)[0], + k.controller, + k.publicKeyBase58 ? k.publicKeyBase58 : k.publicKeyPem, + undefined, + k.priorityRequirement == undefined ? null : k.priorityRequirement + )); + } + + private extractDidKeys(documentDidKeys: DidKeyEntryModel[]): DidKeyModel[] { + return documentDidKeys.map(k => new DidKeyModel( + k.id.split('#')[1], + k.purpose, + k.type.split(this.VerificationKeySuffix)[0], + k.controller, + k.publicKeyBase58 ? k.publicKeyBase58 : k.publicKeyPem, + undefined, + k.priorityRequirement == undefined ? null : k.priorityRequirement + )); + } + + private extractServices(documentServices: ServiceEntryModel[]): ServiceModel[] { + return documentServices.map(s => new ServiceModel( + s.type, + s.serviceEndpoint, + s.id.split('#')[1], + s.priorityRequirement == undefined ? null : s.priorityRequirement + )); + } +} diff --git a/src/app/core/services/index.ts b/src/app/core/services/index.ts new file mode 100644 index 0000000..6cbac0d --- /dev/null +++ b/src/app/core/services/index.ts @@ -0,0 +1,15 @@ +import { DialogsService } from './dialogs/dialogs.service'; +import { DIDService } from './did/did.service'; +import { KeysService } from './keys/keys.service'; +import { SigningService } from './signing/signing.service'; +import { VaultService } from './vault/vault.service'; +import { WorkflowService } from './workflow/workflow.service'; + +export const services = [ + DialogsService, + DIDService, + KeysService, + SigningService, + VaultService, + WorkflowService +]; diff --git a/src/app/core/services/keys/keys.service.ts b/src/app/core/services/keys/keys.service.ts new file mode 100644 index 0000000..9cdab08 --- /dev/null +++ b/src/app/core/services/keys/keys.service.ts @@ -0,0 +1,112 @@ +declare const Buffer; +import * as nacl from 'tweetnacl/nacl-fast'; +import * as base58 from 'bs58'; +import * as elliptic from 'elliptic'; +import { defer, Observable } from 'rxjs'; +import { Injectable } from '@angular/core'; +import { Store } from '@ngrx/store'; + +import * as createDIDActions from '../../store/create-did/create-did.actions'; +import { AppState } from '../../store/app.state'; +import { DidKeyModel } from '../../models/did-key.model'; +import { DIDService } from '../did/did.service'; +import { exportPemKeys } from '../../utils/helpers'; +import { KeyPairModel } from '../../models/key-pair.model'; +import { ManagementKeyModel } from '../../models/management-key.model'; +import { PurposeType } from '../../enums/purpose-type'; +import { SignatureType } from '../../enums/signature-type'; + +@Injectable() +export class KeysService { + + constructor( + private didService: DIDService, + private store: Store) { + } + + generateKeyPair(type: SignatureType): Observable { + return defer(async () => { + if (type === SignatureType.EdDSA) { + return this.generateEdDSAKeyPair(); + } else if (type === SignatureType.ECDSA) { + return this.generateECDSAKeyPair(); + } else if (type === SignatureType.RSA) { + return await this.generateRSAKeyPair(); + } + }); + } + + autoGenerateKeys(): void { + let keyPair = this.generateEdDSAKeyPair(); + const managementKeyAlias = 'default-management-key'; + const managementKeyPriority = 0; + const managementKey = new ManagementKeyModel( + managementKeyAlias, + managementKeyPriority, + SignatureType.EdDSA, + this.didService.getId(), + keyPair.publicKey, + keyPair.privateKey + ); + + this.store.dispatch(new createDIDActions.AddManagementKey(managementKey)); + + keyPair = this.generateEdDSAKeyPair(); + const didKeyAlias = 'default-public-key'; + const didKey = new DidKeyModel( + didKeyAlias, + [PurposeType.PublicKey], + SignatureType.EdDSA, + this.didService.getId(), + keyPair.publicKey, + keyPair.privateKey + ); + + this.store.dispatch(new createDIDActions.AddDIDKey(didKey)); + } + + getPublicKeyFromPrivate(signatureType: SignatureType, privateKey: Buffer): string { + if (signatureType == SignatureType.EdDSA) { + const keyPair = nacl.sign.keyPair.fromSeed(privateKey); + return Buffer.from(keyPair.publicKey).toString('hex'); + } + } + + private generateEdDSAKeyPair(): KeyPairModel { + const seed = nacl.randomBytes(32); + const keyPair = nacl.sign.keyPair.fromSeed(seed); + + const publicKeyBase58 = base58.encode(Buffer.from(keyPair.publicKey)); + const privateKeyBase58 = base58.encode(Buffer.from(keyPair.secretKey)); + + return new KeyPairModel(publicKeyBase58, privateKeyBase58); + } + + private generateECDSAKeyPair(): KeyPairModel { + const ec = new elliptic.ec('secp256k1'); + const key = ec.genKeyPair(); + + const compressedPubPoint = key.getPublic(true, 'hex'); + const privateKey = key.getPrivate('hex'); + + const publicKeyBase58 = base58.encode(Buffer.from(compressedPubPoint, 'hex')); + const privateKeyBase58 = base58.encode(Buffer.from(privateKey, 'hex')); + + return new KeyPairModel(publicKeyBase58, privateKeyBase58); + } + + private async generateRSAKeyPair(): Promise { + const keyPair = await window.crypto.subtle.generateKey( + { + name: "RSASSA-PKCS1-v1_5", + modulusLength: 4096, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + hash: { name: "SHA-256" } + }, + true, + ["sign", "verify"]); + + const exportedKeys = await exportPemKeys(keyPair); + return new KeyPairModel(exportedKeys.publicKey, exportedKeys.privateKey); + } +} diff --git a/src/app/core/services/services.module.ts b/src/app/core/services/services.module.ts new file mode 100644 index 0000000..bfa3f4a --- /dev/null +++ b/src/app/core/services/services.module.ts @@ -0,0 +1,14 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; + +import { services } from './index'; + +@NgModule({ + providers: [ + ...services + ], + imports: [ + CommonModule + ] +}) +export class ServicesModule { } diff --git a/src/app/core/services/signing/signing.service.ts b/src/app/core/services/signing/signing.service.ts new file mode 100644 index 0000000..69437ed --- /dev/null +++ b/src/app/core/services/signing/signing.service.ts @@ -0,0 +1,316 @@ +declare const Buffer; +import * as base58 from 'bs58'; +import * as elliptic from 'elliptic'; +import * as encryptor from 'browser-passworder'; +import * as nacl from 'tweetnacl/nacl-fast'; +import * as naclUtil from 'tweetnacl-util'; +import { addressToKey } from 'factom'; +import { defer, Observable } from 'rxjs'; +import { Injectable, Output, EventEmitter } from '@angular/core'; +import { sha256 } from 'js-sha256'; + +import { convertPemToBinary, calculateDoubleSha256 } from '../../utils/helpers'; +import { DIDDocument } from '../../interfaces/did-document'; +import { DidKeyEntryModel } from '../../interfaces/did-key-entry'; +import { EntryType } from '../../enums/entry-type'; +import { environment } from 'src/environments/environment'; +import { ManagementKeyEntryModel } from '../../interfaces/management-key-entry'; +import { RequestKeyType } from '../../enums/request-key-type'; +import { RevokeModel } from '../../interfaces/revoke-model'; +import { ServiceEntryModel } from '../../interfaces/service-entry'; +import { SignatureDataModel } from '../../models/signature-data.model'; +import { SignatureResultModel } from '../../models/signature-result.model'; +import { SignatureType } from '../../enums/signature-type'; +import { UpdateEntryDocument } from '../../interfaces/update-entry-document'; +import { VaultService } from '../vault/vault.service'; + +const RSA_SIGNING_ALGO_NAME = "RSASSA-PKCS1-v1_5"; + +@Injectable() +export class SigningService { + private entrySchemaVersion = environment.entrySchemaVersion; + private pendingRequestsCount: number; + @Output() change: EventEmitter = new EventEmitter(); + + constructor(private vaultService: VaultService) { } + + signData(data: string, requestKeyType: string, signingKeyOrAddress: any, vaultPassword: string): Observable { + return defer(async () => { + try { + const vault = this.vaultService.getVault(); + const decryptedVault = await encryptor.decrypt(vaultPassword, vault); + const dataToSign = Buffer.from(sha256.update(data).digest()); + let signatureType; + let publicKey; + let signature; + + if (requestKeyType == RequestKeyType.DIDKey || requestKeyType == RequestKeyType.ManagementKey) { + const signingKeyIdParts = signingKeyOrAddress.id.split('#'); + const didId = signingKeyIdParts[0]; + const signingKeyAlias = signingKeyIdParts[1]; + const keys = requestKeyType == RequestKeyType.DIDKey + ? decryptedVault[didId].didKeys + : decryptedVault[didId].managementKeys; + + const privateKey = keys[signingKeyAlias]; + publicKey = signingKeyOrAddress.publicKeyBase58 + ? Buffer.from(base58.decode(signingKeyOrAddress.publicKeyBase58)) + : Buffer.from(convertPemToBinary(signingKeyOrAddress.publicKeyPem)); + + signatureType = signingKeyOrAddress.type.replace('VerificationKey', '') as SignatureType; + signature = Buffer.from(await this.getSignature(dataToSign, signatureType, privateKey)); + + if (signatureType == SignatureType.RSA) { + signatureType = RSA_SIGNING_ALGO_NAME; + } + + } else if (requestKeyType == RequestKeyType.EtherLink) { + const curve = elliptic.ec('secp256k1'); + const keyPair = curve.keyFromPrivate(decryptedVault[signingKeyOrAddress]); + publicKey = Buffer.concat([Buffer.from('04', 'hex'), Buffer.from(keyPair.getPublic('buffer'))]); + signatureType = SignatureType.ECDSA; + signature = keyPair.sign(dataToSign).toDER(); + } else { + // This is a signature with an FCT, EC or BlockSigning key. + let privateKey; + if (requestKeyType == RequestKeyType.FCT || requestKeyType == RequestKeyType.EC) { + privateKey = addressToKey(decryptedVault[signingKeyOrAddress]); + } else { + privateKey = Buffer.from(decryptedVault[signingKeyOrAddress], 'hex'); + } + + const keyPair = nacl.sign.keyPair.fromSeed(privateKey); + publicKey = Buffer.from(keyPair.publicKey); + signatureType = SignatureType.EdDSA; + signature = Buffer.from(nacl.sign.detached(dataToSign, keyPair.secretKey)); + } + + this.vaultService.incrementSignedRequests(); + + return new SignatureDataModel( + dataToSign, + publicKey, + signature, + signatureType + ); + } catch { + return undefined; + } + }); + } + + signPegNetTransaction(data: Buffer, publicAddress: string, vaultPassword: string): Observable { + return defer(async () => { + try { + const vault = this.vaultService.getVault(); + const decryptedVault = await encryptor.decrypt(vaultPassword, vault); + let privateKey; + let signatureType; + let publicKey; + let signature; + + if (publicAddress.startsWith('FA')) { + privateKey = addressToKey(decryptedVault[publicAddress]); + const keyPair = nacl.sign.keyPair.fromSeed(privateKey); + publicKey = Buffer.from(keyPair.publicKey); + signatureType = SignatureType.EdDSA; + signature = Buffer.from(nacl.sign.detached(data, keyPair.secretKey)); + } else { + const curve = elliptic.ec('secp256k1'); + const keyPair = curve.keyFromPrivate(decryptedVault[publicAddress]); + publicKey = Buffer.concat([Buffer.from('04', 'hex'), Buffer.from(keyPair.getPublic('buffer'))]); + signatureType = SignatureType.ECDSA; + signature = keyPair.sign(data).toDER(); + } + + this.vaultService.incrementSignedRequests(); + + return new SignatureDataModel( + data, + publicKey, + signature, + signatureType + ); + } catch { + return undefined; + } + }); + } + + signDIDEntry(fullDIDId: string, signatureType: SignatureType, entry: any, entryType: EntryType, vaultPassword: string): Observable { + return defer(async () => { + try { + const vault = this.vaultService.getVault(); + const decryptedVault = await encryptor.decrypt(vaultPassword, vault); + + const fullDIDIdParts = fullDIDId.split('#'); + const didId = fullDIDIdParts[0]; + const keyAlias = fullDIDIdParts[1]; + + const managementKeys = decryptedVault[didId].managementKeys; + const privateKey = managementKeys[keyAlias]; + + let contentToSign; + if (entryType == EntryType.DeactivateDIDEntry) { + contentToSign = Buffer.from(calculateDoubleSha256( + entryType.concat(this.entrySchemaVersion, fullDIDId) + )); + } else { + contentToSign = Buffer.from(calculateDoubleSha256( + entryType.concat(this.entrySchemaVersion, fullDIDId, JSON.stringify(entry)) + )); + } + + const signature = await this.getSignature(contentToSign, signatureType, privateKey); + const signatureBase64 = naclUtil.encodeBase64(signature); + + return new SignatureResultModel(true, 'Successfully signed the entry', signatureBase64); + } catch { + return new SignatureResultModel(false, 'Incorrect vault password'); + } + }); + } + + getAvailableManagementKeysForSigning(didId: string, entry: UpdateEntryDocument): ManagementKeyEntryModel[] { + const didDocument: DIDDocument = this.vaultService.getDIDPublicInfo(didId).didDocument; + let requiredPriority = 101; + + if (entry.revoke) { + const revokeObject = entry.revoke; + + if (revokeObject.managementKey) { + const revokedManagementKeysRequiredPriority = + this.getRevokedManagementKeysRequiredPriority(revokeObject.managementKey, didDocument.managementKey); + if (revokedManagementKeysRequiredPriority < requiredPriority) { + requiredPriority = revokedManagementKeysRequiredPriority; + } + } + + if (revokeObject.didKey) { + const revokedDidKeysRequiredPriority = this.getRevokedDidKeysRequiredPriority(revokeObject.didKey, didDocument.didKey); + if (revokedDidKeysRequiredPriority < requiredPriority) { + requiredPriority = revokedDidKeysRequiredPriority; + } + } + + if (revokeObject.service) { + const revokedServicesRequiredPriority = this.getRevokedServicesRequiredPriority(revokeObject.service, didDocument.service); + if (revokedServicesRequiredPriority < requiredPriority) { + requiredPriority = revokedServicesRequiredPriority; + } + } + } + + if (entry.add && entry.add.managementKey) { + const addedManagementKeysRequiredPriority = this.getAddedManagementKeysRequiredPriority(entry.add.managementKey); + if (addedManagementKeysRequiredPriority < requiredPriority) { + requiredPriority = addedManagementKeysRequiredPriority; + } + } + + return didDocument.managementKey.filter(mk => mk.priority <= requiredPriority); + } + + checkIfSigningKeyNeedsToBeRevoked(selectedManagementKey: ManagementKeyEntryModel, updateEntry: UpdateEntryDocument): boolean { + if (updateEntry.add && updateEntry.add.managementKey) { + const addedManagementKeys = updateEntry.add.managementKey; + if (addedManagementKeys.some(mk => mk.priority === selectedManagementKey.priority)) { + return true; + } + } + + return false; + } + + updatePendingRequestsCount(pendingRequestsCount: number) { + this.pendingRequestsCount = pendingRequestsCount; + this.change.emit(this.pendingRequestsCount); + } + + private getRevokedManagementKeysRequiredPriority(revokedManagementKeys: RevokeModel[], managementKeys: ManagementKeyEntryModel[]): number { + let requiredPriority = 101; + + for (const revokeManagementKeyObject of revokedManagementKeys) { + const managementKey = managementKeys.find(mk => mk.id === revokeManagementKeyObject.id); + + if (managementKey.priorityRequirement != undefined) { + if (managementKey.priorityRequirement < requiredPriority) { + requiredPriority = managementKey.priorityRequirement; + } + } else { + if (managementKey.priority < requiredPriority) { + requiredPriority = managementKey.priority; + } + } + } + + return requiredPriority; + } + + private getRevokedDidKeysRequiredPriority(revokedDidKeys: RevokeModel[], didKeys: DidKeyEntryModel[]): number { + let requiredPriority = 101; + + for (const revokeDidKeyObject of revokedDidKeys) { + const didKey = didKeys.find(dk => dk.id === revokeDidKeyObject.id); + + if (didKey.priorityRequirement != undefined && didKey.priorityRequirement < requiredPriority) { + requiredPriority = didKey.priorityRequirement; + } + } + + return requiredPriority; + } + + private getRevokedServicesRequiredPriority(revokedServices: RevokeModel[], services: ServiceEntryModel[]): number { + let requiredPriority = 101; + + for (const revokeServiceObject of revokedServices) { + const service = services.find(dk => dk.id === revokeServiceObject.id); + + if (service.priorityRequirement != undefined && service.priorityRequirement < requiredPriority) { + requiredPriority = service.priorityRequirement; + } + } + + return requiredPriority; + } + + private getAddedManagementKeysRequiredPriority(addedManagementKeys: ManagementKeyEntryModel[]): number { + let requiredPriority = 101; + + for (const addManagementKeyObject of addedManagementKeys) { + if (addManagementKeyObject.priority < requiredPriority) { + requiredPriority = addManagementKeyObject.priority; + } + } + + return requiredPriority; + } + + private async getSignature(dataToSign: Buffer, type: SignatureType, privateKey: string): Promise { + if (type === SignatureType.EdDSA) { + const secret = Buffer.from(base58.decode(privateKey)); + const keyPair = nacl.sign.keyPair.fromSecretKey(secret); + + return nacl.sign.detached(dataToSign, keyPair.secretKey); + } else if (type === SignatureType.ECDSA) { + const ec = elliptic.ec('secp256k1'); + const key = ec.keyFromPrivate(base58.decode(privateKey), 'hex'); + const signature = key.sign(dataToSign); + + return signature.toDER(); + } else if (type == SignatureType.RSA) { + const signAlgorithm = { + name: RSA_SIGNING_ALGO_NAME, + modulusLength: 4096, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + hash: { name: "SHA-256" } + }; + + const privateCryptoKey = await window.crypto.subtle.importKey("pkcs8", convertPemToBinary(privateKey) as ArrayBuffer, signAlgorithm, true, ["sign"]); + const signature = await window.crypto.subtle.sign(signAlgorithm, privateCryptoKey, dataToSign); + + return new Uint8Array(signature); + } + } +} diff --git a/src/app/core/services/vault/vault.service.ts b/src/app/core/services/vault/vault.service.ts new file mode 100644 index 0000000..ebc09d9 --- /dev/null +++ b/src/app/core/services/vault/vault.service.ts @@ -0,0 +1,973 @@ +import * as encryptor from 'browser-passworder'; +import { defer, Observable } from 'rxjs'; +import { Injectable } from '@angular/core'; +import LocalStorageStore from 'obs-store/lib/localStorage'; + +import { BackupResultModel } from '../../models/backup-result.model'; +import { convertECDSAPublicKeyToEtherLinkAddress, convertECDSAPublicKeyToEthereumAddress } from '../../utils/helpers'; +import { DIDDocument } from '../../interfaces/did-document'; +import { DidKeyEntryModel } from '../../interfaces/did-key-entry'; +import { DidKeyModel } from '../../models/did-key.model'; +import { environment } from 'src/environments/environment'; +import { FactomAddressType } from '../../enums/factom-address-type'; +import { KeyType } from '../../enums/key-type'; +import { ManagementKeyEntryModel } from '../../interfaces/management-key-entry'; +import { ManagementKeyModel } from '../../models/management-key.model'; +import { RestoreResultModel } from '../../models/restore-result.model'; +import { ResultModel } from '../../models/result.model'; +import { ServiceEntryModel } from '../../interfaces/service-entry'; +import { UpdateEntryDocument } from '../../interfaces/update-entry-document'; + +@Injectable() +export class VaultService { + private localStorageStore: LocalStorageStore; + private supportedLocalStorageVersions = ['1.0', '1.1']; + private defaultWhitelistedDomains = ['https://factomatic.io', 'https://pegnet.exchange']; + + constructor() { + this.localStorageStore = new LocalStorageStore({ storageKey: environment.storageKey }); + } + + createNewVault(password: string): Observable { + return defer(async () => { + const newVault = {}; + const encryptedVault = await encryptor.encrypt(password, newVault); + + this.localStorageStore.putState({ + version: environment.localStorageVersion, + vault: encryptedVault, + didsPublicInfo: JSON.stringify({}), + factomAddressesPublicInfo: JSON.stringify({ + [FactomAddressType.FCT]: {}, + [FactomAddressType.EtherLink]: {}, + [FactomAddressType.EC]: {} + }), + keysPublicInfo: JSON.stringify({ + [KeyType.BlockSigningKey]: {}, + }), + fctAddressesRequestWhitelistedDomains: JSON.stringify(this.defaultWhitelistedDomains), + etherLinkAddressesRequestWhitelistedDomains: JSON.stringify(this.defaultWhitelistedDomains), + ecAddressesRequestWhitelistedDomains: JSON.stringify([]), + blockSigningKeysRequestWhitelistedDomains: JSON.stringify([]), + createdDIDsCount: 0, + createdFCTAddressesCount: 0, + createdEtherLinkAddressesCount: 0, + createdECAddressesCount: 0, + signedRequestsCount: 0, + signedRequestsData: JSON.stringify(new Array(7).fill(0)), + dateOfLastShift: new Date().toDateString() + }); + + chrome.storage.local.set({ + fctAddresses: [], + etherLinkAddresses: [], + ecAddresses: [], + blockSigningKeys: [], + fctAddressesRequestWhitelistedDomains: this.defaultWhitelistedDomains, + etherLinkAddressesRequestWhitelistedDomains: this.defaultWhitelistedDomains, + ecAddressesRequestWhitelistedDomains: [], + blockSigningKeysRequestWhitelistedDomains: [] + }) + }); + } + + upgradeStorageVersion(): boolean { + const state = this.localStorageStore.getState(); + if (state.version !== environment.localStorageVersion) { + const upgradedState = this.upgradeLocalStorageVersion(state); + this.localStorageStore.putState(upgradedState); + this.setChromeStorageState(); + + return true; + } + + return false; + } + + saveDIDToVault( + didId: string, + didDocument: DIDDocument, + managementKeys: ManagementKeyModel[], + didKeys: DidKeyModel[], + vaultPassword: string): Observable { + return defer(async () => { + try { + const state = this.localStorageStore.getState(); + const decryptedVault = await encryptor.decrypt(vaultPassword, state.vault); + + const managementKeysVaultDict = {}; + for (const managementKey of managementKeys) { + managementKeysVaultDict[managementKey.alias] = managementKey.privateKey; + } + + const didKeysVaultDict = {}; + for (const didKey of didKeys) { + didKeysVaultDict[didKey.alias] = didKey.privateKey; + } + + decryptedVault[didId] = { + managementKeys: managementKeysVaultDict, + didKeys: didKeysVaultDict + }; + + const encryptedVault = await encryptor.encrypt(vaultPassword, decryptedVault); + + const createdDIDsCount = state.createdDIDsCount + 1; + const didNickname = `identity-${createdDIDsCount}`; + + const didsPublicInfo = JSON.parse(state.didsPublicInfo); + didsPublicInfo[didId] = { + nickname: didNickname, + didDocument: didDocument + }; + + const newState = Object.assign({}, state, { + vault: encryptedVault, + didsPublicInfo: JSON.stringify(didsPublicInfo), + createdDIDsCount: createdDIDsCount + }); + + this.localStorageStore.putState(newState); + + return new ResultModel(true, 'The Identity was saved successfully'); + } catch { + return new ResultModel(false, 'Incorrect vault password'); + } + }); + } + + saveDIDChangesToVault( + didId: string, + entry: UpdateEntryDocument, + managementKeys: ManagementKeyModel[], + didKeys: DidKeyModel[], + vaultPassword: string): Observable { + return defer(async () => { + try { + const state = this.localStorageStore.getState(); + const decryptedVault = await encryptor.decrypt(vaultPassword, state.vault); + const didsPublicInfo = JSON.parse(state.didsPublicInfo); + + const didNickname = didsPublicInfo[didId].nickname; + const didDocument: DIDDocument = didsPublicInfo[didId].didDocument; + const revokeObject = entry.revoke; + const addObject = entry.add; + + const updateManagementKeysResult = this.updateManagementKeys(revokeObject, addObject, managementKeys, decryptedVault[didId].managementKeys, didDocument.managementKey); + if (updateManagementKeysResult.anyChanges) { + decryptedVault[didId].managementKeys = updateManagementKeysResult.managementKeysVaultDict; + didDocument.managementKey = updateManagementKeysResult.managementKeysInDocument; + } + + const updateDidKeysResult = this.updateDidKeys(revokeObject, addObject, didKeys, decryptedVault[didId].didKeys, didDocument.didKey); + if (updateDidKeysResult.anyChanges) { + decryptedVault[didId].didKeys = updateDidKeysResult.didKeysVaultDict; + didDocument.didKey = updateDidKeysResult.didKeysInDocument; + } + + const updateServicesResult = this.updateServices(revokeObject, addObject, didDocument.service); + if (updateServicesResult.anyChanges) { + didDocument.service = updateServicesResult.servicesInDocument; + } + + const encryptedVault = await encryptor.encrypt(vaultPassword, decryptedVault); + + didsPublicInfo[didId] = { + nickname: didNickname, + didDocument: didDocument + }; + + const newState = Object.assign({}, state, { + vault: encryptedVault, + didsPublicInfo: JSON.stringify(didsPublicInfo) + }); + + this.localStorageStore.putState(newState); + + return new ResultModel(true, 'Vault state was updated successfully'); + } catch { + return new ResultModel(false, 'Incorrect vault password'); + } + }); + } + + removeDIDFromVault(didId: string, vaultPassword: string): Observable { + return defer(async () => { + try { + const state = this.localStorageStore.getState(); + let decryptedVault = await encryptor.decrypt(vaultPassword, state.vault); + + let didsPublicInfo = JSON.parse(state.didsPublicInfo); + delete didsPublicInfo[didId]; + delete decryptedVault[didId]; + + const encryptedVault = await encryptor.encrypt(vaultPassword, decryptedVault); + + const newState = Object.assign({}, state, { + vault: encryptedVault, + didsPublicInfo: JSON.stringify(didsPublicInfo) + }); + + this.localStorageStore.putState(newState); + + return new ResultModel(true, 'The Identity was deleted successfully'); + } catch { + return new ResultModel(false, 'Incorrect vault password'); + } + }); + } + + restoreVault(encryptedState: string, vaultPassword: string): Observable { + return defer(async () => { + try { + let decryptedState = await encryptor.decrypt(vaultPassword, encryptedState); + if (this.isValidState(decryptedState)) { + let versionUpgraded = false; + let restoreMessage = 'Vault successfully restored'; + + if (decryptedState.version !== environment.localStorageVersion) { + decryptedState = this.upgradeLocalStorageVersion(decryptedState); + versionUpgraded = true; + restoreMessage = undefined; + } + + this.localStorageStore.putState(decryptedState); + this.setChromeStorageState(); + this.updateSignedRequestsData(); + + return new RestoreResultModel(true, versionUpgraded, restoreMessage); + } + + return new RestoreResultModel(false, false, 'Invalid vault backup'); + } catch { + return new RestoreResultModel(false, false, 'Invalid vault password or type of vault backup'); + } + }); + } + + removeVault(): void { + localStorage.removeItem(environment.storageKey); + chrome.storage.local.clear(); + } + + canDecryptVault(vaultPassword: string): Observable { + return defer(async () => { + try { + const state = this.localStorageStore.getState(); + await encryptor.decrypt(vaultPassword, state.vault); + return new ResultModel(true, 'Correct vault password'); + } catch { + return new ResultModel(false, 'Incorrect vault password'); + } + }); + } + + changeVaultPassword(oldPassword: string, newPassword: string) { + return defer(async () => { + try { + const state = this.localStorageStore.getState(); + const decryptedVault = await encryptor.decrypt(oldPassword, state.vault); + const encryptedVault = await encryptor.encrypt(newPassword, decryptedVault); + + const newState = Object.assign({}, state, { + vault: encryptedVault + }); + + this.localStorageStore.putState(newState); + + return new ResultModel(true, 'Password was changed successfully'); + } catch { + return new ResultModel(false, 'Incorrect vault password'); + } + }); + } + + backupSingleDIDFromVault(didId: string, vaultPassword: string): Observable { + return defer(async () => { + try { + const state = this.localStorageStore.getState(); + const decryptedVault = await encryptor.decrypt(vaultPassword, state.vault); + const didKeys = decryptedVault[didId]; + const didKeysBackup = await encryptor.encrypt(vaultPassword, didKeys); + + return new BackupResultModel(true, 'Successful Identity backup', didKeysBackup); + } catch { + return new BackupResultModel(false, 'Incorrect vault password'); + } + }); + } + + updateSignedRequestsData() { + const state = this.localStorageStore.getState(); + let dateOfLastShift = state.dateOfLastShift; + let signedRequestsData = JSON.parse(state.signedRequestsData); + + let today = new Date().toDateString(); + if (dateOfLastShift !== today) { + const diffTime = Date.parse(today) - Date.parse(dateOfLastShift); + const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); + if (diffDays < 7) { + signedRequestsData = signedRequestsData.slice(diffDays, signedRequestsData.length).concat(new Array(diffDays).fill(0)); + } else { + signedRequestsData = new Array(7).fill(0); + } + + dateOfLastShift = today; + } + + const newState = Object.assign({}, state, { + signedRequestsData: JSON.stringify(signedRequestsData), + dateOfLastShift: dateOfLastShift + }); + + this.localStorageStore.putState(newState); + } + + incrementSignedRequests() { + const state = this.localStorageStore.getState(); + let signedRequestsData = JSON.parse(state.signedRequestsData); + signedRequestsData[signedRequestsData.length - 1] += 1; + + const newState = Object.assign({}, state, { + signedRequestsCount: state.signedRequestsCount + 1, + signedRequestsData: JSON.stringify(signedRequestsData) + }); + + this.localStorageStore.putState(newState); + } + + updateDIDNickname(didId: string, nickname: string) { + const state = this.localStorageStore.getState(); + const didsPublicInfo = JSON.parse(state.didsPublicInfo); + + didsPublicInfo[didId] = { + nickname: nickname, + didDocument: didsPublicInfo[didId].didDocument + }; + + const newState = Object.assign({}, state, { + didsPublicInfo: JSON.stringify(didsPublicInfo) + }); + + this.localStorageStore.putState(newState); + } + + importFactomAddress(type: FactomAddressType, publicAddress: string, privateAddress: string, vaultPassword: string, nickname?: string) { + return defer(async () => { + try { + const state = this.localStorageStore.getState(); + const decryptedVault = await encryptor.decrypt(vaultPassword, state.vault); + + decryptedVault[publicAddress] = privateAddress; + const encryptedVault = await encryptor.encrypt(vaultPassword, decryptedVault); + + let fctAddressesCount = state.createdFCTAddressesCount; + let etherLinkAddressesCount = state.createdEtherLinkAddressesCount; + let ecAddressesCount = state.createdECAddressesCount; + if (!nickname) { + nickname = (function(addressType) { + switch(addressType) { + case FactomAddressType.FCT: + return `fct-address-${++fctAddressesCount}`; + case FactomAddressType.EtherLink: + return `etherlink-address-${++etherLinkAddressesCount}`; + case FactomAddressType.EC: + return `ec-address-${++ecAddressesCount}`; + } + })(type) + } + + const factomAddressesPublicInfo = JSON.parse(state.factomAddressesPublicInfo); + factomAddressesPublicInfo[type] = Object.assign({}, factomAddressesPublicInfo[type], { + [publicAddress]: nickname + }); + + const newState = Object.assign({}, state, { + vault: encryptedVault, + factomAddressesPublicInfo: JSON.stringify(factomAddressesPublicInfo), + createdFCTAddressesCount: fctAddressesCount, + createdEtherLinkAddressesCount: etherLinkAddressesCount, + createdECAddressesCount: ecAddressesCount + }); + + this.localStorageStore.putState(newState); + + chrome.storage.local.get(['fctAddresses', 'etherLinkAddresses', 'ecAddresses'], function(addressesState) { + if (type === FactomAddressType.FCT) { + if (addressesState.fctAddresses) { + addressesState.fctAddresses = addressesState.fctAddresses.filter(addressObj => Object.keys(addressObj)[0] !== publicAddress); + addressesState.fctAddresses.push({[publicAddress]: nickname}); + } else { + addressesState.fctAddresses = [{[publicAddress]: nickname}]; + } + } else if (type === FactomAddressType.EtherLink) { + const etherLinkAddress = convertECDSAPublicKeyToEtherLinkAddress(publicAddress); + const ethereumAddress = convertECDSAPublicKeyToEthereumAddress(publicAddress); + + if (addressesState.etherLinkAddresses) { + addressesState.etherLinkAddresses = addressesState.etherLinkAddresses.filter(addressObj => addressObj.etherLinkAddress !== etherLinkAddress); + addressesState.etherLinkAddresses.push({etherLinkAddress, ethereumAddress, nickname}); + } else { + addressesState.etherLinkAddresses = [{etherLinkAddress, ethereumAddress, nickname}]; + } + } else if (type === FactomAddressType.EC) { + if (addressesState.ecAddresses) { + addressesState.ecAddresses = addressesState.ecAddresses.filter(addressObj => Object.keys(addressObj)[0] !== publicAddress); + addressesState.ecAddresses.push({[publicAddress]: nickname}); + } else { + addressesState.ecAddresses = [{[publicAddress]: nickname}]; + } + } + + chrome.storage.local.set(addressesState); + }); + + return new ResultModel(true, `${type} address was successfully imported`); + } catch { + return new ResultModel(false, 'Incorrect vault password'); + } + }); + } + + updateFactomAddressNickname(publicAddress: string, type: FactomAddressType, nickname: string) { + const state = this.localStorageStore.getState(); + const factomAddressesPublicInfo = JSON.parse(state.factomAddressesPublicInfo); + + if (factomAddressesPublicInfo[type][publicAddress]) { + factomAddressesPublicInfo[type][publicAddress] = nickname; + } + + chrome.storage.local.get(['fctAddresses', 'etherLinkAddresses', 'ecAddresses'], function(addressesState) { + if (type === FactomAddressType.FCT) { + let fctAddressesObj = addressesState.fctAddresses.find(addressObj => Object.keys(addressObj)[0] === publicAddress); + fctAddressesObj[publicAddress] = nickname; + } else if (type === FactomAddressType.EtherLink) { + const etherLinkAddress = convertECDSAPublicKeyToEtherLinkAddress(publicAddress); + let etherLinkAddressObj = addressesState.etherLinkAddresses.find(addressObj => addressObj.etherLinkAddress === etherLinkAddress); + etherLinkAddressObj.nickname = nickname; + } else if (type === FactomAddressType.EC) { + let ecAddressesObj = addressesState.ecAddresses.find(addressObj => Object.keys(addressObj)[0] === publicAddress); + ecAddressesObj[publicAddress] = nickname; + } + + chrome.storage.local.set(addressesState); + }); + + const newState = Object.assign({}, state, { + factomAddressesPublicInfo: JSON.stringify(factomAddressesPublicInfo) + }); + + this.localStorageStore.putState(newState); + } + + removeFactomAddress(publicAddress: string, type: FactomAddressType, vaultPassword: string) { + return defer(async () => { + try { + const state = this.localStorageStore.getState(); + const decryptedVault = await encryptor.decrypt(vaultPassword, state.vault); + + if (decryptedVault[publicAddress]) { + delete decryptedVault[publicAddress]; + } + + const encryptedVault = await encryptor.encrypt(vaultPassword, decryptedVault); + + const factomAddressesPublicInfo = JSON.parse(state.factomAddressesPublicInfo); + if (factomAddressesPublicInfo[type][publicAddress]) { + delete factomAddressesPublicInfo[type][publicAddress]; + } + + const newState = Object.assign({}, state, { + vault: encryptedVault, + factomAddressesPublicInfo: JSON.stringify(factomAddressesPublicInfo) + }); + + this.localStorageStore.putState(newState); + + chrome.storage.local.get(['fctAddresses', 'etherLinkAddresses', 'ecAddresses'], function(addressesState) { + if (type === FactomAddressType.FCT) { + addressesState.fctAddresses = addressesState.fctAddresses.filter(addressObj => Object.keys(addressObj)[0] !== publicAddress); + } else if (type === FactomAddressType.EtherLink) { + const etherLinkAddress = convertECDSAPublicKeyToEtherLinkAddress(publicAddress); + addressesState.etherLinkAddresses = addressesState.etherLinkAddresses.filter(addressObj => addressObj.etherLinkAddress !== etherLinkAddress); + } else if (type === FactomAddressType.EC) { + addressesState.ecAddresses = addressesState.ecAddresses.filter(addressObj => Object.keys(addressObj)[0] !== publicAddress); + } + + chrome.storage.local.set(addressesState); + }); + + return new ResultModel(true, `${type} address was successfully removed`); + } catch { + return new ResultModel(false, 'Incorrect vault password'); + } + }); + } + + importKey(type: KeyType, publicKey: string, privateKey: string, vaultPassword: string, nickname: string) { + return defer(async () => { + try { + const state = this.localStorageStore.getState(); + const decryptedVault = await encryptor.decrypt(vaultPassword, state.vault); + + decryptedVault[publicKey] = privateKey; + const encryptedVault = await encryptor.encrypt(vaultPassword, decryptedVault); + + const keysPublicInfo = JSON.parse(state.keysPublicInfo); + keysPublicInfo[type] = Object.assign({}, keysPublicInfo[type], { + [publicKey]: nickname + }); + + const newState = Object.assign({}, state, { + vault: encryptedVault, + keysPublicInfo: JSON.stringify(keysPublicInfo) + }); + + this.localStorageStore.putState(newState); + + chrome.storage.local.get(['blockSigningKeys'], function(state) { + if (state.blockSigningKeys) { + state.blockSigningKeys = state.blockSigningKeys.filter(keyObj => Object.keys(keyObj)[0] !== publicKey); + state.blockSigningKeys.push({[publicKey]: nickname}); + } else { + state.blockSigningKeys = [{[publicKey]: nickname}]; + } + + chrome.storage.local.set(state); + }); + + return new ResultModel(true, 'Key was successfully imported'); + } catch { + return new ResultModel(false, 'Incorrect vault password'); + } + }); + } + + updateKeyNickname(type: KeyType, publicKey: string, nickname: string) { + const state = this.localStorageStore.getState(); + const keysPublicInfo = JSON.parse(state.keysPublicInfo); + + if (keysPublicInfo[type][publicKey]) { + keysPublicInfo[type][publicKey] = nickname; + } + + const newState = Object.assign({}, state, { + keysPublicInfo: JSON.stringify(keysPublicInfo) + }); + + this.localStorageStore.putState(newState); + + chrome.storage.local.get(['blockSigningKeys'], function(state) { + let blockSigningKeyObj = state.blockSigningKeys.find(keyObj => Object.keys(keyObj)[0] === publicKey); + blockSigningKeyObj[publicKey] = nickname; + + chrome.storage.local.set(state); + }); + } + + removeKey(type: KeyType, publicKey: string, vaultPassword: string) { + return defer(async () => { + try { + const state = this.localStorageStore.getState(); + const decryptedVault = await encryptor.decrypt(vaultPassword, state.vault); + + if (decryptedVault[publicKey]) { + delete decryptedVault[publicKey]; + } + + const encryptedVault = await encryptor.encrypt(vaultPassword, decryptedVault); + + const keysPublicInfo = JSON.parse(state.keysPublicInfo); + if (keysPublicInfo[type][publicKey]) { + delete keysPublicInfo[type][publicKey]; + } + + const newState = Object.assign({}, state, { + vault: encryptedVault, + keysPublicInfo: JSON.stringify(keysPublicInfo) + }); + + this.localStorageStore.putState(newState); + + chrome.storage.local.get(['blockSigningKeys'], function(state) { + state.blockSigningKeys = state.blockSigningKeys.filter(keyObj => Object.keys(keyObj)[0] !== publicKey); + + chrome.storage.local.set(state); + }); + + return new ResultModel(true, 'Key was successfully removed'); + } catch { + return new ResultModel(false, 'Incorrect vault password'); + } + }); + } + + addWhitelistedDomain(requestType: string, domain: string) { + this.syncWhitelistedDomains(requestType, domain); + } + + removeWhitelistedDomain(requestType: string, domain: string) { + this.syncWhitelistedDomains(requestType, domain, true); + } + + getEncryptedState(vaultPassword: string): Observable { + return defer(async () => { + const decryptResult = await this.canDecryptVault(vaultPassword).toPromise(); + if (!decryptResult.success) { + return new BackupResultModel(false, decryptResult.message); + } + + const state = this.localStorageStore.getState(); + const backup = await encryptor.encrypt(vaultPassword, state); + + return new BackupResultModel(true, 'Successful vault backup', backup); + }); + } + + getPrivateKeyOrAddress(publicKeyOrAddress: string, vaultPassword: string) { + return defer(async () => { + try { + const state = this.localStorageStore.getState(); + const decryptedVault = await encryptor.decrypt(vaultPassword, state.vault); + + return new ResultModel(true, decryptedVault[publicKeyOrAddress]); + } catch { + return new ResultModel(false, 'Incorrect vault password'); + } + }); + } + + getVault(): string { + return this.localStorageStore.getState().vault; + } + + getAllDIDsPublicInfo() { + return JSON.parse(this.localStorageStore.getState().didsPublicInfo); + } + + getDIDsCount(): number { + return Object.keys(this.getAllDIDsPublicInfo()).length; + } + + getSignedRequestsCount(): number { + return this.localStorageStore.getState().signedRequestsCount; + } + + getSignedRequestsData(): number[] { + return JSON.parse(this.localStorageStore.getState().signedRequestsData); + } + + getDIDPublicInfo(didId: string) { + const didsPublicInfo = this.getAllDIDsPublicInfo(); + return didsPublicInfo[didId]; + } + + getFCTAddressesPublicInfo() { + return JSON.parse(this.localStorageStore.getState().factomAddressesPublicInfo)[FactomAddressType.FCT]; + } + + getEtherLinkAddressesPublicInfo() { + return JSON.parse(this.localStorageStore.getState().factomAddressesPublicInfo)[FactomAddressType.EtherLink]; + } + + getECAddressesPublicInfo() { + return JSON.parse(this.localStorageStore.getState().factomAddressesPublicInfo)[FactomAddressType.EC]; + } + + getBlockSigningKeysPublicInfo() { + return JSON.parse(this.localStorageStore.getState().keysPublicInfo)[KeyType.BlockSigningKey]; + } + + getFCTAddressesRequestWhitelistedDomains() { + return JSON.parse(this.localStorageStore.getState().fctAddressesRequestWhitelistedDomains); + } + + getEtherLinkAddressesRequestWhitelistedDomains() { + return JSON.parse(this.localStorageStore.getState().etherLinkAddressesRequestWhitelistedDomains); + } + + getECAddressesRequestWhitelistedDomains() { + return JSON.parse(this.localStorageStore.getState().ecAddressesRequestWhitelistedDomains); + } + + getBlockSigningKeysRequestWhitelistedDomains() { + return JSON.parse(this.localStorageStore.getState().blockSigningKeysRequestWhitelistedDomains); + } + + anyDIDsOrAddresses(): boolean { + return this.getDIDsCount() > 0 + || Object.keys(this.getBlockSigningKeysPublicInfo()).length > 0 + || Object.keys(this.getFCTAddressesPublicInfo()).length > 0 + || Object.keys(this.getEtherLinkAddressesPublicInfo()).length > 0 + || Object.keys(this.getECAddressesPublicInfo()).length > 0; + } + + vaultExists(): boolean { + try { + if (this.localStorageStore.getState().vault) { + return true; + } + + return false; + } catch { + return false; + } + } + + private updateManagementKeys( + revokeObject: any, + addObject: any, + managementKeys: ManagementKeyModel[], + managementKeysVaultDict: object, + managementKeysInDocument: ManagementKeyEntryModel[]) { + const anyRevokedManagementKeys = revokeObject != undefined && revokeObject.managementKey != undefined; + const anyAddedManagementKeys = addObject != undefined && addObject.managementKey != undefined; + + if (anyRevokedManagementKeys || anyAddedManagementKeys) { + for (const managementKey of managementKeys) { + if (!managementKey.privateKey) { + managementKey.privateKey = managementKeysVaultDict[managementKey.alias]; + } + } + + if (anyRevokedManagementKeys) { + for (const revokeKeyObject of revokeObject.managementKey) { + const keyAlias = revokeKeyObject.id.split('#')[1]; + delete managementKeysVaultDict[keyAlias]; + managementKeysInDocument = managementKeysInDocument.filter(k => k.id != revokeKeyObject.id); + } + } + + if (anyAddedManagementKeys) { + if (!managementKeysInDocument) { + managementKeysInDocument = []; + } + + for (const keyEntryModel of addObject.managementKey) { + const keyModel = managementKeys.find(k => k.alias === keyEntryModel.id.split('#')[1]); + managementKeysVaultDict[keyModel.alias] = keyModel.privateKey; + managementKeysInDocument.push(keyEntryModel); + } + } + + return { + anyChanges: true, + managementKeysVaultDict, + managementKeysInDocument + }; + } + + return { + anyChanges: false + }; + } + + private updateDidKeys( + revokeObject: any, + addObject: any, + didKeys: DidKeyModel[], + didKeysVaultDict: object, + didKeysInDocument: DidKeyEntryModel[]) { + const anyRevokedDidKeys = revokeObject != undefined && revokeObject.didKey != undefined; + const anyAddedDidKeys = addObject != undefined && addObject.didKey != undefined; + + if (anyRevokedDidKeys || anyAddedDidKeys) { + for (const didKey of didKeys) { + if (!didKey.privateKey) { + didKey.privateKey = didKeysVaultDict[didKey.alias]; + } + } + + if (anyRevokedDidKeys) { + for (const revokeKeyObject of revokeObject.didKey) { + const keyAlias = revokeKeyObject.id.split('#')[1]; + delete didKeysVaultDict[keyAlias]; + didKeysInDocument = didKeysInDocument.filter(k => k.id != revokeKeyObject.id); + } + } + + if (anyAddedDidKeys) { + if (!didKeysInDocument) { + didKeysInDocument = []; + } + + for (const keyEntryModel of addObject.didKey) { + const keyModel = didKeys.find(k => k.alias === keyEntryModel.id.split('#')[1]); + didKeysVaultDict[keyModel.alias] = keyModel.privateKey; + didKeysInDocument.push(keyEntryModel); + } + } + + return { + anyChanges: true, + didKeysVaultDict, + didKeysInDocument + }; + } + + return { + anyChanges: false + }; + } + + private updateServices( + revokeObject: any, + addObject: any, + servicesInDocument: ServiceEntryModel[]) { + const anyRevokedServices = revokeObject != undefined && revokeObject.service != undefined; + const anyAddedServices = addObject != undefined && addObject.service != undefined; + + if (anyRevokedServices || anyAddedServices) { + if (anyRevokedServices) { + for (const revokeServiceObject of revokeObject.service) { + servicesInDocument = servicesInDocument.filter(s => s.id != revokeServiceObject.id); + } + } + + if (anyAddedServices) { + if (!servicesInDocument) { + servicesInDocument = []; + } + + for (const serviceEntryModel of addObject.service) { + servicesInDocument.push(serviceEntryModel); + } + } + + return { + anyChanges: true, + servicesInDocument + }; + } + + return { + anyChanges: false + }; + } + + private isValidState(state: any): boolean { + if ((this.supportedLocalStorageVersions.includes(state.version)) + && state.vault + && state.didsPublicInfo + && state.factomAddressesPublicInfo + && state.createdDIDsCount >= 0 + && state.createdFCTAddressesCount >= 0 + && state.createdECAddressesCount >= 0 + && state.signedRequestsCount >= 0 + && state.signedRequestsData) { + return true; + } + + return false; + } + + private upgradeLocalStorageVersion(state: any) { + switch(state.version) { + case '1.0': + return this.upgradeStorageToVersion_1_1(state); + default: + break; + } + } + + private upgradeStorageToVersion_1_1(state: any) { + const addressesPublicInfo = Object.assign({}, + JSON.parse(state['factomAddressesPublicInfo']), + {[FactomAddressType.EtherLink]: {}}) + + return Object.assign({}, + state, + { + version: environment.localStorageVersion, + createdEtherLinkAddressesCount: 0, + fctAddressesRequestWhitelistedDomains: JSON.stringify(this.defaultWhitelistedDomains), + etherLinkAddressesRequestWhitelistedDomains: JSON.stringify(this.defaultWhitelistedDomains), + ecAddressesRequestWhitelistedDomains: JSON.stringify([]), + blockSigningKeysRequestWhitelistedDomains: JSON.stringify([]), + keysPublicInfo: JSON.stringify({[KeyType.BlockSigningKey]: {}}), + factomAddressesPublicInfo: JSON.stringify(addressesPublicInfo) + } + ); + } + + private setChromeStorageState() { + const fctAddressesRequestWhitelistedDomains = this.getFCTAddressesRequestWhitelistedDomains(); + const etherLinkAddressesRequestWhitelistedDomains = this.getEtherLinkAddressesRequestWhitelistedDomains(); + const ecAddressesRequestWhitelistedDomains = this.getECAddressesRequestWhitelistedDomains(); + const blockSigningKeysRequestWhitelistedDomains = this.getBlockSigningKeysRequestWhitelistedDomains(); + const fctAddressesPublicInfo = this.getFCTAddressesPublicInfo(); + const etherLinkAddressesPublicInfo = this.getEtherLinkAddressesPublicInfo(); + const ecAddressesPublicInfo = this.getECAddressesPublicInfo(); + const blockSigningKeysPublicInfo = this.getBlockSigningKeysPublicInfo(); + + let fctAddresses = []; + let etherLinkAddresses = []; + let ecAddresses = []; + let blockSigningKeys = []; + + for (const fctPublicAddress of Object.keys(fctAddressesPublicInfo)) { + fctAddresses.push({[fctPublicAddress]: fctAddressesPublicInfo[fctPublicAddress]}); + } + + for (const etherLinkPublicAddress of Object.keys(etherLinkAddressesPublicInfo)) { + const etherLinkAddress = convertECDSAPublicKeyToEtherLinkAddress(etherLinkPublicAddress); + const ethereumAddress = convertECDSAPublicKeyToEthereumAddress(etherLinkPublicAddress); + const nickname = etherLinkAddressesPublicInfo[etherLinkPublicAddress]; + + etherLinkAddresses.push({etherLinkAddress, ethereumAddress, nickname}); + } + + for (const ecPublicAddress of Object.keys(ecAddressesPublicInfo)) { + ecAddresses.push({[ecPublicAddress]: ecAddressesPublicInfo[ecPublicAddress]}); + } + + for (const publicKey of Object.keys(blockSigningKeysPublicInfo)) { + blockSigningKeys.push({[publicKey]: blockSigningKeysPublicInfo[publicKey]}); + } + + chrome.storage.local.set({ + fctAddressesRequestWhitelistedDomains, + etherLinkAddressesRequestWhitelistedDomains, + ecAddressesRequestWhitelistedDomains, + blockSigningKeysRequestWhitelistedDomains, + fctAddresses, + etherLinkAddresses, + ecAddresses, + blockSigningKeys + }); + } + + private syncWhitelistedDomains(requestType: string, domain: string, isRemoveAction: boolean = false) { + const state = this.localStorageStore.getState(); + const whitelistedDomainsKey = (function(requestType) { + switch(requestType) { + case FactomAddressType.FCT: + return 'fctAddressesRequestWhitelistedDomains'; + case FactomAddressType.EtherLink: + return 'etherLinkAddressesRequestWhitelistedDomains'; + case FactomAddressType.EC: + return 'ecAddressesRequestWhitelistedDomains'; + case KeyType.BlockSigningKey: + return 'blockSigningKeysRequestWhitelistedDomains'; + } + })(requestType) + + let whitelistedDomains = JSON.parse(state[whitelistedDomainsKey]); + if (isRemoveAction) { + whitelistedDomains = whitelistedDomains.filter(d => d !== domain); + } else if (!whitelistedDomains.includes(domain)) { + whitelistedDomains.push(domain); + } + + chrome.storage.local.get([whitelistedDomainsKey], function(state) { + state[whitelistedDomainsKey] = whitelistedDomains; + chrome.storage.local.set(state); + }); + + const newState = Object.assign({}, state, { + [whitelistedDomainsKey]: JSON.stringify(whitelistedDomains) + }); + + this.localStorageStore.putState(newState); + } +} diff --git a/src/app/core/services/workflow/workflow.service.ts b/src/app/core/services/workflow/workflow.service.ts new file mode 100644 index 0000000..ad97b4e --- /dev/null +++ b/src/app/core/services/workflow/workflow.service.ts @@ -0,0 +1,53 @@ +import { Injectable, Output, EventEmitter } from '@angular/core'; +import { Router } from '@angular/router'; +import { Store, select } from '@ngrx/store'; + +import { actionRoutes } from '../../enums/action-routes'; +import { AppState } from '../../store/app.state'; +import { MoveToStep } from '../../store/workflow/workflow.actions'; + +@Injectable() +export class WorkflowService { + private selectedAction: string; + private currentStepIndex: number; + private selectedActionRoutes; + @Output() closeFormEvent: EventEmitter = new EventEmitter(); + + constructor ( + private router: Router, + private store: Store) { + this.store.pipe(select(state => state.workflow)) + .subscribe(workflow => { + this.selectedAction = workflow.selectedAction; + this.currentStepIndex = workflow.currentStepIndex; + + if (this.selectedAction) { + this.selectedActionRoutes = actionRoutes[this.selectedAction]; + } + }); + } + + getSelectedAction(): string { + return this.selectedAction; + } + + getCurrentStepIndex(): number { + return this.currentStepIndex; + } + + moveToNextStep(queryParams?: any) { + const nextStepIndex = this.currentStepIndex + 1; + this.store.dispatch(new MoveToStep(nextStepIndex)); + this.router.navigate([this.selectedActionRoutes[nextStepIndex]], queryParams); + } + + moveToPreviousStep() { + const previousStepIndex = this.currentStepIndex - 1; + this.store.dispatch(new MoveToStep(previousStepIndex)); + this.router.navigate([this.selectedActionRoutes[previousStepIndex]]); + } + + closeUpdateForm() { + this.closeFormEvent.emit(); + } +} diff --git a/src/app/core/store/app.reducers.ts b/src/app/core/store/app.reducers.ts new file mode 100644 index 0000000..fd8b94d --- /dev/null +++ b/src/app/core/store/app.reducers.ts @@ -0,0 +1,12 @@ +import { ActionReducerMap } from '@ngrx/store'; + +import { AppState } from './app.state'; +import { createDIDReducers } from './create-did/create-did.reducers'; +import { updateDIDReducers } from './update-did/update-did.reducers'; +import { workflowReducers } from './workflow/workflow.reducers'; + +export const appReducers: ActionReducerMap = { + createDID: createDIDReducers, + updateDID: updateDIDReducers, + workflow: workflowReducers +}; diff --git a/src/app/core/store/app.state.ts b/src/app/core/store/app.state.ts new file mode 100644 index 0000000..214a84b --- /dev/null +++ b/src/app/core/store/app.state.ts @@ -0,0 +1,9 @@ +import { CreateDIDState } from './create-did/create-did.state'; +import { UpdateDIDState } from './update-did/update-did.state'; +import { WorkflowState } from './workflow/workflow.state'; + +export interface AppState { + readonly createDID: CreateDIDState; + readonly updateDID: UpdateDIDState; + readonly workflow: WorkflowState; +} diff --git a/src/app/core/store/create-did/create-did.actions.ts b/src/app/core/store/create-did/create-did.actions.ts new file mode 100644 index 0000000..3efd5c5 --- /dev/null +++ b/src/app/core/store/create-did/create-did.actions.ts @@ -0,0 +1,69 @@ +import { Action } from '@ngrx/store'; + +import { DidKeyModel } from '../../models/did-key.model'; +import { ManagementKeyModel } from '../../models/management-key.model'; +import { ServiceModel } from '../../models/service.model'; + +export enum CreateDIDActionTypes { + ADD_MANAGEMENT_KEY = '[Create DID] Add Management Key', + ADD_DID_KEY = '[Create DID] Add DID Key', + ADD_SERVICE = '[Create DID] Add Service', + UPDATE_MANAGEMENT_KEY = '[Create DID] Update Management Key', + UPDATE_DID_KEY = '[Create DID] Update DID Key', + REMOVE_MANAGEMENT_KEY = '[Create DID] Remove Management Key', + REMOVE_DID_KEY = '[Create DID] Remove DID Key', + REMOVE_SERVICE = '[Create DID] Remove Service', + CLEAR_CREATE_DID_STATE = '[Create DID] Clear Create DID State' +} + +export class AddManagementKey implements Action { + readonly type: string = CreateDIDActionTypes.ADD_MANAGEMENT_KEY; + + constructor (public payload: ManagementKeyModel) { } +} + +export class AddDIDKey implements Action { + readonly type: string = CreateDIDActionTypes.ADD_DID_KEY; + + constructor (public payload: DidKeyModel) { } +} + +export class AddService implements Action { + readonly type: string = CreateDIDActionTypes.ADD_SERVICE; + + constructor (public payload: ServiceModel) { } +} + +export class UpdateManagementKey implements Action { + readonly type: string = CreateDIDActionTypes.UPDATE_MANAGEMENT_KEY; + + constructor (public payload: ManagementKeyModel) { } +} + +export class UpdateDIDKey implements Action { + readonly type: string = CreateDIDActionTypes.UPDATE_DID_KEY; + + constructor (public payload: DidKeyModel) { } +} + +export class RemoveManagementKey implements Action { + readonly type: string = CreateDIDActionTypes.REMOVE_MANAGEMENT_KEY; + + constructor (public payload: ManagementKeyModel) { } +} + +export class RemoveDIDKey implements Action { + readonly type: string = CreateDIDActionTypes.REMOVE_DID_KEY; + + constructor (public payload: DidKeyModel) { } +} + +export class RemoveService implements Action { + readonly type: string = CreateDIDActionTypes.REMOVE_SERVICE; + + constructor (public payload: ServiceModel) { } +} + +export class ClearCreateDIDState implements Action { + readonly type: string = CreateDIDActionTypes.CLEAR_CREATE_DID_STATE; +} \ No newline at end of file diff --git a/src/app/core/store/create-did/create-did.reducers.ts b/src/app/core/store/create-did/create-did.reducers.ts new file mode 100644 index 0000000..af7b08c --- /dev/null +++ b/src/app/core/store/create-did/create-did.reducers.ts @@ -0,0 +1,92 @@ +import { CreateDIDActionTypes } from './create-did.actions'; +import { CreateDIDState } from './create-did.state'; +import { DidKeyModel } from '../../models/did-key.model'; +import { ManagementKeyModel } from '../../models/management-key.model'; +import { ServiceModel } from '../../models/service.model'; + +const initialState: CreateDIDState = { + managementKeys: [], + didKeys: [], + services: [] +}; + +function addManagementKey(state: CreateDIDState, managementKey: ManagementKeyModel) { + return Object.assign({}, state, { + managementKeys: [...state.managementKeys, managementKey] + }); +} + +function addDIDKey(state: CreateDIDState, didKey: DidKeyModel) { + return Object.assign({}, state, { + didKeys: [...state.didKeys, didKey] + }); +} + +function addService(state: CreateDIDState, service: ServiceModel) { + return Object.assign({}, state, { + services: [...state.services, service] + }); +} + +function updateManagementKey(state: CreateDIDState, key: ManagementKeyModel) { + const managementKeys = state.managementKeys.slice(); + const managementKeyIndex = managementKeys.findIndex(k => k.publicKey === key.publicKey); + managementKeys[managementKeyIndex] = key; + + return Object.assign({}, state, { + managementKeys: managementKeys + }); +} + +function updateDidKey(state: CreateDIDState, key: DidKeyModel) { + const didKeys = state.didKeys.slice(); + const didKeyIndex = didKeys.findIndex(k => k.publicKey === key.publicKey); + didKeys[didKeyIndex] = key; + + return Object.assign({}, state, { + didKeys: didKeys + }); +} + +function removeManagementKey(state: CreateDIDState, key: ManagementKeyModel) { + return Object.assign({}, state, { + managementKeys: state.managementKeys.filter(k => k.publicKey !== key.publicKey) + }); +} + +function removeDIDKey(state: CreateDIDState, key: DidKeyModel) { + return Object.assign({}, state, { + didKeys: state.didKeys.filter(k => k.publicKey !== key.publicKey) + }); +} + +function removeService(state: CreateDIDState, service: ServiceModel) { + return Object.assign({}, state, { + services: state.services.filter(s => s !== service) + }); +} + +export function createDIDReducers(state: CreateDIDState = initialState, action) { + switch (action.type) { + case CreateDIDActionTypes.ADD_MANAGEMENT_KEY: + return addManagementKey(state, action.payload); + case CreateDIDActionTypes.ADD_DID_KEY: + return addDIDKey(state, action.payload); + case CreateDIDActionTypes.ADD_SERVICE: + return addService(state, action.payload); + case CreateDIDActionTypes.UPDATE_MANAGEMENT_KEY: + return updateManagementKey(state, action.payload); + case CreateDIDActionTypes.UPDATE_DID_KEY: + return updateDidKey(state, action.payload); + case CreateDIDActionTypes.REMOVE_MANAGEMENT_KEY: + return removeManagementKey(state, action.payload); + case CreateDIDActionTypes.REMOVE_DID_KEY: + return removeDIDKey(state, action.payload); + case CreateDIDActionTypes.REMOVE_SERVICE: + return removeService(state, action.payload); + case CreateDIDActionTypes.CLEAR_CREATE_DID_STATE: + return initialState; + default: + return state; + } +} \ No newline at end of file diff --git a/src/app/core/store/create-did/create-did.state.ts b/src/app/core/store/create-did/create-did.state.ts new file mode 100644 index 0000000..e38c01c --- /dev/null +++ b/src/app/core/store/create-did/create-did.state.ts @@ -0,0 +1,9 @@ +import { DidKeyModel } from '../../models/did-key.model'; +import { ManagementKeyModel } from '../../models/management-key.model'; +import { ServiceModel } from '../../models/service.model'; + +export interface CreateDIDState { + readonly managementKeys: ManagementKeyModel[]; + readonly didKeys: DidKeyModel[]; + readonly services: ServiceModel[]; +} diff --git a/src/app/core/store/update-did/update-did.actions.ts b/src/app/core/store/update-did/update-did.actions.ts new file mode 100644 index 0000000..4e07416 --- /dev/null +++ b/src/app/core/store/update-did/update-did.actions.ts @@ -0,0 +1,86 @@ +import { Action } from "@ngrx/store"; + +import { DidKeyModel } from '../../models/did-key.model'; +import { ManagementKeyModel } from '../../models/management-key.model'; +import { ServiceModel } from '../../models/service.model'; +import { UpdateDIDModel } from '../../models/update-did.model'; + +export enum UpdateDIDActionTypes { + INITIALIZE_DID_UPDATE = '[Update DID] Initialize DID Update', + ADD_MANAGEMENT_KEY = '[Update DID] Add Management Key', + ADD_DID_KEY = '[Update DID] Add DID Key', + ADD_SERVICE = '[Update DID] Add Service', + UPDATE_MANAGEMENT_KEY = '[Update DID] Update Management Key', + UPDATE_DID_KEY = '[Update DID] Update DID Key', + REMOVE_MANAGEMENT_KEY = '[Update DID] Remove Management Key', + REMOVE_DID_KEY = '[Update DID] Remove DID Key', + REMOVE_SERVICE = '[Update DID] Remove Service', + CANCEL_CHANGES = '[Update DID] Cancel Changes', + COMPLETE_DID_UPDATE = '[Update DID] Complete DID Update' +} + +export class InitializeDIDUpdate implements Action { + readonly type: string = UpdateDIDActionTypes.INITIALIZE_DID_UPDATE; + + constructor (public payload: UpdateDIDModel) { } +} + +export class AddManagementKey implements Action { + readonly type: string = UpdateDIDActionTypes.ADD_MANAGEMENT_KEY; + + constructor (public didId: string, public payload: ManagementKeyModel) { } +} + +export class AddDIDKey implements Action { + readonly type: string = UpdateDIDActionTypes.ADD_DID_KEY; + + constructor (public didId: string, public payload: DidKeyModel) { } +} + +export class AddService implements Action { + readonly type: string = UpdateDIDActionTypes.ADD_SERVICE; + + constructor (public didId: string, public payload: ServiceModel) { } +} + +export class UpdateManagementKey implements Action { + readonly type: string = UpdateDIDActionTypes.UPDATE_MANAGEMENT_KEY; + + constructor (public didId: string, public payload: ManagementKeyModel) { } +} + +export class UpdateDIDKey implements Action { + readonly type: string = UpdateDIDActionTypes.UPDATE_DID_KEY; + + constructor (public didId: string, public payload: DidKeyModel) { } +} + +export class RemoveManagementKey implements Action { + readonly type: string = UpdateDIDActionTypes.REMOVE_MANAGEMENT_KEY; + + constructor (public didId: string, public payload: ManagementKeyModel) { } +} + +export class RemoveDIDKey implements Action { + readonly type: string = UpdateDIDActionTypes.REMOVE_DID_KEY; + + constructor (public didId: string, public payload: DidKeyModel) { } +} + +export class RemoveService implements Action { + readonly type: string = UpdateDIDActionTypes.REMOVE_SERVICE; + + constructor (public didId: string, public payload: ServiceModel) { } +} + +export class CancelChanges implements Action { + readonly type: string = UpdateDIDActionTypes.CANCEL_CHANGES; + + constructor (public didId: string) { } +} + +export class CompleteDIDUpdate implements Action { + readonly type: string = UpdateDIDActionTypes.COMPLETE_DID_UPDATE; + + constructor (public didId: string) { } +} \ No newline at end of file diff --git a/src/app/core/store/update-did/update-did.reducers.ts b/src/app/core/store/update-did/update-did.reducers.ts new file mode 100644 index 0000000..2b32547 --- /dev/null +++ b/src/app/core/store/update-did/update-did.reducers.ts @@ -0,0 +1,190 @@ +import { DidKeyModel } from '../../models/did-key.model'; +import { ManagementKeyModel } from '../../models/management-key.model'; +import { ServiceModel } from '../../models/service.model'; +import { UpdateDIDActionTypes } from './update-did.actions'; +import { UpdateDIDState } from './update-did.state'; +import { UpdateDIDModel } from '../../models/update-did.model'; + +const initialState: UpdateDIDState = { + dids: [], + didsWithPendingChanges: [] +}; + +function initializeDIDUpdate(state: UpdateDIDState, did: UpdateDIDModel) { + return Object.assign({}, state, { + dids: [...state.dids, did] + }); +} + +function addManagementKey(state: UpdateDIDState, didId: string, managementKey: ManagementKeyModel) { + const didsCopy = state.dids.slice(); + const did = didsCopy.find(d => d.didId === didId); + if (did) { + did.managementKeys = [...did.managementKeys, managementKey]; + } + + return Object.assign({}, state, { + dids: didsCopy, + didsWithPendingChanges: updatePendingChanges(state, didId) + }); +} + +function addDIDKey(state: UpdateDIDState, didId: string, didKey: DidKeyModel) { + const didsCopy = state.dids.slice(); + const did = didsCopy.find(d => d.didId === didId); + if (did) { + did.didKeys = [...did.didKeys, didKey]; + } + + return Object.assign({}, state, { + dids: didsCopy, + didsWithPendingChanges: updatePendingChanges(state, didId) + }); +} + +function addService(state: UpdateDIDState, didId: string, service: ServiceModel) { + const didsCopy = state.dids.slice(); + const did = didsCopy.find(d => d.didId === didId); + if (did) { + did.services = [...did.services, service]; + } + + return Object.assign({}, state, { + dids: didsCopy, + didsWithPendingChanges: updatePendingChanges(state, didId) + }); +} + +function updateManagementKey(state: UpdateDIDState, didId: string, key: ManagementKeyModel) { + const didsCopy = state.dids.slice(); + const did = didsCopy.find(d => d.didId === didId); + if (did) { + const managementKeyIndex = did.managementKeys.findIndex(mk => mk.publicKey === key.publicKey); + const managementKeysCopy = did.managementKeys.slice(); + managementKeysCopy[managementKeyIndex] = key; + did.managementKeys = managementKeysCopy; + } + + return Object.assign({}, state, { + dids: didsCopy, + didsWithPendingChanges: updatePendingChanges(state, didId) + }); +} + +function updateDidKey(state: UpdateDIDState, didId: string, key: DidKeyModel) { + const didsCopy = state.dids.slice(); + const did = didsCopy.find(d => d.didId === didId); + if (did) { + const didKeyIndex = did.didKeys.findIndex(dk => dk.publicKey === key.publicKey); + const didKeysCopy = did.didKeys.slice(); + didKeysCopy[didKeyIndex] = key; + did.didKeys = didKeysCopy; + } + + return Object.assign({}, state, { + dids: didsCopy, + didsWithPendingChanges: updatePendingChanges(state, didId) + }); +} + +function removeManagementKey(state: UpdateDIDState, didId: string, key: ManagementKeyModel) { + const didsCopy = state.dids.slice(); + const did = didsCopy.find(d => d.didId === didId); + if (did) { + did.managementKeys = did.managementKeys.filter(mk => mk.publicKey !== key.publicKey); + } + + return Object.assign({}, state, { + dids: didsCopy, + didsWithPendingChanges: updatePendingChanges(state, didId) + }); +} + +function removeDidKey(state: UpdateDIDState, didId: string, key: DidKeyModel) { + const didsCopy = state.dids.slice(); + const did = didsCopy.find(d => d.didId === didId); + if (did) { + did.didKeys = did.didKeys.filter(dk => dk.publicKey !== key.publicKey); + } + + return Object.assign({}, state, { + dids: didsCopy, + didsWithPendingChanges: updatePendingChanges(state, didId) + }); +} + +function removeService(state: UpdateDIDState, didId: string, service: ServiceModel) { + const didsCopy = state.dids.slice(); + const did = didsCopy.find(d => d.didId === didId); + if (did) { + did.services = did.services.filter(s => s !== service); + } + + return Object.assign({}, state, { + dids: didsCopy, + didsWithPendingChanges: updatePendingChanges(state, didId) + }); +} + +function cancelChanges(state: UpdateDIDState, didId: string) { + const didsCopy = state.dids.slice(); + const did = didsCopy.find(d => d.didId === didId); + if (did) { + did.managementKeys = did.originalManagementKeys.slice(); + did.didKeys = did.originalDidKeys.slice(); + did.services = did.originalServices.slice(); + } + + return Object.assign({}, state, { + dids: didsCopy, + didsWithPendingChanges: state.didsWithPendingChanges.filter(id => id !== didId) + }); +} + +function completeDIDUpdate(state: UpdateDIDState, didId: string) { + const didsCopy = state.dids.filter(d => d.didId !== didId); + const didsWithPendingChangesCopy = state.didsWithPendingChanges.filter(id => id !== didId); + + return Object.assign({}, state, { + dids: didsCopy, + didsWithPendingChanges: didsWithPendingChangesCopy + }); +} + +function updatePendingChanges(state: UpdateDIDState, didId: string) { + let didsWithPendingChangesCopy = state.didsWithPendingChanges.slice(); + if (!didsWithPendingChangesCopy.includes(didId)) { + didsWithPendingChangesCopy.push(didId); + } + + return didsWithPendingChangesCopy; +} + +export function updateDIDReducers(state: UpdateDIDState = initialState, action): UpdateDIDState { + switch (action.type) { + case UpdateDIDActionTypes.INITIALIZE_DID_UPDATE: + return initializeDIDUpdate(state, action.payload); + case UpdateDIDActionTypes.ADD_MANAGEMENT_KEY: + return addManagementKey(state, action.didId, action.payload); + case UpdateDIDActionTypes.ADD_DID_KEY: + return addDIDKey(state, action.didId, action.payload); + case UpdateDIDActionTypes.ADD_SERVICE: + return addService(state, action.didId, action.payload); + case UpdateDIDActionTypes.UPDATE_MANAGEMENT_KEY: + return updateManagementKey(state, action.didId, action.payload); + case UpdateDIDActionTypes.UPDATE_DID_KEY: + return updateDidKey(state, action.didId, action.payload); + case UpdateDIDActionTypes.REMOVE_MANAGEMENT_KEY: + return removeManagementKey(state, action.didId, action.payload); + case UpdateDIDActionTypes.REMOVE_DID_KEY: + return removeDidKey(state, action.didId, action.payload); + case UpdateDIDActionTypes.REMOVE_SERVICE: + return removeService(state, action.didId, action.payload); + case UpdateDIDActionTypes.CANCEL_CHANGES: + return cancelChanges(state, action.didId); + case UpdateDIDActionTypes.COMPLETE_DID_UPDATE: + return completeDIDUpdate(state, action.didId); + default: + return state; + } +} \ No newline at end of file diff --git a/src/app/core/store/update-did/update-did.state.ts b/src/app/core/store/update-did/update-did.state.ts new file mode 100644 index 0000000..ef2b9b6 --- /dev/null +++ b/src/app/core/store/update-did/update-did.state.ts @@ -0,0 +1,6 @@ +import { UpdateDIDModel } from '../../models/update-did.model'; + +export interface UpdateDIDState { + readonly dids: UpdateDIDModel[]; + readonly didsWithPendingChanges: string[]; +} \ No newline at end of file diff --git a/src/app/core/store/workflow/workflow.actions.ts b/src/app/core/store/workflow/workflow.actions.ts new file mode 100644 index 0000000..7309c76 --- /dev/null +++ b/src/app/core/store/workflow/workflow.actions.ts @@ -0,0 +1,28 @@ +import { Action } from '@ngrx/store'; + +export enum WorkflowActionTypes { + CLEAR_WORKFLOW_STATE = '[Workflow] Clear Workflow State', + MOVE_TO_STEP = '[Workflow] Move To Step', + SELECT_ACTION = '[Workflow] Select Action', + CLOSE_FORM_SCREEN = '[Workflow] Close Form Screen' +} + +export class SelectAction implements Action { + readonly type: string = WorkflowActionTypes.SELECT_ACTION; + + constructor (public payload: string) { } +} + +export class MoveToStep implements Action { + readonly type: string = WorkflowActionTypes.MOVE_TO_STEP; + + constructor (public payload: number) { } +} + +export class ClearWorkflowState implements Action { + readonly type: string = WorkflowActionTypes.CLEAR_WORKFLOW_STATE; +} + +export class CloseFormScreen implements Action { + readonly type: string = WorkflowActionTypes.CLOSE_FORM_SCREEN; +} diff --git a/src/app/core/store/workflow/workflow.reducers.ts b/src/app/core/store/workflow/workflow.reducers.ts new file mode 100644 index 0000000..faadece --- /dev/null +++ b/src/app/core/store/workflow/workflow.reducers.ts @@ -0,0 +1,30 @@ +import { WorkflowState } from './workflow.state'; +import { WorkflowActionTypes } from './workflow.actions'; + +const initialState = { + selectedAction: undefined, + currentStepIndex: 0, + closeFormScreen: false +}; + +export function workflowReducers(state: WorkflowState = initialState, action) { + switch (action.type) { + case WorkflowActionTypes.CLEAR_WORKFLOW_STATE: + return initialState; + case WorkflowActionTypes.MOVE_TO_STEP: + return Object.assign({}, state, { + currentStepIndex: action.payload + }); + case WorkflowActionTypes.SELECT_ACTION: + return Object.assign({}, state, { + selectedAction: action.payload, + currentStepIndex: 0 + }); + case WorkflowActionTypes.CLOSE_FORM_SCREEN: + return Object.assign({}, state, { + closeFormScreen: true + }); + default: + return state; + } +} diff --git a/src/app/core/store/workflow/workflow.state.ts b/src/app/core/store/workflow/workflow.state.ts new file mode 100644 index 0000000..9074224 --- /dev/null +++ b/src/app/core/store/workflow/workflow.state.ts @@ -0,0 +1,5 @@ +export interface WorkflowState { + readonly selectedAction: string; + readonly currentStepIndex: number; + readonly closeFormScreen: boolean; +} diff --git a/src/app/core/utils/alias.validator.ts b/src/app/core/utils/alias.validator.ts new file mode 100644 index 0000000..77dff40 --- /dev/null +++ b/src/app/core/utils/alias.validator.ts @@ -0,0 +1,72 @@ +import { Directive } from '@angular/core'; +import { NG_VALIDATORS, FormControl, ValidatorFn, Validator } from '@angular/forms'; +import { Store, select } from '@ngrx/store'; + +import { ActionType } from '../enums/action-type'; +import { AppState } from 'src/app/core/store/app.state'; +import { DidKeyModel } from '../models/did-key.model'; +import { DIDService } from '../services/did/did.service'; +import { ManagementKeyModel } from '../models/management-key.model'; + +@Directive({ + // tslint:disable-next-line:directive-selector + selector: '[aliasvalidator][ngModel]', + providers: [ + { + provide: NG_VALIDATORS, + useExisting: AliasValidator, + multi: true + } + ] +}) +export class AliasValidator implements Validator { + validator: ValidatorFn; + private managementKeys: ManagementKeyModel[]; + private didKeys: DidKeyModel[]; + private originalAlias: string; + + constructor(private store: Store, private didService: DIDService) { + const subscription = this.store + .pipe(select(state => state)) + .subscribe(state => { + if (state.workflow.selectedAction === ActionType.CreateAdvanced) { + this.managementKeys = state.createDID.managementKeys; + this.didKeys = state.createDID.didKeys; + } else { + const didId = this.didService.getId(); + const didUpdateModel = state.updateDID.dids.find(d => d.didId === didId); + + this.managementKeys = didUpdateModel.managementKeys; + this.didKeys = didUpdateModel.didKeys; + } + }); + + subscription.unsubscribe(); + + this.validator = this.aliasValidator(); + } + + validate(c: FormControl) { + return this.validator(c); + } + + aliasValidator(): ValidatorFn { + return (c: FormControl) => { + if (!this.originalAlias) { + this.originalAlias = c.value; + return null; + } + + if ((!this.managementKeys.find(k => k.alias === c.value) + && !this.didKeys.find(k => k.alias === c.value)) || this.originalAlias === c.value) { + return null; + } + + return { + aliasvalidator: { + valid: false + } + }; + }; + } +} diff --git a/src/app/core/utils/customValidators.ts b/src/app/core/utils/customValidators.ts new file mode 100644 index 0000000..68bb1e9 --- /dev/null +++ b/src/app/core/utils/customValidators.ts @@ -0,0 +1,52 @@ +import { AbstractControl, FormGroup, ValidatorFn } from '@angular/forms'; + +import { DidKeyModel } from '../models/did-key.model'; +import { ManagementKeyModel } from '../models/management-key.model'; +import { ServiceModel } from '../models/service.model'; + +export default class CustomValidators { + static passwordsDoMatch(createFormGroup: FormGroup) { + const password = createFormGroup.controls.password.value; + const repeatPassword = createFormGroup.controls.confirmPassword.value; + + if (!repeatPassword) { + return null; + } + + if (repeatPassword !== password) { + return { + passwordsMismatch: true + }; + } + + return null; + } + + static uniqueKeyAlias(managementKeys: ManagementKeyModel[], didKeys: DidKeyModel[], originalValue?: string): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } | null => { + const hasOriginalValue = originalValue && originalValue.length > 0; + if (control.value !== null && (!hasOriginalValue || originalValue !== control.value)) { + if (!managementKeys.find(k => k.alias === control.value) + && !didKeys.find(k => k.alias === control.value)) { + return null; + } + + return {taken: true}; + } + + return null; + }; + } + + static uniqueServiceAlias(services: ServiceModel[]): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } | null => { + if (control.value !== null) { + if (services.find(s => s.alias === control.value)) { + return {taken: true}; + } + } + + return null; + }; + } +} diff --git a/src/app/core/utils/helpers.ts b/src/app/core/utils/helpers.ts new file mode 100644 index 0000000..87f20c2 --- /dev/null +++ b/src/app/core/utils/helpers.ts @@ -0,0 +1,212 @@ +import * as base58 from 'bs58'; +import * as keccak256 from 'keccak256'; +import * as elliptic from 'elliptic'; +import { sha256 } from 'js-sha256'; + +function minifyPublicKey(publicKey: string) { + if (publicKey.length > 40) { + publicKey = publicKey.substring(0, 30) + '...' + publicKey.substring(publicKey.length - 10); + return publicKey; + } + + return publicKey; +} + +function minifyDid(didId: string) { + return didId.substring(0, 30) + '...' + didId.substring(didId.length - 10); +} + +function minifyAddress(address: string) { + return address.substring(0, 20) + '...' + address.substring(address.length - 10); +} + +function capitalize(string: string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} + +function convertPemToBinary(pem) { + var lines = pem.split('\n'); + var encoded = ''; + for(var i = 0;i < lines.length;i++) { + if (lines[i].trim().length > 0 && + lines[i].indexOf('-BEGIN PRIVATE KEY-') < 0 && + lines[i].indexOf('-BEGIN PUBLIC KEY-') < 0 && + lines[i].indexOf('-END PRIVATE KEY-') < 0 && + lines[i].indexOf('-END PUBLIC KEY-') < 0) { + encoded += lines[i].trim(); + } + } + + return base64StringToArrayBuffer(encoded); +} + +function base64StringToArrayBuffer(b64str) { + var byteStr = atob(b64str); + var bytes = new Uint8Array(byteStr.length); + + for (var i = 0; i < byteStr.length; i++) { + bytes[i] = byteStr.charCodeAt(i); + } + + return bytes.buffer; +} + +function arrayBufferToBase64String(arrayBuffer) { + var byteArray = new Uint8Array(arrayBuffer); + var byteString = ''; + + for (var i=0; i 100)? {"priorityMax": true} : null; + } +} \ No newline at end of file diff --git a/src/app/core/utils/priority.min.validator.ts b/src/app/core/utils/priority.min.validator.ts new file mode 100644 index 0000000..c2238bf --- /dev/null +++ b/src/app/core/utils/priority.min.validator.ts @@ -0,0 +1,21 @@ +import { Directive } from '@angular/core'; +import { NG_VALIDATORS, FormControl, Validator } from '@angular/forms'; + +@Directive({ + // tslint:disable-next-line:directive-selector + selector: '[priorityminvalidator][ngModel]', + providers: [ + { + provide: NG_VALIDATORS, + useExisting: PriorityMinValidator, + multi: true + } + ] +}) +export class PriorityMinValidator implements Validator { + + validate(c: FormControl) { + let v = c.value; + return ( v < 0)? {"priorityMin": true} : null; + } +} \ No newline at end of file diff --git a/src/app/core/utils/tooltip.messages.ts b/src/app/core/utils/tooltip.messages.ts new file mode 100644 index 0000000..5f046c5 --- /dev/null +++ b/src/app/core/utils/tooltip.messages.ts @@ -0,0 +1,24 @@ +export class TooltipMessages { + public static SignatureTypeTooltip = 'All signature types allow you to sign messages ' + + 'and differ only in the mathematics underpinning them. Ed25519 has a number of technical advantages over RSA and ECDSA ' + + 'and unless you have a good reason to choose an alternative you should stick with the default.'; + + public static ControllerTooltip = 'The controller is the entity that will be making the signatures. ' + + 'This is usually the Identity itself, but in case the Identity is for a child, it can be the Identity of the parent; ' + + 'if it is a document, it can be the Identity of the company owning the document, etc. By default the controller is ' + + 'set to the Identity you are currently creating. If the controller is a different Identity, you should input the relevant Identity instead.'; + + public static AliasTooltip = 'A human-readable nickname for the key you are creating. ' + + 'It can help differentiate between different keys more easily if you are creating many.'; + + public static ServicesHeaderTooltip = 'Register services used by the Identity. These can be authentication providers, ' + + 'messaging hubs, credential repositories for verifiable claims, etc.'; + + public static ServicesHeaderBoldPartTooltip = 'DO NOT put links to personally identifiable information ' + + '(such as social media profiles, email addresses, phone numbers, etc.)'; + + public static ServiceTypeTooltip = 'Choose a human-readable description of the type of service, e.g. KYCProvider, ' + + 'CredentialRepositoryService, MessagingHub, etc.'; + + public static ServiceEndpointTooltip = 'Specify the URL for the service, e.g. https://example.com/KYCProvider'; +} diff --git a/src/assets/.gitkeep b/src/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/assets/bootstrap.min.css b/src/assets/bootstrap.min.css new file mode 100644 index 0000000..1789b90 --- /dev/null +++ b/src/assets/bootstrap.min.css @@ -0,0 +1,12 @@ +/*! + * Bootswatch v4.3.1 + * Homepage: https://bootswatch.com + * Copyright 2012-2019 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v4.3.1 (https://getbootstrap.com/) + * Copyright 2011-2019 The Bootstrap Authors + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */@import url("https://fonts.googleapis.com/css?family=Lato:400,700,400italic");:root{--blue: #2C3E50;--indigo: #6610f2;--purple: #6f42c1;--pink: #e83e8c;--red: #E74C3C;--orange: #fd7e14;--yellow: #F39C12;--green: #18BC9C;--teal: #20c997;--cyan: #3498DB;--white: #fff;--gray: #95a5a6;--gray-dark: #343a40;--primary: #2C3E50;--secondary: #95a5a6;--success: #18BC9C;--info: #3498DB;--warning: #F39C12;--danger: #E74C3C;--light: #ecf0f1;--dark: #7b8a8b;--breakpoint-xs: 0;--breakpoint-sm: 576px;--breakpoint-md: 768px;--breakpoint-lg: 992px;--breakpoint-xl: 1200px;--font-family-sans-serif: "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";--font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace}*,*::before,*::after{-webkit-box-sizing:border-box;box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:"Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";font-size:0.9375rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0 !important}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:0.5rem}p{margin-top:0;margin-bottom:1rem}abbr[title],abbr[data-original-title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul,dl{margin-top:0;margin-bottom:1rem}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#18BC9C;text-decoration:none;background-color:transparent}a:hover{color:#0f7864;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):hover,a:not([href]):not([tabindex]):focus{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}pre,code,kbd,samp{font-family:SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:0.75rem;padding-bottom:0.75rem;color:#95a5a6;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:0.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}input,button,select,optgroup,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}button,[type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button:not(:disabled),[type="button"]:not(:disabled),[type="reset"]:not(:disabled),[type="submit"]:not(:disabled){cursor:pointer}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{padding:0;border-style:none}input[type="radio"],input[type="checkbox"]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="date"],input[type="time"],input[type="datetime-local"],input[type="month"]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{outline-offset:-2px;-webkit-appearance:none}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none !important}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{margin-bottom:0.5rem;font-weight:500;line-height:1.2}h1,.h1{font-size:3rem}h2,.h2{font-size:2.5rem}h3,.h3{font-size:2rem}h4,.h4{font-size:1.40625rem}h5,.h5{font-size:1.171875rem}h6,.h6{font-size:0.9375rem}.lead{font-size:1.171875rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,0.1)}small,.small{font-size:80%;font-weight:400}mark,.mark{padding:0.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:0.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.171875rem}.blockquote-footer{display:block;font-size:80%;color:#95a5a6}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:0.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:0.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:0.5rem;line-height:1}.figure-caption{font-size:90%;color:#95a5a6}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:0.2rem 0.4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:0.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width: 576px){.container{max-width:540px}}@media (min-width: 768px){.container{max-width:720px}}@media (min-width: 992px){.container{max-width:960px}}@media (min-width: 1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*="col-"]{padding-right:0;padding-left:0}.col-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12,.col,.col-auto,.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm,.col-sm-auto,.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12,.col-md,.col-md-auto,.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg,.col-lg-auto,.col-xl-1,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-1{margin-left:8.3333333333%}.offset-2{margin-left:16.6666666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.3333333333%}.offset-5{margin-left:41.6666666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.3333333333%}.offset-8{margin-left:66.6666666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.3333333333%}.offset-11{margin-left:91.6666666667%}@media (min-width: 576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-sm-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-sm-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-sm-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-sm-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-sm-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-sm-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-sm-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-sm-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-sm-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-sm-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-sm-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-sm-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-sm-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-sm-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-sm-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-sm-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-sm-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-sm-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-sm-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-sm-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-sm-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-sm-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.3333333333%}.offset-sm-2{margin-left:16.6666666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.3333333333%}.offset-sm-5{margin-left:41.6666666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.3333333333%}.offset-sm-8{margin-left:66.6666666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.3333333333%}.offset-sm-11{margin-left:91.6666666667%}}@media (min-width: 768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-md-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-md-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-md-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-md-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-md-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-md-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-md-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-md-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-md-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-md-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-md-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-md-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-md-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-md-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-md-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-md-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-md-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-md-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-md-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-md-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-md-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-md-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.3333333333%}.offset-md-2{margin-left:16.6666666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.3333333333%}.offset-md-5{margin-left:41.6666666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.3333333333%}.offset-md-8{margin-left:66.6666666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.3333333333%}.offset-md-11{margin-left:91.6666666667%}}@media (min-width: 992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-lg-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-lg-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-lg-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-lg-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-lg-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-lg-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-lg-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-lg-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-lg-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-lg-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-lg-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-lg-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-lg-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-lg-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-lg-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-lg-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-lg-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-lg-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-lg-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-lg-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-lg-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-lg-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.3333333333%}.offset-lg-2{margin-left:16.6666666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.3333333333%}.offset-lg-5{margin-left:41.6666666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.3333333333%}.offset-lg-8{margin-left:66.6666666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.3333333333%}.offset-lg-11{margin-left:91.6666666667%}}@media (min-width: 1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-xl-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-xl-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-xl-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-xl-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-xl-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-xl-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-xl-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-xl-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-xl-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-xl-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-xl-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-xl-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-xl-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-xl-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-xl-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-xl-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-xl-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-xl-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-xl-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-xl-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-xl-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-xl-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.3333333333%}.offset-xl-2{margin-left:16.6666666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.3333333333%}.offset-xl-5{margin-left:41.6666666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.3333333333%}.offset-xl-8{margin-left:66.6666666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.3333333333%}.offset-xl-11{margin-left:91.6666666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table th,.table td{padding:0.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm th,.table-sm td{padding:0.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered th,.table-bordered td{border:1px solid #dee2e6}.table-bordered thead th,.table-bordered thead td{border-bottom-width:2px}.table-borderless th,.table-borderless td,.table-borderless thead th,.table-borderless tbody+tbody{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:#ecf0f1}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,0.075)}.table-primary,.table-primary>th,.table-primary>td{background-color:#c4c9ce}.table-primary th,.table-primary td,.table-primary thead th,.table-primary tbody+tbody{border-color:#919ba4}.table-hover .table-primary:hover{background-color:#b6bcc2}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#b6bcc2}.table-secondary,.table-secondary>th,.table-secondary>td{background-color:#e1e6e6}.table-secondary th,.table-secondary td,.table-secondary thead th,.table-secondary tbody+tbody{border-color:#c8d0d1}.table-hover .table-secondary:hover{background-color:#d3dada}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#d3dada}.table-success,.table-success>th,.table-success>td{background-color:#beece3}.table-success th,.table-success td,.table-success thead th,.table-success tbody+tbody{border-color:#87dccc}.table-hover .table-success:hover{background-color:#aae6db}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#aae6db}.table-info,.table-info>th,.table-info>td{background-color:#c6e2f5}.table-info th,.table-info td,.table-info thead th,.table-info tbody+tbody{border-color:#95c9ec}.table-hover .table-info:hover{background-color:#b0d7f1}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#b0d7f1}.table-warning,.table-warning>th,.table-warning>td{background-color:#fce3bd}.table-warning th,.table-warning td,.table-warning thead th,.table-warning tbody+tbody{border-color:#f9cc84}.table-hover .table-warning:hover{background-color:#fbd9a5}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#fbd9a5}.table-danger,.table-danger>th,.table-danger>td{background-color:#f8cdc8}.table-danger th,.table-danger td,.table-danger thead th,.table-danger tbody+tbody{border-color:#f3a29a}.table-hover .table-danger:hover{background-color:#f5b8b1}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f5b8b1}.table-light,.table-light>th,.table-light>td{background-color:#fafbfb}.table-light th,.table-light td,.table-light thead th,.table-light tbody+tbody{border-color:#f5f7f8}.table-hover .table-light:hover{background-color:#ecf0f0}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ecf0f0}.table-dark,.table-dark>th,.table-dark>td{background-color:#dadedf}.table-dark th,.table-dark td,.table-dark thead th,.table-dark tbody+tbody{border-color:#bac2c3}.table-hover .table-dark:hover{background-color:#ccd2d3}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#ccd2d3}.table-active,.table-active>th,.table-active>td{background-color:rgba(0,0,0,0.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,0.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,0.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#7b8a8b;background-color:#ecf0f1;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark th,.table-dark td,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,0.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,0.075)}@media (max-width: 575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width: 767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width: 991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width: 1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + 0.75rem + 2px);padding:0.375rem 0.75rem;font-size:0.9375rem;font-weight:400;line-height:1.5;color:#7b8a8b;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:0.25rem;-webkit-transition:border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.form-control{-webkit-transition:none;transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#7b8a8b;background-color:#fff;border-color:#597ea2;outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(44,62,80,0.25);box-shadow:0 0 0 0.2rem rgba(44,62,80,0.25)}.form-control::-webkit-input-placeholder{color:#95a5a6;opacity:1}.form-control::-ms-input-placeholder{color:#95a5a6;opacity:1}.form-control::placeholder{color:#95a5a6;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#ecf0f1;opacity:1}select.form-control:focus::-ms-value{color:#7b8a8b;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(0.375rem + 1px);padding-bottom:calc(0.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(0.5rem + 1px);padding-bottom:calc(0.5rem + 1px);font-size:1.171875rem;line-height:1.5}.col-form-label-sm{padding-top:calc(0.25rem + 1px);padding-bottom:calc(0.25rem + 1px);font-size:0.8203125rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:0.375rem;padding-bottom:0.375rem;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-sm,.form-control-plaintext.form-control-lg{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + 0.5rem + 2px);padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5;border-radius:0.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:0.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:0.3rem}select.form-control[size],select.form-control[multiple]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:0.25rem}.form-row{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*="col-"]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:0.3rem;margin-left:-1.25rem}.form-check-input:disabled ~ .form-check-label{color:#95a5a6}.form-check-label{margin-bottom:0}.form-check-inline{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:0.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:0.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:0.25rem;font-size:80%;color:#18BC9C}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:0.25rem 0.5rem;margin-top:.1rem;font-size:0.8203125rem;line-height:1.5;color:#fff;background-color:rgba(24,188,156,0.9);border-radius:0.25rem}.was-validated .form-control:valid,.form-control.is-valid{border-color:#18BC9C;padding-right:calc(1.5em + 0.75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2318BC9C' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:center right calc(0.375em + 0.1875rem);background-size:calc(0.75em + 0.375rem) calc(0.75em + 0.375rem)}.was-validated .form-control:valid:focus,.form-control.is-valid:focus{border-color:#18BC9C;-webkit-box-shadow:0 0 0 0.2rem rgba(24,188,156,0.25);box-shadow:0 0 0 0.2rem rgba(24,188,156,0.25)}.was-validated .form-control:valid ~ .valid-feedback,.was-validated .form-control:valid ~ .valid-tooltip,.form-control.is-valid ~ .valid-feedback,.form-control.is-valid ~ .valid-tooltip{display:block}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + 0.75rem);background-position:top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem)}.was-validated .custom-select:valid,.custom-select.is-valid{border-color:#18BC9C;padding-right:calc((1em + 0.75rem) * 3 / 4 + 1.75rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2318BC9C' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem)}.was-validated .custom-select:valid:focus,.custom-select.is-valid:focus{border-color:#18BC9C;-webkit-box-shadow:0 0 0 0.2rem rgba(24,188,156,0.25);box-shadow:0 0 0 0.2rem rgba(24,188,156,0.25)}.was-validated .custom-select:valid ~ .valid-feedback,.was-validated .custom-select:valid ~ .valid-tooltip,.custom-select.is-valid ~ .valid-feedback,.custom-select.is-valid ~ .valid-tooltip{display:block}.was-validated .form-control-file:valid ~ .valid-feedback,.was-validated .form-control-file:valid ~ .valid-tooltip,.form-control-file.is-valid ~ .valid-feedback,.form-control-file.is-valid ~ .valid-tooltip{display:block}.was-validated .form-check-input:valid ~ .form-check-label,.form-check-input.is-valid ~ .form-check-label{color:#18BC9C}.was-validated .form-check-input:valid ~ .valid-feedback,.was-validated .form-check-input:valid ~ .valid-tooltip,.form-check-input.is-valid ~ .valid-feedback,.form-check-input.is-valid ~ .valid-tooltip{display:block}.was-validated .custom-control-input:valid ~ .custom-control-label,.custom-control-input.is-valid ~ .custom-control-label{color:#18BC9C}.was-validated .custom-control-input:valid ~ .custom-control-label::before,.custom-control-input.is-valid ~ .custom-control-label::before{border-color:#18BC9C}.was-validated .custom-control-input:valid ~ .valid-feedback,.was-validated .custom-control-input:valid ~ .valid-tooltip,.custom-control-input.is-valid ~ .valid-feedback,.custom-control-input.is-valid ~ .valid-tooltip{display:block}.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before,.custom-control-input.is-valid:checked ~ .custom-control-label::before{border-color:#24e3be;background-color:#24e3be}.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before,.custom-control-input.is-valid:focus ~ .custom-control-label::before{-webkit-box-shadow:0 0 0 0.2rem rgba(24,188,156,0.25);box-shadow:0 0 0 0.2rem rgba(24,188,156,0.25)}.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before,.custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before{border-color:#18BC9C}.was-validated .custom-file-input:valid ~ .custom-file-label,.custom-file-input.is-valid ~ .custom-file-label{border-color:#18BC9C}.was-validated .custom-file-input:valid ~ .valid-feedback,.was-validated .custom-file-input:valid ~ .valid-tooltip,.custom-file-input.is-valid ~ .valid-feedback,.custom-file-input.is-valid ~ .valid-tooltip{display:block}.was-validated .custom-file-input:valid:focus ~ .custom-file-label,.custom-file-input.is-valid:focus ~ .custom-file-label{border-color:#18BC9C;-webkit-box-shadow:0 0 0 0.2rem rgba(24,188,156,0.25);box-shadow:0 0 0 0.2rem rgba(24,188,156,0.25)}.invalid-feedback{display:none;width:100%;margin-top:0.25rem;font-size:80%;color:#E74C3C}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:0.25rem 0.5rem;margin-top:.1rem;font-size:0.8203125rem;line-height:1.5;color:#fff;background-color:rgba(231,76,60,0.9);border-radius:0.25rem}.was-validated .form-control:invalid,.form-control.is-invalid{border-color:#E74C3C;padding-right:calc(1.5em + 0.75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23E74C3C' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23E74C3C' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E");background-repeat:no-repeat;background-position:center right calc(0.375em + 0.1875rem);background-size:calc(0.75em + 0.375rem) calc(0.75em + 0.375rem)}.was-validated .form-control:invalid:focus,.form-control.is-invalid:focus{border-color:#E74C3C;-webkit-box-shadow:0 0 0 0.2rem rgba(231,76,60,0.25);box-shadow:0 0 0 0.2rem rgba(231,76,60,0.25)}.was-validated .form-control:invalid ~ .invalid-feedback,.was-validated .form-control:invalid ~ .invalid-tooltip,.form-control.is-invalid ~ .invalid-feedback,.form-control.is-invalid ~ .invalid-tooltip{display:block}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + 0.75rem);background-position:top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem)}.was-validated .custom-select:invalid,.custom-select.is-invalid{border-color:#E74C3C;padding-right:calc((1em + 0.75rem) * 3 / 4 + 1.75rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23E74C3C' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23E74C3C' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem)}.was-validated .custom-select:invalid:focus,.custom-select.is-invalid:focus{border-color:#E74C3C;-webkit-box-shadow:0 0 0 0.2rem rgba(231,76,60,0.25);box-shadow:0 0 0 0.2rem rgba(231,76,60,0.25)}.was-validated .custom-select:invalid ~ .invalid-feedback,.was-validated .custom-select:invalid ~ .invalid-tooltip,.custom-select.is-invalid ~ .invalid-feedback,.custom-select.is-invalid ~ .invalid-tooltip{display:block}.was-validated .form-control-file:invalid ~ .invalid-feedback,.was-validated .form-control-file:invalid ~ .invalid-tooltip,.form-control-file.is-invalid ~ .invalid-feedback,.form-control-file.is-invalid ~ .invalid-tooltip{display:block}.was-validated .form-check-input:invalid ~ .form-check-label,.form-check-input.is-invalid ~ .form-check-label{color:#E74C3C}.was-validated .form-check-input:invalid ~ .invalid-feedback,.was-validated .form-check-input:invalid ~ .invalid-tooltip,.form-check-input.is-invalid ~ .invalid-feedback,.form-check-input.is-invalid ~ .invalid-tooltip{display:block}.was-validated .custom-control-input:invalid ~ .custom-control-label,.custom-control-input.is-invalid ~ .custom-control-label{color:#E74C3C}.was-validated .custom-control-input:invalid ~ .custom-control-label::before,.custom-control-input.is-invalid ~ .custom-control-label::before{border-color:#E74C3C}.was-validated .custom-control-input:invalid ~ .invalid-feedback,.was-validated .custom-control-input:invalid ~ .invalid-tooltip,.custom-control-input.is-invalid ~ .invalid-feedback,.custom-control-input.is-invalid ~ .invalid-tooltip{display:block}.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before,.custom-control-input.is-invalid:checked ~ .custom-control-label::before{border-color:#ed7669;background-color:#ed7669}.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before,.custom-control-input.is-invalid:focus ~ .custom-control-label::before{-webkit-box-shadow:0 0 0 0.2rem rgba(231,76,60,0.25);box-shadow:0 0 0 0.2rem rgba(231,76,60,0.25)}.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before,.custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before{border-color:#E74C3C}.was-validated .custom-file-input:invalid ~ .custom-file-label,.custom-file-input.is-invalid ~ .custom-file-label{border-color:#E74C3C}.was-validated .custom-file-input:invalid ~ .invalid-feedback,.was-validated .custom-file-input:invalid ~ .invalid-tooltip,.custom-file-input.is-invalid ~ .invalid-feedback,.custom-file-input.is-invalid ~ .invalid-tooltip{display:block}.was-validated .custom-file-input:invalid:focus ~ .custom-file-label,.custom-file-input.is-invalid:focus ~ .custom-file-label{border-color:#E74C3C;-webkit-box-shadow:0 0 0 0.2rem rgba(231,76,60,0.25);box-shadow:0 0 0 0.2rem rgba(231,76,60,0.25)}.form-inline{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width: 576px){.form-inline label{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .input-group,.form-inline .custom-select{width:auto}.form-inline .form-check{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:0.25rem;margin-left:0}.form-inline .custom-control{-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:0.375rem 0.75rem;font-size:0.9375rem;line-height:1.5;border-radius:0.25rem;-webkit-transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.btn{-webkit-transition:none;transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn:focus,.btn.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(44,62,80,0.25);box-shadow:0 0 0 0.2rem rgba(44,62,80,0.25)}.btn.disabled,.btn:disabled{opacity:0.65}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#2C3E50;border-color:#2C3E50}.btn-primary:hover{color:#fff;background-color:#1e2b37;border-color:#1a252f}.btn-primary:focus,.btn-primary.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(76,91,106,0.5);box-shadow:0 0 0 0.2rem rgba(76,91,106,0.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#2C3E50;border-color:#2C3E50}.btn-primary:not(:disabled):not(.disabled):active,.btn-primary:not(:disabled):not(.disabled).active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#1a252f;border-color:#151e27}.btn-primary:not(:disabled):not(.disabled):active:focus,.btn-primary:not(:disabled):not(.disabled).active:focus,.show>.btn-primary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(76,91,106,0.5);box-shadow:0 0 0 0.2rem rgba(76,91,106,0.5)}.btn-secondary{color:#fff;background-color:#95a5a6;border-color:#95a5a6}.btn-secondary:hover{color:#fff;background-color:#809395;border-color:#798d8f}.btn-secondary:focus,.btn-secondary.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(165,179,179,0.5);box-shadow:0 0 0 0.2rem rgba(165,179,179,0.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#95a5a6;border-color:#95a5a6}.btn-secondary:not(:disabled):not(.disabled):active,.btn-secondary:not(:disabled):not(.disabled).active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#798d8f;border-color:#738789}.btn-secondary:not(:disabled):not(.disabled):active:focus,.btn-secondary:not(:disabled):not(.disabled).active:focus,.show>.btn-secondary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(165,179,179,0.5);box-shadow:0 0 0 0.2rem rgba(165,179,179,0.5)}.btn-success{color:#fff;background-color:#18BC9C;border-color:#18BC9C}.btn-success:hover{color:#fff;background-color:#149a80;border-color:#128f76}.btn-success:focus,.btn-success.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(59,198,171,0.5);box-shadow:0 0 0 0.2rem rgba(59,198,171,0.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#18BC9C;border-color:#18BC9C}.btn-success:not(:disabled):not(.disabled):active,.btn-success:not(:disabled):not(.disabled).active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#128f76;border-color:#11836d}.btn-success:not(:disabled):not(.disabled):active:focus,.btn-success:not(:disabled):not(.disabled).active:focus,.show>.btn-success.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(59,198,171,0.5);box-shadow:0 0 0 0.2rem rgba(59,198,171,0.5)}.btn-info{color:#fff;background-color:#3498DB;border-color:#3498DB}.btn-info:hover{color:#fff;background-color:#2384c6;border-color:#217dbb}.btn-info:focus,.btn-info.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(82,167,224,0.5);box-shadow:0 0 0 0.2rem rgba(82,167,224,0.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#3498DB;border-color:#3498DB}.btn-info:not(:disabled):not(.disabled):active,.btn-info:not(:disabled):not(.disabled).active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#217dbb;border-color:#1f76b0}.btn-info:not(:disabled):not(.disabled):active:focus,.btn-info:not(:disabled):not(.disabled).active:focus,.show>.btn-info.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(82,167,224,0.5);box-shadow:0 0 0 0.2rem rgba(82,167,224,0.5)}.btn-warning{color:#fff;background-color:#F39C12;border-color:#F39C12}.btn-warning:hover{color:#fff;background-color:#d4860b;border-color:#c87f0a}.btn-warning:focus,.btn-warning.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(245,171,54,0.5);box-shadow:0 0 0 0.2rem rgba(245,171,54,0.5)}.btn-warning.disabled,.btn-warning:disabled{color:#fff;background-color:#F39C12;border-color:#F39C12}.btn-warning:not(:disabled):not(.disabled):active,.btn-warning:not(:disabled):not(.disabled).active,.show>.btn-warning.dropdown-toggle{color:#fff;background-color:#c87f0a;border-color:#bc770a}.btn-warning:not(:disabled):not(.disabled):active:focus,.btn-warning:not(:disabled):not(.disabled).active:focus,.show>.btn-warning.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(245,171,54,0.5);box-shadow:0 0 0 0.2rem rgba(245,171,54,0.5)}.btn-danger{color:#fff;background-color:#E74C3C;border-color:#E74C3C}.btn-danger:hover{color:#fff;background-color:#e12e1c;border-color:#d62c1a}.btn-danger:focus,.btn-danger.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(235,103,89,0.5);box-shadow:0 0 0 0.2rem rgba(235,103,89,0.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#E74C3C;border-color:#E74C3C}.btn-danger:not(:disabled):not(.disabled):active,.btn-danger:not(:disabled):not(.disabled).active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#d62c1a;border-color:#ca2a19}.btn-danger:not(:disabled):not(.disabled):active:focus,.btn-danger:not(:disabled):not(.disabled).active:focus,.show>.btn-danger.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(235,103,89,0.5);box-shadow:0 0 0 0.2rem rgba(235,103,89,0.5)}.btn-light{color:#212529;background-color:#ecf0f1;border-color:#ecf0f1}.btn-light:hover{color:#212529;background-color:#d6dfe1;border-color:#cfd9db}.btn-light:focus,.btn-light.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(206,210,211,0.5);box-shadow:0 0 0 0.2rem rgba(206,210,211,0.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#ecf0f1;border-color:#ecf0f1}.btn-light:not(:disabled):not(.disabled):active,.btn-light:not(:disabled):not(.disabled).active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#cfd9db;border-color:#c7d3d6}.btn-light:not(:disabled):not(.disabled):active:focus,.btn-light:not(:disabled):not(.disabled).active:focus,.show>.btn-light.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(206,210,211,0.5);box-shadow:0 0 0 0.2rem rgba(206,210,211,0.5)}.btn-dark{color:#fff;background-color:#7b8a8b;border-color:#7b8a8b}.btn-dark:hover{color:#fff;background-color:#697677;border-color:#636f70}.btn-dark:focus,.btn-dark.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(143,156,156,0.5);box-shadow:0 0 0 0.2rem rgba(143,156,156,0.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#7b8a8b;border-color:#7b8a8b}.btn-dark:not(:disabled):not(.disabled):active,.btn-dark:not(:disabled):not(.disabled).active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#636f70;border-color:#5d696a}.btn-dark:not(:disabled):not(.disabled):active:focus,.btn-dark:not(:disabled):not(.disabled).active:focus,.show>.btn-dark.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(143,156,156,0.5);box-shadow:0 0 0 0.2rem rgba(143,156,156,0.5)}.btn-outline-primary{color:#2C3E50;border-color:#2C3E50}.btn-outline-primary:hover{color:#fff;background-color:#2C3E50;border-color:#2C3E50}.btn-outline-primary:focus,.btn-outline-primary.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(44,62,80,0.5);box-shadow:0 0 0 0.2rem rgba(44,62,80,0.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#2C3E50;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled):active,.btn-outline-primary:not(:disabled):not(.disabled).active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#2C3E50;border-color:#2C3E50}.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(44,62,80,0.5);box-shadow:0 0 0 0.2rem rgba(44,62,80,0.5)}.btn-outline-secondary{color:#95a5a6;border-color:#95a5a6}.btn-outline-secondary:hover{color:#fff;background-color:#95a5a6;border-color:#95a5a6}.btn-outline-secondary:focus,.btn-outline-secondary.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(149,165,166,0.5);box-shadow:0 0 0 0.2rem rgba(149,165,166,0.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#95a5a6;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled):active,.btn-outline-secondary:not(:disabled):not(.disabled).active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#95a5a6;border-color:#95a5a6}.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(149,165,166,0.5);box-shadow:0 0 0 0.2rem rgba(149,165,166,0.5)}.btn-outline-success{color:#18BC9C;border-color:#18BC9C}.btn-outline-success:hover{color:#fff;background-color:#18BC9C;border-color:#18BC9C}.btn-outline-success:focus,.btn-outline-success.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(24,188,156,0.5);box-shadow:0 0 0 0.2rem rgba(24,188,156,0.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#18BC9C;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled):active,.btn-outline-success:not(:disabled):not(.disabled).active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#18BC9C;border-color:#18BC9C}.btn-outline-success:not(:disabled):not(.disabled):active:focus,.btn-outline-success:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-success.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(24,188,156,0.5);box-shadow:0 0 0 0.2rem rgba(24,188,156,0.5)}.btn-outline-info{color:#3498DB;border-color:#3498DB}.btn-outline-info:hover{color:#fff;background-color:#3498DB;border-color:#3498DB}.btn-outline-info:focus,.btn-outline-info.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(52,152,219,0.5);box-shadow:0 0 0 0.2rem rgba(52,152,219,0.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#3498DB;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled):active,.btn-outline-info:not(:disabled):not(.disabled).active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#3498DB;border-color:#3498DB}.btn-outline-info:not(:disabled):not(.disabled):active:focus,.btn-outline-info:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-info.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(52,152,219,0.5);box-shadow:0 0 0 0.2rem rgba(52,152,219,0.5)}.btn-outline-warning{color:#F39C12;border-color:#F39C12}.btn-outline-warning:hover{color:#fff;background-color:#F39C12;border-color:#F39C12}.btn-outline-warning:focus,.btn-outline-warning.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(243,156,18,0.5);box-shadow:0 0 0 0.2rem rgba(243,156,18,0.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#F39C12;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled):active,.btn-outline-warning:not(:disabled):not(.disabled).active,.show>.btn-outline-warning.dropdown-toggle{color:#fff;background-color:#F39C12;border-color:#F39C12}.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(243,156,18,0.5);box-shadow:0 0 0 0.2rem rgba(243,156,18,0.5)}.btn-outline-danger{color:#E74C3C;border-color:#E74C3C}.btn-outline-danger:hover{color:#fff;background-color:#E74C3C;border-color:#E74C3C}.btn-outline-danger:focus,.btn-outline-danger.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(231,76,60,0.5);box-shadow:0 0 0 0.2rem rgba(231,76,60,0.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#E74C3C;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled):active,.btn-outline-danger:not(:disabled):not(.disabled).active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#E74C3C;border-color:#E74C3C}.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(231,76,60,0.5);box-shadow:0 0 0 0.2rem rgba(231,76,60,0.5)}.btn-outline-light{color:#ecf0f1;border-color:#ecf0f1}.btn-outline-light:hover{color:#212529;background-color:#ecf0f1;border-color:#ecf0f1}.btn-outline-light:focus,.btn-outline-light.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(236,240,241,0.5);box-shadow:0 0 0 0.2rem rgba(236,240,241,0.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#ecf0f1;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled):active,.btn-outline-light:not(:disabled):not(.disabled).active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#ecf0f1;border-color:#ecf0f1}.btn-outline-light:not(:disabled):not(.disabled):active:focus,.btn-outline-light:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-light.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(236,240,241,0.5);box-shadow:0 0 0 0.2rem rgba(236,240,241,0.5)}.btn-outline-dark{color:#7b8a8b;border-color:#7b8a8b}.btn-outline-dark:hover{color:#fff;background-color:#7b8a8b;border-color:#7b8a8b}.btn-outline-dark:focus,.btn-outline-dark.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(123,138,139,0.5);box-shadow:0 0 0 0.2rem rgba(123,138,139,0.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#7b8a8b;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled):active,.btn-outline-dark:not(:disabled):not(.disabled).active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#7b8a8b;border-color:#7b8a8b}.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(123,138,139,0.5);box-shadow:0 0 0 0.2rem rgba(123,138,139,0.5)}.btn-link{font-weight:400;color:#18BC9C;text-decoration:none}.btn-link:hover{color:#0f7864;text-decoration:underline}.btn-link:focus,.btn-link.focus{text-decoration:underline;-webkit-box-shadow:none;box-shadow:none}.btn-link:disabled,.btn-link.disabled{color:#95a5a6;pointer-events:none}.btn-lg,.btn-group-lg>.btn{padding:0.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:0.3rem}.btn-sm,.btn-group-sm>.btn{padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5;border-radius:0.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:0.5rem}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{-webkit-transition:opacity 0.15s linear;transition:opacity 0.15s linear}@media (prefers-reduced-motion: reduce){.fade{-webkit-transition:none;transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height 0.35s ease;transition:height 0.35s ease}@media (prefers-reduced-motion: reduce){.collapsing{-webkit-transition:none;transition:none}}.dropup,.dropright,.dropdown,.dropleft{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:0.255em;vertical-align:0.255em;content:"";border-top:0.3em solid;border-right:0.3em solid transparent;border-bottom:0;border-left:0.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:0.5rem 0;margin:0.125rem 0 0;font-size:0.9375rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,0.15);border-radius:0.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width: 576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width: 768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width: 992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width: 1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:0.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:0.255em;vertical-align:0.255em;content:"";border-top:0;border-right:0.3em solid transparent;border-bottom:0.3em solid;border-left:0.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:0.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:0.255em;vertical-align:0.255em;content:"";border-top:0.3em solid transparent;border-right:0;border-bottom:0.3em solid transparent;border-left:0.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:0.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:0.255em;vertical-align:0.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:0.255em;vertical-align:0.255em;content:"";border-top:0.3em solid transparent;border-right:0.3em solid;border-bottom:0.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^="top"],.dropdown-menu[x-placement^="right"],.dropdown-menu[x-placement^="bottom"],.dropdown-menu[x-placement^="left"]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:0.5rem 0;overflow:hidden;border-top:1px solid #ecf0f1}.dropdown-item{display:block;width:100%;padding:0.25rem 1.5rem;clear:both;font-weight:400;color:#7b8a8b;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:hover,.dropdown-item:focus{color:#fff;text-decoration:none;background-color:#2C3E50}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#2C3E50}.dropdown-item.disabled,.dropdown-item:disabled{color:#95a5a6;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:0.5rem 1.5rem;margin-bottom:0;font-size:0.8203125rem;color:#95a5a6;white-space:nowrap}.dropdown-item-text{display:block;padding:0.25rem 1.5rem;color:#7b8a8b}.btn-group,.btn-group-vertical{position:relative;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover{z-index:1}.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:1}.btn-toolbar{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn:not(:first-child),.btn-group>.btn-group:not(:first-child){margin-left:-1px}.btn-group>.btn:not(:last-child):not(.dropdown-toggle),.btn-group>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:not(:first-child),.btn-group>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:0.5625rem;padding-left:0.5625rem}.dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-sm+.dropdown-toggle-split,.btn-group-sm>.btn+.dropdown-toggle-split{padding-right:0.375rem;padding-left:0.375rem}.btn-lg+.dropdown-toggle-split,.btn-group-lg>.btn+.dropdown-toggle-split{padding-right:0.75rem;padding-left:0.75rem}.btn-group-vertical{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle),.btn-group-vertical>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type="radio"],.btn-group-toggle>.btn input[type="checkbox"],.btn-group-toggle>.btn-group>.btn input[type="radio"],.btn-group-toggle>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-control-plaintext,.input-group>.custom-select,.input-group>.custom-file{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.form-control+.form-control,.input-group>.form-control+.custom-select,.input-group>.form-control+.custom-file,.input-group>.form-control-plaintext+.form-control,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.custom-file,.input-group>.custom-select+.form-control,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.custom-file,.input-group>.custom-file+.form-control,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.custom-file{margin-left:-1px}.input-group>.form-control:focus,.input-group>.custom-select:focus,.input-group>.custom-file .custom-file-input:focus ~ .custom-file-label{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.form-control:not(:last-child),.input-group>.custom-select:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.form-control:not(:first-child),.input-group>.custom-select:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-prepend,.input-group-append{display:-webkit-box;display:-ms-flexbox;display:flex}.input-group-prepend .btn,.input-group-append .btn{position:relative;z-index:2}.input-group-prepend .btn:focus,.input-group-append .btn:focus{z-index:3}.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.input-group-text,.input-group-append .input-group-text+.btn{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:0.375rem 0.75rem;margin-bottom:0;font-size:0.9375rem;font-weight:400;line-height:1.5;color:#7b8a8b;text-align:center;white-space:nowrap;background-color:#ecf0f1;border:1px solid #ced4da;border-radius:0.25rem}.input-group-text input[type="radio"],.input-group-text input[type="checkbox"]{margin-top:0}.input-group-lg>.form-control:not(textarea),.input-group-lg>.custom-select{height:calc(1.5em + 1rem + 2px)}.input-group-lg>.form-control,.input-group-lg>.custom-select,.input-group-lg>.input-group-prepend>.input-group-text,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-append>.btn{padding:0.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:0.3rem}.input-group-sm>.form-control:not(textarea),.input-group-sm>.custom-select{height:calc(1.5em + 0.5rem + 2px)}.input-group-sm>.form-control,.input-group-sm>.custom-select,.input-group-sm>.input-group-prepend>.input-group-text,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-append>.btn{padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5;border-radius:0.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text,.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.40625rem;padding-left:1.5rem}.custom-control-inline{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked ~ .custom-control-label::before{color:#fff;border-color:#2C3E50;background-color:#2C3E50}.custom-control-input:focus ~ .custom-control-label::before{-webkit-box-shadow:0 0 0 0.2rem rgba(44,62,80,0.25);box-shadow:0 0 0 0.2rem rgba(44,62,80,0.25)}.custom-control-input:focus:not(:checked) ~ .custom-control-label::before{border-color:#597ea2}.custom-control-input:not(:disabled):active ~ .custom-control-label::before{color:#fff;background-color:#7997b5;border-color:#7997b5}.custom-control-input:disabled ~ .custom-control-label{color:#95a5a6}.custom-control-input:disabled ~ .custom-control-label::before{background-color:#ecf0f1}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:0.203125rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#b4bcc2 solid 1px}.custom-control-label::after{position:absolute;top:0.203125rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50% / 50% 50%}.custom-checkbox .custom-control-label::before{border-radius:0.25rem}.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before{border-color:#2C3E50;background-color:#2C3E50}.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before{background-color:rgba(44,62,80,0.5)}.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before{background-color:rgba(44,62,80,0.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked ~ .custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before{background-color:rgba(44,62,80,0.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:0.5rem}.custom-switch .custom-control-label::after{top:calc(0.203125rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#b4bcc2;border-radius:0.5rem;-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.custom-switch .custom-control-label::after{-webkit-transition:none;transition:none}}.custom-switch .custom-control-input:checked ~ .custom-control-label::after{background-color:#fff;-webkit-transform:translateX(0.75rem);transform:translateX(0.75rem)}.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before{background-color:rgba(44,62,80,0.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + 0.75rem + 2px);padding:0.375rem 1.75rem 0.375rem 0.75rem;font-size:0.9375rem;font-weight:400;line-height:1.5;color:#7b8a8b;vertical-align:middle;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px;background-color:#fff;border:1px solid #ced4da;border-radius:0.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#597ea2;outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(44,62,80,0.25);box-shadow:0 0 0 0.2rem rgba(44,62,80,0.25)}.custom-select:focus::-ms-value{color:#7b8a8b;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:0.75rem;background-image:none}.custom-select:disabled{color:#95a5a6;background-color:#ecf0f1}.custom-select::-ms-expand{display:none}.custom-select-sm{height:calc(1.5em + 0.5rem + 2px);padding-top:0.25rem;padding-bottom:0.25rem;padding-left:0.5rem;font-size:0.8203125rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:0.5rem;padding-bottom:0.5rem;padding-left:1rem;font-size:1.171875rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + 0.75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + 0.75rem + 2px);margin:0;opacity:0}.custom-file-input:focus ~ .custom-file-label{border-color:#597ea2;-webkit-box-shadow:0 0 0 0.2rem rgba(44,62,80,0.25);box-shadow:0 0 0 0.2rem rgba(44,62,80,0.25)}.custom-file-input:disabled ~ .custom-file-label{background-color:#ecf0f1}.custom-file-input:lang(en) ~ .custom-file-label::after{content:"Browse"}.custom-file-input ~ .custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + 0.75rem + 2px);padding:0.375rem 0.75rem;font-weight:400;line-height:1.5;color:#7b8a8b;background-color:#fff;border:1px solid #ced4da;border-radius:0.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + 0.75rem);padding:0.375rem 0.75rem;line-height:1.5;color:#7b8a8b;content:"Browse";background-color:#ecf0f1;border-left:inherit;border-radius:0 0.25rem 0.25rem 0}.custom-range{width:100%;height:calc(1rem + 0.4rem);padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:none}.custom-range:focus::-webkit-slider-thumb{-webkit-box-shadow:0 0 0 1px #fff,0 0 0 0.2rem rgba(44,62,80,0.25);box-shadow:0 0 0 1px #fff,0 0 0 0.2rem rgba(44,62,80,0.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 0.2rem rgba(44,62,80,0.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 0.2rem rgba(44,62,80,0.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-0.25rem;background-color:#2C3E50;border:0;border-radius:1rem;-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion: reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#7997b5}.custom-range::-webkit-slider-runnable-track{width:100%;height:0.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#2C3E50;border:0;border-radius:1rem;-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion: reduce){.custom-range::-moz-range-thumb{-webkit-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#7997b5}.custom-range::-moz-range-track{width:100%;height:0.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:0.2rem;margin-left:0.2rem;background-color:#2C3E50;border:0;border-radius:1rem;-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;appearance:none}@media (prefers-reduced-motion: reduce){.custom-range::-ms-thumb{-webkit-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#7997b5}.custom-range::-ms-track{width:100%;height:0.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:0.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#b4bcc2}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#b4bcc2}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#b4bcc2}.custom-control-label::before,.custom-file-label,.custom-select{-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.custom-control-label::before,.custom-file-label,.custom-select{-webkit-transition:none;transition:none}}.nav{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:0.5rem 2rem}.nav-link:hover,.nav-link:focus{text-decoration:none}.nav-link.disabled{color:#95a5a6;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #ecf0f1}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:0.25rem;border-top-right-radius:0.25rem}.nav-tabs .nav-link:hover,.nav-tabs .nav-link:focus{border-color:#ecf0f1 #ecf0f1 #ecf0f1}.nav-tabs .nav-link.disabled{color:#95a5a6;background-color:transparent;border-color:transparent}.nav-tabs .nav-link.active,.nav-tabs .nav-item.show .nav-link{color:#7b8a8b;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:0.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#2C3E50}.nav-fill .nav-item{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem}.navbar>.container,.navbar>.container-fluid{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:0.32421875rem;padding-bottom:0.32421875rem;margin-right:1rem;font-size:1.171875rem;line-height:inherit;white-space:nowrap}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-nav{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:0.5rem;padding-bottom:0.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:0.25rem 0.75rem;font-size:1.171875rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:0.25rem}.navbar-toggler:hover,.navbar-toggler:focus{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width: 575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width: 576px){.navbar-expand-sm{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:0.5rem;padding-left:0.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width: 767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width: 768px){.navbar-expand-md{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:0.5rem;padding-left:0.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width: 991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width: 992px){.navbar-expand-lg{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:0.5rem;padding-left:0.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width: 1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width: 1200px){.navbar-expand-xl{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:0.5rem;padding-left:0.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:0.5rem;padding-left:0.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,0.9)}.navbar-light .navbar-brand:hover,.navbar-light .navbar-brand:focus{color:rgba(0,0,0,0.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,0.5)}.navbar-light .navbar-nav .nav-link:hover,.navbar-light .navbar-nav .nav-link:focus{color:rgba(0,0,0,0.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,0.3)}.navbar-light .navbar-nav .show>.nav-link,.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .nav-link.active{color:rgba(0,0,0,0.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,0.5);border-color:rgba(0,0,0,0.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,0.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,0.9)}.navbar-light .navbar-text a:hover,.navbar-light .navbar-text a:focus{color:rgba(0,0,0,0.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:hover,.navbar-dark .navbar-brand:focus{color:#fff}.navbar-dark .navbar-nav .nav-link{color:#fff}.navbar-dark .navbar-nav .nav-link:hover,.navbar-dark .navbar-nav .nav-link:focus{color:#18BC9C}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,0.25)}.navbar-dark .navbar-nav .show>.nav-link,.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .nav-link.active{color:#fff}.navbar-dark .navbar-toggler{color:#fff;border-color:rgba(255,255,255,0.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:#fff}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:hover,.navbar-dark .navbar-text a:focus{color:#fff}.card{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,0.125);border-radius:0.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:0.25rem;border-top-right-radius:0.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:0.25rem;border-bottom-left-radius:0.25rem}.card-body{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:0.75rem}.card-subtitle{margin-top:-0.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:0.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,0.03);border-bottom:1px solid rgba(0,0,0,0.125)}.card-header:first-child{border-radius:calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:0.75rem 1.25rem;background-color:rgba(0,0,0,0.03);border-top:1px solid rgba(0,0,0,0.125)}.card-footer:last-child{border-radius:0 0 calc(0.25rem - 1px) calc(0.25rem - 1px)}.card-header-tabs{margin-right:-0.625rem;margin-bottom:-0.75rem;margin-left:-0.625rem;border-bottom:0}.card-header-pills{margin-right:-0.625rem;margin-left:-0.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(0.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(0.25rem - 1px);border-top-right-radius:calc(0.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(0.25rem - 1px);border-bottom-left-radius:calc(0.25rem - 1px)}.card-deck{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width: 576px){.card-deck{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1 0 0%;flex:1 0 0%;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width: 576px){.card-group{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-webkit-box-flex:1;-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-img-top,.card-group>.card:not(:last-child) .card-header{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-img-bottom,.card-group>.card:not(:last-child) .card-footer{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-img-top,.card-group>.card:not(:first-child) .card-header{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-img-bottom,.card-group>.card:not(:first-child) .card-footer{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:0.75rem}@media (min-width: 576px){.card-columns{-webkit-column-count:3;column-count:3;-webkit-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion>.card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion>.card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.accordion>.card .card-header{margin-bottom:-1px}.breadcrumb{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:0.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#ecf0f1;border-radius:0.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:0.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:0.5rem;color:#95a5a6;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#95a5a6}.pagination{display:-webkit-box;display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:0.25rem}.page-link{position:relative;display:block;padding:0.5rem 0.75rem;margin-left:0;line-height:1.25;color:#fff;background-color:#18BC9C;border:0 solid transparent}.page-link:hover{z-index:2;color:#fff;text-decoration:none;background-color:#0f7864;border-color:transparent}.page-link:focus{z-index:2;outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(44,62,80,0.25);box-shadow:0 0 0 0.2rem rgba(44,62,80,0.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:0.25rem;border-bottom-left-radius:0.25rem}.page-item:last-child .page-link{border-top-right-radius:0.25rem;border-bottom-right-radius:0.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#0f7864;border-color:transparent}.page-item.disabled .page-link{color:#ecf0f1;pointer-events:none;cursor:auto;background-color:#3be6c4;border-color:transparent}.pagination-lg .page-link{padding:0.75rem 1.5rem;font-size:1.171875rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:0.3rem;border-bottom-left-radius:0.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:0.3rem;border-bottom-right-radius:0.3rem}.pagination-sm .page-link{padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:0.2rem;border-bottom-left-radius:0.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:0.2rem;border-bottom-right-radius:0.2rem}.badge{display:inline-block;padding:0.25em 0.4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:0.25rem;-webkit-transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.badge{-webkit-transition:none;transition:none}}a.badge:hover,a.badge:focus{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:0.6em;padding-left:0.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#2C3E50}a.badge-primary:hover,a.badge-primary:focus{color:#fff;background-color:#1a252f}a.badge-primary:focus,a.badge-primary.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(44,62,80,0.5);box-shadow:0 0 0 0.2rem rgba(44,62,80,0.5)}.badge-secondary{color:#fff;background-color:#95a5a6}a.badge-secondary:hover,a.badge-secondary:focus{color:#fff;background-color:#798d8f}a.badge-secondary:focus,a.badge-secondary.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(149,165,166,0.5);box-shadow:0 0 0 0.2rem rgba(149,165,166,0.5)}.badge-success{color:#fff;background-color:#18BC9C}a.badge-success:hover,a.badge-success:focus{color:#fff;background-color:#128f76}a.badge-success:focus,a.badge-success.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(24,188,156,0.5);box-shadow:0 0 0 0.2rem rgba(24,188,156,0.5)}.badge-info{color:#fff;background-color:#3498DB}a.badge-info:hover,a.badge-info:focus{color:#fff;background-color:#217dbb}a.badge-info:focus,a.badge-info.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(52,152,219,0.5);box-shadow:0 0 0 0.2rem rgba(52,152,219,0.5)}.badge-warning{color:#fff;background-color:#F39C12}a.badge-warning:hover,a.badge-warning:focus{color:#fff;background-color:#c87f0a}a.badge-warning:focus,a.badge-warning.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(243,156,18,0.5);box-shadow:0 0 0 0.2rem rgba(243,156,18,0.5)}.badge-danger{color:#fff;background-color:#E74C3C}a.badge-danger:hover,a.badge-danger:focus{color:#fff;background-color:#d62c1a}a.badge-danger:focus,a.badge-danger.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(231,76,60,0.5);box-shadow:0 0 0 0.2rem rgba(231,76,60,0.5)}.badge-light{color:#212529;background-color:#ecf0f1}a.badge-light:hover,a.badge-light:focus{color:#212529;background-color:#cfd9db}a.badge-light:focus,a.badge-light.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(236,240,241,0.5);box-shadow:0 0 0 0.2rem rgba(236,240,241,0.5)}.badge-dark{color:#fff;background-color:#7b8a8b}a.badge-dark:hover,a.badge-dark:focus{color:#fff;background-color:#636f70}a.badge-dark:focus,a.badge-dark.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(123,138,139,0.5);box-shadow:0 0 0 0.2rem rgba(123,138,139,0.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#ecf0f1;border-radius:0.3rem}@media (min-width: 576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:0.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:0.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3.90625rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:0.75rem 1.25rem;color:inherit}.alert-primary{color:#17202a;background-color:#d5d8dc;border-color:#c4c9ce}.alert-primary hr{border-top-color:#b6bcc2}.alert-primary .alert-link{color:#050709}.alert-secondary{color:#4d5656;background-color:#eaeded;border-color:#e1e6e6}.alert-secondary hr{border-top-color:#d3dada}.alert-secondary .alert-link{color:#353b3b}.alert-success{color:#0c6251;background-color:#d1f2eb;border-color:#beece3}.alert-success hr{border-top-color:#aae6db}.alert-success .alert-link{color:#06352b}.alert-info{color:#1b4f72;background-color:#d6eaf8;border-color:#c6e2f5}.alert-info hr{border-top-color:#b0d7f1}.alert-info .alert-link{color:#113249}.alert-warning{color:#7e5109;background-color:#fdebd0;border-color:#fce3bd}.alert-warning hr{border-top-color:#fbd9a5}.alert-warning .alert-link{color:#4e3206}.alert-danger{color:#78281f;background-color:#fadbd8;border-color:#f8cdc8}.alert-danger hr{border-top-color:#f5b8b1}.alert-danger .alert-link{color:#4f1a15}.alert-light{color:#7b7d7d;background-color:#fbfcfc;border-color:#fafbfb}.alert-light hr{border-top-color:#ecf0f0}.alert-light .alert-link{color:#626363}.alert-dark{color:#404848;background-color:#e5e8e8;border-color:#dadedf}.alert-dark hr{border-top-color:#ccd2d3}.alert-dark .alert-link{color:#282d2d}@-webkit-keyframes progress-bar-stripes{from{background-position:0.625rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:0.625rem 0}to{background-position:0 0}}.progress{display:-webkit-box;display:-ms-flexbox;display:flex;height:0.625rem;overflow:hidden;font-size:0.625rem;background-color:#ecf0f1;border-radius:0.25rem}.progress-bar{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#2C3E50;-webkit-transition:width 0.6s ease;transition:width 0.6s ease}@media (prefers-reduced-motion: reduce){.progress-bar{-webkit-transition:none;transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:0.625rem 0.625rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion: reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.media-body{-webkit-box-flex:1;-ms-flex:1;flex:1}.list-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#7b8a8b;text-align:inherit}.list-group-item-action:hover,.list-group-item-action:focus{z-index:1;color:#7b8a8b;text-decoration:none;background-color:#ecf0f1}.list-group-item-action:active{color:#212529;background-color:#ecf0f1}.list-group-item{position:relative;display:block;padding:0.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,0.125)}.list-group-item:first-child{border-top-left-radius:0.25rem;border-top-right-radius:0.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:0.25rem;border-bottom-left-radius:0.25rem}.list-group-item.disabled,.list-group-item:disabled{color:#95a5a6;pointer-events:none;background-color:#ecf0f1}.list-group-item.active{z-index:2;color:#fff;background-color:#2C3E50;border-color:#2C3E50}.list-group-horizontal{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.list-group-horizontal .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal .list-group-item:first-child{border-top-left-radius:0.25rem;border-bottom-left-radius:0.25rem;border-top-right-radius:0}.list-group-horizontal .list-group-item:last-child{margin-right:0;border-top-right-radius:0.25rem;border-bottom-right-radius:0.25rem;border-bottom-left-radius:0}@media (min-width: 576px){.list-group-horizontal-sm{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-sm .list-group-item:first-child{border-top-left-radius:0.25rem;border-bottom-left-radius:0.25rem;border-top-right-radius:0}.list-group-horizontal-sm .list-group-item:last-child{margin-right:0;border-top-right-radius:0.25rem;border-bottom-right-radius:0.25rem;border-bottom-left-radius:0}}@media (min-width: 768px){.list-group-horizontal-md{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-md .list-group-item:first-child{border-top-left-radius:0.25rem;border-bottom-left-radius:0.25rem;border-top-right-radius:0}.list-group-horizontal-md .list-group-item:last-child{margin-right:0;border-top-right-radius:0.25rem;border-bottom-right-radius:0.25rem;border-bottom-left-radius:0}}@media (min-width: 992px){.list-group-horizontal-lg{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-lg .list-group-item:first-child{border-top-left-radius:0.25rem;border-bottom-left-radius:0.25rem;border-top-right-radius:0}.list-group-horizontal-lg .list-group-item:last-child{margin-right:0;border-top-right-radius:0.25rem;border-bottom-right-radius:0.25rem;border-bottom-left-radius:0}}@media (min-width: 1200px){.list-group-horizontal-xl{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-xl .list-group-item:first-child{border-top-left-radius:0.25rem;border-bottom-left-radius:0.25rem;border-top-right-radius:0}.list-group-horizontal-xl .list-group-item:last-child{margin-right:0;border-top-right-radius:0.25rem;border-bottom-right-radius:0.25rem;border-bottom-left-radius:0}}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush .list-group-item:last-child{margin-bottom:-1px}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{margin-bottom:0;border-bottom:0}.list-group-item-primary{color:#17202a;background-color:#c4c9ce}.list-group-item-primary.list-group-item-action:hover,.list-group-item-primary.list-group-item-action:focus{color:#17202a;background-color:#b6bcc2}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#17202a;border-color:#17202a}.list-group-item-secondary{color:#4d5656;background-color:#e1e6e6}.list-group-item-secondary.list-group-item-action:hover,.list-group-item-secondary.list-group-item-action:focus{color:#4d5656;background-color:#d3dada}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#4d5656;border-color:#4d5656}.list-group-item-success{color:#0c6251;background-color:#beece3}.list-group-item-success.list-group-item-action:hover,.list-group-item-success.list-group-item-action:focus{color:#0c6251;background-color:#aae6db}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#0c6251;border-color:#0c6251}.list-group-item-info{color:#1b4f72;background-color:#c6e2f5}.list-group-item-info.list-group-item-action:hover,.list-group-item-info.list-group-item-action:focus{color:#1b4f72;background-color:#b0d7f1}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#1b4f72;border-color:#1b4f72}.list-group-item-warning{color:#7e5109;background-color:#fce3bd}.list-group-item-warning.list-group-item-action:hover,.list-group-item-warning.list-group-item-action:focus{color:#7e5109;background-color:#fbd9a5}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#7e5109;border-color:#7e5109}.list-group-item-danger{color:#78281f;background-color:#f8cdc8}.list-group-item-danger.list-group-item-action:hover,.list-group-item-danger.list-group-item-action:focus{color:#78281f;background-color:#f5b8b1}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#78281f;border-color:#78281f}.list-group-item-light{color:#7b7d7d;background-color:#fafbfb}.list-group-item-light.list-group-item-action:hover,.list-group-item-light.list-group-item-action:focus{color:#7b7d7d;background-color:#ecf0f0}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#7b7d7d;border-color:#7b7d7d}.list-group-item-dark{color:#404848;background-color:#dadedf}.list-group-item-dark.list-group-item-action:hover,.list-group-item-dark.list-group-item-action:focus{color:#404848;background-color:#ccd2d3}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#404848;border-color:#404848}.close{float:right;font-size:1.40625rem;font-weight:700;line-height:1;color:#fff;text-shadow:none;opacity:.5}.close:hover{color:#fff;text-decoration:none}.close:not(:disabled):not(.disabled):hover,.close:not(:disabled):not(.disabled):focus{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:0.875rem;background-color:rgba(255,255,255,0.85);background-clip:padding-box;border:1px solid rgba(0,0,0,0.1);-webkit-box-shadow:0 0.25rem 0.75rem rgba(0,0,0,0.1);box-shadow:0 0.25rem 0.75rem rgba(0,0,0,0.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:0.25rem}.toast:not(:last-child){margin-bottom:0.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:0.25rem 0.75rem;color:#95a5a6;background-color:rgba(255,255,255,0.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,0.05)}.toast-body{padding:0.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:0.5rem;pointer-events:none}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform 0.3s ease-out;transition:-webkit-transform 0.3s ease-out;transition:transform 0.3s ease-out;transition:transform 0.3s ease-out, -webkit-transform 0.3s ease-out;-webkit-transform:translate(0, -50px);transform:translate(0, -50px)}@media (prefers-reduced-motion: reduce){.modal.fade .modal-dialog{-webkit-transition:none;transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal-dialog-scrollable{display:-webkit-box;display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-header,.modal-dialog-scrollable .modal-footer{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);content:""}.modal-dialog-centered.modal-dialog-scrollable{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,0.2);border-radius:0.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:0.5}.modal-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:0.3rem;border-top-right-radius:0.3rem}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #dee2e6;border-bottom-right-radius:0.3rem;border-bottom-left-radius:0.3rem}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width: 576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width: 992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width: 1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:"Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:0.8203125rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:0.9}.tooltip .arrow{position:absolute;display:block;width:0.8rem;height:0.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-top,.bs-tooltip-auto[x-placement^="top"]{padding:0.4rem 0}.bs-tooltip-top .arrow,.bs-tooltip-auto[x-placement^="top"] .arrow{bottom:0}.bs-tooltip-top .arrow::before,.bs-tooltip-auto[x-placement^="top"] .arrow::before{top:0;border-width:0.4rem 0.4rem 0;border-top-color:#000}.bs-tooltip-right,.bs-tooltip-auto[x-placement^="right"]{padding:0 0.4rem}.bs-tooltip-right .arrow,.bs-tooltip-auto[x-placement^="right"] .arrow{left:0;width:0.4rem;height:0.8rem}.bs-tooltip-right .arrow::before,.bs-tooltip-auto[x-placement^="right"] .arrow::before{right:0;border-width:0.4rem 0.4rem 0.4rem 0;border-right-color:#000}.bs-tooltip-bottom,.bs-tooltip-auto[x-placement^="bottom"]{padding:0.4rem 0}.bs-tooltip-bottom .arrow,.bs-tooltip-auto[x-placement^="bottom"] .arrow{top:0}.bs-tooltip-bottom .arrow::before,.bs-tooltip-auto[x-placement^="bottom"] .arrow::before{bottom:0;border-width:0 0.4rem 0.4rem;border-bottom-color:#000}.bs-tooltip-left,.bs-tooltip-auto[x-placement^="left"]{padding:0 0.4rem}.bs-tooltip-left .arrow,.bs-tooltip-auto[x-placement^="left"] .arrow{right:0;width:0.4rem;height:0.8rem}.bs-tooltip-left .arrow::before,.bs-tooltip-auto[x-placement^="left"] .arrow::before{left:0;border-width:0.4rem 0 0.4rem 0.4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:0.25rem 0.5rem;color:#fff;text-align:center;background-color:#000;border-radius:0.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:"Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:0.8203125rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,0.2);border-radius:0.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:0.5rem;margin:0 0.3rem}.popover .arrow::before,.popover .arrow::after{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-top,.bs-popover-auto[x-placement^="top"]{margin-bottom:0.5rem}.bs-popover-top>.arrow,.bs-popover-auto[x-placement^="top"]>.arrow{bottom:calc((0.5rem + 1px) * -1)}.bs-popover-top>.arrow::before,.bs-popover-auto[x-placement^="top"]>.arrow::before{bottom:0;border-width:0.5rem 0.5rem 0;border-top-color:rgba(0,0,0,0.25)}.bs-popover-top>.arrow::after,.bs-popover-auto[x-placement^="top"]>.arrow::after{bottom:1px;border-width:0.5rem 0.5rem 0;border-top-color:#fff}.bs-popover-right,.bs-popover-auto[x-placement^="right"]{margin-left:0.5rem}.bs-popover-right>.arrow,.bs-popover-auto[x-placement^="right"]>.arrow{left:calc((0.5rem + 1px) * -1);width:0.5rem;height:1rem;margin:0.3rem 0}.bs-popover-right>.arrow::before,.bs-popover-auto[x-placement^="right"]>.arrow::before{left:0;border-width:0.5rem 0.5rem 0.5rem 0;border-right-color:rgba(0,0,0,0.25)}.bs-popover-right>.arrow::after,.bs-popover-auto[x-placement^="right"]>.arrow::after{left:1px;border-width:0.5rem 0.5rem 0.5rem 0;border-right-color:#fff}.bs-popover-bottom,.bs-popover-auto[x-placement^="bottom"]{margin-top:0.5rem}.bs-popover-bottom>.arrow,.bs-popover-auto[x-placement^="bottom"]>.arrow{top:calc((0.5rem + 1px) * -1)}.bs-popover-bottom>.arrow::before,.bs-popover-auto[x-placement^="bottom"]>.arrow::before{top:0;border-width:0 0.5rem 0.5rem 0.5rem;border-bottom-color:rgba(0,0,0,0.25)}.bs-popover-bottom>.arrow::after,.bs-popover-auto[x-placement^="bottom"]>.arrow::after{top:1px;border-width:0 0.5rem 0.5rem 0.5rem;border-bottom-color:#fff}.bs-popover-bottom .popover-header::before,.bs-popover-auto[x-placement^="bottom"] .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-0.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-left,.bs-popover-auto[x-placement^="left"]{margin-right:0.5rem}.bs-popover-left>.arrow,.bs-popover-auto[x-placement^="left"]>.arrow{right:calc((0.5rem + 1px) * -1);width:0.5rem;height:1rem;margin:0.3rem 0}.bs-popover-left>.arrow::before,.bs-popover-auto[x-placement^="left"]>.arrow::before{right:0;border-width:0.5rem 0 0.5rem 0.5rem;border-left-color:rgba(0,0,0,0.25)}.bs-popover-left>.arrow::after,.bs-popover-auto[x-placement^="left"]>.arrow::after{right:1px;border-width:0.5rem 0 0.5rem 0.5rem;border-left-color:#fff}.popover-header{padding:0.5rem 0.75rem;margin-bottom:0;font-size:0.9375rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(0.3rem - 1px);border-top-right-radius:calc(0.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:0.5rem 0.75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transition:-webkit-transform 0.6s ease-in-out;transition:-webkit-transform 0.6s ease-in-out;transition:transform 0.6s ease-in-out;transition:transform 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out}@media (prefers-reduced-motion: reduce){.carousel-item{-webkit-transition:none;transition:none}}.carousel-item.active,.carousel-item-next,.carousel-item-prev{display:block}.carousel-item-next:not(.carousel-item-left),.active.carousel-item-right{-webkit-transform:translateX(100%);transform:translateX(100%)}.carousel-item-prev:not(.carousel-item-right),.active.carousel-item-left{-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;-webkit-transition-property:opacity;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item.active,.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;-webkit-transition:0s 0.6s opacity;transition:0s 0.6s opacity}@media (prefers-reduced-motion: reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{-webkit-transition:none;transition:none}}.carousel-control-prev,.carousel-control-next{position:absolute;top:0;bottom:0;z-index:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:0.5;-webkit-transition:opacity 0.15s ease;transition:opacity 0.15s ease}@media (prefers-reduced-motion: reduce){.carousel-control-prev,.carousel-control-next{-webkit-transition:none;transition:none}}.carousel-control-prev:hover,.carousel-control-prev:focus,.carousel-control-next:hover,.carousel-control-next:focus{color:#fff;text-decoration:none;outline:0;opacity:0.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-prev-icon,.carousel-control-next-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50% / 100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{-webkit-box-sizing:content-box;box-sizing:content-box;-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;-webkit-transition:opacity 0.6s ease;transition:opacity 0.6s ease}@media (prefers-reduced-motion: reduce){.carousel-indicators li{-webkit-transition:none;transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:0.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:0.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline !important}.align-top{vertical-align:top !important}.align-middle{vertical-align:middle !important}.align-bottom{vertical-align:bottom !important}.align-text-bottom{vertical-align:text-bottom !important}.align-text-top{vertical-align:text-top !important}.bg-primary{background-color:#2C3E50 !important}a.bg-primary:hover,a.bg-primary:focus,button.bg-primary:hover,button.bg-primary:focus{background-color:#1a252f !important}.bg-secondary{background-color:#95a5a6 !important}a.bg-secondary:hover,a.bg-secondary:focus,button.bg-secondary:hover,button.bg-secondary:focus{background-color:#798d8f !important}.bg-success{background-color:#18BC9C !important}a.bg-success:hover,a.bg-success:focus,button.bg-success:hover,button.bg-success:focus{background-color:#128f76 !important}.bg-info{background-color:#3498DB !important}a.bg-info:hover,a.bg-info:focus,button.bg-info:hover,button.bg-info:focus{background-color:#217dbb !important}.bg-warning{background-color:#F39C12 !important}a.bg-warning:hover,a.bg-warning:focus,button.bg-warning:hover,button.bg-warning:focus{background-color:#c87f0a !important}.bg-danger{background-color:#E74C3C !important}a.bg-danger:hover,a.bg-danger:focus,button.bg-danger:hover,button.bg-danger:focus{background-color:#d62c1a !important}.bg-light{background-color:#ecf0f1 !important}a.bg-light:hover,a.bg-light:focus,button.bg-light:hover,button.bg-light:focus{background-color:#cfd9db !important}.bg-dark{background-color:#7b8a8b !important}a.bg-dark:hover,a.bg-dark:focus,button.bg-dark:hover,button.bg-dark:focus{background-color:#636f70 !important}.bg-white{background-color:#fff !important}.bg-transparent{background-color:transparent !important}.border{border:1px solid #dee2e6 !important}.border-top{border-top:1px solid #dee2e6 !important}.border-right{border-right:1px solid #dee2e6 !important}.border-bottom{border-bottom:1px solid #dee2e6 !important}.border-left{border-left:1px solid #dee2e6 !important}.border-0{border:0 !important}.border-top-0{border-top:0 !important}.border-right-0{border-right:0 !important}.border-bottom-0{border-bottom:0 !important}.border-left-0{border-left:0 !important}.border-primary{border-color:#2C3E50 !important}.border-secondary{border-color:#95a5a6 !important}.border-success{border-color:#18BC9C !important}.border-info{border-color:#3498DB !important}.border-warning{border-color:#F39C12 !important}.border-danger{border-color:#E74C3C !important}.border-light{border-color:#ecf0f1 !important}.border-dark{border-color:#7b8a8b !important}.border-white{border-color:#fff !important}.rounded-sm{border-radius:0.2rem !important}.rounded{border-radius:0.25rem !important}.rounded-top{border-top-left-radius:0.25rem !important;border-top-right-radius:0.25rem !important}.rounded-right{border-top-right-radius:0.25rem !important;border-bottom-right-radius:0.25rem !important}.rounded-bottom{border-bottom-right-radius:0.25rem !important;border-bottom-left-radius:0.25rem !important}.rounded-left{border-top-left-radius:0.25rem !important;border-bottom-left-radius:0.25rem !important}.rounded-lg{border-radius:0.3rem !important}.rounded-circle{border-radius:50% !important}.rounded-pill{border-radius:50rem !important}.rounded-0{border-radius:0 !important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none !important}.d-inline{display:inline !important}.d-inline-block{display:inline-block !important}.d-block{display:block !important}.d-table{display:table !important}.d-table-row{display:table-row !important}.d-table-cell{display:table-cell !important}.d-flex{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-inline-flex{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important}@media (min-width: 576px){.d-sm-none{display:none !important}.d-sm-inline{display:inline !important}.d-sm-inline-block{display:inline-block !important}.d-sm-block{display:block !important}.d-sm-table{display:table !important}.d-sm-table-row{display:table-row !important}.d-sm-table-cell{display:table-cell !important}.d-sm-flex{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-sm-inline-flex{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important}}@media (min-width: 768px){.d-md-none{display:none !important}.d-md-inline{display:inline !important}.d-md-inline-block{display:inline-block !important}.d-md-block{display:block !important}.d-md-table{display:table !important}.d-md-table-row{display:table-row !important}.d-md-table-cell{display:table-cell !important}.d-md-flex{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-md-inline-flex{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important}}@media (min-width: 992px){.d-lg-none{display:none !important}.d-lg-inline{display:inline !important}.d-lg-inline-block{display:inline-block !important}.d-lg-block{display:block !important}.d-lg-table{display:table !important}.d-lg-table-row{display:table-row !important}.d-lg-table-cell{display:table-cell !important}.d-lg-flex{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-lg-inline-flex{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important}}@media (min-width: 1200px){.d-xl-none{display:none !important}.d-xl-inline{display:inline !important}.d-xl-inline-block{display:inline-block !important}.d-xl-block{display:block !important}.d-xl-table{display:table !important}.d-xl-table-row{display:table-row !important}.d-xl-table-cell{display:table-cell !important}.d-xl-flex{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-xl-inline-flex{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important}}@media print{.d-print-none{display:none !important}.d-print-inline{display:inline !important}.d-print-inline-block{display:inline-block !important}.d-print-block{display:block !important}.d-print-table{display:table !important}.d-print-table-row{display:table-row !important}.d-print-table-cell{display:table-cell !important}.d-print-flex{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-print-inline-flex{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.8571428571%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-ms-flex-direction:column-reverse !important;flex-direction:column-reverse !important}.flex-wrap{-ms-flex-wrap:wrap !important;flex-wrap:wrap !important}.flex-nowrap{-ms-flex-wrap:nowrap !important;flex-wrap:nowrap !important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse !important;flex-wrap:wrap-reverse !important}.flex-fill{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-grow-1{-webkit-box-flex:1 !important;-ms-flex-positive:1 !important;flex-grow:1 !important}.flex-shrink-0{-ms-flex-negative:0 !important;flex-shrink:0 !important}.flex-shrink-1{-ms-flex-negative:1 !important;flex-shrink:1 !important}.justify-content-start{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-between{-webkit-box-pack:justify !important;-ms-flex-pack:justify !important;justify-content:space-between !important}.justify-content-around{-ms-flex-pack:distribute !important;justify-content:space-around !important}.align-items-start{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-stretch{-webkit-box-align:stretch !important;-ms-flex-align:stretch !important;align-items:stretch !important}.align-content-start{-ms-flex-line-pack:start !important;align-content:flex-start !important}.align-content-end{-ms-flex-line-pack:end !important;align-content:flex-end !important}.align-content-center{-ms-flex-line-pack:center !important;align-content:center !important}.align-content-between{-ms-flex-line-pack:justify !important;align-content:space-between !important}.align-content-around{-ms-flex-line-pack:distribute !important;align-content:space-around !important}.align-content-stretch{-ms-flex-line-pack:stretch !important;align-content:stretch !important}.align-self-auto{-ms-flex-item-align:auto !important;align-self:auto !important}.align-self-start{-ms-flex-item-align:start !important;align-self:flex-start !important}.align-self-end{-ms-flex-item-align:end !important;align-self:flex-end !important}.align-self-center{-ms-flex-item-align:center !important;align-self:center !important}.align-self-baseline{-ms-flex-item-align:baseline !important;align-self:baseline !important}.align-self-stretch{-ms-flex-item-align:stretch !important;align-self:stretch !important}@media (min-width: 576px){.flex-sm-row{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-sm-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-sm-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-sm-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-ms-flex-direction:column-reverse !important;flex-direction:column-reverse !important}.flex-sm-wrap{-ms-flex-wrap:wrap !important;flex-wrap:wrap !important}.flex-sm-nowrap{-ms-flex-wrap:nowrap !important;flex-wrap:nowrap !important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse !important;flex-wrap:wrap-reverse !important}.flex-sm-fill{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-sm-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-sm-grow-1{-webkit-box-flex:1 !important;-ms-flex-positive:1 !important;flex-grow:1 !important}.flex-sm-shrink-0{-ms-flex-negative:0 !important;flex-shrink:0 !important}.flex-sm-shrink-1{-ms-flex-negative:1 !important;flex-shrink:1 !important}.justify-content-sm-start{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-sm-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-sm-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-sm-between{-webkit-box-pack:justify !important;-ms-flex-pack:justify !important;justify-content:space-between !important}.justify-content-sm-around{-ms-flex-pack:distribute !important;justify-content:space-around !important}.align-items-sm-start{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-sm-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-sm-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-sm-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-sm-stretch{-webkit-box-align:stretch !important;-ms-flex-align:stretch !important;align-items:stretch !important}.align-content-sm-start{-ms-flex-line-pack:start !important;align-content:flex-start !important}.align-content-sm-end{-ms-flex-line-pack:end !important;align-content:flex-end !important}.align-content-sm-center{-ms-flex-line-pack:center !important;align-content:center !important}.align-content-sm-between{-ms-flex-line-pack:justify !important;align-content:space-between !important}.align-content-sm-around{-ms-flex-line-pack:distribute !important;align-content:space-around !important}.align-content-sm-stretch{-ms-flex-line-pack:stretch !important;align-content:stretch !important}.align-self-sm-auto{-ms-flex-item-align:auto !important;align-self:auto !important}.align-self-sm-start{-ms-flex-item-align:start !important;align-self:flex-start !important}.align-self-sm-end{-ms-flex-item-align:end !important;align-self:flex-end !important}.align-self-sm-center{-ms-flex-item-align:center !important;align-self:center !important}.align-self-sm-baseline{-ms-flex-item-align:baseline !important;align-self:baseline !important}.align-self-sm-stretch{-ms-flex-item-align:stretch !important;align-self:stretch !important}}@media (min-width: 768px){.flex-md-row{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-md-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-md-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-md-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-ms-flex-direction:column-reverse !important;flex-direction:column-reverse !important}.flex-md-wrap{-ms-flex-wrap:wrap !important;flex-wrap:wrap !important}.flex-md-nowrap{-ms-flex-wrap:nowrap !important;flex-wrap:nowrap !important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse !important;flex-wrap:wrap-reverse !important}.flex-md-fill{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-md-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-md-grow-1{-webkit-box-flex:1 !important;-ms-flex-positive:1 !important;flex-grow:1 !important}.flex-md-shrink-0{-ms-flex-negative:0 !important;flex-shrink:0 !important}.flex-md-shrink-1{-ms-flex-negative:1 !important;flex-shrink:1 !important}.justify-content-md-start{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-md-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-md-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-md-between{-webkit-box-pack:justify !important;-ms-flex-pack:justify !important;justify-content:space-between !important}.justify-content-md-around{-ms-flex-pack:distribute !important;justify-content:space-around !important}.align-items-md-start{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-md-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-md-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-md-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-md-stretch{-webkit-box-align:stretch !important;-ms-flex-align:stretch !important;align-items:stretch !important}.align-content-md-start{-ms-flex-line-pack:start !important;align-content:flex-start !important}.align-content-md-end{-ms-flex-line-pack:end !important;align-content:flex-end !important}.align-content-md-center{-ms-flex-line-pack:center !important;align-content:center !important}.align-content-md-between{-ms-flex-line-pack:justify !important;align-content:space-between !important}.align-content-md-around{-ms-flex-line-pack:distribute !important;align-content:space-around !important}.align-content-md-stretch{-ms-flex-line-pack:stretch !important;align-content:stretch !important}.align-self-md-auto{-ms-flex-item-align:auto !important;align-self:auto !important}.align-self-md-start{-ms-flex-item-align:start !important;align-self:flex-start !important}.align-self-md-end{-ms-flex-item-align:end !important;align-self:flex-end !important}.align-self-md-center{-ms-flex-item-align:center !important;align-self:center !important}.align-self-md-baseline{-ms-flex-item-align:baseline !important;align-self:baseline !important}.align-self-md-stretch{-ms-flex-item-align:stretch !important;align-self:stretch !important}}@media (min-width: 992px){.flex-lg-row{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-lg-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-lg-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-lg-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-ms-flex-direction:column-reverse !important;flex-direction:column-reverse !important}.flex-lg-wrap{-ms-flex-wrap:wrap !important;flex-wrap:wrap !important}.flex-lg-nowrap{-ms-flex-wrap:nowrap !important;flex-wrap:nowrap !important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse !important;flex-wrap:wrap-reverse !important}.flex-lg-fill{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-lg-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-lg-grow-1{-webkit-box-flex:1 !important;-ms-flex-positive:1 !important;flex-grow:1 !important}.flex-lg-shrink-0{-ms-flex-negative:0 !important;flex-shrink:0 !important}.flex-lg-shrink-1{-ms-flex-negative:1 !important;flex-shrink:1 !important}.justify-content-lg-start{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-lg-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-lg-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-lg-between{-webkit-box-pack:justify !important;-ms-flex-pack:justify !important;justify-content:space-between !important}.justify-content-lg-around{-ms-flex-pack:distribute !important;justify-content:space-around !important}.align-items-lg-start{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-lg-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-lg-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-lg-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-lg-stretch{-webkit-box-align:stretch !important;-ms-flex-align:stretch !important;align-items:stretch !important}.align-content-lg-start{-ms-flex-line-pack:start !important;align-content:flex-start !important}.align-content-lg-end{-ms-flex-line-pack:end !important;align-content:flex-end !important}.align-content-lg-center{-ms-flex-line-pack:center !important;align-content:center !important}.align-content-lg-between{-ms-flex-line-pack:justify !important;align-content:space-between !important}.align-content-lg-around{-ms-flex-line-pack:distribute !important;align-content:space-around !important}.align-content-lg-stretch{-ms-flex-line-pack:stretch !important;align-content:stretch !important}.align-self-lg-auto{-ms-flex-item-align:auto !important;align-self:auto !important}.align-self-lg-start{-ms-flex-item-align:start !important;align-self:flex-start !important}.align-self-lg-end{-ms-flex-item-align:end !important;align-self:flex-end !important}.align-self-lg-center{-ms-flex-item-align:center !important;align-self:center !important}.align-self-lg-baseline{-ms-flex-item-align:baseline !important;align-self:baseline !important}.align-self-lg-stretch{-ms-flex-item-align:stretch !important;align-self:stretch !important}}@media (min-width: 1200px){.flex-xl-row{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-xl-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-xl-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-xl-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-ms-flex-direction:column-reverse !important;flex-direction:column-reverse !important}.flex-xl-wrap{-ms-flex-wrap:wrap !important;flex-wrap:wrap !important}.flex-xl-nowrap{-ms-flex-wrap:nowrap !important;flex-wrap:nowrap !important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse !important;flex-wrap:wrap-reverse !important}.flex-xl-fill{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-xl-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-xl-grow-1{-webkit-box-flex:1 !important;-ms-flex-positive:1 !important;flex-grow:1 !important}.flex-xl-shrink-0{-ms-flex-negative:0 !important;flex-shrink:0 !important}.flex-xl-shrink-1{-ms-flex-negative:1 !important;flex-shrink:1 !important}.justify-content-xl-start{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-xl-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-xl-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-xl-between{-webkit-box-pack:justify !important;-ms-flex-pack:justify !important;justify-content:space-between !important}.justify-content-xl-around{-ms-flex-pack:distribute !important;justify-content:space-around !important}.align-items-xl-start{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-xl-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-xl-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-xl-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-xl-stretch{-webkit-box-align:stretch !important;-ms-flex-align:stretch !important;align-items:stretch !important}.align-content-xl-start{-ms-flex-line-pack:start !important;align-content:flex-start !important}.align-content-xl-end{-ms-flex-line-pack:end !important;align-content:flex-end !important}.align-content-xl-center{-ms-flex-line-pack:center !important;align-content:center !important}.align-content-xl-between{-ms-flex-line-pack:justify !important;align-content:space-between !important}.align-content-xl-around{-ms-flex-line-pack:distribute !important;align-content:space-around !important}.align-content-xl-stretch{-ms-flex-line-pack:stretch !important;align-content:stretch !important}.align-self-xl-auto{-ms-flex-item-align:auto !important;align-self:auto !important}.align-self-xl-start{-ms-flex-item-align:start !important;align-self:flex-start !important}.align-self-xl-end{-ms-flex-item-align:end !important;align-self:flex-end !important}.align-self-xl-center{-ms-flex-item-align:center !important;align-self:center !important}.align-self-xl-baseline{-ms-flex-item-align:baseline !important;align-self:baseline !important}.align-self-xl-stretch{-ms-flex-item-align:stretch !important;align-self:stretch !important}}.float-left{float:left !important}.float-right{float:right !important}.float-none{float:none !important}@media (min-width: 576px){.float-sm-left{float:left !important}.float-sm-right{float:right !important}.float-sm-none{float:none !important}}@media (min-width: 768px){.float-md-left{float:left !important}.float-md-right{float:right !important}.float-md-none{float:none !important}}@media (min-width: 992px){.float-lg-left{float:left !important}.float-lg-right{float:right !important}.float-lg-none{float:none !important}}@media (min-width: 1200px){.float-xl-left{float:left !important}.float-xl-right{float:right !important}.float-xl-none{float:none !important}}.overflow-auto{overflow:auto !important}.overflow-hidden{overflow:hidden !important}.position-static{position:static !important}.position-relative{position:relative !important}.position-absolute{position:absolute !important}.position-fixed{position:fixed !important}.position-sticky{position:-webkit-sticky !important;position:sticky !important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports (position: -webkit-sticky) or (position: sticky){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{-webkit-box-shadow:0 0.125rem 0.25rem rgba(0,0,0,0.075) !important;box-shadow:0 0.125rem 0.25rem rgba(0,0,0,0.075) !important}.shadow{-webkit-box-shadow:0 0.5rem 1rem rgba(0,0,0,0.15) !important;box-shadow:0 0.5rem 1rem rgba(0,0,0,0.15) !important}.shadow-lg{-webkit-box-shadow:0 1rem 3rem rgba(0,0,0,0.175) !important;box-shadow:0 1rem 3rem rgba(0,0,0,0.175) !important}.shadow-none{-webkit-box-shadow:none !important;box-shadow:none !important}.w-25{width:25% !important}.w-50{width:50% !important}.w-75{width:75% !important}.w-100{width:100% !important}.w-auto{width:auto !important}.h-25{height:25% !important}.h-50{height:50% !important}.h-75{height:75% !important}.h-100{height:100% !important}.h-auto{height:auto !important}.mw-100{max-width:100% !important}.mh-100{max-height:100% !important}.min-vw-100{min-width:100vw !important}.min-vh-100{min-height:100vh !important}.vw-100{width:100vw !important}.vh-100{height:100vh !important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.m-0{margin:0 !important}.mt-0,.my-0{margin-top:0 !important}.mr-0,.mx-0{margin-right:0 !important}.mb-0,.my-0{margin-bottom:0 !important}.ml-0,.mx-0{margin-left:0 !important}.m-1{margin:0.25rem !important}.mt-1,.my-1{margin-top:0.25rem !important}.mr-1,.mx-1{margin-right:0.25rem !important}.mb-1,.my-1{margin-bottom:0.25rem !important}.ml-1,.mx-1{margin-left:0.25rem !important}.m-2{margin:0.5rem !important}.mt-2,.my-2{margin-top:0.5rem !important}.mr-2,.mx-2{margin-right:0.5rem !important}.mb-2,.my-2{margin-bottom:0.5rem !important}.ml-2,.mx-2{margin-left:0.5rem !important}.m-3{margin:1rem !important}.mt-3,.my-3{margin-top:1rem !important}.mr-3,.mx-3{margin-right:1rem !important}.mb-3,.my-3{margin-bottom:1rem !important}.ml-3,.mx-3{margin-left:1rem !important}.m-4{margin:1.5rem !important}.mt-4,.my-4{margin-top:1.5rem !important}.mr-4,.mx-4{margin-right:1.5rem !important}.mb-4,.my-4{margin-bottom:1.5rem !important}.ml-4,.mx-4{margin-left:1.5rem !important}.m-5{margin:3rem !important}.mt-5,.my-5{margin-top:3rem !important}.mr-5,.mx-5{margin-right:3rem !important}.mb-5,.my-5{margin-bottom:3rem !important}.ml-5,.mx-5{margin-left:3rem !important}.p-0{padding:0 !important}.pt-0,.py-0{padding-top:0 !important}.pr-0,.px-0{padding-right:0 !important}.pb-0,.py-0{padding-bottom:0 !important}.pl-0,.px-0{padding-left:0 !important}.p-1{padding:0.25rem !important}.pt-1,.py-1{padding-top:0.25rem !important}.pr-1,.px-1{padding-right:0.25rem !important}.pb-1,.py-1{padding-bottom:0.25rem !important}.pl-1,.px-1{padding-left:0.25rem !important}.p-2{padding:0.5rem !important}.pt-2,.py-2{padding-top:0.5rem !important}.pr-2,.px-2{padding-right:0.5rem !important}.pb-2,.py-2{padding-bottom:0.5rem !important}.pl-2,.px-2{padding-left:0.5rem !important}.p-3{padding:1rem !important}.pt-3,.py-3{padding-top:1rem !important}.pr-3,.px-3{padding-right:1rem !important}.pb-3,.py-3{padding-bottom:1rem !important}.pl-3,.px-3{padding-left:1rem !important}.p-4{padding:1.5rem !important}.pt-4,.py-4{padding-top:1.5rem !important}.pr-4,.px-4{padding-right:1.5rem !important}.pb-4,.py-4{padding-bottom:1.5rem !important}.pl-4,.px-4{padding-left:1.5rem !important}.p-5{padding:3rem !important}.pt-5,.py-5{padding-top:3rem !important}.pr-5,.px-5{padding-right:3rem !important}.pb-5,.py-5{padding-bottom:3rem !important}.pl-5,.px-5{padding-left:3rem !important}.m-n1{margin:-0.25rem !important}.mt-n1,.my-n1{margin-top:-0.25rem !important}.mr-n1,.mx-n1{margin-right:-0.25rem !important}.mb-n1,.my-n1{margin-bottom:-0.25rem !important}.ml-n1,.mx-n1{margin-left:-0.25rem !important}.m-n2{margin:-0.5rem !important}.mt-n2,.my-n2{margin-top:-0.5rem !important}.mr-n2,.mx-n2{margin-right:-0.5rem !important}.mb-n2,.my-n2{margin-bottom:-0.5rem !important}.ml-n2,.mx-n2{margin-left:-0.5rem !important}.m-n3{margin:-1rem !important}.mt-n3,.my-n3{margin-top:-1rem !important}.mr-n3,.mx-n3{margin-right:-1rem !important}.mb-n3,.my-n3{margin-bottom:-1rem !important}.ml-n3,.mx-n3{margin-left:-1rem !important}.m-n4{margin:-1.5rem !important}.mt-n4,.my-n4{margin-top:-1.5rem !important}.mr-n4,.mx-n4{margin-right:-1.5rem !important}.mb-n4,.my-n4{margin-bottom:-1.5rem !important}.ml-n4,.mx-n4{margin-left:-1.5rem !important}.m-n5{margin:-3rem !important}.mt-n5,.my-n5{margin-top:-3rem !important}.mr-n5,.mx-n5{margin-right:-3rem !important}.mb-n5,.my-n5{margin-bottom:-3rem !important}.ml-n5,.mx-n5{margin-left:-3rem !important}.m-auto{margin:auto !important}.mt-auto,.my-auto{margin-top:auto !important}.mr-auto,.mx-auto{margin-right:auto !important}.mb-auto,.my-auto{margin-bottom:auto !important}.ml-auto,.mx-auto{margin-left:auto !important}@media (min-width: 576px){.m-sm-0{margin:0 !important}.mt-sm-0,.my-sm-0{margin-top:0 !important}.mr-sm-0,.mx-sm-0{margin-right:0 !important}.mb-sm-0,.my-sm-0{margin-bottom:0 !important}.ml-sm-0,.mx-sm-0{margin-left:0 !important}.m-sm-1{margin:0.25rem !important}.mt-sm-1,.my-sm-1{margin-top:0.25rem !important}.mr-sm-1,.mx-sm-1{margin-right:0.25rem !important}.mb-sm-1,.my-sm-1{margin-bottom:0.25rem !important}.ml-sm-1,.mx-sm-1{margin-left:0.25rem !important}.m-sm-2{margin:0.5rem !important}.mt-sm-2,.my-sm-2{margin-top:0.5rem !important}.mr-sm-2,.mx-sm-2{margin-right:0.5rem !important}.mb-sm-2,.my-sm-2{margin-bottom:0.5rem !important}.ml-sm-2,.mx-sm-2{margin-left:0.5rem !important}.m-sm-3{margin:1rem !important}.mt-sm-3,.my-sm-3{margin-top:1rem !important}.mr-sm-3,.mx-sm-3{margin-right:1rem !important}.mb-sm-3,.my-sm-3{margin-bottom:1rem !important}.ml-sm-3,.mx-sm-3{margin-left:1rem !important}.m-sm-4{margin:1.5rem !important}.mt-sm-4,.my-sm-4{margin-top:1.5rem !important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem !important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem !important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem !important}.m-sm-5{margin:3rem !important}.mt-sm-5,.my-sm-5{margin-top:3rem !important}.mr-sm-5,.mx-sm-5{margin-right:3rem !important}.mb-sm-5,.my-sm-5{margin-bottom:3rem !important}.ml-sm-5,.mx-sm-5{margin-left:3rem !important}.p-sm-0{padding:0 !important}.pt-sm-0,.py-sm-0{padding-top:0 !important}.pr-sm-0,.px-sm-0{padding-right:0 !important}.pb-sm-0,.py-sm-0{padding-bottom:0 !important}.pl-sm-0,.px-sm-0{padding-left:0 !important}.p-sm-1{padding:0.25rem !important}.pt-sm-1,.py-sm-1{padding-top:0.25rem !important}.pr-sm-1,.px-sm-1{padding-right:0.25rem !important}.pb-sm-1,.py-sm-1{padding-bottom:0.25rem !important}.pl-sm-1,.px-sm-1{padding-left:0.25rem !important}.p-sm-2{padding:0.5rem !important}.pt-sm-2,.py-sm-2{padding-top:0.5rem !important}.pr-sm-2,.px-sm-2{padding-right:0.5rem !important}.pb-sm-2,.py-sm-2{padding-bottom:0.5rem !important}.pl-sm-2,.px-sm-2{padding-left:0.5rem !important}.p-sm-3{padding:1rem !important}.pt-sm-3,.py-sm-3{padding-top:1rem !important}.pr-sm-3,.px-sm-3{padding-right:1rem !important}.pb-sm-3,.py-sm-3{padding-bottom:1rem !important}.pl-sm-3,.px-sm-3{padding-left:1rem !important}.p-sm-4{padding:1.5rem !important}.pt-sm-4,.py-sm-4{padding-top:1.5rem !important}.pr-sm-4,.px-sm-4{padding-right:1.5rem !important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem !important}.pl-sm-4,.px-sm-4{padding-left:1.5rem !important}.p-sm-5{padding:3rem !important}.pt-sm-5,.py-sm-5{padding-top:3rem !important}.pr-sm-5,.px-sm-5{padding-right:3rem !important}.pb-sm-5,.py-sm-5{padding-bottom:3rem !important}.pl-sm-5,.px-sm-5{padding-left:3rem !important}.m-sm-n1{margin:-0.25rem !important}.mt-sm-n1,.my-sm-n1{margin-top:-0.25rem !important}.mr-sm-n1,.mx-sm-n1{margin-right:-0.25rem !important}.mb-sm-n1,.my-sm-n1{margin-bottom:-0.25rem !important}.ml-sm-n1,.mx-sm-n1{margin-left:-0.25rem !important}.m-sm-n2{margin:-0.5rem !important}.mt-sm-n2,.my-sm-n2{margin-top:-0.5rem !important}.mr-sm-n2,.mx-sm-n2{margin-right:-0.5rem !important}.mb-sm-n2,.my-sm-n2{margin-bottom:-0.5rem !important}.ml-sm-n2,.mx-sm-n2{margin-left:-0.5rem !important}.m-sm-n3{margin:-1rem !important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem !important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem !important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem !important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem !important}.m-sm-n4{margin:-1.5rem !important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem !important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem !important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem !important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem !important}.m-sm-n5{margin:-3rem !important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem !important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem !important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem !important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem !important}.m-sm-auto{margin:auto !important}.mt-sm-auto,.my-sm-auto{margin-top:auto !important}.mr-sm-auto,.mx-sm-auto{margin-right:auto !important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto !important}.ml-sm-auto,.mx-sm-auto{margin-left:auto !important}}@media (min-width: 768px){.m-md-0{margin:0 !important}.mt-md-0,.my-md-0{margin-top:0 !important}.mr-md-0,.mx-md-0{margin-right:0 !important}.mb-md-0,.my-md-0{margin-bottom:0 !important}.ml-md-0,.mx-md-0{margin-left:0 !important}.m-md-1{margin:0.25rem !important}.mt-md-1,.my-md-1{margin-top:0.25rem !important}.mr-md-1,.mx-md-1{margin-right:0.25rem !important}.mb-md-1,.my-md-1{margin-bottom:0.25rem !important}.ml-md-1,.mx-md-1{margin-left:0.25rem !important}.m-md-2{margin:0.5rem !important}.mt-md-2,.my-md-2{margin-top:0.5rem !important}.mr-md-2,.mx-md-2{margin-right:0.5rem !important}.mb-md-2,.my-md-2{margin-bottom:0.5rem !important}.ml-md-2,.mx-md-2{margin-left:0.5rem !important}.m-md-3{margin:1rem !important}.mt-md-3,.my-md-3{margin-top:1rem !important}.mr-md-3,.mx-md-3{margin-right:1rem !important}.mb-md-3,.my-md-3{margin-bottom:1rem !important}.ml-md-3,.mx-md-3{margin-left:1rem !important}.m-md-4{margin:1.5rem !important}.mt-md-4,.my-md-4{margin-top:1.5rem !important}.mr-md-4,.mx-md-4{margin-right:1.5rem !important}.mb-md-4,.my-md-4{margin-bottom:1.5rem !important}.ml-md-4,.mx-md-4{margin-left:1.5rem !important}.m-md-5{margin:3rem !important}.mt-md-5,.my-md-5{margin-top:3rem !important}.mr-md-5,.mx-md-5{margin-right:3rem !important}.mb-md-5,.my-md-5{margin-bottom:3rem !important}.ml-md-5,.mx-md-5{margin-left:3rem !important}.p-md-0{padding:0 !important}.pt-md-0,.py-md-0{padding-top:0 !important}.pr-md-0,.px-md-0{padding-right:0 !important}.pb-md-0,.py-md-0{padding-bottom:0 !important}.pl-md-0,.px-md-0{padding-left:0 !important}.p-md-1{padding:0.25rem !important}.pt-md-1,.py-md-1{padding-top:0.25rem !important}.pr-md-1,.px-md-1{padding-right:0.25rem !important}.pb-md-1,.py-md-1{padding-bottom:0.25rem !important}.pl-md-1,.px-md-1{padding-left:0.25rem !important}.p-md-2{padding:0.5rem !important}.pt-md-2,.py-md-2{padding-top:0.5rem !important}.pr-md-2,.px-md-2{padding-right:0.5rem !important}.pb-md-2,.py-md-2{padding-bottom:0.5rem !important}.pl-md-2,.px-md-2{padding-left:0.5rem !important}.p-md-3{padding:1rem !important}.pt-md-3,.py-md-3{padding-top:1rem !important}.pr-md-3,.px-md-3{padding-right:1rem !important}.pb-md-3,.py-md-3{padding-bottom:1rem !important}.pl-md-3,.px-md-3{padding-left:1rem !important}.p-md-4{padding:1.5rem !important}.pt-md-4,.py-md-4{padding-top:1.5rem !important}.pr-md-4,.px-md-4{padding-right:1.5rem !important}.pb-md-4,.py-md-4{padding-bottom:1.5rem !important}.pl-md-4,.px-md-4{padding-left:1.5rem !important}.p-md-5{padding:3rem !important}.pt-md-5,.py-md-5{padding-top:3rem !important}.pr-md-5,.px-md-5{padding-right:3rem !important}.pb-md-5,.py-md-5{padding-bottom:3rem !important}.pl-md-5,.px-md-5{padding-left:3rem !important}.m-md-n1{margin:-0.25rem !important}.mt-md-n1,.my-md-n1{margin-top:-0.25rem !important}.mr-md-n1,.mx-md-n1{margin-right:-0.25rem !important}.mb-md-n1,.my-md-n1{margin-bottom:-0.25rem !important}.ml-md-n1,.mx-md-n1{margin-left:-0.25rem !important}.m-md-n2{margin:-0.5rem !important}.mt-md-n2,.my-md-n2{margin-top:-0.5rem !important}.mr-md-n2,.mx-md-n2{margin-right:-0.5rem !important}.mb-md-n2,.my-md-n2{margin-bottom:-0.5rem !important}.ml-md-n2,.mx-md-n2{margin-left:-0.5rem !important}.m-md-n3{margin:-1rem !important}.mt-md-n3,.my-md-n3{margin-top:-1rem !important}.mr-md-n3,.mx-md-n3{margin-right:-1rem !important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem !important}.ml-md-n3,.mx-md-n3{margin-left:-1rem !important}.m-md-n4{margin:-1.5rem !important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem !important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem !important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem !important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem !important}.m-md-n5{margin:-3rem !important}.mt-md-n5,.my-md-n5{margin-top:-3rem !important}.mr-md-n5,.mx-md-n5{margin-right:-3rem !important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem !important}.ml-md-n5,.mx-md-n5{margin-left:-3rem !important}.m-md-auto{margin:auto !important}.mt-md-auto,.my-md-auto{margin-top:auto !important}.mr-md-auto,.mx-md-auto{margin-right:auto !important}.mb-md-auto,.my-md-auto{margin-bottom:auto !important}.ml-md-auto,.mx-md-auto{margin-left:auto !important}}@media (min-width: 992px){.m-lg-0{margin:0 !important}.mt-lg-0,.my-lg-0{margin-top:0 !important}.mr-lg-0,.mx-lg-0{margin-right:0 !important}.mb-lg-0,.my-lg-0{margin-bottom:0 !important}.ml-lg-0,.mx-lg-0{margin-left:0 !important}.m-lg-1{margin:0.25rem !important}.mt-lg-1,.my-lg-1{margin-top:0.25rem !important}.mr-lg-1,.mx-lg-1{margin-right:0.25rem !important}.mb-lg-1,.my-lg-1{margin-bottom:0.25rem !important}.ml-lg-1,.mx-lg-1{margin-left:0.25rem !important}.m-lg-2{margin:0.5rem !important}.mt-lg-2,.my-lg-2{margin-top:0.5rem !important}.mr-lg-2,.mx-lg-2{margin-right:0.5rem !important}.mb-lg-2,.my-lg-2{margin-bottom:0.5rem !important}.ml-lg-2,.mx-lg-2{margin-left:0.5rem !important}.m-lg-3{margin:1rem !important}.mt-lg-3,.my-lg-3{margin-top:1rem !important}.mr-lg-3,.mx-lg-3{margin-right:1rem !important}.mb-lg-3,.my-lg-3{margin-bottom:1rem !important}.ml-lg-3,.mx-lg-3{margin-left:1rem !important}.m-lg-4{margin:1.5rem !important}.mt-lg-4,.my-lg-4{margin-top:1.5rem !important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem !important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem !important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem !important}.m-lg-5{margin:3rem !important}.mt-lg-5,.my-lg-5{margin-top:3rem !important}.mr-lg-5,.mx-lg-5{margin-right:3rem !important}.mb-lg-5,.my-lg-5{margin-bottom:3rem !important}.ml-lg-5,.mx-lg-5{margin-left:3rem !important}.p-lg-0{padding:0 !important}.pt-lg-0,.py-lg-0{padding-top:0 !important}.pr-lg-0,.px-lg-0{padding-right:0 !important}.pb-lg-0,.py-lg-0{padding-bottom:0 !important}.pl-lg-0,.px-lg-0{padding-left:0 !important}.p-lg-1{padding:0.25rem !important}.pt-lg-1,.py-lg-1{padding-top:0.25rem !important}.pr-lg-1,.px-lg-1{padding-right:0.25rem !important}.pb-lg-1,.py-lg-1{padding-bottom:0.25rem !important}.pl-lg-1,.px-lg-1{padding-left:0.25rem !important}.p-lg-2{padding:0.5rem !important}.pt-lg-2,.py-lg-2{padding-top:0.5rem !important}.pr-lg-2,.px-lg-2{padding-right:0.5rem !important}.pb-lg-2,.py-lg-2{padding-bottom:0.5rem !important}.pl-lg-2,.px-lg-2{padding-left:0.5rem !important}.p-lg-3{padding:1rem !important}.pt-lg-3,.py-lg-3{padding-top:1rem !important}.pr-lg-3,.px-lg-3{padding-right:1rem !important}.pb-lg-3,.py-lg-3{padding-bottom:1rem !important}.pl-lg-3,.px-lg-3{padding-left:1rem !important}.p-lg-4{padding:1.5rem !important}.pt-lg-4,.py-lg-4{padding-top:1.5rem !important}.pr-lg-4,.px-lg-4{padding-right:1.5rem !important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem !important}.pl-lg-4,.px-lg-4{padding-left:1.5rem !important}.p-lg-5{padding:3rem !important}.pt-lg-5,.py-lg-5{padding-top:3rem !important}.pr-lg-5,.px-lg-5{padding-right:3rem !important}.pb-lg-5,.py-lg-5{padding-bottom:3rem !important}.pl-lg-5,.px-lg-5{padding-left:3rem !important}.m-lg-n1{margin:-0.25rem !important}.mt-lg-n1,.my-lg-n1{margin-top:-0.25rem !important}.mr-lg-n1,.mx-lg-n1{margin-right:-0.25rem !important}.mb-lg-n1,.my-lg-n1{margin-bottom:-0.25rem !important}.ml-lg-n1,.mx-lg-n1{margin-left:-0.25rem !important}.m-lg-n2{margin:-0.5rem !important}.mt-lg-n2,.my-lg-n2{margin-top:-0.5rem !important}.mr-lg-n2,.mx-lg-n2{margin-right:-0.5rem !important}.mb-lg-n2,.my-lg-n2{margin-bottom:-0.5rem !important}.ml-lg-n2,.mx-lg-n2{margin-left:-0.5rem !important}.m-lg-n3{margin:-1rem !important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem !important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem !important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem !important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem !important}.m-lg-n4{margin:-1.5rem !important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem !important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem !important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem !important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem !important}.m-lg-n5{margin:-3rem !important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem !important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem !important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem !important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem !important}.m-lg-auto{margin:auto !important}.mt-lg-auto,.my-lg-auto{margin-top:auto !important}.mr-lg-auto,.mx-lg-auto{margin-right:auto !important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto !important}.ml-lg-auto,.mx-lg-auto{margin-left:auto !important}}@media (min-width: 1200px){.m-xl-0{margin:0 !important}.mt-xl-0,.my-xl-0{margin-top:0 !important}.mr-xl-0,.mx-xl-0{margin-right:0 !important}.mb-xl-0,.my-xl-0{margin-bottom:0 !important}.ml-xl-0,.mx-xl-0{margin-left:0 !important}.m-xl-1{margin:0.25rem !important}.mt-xl-1,.my-xl-1{margin-top:0.25rem !important}.mr-xl-1,.mx-xl-1{margin-right:0.25rem !important}.mb-xl-1,.my-xl-1{margin-bottom:0.25rem !important}.ml-xl-1,.mx-xl-1{margin-left:0.25rem !important}.m-xl-2{margin:0.5rem !important}.mt-xl-2,.my-xl-2{margin-top:0.5rem !important}.mr-xl-2,.mx-xl-2{margin-right:0.5rem !important}.mb-xl-2,.my-xl-2{margin-bottom:0.5rem !important}.ml-xl-2,.mx-xl-2{margin-left:0.5rem !important}.m-xl-3{margin:1rem !important}.mt-xl-3,.my-xl-3{margin-top:1rem !important}.mr-xl-3,.mx-xl-3{margin-right:1rem !important}.mb-xl-3,.my-xl-3{margin-bottom:1rem !important}.ml-xl-3,.mx-xl-3{margin-left:1rem !important}.m-xl-4{margin:1.5rem !important}.mt-xl-4,.my-xl-4{margin-top:1.5rem !important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem !important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem !important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem !important}.m-xl-5{margin:3rem !important}.mt-xl-5,.my-xl-5{margin-top:3rem !important}.mr-xl-5,.mx-xl-5{margin-right:3rem !important}.mb-xl-5,.my-xl-5{margin-bottom:3rem !important}.ml-xl-5,.mx-xl-5{margin-left:3rem !important}.p-xl-0{padding:0 !important}.pt-xl-0,.py-xl-0{padding-top:0 !important}.pr-xl-0,.px-xl-0{padding-right:0 !important}.pb-xl-0,.py-xl-0{padding-bottom:0 !important}.pl-xl-0,.px-xl-0{padding-left:0 !important}.p-xl-1{padding:0.25rem !important}.pt-xl-1,.py-xl-1{padding-top:0.25rem !important}.pr-xl-1,.px-xl-1{padding-right:0.25rem !important}.pb-xl-1,.py-xl-1{padding-bottom:0.25rem !important}.pl-xl-1,.px-xl-1{padding-left:0.25rem !important}.p-xl-2{padding:0.5rem !important}.pt-xl-2,.py-xl-2{padding-top:0.5rem !important}.pr-xl-2,.px-xl-2{padding-right:0.5rem !important}.pb-xl-2,.py-xl-2{padding-bottom:0.5rem !important}.pl-xl-2,.px-xl-2{padding-left:0.5rem !important}.p-xl-3{padding:1rem !important}.pt-xl-3,.py-xl-3{padding-top:1rem !important}.pr-xl-3,.px-xl-3{padding-right:1rem !important}.pb-xl-3,.py-xl-3{padding-bottom:1rem !important}.pl-xl-3,.px-xl-3{padding-left:1rem !important}.p-xl-4{padding:1.5rem !important}.pt-xl-4,.py-xl-4{padding-top:1.5rem !important}.pr-xl-4,.px-xl-4{padding-right:1.5rem !important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem !important}.pl-xl-4,.px-xl-4{padding-left:1.5rem !important}.p-xl-5{padding:3rem !important}.pt-xl-5,.py-xl-5{padding-top:3rem !important}.pr-xl-5,.px-xl-5{padding-right:3rem !important}.pb-xl-5,.py-xl-5{padding-bottom:3rem !important}.pl-xl-5,.px-xl-5{padding-left:3rem !important}.m-xl-n1{margin:-0.25rem !important}.mt-xl-n1,.my-xl-n1{margin-top:-0.25rem !important}.mr-xl-n1,.mx-xl-n1{margin-right:-0.25rem !important}.mb-xl-n1,.my-xl-n1{margin-bottom:-0.25rem !important}.ml-xl-n1,.mx-xl-n1{margin-left:-0.25rem !important}.m-xl-n2{margin:-0.5rem !important}.mt-xl-n2,.my-xl-n2{margin-top:-0.5rem !important}.mr-xl-n2,.mx-xl-n2{margin-right:-0.5rem !important}.mb-xl-n2,.my-xl-n2{margin-bottom:-0.5rem !important}.ml-xl-n2,.mx-xl-n2{margin-left:-0.5rem !important}.m-xl-n3{margin:-1rem !important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem !important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem !important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem !important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem !important}.m-xl-n4{margin:-1.5rem !important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem !important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem !important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem !important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem !important}.m-xl-n5{margin:-3rem !important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem !important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem !important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem !important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem !important}.m-xl-auto{margin:auto !important}.mt-xl-auto,.my-xl-auto{margin-top:auto !important}.mr-xl-auto,.mx-xl-auto{margin-right:auto !important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto !important}.ml-xl-auto,.mx-xl-auto{margin-left:auto !important}}.text-monospace{font-family:SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important}.text-justify{text-align:justify !important}.text-wrap{white-space:normal !important}.text-nowrap{white-space:nowrap !important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left !important}.text-right{text-align:right !important}.text-center{text-align:center !important}@media (min-width: 576px){.text-sm-left{text-align:left !important}.text-sm-right{text-align:right !important}.text-sm-center{text-align:center !important}}@media (min-width: 768px){.text-md-left{text-align:left !important}.text-md-right{text-align:right !important}.text-md-center{text-align:center !important}}@media (min-width: 992px){.text-lg-left{text-align:left !important}.text-lg-right{text-align:right !important}.text-lg-center{text-align:center !important}}@media (min-width: 1200px){.text-xl-left{text-align:left !important}.text-xl-right{text-align:right !important}.text-xl-center{text-align:center !important}}.text-lowercase{text-transform:lowercase !important}.text-uppercase{text-transform:uppercase !important}.text-capitalize{text-transform:capitalize !important}.font-weight-light{font-weight:300 !important}.font-weight-lighter{font-weight:lighter !important}.font-weight-normal{font-weight:400 !important}.font-weight-bold{font-weight:700 !important}.font-weight-bolder{font-weight:bolder !important}.font-italic{font-style:italic !important}.text-white{color:#fff !important}.text-primary{color:#2C3E50 !important}a.text-primary:hover,a.text-primary:focus{color:#11181f !important}.text-secondary{color:#95a5a6 !important}a.text-secondary:hover,a.text-secondary:focus{color:#6d8082 !important}.text-success{color:#18BC9C !important}a.text-success:hover,a.text-success:focus{color:#0f7864 !important}.text-info{color:#3498DB !important}a.text-info:hover,a.text-info:focus{color:#1d6fa5 !important}.text-warning{color:#F39C12 !important}a.text-warning:hover,a.text-warning:focus{color:#b06f09 !important}.text-danger{color:#E74C3C !important}a.text-danger:hover,a.text-danger:focus{color:#bf2718 !important}.text-light{color:#ecf0f1 !important}a.text-light:hover,a.text-light:focus{color:#c0cdd1 !important}.text-dark{color:#7b8a8b !important}a.text-dark:hover,a.text-dark:focus{color:#576263 !important}.text-body{color:#212529 !important}.text-muted{color:#95a5a6 !important}.text-black-50{color:rgba(0,0,0,0.5) !important}.text-white-50{color:rgba(255,255,255,0.5) !important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none !important}.text-break{word-break:break-word !important;overflow-wrap:break-word !important}.text-reset{color:inherit !important}.visible{visibility:visible !important}.invisible{visibility:hidden !important}@media print{*,*::before,*::after{text-shadow:none !important;-webkit-box-shadow:none !important;box-shadow:none !important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap !important}pre,blockquote{border:1px solid #b4bcc2;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px !important}.container{min-width:992px !important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #dee2e6 !important}.table-dark{color:inherit}.table-dark th,.table-dark td,.table-dark thead th,.table-dark tbody+tbody{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}}.bg-primary .navbar-nav .active>.nav-link{color:#18BC9C !important}.bg-dark{background-color:#18BC9C !important}.bg-dark.navbar-dark .navbar-nav .nav-link:focus,.bg-dark.navbar-dark .navbar-nav .nav-link:hover,.bg-dark.navbar-dark .navbar-nav .active>.nav-link{color:#2C3E50 !important}.btn-secondary,.btn-secondary:hover,.btn-warning,.btn-warning:hover{color:#fff}.table-primary,.table-secondary,.table-success,.table-info,.table-warning,.table-danger{color:#fff}.table-primary,.table-primary>th,.table-primary>td{background-color:#2C3E50}.table-secondary,.table-secondary>th,.table-secondary>td{background-color:#95a5a6}.table-light,.table-light>th,.table-light>td{background-color:#ecf0f1}.table-dark,.table-dark>th,.table-dark>td{background-color:#7b8a8b}.table-success,.table-success>th,.table-success>td{background-color:#18BC9C}.table-info,.table-info>th,.table-info>td{background-color:#3498DB}.table-danger,.table-danger>th,.table-danger>td{background-color:#E74C3C}.table-warning,.table-warning>th,.table-warning>td{background-color:#F39C12}.table-active,.table-active>th,.table-active>td{background-color:rgba(0,0,0,0.075)}.table-hover .table-primary:hover,.table-hover .table-primary:hover>th,.table-hover .table-primary:hover>td{background-color:#233140}.table-hover .table-secondary:hover,.table-hover .table-secondary:hover>th,.table-hover .table-secondary:hover>td{background-color:#87999a}.table-hover .table-light:hover,.table-hover .table-light:hover>th,.table-hover .table-light:hover>td{background-color:#dde4e6}.table-hover .table-dark:hover,.table-hover .table-dark:hover>th,.table-hover .table-dark:hover>td{background-color:#6f7d7e}.table-hover .table-success:hover,.table-hover .table-success:hover>th,.table-hover .table-success:hover>td{background-color:#15a589}.table-hover .table-info:hover,.table-hover .table-info:hover>th,.table-hover .table-info:hover>td{background-color:#258cd1}.table-hover .table-danger:hover,.table-hover .table-danger:hover>th,.table-hover .table-danger:hover>td{background-color:#e43725}.table-hover .table-warning:hover,.table-hover .table-warning:hover>th,.table-hover .table-warning:hover>td{background-color:#e08e0b}.table-hover .table-active:hover,.table-hover .table-active:hover>th,.table-hover .table-active:hover>td{background-color:rgba(0,0,0,0.075)}.nav-tabs .nav-link.active,.nav-tabs .nav-link.active:focus,.nav-tabs .nav-link.active:hover,.nav-tabs .nav-item.open .nav-link,.nav-tabs .nav-item.open .nav-link:focus,.nav-tabs .nav-item.open .nav-link:hover{color:#2C3E50}.pagination a:hover{text-decoration:none}.close{text-decoration:none;opacity:0.4}.close:hover,.close:focus{opacity:1}.badge-secondary,.badge-warning{color:#fff}.alert{border:none;color:#fff}.alert a,.alert .alert-link{color:#fff;text-decoration:underline}.alert-primary{background-color:#2C3E50}.alert-secondary{background-color:#95a5a6}.alert-success{background-color:#18BC9C}.alert-info{background-color:#3498DB}.alert-warning{background-color:#F39C12}.alert-danger{background-color:#E74C3C}.alert-light{background-color:#ecf0f1}.alert-dark{background-color:#7b8a8b}.alert-light,.alert-light a,.alert-light .alert-link{color:#212529}.modal .close{color:#000}.modal .close:not(:disabled):not(.disabled):hover,.modal .close:not(:disabled):not(.disabled):focus{color:#000} diff --git a/src/assets/close.svg b/src/assets/close.svg new file mode 100644 index 0000000..7894824 --- /dev/null +++ b/src/assets/close.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/crypto-auth.png b/src/assets/crypto-auth.png new file mode 100644 index 0000000000000000000000000000000000000000..a7c63a9bf4e8fa33e68671149a7828f865c22525 GIT binary patch literal 102894 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 H)xiG(;|XAt literal 0 HcmV?d00001 diff --git a/src/assets/factom-protocol.png b/src/assets/factom-protocol.png new file mode 100644 index 0000000000000000000000000000000000000000..1da7a5bc351602b1a8674aba1ddf672049a818da GIT binary patch literal 19365 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 AWB>pF literal 0 HcmV?d00001 diff --git a/src/assets/factomatic-logo-transparent.png b/src/assets/factomatic-logo-transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..44403db8578d5f6f6292b0b98c01b77974cc39e4 GIT binary patch literal 66164 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 literal 0 HcmV?d00001 diff --git a/src/assets/kambani-logo.png b/src/assets/kambani-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b5b370dd26f523c0a35d6a66680255d5339420bb GIT binary patch literal 43662 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 oo}rnZ-aw$iq5s(s9uasYIPU*%Xf;jC0vjM(Yll + + + + + diff --git a/src/background.js b/src/background.js new file mode 100644 index 0000000..c27e399 --- /dev/null +++ b/src/background.js @@ -0,0 +1,325 @@ +const NEW_TAB_OPEN = 'newTabOpen'; +const RESTORE_VAULT_REQUEST = 'restoreVaultRequest'; +const MANAGE_DIDS_REQUEST = 'manageDidsRequest'; +const MANAGE_FACTOM_ADDRESSES_REQUEST = 'manageFactomAddressesRequest'; +const MANAGE_KEYS_REQUEST = 'manageKeysRequest'; +const SETTINGS_REQUEST = 'settingsRequest'; +const CHECK_REQUESTS = 'checkRequests'; +const PENDING_SIGNING_REQUESTS_COUNT = 'pendingSigningRequestsCount'; +const GET_SIGNING_REQUEST = 'getSigningRequest'; +const CANCEL_SIGNING_REQUEST = 'cancelSigningRequest'; +const SKIP_SIGNING_REQUEST = 'skipSigningRequest'; +const SEND_SIGNING_REQUEST_RESPONSE = 'sendSigningRequestResponse'; +const RECEIVE_SIGNING_REQUEST = 'receiveSigningRequest'; +const APPROVAL_REQUESTS_COUNT = 'approvalRequestsCount'; +const GET_APPROVAL_REQUEST = 'getApprovalRequest'; +const RECEIVE_APPROVAL_REQUEST = 'receiveApprovalRequest'; +const SEND_APPROVAL_REQUEST_RESPONSE = 'sendApprovalRequestResponse'; +const INVALID_REQUEST_RESPONSE = 'Invalid request!'; +const DATA_REQUEST = 'data'; +const PEGNET_REQUEST = 'pegnet'; +const DID_KEY_TYPE = 'didKey'; +const MANAGEMENT_KEY_TYPE = 'managementKey'; +const FCT_KEY_TYPE = 'fct'; +const EC_KEY_TYPE = 'ec'; +const BLOCK_SIGNING_KEY_TYPE = 'blockSigningKey'; +const BURN_TX_TYPE = 'burn'; +const CONVERSION_TX_TYPE = 'conversion'; +const TRANSFER_TX_TYPE = 'transfer'; + +(function() { + let signingRequests = []; + let signingRequestsCallbacks = []; + let approvalRequests = []; + let approvalRequestsCallbacks = []; + let currentSigningRequestIndex = -1; + let restoreVaultRequested = false; + let manageDidsRequested = false; + let manageFactomAddressesRequested = false; + let manageKeysRequested = false; + let settingsRequested = false; + + chrome.browserAction.setBadgeText({text: "0"}); + + chrome.runtime.onMessage.addListener((msg, sender, response) => { + switch (msg.type) { + case RESTORE_VAULT_REQUEST: + restoreVaultRequested = true; + response({success: true}); + break; + case MANAGE_DIDS_REQUEST: + manageDidsRequested = true; + response({success: true}); + break; + case MANAGE_FACTOM_ADDRESSES_REQUEST: + manageFactomAddressesRequested = true; + response({success: true}); + break; + case MANAGE_KEYS_REQUEST: + manageKeysRequested = true; + response({success: true}); + break; + case SETTINGS_REQUEST: + settingsRequested = true; + response({success: true}); + break; + case CHECK_REQUESTS: + response({ + restoreVaultRequested: restoreVaultRequested, + manageDidsRequested: manageDidsRequested, + manageFactomAddressesRequested: manageFactomAddressesRequested, + manageKeysRequested: manageKeysRequested, + settingsRequested: settingsRequested, + approvalRequests: approvalRequests.length > 0 + }); + break; + case NEW_TAB_OPEN: + restoreVaultRequested = false; + manageDidsRequested = false; + manageFactomAddressesRequested = false; + manageKeysRequested = false; + settingsRequested = false; + break; + case RECEIVE_SIGNING_REQUEST: + if (isValidRequest(msg.content)) { + signingRequests.push({ + content: msg.content, + from: sender.url + }); + + signingRequestsCallbacks.push(response); + currentSigningRequestIndex = -1; + + chrome.browserAction.getBadgeText({}, function(result) { + const number = parseInt(result) + 1; + chrome.browserAction.setBadgeText({text: number.toString()}); + }); + + chrome.notifications.create({ + type: "basic", + title: "Notification message", + message: "New signing request received", + iconUrl: "assets/kambani-logo.png" + }); + + return true; + } else { + response({ + success: false, + requestId: msg.content.requestId + }); + } + break; + case PENDING_SIGNING_REQUESTS_COUNT: + response({ + pendingSigningRequestsCount: signingRequests.length, + }); + break; + case GET_SIGNING_REQUEST: + if (signingRequests.length > 0) { + if(currentSigningRequestIndex < 0 || currentSigningRequestIndex >= signingRequests.length) { + currentSigningRequestIndex = signingRequests.length - 1; + } + + response({ + success: true, + signingRequest: signingRequests[currentSigningRequestIndex] + }); + } else { + response({ + success: false + }); + } + break; + case CANCEL_SIGNING_REQUEST: + if(signingRequestsCallbacks.length > 0) { + const responseCallback = signingRequestsCallbacks[currentSigningRequestIndex]; + responseCallback({ + success: false, + ...msg.data + }); + + chrome.browserAction.getBadgeText({}, function(result) { + let number = parseInt(result) - 1; + if (number < 0) number = 0; + chrome.browserAction.setBadgeText({text: number.toString()}); + }); + + signingRequests.splice(currentSigningRequestIndex, 1); + signingRequestsCallbacks.splice(currentSigningRequestIndex, 1); + if (signingRequests.length === 0) { + currentSigningRequestIndex = -1; + } + } + break; + case SKIP_SIGNING_REQUEST: + currentSigningRequestIndex--; + break; + case SEND_SIGNING_REQUEST_RESPONSE: + if(signingRequestsCallbacks.length > 0) { + const responseCallback = signingRequestsCallbacks[currentSigningRequestIndex]; + responseCallback({ + success: true, + ...msg.data + }); + + chrome.browserAction.getBadgeText({}, function(result) { + const number = parseInt(result) - 1; + chrome.browserAction.setBadgeText({text: number.toString()}); + }); + + signingRequests.splice(currentSigningRequestIndex, 1); + signingRequestsCallbacks.splice(currentSigningRequestIndex, 1); + if (signingRequests.length === 0) { + currentSigningRequestIndex = -1; + } + } + break; + case APPROVAL_REQUESTS_COUNT: + response({ + approvalRequestsCount: approvalRequests.length, + }); + break; + case GET_APPROVAL_REQUEST: + if (approvalRequests.length > 0) { + response({ + success: true, + approvalRequest: approvalRequests[approvalRequests.length - 1] + }); + } else { + response({ + success: false + }); + } + break; + case RECEIVE_APPROVAL_REQUEST: + /* + Checks if there is an existing approval request with the same type and origin + -> if true deletes the old request and saves the new one + */ + const existingRequestIndex = approvalRequests.findIndex(r => r.type === msg.requestType && r.from === msg.from); + if (existingRequestIndex >= 0) { + approvalRequests.splice(existingRequestIndex, 1); + approvalRequestsCallbacks.splice(existingRequestIndex, 1); + } + + approvalRequestsCallbacks.push(response); + approvalRequests.push({ + from: msg.from, + type: msg.requestType + }); + + chrome.notifications.create({ + type: "basic", + title: "Notification message", + message: "New Approval Request Received", + iconUrl: "assets/kambani-logo.png" + }); + return true; + case SEND_APPROVAL_REQUEST_RESPONSE: + if (approvalRequestsCallbacks.length > 0) { + approvalRequests.pop(); + const responseCallback = approvalRequestsCallbacks.pop(); + responseCallback({ + success: msg.success + }); + + response({ + approvalRequestsCount: approvalRequests.length + }); + } + break; + default: + response(INVALID_REQUEST_RESPONSE); + break; + } + }); +}()); + +function isValidRequest (requestContent) { + const requestType = requestContent.requestType; + const requestInfo = requestContent.requestInfo; + + if (requestContent.requestId == undefined || requestType == undefined || requestInfo == undefined) { + return false; + } + + const requestTypes = [DATA_REQUEST, PEGNET_REQUEST]; + if (!requestTypes.includes(requestType)) { + return false; + } + + if (requestType == DATA_REQUEST) { + const keyType = requestInfo.keyType; + const keyIdentifier = requestInfo.keyIdentifier; + const did = requestInfo.did; + + if (requestInfo.data == undefined) { + return false; + } + + const keyTypes = [DID_KEY_TYPE, MANAGEMENT_KEY_TYPE, FCT_KEY_TYPE, EC_KEY_TYPE, BLOCK_SIGNING_KEY_TYPE]; + if (!keyTypes.includes(keyType)) { + return false; + } + + if (keyIdentifier !== undefined) { + if (keyType == FCT_KEY_TYPE) { + if (keyIdentifier.substring(0, 2) !== 'FA') { + return false; + } + } else if (keyType == EC_KEY_TYPE) { + if (keyIdentifier.substring(0, 2) !== 'EC') { + return false; + } + } else if (did == undefined && keyType !== BLOCK_SIGNING_KEY_TYPE) { + return false; + } + } + + if (did !== undefined) { + if (keyType == FCT_KEY_TYPE || keyType == EC_KEY_TYPE) { + return false; + } + + if (!/did:factom:[a-f0-9]{64}/.test(did)) { + return false; + } + } + } + + if (requestType == PEGNET_REQUEST) { + const txType = requestInfo.txType; + const inputAddress = requestInfo.inputAddress; + const inputAmount = requestInfo.inputAmount; + const inputAsset = requestInfo.inputAsset; + const outputAddress = requestInfo.outputAddress; + const outputAsset = requestInfo.outputAsset; + + if (txType == undefined || ![BURN_TX_TYPE, CONVERSION_TX_TYPE, TRANSFER_TX_TYPE].includes(txType)) { + return false; + } + + if (inputAddress == undefined || inputAddress.substring(0, 2) !== 'FA') { + return false; + } + + if (inputAmount == undefined || typeof inputAmount !== "number" || inputAmount <= 0) { + return false; + } + + if (txType !== BURN_TX_TYPE && inputAsset == undefined) { + return false; + } + + if (txType == CONVERSION_TX_TYPE && outputAsset == undefined) { + return false; + } + + if (txType == TRANSFER_TX_TYPE && (outputAddress == undefined || outputAddress.substring(0, 2) !== 'FA')) { + return false; + } + } + + return true; +} diff --git a/src/browserslist b/src/browserslist new file mode 100644 index 0000000..37371cb --- /dev/null +++ b/src/browserslist @@ -0,0 +1,11 @@ +# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries +# +# For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed + +> 0.5% +last 2 versions +Firefox ESR +not dead +not IE 9-11 \ No newline at end of file diff --git a/src/contentScript.js b/src/contentScript.js new file mode 100644 index 0000000..af914b1 --- /dev/null +++ b/src/contentScript.js @@ -0,0 +1,156 @@ +window.addEventListener('IsKambaniInstalled', () => { + const responseEvent = new CustomEvent('KambaniInstalled'); + window.dispatchEvent(responseEvent); +}); + +window.addEventListener('SigningRequest', (event) => { + chrome.runtime.sendMessage({type: 'receiveSigningRequest', content: event.detail}, + function(response) { + const signingResponseEvent = new CustomEvent('SigningResponse', {detail: response}); + window.dispatchEvent(signingResponseEvent); + } + ); +}); + +registerKeysOrAddressesEventListener('GetFCTAddresses', 'FCTAddresses', 'FCT', 'fctAddressesRequestWhitelistedDomains', 'fctAddresses', 'FCTAddressesChanged'); +registerKeysOrAddressesEventListener('GetECAddresses', 'ECAddresses', 'EC', 'ecAddressesRequestWhitelistedDomains', 'ecAddresses', 'ECAddressesChanged'); +registerKeysOrAddressesEventListener('GetBlockSigningKeys', 'BlockSigningKeys', 'BlockSigningKey', 'blockSigningKeysRequestWhitelistedDomains', 'blockSigningKeys', 'BlockSigningKeysChanged'); + +window.addEventListener('GetPegnetAddresses', (event) => { + chrome.storage.local.get([ + 'fctAddressesRequestWhitelistedDomains', + 'etherLinkAddressesRequestWhitelistedDomains', + 'fctAddresses', + 'etherLinkAddresses' + ], function(result) { + if (result.fctAddresses === undefined && result.etherLinkAddresses === undefined) { + /* + Return { success: false } if fctAddresses and etherLinkAddresses properties in chrome storage are undefined. + This should happen only if there is no vault created by the user. + */ + window.dispatchEvent(new CustomEvent('PegnetAddresses', { detail: { success: false } })); + } else { + let pegnetAddressesEvent = new CustomEvent('PegnetAddresses', { + detail: { success: true, fctAddresses: result.fctAddresses, etherLinkAddresses: result.etherLinkAddresses } + }); + + const requestOrigin = event.target.origin; + const etherLinkWhitelistedDomains = result.etherLinkAddressesRequestWhitelistedDomains; + const fctWhitelistedDomains = result.fctAddressesRequestWhitelistedDomains; + if (etherLinkWhitelistedDomains !== undefined + && fctWhitelistedDomains !== undefined + && etherLinkWhitelistedDomains.includes(requestOrigin) + && fctWhitelistedDomains.includes(requestOrigin)) { + window.dispatchEvent(pegnetAddressesEvent); + addAddressesChangesListener('etherLinkAddresses', 'EtherLinkAddressesChanged'); + addAddressesChangesListener('fctAddresses', 'FCTAddressesChanged'); + } else { + chrome.runtime.sendMessage({type: 'receiveApprovalRequest', requestType: 'Pegnet', from: requestOrigin}, + function(response) { + if (response.success) { + window.dispatchEvent(pegnetAddressesEvent); + addAddressesChangesListener('etherLinkAddresses', 'EtherLinkAddressesChanged'); + addAddressesChangesListener('fctAddresses', 'FCTAddressesChanged'); + } else { + pegnetAddressesEvent = new CustomEvent('PegnetAddresses', {detail: response}); + window.dispatchEvent(pegnetAddressesEvent); + } + } + ); + } + } + }); +}); + +function registerKeysOrAddressesEventListener(eventName, responseEventName, requestType, whitelistedDomainsPropertyName, keysOrAddressesPropertyName, changedEventName) { + window.addEventListener(eventName, (event) => { + chrome.storage.local.get([whitelistedDomainsPropertyName, keysOrAddressesPropertyName], function(result) { + if (result[keysOrAddressesPropertyName] === undefined) { + /* + Return { success: false } if the property in chrome storage is undefined. + This should happen only if there is no vault created by the user. + */ + window.dispatchEvent(new CustomEvent(responseEventName, { detail: { success: false } })); + } else { + let responseEvent = new CustomEvent(responseEventName, { + detail: { success: true, [keysOrAddressesPropertyName]: result[keysOrAddressesPropertyName] } + }); + + const requestOrigin = event.target.origin; + const whitelistedDomains = result[whitelistedDomainsPropertyName]; + if (whitelistedDomains !== undefined && whitelistedDomains.includes(requestOrigin)) { + window.dispatchEvent(responseEvent); + addAddressesChangesListener(keysOrAddressesPropertyName, changedEventName); + } else { + chrome.runtime.sendMessage({type: 'receiveApprovalRequest', requestType: requestType, from: requestOrigin}, + function(response) { + if (response.success) { + window.dispatchEvent(responseEvent); + addAddressesChangesListener(keysOrAddressesPropertyName, changedEventName); + } else { + responseEvent = new CustomEvent(responseEventName, {detail: response}); + window.dispatchEvent(responseEvent); + } + } + ); + } + } + }); + }); +} + +function addAddressesChangesListener(addressType, eventName) { + chrome.storage.onChanged.addListener(function(changes) { + if (changes[addressType]) { + let addressesOldValue = changes[addressType].oldValue; + let addressesNewValue = changes[addressType].newValue; + + if (addressesOldValue == undefined) { + addressesOldValue = [] + } + + if (addressesNewValue == undefined) { + addressesNewValue = [] + } + + if (addressesOldValue.length < addressesNewValue.length) { + const oldAddresses = addressesOldValue.map(addr => JSON.stringify(addr)); + const addedAddresses = addressesNewValue.filter(addr => !oldAddresses.includes(JSON.stringify(addr))); + + const event = new CustomEvent(eventName, { + detail: { + "added": addedAddresses + } + }); + + window.dispatchEvent(event); + } else if (addressesOldValue.length > addressesNewValue.length) { + const currentAddresses = addressesNewValue.map(addr => JSON.stringify(addr)); + const removedAddresses = addressesOldValue.filter(addr => !currentAddresses.includes(JSON.stringify(addr))); + + const event = new CustomEvent(eventName, { + detail: { + "removed": removedAddresses + } + }); + + window.dispatchEvent(event); + } else { + const oldAddresses = addressesOldValue.map(addr => JSON.stringify(addr)); + const addedAddresses = addressesNewValue.filter(addr => !oldAddresses.includes(JSON.stringify(addr))); + + const currentAddresses = addressesNewValue.map(addr => JSON.stringify(addr)); + const removedAddresses = addressesOldValue.filter(addr => !currentAddresses.includes(JSON.stringify(addr))); + + const event = new CustomEvent(eventName, { + detail: { + "added": addedAddresses, + "removed": removedAddresses + } + }); + + window.dispatchEvent(event); + } + } + }); +} \ No newline at end of file diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts new file mode 100644 index 0000000..b8b1a13 --- /dev/null +++ b/src/environments/environment.prod.ts @@ -0,0 +1,10 @@ +export const environment = { + production: true, + storageKey: '@#signerextension#@', + staging: false, + entrySchemaVersion: '1.0.0', + didMethodSpecVersion: '0.2.0', + localStorageVersion: '1.1', + entrySizeLimit: 10275, + apiUrl: 'https://testnet-api.factomatic.io/write-did' +}; diff --git a/src/environments/environment.staging.ts b/src/environments/environment.staging.ts new file mode 100644 index 0000000..3b87d0d --- /dev/null +++ b/src/environments/environment.staging.ts @@ -0,0 +1,10 @@ +export const environment = { + production: false, + storageKey: '@#signerextension#@', + staging: true, + entrySchemaVersion: '1.0.0', + didMethodSpecVersion: '0.2.0', + localStorageVersion: '1.1', + entrySizeLimit: 10275, + apiUrl: 'https://testnet-api.factomatic.io/write-did' +}; diff --git a/src/environments/environment.ts b/src/environments/environment.ts new file mode 100644 index 0000000..65ff0fe --- /dev/null +++ b/src/environments/environment.ts @@ -0,0 +1,23 @@ +// This file can be replaced during build by using the `fileReplacements` array. +// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. +// The list of file replacements can be found in `angular.json`. + +export const environment = { + production: false, + storageKey: '@#signerextension#@', + staging: false, + entrySchemaVersion: '1.0.0', + didMethodSpecVersion: '0.2.0', + localStorageVersion: '1.1', + entrySizeLimit: 10275, + apiUrl: 'https://testnet-api.factomatic.io/write-did' +}; + +/* + * For easier debugging in development mode, you can import the following file + * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. + * + * This import should be commented out in production mode because it will have a negative impact + * on performance if an error is thrown. + */ +// import 'zone.js/dist/zone-error'; // Included with Angular CLI. diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..89b4d54 --- /dev/null +++ b/src/index.html @@ -0,0 +1,13 @@ + + + + + Kambani + + + + + + + + diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..c7b673c --- /dev/null +++ b/src/main.ts @@ -0,0 +1,12 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); diff --git a/src/manifest.json b/src/manifest.json new file mode 100644 index 0000000..2b52eb2 --- /dev/null +++ b/src/manifest.json @@ -0,0 +1,37 @@ +{ + "manifest_version": 2, + "name":"Kambani", + "description": "A Factom addresses and identity management system in your browser.", + "version": "0.1.3", + "permissions": [ + "activeTab", + "notifications", + "storage", + "unlimitedStorage", + "clipboardWrite", + "https://testnet-api.factomatic.io/write-did" + ], + "browser_action": { + "default_title": "Kambani", + "default_popup": "index.html" + }, + "background": { + "scripts": ["background.js"] + }, + "content_scripts": [ + { + "matches": [ + "file://*/*", + "http://*/*", + "https://*/*" + ], + "js": ["contentScript.js"], + "run_at": "document_start" + } + ], + "icons": { + "16": "assets/kambani-logo.png", + "48": "assets/kambani-logo.png", + "128": "assets/kambani-logo.png" + } +} diff --git a/src/polyfills.ts b/src/polyfills.ts new file mode 100644 index 0000000..682e381 --- /dev/null +++ b/src/polyfills.ts @@ -0,0 +1,89 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), + * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. + * + * Learn more in https://angular.io/guide/browser-support + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** IE9, IE10, IE11, and Chrome <55 requires all of the following polyfills. + * This also includes Android Emulators with older versions of Chrome and Google Search/Googlebot + */ + +// import 'core-js/es6/symbol'; +// import 'core-js/es6/object'; +// import 'core-js/es6/function'; +// import 'core-js/es6/parse-int'; +// import 'core-js/es6/parse-float'; +// import 'core-js/es6/number'; +// import 'core-js/es6/math'; +// import 'core-js/es6/string'; +// import 'core-js/es6/date'; +// import 'core-js/es6/array'; +// import 'core-js/es6/regexp'; +// import 'core-js/es6/map'; +// import 'core-js/es6/weak-map'; +// import 'core-js/es6/set'; + +/** IE10 and IE11 requires the following for NgClass support on SVG elements */ +// import 'classlist.js'; // Run `npm install --save classlist.js`. + +/** IE10 and IE11 requires the following for the Reflect API. */ +// import 'core-js/es6/reflect'; + +/** + * Web Animations `@angular/platform-browser/animations` + * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. + * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). + */ +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. + +/** + * By default, zone.js will patch all possible macroTask and DomEvents + * user can disable parts of macroTask/DomEvents patch by setting following flags + * because those flags need to be set before `zone.js` being loaded, and webpack + * will put import in the top of bundle, so user need to create a separate file + * in this directory (for example: zone-flags.ts), and put the following flags + * into that file, and then add the following code before importing zone.js. + * import './zone-flags.ts'; + * + * The flags allowed in zone-flags.ts are listed here. + * + * The following flags will work for all browsers. + * + * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame + * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick + * (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames + * + * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js + * with the following flag, it will bypass `zone.js` patch for IE/Edge + * + * (window as any).__Zone_enable_cross_context_check = true; + * + */ + +/*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ +import 'zone.js/dist/zone'; // Included with Angular CLI. + + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ + +(window as any).global = window; +// @ts-ignore +window.Buffer = window.Buffer || require('buffer').Buffer; diff --git a/src/styles.scss b/src/styles.scss new file mode 100644 index 0000000..3398d19 --- /dev/null +++ b/src/styles.scss @@ -0,0 +1,207 @@ +/* You can add global styles to this file, and also import other style files */ +@import url("//netdna.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.css"); +body { + height: 600px; + font-size: 0.85rem !important; + font-family: 'Montserrat', sans-serif; + background: #DFE7F2; +} + +@media (max-width: 370px) { + body { + width: 350px; + } +} + +h1, .h1 { + font-size: 1.5rem !important; +} + +h2, .h2 { + font-size: 1.3rem !important; +} + +h3, .h3 { + font-size: 1rem !important; +} + +$mainColor: #2F5BE7; +$mainColorHover: #1d45c3; +$redColor: #e85658; +$lightColor: #F8F8F8; +.btn { + height: 40px; + border-radius: 5px; + outline: none!important; + // font-weight: 600; + line-height: 40px; + padding: 0px 12px; + transition: all 0.3s ease; + &.btn-primary { + background: $mainColor!important; + border-color: $mainColor!important; + &.outline { + background: transparent; + color: $mainColor; + border: 1px solid $mainColor; + } + &:active, &:hover{ + background: $mainColorHover; + } + &:active { + border-color: $mainColorHover!important; + background: $mainColorHover!important; + outline: initial!important; + box-shadow: 0 0 0 3px rgba(47, 91, 231, 0.45); + } + &:focus { + border-color: $mainColor; + outline: initial!important; + box-shadow: none; + } + } + &.btn-success { + background-color: #4caf50; + border-color: #4caf50; + &.outline { + background: transparent; + color: #4caf50; + border: 1px solid #4caf50; + } + &:active { + background-color: #409444; + border-color: #3d8b40; + box-shadow: 0 0 0 3px rgba(76,175,80,.5); + } + &:focus { + background-color: #4caf50; + border-color: #4caf50; + box-shadow: none; + } + &:hover { + color: #fff; + background-color: #409444; + border-color: #3d8b40; + } + } + &.btn-warning { + color: #47404f; + background-color: #ffc107; + border-color: #ffc107; + &.outline { + background: transparent; + color: #ffc107; + border: 1px solid #ffc107; + } + &:active { + color: #47404f; + background-color: #e0a800; + border-color: #d39e00; + box-shadow: 0 0 0 3px rgba(255,193,7,.5); + } + &:focus { + color: #47404f; + background-color: #ffc107; + border-color: #ffc107; + box-shadow: none; + } + &:hover { + color: #47404f; + background-color: #e0a800; + border-color: #d39e00; + } + } + &.btn-danger { + color: #fff; + background-color: #d22346; + border-color: #d22346; + &.outline { + background: transparent; + color: #d22346; + border: 1px solid #d22346; + } + &:active { + color: #fff; + background-color: #b11e3b; + border-color: #a61c37; + box-shadow: 0 0 0 3px rgba(210,35,70,.5); + } + &:focus { + color: #fff; + background-color: #d22346; + border-color: #d22346; + box-shadow: none; + } + &:hover { + color: #fff; + background-color: #b11e3b; + border-color: #a61c37; + } + } + &.btn-default { + background: $lightColor; + } + &.btn-xs { + height: 30px; + text-transform: none; + line-height: 30px; + } +} +.text-primary { + color: $mainColor!important; +} +.button-holder { + .pull-left { + float: left; + } + .pull-right { + float: right; + } +} + +.page-content { + border-radius: 3px; + background: #fff; + width: 100%; + margin-top: 20px; + padding: 20px; + .page-title { + font-size: 23px; + margin-bottom: 20px; + font-weight: 700; + } +} + + +#toast-container > div { + opacity:1; +} + +#toast-container > .toast-success { + background-color: #51A351; +} + +#toast-container > .toast-error { + background-color: #BD362F; +} + +.heading{ + margin: 20px; +} + + +.heading{ + margin-top: 40px; +} + +.center { + text-align: center; +} + +.pull-left { + float: left; +} + +.pull-right { + float: right; +} diff --git a/src/tsconfig.app.json b/src/tsconfig.app.json new file mode 100644 index 0000000..7d9525e --- /dev/null +++ b/src/tsconfig.app.json @@ -0,0 +1,14 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/app", + "types": [ + "node", + "chrome" + ] + }, + "exclude": [ + "test.ts", + "**/*.spec.ts" + ] +} diff --git a/src/tsconfig.spec.json b/src/tsconfig.spec.json new file mode 100644 index 0000000..de77336 --- /dev/null +++ b/src/tsconfig.spec.json @@ -0,0 +1,18 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/spec", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "test.ts", + "polyfills.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/src/tslint.json b/src/tslint.json new file mode 100644 index 0000000..52e2c1a --- /dev/null +++ b/src/tslint.json @@ -0,0 +1,17 @@ +{ + "extends": "../tslint.json", + "rules": { + "directive-selector": [ + true, + "attribute", + "app", + "camelCase" + ], + "component-selector": [ + true, + "element", + "app", + "kebab-case" + ] + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..b271fd9 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "baseUrl": "./", + "outDir": "./dist/out-tsc", + "sourceMap": true, + "declaration": false, + "module": "es2015", + "moduleResolution": "node", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "importHelpers": true, + "target": "es5", + "typeRoots": [ + "node_modules/@types" + ], + "lib": [ + "es2018", + "dom" + ] + } +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..c740a7b --- /dev/null +++ b/tslint.json @@ -0,0 +1,131 @@ +{ + "rulesDirectory": [ + "codelyzer" + ], + "rules": { + "arrow-return-shorthand": true, + "callable-types": true, + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "curly": true, + "deprecation": { + "severity": "warn" + }, + "eofline": true, + "forin": true, + "import-blacklist": [ + true, + "rxjs/Rx" + ], + "import-spacing": true, + "indent": [ + true, + "spaces" + ], + "interface-over-type-literal": true, + "label-position": true, + "max-line-length": [ + true, + 140 + ], + "member-access": false, + "member-ordering": [ + true, + { + "order": [ + "static-field", + "instance-field", + "static-method", + "instance-method" + ] + } + ], + "no-arg": true, + "no-bitwise": true, + "no-console": [ + true, + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-construct": true, + "no-debugger": true, + "no-duplicate-super": true, + "no-empty": false, + "no-empty-interface": true, + "no-eval": true, + "no-inferrable-types": [ + true, + "ignore-params" + ], + "no-misused-new": true, + "no-non-null-assertion": true, + "no-redundant-jsdoc": true, + "no-shadowed-variable": true, + "no-string-literal": false, + "no-string-throw": true, + "no-switch-case-fall-through": true, + "no-trailing-whitespace": true, + "no-unnecessary-initializer": true, + "no-unused-expression": true, + "no-use-before-declare": true, + "no-var-keyword": true, + "object-literal-sort-keys": false, + "one-line": [ + true, + "check-open-brace", + "check-catch", + "check-else", + "check-whitespace" + ], + "prefer-const": true, + "quotemark": [ + true, + "single" + ], + "radix": true, + "semicolon": [ + true, + "always" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "unified-signatures": true, + "variable-name": false, + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ], + "no-output-on-prefix": true, + "use-input-property-decorator": true, + "use-output-property-decorator": true, + "use-host-property-decorator": true, + "no-input-rename": true, + "no-output-rename": true, + "use-life-cycle-interface": true, + "use-pipe-transform-interface": true, + "component-class-suffix": true, + "directive-class-suffix": true + } +} diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..9cfeb62 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,5 @@ +module.exports = { + node: { + https: true, + } +} \ No newline at end of file