diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..0e8a9c47e --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,51 @@ +{ + "root": true, + "ignorePatterns": [ + "projects/**/*" + ], + "overrides": [ + { + "files": [ + "*.ts" + ], + "parserOptions": { + "project": [ + "tsconfig.json", + "e2e/tsconfig.json" + ], + "createDefaultProgram": true + }, + "extends": [ + "plugin:@angular-eslint/ng-cli-compat", + "plugin:@angular-eslint/ng-cli-compat--formatting-add-on", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": { + "@typescript-eslint/consistent-type-definitions": "error", + "@typescript-eslint/dot-notation": "off", + "@typescript-eslint/explicit-member-accessibility": [ + "off", + { + "accessibility": "explicit" + } + ], + "brace-style": [ + "error", + "1tbs" + ], + "id-blacklist": "off", + "id-match": "off", + "no-underscore-dangle": "off" + } + }, + { + "files": [ + "*.html" + ], + "extends": [ + "plugin:@angular-eslint/template/recommended" + ], + "rules": {} + } + ] +} diff --git a/.gitignore b/.gitignore index d58dc112b..7a18bd11a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /dist /tmp /out-tsc +/.angular # dependencies /node_modules @@ -24,6 +25,11 @@ !.vscode/launch.json !.vscode/extensions.json +# IDE - VSCode 2019 +.vs/* + +pre-commit + # misc /.sass-cache /connect.lock @@ -48,4 +54,4 @@ Thumbs.db /lib/dojo-custom-jsdraw/**/* -/**/venv \ No newline at end of file +/**/venv diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..936b00902 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,28 @@ +dist: trusty + +language: node_js +node_js: + - 14.17.0 + + +install: + + - cp package.dev.json package.json + - rm -rf node_modules + - rm -rf package-lock.json + - rm -rf package.json + - cp package.dev.json package.json + - npm install + - npm run build-file-select + - npm run build-jsdraw-wrapper + - npm run build-ketcher-wrapper + - cp package.real.json package.json + - npm install --legacy-peer-deps + - npm install webpack + - npm i --save webpack-sources --legacy-peer-deps + - npm install -f @types/ws@8.5.4 + - export NODE_OPTIONS="--max-old-space-size=8192" +script: + - npm run build:fda:prod +after_success: + - if [ $TRAVIS_BRANCH = 'development_3.0' ]; then bash travisScript.sh; else echo "Not a branch set to push"; fi diff --git a/README.md b/README.md index b096f62ac..ebb34247c 100644 --- a/README.md +++ b/README.md @@ -7,33 +7,69 @@ Technology stack - [Angular CLI](https://github.com/angular/angular-cli) - [Angular Material](https://material.angular.io/) based on [Google's Material Design methodology](https://material.io/design/) -## Requirements +## Getting Started Overview + +The full steps for a complete build are as follows, each will be given in more detail: + +1. Step 1 [Required] Obtain the required software dependencies (node, npm, and angular CLI, as mentioned above) +2. Step 2 [Optional] Clear any previous build files, locks, and local dependencies (this step is not typically necessary, but it ensures constency if there were previous local builds) +3. Step 3 [Optional] Prepare dojo dependencies and place in zip file `lib/dojo-custom-jsdraw.zip` (this dojo build is already prepared by default so this step is optional) +4. Step 4 [Required] Prepare fundamental dependencies by doing `npm install` with `package.dev.json` file. +5. Step 5 [Required] Prepare extended one-time build dependencies using specific build commands +6. Step 6 [Required] Build, run or test the codebase as normal + +If you are using a bash terminal, steps 2-5 can be accomplished by simply running: +``` +bash build.sh +``` + +## Step 1: Software Requirements Make sure to have these installed in order to run the application: -- [Node](https://nodejs.org/en/) -- [npm](https://www.npmjs.com/) - usually included in the node installation -- Angular CLI - on any command line run `npm install -g @angular/cli@latest` - - On Windows 7, the angular cli "ng" executable will be located in this folder: - - C:\Users\\AppData\Roaming\npm\ - - Hopefully, you've already added this folder to you windows environment path. - - ... the ng.exe program will be used quite a bit. +* [Node](https://nodejs.org/en/) +* [npm](https://www.npmjs.com/) - usually included in the node installation +* Angular CLI - on any command line run `npm install -g @angular/cli@latest` + * The angular cli "ng" executable will be located in this folder: + * `C:\Users\\AppData\Roaming\npm\` (Windows 7) + * `~/.npm-global/bin/` (linux) + * Note: For best results, this path should be added to your windows/bash + path as the command will be used a lot. + * The angular CLI tool currently needs to have build-angular version <=0.803.25 + * To force this installation after an audit fix run `npm i @angular-devkit/build-angular@0.803.25` + +## Step 2 [Optional]: Clear any Previous Build Files + +This step isn't always necessary, but can be useful when attempting to force a de novo build. The following files should be removed from the root directory: -## Install Application Packages +``` +package-lock.json +node_modules +package.json +``` -After you have cloned the application to your local computer, and prepared the dojo dependencies, open your favorite command line, navigate to the root directory (where the `package.json` file is located), -and copy the `package.dev.json` file to a new file called `package.json`, then run the command: +## Step 3 [Optional]: Prepare Custom Dojo Dependency +This step isn't typically necessary. The purpose of this step is to prepare a slimmed down version of dojo for the jsdraw structure editor component, but the default build already has a form of this prepackaged. To perform a more custom dojo build, read the `lib/README.md` file and follow its instructions. -- `npm install` +## Step 4: Install Fundamental Dependencies -The first time you do this, it will take a while to download all required packages. +This step acquires the "base" dependencies needed to do further builds. The full _real_ `package.json` file would have a cyclic dependency issue if we attempted to install using it directly, so we first build only the bare minimum pieces needed to build the other dependencies. This is accomplished by using a trimmed down version of the package.json file named `package.dev.json`. The following commands will make this happen: -You should repeat this step whenever somebody adds a new package to the application. It's probably not a bad idea to run it whenever you start working on the application +bash: +``` +cp package.dev.json package.json +npm install +``` +windows CMD: +``` +copy package.dev.json package.json +npm install +``` -## Perform a One-Time Build of dependencies +## Step 5: Perform a One-Time Build of Dependencies You'll have to run the following commands the first time you work on the application to make sure a few libraries are built and ready to be used by the application: @@ -43,9 +79,27 @@ npm run build-jsdraw-wrapper npm run build-ketcher-wrapper ``` -After this is done, the file `package.real.json` should be copied and replace the `package.json` file. +This MUST be done while the `package.dev.json` file is being used as `package.json`, as in step 4. Once this is done, you then replace the package.json file with package.real.json to complete the preperation. This can be accomplished with the following commands: + +bash: +``` +cp package.real.json package.json +npm install +``` + +windows CMD: +``` +copy package.real.json package.json +npm install +``` -## Run Application for Specific Environment +Doing the install at this time may reveal some elements that suggest an audit fix. While this can be done, newer versions of the angular build tool are not compatible with the current build process, so the specific <=0.803.25 version must be forced. To both do a basic audit fix and force this version, you can run the following commands (either windows CMD or bash): + +``` +npm audit fix // this step not needed after angular upgrade +``` + +## Step 6: Run Application for Specific Environment - Go the the package.json file and look at the scripts property to see what availabe commands exist - The commands to run during development begin with "start" @@ -54,6 +108,10 @@ After this is done, the file `package.real.json` should be copied and replace th - After a few seconds of compiling the application, you're ready to view your application - Open your browser and navigate to http://localhost:4200 +## Troubleshooting + +GSRSFrontend uses node-sass, which has varying compatibilities based on the version of node.js being used. See https://www.npmjs.com/package/node-sass to check which version is compatable with your node version. The value of node-sass being used can be changed in the root 'package.json' file, where the default value is set as `"node-sass": "4.13.1",` + # Development tools @@ -69,6 +127,8 @@ Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github. Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). + + ## Further help To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). diff --git a/angular.json b/angular.json index 5bd4499ac..5884be806 100644 --- a/angular.json +++ b/angular.json @@ -1,420 +1,488 @@ -{ - "$schema": "./node_modules/@angular/cli/lib/config/schema.json", - "version": 1, - "newProjectRoot": "projects", - "projects": { - "gsrs-client": { - "root": "", - "sourceRoot": "src", - "projectType": "application", - "prefix": "app", - "schematics": { - "@schematics/angular:component": { - "styleext": "scss" - } - }, - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:browser", - "options": { - "outputPath": "dist/browser", - "index": "src/index.html", - "main": "src/main.ts", - "polyfills": "src/polyfills.ts", - "tsConfig": "src/tsconfig.app.json", - "assets": [ - { "glob": "**/*", "input": "src/app/core/assets/", "output": "/assets/" }, - { "glob": "config.json", "input": "src/app/core/config/", "output": "/assets/data/" } - ], - "styles": [ - { - "input": "node_modules/@angular/material/prebuilt-themes/indigo-pink.css" - }, - "src/styles/main.scss" - ] - }, - "configurations": { - "gsrs.prod": { - "assets": [ - { "glob": "**/*", "input": "src/app/core/assets/", "output": "/assets/" }, - { "glob": "config.json", "input": "src/app/core/config/", "output": "/assets/data/" } - ], - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.gsrs.prod.ts" - } - ], - "optimization": true, - "outputHashing": "all", - "sourceMap": true, - "extractCss": true, - "namedChunks": false, - "aot": true, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true - }, - "gsrs.pre-prod": { - "assets": [ - { "glob": "**/*", "input": "src/app/core/assets/", "output": "/assets/" }, - { "glob": "config.json", "input": "src/app/core/config/", "output": "/assets/data/" } - ], - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.gsrs.pre-prod.ts" - } - ], - "optimization": true, - "outputHashing": "all", - "sourceMap": true, - "extractCss": true, - "namedChunks": false, - "aot": true, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true - }, - "gsrs.dev": { - "assets": [ - { "glob": "**/*", "input": "src/app/core/assets/", "output": "/assets/" }, - { "glob": "config.json", "input": "src/app/core/config/", "output": "/assets/data/" } - ], - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.gsrs.dev.ts" - } - ] - }, - "gsrs.local": { - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.gsrs.local.ts" - } - ] - }, - "fda.prod": { - "assets": [ - { "glob": "**/*", "input": "src/app/core/assets/", "output": "/assets/" }, - { "glob": "config.json", "input": "src/app/fda/config/", "output": "/assets/data/" }, - { "glob": "**/*", "input": "src/app/fda/assets", "output": "/assets/" } - ], - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.fda.prod.ts" - } - ], - "optimization": true, - "outputHashing": "all", - "sourceMap": true, - "extractCss": true, - "namedChunks": false, - "aot": true, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true - }, - "fda.pre-prod": { - "assets": [ - { "glob": "**/*", "input": "src/app/core/assets/", "output": "/assets/" }, - { "glob": "config.json", "input": "src/app/fda/config/", "output": "/assets/data/" }, - { "glob": "**/*", "input": "src/app/fda/assets", "output": "/assets/" } - ], - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.fda.pre-prod.ts" - } - ], - "optimization": true, - "outputHashing": "all", - "sourceMap": true, - "extractCss": true, - "namedChunks": false, - "aot": true, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true - }, - "fda.dev": { - "assets": [ - { "glob": "**/*", "input": "src/app/core/assets/", "output": "/assets/" }, - { "glob": "config.json", "input": "src/app/fda/config/", "output": "/assets/data/" }, - { "glob": "**/*", "input": "src/app/fda/assets", "output": "/assets/" } - ], - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.fda.dev.ts" - } - ] - }, - "fda.local": { - "assets": [ - { "glob": "**/*", "input": "src/app/core/assets/", "output": "/assets/" }, - { "glob": "config.json", "input": "src/app/fda/config/", "output": "/assets/data/" }, - { "glob": "**/*", "input": "src/app/fda/assets", "output": "/assets/" } - ], - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.fda.local.ts" - } - ] - }, - "cbg.prod": { - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.cbg.prod.ts" - } - ], - "optimization": true, - "outputHashing": "all", - "sourceMap": true, - "extractCss": true, - "namedChunks": false, - "aot": true, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true - } - } - }, - "serve": { - "builder": "@angular-devkit/build-angular:dev-server", - "options": { - "browserTarget": "gsrs-client:build" - }, - "configurations": { - "fda.dev": { - "browserTarget": "gsrs-client:build:fda.dev" - }, - "fda.local": { - "browserTarget": "gsrs-client:build:fda.local" - }, - "fda.pre-prod": { - "browserTarget": "gsrs-client:build:fda.pre-prod" - }, - "fda.prod": { - "browserTarget": "gsrs-client:build:fda.prod" - }, - "gsrs.dev": { - "browserTarget": "gsrs-client:build:gsrs.dev" - }, - "gsrs.local": { - "browserTarget": "gsrs-client:build:gsrs.local" - }, - "gsrs.pre-prod": { - "browserTarget": "gsrs-client:build:gsrs.pre-prod" - }, - "gsrs.prod": { - "browserTarget": "gsrs-client:build:gsrs.prod" - }, - "cbg.prod": { - "browserTarget": "gsrs-client:build:cbg.prod" - } - } - }, - "extract-i18n": { - "builder": "@angular-devkit/build-angular:extract-i18n", - "options": { - "browserTarget": "gsrs-client:build" - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "src/test.ts", - "polyfills": "src/polyfills.ts", - "tsConfig": "src/tsconfig.spec.json", - "karmaConfig": "src/karma.conf.js", - "styles": [ - { - "input": "node_modules/@angular/material/prebuilt-themes/indigo-pink.css" - }, - "src/styles.scss" - ], - "scripts": [], - "assets": [ - "src/favicon.ico", - "src/app/core/assets" - ] - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "src/tsconfig.app.json", - "src/tsconfig.spec.json" - ], - "exclude": [ - "**/node_modules/**" - ] - } - }, - "server": { - "builder": "@angular-devkit/build-angular:server", - "options": { - "outputPath": "dist/server", - "main": "src/main.server.ts", - "tsConfig": "src/tsconfig.server.json" - } - } - } - }, - "gsrs-client-e2e": { - "root": "e2e/", - "projectType": "application", - "architect": { - "e2e": { - "builder": "@angular-devkit/build-angular:protractor", - "options": { - "protractorConfig": "e2e/protractor.conf.js", - "devServerTarget": "gsrs-client:serve" - }, - "configurations": { - "production": { - "devServerTarget": "gsrs-client:serve:production" - } - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": "e2e/tsconfig.e2e.json", - "exclude": [ - "**/node_modules/**" - ] - } - } - } - }, - "jsdraw-wrapper": { - "root": "projects/jsdraw-wrapper", - "sourceRoot": "projects/jsdraw-wrapper/src", - "projectType": "library", - "prefix": "ncats", - "architect": { - "build": { - "builder": "@angular-devkit/build-ng-packagr:build", - "options": { - "tsConfig": "projects/jsdraw-wrapper/tsconfig.lib.json", - "project": "projects/jsdraw-wrapper/ng-package.json" - }, - "configurations": { - "production": { - "project": "projects/jsdraw-wrapper/ng-package.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "projects/jsdraw-wrapper/src/test.ts", - "tsConfig": "projects/jsdraw-wrapper/tsconfig.spec.json", - "karmaConfig": "projects/jsdraw-wrapper/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "projects/jsdraw-wrapper/tsconfig.lib.json", - "projects/jsdraw-wrapper/tsconfig.spec.json" - ], - "exclude": [ - "**/node_modules/**" - ] - } - } - } - }, - "ketcher-wrapper": { - "root": "projects/ketcher-wrapper", - "sourceRoot": "projects/ketcher-wrapper/src", - "projectType": "library", - "prefix": "ncats", - "architect": { - "build": { - "builder": "@angular-devkit/build-ng-packagr:build", - "options": { - "tsConfig": "projects/ketcher-wrapper/tsconfig.lib.json", - "project": "projects/ketcher-wrapper/ng-package.json" - }, - "configurations": { - "production": { - "project": "projects/ketcher-wrapper/ng-package.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "projects/ketcher-wrapper/src/test.ts", - "tsConfig": "projects/ketcher-wrapper/tsconfig.spec.json", - "karmaConfig": "projects/ketcher-wrapper/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "projects/ketcher-wrapper/tsconfig.lib.json", - "projects/ketcher-wrapper/tsconfig.spec.json" - ], - "exclude": [ - "**/node_modules/**" - ] - } - } - } - }, - "file-select": { - "root": "projects/file-select", - "sourceRoot": "projects/file-select/src", - "projectType": "library", - "prefix": "ncats", - "architect": { - "build": { - "builder": "@angular-devkit/build-ng-packagr:build", - "options": { - "tsConfig": "projects/file-select/tsconfig.lib.json", - "project": "projects/file-select/ng-package.json" - }, - "configurations": { - "production": { - "project": "projects/file-select/ng-package.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "projects/file-select/src/test.ts", - "tsConfig": "projects/file-select/tsconfig.spec.json", - "karmaConfig": "projects/file-select/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "projects/file-select/tsconfig.lib.json", - "projects/file-select/tsconfig.spec.json" - ], - "exclude": [ - "**/node_modules/**" - ] - } - } - } - } - }, - "defaultProject": "gsrs-client" -} \ No newline at end of file +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "gsrs-client": { + "root": "", + "sourceRoot": "src", + "projectType": "application", + "prefix": "app", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "sourceMap": true, + "optimization": false, + "outputPath": "dist/browser", + "index": "src/index.html", + "main": "src/main.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "src/tsconfig.app.json", + "assets": [ + { + "glob": "**/*", + "input": "src/app/core/assets/", + "output": "/assets/" + }, + { + "glob": "config.json", + "input": "src/app/core/config/", + "output": "/assets/data/" + } + ], + "styles": [ + { + "input": "node_modules/@angular/material/prebuilt-themes/indigo-pink.css" + }, + "src/styles/main.scss" + ] + }, + "configurations": { + "gsrs.prod": { + "assets": [ + { + "glob": "**/*", + "input": "src/app/core/assets/", + "output": "/assets/" + }, + { + "glob": "config.json", + "input": "src/app/core/config/", + "output": "/assets/data/" + } + ], + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.gsrs.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": true, + "namedChunks": false, + "aot": true, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true + }, + "gsrs.pre-prod": { + "assets": [ + { + "glob": "**/*", + "input": "src/app/core/assets/", + "output": "/assets/" + }, + { + "glob": "config.json", + "input": "src/app/core/config/", + "output": "/assets/data/" + } + ], + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.gsrs.pre-prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": true, + "namedChunks": false, + "aot": true, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true + }, + "gsrs.dev": { + "assets": [ + { + "glob": "**/*", + "input": "src/app/core/assets/", + "output": "/assets/" + }, + { + "glob": "config.json", + "input": "src/app/core/config/", + "output": "/assets/data/" + } + ], + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.gsrs.dev.ts" + } + ] + }, + "gsrs.local": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.gsrs.local.ts" + } + ] + }, + "fda.prod": { + "assets": [ + { + "glob": "**/*", + "input": "src/app/core/assets/", + "output": "/assets/" + }, + { + "glob": "config.json", + "input": "src/app/fda/config/", + "output": "/assets/data/" + }, + { + "glob": "**/*", + "input": "src/app/fda/assets", + "output": "/assets/" + } + ], + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.fda.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": true, + "namedChunks": false, + "aot": true, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true + }, + "fda.pre-prod": { + "assets": [ + { + "glob": "**/*", + "input": "src/app/core/assets/", + "output": "/assets/" + }, + { + "glob": "config.json", + "input": "src/app/fda/config/", + "output": "/assets/data/" + }, + { + "glob": "**/*", + "input": "src/app/fda/assets", + "output": "/assets/" + } + ], + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.fda.pre-prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": true, + "namedChunks": false, + "aot": true, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true + }, + "fda.dev": { + "assets": [ + { + "glob": "**/*", + "input": "src/app/core/assets/", + "output": "/assets/" + }, + { + "glob": "config.json", + "input": "src/app/fda/config/", + "output": "/assets/data/" + }, + { + "glob": "**/*", + "input": "src/app/fda/assets", + "output": "/assets/" + } + ], + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.fda.dev.ts" + } + ] + }, + "fda.local": { + "assets": [ + { + "glob": "**/*", + "input": "src/app/core/assets/", + "output": "/assets/" + }, + { + "glob": "config.json", + "input": "src/app/fda/config/", + "output": "/assets/data/" + }, + { + "glob": "**/*", + "input": "src/app/fda/assets", + "output": "/assets/" + } + ], + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.fda.local.ts" + } + ] + }, + "cbg.prod": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.cbg.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": true, + "namedChunks": false, + "aot": true, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true + } + } + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "options": { + "browserTarget": "gsrs-client:build" + }, + "configurations": { + "fda.dev": { + "browserTarget": "gsrs-client:build:fda.dev" + }, + "fda.local": { + "browserTarget": "gsrs-client:build:fda.local" + }, + "fda.pre-prod": { + "browserTarget": "gsrs-client:build:fda.pre-prod" + }, + "fda.prod": { + "browserTarget": "gsrs-client:build:fda.prod" + }, + "gsrs.dev": { + "browserTarget": "gsrs-client:build:gsrs.dev" + }, + "gsrs.local": { + "browserTarget": "gsrs-client:build:gsrs.local" + }, + "gsrs.pre-prod": { + "browserTarget": "gsrs-client:build:gsrs.pre-prod" + }, + "gsrs.prod": { + "browserTarget": "gsrs-client:build:gsrs.prod" + }, + "cbg.prod": { + "browserTarget": "gsrs-client:build:cbg.prod" + } + } + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "gsrs-client:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "src/test.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "src/tsconfig.spec.json", + "karmaConfig": "src/karma.conf.js", + "styles": [ + { + "input": "node_modules/@angular/material/prebuilt-themes/indigo-pink.css" + }, + "src/styles.scss" + ], + "scripts": [], + "assets": [ + "src/favicon.ico", + "src/app/core/assets" + ] + } + }, + "lint": { + "builder": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": [ + "src/**/*.ts", + "src/**/*.html" + ] + } + }, + "server": { + "builder": "@angular-devkit/build-angular:server", + "options": { + "outputPath": "dist/server", + "main": "src/main.server.ts", + "tsConfig": "src/tsconfig.server.json" + } + } + } + }, + "gsrs-client-e2e": { + "root": "e2e/", + "projectType": "application", + "architect": { + "e2e": { + "builder": "@angular-devkit/build-angular:protractor", + "options": { + "protractorConfig": "e2e/protractor.conf.js", + "devServerTarget": "gsrs-client:serve" + }, + "configurations": { + "production": { + "devServerTarget": "gsrs-client:serve:production" + } + } + }, + "lint": { + "builder": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": [ + "e2e//**/*.ts", + "e2e//**/*.html" + ] + } + } + } + }, + "jsdraw-wrapper": { + "root": "projects/jsdraw-wrapper", + "sourceRoot": "projects/jsdraw-wrapper/src", + "projectType": "library", + "prefix": "ncats", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/jsdraw-wrapper/tsconfig.lib.json", + "project": "projects/jsdraw-wrapper/ng-package.json" + }, + "configurations": { + "production": { + "project": "projects/jsdraw-wrapper/ng-package.prod.json" + } + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/jsdraw-wrapper/src/test.ts", + "tsConfig": "projects/jsdraw-wrapper/tsconfig.spec.json", + "karmaConfig": "projects/jsdraw-wrapper/karma.conf.js" + } + }, + "lint": { + "builder": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": [ + "projects/jsdraw-wrapper/**/*.ts", + "projects/jsdraw-wrapper/**/*.html" + ] + } + } + } + }, + "ketcher-wrapper": { + "root": "projects/ketcher-wrapper", + "sourceRoot": "projects/ketcher-wrapper/src", + "projectType": "library", + "prefix": "ncats", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/ketcher-wrapper/tsconfig.lib.json", + "project": "projects/ketcher-wrapper/ng-package.json" + }, + "configurations": { + "production": { + "project": "projects/ketcher-wrapper/ng-package.prod.json" + } + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/ketcher-wrapper/src/test.ts", + "tsConfig": "projects/ketcher-wrapper/tsconfig.spec.json", + "karmaConfig": "projects/ketcher-wrapper/karma.conf.js" + } + }, + "lint": { + "builder": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": [ + "projects/ketcher-wrapper/**/*.ts", + "projects/ketcher-wrapper/**/*.html" + ] + } + } + } + }, + "file-select": { + "root": "projects/file-select", + "sourceRoot": "projects/file-select/src", + "projectType": "library", + "prefix": "ncats", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/file-select/tsconfig.lib.json", + "project": "projects/file-select/ng-package.json" + }, + "configurations": { + "production": { + "project": "projects/file-select/ng-package.prod.json" + } + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/file-select/src/test.ts", + "tsConfig": "projects/file-select/tsconfig.spec.json", + "karmaConfig": "projects/file-select/karma.conf.js" + } + }, + "lint": { + "builder": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": [ + "projects/file-select/**/*.ts", + "projects/file-select/**/*.html" + ] + } + } + } + } + }, + "defaultProject": "gsrs-client", + "cli": { + "defaultCollection": "@angular-eslint/schematics" + } +} diff --git a/build.sh b/build.sh index 1b9db6563..b084d8232 100644 --- a/build.sh +++ b/build.sh @@ -10,6 +10,6 @@ npm run build-file-select npm run build-jsdraw-wrapper npm run build-ketcher-wrapper cp package.real.json package.json -npm install -npm audit fix +npm install --legacy-peer-deps export NODE_OPTIONS="--max-old-space-size=8192" + diff --git a/e2e/.eslintrc.json b/e2e/.eslintrc.json new file mode 100644 index 000000000..9c9ef7f55 --- /dev/null +++ b/e2e/.eslintrc.json @@ -0,0 +1,45 @@ +{ + "extends": "../.eslintrc.json", + "ignorePatterns": [ + "!**/*" + ], + "overrides": [ + { + "files": [ + "*.ts" + ], + "parserOptions": { + "project": [ + "e2e//.eslintrc.json" + //"e2e//tsconfig.app.json", + //"e2e//tsconfig.spec.json", + //"e2e//e2e/tsconfig.json" + ], + "createDefaultProgram": true + }, + "rules": { + "@typescript-eslint/consistent-type-definitions": "error", + "@typescript-eslint/dot-notation": "off", + "@typescript-eslint/explicit-member-accessibility": [ + "off", + { + "accessibility": "explicit" + } + ], + "brace-style": [ + "error", + "1tbs" + ], + "id-blacklist": "off", + "id-match": "off", + "no-underscore-dangle": "off" + } + }, + { + "files": [ + "*.html" + ], + "rules": {} + } + ] +} diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index d922da460..000000000 --- a/package-lock.json +++ /dev/null @@ -1,14843 +0,0 @@ -{ - "name": "gsrs-client", - "version": "0.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@angular-devkit/architect": { - "version": "0.803.24", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.803.24.tgz", - "integrity": "sha512-ONY/Ppzyvtb0tqgwnzQvlGlexb5nTyy58ljgL1aQLTO3cNTkpl4IQYUCTdvn61gGA+FWPAXMCCbNqOPZMsOZCQ==", - "dev": true, - "requires": { - "@angular-devkit/core": "8.3.24", - "rxjs": "6.4.0" - }, - "dependencies": { - "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@angular-devkit/build-angular": { - "version": "0.803.24", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.803.24.tgz", - "integrity": "sha512-uA789spMVghXehwAhl5zK0loY/wfxblUiL+y21T24LMCJc15a9QX5dwbXH72ioHz7qdzb/agXk7AK+foc2/0Hw==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.803.24", - "@angular-devkit/build-optimizer": "0.803.24", - "@angular-devkit/build-webpack": "0.803.24", - "@angular-devkit/core": "8.3.24", - "@babel/core": "7.8.3", - "@babel/preset-env": "7.8.3", - "@ngtools/webpack": "8.3.24", - "ajv": "6.10.2", - "autoprefixer": "9.6.1", - "browserslist": "4.8.3", - "cacache": "12.0.2", - "caniuse-lite": "1.0.30001019", - "circular-dependency-plugin": "5.2.0", - "clean-css": "4.2.1", - "copy-webpack-plugin": "5.1.1", - "core-js": "3.6.4", - "coverage-istanbul-loader": "2.0.3", - "file-loader": "4.2.0", - "find-cache-dir": "3.0.0", - "glob": "7.1.4", - "jest-worker": "24.9.0", - "karma-source-map-support": "1.4.0", - "less": "3.9.0", - "less-loader": "5.0.0", - "license-webpack-plugin": "2.1.2", - "loader-utils": "1.2.3", - "mini-css-extract-plugin": "0.8.0", - "minimatch": "3.0.4", - "open": "6.4.0", - "parse5": "4.0.0", - "postcss": "7.0.17", - "postcss-import": "12.0.1", - "postcss-loader": "3.0.0", - "raw-loader": "3.1.0", - "regenerator-runtime": "0.13.3", - "rxjs": "6.4.0", - "sass": "1.22.9", - "sass-loader": "7.2.0", - "semver": "6.3.0", - "source-map": "0.7.3", - "source-map-loader": "0.2.4", - "source-map-support": "0.5.13", - "speed-measure-webpack-plugin": "1.3.1", - "style-loader": "1.0.0", - "stylus": "0.54.5", - "stylus-loader": "3.0.2", - "terser": "4.6.3", - "terser-webpack-plugin": "1.4.3", - "tree-kill": "1.2.2", - "webpack": "4.39.2", - "webpack-dev-middleware": "3.7.2", - "webpack-dev-server": "3.9.0", - "webpack-merge": "4.2.1", - "webpack-sources": "1.4.3", - "webpack-subresource-integrity": "1.1.0-rc.6", - "worker-plugin": "3.2.0" - }, - "dependencies": { - "core-js": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz", - "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==", - "dev": true - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "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" - } - }, - "parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true - }, - "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "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 - } - } - }, - "@angular-devkit/build-ng-packagr": { - "version": "0.803.22", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-ng-packagr/-/build-ng-packagr-0.803.22.tgz", - "integrity": "sha512-mT1f/V4dwpPCgVs7qRIeR7AT5wbTDcqMt9FFk1UEh+zq1xMe0CH2Udnv8GaJo3vcFiTGad0bxBOV5jEYH6OJQw==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.803.22", - "rxjs": "6.4.0" - }, - "dependencies": { - "@angular-devkit/architect": { - "version": "0.803.22", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.803.22.tgz", - "integrity": "sha512-5Gr0LH+Hjd/NLdmi660VBoo3WbzQM7/yeG+ziktb7hbeVaYK4Mejtcg/DJnCoZ3hzlZuZokWVwvpdFo+A9xKbg==", - "dev": true, - "requires": { - "@angular-devkit/core": "8.3.22", - "rxjs": "6.4.0" - } - }, - "@angular-devkit/core": { - "version": "8.3.22", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.22.tgz", - "integrity": "sha512-lOEYcvK3MktjR9YZT/cUjiQE5dZxl8rZ/vgWgwDiL7RtzfXTt8lPapoJe7YKS53gLbUYiBNPCtTyTAqnslWgGA==", - "dev": true, - "requires": { - "ajv": "6.10.2", - "fast-json-stable-stringify": "2.0.0", - "magic-string": "0.25.3", - "rxjs": "6.4.0", - "source-map": "0.7.3" - } - }, - "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "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 - } - } - }, - "@angular-devkit/build-optimizer": { - "version": "0.803.24", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.803.24.tgz", - "integrity": "sha512-Z+d7M+WpBq7AWWRwbxzb1l9O9qkylxnDRKxXvq3Tzjn43g+2WyspE91dMyrg1ISc+p8jgX6xKSblRLvtWqpA8w==", - "dev": true, - "requires": { - "loader-utils": "1.2.3", - "source-map": "0.7.3", - "tslib": "1.10.0", - "typescript": "3.5.3", - "webpack-sources": "1.4.3" - }, - "dependencies": { - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "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" - } - }, - "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 - } - } - }, - "@angular-devkit/build-webpack": { - "version": "0.803.24", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.803.24.tgz", - "integrity": "sha512-Bbd5KUGaE+edN0sp8K3azuqS/JTBmeWXIumdBEtqWyL6VsohX7fL+toJlSvRkj8lg02LVyozAFetXKnyaBkfCQ==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.803.24", - "@angular-devkit/core": "8.3.24", - "rxjs": "6.4.0" - }, - "dependencies": { - "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@angular-devkit/core": { - "version": "8.3.24", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.24.tgz", - "integrity": "sha512-xpT5yg+ddGDnifryBv2sRSYtq5F3iZIS+lN/K2AhhEa50B7Z+QaCVlEzoV/IfrGd6sLArdnKYwjLHFZ0LElUuw==", - "dev": true, - "requires": { - "ajv": "6.10.2", - "fast-json-stable-stringify": "2.0.0", - "magic-string": "0.25.3", - "rxjs": "6.4.0", - "source-map": "0.7.3" - }, - "dependencies": { - "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "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 - } - } - }, - "@angular-devkit/schematics": { - "version": "8.3.22", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-8.3.22.tgz", - "integrity": "sha512-ETLdV1ftT+ZuuiHl6FjFQ4XLQznWMcxWognX+qgByn+DQOXsYRRvZK1L5eG/SG8CKJ8NL5oteTDloDnghARHFw==", - "dev": true, - "requires": { - "@angular-devkit/core": "8.3.22", - "rxjs": "6.4.0" - }, - "dependencies": { - "@angular-devkit/core": { - "version": "8.3.22", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.22.tgz", - "integrity": "sha512-lOEYcvK3MktjR9YZT/cUjiQE5dZxl8rZ/vgWgwDiL7RtzfXTt8lPapoJe7YKS53gLbUYiBNPCtTyTAqnslWgGA==", - "dev": true, - "requires": { - "ajv": "6.10.2", - "fast-json-stable-stringify": "2.0.0", - "magic-string": "0.25.3", - "rxjs": "6.4.0", - "source-map": "0.7.3" - } - }, - "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "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 - } - } - }, - "@angular/animations": { - "version": "8.2.14", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-8.2.14.tgz", - "integrity": "sha512-3Vc9TnNpKdtvKIXcWDFINSsnwgEMiDmLzjceWg1iYKwpeZGQahUXPoesLwQazBMmxJzQiA4HOMj0TTXKZ+Jzkg==", - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - } - } - }, - "@angular/cdk": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-8.2.3.tgz", - "integrity": "sha512-ZwO5Sn720RA2YvBqud0JAHkZXjmjxM0yNzCO8RVtRE9i8Gl26Wk0j0nQeJkVm4zwv2QO8MwbKUKGTMt8evsokA==", - "requires": { - "parse5": "^5.0.0", - "tslib": "^1.7.1" - }, - "dependencies": { - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - } - } - }, - "@angular/cli": { - "version": "8.3.22", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-8.3.22.tgz", - "integrity": "sha512-OT2rzwnxwI0ETP7rXCxjxsIAZEYo9wHP/5rRbu3m15GlQ3Bclq34ZDRwC/bRxXL5+1DfmhAs9AjtYNoFoDM4Tg==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.803.22", - "@angular-devkit/core": "8.3.22", - "@angular-devkit/schematics": "8.3.22", - "@schematics/angular": "8.3.22", - "@schematics/update": "0.803.22", - "@yarnpkg/lockfile": "1.1.0", - "ansi-colors": "4.1.1", - "debug": "^4.1.1", - "ini": "1.3.5", - "inquirer": "6.5.1", - "npm-package-arg": "6.1.0", - "npm-pick-manifest": "3.0.2", - "open": "6.4.0", - "pacote": "9.5.5", - "read-package-tree": "5.3.1", - "rimraf": "3.0.0", - "semver": "6.3.0", - "symbol-observable": "1.2.0", - "universal-analytics": "^0.4.20", - "uuid": "^3.3.2" - }, - "dependencies": { - "@angular-devkit/architect": { - "version": "0.803.22", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.803.22.tgz", - "integrity": "sha512-5Gr0LH+Hjd/NLdmi660VBoo3WbzQM7/yeG+ziktb7hbeVaYK4Mejtcg/DJnCoZ3hzlZuZokWVwvpdFo+A9xKbg==", - "dev": true, - "requires": { - "@angular-devkit/core": "8.3.22", - "rxjs": "6.4.0" - } - }, - "@angular-devkit/core": { - "version": "8.3.22", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.22.tgz", - "integrity": "sha512-lOEYcvK3MktjR9YZT/cUjiQE5dZxl8rZ/vgWgwDiL7RtzfXTt8lPapoJe7YKS53gLbUYiBNPCtTyTAqnslWgGA==", - "dev": true, - "requires": { - "ajv": "6.10.2", - "fast-json-stable-stringify": "2.0.0", - "magic-string": "0.25.3", - "rxjs": "6.4.0", - "source-map": "0.7.3" - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "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.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "rimraf": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", - "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "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 - } - } - }, - "@angular/common": { - "version": "8.2.14", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-8.2.14.tgz", - "integrity": "sha512-Qmt+aX2quUW54kaNT7QH7WGXnFxr/cC2C6sf5SW5SdkZfDQSiz8IaItvieZfXVQUbBOQKFRJ7TlSkt0jI/yjvw==", - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - } - } - }, - "@angular/compiler": { - "version": "8.2.14", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-8.2.14.tgz", - "integrity": "sha512-ABZO4E7eeFA1QyJ2trDezxeQM5ZFa1dXw1Mpl/+1vuXDKNjJgNyWYwKp/NwRkLmrsuV0yv4UDCDe4kJOGbPKnw==", - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - } - } - }, - "@angular/compiler-cli": { - "version": "8.2.14", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-8.2.14.tgz", - "integrity": "sha512-XDrTyrlIZM+0NquVT+Kbg5bn48AaWFT+B3bAT288PENrTdkuxuF9AhjFRZj8jnMdmaE4O2rioEkXBtl6z3zptA==", - "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", - "source-map": "^0.6.1", - "tslib": "^1.9.0", - "yargs": "13.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "dev": true, - "optional": true, - "requires": { - "nan": "^2.12.1" - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "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" - } - }, - "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" - } - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "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": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "yargs": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.1.0.tgz", - "integrity": "sha512-1UhJbXfzHiPqkfXNHYhiz79qM/kZqjTE8yGlEjZa85Q+3+OwcV6NRkV7XOV1W2Eom2bzILeUn55pQYffjVOLAg==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.0.0" - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "@angular/core": { - "version": "8.2.14", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-8.2.14.tgz", - "integrity": "sha512-zeePkigi+hPh3rN7yoNENG/YUBUsIvUXdxx+AZq+QPaFeKEA2FBSrKn36ojHFrdJUjKzl0lPMEiGC2b6a6bo6g==", - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - } - } - }, - "@angular/forms": { - "version": "8.2.14", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-8.2.14.tgz", - "integrity": "sha512-zhyKL3CFIqcyHJ/TQF/h1OZztK611a6rxuPHCrt/5Sn1SuBTJJQ1pPTkOYIDy6IrCrtyANc8qB6P17Mao71DNQ==", - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - } - } - }, - "@angular/language-service": { - "version": "8.2.14", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-8.2.14.tgz", - "integrity": "sha512-7EhN9JJbAJcH2xCa+rIOmekjiEuB0qwPdHuD5qn/wwMfRzMZo+Db4hHbR9KHrLH6H82PTwYKye/LLpDaZqoHOA==", - "dev": true - }, - "@angular/material": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-8.2.3.tgz", - "integrity": "sha512-SOczkIaqes+r+9XF/UUiokidfFKBpHkOPIaFK857sFD0FBNPvPEpOr5oHKCG3feERRwAFqHS7Wo2ohVEWypb5A==", - "requires": { - "tslib": "^1.7.1" - }, - "dependencies": { - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - } - } - }, - "@angular/platform-browser": { - "version": "8.2.14", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-8.2.14.tgz", - "integrity": "sha512-MtJptptyKzsE37JZ2VB/tI4cvMrdAH+cT9pMBYZd66YSZfKjIj5s+AZo7z8ncoskQSB1o3HMfDjSK7QXGx1mLQ==", - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - } - } - }, - "@angular/platform-browser-dynamic": { - "version": "8.2.14", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-8.2.14.tgz", - "integrity": "sha512-mO2JPR5kLU/A3AQngy9+R/Q5gaF9csMStBQjwsCRI0wNtlItOIGL6+wTYpiTuh/ux+WVN1F2sLcEYU4Zf1ud9A==", - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - } - } - }, - "@angular/platform-server": { - "version": "8.2.14", - "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-8.2.14.tgz", - "integrity": "sha512-gGAgxMmac5CyLcwgB+qCD1o75An0NmpREh/lxPgz6n6Zs9JqdqpZROLSIHqGBaU6MWo1qiOfS6L08HwYPx7ipQ==", - "requires": { - "domino": "^2.1.2", - "tslib": "^1.9.0", - "xhr2": "^0.1.4" - }, - "dependencies": { - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - } - } - }, - "@angular/router": { - "version": "8.2.14", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-8.2.14.tgz", - "integrity": "sha512-DHA2BhODqV7F0g6ZKgFaZgbsqzHHWRcfWchCOrOVKu2rYiKUTwwHVLBgZAhrpNeinq2pWanVYSIhMr7wy+LfEA==", - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - } - } - }, - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/compat-data": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.10.4.tgz", - "integrity": "sha512-t+rjExOrSVvjQQXNp5zAIYDp00KjdvGl/TpDX5REPr0S9IAIPQMTilcfG6q8c0QFmj9lSTVySV2VTsyggvtNIw==", - "dev": true, - "requires": { - "browserslist": "^4.12.0", - "invariant": "^2.2.4", - "semver": "^5.5.0" - }, - "dependencies": { - "browserslist": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.13.0.tgz", - "integrity": "sha512-MINatJ5ZNrLnQ6blGvePd/QOz9Xtu+Ne+x29iQSCHfkU5BugKVJwZKn/iiL8UbpIpa3JhviKjz+XxMo0m2caFQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001093", - "electron-to-chromium": "^1.3.488", - "escalade": "^3.0.1", - "node-releases": "^1.1.58" - } - }, - "caniuse-lite": { - "version": "1.0.30001096", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001096.tgz", - "integrity": "sha512-PFTw9UyVfbkcMEFs82q8XVlRayj7HKvnhu5BLcmjGpv+SNyiWasCcWXPGJuO0rK0dhLRDJmtZcJ+LHUfypbw1w==", - "dev": true - } - } - }, - "@babel/core": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.3.tgz", - "integrity": "sha512-4XFkf8AwyrEG7Ziu3L2L0Cv+WyY47Tcsp70JFmpftbAA1K7YL/sgE9jh9HyNj08Y/U50ItUchpN0w6HxAoX1rA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.8.3", - "@babel/helpers": "^7.8.3", - "@babel/parser": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.3", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.0", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.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" - } - }, - "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "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 - } - } - }, - "@babel/generator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.4.tgz", - "integrity": "sha512-toLIHUIAgcQygFZRAQcsLQV3CBuX6yOIru1kJk/qqqvcRmZrYe6WavZTSG+bB8MxhnL9YPf+pKQfuiP161q7ng==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", - "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", - "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", - "dev": true, - "requires": { - "@babel/helper-explode-assignable-expression": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz", - "integrity": "sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.10.4", - "browserslist": "^4.12.0", - "invariant": "^2.2.4", - "levenary": "^1.1.1", - "semver": "^5.5.0" - }, - "dependencies": { - "browserslist": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.13.0.tgz", - "integrity": "sha512-MINatJ5ZNrLnQ6blGvePd/QOz9Xtu+Ne+x29iQSCHfkU5BugKVJwZKn/iiL8UbpIpa3JhviKjz+XxMo0m2caFQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001093", - "electron-to-chromium": "^1.3.488", - "escalade": "^3.0.1", - "node-releases": "^1.1.58" - } - }, - "caniuse-lite": { - "version": "1.0.30001096", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001096.tgz", - "integrity": "sha512-PFTw9UyVfbkcMEFs82q8XVlRayj7HKvnhu5BLcmjGpv+SNyiWasCcWXPGJuO0rK0dhLRDJmtZcJ+LHUfypbw1w==", - "dev": true - } - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz", - "integrity": "sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-regex": "^7.10.4", - "regexpu-core": "^4.7.0" - } - }, - "@babel/helper-define-map": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.4.tgz", - "integrity": "sha512-nIij0oKErfCnLUCWaCaHW0Bmtl2RO9cN7+u2QT8yqTywgALKlyUVOvHDElh+b5DwVC6YB1FOYFOTWcN/+41EDA==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/types": "^7.10.4", - "lodash": "^4.17.13" - } - }, - "@babel/helper-explode-assignable-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.4.tgz", - "integrity": "sha512-4K71RyRQNPRrR85sr5QY4X3VwG4wtVoXZB9+L3r1Gp38DhELyHCtovqydRi7c1Ovb17eRGiQ/FD5s8JdU0Uy5A==", - "dev": true, - "requires": { - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", - "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.4.tgz", - "integrity": "sha512-m5j85pK/KZhuSdM/8cHUABQTAslV47OjfIB9Cc7P+PvlAoBzdb79BGNfw8RhT5Mq3p+xGd0ZfAKixbrUZx0C7A==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-module-imports": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", - "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-module-transforms": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.10.4.tgz", - "integrity": "sha512-Er2FQX0oa3nV7eM1o0tNCTx7izmQtwAQsIiaLRWtavAAEcskb0XJ5OjJbVrYXWOTr8om921Scabn4/tzlx7j1Q==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4", - "lodash": "^4.17.13" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", - "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - }, - "@babel/helper-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.4.tgz", - "integrity": "sha512-inWpnHGgtg5NOF0eyHlC0/74/VkdRITY9dtTpB2PrxKKn+AkVMRiZz/Adrx+Ssg+MLDesi2zohBW6MVq6b4pOQ==", - "dev": true, - "requires": { - "lodash": "^4.17.13" - } - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.4.tgz", - "integrity": "sha512-86Lsr6NNw3qTNl+TBcF1oRZMaVzJtbWTyTko+CQL/tvNvcGYEFKbLXDPxtW0HKk3McNOk4KzY55itGWCAGK5tg==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-wrap-function": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-replace-supers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", - "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-simple-access": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", - "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", - "dev": true, - "requires": { - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz", - "integrity": "sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz", - "integrity": "sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helpers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", - "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", - "dev": true, - "requires": { - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.4.tgz", - "integrity": "sha512-8jHII4hf+YVDsskTF6WuMB3X4Eh+PsUkC2ljq22so5rHvH+T8BzyL94VOdyFLNR8tBSVXOTbNHOKpR4TfRxVtA==", - "dev": true - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.4.tgz", - "integrity": "sha512-MJbxGSmejEFVOANAezdO39SObkURO5o/8b6fSH6D1pi9RZQt+ldppKPXfqgUWpSQ9asM6xaSaSJIaeWMDRP0Zg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.10.4", - "@babel/plugin-syntax-async-generators": "^7.8.0" - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz", - "integrity": "sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-dynamic-import": "^7.8.0" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz", - "integrity": "sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.0" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz", - "integrity": "sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.4.tgz", - "integrity": "sha512-6vh4SqRuLLarjgeOf4EaROJAHjvu9Gl+/346PbDH9yWbJyfnJ/ah3jmYKYtswEyCoWZiidvVHjHshd4WgjB9BA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.10.4" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz", - "integrity": "sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.4.tgz", - "integrity": "sha512-ZIhQIEeavTgouyMSdZRap4VPPHqJJ3NEs2cuHs5p0erH+iz6khB0qfgU8g7UuJkG88+fBMy23ZiU+nuHvekJeQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-optional-chaining": "^7.8.0" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz", - "integrity": "sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz", - "integrity": "sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz", - "integrity": "sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz", - "integrity": "sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.10.4" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz", - "integrity": "sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.4.tgz", - "integrity": "sha512-J3b5CluMg3hPUii2onJDRiaVbPtKFPLEaV5dOPY5OeAbDi1iU/UbbFFTgwb7WnanaDy7bjU35kc26W3eM5Qa0A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "lodash": "^4.17.13" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz", - "integrity": "sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-define-map": "^7.10.4", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz", - "integrity": "sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz", - "integrity": "sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz", - "integrity": "sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz", - "integrity": "sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz", - "integrity": "sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==", - "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz", - "integrity": "sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz", - "integrity": "sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz", - "integrity": "sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz", - "integrity": "sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.4.tgz", - "integrity": "sha512-3Fw+H3WLUrTlzi3zMiZWp3AR4xadAEMv6XRCYnd5jAlLM61Rn+CRJaZMaNvIpcJpQ3vs1kyifYvEVPFfoSkKOA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz", - "integrity": "sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.4.tgz", - "integrity": "sha512-Tb28LlfxrTiOTGtZFsvkjpyjCl9IoaRI52AEU/VIwOwvDQWtbNJsAqTXzh+5R7i74e/OZHH2c2w2fsOqAfnQYQ==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.10.4", - "@babel/helper-module-transforms": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz", - "integrity": "sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz", - "integrity": "sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz", - "integrity": "sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz", - "integrity": "sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.4.tgz", - "integrity": "sha512-RurVtZ/D5nYfEg0iVERXYKEgDFeesHrHfx8RT05Sq57ucj2eOYAP6eu5fynL4Adju4I/mP/I6SO0DqNWAXjfLQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz", - "integrity": "sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz", - "integrity": "sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==", - "dev": true, - "requires": { - "regenerator-transform": "^0.14.2" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz", - "integrity": "sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz", - "integrity": "sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.10.4.tgz", - "integrity": "sha512-1e/51G/Ni+7uH5gktbWv+eCED9pP8ZpRhZB3jOaI3mmzfvJTWHkuyYTv0Z5PYtyM+Tr2Ccr9kUdQxn60fI5WuQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz", - "integrity": "sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-regex": "^7.10.4" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.4.tgz", - "integrity": "sha512-4NErciJkAYe+xI5cqfS8pV/0ntlY5N5Ske/4ImxAVX7mk9Rxt2bwDTGv1Msc2BRJvWQcmYEC+yoMLdX22aE4VQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz", - "integrity": "sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz", - "integrity": "sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/preset-env": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.8.3.tgz", - "integrity": "sha512-Rs4RPL2KjSLSE2mWAx5/iCH+GC1ikKdxPrhnRS6PfFVaiZeom22VFKN4X8ZthyN61kAaR05tfXTbCvatl9WIQg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.8.0", - "@babel/helper-compilation-targets": "^7.8.3", - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-proposal-async-generator-functions": "^7.8.3", - "@babel/plugin-proposal-dynamic-import": "^7.8.3", - "@babel/plugin-proposal-json-strings": "^7.8.3", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-proposal-object-rest-spread": "^7.8.3", - "@babel/plugin-proposal-optional-catch-binding": "^7.8.3", - "@babel/plugin-proposal-optional-chaining": "^7.8.3", - "@babel/plugin-proposal-unicode-property-regex": "^7.8.3", - "@babel/plugin-syntax-async-generators": "^7.8.0", - "@babel/plugin-syntax-dynamic-import": "^7.8.0", - "@babel/plugin-syntax-json-strings": "^7.8.0", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.0", - "@babel/plugin-syntax-top-level-await": "^7.8.3", - "@babel/plugin-transform-arrow-functions": "^7.8.3", - "@babel/plugin-transform-async-to-generator": "^7.8.3", - "@babel/plugin-transform-block-scoped-functions": "^7.8.3", - "@babel/plugin-transform-block-scoping": "^7.8.3", - "@babel/plugin-transform-classes": "^7.8.3", - "@babel/plugin-transform-computed-properties": "^7.8.3", - "@babel/plugin-transform-destructuring": "^7.8.3", - "@babel/plugin-transform-dotall-regex": "^7.8.3", - "@babel/plugin-transform-duplicate-keys": "^7.8.3", - "@babel/plugin-transform-exponentiation-operator": "^7.8.3", - "@babel/plugin-transform-for-of": "^7.8.3", - "@babel/plugin-transform-function-name": "^7.8.3", - "@babel/plugin-transform-literals": "^7.8.3", - "@babel/plugin-transform-member-expression-literals": "^7.8.3", - "@babel/plugin-transform-modules-amd": "^7.8.3", - "@babel/plugin-transform-modules-commonjs": "^7.8.3", - "@babel/plugin-transform-modules-systemjs": "^7.8.3", - "@babel/plugin-transform-modules-umd": "^7.8.3", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3", - "@babel/plugin-transform-new-target": "^7.8.3", - "@babel/plugin-transform-object-super": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.8.3", - "@babel/plugin-transform-property-literals": "^7.8.3", - "@babel/plugin-transform-regenerator": "^7.8.3", - "@babel/plugin-transform-reserved-words": "^7.8.3", - "@babel/plugin-transform-shorthand-properties": "^7.8.3", - "@babel/plugin-transform-spread": "^7.8.3", - "@babel/plugin-transform-sticky-regex": "^7.8.3", - "@babel/plugin-transform-template-literals": "^7.8.3", - "@babel/plugin-transform-typeof-symbol": "^7.8.3", - "@babel/plugin-transform-unicode-regex": "^7.8.3", - "@babel/types": "^7.8.3", - "browserslist": "^4.8.2", - "core-js-compat": "^3.6.2", - "invariant": "^2.2.2", - "levenary": "^1.1.0", - "semver": "^5.5.0" - } - }, - "@babel/runtime": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.4.tgz", - "integrity": "sha512-UpTN5yUJr9b4EX2CnGNWIvER7Ab83ibv0pcvvHc4UOdrBI5jb8bj+32cCwPX6xu0mt2daFNjYhoi+X7beH0RSw==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.4" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", - "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", - "dev": true - } - } - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/traverse": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.4.tgz", - "integrity": "sha512-aSy7p5THgSYm4YyxNGz6jZpXf+Ok40QF3aA2LyIONkDHpAcJzDUqlCKXv6peqYUs2gmic849C/t2HKw2a2K20Q==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.10.4", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - }, - "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.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.4.tgz", - "integrity": "sha512-UTCFOxC3FsFHb7lkRMVvgLzaRVamXuAs2Tz4wajva4WxtVY82eZeaUBtC2Zt95FU9TiznuC0Zk35tsim8jeVpg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", - "dev": true - }, - "@ngtools/webpack": { - "version": "8.3.24", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-8.3.24.tgz", - "integrity": "sha512-OpR7t/99qNOpADayCuM67agBVdYkdbFyEEcOLaDFYh3LsefHOSSxtAGv8M77e7dguvtaljHTiVkMxgcXFsZM0Q==", - "dev": true, - "requires": { - "@angular-devkit/core": "8.3.24", - "enhanced-resolve": "4.1.0", - "rxjs": "6.4.0", - "tree-kill": "1.2.2", - "webpack-sources": "1.4.3" - }, - "dependencies": { - "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" - } - }, - "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" - } - }, - "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@nguniversal/express-engine": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/@nguniversal/express-engine/-/express-engine-8.2.6.tgz", - "integrity": "sha512-IKUKTpesgjYyB0Xg+fFhSbwbGBJhG0Wfn8MkQAi9RgSi8QsrSMkI3oUXc86Z7fpQL55D/ZIH7PekoC0Fmh/kxA==" - }, - "@nguniversal/module-map-ngfactory-loader": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/@nguniversal/module-map-ngfactory-loader/-/module-map-ngfactory-loader-8.2.6.tgz", - "integrity": "sha512-YcxXSrDZt6iDR+YbesJvprNpHd1nRLeThJwAFlcwvK/GVGSyKeWV6eqk3bRkBkgkw8OwaG/4lOQ4aofxQw+13w==" - }, - "@schematics/angular": { - "version": "8.3.22", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-8.3.22.tgz", - "integrity": "sha512-vD+UgPdbEoFPOH6xe2laFpHn/MC9R5C4A/+J9yQ6HBg5kt1YdyIBakvPOcXQCyWr5VZzDmTyMO76rd3zaef3DQ==", - "dev": true, - "requires": { - "@angular-devkit/core": "8.3.22", - "@angular-devkit/schematics": "8.3.22" - }, - "dependencies": { - "@angular-devkit/core": { - "version": "8.3.22", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.22.tgz", - "integrity": "sha512-lOEYcvK3MktjR9YZT/cUjiQE5dZxl8rZ/vgWgwDiL7RtzfXTt8lPapoJe7YKS53gLbUYiBNPCtTyTAqnslWgGA==", - "dev": true, - "requires": { - "ajv": "6.10.2", - "fast-json-stable-stringify": "2.0.0", - "magic-string": "0.25.3", - "rxjs": "6.4.0", - "source-map": "0.7.3" - } - }, - "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "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 - } - } - }, - "@schematics/update": { - "version": "0.803.22", - "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.803.22.tgz", - "integrity": "sha512-X+1sJ7YadcYxDqcLX7l7MEAIL3SHIXpCqToQdAZbAE06NdTFvg5eqiKreSdmm7ZdfL0dBe6oXi/yCDVMoL2zcw==", - "dev": true, - "requires": { - "@angular-devkit/core": "8.3.22", - "@angular-devkit/schematics": "8.3.22", - "@yarnpkg/lockfile": "1.1.0", - "ini": "1.3.5", - "pacote": "9.5.5", - "rxjs": "6.4.0", - "semver": "6.3.0", - "semver-intersect": "1.4.0" - }, - "dependencies": { - "@angular-devkit/core": { - "version": "8.3.22", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.22.tgz", - "integrity": "sha512-lOEYcvK3MktjR9YZT/cUjiQE5dZxl8rZ/vgWgwDiL7RtzfXTt8lPapoJe7YKS53gLbUYiBNPCtTyTAqnslWgGA==", - "dev": true, - "requires": { - "ajv": "6.10.2", - "fast-json-stable-stringify": "2.0.0", - "magic-string": "0.25.3", - "rxjs": "6.4.0", - "source-map": "0.7.3" - } - }, - "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "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 - } - } - }, - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "dev": true - }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, - "requires": { - "defer-to-connect": "^1.0.1" - } - }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true - }, - "@types/estree": { - "version": "0.0.45", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.45.tgz", - "integrity": "sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==", - "dev": true - }, - "@types/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", - "dev": true, - "requires": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "@types/hammerjs": { - "version": "2.0.36", - "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.36.tgz", - "integrity": "sha512-7TUK/k2/QGpEAv/BCwSHlYu3NXZhQ9ZwBYpzr9tjlPIL2C5BeGhH3DmVavRx3ZNyELX5TLC91JTz/cen6AAtIQ==" - }, - "@types/jasmine": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.6.tgz", - "integrity": "sha512-clg9raJTY0EOo5pVZKX3ZlMjlYzVU73L71q5OV1jhE2Uezb7oF94jh4CvwrW6wInquQAdhOxJz5VDF2TLUGmmA==", - "dev": true - }, - "@types/jasminewd2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.3.tgz", - "integrity": "sha512-hYDVmQZT5VA2kigd4H4bv7vl/OhlympwREUemqBdOqtrYTo5Ytm12a5W5/nGgGYdanGVxj0x/VhZ7J3hOg/YKg==", - "dev": true, - "requires": { - "@types/jasmine": "*" - } - }, - "@types/json-schema": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", - "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==", - "dev": true - }, - "@types/lodash": { - "version": "4.14.116", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.116.tgz", - "integrity": "sha512-lRnAtKnxMXcYYXqOiotTmJd74uawNWuPnsnPrrO7HiFuE3npE2iQhfABatbYDyxTNqZNuXzcKGhw37R7RjBFLg==", - "dev": true - }, - "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true - }, - "@types/node": { - "version": "8.9.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.9.4.tgz", - "integrity": "sha512-dSvD36qnQs78G1BPsrZFdPpvLgMW/dnvr5+nTW2csMs5TiP9MOXrjUbnMZOEwnIuBklXtn7b6TPA2Cuq07bDHA==", - "dev": true - }, - "@types/normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", - "dev": true - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "@types/q": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", - "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", - "dev": true - }, - "@types/resolve": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", - "integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/selenium-webdriver": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.17.tgz", - "integrity": "sha512-tGomyEuzSC1H28y2zlW6XPCaDaXFaD6soTdb4GNdmte2qfHtrKqhy0ZFs4r/1hpazCfEZqeTSRLvSasmEx89uw==", - "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.8", - "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.8.tgz", - "integrity": "sha512-JHB2/xZlXOjzjBB6fMOpH1eQAfsrpqVVIbneE0Rok16WXwFaznaI5vfg75U5WgGJm7V9W1c4xeRQDjX/zwvghA==", - "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.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", - "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", - "dev": true, - "requires": { - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", - "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", - "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", - "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==", - "dev": true - }, - "@webassemblyjs/helper-code-frame": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", - "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", - "dev": true, - "requires": { - "@webassemblyjs/wast-printer": "1.8.5" - } - }, - "@webassemblyjs/helper-fsm": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", - "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==", - "dev": true - }, - "@webassemblyjs/helper-module-context": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", - "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "mamacro": "^0.0.3" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", - "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", - "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", - "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", - "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", - "dev": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", - "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", - "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/helper-wasm-section": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-opt": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "@webassemblyjs/wast-printer": "1.8.5" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", - "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", - "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", - "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" - } - }, - "@webassemblyjs/wast-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", - "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/floating-point-hex-parser": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-code-frame": "1.8.5", - "@webassemblyjs/helper-fsm": "1.8.5", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", - "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5", - "@xtuc/long": "4.2.2" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "@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 - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "acorn": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", - "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", - "dev": true - }, - "adm-zip": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", - "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", - "dev": true - }, - "after": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", - "dev": true - }, - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "agentkeepalive": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", - "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", - "dev": true, - "requires": { - "humanize-ms": "^1.2.1" - } - }, - "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "dev": true - }, - "ajv-keywords": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.1.tgz", - "integrity": "sha512-KWcq3xN8fDjSB+IMoh2VaXVhRI0BBGxoYp3rx7Pkb6z0cFjYR9Q9l4yZqqals0/zsioCmocC5H6UvsGD4MoIBA==", - "dev": true - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true - }, - "ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", - "dev": true, - "requires": { - "string-width": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "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": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", - "dev": true, - "requires": { - "type-fest": "^0.11.0" - } - }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "app-root-path": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.2.1.tgz", - "integrity": "sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA==", - "dev": true - }, - "append-transform": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", - "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", - "dev": true, - "requires": { - "default-require-extensions": "^1.0.0" - } - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "dev": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "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" - } - }, - "aria-query": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", - "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", - "dev": true, - "requires": { - "ast-types-flow": "0.0.7", - "commander": "^2.11.0" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" - }, - "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==" - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true - }, - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - }, - "array-slice": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", - "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=" - }, - "arraybuffer.slice": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", - "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco=", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "dev": true - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, - "assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", - "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", - "dev": true, - "requires": { - "object-assign": "^4.1.1", - "util": "0.10.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - } - } - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" - }, - "ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", - "dev": true - }, - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", - "dev": true - }, - "async-foreach": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", - "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", - "dev": true - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" - }, - "autoprefixer": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.6.1.tgz", - "integrity": "sha512-aVo5WxR3VyvyJxcJC3h4FKfwCQvQWb1tSI5VHNibddCVWrcD1NvlxEweg3TSgiPztMnWfjpy2FURKA2kvDE+Tw==", - "dev": true, - "requires": { - "browserslist": "^4.6.3", - "caniuse-lite": "^1.0.30000980", - "chalk": "^2.4.2", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^7.0.17", - "postcss-value-parser": "^4.0.0" - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", - "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==", - "dev": true - }, - "axobject-query": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", - "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", - "dev": true - }, - "babel-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=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "babel-generator": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", - "dev": true, - "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" - }, - "dependencies": { - "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", - "dev": true - } - } - }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, - "requires": { - "object.assign": "^4.1.0" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - } - } - }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" - } - }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" - }, - "dependencies": { - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - } - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - }, - "dependencies": { - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - } - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", - "dev": true - }, - "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==", - "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=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", - "dev": true - }, - "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true - }, - "base64id": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", - "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", - "dev": true - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", - "dev": true, - "requires": { - "callsite": "1.0.0" - } - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" - }, - "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true - }, - "blob": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", - "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=", - "dev": true - }, - "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, - "requires": { - "inherits": "~2.0.0" - } - }, - "blocking-proxy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz", - "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "bn.js": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.2.tgz", - "integrity": "sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA==", - "dev": true - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "dev": true, - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "dependencies": { - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "dev": true - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "dev": true - } - } - }, - "bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", - "dev": true, - "requires": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true - }, - "bowser": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.10.0.tgz", - "integrity": "sha512-OCsqTQboTEWWsUjcp5jLSw2ZHsBiv2C105iFs61bOT0Hnwi9p7/uuXdd7mu8RYcarREfdjNN+8LitmEHATsLYg==" - }, - "boxen": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-3.2.0.tgz", - "integrity": "sha512-cU4J/+NodM3IHdSL2yN8bqYqnmlBTidDR4RC7nJs61ZmtGz8VZzM3HLQX0zY5mrSmPtR3xWwsq2jOUQqFZN8+A==", - "dev": true, - "requires": { - "ansi-align": "^3.0.0", - "camelcase": "^5.3.1", - "chalk": "^2.4.2", - "cli-boxes": "^2.2.0", - "string-width": "^3.0.0", - "term-size": "^1.2.0", - "type-fest": "^0.3.0", - "widest-line": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "type-fest": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", - "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", - "dev": true - } - } - }, - "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==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, - "browserify-sign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.0.tgz", - "integrity": "sha512-hEZC1KEeYuoHRqhGhTy6gWrpJA3ZDjFWv0DE61643ZnOXAKJb3u7yWcrU0mMc9SwAqK1n7myPGndkp0dFG7NFA==", - "dev": true, - "requires": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "elliptic": "^6.5.2", - "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "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.8.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.3.tgz", - "integrity": "sha512-iU43cMMknxG1ClEZ2MDKeonKE1CCrFVkQK2AqO2YWFmvIrx4JWrvQ4w4hQez6EpVI8rHTtqh/ruHHDHSOKxvUg==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001017", - "electron-to-chromium": "^1.3.322", - "node-releases": "^1.1.44" - } - }, - "browserstack": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.6.0.tgz", - "integrity": "sha512-HJDJ0TSlmkwnt9RZ+v5gFpa1XZTBYTj0ywvLwJ3241J7vMw2jAsGNVhKHtmCOyg+VxeLZyaibO9UL71AsUeDIw==", - "dev": true, - "requires": { - "https-proxy-agent": "^2.2.1" - } - }, - "buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "dev": true, - "requires": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "dev": true - }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" - }, - "buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", - "dev": true - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" - }, - "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": "3.1.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", - "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", - "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=" - }, - "cacache": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.2.tgz", - "integrity": "sha512-ifKgxH2CKhJEg6tNdAwziu6Q33EvuG26tYcda6PT3WKisZcYDXsnEdnRv67Po3yCzFfaSoMjGZzJyD2c3DT1dg==", - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dev": true, - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true - }, - "normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", - "dev": true - } - } - }, - "caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", - "dev": true, - "requires": { - "callsites": "^2.0.0" - } - }, - "caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", - "dev": true, - "requires": { - "caller-callsite": "^2.0.0" - } - }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", - "dev": true - }, - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true, - "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" - }, - "dependencies": { - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true - } - } - }, - "camelize": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", - "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" - }, - "caniuse-lite": { - "version": "1.0.30001019", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001019.tgz", - "integrity": "sha512-6ljkLtF1KM5fQ+5ZN0wuyVvvebJxgJPTmScOMaFuQN2QuOzvRJnWSKfzQskQU5IOU4Gap3zasYPIinzwUjoj/g==", - "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 - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "cheerio": { - "version": "1.0.0-rc.2", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.2.tgz", - "integrity": "sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs=", - "dev": true, - "requires": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.0", - "entities": "~1.1.1", - "htmlparser2": "^3.9.1", - "lodash": "^4.15.0", - "parse5": "^3.0.1" - }, - "dependencies": { - "parse5": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", - "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", - "dev": true, - "requires": { - "@types/node": "*" - } - } - } - }, - "chokidar": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz", - "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.4.0" - }, - "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "chrome-trace-event": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", - "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "circular-dependency-plugin": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz", - "integrity": "sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw==", - "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==", - "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=", - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "classlist.js": { - "version": "1.1.20150312", - "resolved": "https://registry.npmjs.org/classlist.js/-/classlist.js-1.1.20150312.tgz", - "integrity": "sha1-HXCEL3Ai8I2awIbOaeWyUPLFd4k=" - }, - "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-boxes": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz", - "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==", - "dev": true - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-width": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", - "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", - "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": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "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": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - } - }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "codelyzer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-5.0.1.tgz", - "integrity": "sha512-UVV76+/y1RwaxzCeGPFE3G4GFtfV42r3x8EmRd7XMNFLlLC0ewdtCqWTbvhwPQMxFZZ+OTLEOJNWfyPPn3QFWg==", - "dev": true, - "requires": { - "app-root-path": "^2.1.0", - "aria-query": "^3.0.0", - "axobject-query": "^2.0.2", - "css-selector-tokenizer": "^0.7.1", - "cssauron": "^1.4.0", - "damerau-levenshtein": "^1.0.4", - "semver-dsl": "^1.0.1", - "source-map": "^0.5.7", - "sprintf-js": "^1.1.2" - }, - "dependencies": { - "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", - "dev": true - } - } - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "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 - }, - "combine-lists": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz", - "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", - "dev": true, - "requires": { - "lodash": "^4.5.0" - } - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "compare-versions": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", - "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", - "dev": true - }, - "component-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", - "dev": true - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "component-inherit": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", - "dev": true - }, - "compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "requires": { - "mime-db": ">= 1.43.0 < 2" - } - }, - "compression": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz", - "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==", - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.14", - "debug": "2.6.9", - "on-headers": "~1.0.1", - "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==", - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "configstore": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-4.0.0.tgz", - "integrity": "sha512-CmquAXFBocrzaSM8mtGPMM/HiWmyIpr4CcJl/rgY2uCObZ/S7cKU0silxslqJejl+t/T9HS8E0PUNQD81JGUEQ==", - "dev": true, - "requires": { - "dot-prop": "^4.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" - }, - "dependencies": { - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "dev": true, - "requires": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - } - }, - "connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", - "dev": true - }, - "console-browserify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", - "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "dev": true, - "requires": { - "safe-buffer": "5.1.2" - } - }, - "content-security-policy-builder": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-2.1.0.tgz", - "integrity": "sha512-/MtLWhJVvJNkA9dVLAp6fg9LxD2gfI6R2Fi1hPmfjYXSahJJzcfvoeDOxSyp4NvxMuwWv3WMssE9o31DoULHrQ==" - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true - }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true - }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" - }, - "copy-webpack-plugin": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz", - "integrity": "sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg==", - "dev": true, - "requires": { - "cacache": "^12.0.3", - "find-cache-dir": "^2.1.0", - "glob-parent": "^3.1.0", - "globby": "^7.1.1", - "is-glob": "^4.0.1", - "loader-utils": "^1.2.3", - "minimatch": "^3.0.4", - "normalize-path": "^3.0.0", - "p-limit": "^2.2.1", - "schema-utils": "^1.0.0", - "serialize-javascript": "^2.1.2", - "webpack-log": "^2.0.0" - }, - "dependencies": { - "cacache": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", - "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "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" - } - } - } - }, - "core-js": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.4.tgz", - "integrity": "sha1-8si/GB8qgLkvNgEhQpzmOi8K6uA=" - }, - "core-js-compat": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz", - "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==", - "dev": true, - "requires": { - "browserslist": "^4.8.5", - "semver": "7.0.0" - }, - "dependencies": { - "browserslist": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.13.0.tgz", - "integrity": "sha512-MINatJ5ZNrLnQ6blGvePd/QOz9Xtu+Ne+x29iQSCHfkU5BugKVJwZKn/iiL8UbpIpa3JhviKjz+XxMo0m2caFQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001093", - "electron-to-chromium": "^1.3.488", - "escalade": "^3.0.1", - "node-releases": "^1.1.58" - } - }, - "caniuse-lite": { - "version": "1.0.30001096", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001096.tgz", - "integrity": "sha512-PFTw9UyVfbkcMEFs82q8XVlRayj7HKvnhu5BLcmjGpv+SNyiWasCcWXPGJuO0rK0dhLRDJmtZcJ+LHUfypbw1w==", - "dev": true - }, - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "cosmiconfig": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", - "dev": true, - "requires": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.1", - "parse-json": "^4.0.0" - } - }, - "coverage-istanbul-loader": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/coverage-istanbul-loader/-/coverage-istanbul-loader-2.0.3.tgz", - "integrity": "sha512-LiGRvyIuzVYs3M1ZYK1tF0HekjH0DJ8zFdUwAZq378EJzqOgToyb1690dp3TAUlP6Y+82uu42LRjuROVeJ54CA==", - "dev": true, - "requires": { - "convert-source-map": "^1.7.0", - "istanbul-lib-instrument": "^4.0.0", - "loader-utils": "^1.2.3", - "merge-source-map": "^1.1.0", - "schema-utils": "^2.6.1" - }, - "dependencies": { - "ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - } - } - } - }, - "cpx": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/cpx/-/cpx-1.5.0.tgz", - "integrity": "sha1-GFvgGFEdhycN7czCkxceN2VauI8=", - "dev": true, - "requires": { - "babel-runtime": "^6.9.2", - "chokidar": "^1.6.0", - "duplexer": "^0.1.1", - "glob": "^7.0.5", - "glob2base": "^0.0.12", - "minimatch": "^3.0.2", - "mkdirp": "^0.5.1", - "resolve": "^1.1.7", - "safe-buffer": "^5.0.1", - "shell-quote": "^1.6.1", - "subarg": "^1.0.0" - }, - "dependencies": { - "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "dev": true, - "requires": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" - } - }, - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "^1.0.1" - } - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - } - }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "dev": true, - "requires": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "fsevents": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" - } - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "^0.1.0" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "dev": true, - "optional": true, - "requires": { - "nan": "^2.12.1" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "^2.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" - }, - "dependencies": { - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "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-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" - }, - "dependencies": { - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "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" - }, - "dependencies": { - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "^1.0.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" - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - } - }, - "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" - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "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" - } - } - } - } - } - }, - "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" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "cronstrue": { - "version": "1.94.0", - "resolved": "https://registry.npmjs.org/cronstrue/-/cronstrue-1.94.0.tgz", - "integrity": "sha512-DW5OIfJwNGj9R8RCRGsFt0lxp0LKUl6BOElhaNAEkswwihbv6s867oKyOVgh/eQt1z90bBpelWMTR5/mGRB9Hw==" - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - } - }, - "crypto-random-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", - "dev": true - }, - "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-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "dev": true, - "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, - "css-selector-tokenizer": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.2.tgz", - "integrity": "sha512-yj856NGuAymN6r8bn8/Jl46pR+OC3eEvAhfGYDUe7YPtTPAYrSSw4oAniZ9Y8T5B92hjhwTBLUen0/vKPxf6pw==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "fastparse": "^1.1.2", - "regexpu-core": "^4.6.0" - } - }, - "css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", - "dev": true - }, - "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": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true - }, - "cuint": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", - "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=", - "dev": true - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "requires": { - "array-find-index": "^1.0.1" - } - }, - "custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", - "dev": true - }, - "cyclist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", - "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", - "dev": true - }, - "damerau-levenshtein": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", - "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==", - "dev": true - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "dasherize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dasherize/-/dasherize-2.0.0.tgz", - "integrity": "sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg=" - }, - "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" - } - }, - "debuglog": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", - "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=", - "dev": true - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "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=" - }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", - "dev": true, - "requires": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, - "default-gateway": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", - "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "ip-regex": "^2.1.0" - } - }, - "default-require-extensions": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", - "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", - "dev": true, - "requires": { - "strip-bom": "^2.0.0" - } - }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true - }, - "defiant.js": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/defiant.js/-/defiant.js-2.2.6.tgz", - "integrity": "sha512-c2wOifF1AnxtqnYk4HE/k44OHCu6hskQYQBYUey9GNcvsom8eDKJoBHCzncC38sCrR1iuAvFnxRkXbF6KSubKA==", - "requires": { - "puppeteer": "^1.7.0" - } - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "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==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "del": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", - "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", - "dev": true, - "requires": { - "@types/glob": "^7.1.1", - "globby": "^6.1.0", - "is-path-cwd": "^2.0.0", - "is-path-in-cwd": "^2.0.0", - "p-map": "^2.0.0", - "pify": "^4.0.1", - "rimraf": "^2.6.3" - }, - "dependencies": { - "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 - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "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.1", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", - "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "dev": true - }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", - "dev": true - }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true, - "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 - }, - "dezalgo": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", - "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", - "dev": true, - "requires": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", - "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" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, - "dir-glob": { - "version": "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-prefetch-control": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dns-prefetch-control/-/dns-prefetch-control-0.2.0.tgz", - "integrity": "sha512-hvSnros73+qyZXhHFjx2CMLwoj3Fe7eR9EJsFsqmcI1bB2OBWL/+0YzaEaKssCHnj/6crawNnUyw74Gm2EKe+Q==" - }, - "dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", - "dev": true, - "requires": { - "buffer-indexof": "^1.0.0" - } - }, - "dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", - "dev": true, - "requires": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" - } - }, - "dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", - "dev": true, - "requires": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" - } - }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true - }, - "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "dev": true, - "requires": { - "domelementtype": "1" - } - }, - "domino": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/domino/-/domino-2.1.5.tgz", - "integrity": "sha512-vMDo7f6ogUV9PkzmxXLiXzJkJZqU09Le4C40mj+HmAGS/2FPmdetoNOQZXpu2kekn0GJKvtwKMAVoruTj60Xww==" - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "dont-sniff-mimetype": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/dont-sniff-mimetype/-/dont-sniff-mimetype-1.1.0.tgz", - "integrity": "sha512-ZjI4zqTaxveH2/tTlzS1wFp+7ncxNZaIEWYg3lzZRHkKf5zPT/MnEG6WL0BhHMJUabkh8GeU5NL5j+rEUCb7Ug==" - }, - "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", - "dev": true, - "requires": { - "is-obj": "^1.0.0" - } - }, - "duplexer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", - "dev": true - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "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, - "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.492", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.492.tgz", - "integrity": "sha512-AD6v9Y2wN0HuoRH4LwCmlSHjkKq51D1U52bTuvM5uPzisbHVm3Hms15c42TBFLewxnSqxAynK/tbeaUi4Rnjqw==", - "dev": true - }, - "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", - "dev": true, - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" - }, - "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.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "engine.io": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.3.tgz", - "integrity": "sha1-jef5eJXSDTm4X4ju7nd7K9QrE9Q=", - "dev": true, - "requires": { - "accepts": "1.3.3", - "base64id": "1.0.0", - "cookie": "0.3.1", - "debug": "2.3.3", - "engine.io-parser": "1.3.2", - "ws": "1.1.2" - }, - "dependencies": { - "accepts": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", - "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", - "dev": true, - "requires": { - "mime-types": "~2.1.11", - "negotiator": "0.6.1" - } - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true - }, - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", - "dev": true - }, - "ws": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz", - "integrity": "sha1-iiRPoFJAHgjJiGz0SoUYnh/UBn8=", - "dev": true, - "requires": { - "options": ">=0.0.5", - "ultron": "1.0.x" - } - } - } - }, - "engine.io-client": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.8.3.tgz", - "integrity": "sha1-F5jtk0USRkU9TG9jXXogH+lA1as=", - "dev": true, - "requires": { - "component-emitter": "1.2.1", - "component-inherit": "0.0.3", - "debug": "2.3.3", - "engine.io-parser": "1.3.2", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parsejson": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "ws": "1.1.2", - "xmlhttprequest-ssl": "1.5.3", - "yeast": "0.1.2" - }, - "dependencies": { - "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 - }, - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - }, - "ws": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz", - "integrity": "sha1-iiRPoFJAHgjJiGz0SoUYnh/UBn8=", - "dev": true, - "requires": { - "options": ">=0.0.5", - "ultron": "1.0.x" - } - } - } - }, - "engine.io-parser": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz", - "integrity": "sha1-k3sHnwAH0Ik+xW1GyyILjLQ1Igo=", - "dev": true, - "requires": { - "after": "0.8.2", - "arraybuffer.slice": "0.0.6", - "base64-arraybuffer": "0.1.5", - "blob": "0.0.4", - "has-binary": "0.1.7", - "wtf-8": "1.0.0" - } - }, - "enhanced-resolve": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.2.0.tgz", - "integrity": "sha512-S7eiFb/erugyd1rLb6mQ3Vuq+EXHv5cpCkNqqIkYkBgN2QdFnyCZzFBleqwGEx4lgNGYij81BWnCrFNK7vxvjQ==", - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.5.0", - "tapable": "^1.0.0" - } - }, - "ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", - "dev": true - }, - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "dev": true - }, - "err-code": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", - "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", - "dev": true - }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "requires": { - "prr": "~1.0.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "requires": { - "es6-promise": "^4.0.3" - } - }, - "escalade": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.1.tgz", - "integrity": "sha512-DR6NO3h9niOT+MZs7bjxlj2a1k+POu5RN8CLTPX2+i78bRi9eLe7+0zXgUHMnGXWybYcL61E9hGhPKqedy8tQA==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "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.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "estree-walker": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", - "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "dev": true - }, - "eventemitter3": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", - "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==", - "dev": true - }, - "events": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", - "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==", - "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": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expand-braces": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz", - "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", - "dev": true, - "requires": { - "array-slice": "^0.2.3", - "array-unique": "^0.2.1", - "braces": "^0.1.2" - }, - "dependencies": { - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "braces": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz", - "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", - "dev": true, - "requires": { - "expand-range": "^0.1.0" - } - }, - "expand-range": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz", - "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", - "dev": true, - "requires": { - "is-number": "^0.1.1", - "repeat-string": "^0.2.2" - } - }, - "is-number": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz", - "integrity": "sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=", - "dev": true - }, - "repeat-string": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz", - "integrity": "sha1-x6jTI2BoNiBZp+RlH8aITosftK4=", - "dev": true - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true, - "requires": { - "fill-range": "^2.1.0" - }, - "dependencies": { - "fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", - "dev": true, - "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" - } - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "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" - } - }, - "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" - } - } - } - }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, - "expect-ct": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/expect-ct/-/expect-ct-0.2.0.tgz", - "integrity": "sha512-6SK3MG/Bbhm8MsgyJAylg+ucIOU71/FzyFalcfu5nY19dH8y/z0tBJU0wrNBXD4B27EoQtqPF/9wqH0iYAd04g==" - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "dev": true, - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "dev": true - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "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==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "extract-zip": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", - "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", - "dev": true, - "requires": { - "concat-stream": "1.6.2", - "debug": "2.6.9", - "mkdirp": "0.5.1", - "yauzl": "2.4.1" - }, - "dependencies": { - "fd-slicer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", - "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", - "dev": true, - "requires": { - "pend": "~1.2.0" - } - }, - "yauzl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", - "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", - "dev": true, - "requires": { - "fd-slicer": "~1.0.1" - } - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "fast-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" - } - }, - "fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "requires": { - "pend": "~1.2.0" - } - }, - "feature-policy": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/feature-policy/-/feature-policy-0.3.0.tgz", - "integrity": "sha512-ZtijOTFN7TzCujt1fnNhfWPFPSHeZkesff9AXZj+UEjYBynWNUIYpC87Ve4wHzyexQsImicLu7WsC2LHq7/xrQ==" - }, - "figgy-pudding": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", - "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", - "dev": true - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-loader": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.2.0.tgz", - "integrity": "sha512-+xZnaK5R8kBJrHK0/6HRlrKNamvVS5rjyuju+rnyxRGuwUJwpAMsVzUl5dz6rK8brkzjV6JpcFNjp6NqV0g1OQ==", - "dev": true, - "requires": { - "loader-utils": "^1.2.3", - "schema-utils": "^2.0.0" - }, - "dependencies": { - "ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - } - } - } - }, - "file-select": { - "version": "file:dist/file-select/file-select-0.0.1.tgz", - "integrity": "sha512-7s7MRWvt+LQp9WB+3OIPfsLM0OOJT4VqmyUbsNGccP08p/X0c/PnzoYlF+5iYlGF/iL99Yf5WMFsrYx5jooJSA==", - "requires": { - "tslib": "^1.9.0" - } - }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true - }, - "fileset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", - "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", - "dev": true, - "requires": { - "glob": "^7.0.3", - "minimatch": "^3.0.3" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "find-cache-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.0.0.tgz", - "integrity": "sha512-t7ulV1fmbxh5G9l/492O1p5+EBbr3uwpt6odhFTMc+nWyhmbloe+ja9BZ8pIBtqFWhOmCWVjx+pTW4zDkFoclw==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.0", - "pkg-dir": "^4.1.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "find-index": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz", - "integrity": "sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ=", - "dev": true - }, - "find-parent-dir": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.0.tgz", - "integrity": "sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=", - "dev": true - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "find-versions": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz", - "integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==", - "dev": true, - "requires": { - "semver-regex": "^2.0.0" - } - }, - "findup-sync": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", - "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", - "dev": true, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^3.1.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" - }, - "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" - } - } - } - }, - "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.12.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", - "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==", - "dev": true - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" - }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "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 - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "dev": true - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "requires": { - "map-cache": "^0.2.2" - } - }, - "frameguard": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/frameguard/-/frameguard-3.1.0.tgz", - "integrity": "sha512-TxgSKM+7LTA6sidjOiSZK9wxY0ffMPY3Wta//MqwmX0nZuEHc8QrkV8Fh3ZhMJeiH+Uyh/tcaarImRy8u77O7g==" - }, - "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-access": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", - "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", - "dev": true, - "requires": { - "null-check": "^1.0.0" - } - }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "dev": true, - "requires": { - "minipass": "^2.6.0" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "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=" - }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "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, - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", - "dev": true, - "requires": { - "globule": "^1.0.0" - } - }, - "genfun": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", - "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", - "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 - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" - }, - "dependencies": { - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "^2.0.0" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "^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" - } - } - } - }, - "glob2base": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", - "integrity": "sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY=", - "dev": true, - "requires": { - "find-index": "^0.1.1" - } - }, - "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", - "dev": true, - "requires": { - "ini": "^1.3.4" - } - }, - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - } - }, - "global-modules-path": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/global-modules-path/-/global-modules-path-2.3.1.tgz", - "integrity": "sha512-y+shkf4InI7mPRHSo2b/k6ix6+NLDtyccYv86whhxrSGX9wjPX1VMITmrDbE1eh7zkzhiWtW2sHklJYoQ62Cxg==", - "dev": true - }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "globby": { - "version": "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" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "globule": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", - "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", - "dev": true, - "requires": { - "glob": "~7.1.1", - "lodash": "~4.17.10", - "minimatch": "~3.0.2" - } - }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" - }, - "hammerjs": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", - "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=" - }, - "handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "dev": true - }, - "handlebars": { - "version": "4.7.6", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", - "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", - "dev": true, - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "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 - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "dev": true, - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-binary": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", - "integrity": "sha1-aOYesWIQyVRaClzOBqhzkS/h5ow=", - "dev": true, - "requires": { - "isarray": "0.0.1" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - } - } - }, - "has-cors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "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=", - "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=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", - "dev": true - }, - "hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dev": true, - "requires": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "helmet": { - "version": "3.21.2", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.21.2.tgz", - "integrity": "sha512-okUo+MeWgg00cKB8Csblu8EXgcIoDyb5ZS/3u0W4spCimeVuCUvVZ6Vj3O2VJ1Sxpyb8jCDvzu0L1KKT11pkIg==", - "requires": { - "depd": "2.0.0", - "dns-prefetch-control": "0.2.0", - "dont-sniff-mimetype": "1.1.0", - "expect-ct": "0.2.0", - "feature-policy": "0.3.0", - "frameguard": "3.1.0", - "helmet-crossdomain": "0.4.0", - "helmet-csp": "2.9.4", - "hide-powered-by": "1.1.0", - "hpkp": "2.0.0", - "hsts": "2.2.0", - "ienoopen": "1.1.0", - "nocache": "2.1.0", - "referrer-policy": "1.2.0", - "x-xss-protection": "1.3.0" - } - }, - "helmet-crossdomain": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/helmet-crossdomain/-/helmet-crossdomain-0.4.0.tgz", - "integrity": "sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA==" - }, - "helmet-csp": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.9.4.tgz", - "integrity": "sha512-qUgGx8+yk7Xl8XFEGI4MFu1oNmulxhQVTlV8HP8tV3tpfslCs30OZz/9uQqsWPvDISiu/NwrrCowsZBhFADYqg==", - "requires": { - "bowser": "^2.7.0", - "camelize": "1.0.0", - "content-security-policy-builder": "2.1.0", - "dasherize": "2.0.0" - } - }, - "hide-powered-by": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hide-powered-by/-/hide-powered-by-1.1.0.tgz", - "integrity": "sha512-Io1zA2yOA1YJslkr+AJlWSf2yWFkKjvkcL9Ni1XSUqnGLr/qRQe2UI3Cn/J9MsJht7yEVCe0SscY1HgVMujbgg==" - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "requires": { - "parse-passwd": "^1.0.0" - } - }, - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "hpkp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hpkp/-/hpkp-2.0.0.tgz", - "integrity": "sha1-EOFCJk52IVpdMMROxD3mTe5tFnI=" - }, - "hsts": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.2.0.tgz", - "integrity": "sha512-ToaTnQ2TbJkochoVcdXYm4HOCliNozlviNsg+X2XQLQvZNI/kCHR9rZxVYpJB3UPcHz80PgxRyWQ7PdU1r+VBQ==", - "requires": { - "depd": "2.0.0" - } - }, - "html-entities": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz", - "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==", - "dev": true - }, - "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "dev": true, - "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "http-cache-semantics": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", - "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", - "dev": true - }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", - "dev": true - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "dependencies": { - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - } - } - }, - "http-parser-js": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz", - "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==", - "dev": true - }, - "http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "http-proxy-agent": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", - "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", - "dev": true, - "requires": { - "agent-base": "4", - "debug": "3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "http-proxy-middleware": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", - "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", - "dev": true, - "requires": { - "http-proxy": "^1.17.0", - "is-glob": "^4.0.0", - "lodash": "^4.17.11", - "micromatch": "^3.1.10" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "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==" - } - } - }, - "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" - } - }, - "husky": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/husky/-/husky-4.2.1.tgz", - "integrity": "sha512-Qa0lRreeIf4Tl92sSs42ER6qc3hzoyQPPorzOrFWfPEVbdi6LuvJEqWKPk905fOWIR76iBpp7ECZNIwk+a8xuQ==", - "dev": true, - "requires": { - "chalk": "^3.0.0", - "ci-info": "^2.0.0", - "compare-versions": "^3.5.1", - "cosmiconfig": "^6.0.0", - "find-versions": "^3.2.0", - "opencollective-postinstall": "^2.0.2", - "pkg-dir": "^4.2.0", - "please-upgrade-node": "^3.2.0", - "slash": "^3.0.0", - "which-pm-runs": "^1.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "parse-json": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", - "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1", - "lines-and-columns": "^1.1.6" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true - }, - "ienoopen": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ienoopen/-/ienoopen-1.1.0.tgz", - "integrity": "sha512-MFs36e/ca6ohEKtinTJ5VvAJ6oDRAYFdYXweUnGY9L9vcoqFOU4n2ZhmJ0C4z/cwGZ3YIQRSB3XZ1+ghZkY5NQ==" - }, - "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.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "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 - }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", - "dev": 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-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", - "dev": true, - "requires": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" - } - }, - "import-from": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", - "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" - } - }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "dev": true - }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dev": true, - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - } - }, - "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.1", - "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz", - "integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==", - "dev": 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, - "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 - }, - "infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "injection-js": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/injection-js/-/injection-js-2.3.1.tgz", - "integrity": "sha512-t+kpDAOL/DUZ68JncAhsb8C91qhJ6dXRMcOuvJfNA7sp63etdiQe6KQoxE/nZ5b2eTi0TQX6OothOCm89cLAJQ==", - "dev": true, - "requires": { - "tslib": "^1.9.3" - } - }, - "inquirer": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.1.tgz", - "integrity": "sha512-uxNHBeQhRXIoHWTSNYUFhQVrHYFThIt6IVo2fFmSe8aBwdR3/w6b58hJpiL/fMukFkvGzjg+hSxFtwvVmKZmXw==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^2.4.2", - "cli-cursor": "^3.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.15", - "mute-stream": "0.0.8", - "run-async": "^2.2.0", - "rxjs": "^6.4.0", - "string-width": "^4.1.0", - "strip-ansi": "^5.1.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - } - } - } - } - }, - "internal-ip": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", - "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", - "dev": true, - "requires": { - "default-gateway": "^4.2.0", - "ipaddr.js": "^1.9.0" - } - }, - "interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true - }, - "is-absolute-url": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", - "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-arguments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", - "dev": true - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "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==" - } - } - }, - "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-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true, - "requires": { - "is-primitive": "^2.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" - }, - "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.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-installed-globally": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", - "dev": true, - "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" - }, - "dependencies": { - "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-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", - "dev": true - }, - "is-npm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-3.0.0.tgz", - "integrity": "sha512-wsigDr1Kkschp2opC4G3yA6r9EgVA6NjRpWzIi9axXqeIaAATPRJc4uLujXe3Nd9uO8KoDyA4MD6aZSeXTADhA==", - "dev": true - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true - }, - "is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true - }, - "is-path-in-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", - "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", - "dev": true, - "requires": { - "is-path-inside": "^2.1.0" - } - }, - "is-path-inside": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", - "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", - "dev": true, - "requires": { - "path-is-inside": "^1.0.2" - } - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "requires": { - "isobject": "^3.0.1" - } - }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true - }, - "is-reference": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", - "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", - "dev": true, - "requires": { - "@types/estree": "*" - } - }, - "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - }, - "is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isbinaryfile": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", - "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", - "dev": true, - "requires": { - "buffer-alloc": "^1.2.0" - } - }, - "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=" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul-api": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.3.7.tgz", - "integrity": "sha512-4/ApBnMVeEPG3EkSzcw25wDe4N66wxwn+KKn6b47vyek8Xb3NBAcg4xfuQbS7BqcZuTX4wxfD5lVagdggR3gyA==", - "dev": true, - "requires": { - "async": "^2.1.4", - "fileset": "^2.0.2", - "istanbul-lib-coverage": "^1.2.1", - "istanbul-lib-hook": "^1.2.2", - "istanbul-lib-instrument": "^1.10.2", - "istanbul-lib-report": "^1.1.5", - "istanbul-lib-source-maps": "^1.2.6", - "istanbul-reports": "^1.5.1", - "js-yaml": "^3.7.0", - "mkdirp": "^0.5.1", - "once": "^1.4.0" - }, - "dependencies": { - "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" - } - } - } - }, - "istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.2.2.tgz", - "integrity": "sha512-/Jmq7Y1VeHnZEQ3TL10VHyb564mn6VrQXHchON9Jf/AEcmQ3ZIiyD1BVzNOKTZf/G3gE+kiGK6SmpF9y3qGPLw==", - "dev": true, - "requires": { - "append-transform": "^0.4.0" - } - }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.5.tgz", - "integrity": "sha512-UsYfRMoi6QO/doUshYNqcKJqVmFe9w51GZz8BS3WB0lYxAllQYklka2wP9+dGZeHYaWIdcXUx8JGdbqaoXRXzw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^1.2.1", - "mkdirp": "^0.5.1", - "path-parse": "^1.0.5", - "supports-color": "^3.1.2" - }, - "dependencies": { - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "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 - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.6.tgz", - "integrity": "sha512-TtbsY5GIHgbMsMiRw35YBHGpZ1DVFEO19vxxeiDMYaeOFOCzfnYVxvl6pOUIZR4dtPhAGpSMup8OyF8ubsaqEg==", - "dev": true, - "requires": { - "debug": "^3.1.0", - "istanbul-lib-coverage": "^1.2.1", - "mkdirp": "^0.5.1", - "rimraf": "^2.6.1", - "source-map": "^0.5.3" - }, - "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" - } - }, - "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 - }, - "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 - } - } - }, - "istanbul-reports": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.5.1.tgz", - "integrity": "sha512-+cfoZ0UXzWjhAdzosCPP3AN8vvef8XDkWtTfgaN+7L3YTpNYITnCaEkceo5SEYy644VkHka/P1FvkWvrG/rrJw==", - "dev": true, - "requires": { - "handlebars": "^4.0.3" - } - }, - "jasmine": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", - "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", - "dev": true, - "requires": { - "exit": "^0.1.2", - "glob": "^7.0.6", - "jasmine-core": "~2.8.0" - }, - "dependencies": { - "jasmine-core": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", - "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", - "dev": true - } - } - }, - "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" - } - }, - "jasminewd2": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", - "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=", - "dev": true - }, - "jest-worker": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", - "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", - "dev": true, - "requires": { - "merge-stream": "^2.0.0", - "supports-color": "^6.1.0" - }, - "dependencies": { - "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" - } - } - } - }, - "js-base64": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.2.tgz", - "integrity": "sha512-1hgLrLIrmCgZG+ID3VoLNLOSwjGnoZa8tyrUdEteMeIzsT6PH7PMLyUvbDwzNE56P3PNxyvuIOx4Uh2E5rzQIw==", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", - "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 - }, - "jsdraw-wrapper": { - "version": "file:dist/jsdraw-wrapper/jsdraw-wrapper-0.0.1.tgz", - "integrity": "sha512-p8ku4Cien8NEKpBSxrngixLCqPbpglP8Y8ZZqxsnNZfUsbum2VH+bKgkX512QuBRC1xo+MS0GPNvJu9aDq6iMA==", - "requires": { - "tslib": "^1.9.0" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json3": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", - "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "requires": { - "minimist": "^1.2.0" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "jszip": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz", - "integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==", - "dev": true, - "requires": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "set-immediate-shim": "~1.0.1" - } - }, - "karma": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/karma/-/karma-1.7.1.tgz", - "integrity": "sha512-k5pBjHDhmkdaUccnC7gE3mBzZjcxyxYsYVaqiL2G5AqlfLyBO5nw2VdNK+O16cveEPd/gIOWULH7gkiYYwVNHg==", - "dev": true, - "requires": { - "bluebird": "^3.3.0", - "body-parser": "^1.16.1", - "chokidar": "^1.4.1", - "colors": "^1.1.0", - "combine-lists": "^1.0.0", - "connect": "^3.6.0", - "core-js": "^2.2.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.0", - "expand-braces": "^0.1.1", - "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "http-proxy": "^1.13.0", - "isbinaryfile": "^3.0.0", - "lodash": "^3.8.0", - "log4js": "^0.6.31", - "mime": "^1.3.4", - "minimatch": "^3.0.2", - "optimist": "^0.6.1", - "qjobs": "^1.1.4", - "range-parser": "^1.2.0", - "rimraf": "^2.6.0", - "safe-buffer": "^5.0.1", - "socket.io": "1.7.3", - "source-map": "^0.5.3", - "tmp": "0.0.31", - "useragent": "^2.1.12" - }, - "dependencies": { - "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "dev": true, - "requires": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" - } - }, - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "^1.0.1" - } - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - } - }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "dev": true, - "requires": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "fsevents": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" - } - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "^0.1.0" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "dev": true, - "optional": true, - "requires": { - "nan": "^2.12.1" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "^2.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" - }, - "dependencies": { - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "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-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" - }, - "dependencies": { - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "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" - }, - "dependencies": { - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "^1.0.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" - } - }, - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "dev": true - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - }, - "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" - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "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" - } - } - } - }, - "tmp": { - "version": "0.0.31", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", - "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.1" - } - } - } - }, - "karma-chrome-launcher": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz", - "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==", - "dev": true, - "requires": { - "fs-access": "^1.0.0", - "which": "^1.2.1" - } - }, - "karma-coverage-istanbul-reporter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.0.0.tgz", - "integrity": "sha512-f9I5fro1Z3efBK1fhEmhb8xTQKiM5tlBSWTjJmdxR8ULy+oeI7fRpczCEaiWzHya0Zfz1/oBTrswEoZsEYXI6g==", - "dev": true, - "requires": { - "istanbul-api": "^1.3.1", - "minimatch": "^3.0.4" - } - }, - "karma-jasmine": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.1.tgz", - "integrity": "sha1-b+hA51oRYAydkehLM8RY4cRqNSk=", - "dev": true - }, - "karma-jasmine-html-reporter": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-0.2.2.tgz", - "integrity": "sha1-SKjl7xiAdhfuK14zwRlMNbQ5Ukw=", - "dev": true, - "requires": { - "karma-jasmine": "^1.0.2" - } - }, - "karma-source-map-support": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", - "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", - "dev": true, - "requires": { - "source-map-support": "^0.5.5" - } - }, - "ketcher-wrapper": { - "version": "file:dist/ketcher-wrapper/ketcher-wrapper-0.0.1.tgz", - "integrity": "sha512-XU6jCuYiFv0k4EUd/OX5RZ9DlyGgu+g6Vmyq7BWsUAusKBbjEym5QGDXt/+SANdqz/DLuBdiIYtYQnm1mXDrqg==", - "requires": { - "tslib": "^1.9.0" - } - }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dev": true, - "requires": { - "json-buffer": "3.0.0" - } - }, - "killable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", - "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" - }, - "latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", - "dev": true, - "requires": { - "package-json": "^6.3.0" - } - }, - "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" - } - }, - "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": { - "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 - }, - "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": "5.0.0", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-5.0.0.tgz", - "integrity": "sha512-bquCU89mO/yWLaUq0Clk7qCsKhsF/TZpJUzETRvJa9KSVEL9SO3ovCvdEHISBhrC81OwC8QSVX7E0bzElZj9cg==", - "dev": true, - "requires": { - "clone": "^2.1.1", - "loader-utils": "^1.1.0", - "pify": "^4.0.1" - } - }, - "less-plugin-npm-import": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/less-plugin-npm-import/-/less-plugin-npm-import-2.1.0.tgz", - "integrity": "sha1-gj5phskzGKmBccqFiEi2vq1Vvz4=", - "dev": true, - "requires": { - "promise": "~7.0.1", - "resolve": "~1.1.6" - }, - "dependencies": { - "promise": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.0.4.tgz", - "integrity": "sha1-Nj6EpMNsg1a4kP7WLJHOhdAu1Tk=", - "dev": true, - "requires": { - "asap": "~2.0.3" - } - }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } - } - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levenary": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", - "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", - "dev": true, - "requires": { - "leven": "^3.1.0" - } - }, - "license-webpack-plugin": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.1.2.tgz", - "integrity": "sha512-7poZHRla+ae0eEButlwMrPpkXyhNVBf2EHePYWT0jyLnI6311/OXJkTI2sOIRungRpQgU2oDMpro5bSFPT5F0A==", - "dev": true, - "requires": { - "@types/webpack-sources": "^0.1.5", - "webpack-sources": "^1.2.0" - } - }, - "lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "dev": true, - "requires": { - "immediate": "~3.0.5" - } - }, - "lightercollective": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lightercollective/-/lightercollective-0.1.0.tgz", - "integrity": "sha512-J9tg5uraYoQKaWbmrzDDexbG6hHnMcWS1qLYgJSWE+mpA3U5OCSeMUhb+K55otgZJ34oFdR0ECvdIb3xuO5JOQ==", - "dev": true - }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - }, - "dependencies": { - "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" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", - "dev": true - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" - }, - "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 - }, - "log4js": { - "version": "0.6.38", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-0.6.38.tgz", - "integrity": "sha1-LElBFmldb7JUgJQ9P8hy5mKlIv0=", - "dev": true, - "requires": { - "readable-stream": "~1.0.2", - "semver": "~4.3.3" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "semver": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", - "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", - "dev": true - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "loglevel": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz", - "integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==", - "dev": true - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true, - "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - }, - "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" - } - }, - "lucene-query-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/lucene-query-parser/-/lucene-query-parser-1.2.0.tgz", - "integrity": "sha1-RtrVtN3Fmrvyf530xRnZWcIDNDI=" - }, - "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": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "make-fetch-happen": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz", - "integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==", - "dev": true, - "requires": { - "agentkeepalive": "^3.4.1", - "cacache": "^12.0.0", - "http-cache-semantics": "^3.8.1", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "node-fetch-npm": "^2.0.2", - "promise-retry": "^1.1.1", - "socks-proxy-agent": "^4.0.0", - "ssri": "^6.0.0" - } - }, - "mamacro": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", - "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", - "dev": true - }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "requires": { - "object-visit": "^1.0.0" - } - }, - "math-random": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", - "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", - "dev": true - }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "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" - } - }, - "memory-fs": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", - "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", - "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, - "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": { - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - } - } - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true - }, - "merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "requires": { - "source-map": "^0.6.1" - }, - "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 - } - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "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==", - "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" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, - "mime": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", - "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==" - }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" - }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "requires": { - "mime-db": "1.44.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true - }, - "mini-css-extract-plugin": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.0.tgz", - "integrity": "sha512-MNpRGbNA52q6U92i0qbVpQNsgk7LExy41MdAlG84FeytfDOtRIf/mCHdEgG8rpTKOaNKiqUnZdlptF469hxqOw==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "normalize-url": "1.9.1", - "schema-utils": "^1.0.0", - "webpack-sources": "^1.1.0" - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "dev": true, - "requires": { - "minipass": "^2.9.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" - } - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "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==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } - } - }, - "moment": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz", - "integrity": "sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==" - }, - "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.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "nan": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", - "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", - "dev": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "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==" - }, - "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-multiselect-dropdown": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/ng-multiselect-dropdown/-/ng-multiselect-dropdown-0.2.10.tgz", - "integrity": "sha512-X7JlVgWMUmkabWxGNxlNmPmBi/dPAPTSc5lkBmeZbWBehL+yrfrHVAs2F27w5genI7u64CIR1AHMINy51/ueUg==", - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - } - } - }, - "ng-packagr": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-5.4.0.tgz", - "integrity": "sha512-IMS7RPmobKz7RR8UHvl6r0jV5TZlTv0a+vSeEEdFJCAMu0W2Bnh+iq8vH4RivHuxttc7ulhpMe526qFXMgGJOw==", - "dev": true, - "requires": { - "ajv": "^6.10.2", - "autoprefixer": "^9.6.0", - "browserslist": "^4.0.0", - "chalk": "^2.3.1", - "chokidar": "^3.0.0", - "clean-css": "^4.1.11", - "commander": "^2.12.0", - "fs-extra": "^8.0.0", - "glob": "^7.1.2", - "injection-js": "^2.2.1", - "less": "^3.8.0", - "less-plugin-npm-import": "^2.1.0", - "node-sass-tilde-importer": "^1.0.0", - "postcss": "^7.0.0", - "postcss-url": "^8.0.0", - "read-pkg-up": "^5.0.0", - "rimraf": "^2.6.1", - "rollup": "^1.12.1", - "rollup-plugin-commonjs": "^10.0.0", - "rollup-plugin-json": "^4.0.0", - "rollup-plugin-node-resolve": "^5.0.0", - "rollup-plugin-sourcemaps": "^0.4.2", - "rxjs": "^6.0.0", - "sass": "^1.17.3", - "stylus": "^0.54.5", - "terser": "^4.1.2", - "update-notifier": "^3.0.0" - } - }, - "ngx-json-viewer": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ngx-json-viewer/-/ngx-json-viewer-2.4.0.tgz", - "integrity": "sha512-26QmLp+0ds90aFug3KbSIwqtmQgCcJYFNNNcmcZHgPRj75nhKzbo4ceKxkhWmY5auKZClVO0HTZSs5bBhgb1Bw==" - }, - "ngx-moment": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/ngx-moment/-/ngx-moment-3.5.0.tgz", - "integrity": "sha512-QC/5XNC0BW6WkJkwZT4r2A29j/8sJAmhuQJrEnEdpW35GvkemccuxEUAwo/PwkzPB/CHaquR00E6P2HVEQ1iEg==", - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - } - } - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "nocache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz", - "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==" - }, - "node-fetch-npm": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz", - "integrity": "sha512-iOuIQDWDyjhv9qSDrj9aq/klt6F9z1p2otB3AV7v3zBDcL/x+OfGsvGQZZCcMZbUf4Ujw1xGNQkjvGnVT22cKg==", - "dev": true, - "requires": { - "encoding": "^0.1.11", - "json-parse-better-errors": "^1.0.0", - "safe-buffer": "^5.1.1" - } - }, - "node-forge": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", - "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==", - "dev": true - }, - "node-gyp": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", - "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", - "dev": true, - "requires": { - "fstream": "^1.0.0", - "glob": "^7.0.3", - "graceful-fs": "^4.1.2", - "mkdirp": "^0.5.0", - "nopt": "2 || 3", - "npmlog": "0 || 1 || 2 || 3 || 4", - "osenv": "0", - "request": "^2.87.0", - "rimraf": "2", - "semver": "~5.3.0", - "tar": "^2.0.0", - "which": "1" - }, - "dependencies": { - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "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, - "requires": { - "block-stream": "*", - "fstream": "^1.0.12", - "inherits": "2" - } - } - } - }, - "node-libs-browser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", - "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", - "dev": true, - "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.1", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "^1.0.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } - } - }, - "node-releases": { - "version": "1.1.58", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.58.tgz", - "integrity": "sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg==", - "dev": true - }, - "node-sass": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.1.tgz", - "integrity": "sha512-TTWFx+ZhyDx1Biiez2nB0L3YrCZ/8oHagaDalbuBSlqXgUPsdkUSzJsVxeDO9LtPB49+Fh3WQl3slABo6AotNw==", - "dev": true, - "requires": { - "async-foreach": "^0.1.3", - "chalk": "^1.1.1", - "cross-spawn": "^3.0.0", - "gaze": "^1.0.0", - "get-stdin": "^4.0.1", - "glob": "^7.0.3", - "in-publish": "^2.0.0", - "lodash": "^4.17.15", - "meow": "^3.7.0", - "mkdirp": "^0.5.1", - "nan": "^2.13.2", - "node-gyp": "^3.8.0", - "npmlog": "^4.0.0", - "request": "^2.88.0", - "sass-graph": "^2.2.4", - "stdout-stream": "^1.4.0", - "true-case-path": "^1.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "cross-spawn": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", - "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - }, - "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" - } - }, - "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 - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } - }, - "node-sass-tilde-importer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/node-sass-tilde-importer/-/node-sass-tilde-importer-1.0.2.tgz", - "integrity": "sha512-Swcmr38Y7uB78itQeBm3mThjxBy9/Ah/ykPIaURY/L6Nec9AyRoL/jJ7ECfMR+oZeCTVQNxVMu/aHU+TLRVbdg==", - "dev": true, - "requires": { - "find-parent-dir": "^0.3.0" - } - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "requires": { - "abbrev": "1" - } - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "normalize-url": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", - "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", - "dev": true, - "requires": { - "object-assign": "^4.0.1", - "prepend-http": "^1.0.0", - "query-string": "^4.1.0", - "sort-keys": "^1.0.0" - } - }, - "npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "dev": true, - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", - "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.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "dev": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-pick-manifest": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz", - "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1", - "npm-package-arg": "^6.0.0", - "semver": "^5.4.1" - } - }, - "npm-registry-fetch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.5.tgz", - "integrity": "sha512-yQ0/U4fYpCCqmueB2g8sc+89ckQ3eXpmU4+Yi2j5o/r0WkKvE2+Y0tK3DEILAtn2UaQTkjTHxIXe2/CSdit+/Q==", - "dev": true, - "requires": { - "JSONStream": "^1.3.4", - "bluebird": "^3.5.1", - "figgy-pudding": "^3.4.1", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^5.0.0", - "npm-package-arg": "^6.1.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "dev": true, - "requires": { - "boolbase": "~1.0.0" - } - }, - "null-check": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", - "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", - "dev": true - }, - "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=", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-component": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", - "dev": true - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "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=", - "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=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", - "dev": true - }, - "object-is": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz", - "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "requires": { - "isobject": "^3.0.0" - } - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true, - "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "requires": { - "isobject": "^3.0.1" - } - }, - "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==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "open": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", - "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", - "dev": true, - "requires": { - "is-wsl": "^1.1.0" - } - }, - "opencollective-postinstall": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", - "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", - "dev": true - }, - "opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", - "dev": true, - "requires": { - "is-wsl": "^1.1.0" - } - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - }, - "dependencies": { - "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true - } - } - }, - "options": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", - "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=", - "dev": true - }, - "original": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", - "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", - "dev": true, - "requires": { - "url-parse": "^1.4.3" - } - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "dev": true - }, - "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": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true - }, - "p-retry": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", - "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", - "dev": true, - "requires": { - "retry": "^0.12.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "dev": true, - "requires": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "pacote": { - "version": "9.5.5", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.5.5.tgz", - "integrity": "sha512-jAEP+Nqj4kyMWyNpfTU/Whx1jA7jEc5cCOlurm0/0oL+v8TAp1QSsK83N7bYe+2bEdFzMAtPG5TBebjzzGV0cA==", - "dev": true, - "requires": { - "bluebird": "^3.5.3", - "cacache": "^12.0.2", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.1.0", - "glob": "^7.1.3", - "infer-owner": "^1.0.4", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^5.0.0", - "minimatch": "^3.0.4", - "minipass": "^2.3.5", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "normalize-package-data": "^2.4.0", - "npm-package-arg": "^6.1.0", - "npm-packlist": "^1.1.12", - "npm-pick-manifest": "^2.2.3", - "npm-registry-fetch": "^4.0.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.8", - "unique-filename": "^1.1.1", - "which": "^1.3.1" - }, - "dependencies": { - "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" - } - } - } - }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "parallel-transform": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", - "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", - "dev": true, - "requires": { - "cyclist": "^1.0.1", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - }, - "dependencies": { - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - } - } - }, - "parse-asn1": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", - "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", - "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-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true, - "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" - }, - "dependencies": { - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - } - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true - }, - "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "optional": true - }, - "parsejson": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz", - "integrity": "sha1-q343WfIJ7OmUN5c/fQ8fZK4OZKs=", - "dev": true, - "requires": { - "better-assert": "~1.0.0" - } - }, - "parseqs": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "dev": true, - "requires": { - "better-assert": "~1.0.0" - } - }, - "parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "dev": true, - "requires": { - "better-assert": "~1.0.0" - } - }, - "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=" - }, - "path-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", - "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "pbkdf2": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", - "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", - "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" - } - }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, - "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" - } - }, - "please-upgrade-node": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", - "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", - "dev": true, - "requires": { - "semver-compare": "^1.0.0" - } - }, - "portfinder": { - "version": "1.0.26", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.26.tgz", - "integrity": "sha512-Xi7mKxJHHMI3rIUrnm/jjUgwhbYMkp/XKEcZX3aG4BrumLpq3nmoQMX+ClYnDZnZ/New7IatC1no5RX0zo1vXQ==", - "dev": true, - "requires": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.1" - }, - "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.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "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=" - }, - "postcss": { - "version": "7.0.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.17.tgz", - "integrity": "sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "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" - }, - "dependencies": { - "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 - } - } - }, - "postcss-load-config": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz", - "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==", - "dev": true, - "requires": { - "cosmiconfig": "^5.0.0", - "import-cwd": "^2.0.0" - } - }, - "postcss-loader": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", - "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "postcss": "^7.0.0", - "postcss-load-config": "^2.0.0", - "schema-utils": "^1.0.0" - } - }, - "postcss-url": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-8.0.0.tgz", - "integrity": "sha512-E2cbOQ5aii2zNHh8F6fk1cxls7QVFZjLPSrqvmiza8OuXLzIpErij8BDS5Y3STPfJgpIMNCPEr8JlKQWEoozUw==", - "dev": true, - "requires": { - "mime": "^2.3.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.0", - "postcss": "^7.0.2", - "xxhashjs": "^0.2.1" - } - }, - "postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", - "dev": true - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true - }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" - }, - "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" - }, - "dependencies": { - "retry": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", - "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", - "dev": true - } - } - }, - "protoduck": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz", - "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", - "dev": true, - "requires": { - "genfun": "^5.0.0" - } - }, - "protractor": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.0.tgz", - "integrity": "sha512-6TSYqMhUUzxr4/wN0ttSISqPMKvcVRXF4k8jOEpGWD8OioLak4KLgfzHK9FJ49IrjzRrZ+Mx1q2Op8Rk0zEcnQ==", - "dev": true, - "requires": { - "@types/node": "^6.0.46", - "@types/q": "^0.0.32", - "@types/selenium-webdriver": "^3.0.0", - "blocking-proxy": "^1.0.0", - "browserstack": "^1.5.1", - "chalk": "^1.1.3", - "glob": "^7.0.3", - "jasmine": "2.8.0", - "jasminewd2": "^2.1.0", - "optimist": "~0.6.0", - "q": "1.4.1", - "saucelabs": "^1.5.0", - "selenium-webdriver": "3.6.0", - "source-map-support": "~0.4.0", - "webdriver-js-extender": "2.0.0", - "webdriver-manager": "^12.0.6" - }, - "dependencies": { - "@types/node": { - "version": "6.14.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.10.tgz", - "integrity": "sha512-pF4HjZGSog75kGq7B1InK/wt/N08BuPATo+7HRfv7gZUzccebwv/fmWVGs/j6LvSiLWpCuGGhql51M/wcQsNzA==", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "dev": true, - "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" - } - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "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" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "source-map": "^0.5.6" - } - }, - "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 - }, - "webdriver-manager": { - "version": "12.1.7", - "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.7.tgz", - "integrity": "sha512-XINj6b8CYuUYC93SG3xPkxlyUc3IJbD6Vvo75CVGuG9uzsefDzWQrhz0Lq8vbPxtb4d63CZdYophF8k8Or/YiA==", - "dev": true, - "requires": { - "adm-zip": "^0.4.9", - "chalk": "^1.1.1", - "del": "^2.2.0", - "glob": "^7.0.3", - "ini": "^1.3.4", - "minimist": "^1.2.0", - "q": "^1.4.1", - "request": "^2.87.0", - "rimraf": "^2.5.2", - "semver": "^5.3.0", - "xml2js": "^0.4.17" - } - } - } - }, - "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "dev": true, - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - } - }, - "proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "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" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - }, - "dependencies": { - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "puppeteer": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.20.0.tgz", - "integrity": "sha512-bt48RDBy2eIwZPrkgbcwHtb51mj2nKvHOPMaSH2IsWiv7lOG9k9zhaRzpDZafrk05ajMc3cu+lSQYYOfH2DkVQ==", - "requires": { - "debug": "^4.1.0", - "extract-zip": "^1.6.6", - "https-proxy-agent": "^2.2.1", - "mime": "^2.0.3", - "progress": "^2.0.1", - "proxy-from-env": "^1.0.0", - "rimraf": "^2.6.1", - "ws": "^6.1.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "extract-zip": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", - "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", - "requires": { - "concat-stream": "^1.6.2", - "debug": "^2.6.9", - "mkdirp": "^0.5.4", - "yauzl": "^2.10.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", - "dev": true - }, - "qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", - "dev": true - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "dev": true, - "requires": { - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - } - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, - "querystringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", - "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", - "dev": true - }, - "randomatic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", - "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", - "dev": true, - "requires": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - } - } - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "dev": true, - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "dependencies": { - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "dev": true - } - } - }, - "raw-loader": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-3.1.0.tgz", - "integrity": "sha512-lzUVMuJ06HF4rYveaz9Tv0WRlUMxJ0Y1hgSkkgg+50iEdaI0TthyEDe08KIHb0XsF6rn8WYTqPCaGTZg3sX+qA==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "schema-utils": "^2.0.1" - }, - "dependencies": { - "ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - } - } - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "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-package-json": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.1.tgz", - "integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==", - "dev": true, - "requires": { - "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "json-parse-better-errors": "^1.0.1", - "normalize-package-data": "^2.0.0", - "npm-normalize-package-bin": "^1.0.0" - } - }, - "read-package-tree": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz", - "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==", - "dev": true, - "requires": { - "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0", - "util-promisify": "^2.1.0" - } - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "parse-json": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", - "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1", - "lines-and-columns": "^1.1.6" - } - }, - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-5.0.0.tgz", - "integrity": "sha512-XBQjqOBtTzyol2CpsQOw8LHV0XbDZVG7xMMjmXAJomlVY03WOBRmYgDJETlvcg0H63AJvPRwT7GFi5rvOzUOKg==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^5.0.0" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "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" - } - }, - "readdir-scoped-modules": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", - "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", - "dev": true, - "requires": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" - } - }, - "readdirp": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", - "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true, - "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" - } - }, - "referrer-policy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.2.0.tgz", - "integrity": "sha512-LgQJIuS6nAy1Jd88DCQRemyE3mS+ispwlqMk3b0yjZ257fI1v9c+/p6SD5gP5FGyXUIgrNOAfmyioHwZtYv2VA==" - }, - "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==" - }, - "regenerate": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", - "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", - "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", - "dev": true, - "requires": { - "regenerate": "^1.4.0" - } - }, - "regenerator-runtime": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", - "dev": true - }, - "regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", - "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.8.4" - } - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "dev": true, - "requires": { - "is-equal-shallow": "^0.1.3" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "regexp.prototype.flags": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", - "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, - "regexpu-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", - "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", - "dev": true, - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" - } - }, - "registry-auth-token": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.1.1.tgz", - "integrity": "sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA==", - "dev": true, - "requires": { - "rc": "^1.2.8" - } - }, - "registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "dev": true, - "requires": { - "rc": "^1.2.8" - } - }, - "regjsgen": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", - "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", - "dev": true - }, - "regjsparser": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", - "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "^1.0.0" - } - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "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.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" - } - }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - } - }, - "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=" - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dev": true, - "requires": { - "lowercase-keys": "^1.0.0" - } - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" - }, - "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", - "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" - } - }, - "rollup": { - "version": "1.32.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.32.1.tgz", - "integrity": "sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A==", - "dev": true, - "requires": { - "@types/estree": "*", - "@types/node": "*", - "acorn": "^7.1.0" - }, - "dependencies": { - "acorn": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", - "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", - "dev": true - } - } - }, - "rollup-plugin-commonjs": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz", - "integrity": "sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q==", - "dev": true, - "requires": { - "estree-walker": "^0.6.1", - "is-reference": "^1.1.2", - "magic-string": "^0.25.2", - "resolve": "^1.11.0", - "rollup-pluginutils": "^2.8.1" - } - }, - "rollup-plugin-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-json/-/rollup-plugin-json-4.0.0.tgz", - "integrity": "sha512-hgb8N7Cgfw5SZAkb3jf0QXii6QX/FOkiIq2M7BAQIEydjHvTyxXHQiIzZaTFgx1GK0cRCHOCBHIyEkkLdWKxow==", - "dev": true, - "requires": { - "rollup-pluginutils": "^2.5.0" - } - }, - "rollup-plugin-node-resolve": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz", - "integrity": "sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw==", - "dev": true, - "requires": { - "@types/resolve": "0.0.8", - "builtin-modules": "^3.1.0", - "is-module": "^1.0.0", - "resolve": "^1.11.1", - "rollup-pluginutils": "^2.8.1" - } - }, - "rollup-plugin-sourcemaps": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.4.2.tgz", - "integrity": "sha1-YhJaqUCHqt97g+9N+vYptHMTXoc=", - "dev": true, - "requires": { - "rollup-pluginutils": "^2.0.1", - "source-map-resolve": "^0.5.0" - } - }, - "rollup-pluginutils": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", - "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", - "dev": true, - "requires": { - "estree-walker": "^0.6.1" - } - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "dev": true, - "requires": { - "aproba": "^1.1.1" - } - }, - "rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - } - } - }, - "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=", - "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": { - "version": "1.22.9", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.22.9.tgz", - "integrity": "sha512-FzU1X2V8DlnqabrL4u7OBwD2vcOzNMongEJEx3xMEhWY/v26FFR3aG0hyeu2T965sfR0E9ufJwmG+Qjz78vFPQ==", - "dev": true, - "requires": { - "chokidar": ">=2.0.0 <4.0.0" - } - }, - "sass-graph": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.6.tgz", - "integrity": "sha512-MKuEYXFSGuRSi8FZ3A7imN1CeVn9Gpw0/SFJKdL1ejXJneI9a5rwlEZrKejhEFAA3O6yr3eIyl/WuvASvlT36g==", - "dev": true, - "requires": { - "glob": "^7.0.0", - "lodash": "^4.0.0", - "scss-tokenizer": "^0.2.3", - "yargs": "^7.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 - }, - "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" - } - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "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 - }, - "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" - } - }, - "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" - } - }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, - "requires": { - "lcid": "^1.0.0" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - } - }, - "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" - } - }, - "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 - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yargs": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.1.tgz", - "integrity": "sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g==", - "dev": 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-security.0" - } - }, - "yargs-parser": { - "version": "5.0.0-security.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz", - "integrity": "sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ==", - "dev": true, - "requires": { - "camelcase": "^3.0.0", - "object.assign": "^4.1.0" - } - } - } - }, - "sass-loader": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.2.0.tgz", - "integrity": "sha512-h8yUWaWtsbuIiOCgR9fd9c2lRXZ2uG+h8Dzg/AGNj+Hg/3TO8+BBAW9mEP+mh8ei+qBKqSJ0F1FLlYjNBc61OA==", - "dev": true, - "requires": { - "clone-deep": "^4.0.1", - "loader-utils": "^1.0.1", - "neo-async": "^2.5.0", - "pify": "^4.0.1", - "semver": "^5.5.0" - } - }, - "saucelabs": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", - "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", - "dev": true, - "requires": { - "https-proxy-agent": "^2.2.1" - } - }, - "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, - "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, - "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 - }, - "selenium-webdriver": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", - "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", - "dev": true, - "requires": { - "jszip": "^3.1.3", - "rimraf": "^2.5.4", - "tmp": "0.0.30", - "xml2js": "^0.4.17" - }, - "dependencies": { - "tmp": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", - "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.1" - } - } - } - }, - "selfsigned": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz", - "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==", - "dev": true, - "requires": { - "node-forge": "0.9.0" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", - "dev": true - }, - "semver-diff": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", - "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", - "dev": true, - "requires": { - "semver": "^5.0.3" - } - }, - "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" - } - }, - "semver-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", - "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", - "dev": true - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "serialize-javascript": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", - "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", - "dev": true - }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "dependencies": { - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "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" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "dev": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "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==", - "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=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", - "dev": true - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "requires": { - "kind-of": "^6.0.2" - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "shell-quote": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", - "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", - "dev": true - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, - "smart-buffer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", - "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", - "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==", - "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=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "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==", - "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=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "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=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "socket.io": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-1.7.3.tgz", - "integrity": "sha1-uK+cq6AJSeVo42nxMn6pvp6iRhs=", - "dev": true, - "requires": { - "debug": "2.3.3", - "engine.io": "1.8.3", - "has-binary": "0.1.7", - "object-assign": "4.1.0", - "socket.io-adapter": "0.5.0", - "socket.io-client": "1.7.3", - "socket.io-parser": "2.3.1" - }, - "dependencies": { - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - }, - "object-assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", - "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=", - "dev": true - } - } - }, - "socket.io-adapter": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz", - "integrity": "sha1-y21LuL7IHhB4uZZ3+c7QBGBmu4s=", - "dev": true, - "requires": { - "debug": "2.3.3", - "socket.io-parser": "2.3.1" - }, - "dependencies": { - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - } - } - }, - "socket.io-client": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.3.tgz", - "integrity": "sha1-sw6GqhDV7zVGYBwJzeR2Xjgdo3c=", - "dev": true, - "requires": { - "backo2": "1.0.2", - "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "2.3.3", - "engine.io-client": "1.8.3", - "has-binary": "0.1.7", - "indexof": "0.0.1", - "object-component": "0.0.3", - "parseuri": "0.0.5", - "socket.io-parser": "2.3.1", - "to-array": "0.1.4" - }, - "dependencies": { - "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 - }, - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - } - } - }, - "socket.io-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz", - "integrity": "sha1-3VMgJRA85Clpcya+/WQAX8/ltKA=", - "dev": true, - "requires": { - "component-emitter": "1.1.2", - "debug": "2.2.0", - "isarray": "0.0.1", - "json3": "3.3.2" - }, - "dependencies": { - "component-emitter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", - "integrity": "sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM=", - "dev": true - }, - "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "dev": true, - "requires": { - "ms": "0.7.1" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "json3": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", - "dev": true - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true - } - } - }, - "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.4.0", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", - "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", - "dev": true, - "requires": { - "debug": "^3.2.5", - "eventsource": "^1.0.7", - "faye-websocket": "~0.11.1", - "inherits": "^2.0.3", - "json3": "^3.3.2", - "url-parse": "^1.4.3" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "faye-websocket": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", - "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "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 - } - } - }, - "socks": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz", - "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==", - "dev": true, - "requires": { - "ip": "1.1.5", - "smart-buffer": "^4.1.0" - } - }, - "socks-proxy-agent": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz", - "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==", - "dev": true, - "requires": { - "agent-base": "~4.2.1", - "socks": "~2.3.2" - }, - "dependencies": { - "agent-base": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", - "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - } - } - }, - "sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", - "dev": true, - "requires": { - "is-plain-obj": "^1.0.0" - } - }, - "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.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - }, - "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.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "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=" - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", - "dev": true - }, - "spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "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.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "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.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "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==", - "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, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "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, - "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.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", - "dev": true - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", - "dev": true - }, - "string-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": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "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=", - "dev": true, - "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, - "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, - "requires": { - "get-stdin": "^4.0.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "style-loader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.0.0.tgz", - "integrity": "sha512-B0dOCFwv7/eY31a5PCieNwMgMhVGFe9w+rh7s/Bx8kfFkrth9zfTZquoYvdw8URgiqxObQKcpW51Ugz1HjfdZw==", - "dev": true, - "requires": { - "loader-utils": "^1.2.3", - "schema-utils": "^2.0.1" - }, - "dependencies": { - "ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - } - } - } - }, - "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" - } - }, - "subarg": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", - "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", - "dev": true, - "requires": { - "minimist": "^1.1.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "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==" - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "dev": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "term-size": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", - "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", - "dev": true, - "requires": { - "execa": "^0.7.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "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" - } - }, - "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 - }, - "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" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } - }, - "terser": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.3.tgz", - "integrity": "sha512-Lw+ieAXmY69d09IIc/yqeBqXpEQIpDGZqT34ui1QWXIUpR2RjbqEkT8X7Lgex19hslSqcWM5iMN2kM11eMsESQ==", - "dev": true, - "requires": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - }, - "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 - } - } - }, - "terser-webpack-plugin": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", - "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", - "dev": true, - "requires": { - "cacache": "^12.0.2", - "find-cache-dir": "^2.1.0", - "is-wsl": "^1.1.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^2.1.2", - "source-map": "^0.6.1", - "terser": "^4.1.2", - "webpack-sources": "^1.4.0", - "worker-farm": "^1.7.0" - }, - "dependencies": { - "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" - } - }, - "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 - } - } - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true - }, - "timers-browserify": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", - "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", - "dev": true, - "requires": { - "setimmediate": "^1.0.4" - } - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "to-array": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", - "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", - "dev": true - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "dev": true - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "dev": true - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true - }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true - }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, - "true-case-path": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", - "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", - "dev": true, - "requires": { - "glob": "^7.1.2" - } - }, - "ts-loader": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-4.5.0.tgz", - "integrity": "sha512-ihgVaSmgrX4crGV4n7yuoHPoCHbDzj9aepCZR9TgIx4SgJ9gdnB6xLHgUBb7bsFM/f0K6x9iXa65KY/Fu1Klkw==", - "requires": { - "chalk": "^2.3.0", - "enhanced-resolve": "^4.0.0", - "loader-utils": "^1.0.2", - "micromatch": "^3.1.4", - "semver": "^5.0.1" - } - }, - "ts-node": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-5.0.1.tgz", - "integrity": "sha512-XK7QmDcNHVmZkVtkiwNDWiERRHPyU8nBqZB1+iv2UhOG0q3RQ9HsZ2CMqISlFbxjrYFGfG2mX7bW4dAyxBVzUw==", - "dev": true, - "requires": { - "arrify": "^1.0.0", - "chalk": "^2.3.0", - "diff": "^3.1.0", - "make-error": "^1.1.1", - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "source-map-support": "^0.5.3", - "yn": "^2.0.0" - } - }, - "tsickle": { - "version": "0.37.0", - "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.37.0.tgz", - "integrity": "sha512-ufUZqLUNqh+kOfr52N/hJ5JbiDO32/CO7ZCteZBX9HA2kiejwEgDaJeJe1GAj2TIu683IgTA/LPKvlns6Liw0w==", - "dev": true, - "requires": { - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "source-map": "^0.7.3" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } - } - }, - "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" - }, - "tslint": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz", - "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^4.0.1", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.8.0", - "tsutils": "^2.29.0" - }, - "dependencies": { - "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 - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - } - } - }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", - "dev": true - }, - "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=" - }, - "typescript": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", - "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==", - "dev": true - }, - "uglify-js": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.0.tgz", - "integrity": "sha512-Esj5HG5WAyrLIdYU74Z3JdG2PxdIusvj6IWHMtlyESxc7kcDz7zYlYjpnSokn1UbpV0d/QX9fan7gkCNd/9BQA==", - "dev": true, - "optional": true - }, - "ultron": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", - "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=", - "dev": true - }, - "unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", - "dev": true - }, - "unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", - "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", - "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", - "dev": true - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "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.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4" - } - }, - "unique-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", - "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", - "dev": true, - "requires": { - "crypto-random-string": "^1.0.0" - } - }, - "universal-analytics": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/universal-analytics/-/universal-analytics-0.4.23.tgz", - "integrity": "sha512-lgMIH7XBI6OgYn1woDEmxhGdj8yDefMKg7GkWdeATAlQZFrMrNyxSkpDzY57iY0/6fdlzTbBV03OawvvzG+q7A==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "request": "^2.88.2", - "uuid": "^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.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" - } - } - }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true - }, - "update-notifier": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-3.0.1.tgz", - "integrity": "sha512-grrmrB6Zb8DUiyDIaeRTBCkgISYUgETNe7NglEbVsrLWXeESnlCSP50WfRSj/GmzMPl6Uchj24S/p80nP/ZQrQ==", - "dev": true, - "requires": { - "boxen": "^3.0.0", - "chalk": "^2.0.1", - "configstore": "^4.0.0", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.1.0", - "is-npm": "^3.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" - } - }, - "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=" - }, - "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" - } - }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dev": true, - "requires": { - "prepend-http": "^2.0.0" - }, - "dependencies": { - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true - } - } - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" - }, - "useragent": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", - "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", - "dev": true, - "requires": { - "lru-cache": "4.1.x", - "tmp": "0.0.x" - }, - "dependencies": { - "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" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } - }, - "util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "dev": true, - "requires": { - "inherits": "2.0.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "util-promisify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz", - "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=", - "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "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.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - }, - "v8-compile-cache": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", - "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", - "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=" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "vm-browserify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", - "dev": true - }, - "void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", - "dev": true - }, - "watchpack": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.2.tgz", - "integrity": "sha512-ymVbbQP40MFTp+cNMvpyBpBtygHnPzPkHqoIwRRj/0B8KhqQwV8LaKjtbaxF2lK4vl8zN9wCxS46IFCU5K4W0g==", - "dev": true, - "requires": { - "chokidar": "^3.4.0", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0", - "watchpack-chokidar2": "^2.0.0" - } - }, - "watchpack-chokidar2": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz", - "integrity": "sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==", - "dev": true, - "optional": true, - "requires": { - "chokidar": "^2.1.8" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "optional": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "optional": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true, - "optional": true - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "optional": 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" - } - }, - "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "dev": true, - "optional": true, - "requires": { - "nan": "^2.12.1" - } - }, - "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, - "optional": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "optional": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - } - } - } - }, - "wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "requires": { - "minimalistic-assert": "^1.0.0" - } - }, - "web-animations-js": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/web-animations-js/-/web-animations-js-2.3.2.tgz", - "integrity": "sha512-TOMFWtQdxzjWp8qx4DAraTWTsdhxVSiWa6NkPFSaPtZ1diKUxTn4yTix73A1euG1WbSOMMPcY51cnjTIHrGtDA==" - }, - "webdriver-js-extender": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.0.0.tgz", - "integrity": "sha512-fbyKiVu3azzIc5d4+26YfuPQcFTlgFQV5yQ/0OQj4Ybkl4g1YQuIPskf5v5wqwRJhHJnPHthB6tqCjWHOKLWag==", - "dev": true, - "requires": { - "@types/selenium-webdriver": "^3.0.0", - "selenium-webdriver": "^3.0.1" - } - }, - "webpack": { - "version": "4.39.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.39.2.tgz", - "integrity": "sha512-AKgTfz3xPSsEibH00JfZ9sHXGUwIQ6eZ9tLN8+VLzachk1Cw2LVmy+4R7ZiwTa9cZZ15tzySjeMui/UnSCAZhA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/wasm-edit": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "acorn": "^6.2.1", - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^4.1.0", - "eslint-scope": "^4.0.3", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.4.0", - "loader-utils": "^1.2.3", - "memory-fs": "^0.4.1", - "micromatch": "^3.1.10", - "mkdirp": "^0.5.1", - "neo-async": "^2.6.1", - "node-libs-browser": "^2.2.1", - "schema-utils": "^1.0.0", - "tapable": "^1.1.3", - "terser-webpack-plugin": "^1.4.1", - "watchpack": "^1.6.0", - "webpack-sources": "^1.4.1" - }, - "dependencies": { - "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" - } - } - } - }, - "webpack-cli": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.2.1.tgz", - "integrity": "sha512-jeJveHwz/vwpJ3B8bxEL5a/rVKIpRNJDsKggfKnxuYeohNDW4Y/wB9N/XHJA093qZyS0r6mYL+/crLsIol4WKA==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "enhanced-resolve": "^4.1.0", - "findup-sync": "^2.0.0", - "global-modules": "^1.0.0", - "global-modules-path": "^2.3.0", - "import-local": "^2.0.0", - "interpret": "^1.1.0", - "lightercollective": "^0.1.0", - "loader-utils": "^1.1.0", - "supports-color": "^5.5.0", - "v8-compile-cache": "^2.0.2", - "yargs": "^12.0.4" - } - }, - "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.7.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", - "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", - "dev": true, - "requires": { - "memory-fs": "^0.4.1", - "mime": "^2.4.4", - "mkdirp": "^0.5.1", - "range-parser": "^1.2.1", - "webpack-log": "^2.0.0" - }, - "dependencies": { - "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" - } - } - } - }, - "webpack-dev-server": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.9.0.tgz", - "integrity": "sha512-E6uQ4kRrTX9URN9s/lIbqTAztwEPdvzVrcmHE8EQ9YnuT9J8Es5Wrd8n9BKg1a0oZ5EgEke/EQFgUsp18dSTBw==", - "dev": true, - "requires": { - "ansi-html": "0.0.7", - "bonjour": "^3.5.0", - "chokidar": "^2.1.8", - "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", - "debug": "^4.1.1", - "del": "^4.1.1", - "express": "^4.17.1", - "html-entities": "^1.2.1", - "http-proxy-middleware": "0.19.1", - "import-local": "^2.0.0", - "internal-ip": "^4.3.0", - "ip": "^1.1.5", - "is-absolute-url": "^3.0.3", - "killable": "^1.0.1", - "loglevel": "^1.6.4", - "opn": "^5.5.0", - "p-retry": "^3.0.1", - "portfinder": "^1.0.25", - "schema-utils": "^1.0.0", - "selfsigned": "^1.10.7", - "semver": "^6.3.0", - "serve-index": "^1.9.1", - "sockjs": "0.3.19", - "sockjs-client": "1.4.0", - "spdy": "^4.0.1", - "strip-ansi": "^3.0.1", - "supports-color": "^6.1.0", - "url": "^0.11.0", - "webpack-dev-middleware": "^3.7.2", - "webpack-log": "^2.0.0", - "ws": "^6.2.1", - "yargs": "12.0.5" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dev": true, - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "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" - }, - "dependencies": { - "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 - } - } - }, - "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "dev": true, - "optional": true, - "requires": { - "nan": "^2.12.1" - } - }, - "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" - } - }, - "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" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "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" - } - } - } - }, - "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.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - }, - "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.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, - "requires": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true - }, - "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": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "which-pm-runs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", - "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", - "dev": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "widest-line": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", - "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", - "dev": true, - "requires": { - "string-width": "^2.1.1" - } - }, - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true - }, - "worker-farm": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", - "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", - "dev": true, - "requires": { - "errno": "~0.1.7" - } - }, - "worker-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/worker-plugin/-/worker-plugin-3.2.0.tgz", - "integrity": "sha512-W5nRkw7+HlbsEt3qRP6MczwDDISjiRj2GYt9+bpe8A2La00TmJdwzG5bpdMXhRt1qcWmwAvl1TiKaHRa+XDS9Q==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, - "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "requires": { - "async-limiter": "~1.0.0" - } - }, - "wtf-8": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wtf-8/-/wtf-8-1.0.0.tgz", - "integrity": "sha1-OS2LotDxw00e4tYw8V0O+2jhBIo=", - "dev": true - }, - "x-xss-protection": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.3.0.tgz", - "integrity": "sha512-kpyBI9TlVipZO4diReZMAHWtS0MMa/7Kgx8hwG/EuZLiA6sg4Ah/4TRdASHhRRN3boobzcYgFRUFSgHRge6Qhg==" - }, - "xdg-basedir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", - "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", - "dev": true - }, - "xhr2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.1.4.tgz", - "integrity": "sha1-f4dliEdxbbUCYyOBL4GMras4el8=" - }, - "xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "dev": true, - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "dependencies": { - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - } - } - }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "dev": true - }, - "xmlhttprequest-ssl": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz", - "integrity": "sha1-GFqIjATspGw+QHDZn3tJ3jUomS0=", - "dev": true - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - }, - "xxhashjs": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", - "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", - "dev": true, - "requires": { - "cuint": "^0.2.2" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "yaml": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", - "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", - "dev": true - }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "requires": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, - "yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", - "dev": 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.9.1", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.9.1.tgz", - "integrity": "sha512-GkPiJL8jifSrKReKaTZ5jkhrMEgXbXYC+IPo1iquBjayRa0q86w3Dipjn8b415jpitMExe9lV8iTsv8tk3DGag==" - } - } -} - diff --git a/package.dev.json b/package.dev.json index 585f2aef5..e44eca575 100644 --- a/package.dev.json +++ b/package.dev.json @@ -3,10 +3,10 @@ "version": "0.0.0", "scripts": { "ng": "ng", - "test-script": "mkdirp src/app/core/assets/dojo && cpx \"lib/dojo-custom-jsdraw/dojo/**/*\" src/app/core/assets/dojo -u", - "copy-libs-src": "mkdirp src/app/core/assets/dojo && cpx \"lib/dojo-custom-jsdraw/dojo/**/*\" src/app/core/assets/dojo -u && mkdirp src/app/core/assets/dojox && cpx \"lib/dojo-custom-jsdraw/dojox/**/*\" src/app/core/assets/dojox -u && mkdirp src/app/core/assets/dijit && cpx \"lib/dojo-custom-jsdraw/dijit/**/*\" src/app/core/assets/dijit -u", + "test-script": "mkdirp src/app/core/assets/dojo && ccpx-fixedpx \"lib/dojo-custom-jsdraw/dojo/**/*\" src/app/core/assets/dojo -u", + "copy-libs-src": "mkdirp src/app/core/assets/dojo && cpx-fixed \"lib/dojo-custom-jsdraw/dojo/**/*\" src/app/core/assets/dojo -u && mkdirp src/app/core/assets/dojox && cpx-fixed \"lib/dojo-custom-jsdraw/dojox/**/*\" src/app/core/assets/dojox -u && mkdirp src/app/core/assets/dijit && cpx-fixed \"lib/dojo-custom-jsdraw/dijit/**/*\" src/app/core/assets/dijit -u", "clear-libs-src": "rimraf src/app/core/assets/dojo && rimraf src/app/core/assets/dojox && rimraf src/app/core/assets/dijit", - "copy-libs-dist": "mkdirp dist/browser/assets/dojo && cpx \"lib/dojo-custom-jsdraw/dojo/**/*\" dist/browser/assets/dojo -u && mkdirp dist/browser/assets/dojox && cpx \"lib/dojo-custom-jsdraw/dojox/**/*\" dist/browser/assets/dojox -u && mkdirp dist/browser/assets/dijit && cpx \"lib/dojo-custom-jsdraw/dijit/**/*\" dist/browser/assets/dijit -u", + "copy-libs-dist": "mkdirp dist/browser/assets/dojo && cpx-fixed \"lib/dojo-custom-jsdraw/dojo/**/*\" dist/browser/assets/dojo -u && mkdirp dist/browser/assets/dojox && cpx-fixed \"lib/dojo-custom-jsdraw/dojox/**/*\" dist/browser/assets/dojox -u && mkdirp dist/browser/assets/dijit && cpx-fixed \"lib/dojo-custom-jsdraw/dijit/**/*\" dist/browser/assets/dijit -u", "process-dojo": "cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run clear-libs-src && npm run copy-libs-src", "build-file-select": "ng build --prod file-select && cd dist/file-select && npm pack", "build-jsdraw-wrapper": "ng build --prod jsdraw-wrapper && cd dist/jsdraw-wrapper && npm pack", @@ -14,15 +14,15 @@ "build-libraries": "npm run build-file-select && npm run build-jsdraw-wrapper && npm run build-ketcher-wrapper", "build-server": "ng run gsrs-client:server && npm run webpack:server", "start:fda:dev": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=fda.dev --sourceMap=true", - "start:fda:local": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=fda.local --sourceMap=true", + "start:fda:local": "npm run build-libraries && npm run process-dojo && ng serve --configuration=fda.local", "start:fda:pre-prod": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=fda.pre-prod --sourceMap=true", "start:fda:prod": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=fda.prod --sourceMap=true", "start:gsrs:dev": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=gsrs.dev --sourceMap=true", - "start:gsrs:local": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=gsrs.local --sourceMap=true", + "start:gsrs:local": "npm run build-libraries && npm run process-dojo && ng serve --configuration=gsrs.local", "start:gsrs:pre-prod": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=gsrs.pre-prod --sourceMap=true", "start:gsrs:prod": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=gsrs.prod --sourceMap=true", "build:fda:pre-prod": "npm run build-libraries && npm run clear-libs-src && ng build --base-href=/ginas/app/beta/ --aot=true --configuration=fda.pre-prod --sourceMap=true && node process-index.js && cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run copy-libs-dist && npm run build-server", - "build:fda:prod": "npm run build-libraries && npm run clear-libs-src && ng build --base-href=/ginas/app/beta/ --aot=true --configuration=fda.prod --sourceMap=true && node process-index.js && cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run copy-libs-dist && npm run build-server", + "build:fda:prod": "npm run build-libraries && npm run clear-libs-src && ng build --base-href=/ginas/app/beta/ --configuration=fda.prod && node process-index.js && cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run copy-libs-dist && npm run build-server", "build:gsrs:pre-prod": "npm run build-libraries && npm run clear-libs-src && ng build --base-href=/ginas/app/beta/ --aot=true --configuration=gsrs.pre-prod --sourceMap=true && node process-index.js && cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run copy-libs-dist && npm run build-server", "build:gsrs:prod": "npm run build-libraries && npm run clear-libs-src && ng build --base-href=/ginas/app/beta/ --aot=true --configuration=gsrs.prod --sourceMap=true && node process-index.js && cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run copy-libs-dist && npm run build-server", "build:cbg:prod": "npm run build-libraries && npm run clear-libs-src && ng build --base-href=/gsrs/app/beta/ --aot=true --configuration=cbg.prod --sourceMap=true && node process-index.js && cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run copy-libs-dist && npm run build-server", @@ -31,24 +31,23 @@ "tslint": "tslint", "e2e": "ng e2e", "serve:ssr": "node dist/server", - "webpack:server": "webpack --config webpack.server.config.js --progress --colors", + "webpack:server": "webpack --config webpack.server.config.js --progress", "precommit": "ng lint" }, "private": true, "dependencies": { - "@angular/animations": "8.2.14", - "@angular/cdk": "8.2.3", - "@angular/common": "8.2.14", - "@angular/compiler": "8.2.14", - "@angular/core": "8.2.14", - "@angular/forms": "8.2.14", - "@angular/material": "8.2.3", - "@angular/platform-browser": "8.2.14", - "@angular/platform-browser-dynamic": "8.2.14", - "@angular/platform-server": "8.2.14", - "@angular/router": "8.2.14", - "@nguniversal/express-engine": "8.2.6", - "@nguniversal/module-map-ngfactory-loader": "8.2.6", + "@angular/animations": "13.2.1", + "@angular/cdk": "13.2.1", + "@angular/common": "13.2.1", + "@angular/compiler": "13.2.1", + "@angular/core": "13.2.1", + "@angular/forms": "13.2.1", + "@angular/material": "13.2.1", + "@angular/platform-browser": "13.2.1", + "@angular/platform-browser-dynamic": "13.2.1", + "@angular/platform-server": "13.2.1", + "@angular/router": "13.2.1", + "@nguniversal/express-engine": "13.0.2", "@types/hammerjs": "2.0.36", "classlist.js": "1.1.20150312", "compression": "1.7.3", @@ -57,51 +56,69 @@ "defiant.js": "2.2.6", "hammerjs": "2.0.8", "helmet": "3.21.2", - "lodash": "4.17.15", + "lodash": "4.17.21", "lucene-query-parser": "1.2.0", - "moment": "2.26.0", + "moment": "2.29.2", "ng-multiselect-dropdown": "0.2.10", "ngx-json-viewer": "2.4.0", "ngx-moment": "3.5.0", + "primeng": "^13.2.0", "reflect-metadata": "0.1.13", - "rxjs": "6.5.4", + "rxjs": "7.5.4", "ts-loader": "4.5.0", + "util": "0.12.4", "web-animations-js": "2.3.2", - "zone.js": "0.9.1" + "zone.js": "0.11.4", + "z-schema": "5.0.4", + "jexl": "2.3.0" + + + }, "devDependencies": { - "@angular-devkit/build-angular": "0.803.24", - "@angular-devkit/build-ng-packagr": "0.803.22", - "@angular/cli": "8.3.22", - "@angular/compiler-cli": "8.2.14", - "@angular/language-service": "8.2.14", - "@types/jasmine": "2.8.6", - "@types/jasminewd2": "2.0.3", - "@types/lodash": "4.14.116", - "@types/node": "8.9.4", - "cheerio": "1.0.0-rc.2", - "codelyzer": "5.0.1", - "cpx": "1.5.0", - "extract-zip": "1.6.7", - "husky": "4.2.1", - "jasmine-core": "2.99.1", - "jasmine-spec-reporter": "4.2.1", - "karma": "1.7.1", - "karma-chrome-launcher": "2.2.0", - "karma-coverage-istanbul-reporter": "2.0.0", - "karma-jasmine": "1.1.1", - "karma-jasmine-html-reporter": "0.2.2", - "mkdirp": "0.5.1", - "ng-packagr": "5.4.0", - "node-sass": "4.13.1", - "protractor": "5.4.0", - "rimraf": "2.6.3", - "ts-node": "5.0.1", - "tsickle": "0.37.0", - "tslib": "1.10.0", + "@angular-devkit/build-angular": "13.2.1", + "@angular-eslint/builder": "13.0.1", + "@angular-eslint/eslint-plugin": "13.0.1", + "@angular-eslint/eslint-plugin-template": "13.0.1", + "@angular-eslint/schematics": "13.0.1", + "@angular-eslint/template-parser": "13.0.1", + "@angular/cli": "13.2.1", + "@angular/compiler-cli": "13.2.1", + "@angular/language-service": "13.2.1", + "@types/estree": "0.0.51", + "@types/jasmine": "3.10.3", + "@types/jasminewd2": "2.0.10", + "@types/lodash": "4.14.178", + "@types/node": "17.0.16", + "@typescript-eslint/eslint-plugin": "5.11.0", + "@typescript-eslint/parser": "5.11.0", + "cheerio": "^1.0.0-rc.2", + "codelyzer": "6.0.2", + "cpx-fixed": "1.6.0", + "eslint": "8.8.0", + "eslint-plugin-import": "latest", + "eslint-plugin-jsdoc": "latest", + "eslint-plugin-prefer-arrow": "latest", + "extract-zip": "2.0.1", + "husky": "7.0.4", + "jasmine-core": "4.0.0", + "jasmine-spec-reporter": "7.0.0", + "karma": "6.3.15", + "karma-chrome-launcher": "3.1.0", + "karma-coverage-istanbul-reporter": "3.0.3", + "karma-jasmine": "4.0.1", + "karma-jasmine-html-reporter": "1.7.0", + "mkdirp": "1.0.4", + "ng-packagr": "13.2.1", + "node-sass": "7.0.1", + "protractor": "7.0.0", + "raw-loader": "4.0.2", + "rimraf": "3.0.2", + "ts-node": "10.5.0", + "tslib": "2.3.1", "tslint": "5.20.1", - "typescript": "3.5.3", - "webpack-cli": "3.2.1" + "typescript": "4.5.5", + "webpack-cli": "4.9.2" }, "husky": { "hooks": { diff --git a/package.json b/package.json index 14d36d31d..01eafe93b 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,10 @@ "version": "0.0.0", "scripts": { "ng": "ng", - "test-script": "mkdirp src/app/core/assets/dojo && cpx \"lib/dojo-custom-jsdraw/dojo/**/*\" src/app/core/assets/dojo -u", - "copy-libs-src": "mkdirp src/app/core/assets/dojo && cpx \"lib/dojo-custom-jsdraw/dojo/**/*\" src/app/core/assets/dojo -u && mkdirp src/app/core/assets/dojox && cpx \"lib/dojo-custom-jsdraw/dojox/**/*\" src/app/core/assets/dojox -u && mkdirp src/app/core/assets/dijit && cpx \"lib/dojo-custom-jsdraw/dijit/**/*\" src/app/core/assets/dijit -u", + "test-script": "mkdirp src/app/core/assets/dojo && cpx-fixed \"lib/dojo-custom-jsdraw/dojo/**/*\" src/app/core/assets/dojo -u", + "copy-libs-src": "mkdirp src/app/core/assets/dojo && cpx-fixed \"lib/dojo-custom-jsdraw/dojo/**/*\" src/app/core/assets/dojo -u && mkdirp src/app/core/assets/dojox && cpx-fixed \"lib/dojo-custom-jsdraw/dojox/**/*\" src/app/core/assets/dojox -u && mkdirp src/app/core/assets/dijit && cpx-fixed \"lib/dojo-custom-jsdraw/dijit/**/*\" src/app/core/assets/dijit -u", "clear-libs-src": "rimraf src/app/core/assets/dojo && rimraf src/app/core/assets/dojox && rimraf src/app/core/assets/dijit", - "copy-libs-dist": "mkdirp dist/browser/assets/dojo && cpx \"lib/dojo-custom-jsdraw/dojo/**/*\" dist/browser/assets/dojo -u && mkdirp dist/browser/assets/dojox && cpx \"lib/dojo-custom-jsdraw/dojox/**/*\" dist/browser/assets/dojox -u && mkdirp dist/browser/assets/dijit && cpx \"lib/dojo-custom-jsdraw/dijit/**/*\" dist/browser/assets/dijit -u", + "copy-libs-dist": "mkdirp dist/browser/assets/dojo && cpx-fixed \"lib/dojo-custom-jsdraw/dojo/**/*\" dist/browser/assets/dojo -u && mkdirp dist/browser/assets/dojox && cpx-fixed \"lib/dojo-custom-jsdraw/dojox/**/*\" dist/browser/assets/dojox -u && mkdirp dist/browser/assets/dijit && cpx-fixed \"lib/dojo-custom-jsdraw/dijit/**/*\" dist/browser/assets/dijit -u", "process-dojo": "cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run clear-libs-src && npm run copy-libs-src", "build-file-select": "ng build --prod file-select && cd dist/file-select && npm pack", "build-jsdraw-wrapper": "ng build --prod jsdraw-wrapper && cd dist/jsdraw-wrapper && npm pack", @@ -14,15 +14,15 @@ "build-libraries": "npm run build-file-select && npm run build-jsdraw-wrapper && npm run build-ketcher-wrapper", "build-server": "ng run gsrs-client:server && npm run webpack:server", "start:fda:dev": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=fda.dev --sourceMap=true", - "start:fda:local": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=fda.local --sourceMap=true", + "start:fda:local": "npm run build-libraries && npm run process-dojo && ng serve --configuration=fda.local", "start:fda:pre-prod": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=fda.pre-prod --sourceMap=true", "start:fda:prod": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=fda.prod --sourceMap=true", "start:gsrs:dev": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=gsrs.dev --sourceMap=true", - "start:gsrs:local": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=gsrs.local --sourceMap=true", + "start:gsrs:local": "npm run build-libraries && npm run process-dojo && ng serve --configuration=gsrs.local", "start:gsrs:pre-prod": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=gsrs.pre-prod --sourceMap=true", "start:gsrs:prod": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=gsrs.prod --sourceMap=true", "build:fda:pre-prod": "npm run build-libraries && npm run clear-libs-src && ng build --base-href=/ginas/app/beta/ --aot=true --configuration=fda.pre-prod --sourceMap=true && node process-index.js && cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run copy-libs-dist && npm run build-server", - "build:fda:prod": "npm run build-libraries && npm run clear-libs-src && ng build --base-href=/ginas/app/beta/ --aot=true --configuration=fda.prod --sourceMap=true && node process-index.js && cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run copy-libs-dist && npm run build-server", + "build:fda:prod": "npm run build-libraries && npm run clear-libs-src && ng build --base-href=/ginas/app/beta/ --configuration=fda.prod && node process-index.js && cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run copy-libs-dist && npm run build-server", "build:gsrs:pre-prod": "npm run build-libraries && npm run clear-libs-src && ng build --base-href=/ginas/app/beta/ --aot=true --configuration=gsrs.pre-prod --sourceMap=true && node process-index.js && cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run copy-libs-dist && npm run build-server", "build:gsrs:prod": "npm run build-libraries && npm run clear-libs-src && ng build --base-href=/ginas/app/beta/ --aot=true --configuration=gsrs.prod --sourceMap=true && node process-index.js && cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run copy-libs-dist && npm run build-server", "build:cbg:prod": "npm run build-libraries && npm run clear-libs-src && ng build --base-href=/gsrs/app/beta/ --aot=true --configuration=cbg.prod --sourceMap=true && node process-index.js && cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run copy-libs-dist && npm run build-server", @@ -31,24 +31,23 @@ "tslint": "tslint", "e2e": "ng e2e", "serve:ssr": "node dist/server", - "webpack:server": "webpack --config webpack.server.config.js --progress --colors", + "webpack:server": "webpack --config webpack.server.config.js --progress", "precommit": "ng lint" }, "private": true, "dependencies": { - "@angular/animations": "8.2.14", - "@angular/cdk": "8.2.3", - "@angular/common": "8.2.14", - "@angular/compiler": "8.2.14", - "@angular/core": "8.2.14", - "@angular/forms": "8.2.14", - "@angular/material": "8.2.3", - "@angular/platform-browser": "8.2.14", - "@angular/platform-browser-dynamic": "8.2.14", - "@angular/platform-server": "8.2.14", - "@angular/router": "8.2.14", - "@nguniversal/express-engine": "8.2.6", - "@nguniversal/module-map-ngfactory-loader": "8.2.6", + "@angular/animations": "13.2.1", + "@angular/cdk": "13.2.1", + "@angular/common": "13.2.1", + "@angular/compiler": "13.2.1", + "@angular/core": "13.2.1", + "@angular/forms": "13.2.1", + "@angular/material": "13.2.1", + "@angular/platform-browser": "13.2.1", + "@angular/platform-browser-dynamic": "13.2.1", + "@angular/platform-server": "13.2.1", + "@angular/router": "13.2.1", + "@nguniversal/express-engine": "13.0.2", "@types/hammerjs": "2.0.36", "classlist.js": "1.1.20150312", "compression": "1.7.3", @@ -58,53 +57,71 @@ "file-select": "file:dist/file-select/file-select-0.0.1.tgz", "hammerjs": "2.0.8", "helmet": "3.21.2", + "jexl": "2.3.0", "jsdraw-wrapper": "file:dist/jsdraw-wrapper/jsdraw-wrapper-0.0.1.tgz", "ketcher-wrapper": "file:dist/ketcher-wrapper/ketcher-wrapper-0.0.1.tgz", - "lodash": "4.17.15", + "lodash": "4.17.21", "lucene-query-parser": "1.2.0", - "moment": "2.26.0", + "moment": "2.29.4", "ng-multiselect-dropdown": "0.2.10", "ngx-json-viewer": "2.4.0", "ngx-moment": "3.5.0", + "ngx-schema-form": "2.7.0", + "primeng": "^13.2.0", "reflect-metadata": "0.1.13", - "rxjs": "6.5.4", + "rxjs": "7.5.4", "ts-loader": "4.5.0", + "util": "0.12.4", "web-animations-js": "2.3.2", - "zone.js": "0.9.1" + "webpack": "^5.74.0", + "webpack-sources": "^3.2.3", + "z-schema": "4.2.2", + "zone.js": "0.11.4" }, "devDependencies": { - "@angular-devkit/build-angular": "0.803.24", - "@angular-devkit/build-ng-packagr": "0.803.22", - "@angular/cli": "8.3.22", - "@angular/compiler-cli": "8.2.14", - "@angular/language-service": "8.2.14", - "@types/jasmine": "2.8.6", - "@types/jasminewd2": "2.0.3", - "@types/lodash": "4.14.116", - "@types/node": "8.9.4", - "cheerio": "1.0.0-rc.2", - "codelyzer": "5.0.1", - "cpx": "1.5.0", - "extract-zip": "1.6.7", - "husky": "4.2.1", - "jasmine-core": "2.99.1", - "jasmine-spec-reporter": "4.2.1", - "karma": "1.7.1", - "karma-chrome-launcher": "2.2.0", - "karma-coverage-istanbul-reporter": "2.0.0", - "karma-jasmine": "1.1.1", - "karma-jasmine-html-reporter": "0.2.2", - "mkdirp": "0.5.1", - "ng-packagr": "5.4.0", - "node-sass": "4.13.1", - "protractor": "5.4.0", - "rimraf": "2.6.3", - "ts-node": "5.0.1", - "tsickle": "0.37.0", - "tslib": "1.10.0", + "@angular-devkit/build-angular": "13.2.1", + "@angular-eslint/builder": "13.0.1", + "@angular-eslint/eslint-plugin": "13.0.1", + "@angular-eslint/eslint-plugin-template": "13.0.1", + "@angular-eslint/schematics": "13.0.1", + "@angular-eslint/template-parser": "13.0.1", + "@angular/cli": "13.2.1", + "@angular/compiler-cli": "13.2.1", + "@angular/language-service": "13.2.1", + "@types/estree": "0.0.51", + "@types/jasmine": "3.10.3", + "@types/jasminewd2": "2.0.10", + "@types/lodash": "4.14.178", + "@types/node": "17.0.16", + "@typescript-eslint/eslint-plugin": "5.11.0", + "@typescript-eslint/parser": "5.11.0", + "cheerio": "^1.0.0-rc.2", + "codelyzer": "6.0.2", + "cpx-fixed": "1.6.0", + "eslint": "8.8.0", + "eslint-plugin-import": "latest", + "eslint-plugin-jsdoc": "latest", + "eslint-plugin-prefer-arrow": "latest", + "extract-zip": "2.0.1", + "husky": "7.0.4", + "jasmine-core": "4.0.0", + "jasmine-spec-reporter": "7.0.0", + "karma": "6.4.2", + "karma-chrome-launcher": "3.1.0", + "karma-coverage-istanbul-reporter": "3.0.3", + "karma-jasmine": "4.0.1", + "karma-jasmine-html-reporter": "1.7.0", + "mkdirp": "1.0.4", + "ng-packagr": "13.2.1", + "node-sass": "7.0.1", + "protractor": "7.0.0", + "raw-loader": "4.0.2", + "rimraf": "3.0.2", + "ts-node": "10.5.0", + "tslib": "2.3.1", "tslint": "5.20.1", - "typescript": "3.5.3", - "webpack-cli": "3.2.1" + "typescript": "4.5.5", + "webpack-cli": "4.9.2" }, "husky": { "hooks": { @@ -112,4 +129,3 @@ } } } - diff --git a/package.real.json b/package.real.json index 4864c8752..58766d0c9 100644 --- a/package.real.json +++ b/package.real.json @@ -3,10 +3,10 @@ "version": "0.0.0", "scripts": { "ng": "ng", - "test-script": "mkdirp src/app/core/assets/dojo && cpx \"lib/dojo-custom-jsdraw/dojo/**/*\" src/app/core/assets/dojo -u", - "copy-libs-src": "mkdirp src/app/core/assets/dojo && cpx \"lib/dojo-custom-jsdraw/dojo/**/*\" src/app/core/assets/dojo -u && mkdirp src/app/core/assets/dojox && cpx \"lib/dojo-custom-jsdraw/dojox/**/*\" src/app/core/assets/dojox -u && mkdirp src/app/core/assets/dijit && cpx \"lib/dojo-custom-jsdraw/dijit/**/*\" src/app/core/assets/dijit -u", + "test-script": "mkdirp src/app/core/assets/dojo && cpx-fixed \"lib/dojo-custom-jsdraw/dojo/**/*\" src/app/core/assets/dojo -u", + "copy-libs-src": "mkdirp src/app/core/assets/dojo && cpx-fixed \"lib/dojo-custom-jsdraw/dojo/**/*\" src/app/core/assets/dojo -u && mkdirp src/app/core/assets/dojox && cpx-fixed \"lib/dojo-custom-jsdraw/dojox/**/*\" src/app/core/assets/dojox -u && mkdirp src/app/core/assets/dijit && cpx-fixed \"lib/dojo-custom-jsdraw/dijit/**/*\" src/app/core/assets/dijit -u", "clear-libs-src": "rimraf src/app/core/assets/dojo && rimraf src/app/core/assets/dojox && rimraf src/app/core/assets/dijit", - "copy-libs-dist": "mkdirp dist/browser/assets/dojo && cpx \"lib/dojo-custom-jsdraw/dojo/**/*\" dist/browser/assets/dojo -u && mkdirp dist/browser/assets/dojox && cpx \"lib/dojo-custom-jsdraw/dojox/**/*\" dist/browser/assets/dojox -u && mkdirp dist/browser/assets/dijit && cpx \"lib/dojo-custom-jsdraw/dijit/**/*\" dist/browser/assets/dijit -u", + "copy-libs-dist": "mkdirp dist/browser/assets/dojo && cpx-fixed \"lib/dojo-custom-jsdraw/dojo/**/*\" dist/browser/assets/dojo -u && mkdirp dist/browser/assets/dojox && cpx-fixed \"lib/dojo-custom-jsdraw/dojox/**/*\" dist/browser/assets/dojox -u && mkdirp dist/browser/assets/dijit && cpx-fixed \"lib/dojo-custom-jsdraw/dijit/**/*\" dist/browser/assets/dijit -u", "process-dojo": "cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run clear-libs-src && npm run copy-libs-src", "build-file-select": "ng build --prod file-select && cd dist/file-select && npm pack", "build-jsdraw-wrapper": "ng build --prod jsdraw-wrapper && cd dist/jsdraw-wrapper && npm pack", @@ -14,15 +14,15 @@ "build-libraries": "npm run build-file-select && npm run build-jsdraw-wrapper && npm run build-ketcher-wrapper", "build-server": "ng run gsrs-client:server && npm run webpack:server", "start:fda:dev": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=fda.dev --sourceMap=true", - "start:fda:local": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=fda.local --sourceMap=true", + "start:fda:local": "npm run build-libraries && npm run process-dojo && ng serve --configuration=fda.local", "start:fda:pre-prod": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=fda.pre-prod --sourceMap=true", "start:fda:prod": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=fda.prod --sourceMap=true", "start:gsrs:dev": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=gsrs.dev --sourceMap=true", - "start:gsrs:local": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=gsrs.local --sourceMap=true", + "start:gsrs:local": "npm run build-libraries && npm run process-dojo && ng serve --configuration=gsrs.local", "start:gsrs:pre-prod": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=gsrs.pre-prod --sourceMap=true", "start:gsrs:prod": "npm run build-libraries && npm run process-dojo && ng serve --aot=true --configuration=gsrs.prod --sourceMap=true", "build:fda:pre-prod": "npm run build-libraries && npm run clear-libs-src && ng build --base-href=/ginas/app/beta/ --aot=true --configuration=fda.pre-prod --sourceMap=true && node process-index.js && cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run copy-libs-dist && npm run build-server", - "build:fda:prod": "npm run build-libraries && npm run clear-libs-src && ng build --base-href=/ginas/app/beta/ --aot=true --configuration=fda.prod --sourceMap=true && node process-index.js && cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run copy-libs-dist && npm run build-server", + "build:fda:prod": "npm run build-libraries && npm run clear-libs-src && ng build --base-href=/ginas/app/beta/ --configuration=fda.prod && node process-index.js && cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run copy-libs-dist && npm run build-server", "build:gsrs:pre-prod": "npm run build-libraries && npm run clear-libs-src && ng build --base-href=/ginas/app/beta/ --aot=true --configuration=gsrs.pre-prod --sourceMap=true && node process-index.js && cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run copy-libs-dist && npm run build-server", "build:gsrs:prod": "npm run build-libraries && npm run clear-libs-src && ng build --base-href=/ginas/app/beta/ --aot=true --configuration=gsrs.prod --sourceMap=true && node process-index.js && cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run copy-libs-dist && npm run build-server", "build:cbg:prod": "npm run build-libraries && npm run clear-libs-src && ng build --base-href=/gsrs/app/beta/ --aot=true --configuration=cbg.prod --sourceMap=true && node process-index.js && cd lib && extract-zip dojo-custom-jsdraw.zip && cd .. && npm run copy-libs-dist && npm run build-server", @@ -31,24 +31,23 @@ "tslint": "tslint", "e2e": "ng e2e", "serve:ssr": "node dist/server", - "webpack:server": "webpack --config webpack.server.config.js --progress --colors", + "webpack:server": "webpack --config webpack.server.config.js --progress", "precommit": "ng lint" }, "private": true, "dependencies": { - "@angular/animations": "8.2.14", - "@angular/cdk": "8.2.3", - "@angular/common": "8.2.14", - "@angular/compiler": "8.2.14", - "@angular/core": "8.2.14", - "@angular/forms": "8.2.14", - "@angular/material": "8.2.3", - "@angular/platform-browser": "8.2.14", - "@angular/platform-browser-dynamic": "8.2.14", - "@angular/platform-server": "8.2.14", - "@angular/router": "8.2.14", - "@nguniversal/express-engine": "8.2.6", - "@nguniversal/module-map-ngfactory-loader": "8.2.6", + "@angular/animations": "13.2.1", + "@angular/cdk": "13.2.1", + "@angular/common": "13.2.1", + "@angular/compiler": "13.2.1", + "@angular/core": "13.2.1", + "@angular/forms": "13.2.1", + "@angular/material": "13.2.1", + "@angular/platform-browser": "13.2.1", + "@angular/platform-browser-dynamic": "13.2.1", + "@angular/platform-server": "13.2.1", + "@angular/router": "13.2.1", + "@nguniversal/express-engine": "13.0.2", "@types/hammerjs": "2.0.36", "classlist.js": "1.1.20150312", "compression": "1.7.3", @@ -60,51 +59,67 @@ "helmet": "3.21.2", "jsdraw-wrapper": "file:dist/jsdraw-wrapper/jsdraw-wrapper-0.0.1.tgz", "ketcher-wrapper": "file:dist/ketcher-wrapper/ketcher-wrapper-0.0.1.tgz", - "lodash": "4.17.15", + "lodash": "4.17.21", "lucene-query-parser": "1.2.0", - "moment": "2.26.0", + "moment": "2.29.2", "ng-multiselect-dropdown": "0.2.10", "ngx-json-viewer": "2.4.0", "ngx-moment": "3.5.0", + "primeng": "^13.2.0", "reflect-metadata": "0.1.13", - "rxjs": "6.5.4", + "rxjs": "7.5.4", "ts-loader": "4.5.0", + "util": "0.12.4", "web-animations-js": "2.3.2", - "zone.js": "0.9.1" + "zone.js": "0.11.4", + "z-schema": "4.2.2", + "jexl": "2.3.0", + "ngx-schema-form": "2.7.0" }, "devDependencies": { - "@angular-devkit/build-angular": "0.803.24", - "@angular-devkit/build-ng-packagr": "0.803.22", - "@angular/cli": "8.3.22", - "@angular/compiler-cli": "8.2.14", - "@angular/language-service": "8.2.14", - "@types/jasmine": "2.8.6", - "@types/jasminewd2": "2.0.3", - "@types/lodash": "4.14.116", - "@types/node": "8.9.4", - "cheerio": "1.0.0-rc.2", - "codelyzer": "5.0.1", - "cpx": "1.5.0", - "extract-zip": "1.6.7", - "husky": "4.2.1", - "jasmine-core": "2.99.1", - "jasmine-spec-reporter": "4.2.1", - "karma": "1.7.1", - "karma-chrome-launcher": "2.2.0", - "karma-coverage-istanbul-reporter": "2.0.0", - "karma-jasmine": "1.1.1", - "karma-jasmine-html-reporter": "0.2.2", - "mkdirp": "0.5.1", - "ng-packagr": "5.4.0", - "node-sass": "4.13.1", - "protractor": "5.4.0", - "rimraf": "2.6.3", - "ts-node": "5.0.1", - "tsickle": "0.37.0", - "tslib": "1.10.0", + "@angular-devkit/build-angular": "13.2.1", + "@angular-eslint/builder": "13.0.1", + "@angular-eslint/eslint-plugin": "13.0.1", + "@angular-eslint/eslint-plugin-template": "13.0.1", + "@angular-eslint/schematics": "13.0.1", + "@angular-eslint/template-parser": "13.0.1", + "@angular/cli": "13.2.1", + "@angular/compiler-cli": "13.2.1", + "@angular/language-service": "13.2.1", + "@types/estree": "0.0.51", + "@types/jasmine": "3.10.3", + "@types/jasminewd2": "2.0.10", + "@types/lodash": "4.14.178", + "@types/node": "17.0.16", + "@typescript-eslint/eslint-plugin": "5.11.0", + "@typescript-eslint/parser": "5.11.0", + "cheerio": "^1.0.0-rc.2", + "codelyzer": "6.0.2", + "cpx-fixed": "1.6.0", + "eslint": "8.8.0", + "eslint-plugin-import": "latest", + "eslint-plugin-jsdoc": "latest", + "eslint-plugin-prefer-arrow": "latest", + "extract-zip": "2.0.1", + "husky": "7.0.4", + "jasmine-core": "4.0.0", + "jasmine-spec-reporter": "7.0.0", + "karma": "6.3.15", + "karma-chrome-launcher": "3.1.0", + "karma-coverage-istanbul-reporter": "3.0.3", + "karma-jasmine": "4.0.1", + "karma-jasmine-html-reporter": "1.7.0", + "mkdirp": "1.0.4", + "ng-packagr": "13.2.1", + "node-sass": "7.0.1", + "protractor": "7.0.0", + "raw-loader": "4.0.2", + "rimraf": "3.0.2", + "ts-node": "10.5.0", + "tslib": "2.3.1", "tslint": "5.20.1", - "typescript": "3.5.3", - "webpack-cli": "3.2.1" + "typescript": "4.5.5", + "webpack-cli": "4.9.2" }, "husky": { "hooks": { diff --git a/projects/file-select/.eslintrc.json b/projects/file-select/.eslintrc.json new file mode 100644 index 000000000..d4b994224 --- /dev/null +++ b/projects/file-select/.eslintrc.json @@ -0,0 +1,59 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": [ + "!**/*" + ], + "overrides": [ + { + "files": [ + "*.ts" + ], + "parserOptions": { + "project": [ + "projects/file-select/tsconfig.lib.json", + "projects/file-select/tsconfig.spec.json" + ], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "ncats", + "style": "kebab-case" + } + ], + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "ncats", + "style": "camelCase" + } + ], + "@typescript-eslint/consistent-type-definitions": "error", + "@typescript-eslint/dot-notation": "off", + "@typescript-eslint/explicit-member-accessibility": [ + "off", + { + "accessibility": "explicit" + } + ], + "brace-style": [ + "error", + "1tbs" + ], + "id-blacklist": "off", + "id-match": "off", + "no-underscore-dangle": "off" + } + }, + { + "files": [ + "*.html" + ], + "rules": {} + } + ] +} diff --git a/projects/file-select/package.json b/projects/file-select/package.json index 0dd389685..58160fdb2 100644 --- a/projects/file-select/package.json +++ b/projects/file-select/package.json @@ -2,7 +2,7 @@ "name": "file-select", "version": "0.0.1", "peerDependencies": { - "@angular/common": "^6.0.0-rc.0 || ^6.0.0", - "@angular/core": "^6.0.0-rc.0 || ^6.0.0" + "@angular/common": "^13.2.1-rc.0 || ^13.2.1", + "@angular/core": "^13.2.1-rc.0 || ^13.2.1" } } diff --git a/projects/file-select/tslint.json b/projects/file-select/tslint.json deleted file mode 100644 index 36f82781f..000000000 --- a/projects/file-select/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "ncats", - "camelCase" - ], - "component-selector": [ - true, - "element", - "ncats", - "kebab-case" - ] - } -} diff --git a/projects/jsdraw-wrapper/.eslintrc.json b/projects/jsdraw-wrapper/.eslintrc.json new file mode 100644 index 000000000..809c3b59b --- /dev/null +++ b/projects/jsdraw-wrapper/.eslintrc.json @@ -0,0 +1,59 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": [ + "!**/*" + ], + "overrides": [ + { + "files": [ + "*.ts" + ], + "parserOptions": { + "project": [ + "projects/jsdraw-wrapper/tsconfig.lib.json", + "projects/jsdraw-wrapper/tsconfig.spec.json" + ], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "ncats", + "style": "kebab-case" + } + ], + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "ncats", + "style": "camelCase" + } + ], + "@typescript-eslint/consistent-type-definitions": "error", + "@typescript-eslint/dot-notation": "off", + "@typescript-eslint/explicit-member-accessibility": [ + "off", + { + "accessibility": "explicit" + } + ], + "brace-style": [ + "error", + "1tbs" + ], + "id-blacklist": "off", + "id-match": "off", + "no-underscore-dangle": "off" + } + }, + { + "files": [ + "*.html" + ], + "rules": {} + } + ] +} diff --git a/projects/jsdraw-wrapper/package.json b/projects/jsdraw-wrapper/package.json index 7dfcc437d..05cd8576c 100644 --- a/projects/jsdraw-wrapper/package.json +++ b/projects/jsdraw-wrapper/package.json @@ -2,7 +2,7 @@ "name": "jsdraw-wrapper", "version": "0.0.1", "peerDependencies": { - "@angular/common": "^6.0.0-rc.0 || ^6.0.0", - "@angular/core": "^6.0.0-rc.0 || ^6.0.0" + "@angular/common": "^13.2.1-rc.0 || ^13.2.1", + "@angular/core": "^13.2.1-rc.0 || ^13.2.1" } } diff --git a/projects/jsdraw-wrapper/src/lib/jsdraw-wrapper.component.ts b/projects/jsdraw-wrapper/src/lib/jsdraw-wrapper.component.ts index 6c032d999..a68d4950f 100644 --- a/projects/jsdraw-wrapper/src/lib/jsdraw-wrapper.component.ts +++ b/projects/jsdraw-wrapper/src/lib/jsdraw-wrapper.component.ts @@ -1,6 +1,6 @@ import { Component, AfterViewInit, Output, EventEmitter, PLATFORM_ID, Inject } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; -import { JSDraw } from './jsdraw'; +import { JSDraw } from './jsdraw.model'; @Component({ selector: 'ncats-jsdraw-wrapper', @@ -24,10 +24,15 @@ export class JsdrawWrapperComponent implements AfterViewInit { let count = 0; if (isPlatformBrowser(this.platformId)) { - if (window['JSDraw'] && window['dojo']) { + //this will ensure that the extra resources file has been loaded before the full editor is + if (window['JSDraw'] && window['dojo'] && window['scil'] && window['scil'].Utils && window['scil'].Utils._loadedAdditions) { window['dojo'].ready(() => { this.jsdraw = new window['JSDraw'](this.randomId); this.jsDrawOnLoad.emit(this.jsdraw); + //customization of buttons + if(window['afterSketcherMade']){ + window['afterSketcherMade'](); + } }); } else if (count < 5000) { count++; diff --git a/projects/jsdraw-wrapper/src/lib/jsdraw.d.ts b/projects/jsdraw-wrapper/src/lib/jsdraw.d.ts index 4fde2fab0..01522d446 100644 --- a/projects/jsdraw-wrapper/src/lib/jsdraw.d.ts +++ b/projects/jsdraw-wrapper/src/lib/jsdraw.d.ts @@ -1,3 +1,6 @@ +import { Injectable } from "@angular/core"; + +@Injectable() export class JSDraw { constructor(elementId: string); } diff --git a/projects/jsdraw-wrapper/tslint.json b/projects/jsdraw-wrapper/tslint.json deleted file mode 100644 index 36f82781f..000000000 --- a/projects/jsdraw-wrapper/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "ncats", - "camelCase" - ], - "component-selector": [ - true, - "element", - "ncats", - "kebab-case" - ] - } -} diff --git a/projects/ketcher-wrapper/.eslintrc.json b/projects/ketcher-wrapper/.eslintrc.json new file mode 100644 index 000000000..7956ba48e --- /dev/null +++ b/projects/ketcher-wrapper/.eslintrc.json @@ -0,0 +1,59 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": [ + "!**/*" + ], + "overrides": [ + { + "files": [ + "*.ts" + ], + "parserOptions": { + "project": [ + "projects/ketcher-wrapper/tsconfig.lib.json", + "projects/ketcher-wrapper/tsconfig.spec.json" + ], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "ncats", + "style": "kebab-case" + } + ], + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "ncats", + "style": "camelCase" + } + ], + "@typescript-eslint/consistent-type-definitions": "error", + "@typescript-eslint/dot-notation": "off", + "@typescript-eslint/explicit-member-accessibility": [ + "off", + { + "accessibility": "explicit" + } + ], + "brace-style": [ + "error", + "1tbs" + ], + "id-blacklist": "off", + "id-match": "off", + "no-underscore-dangle": "off" + } + }, + { + "files": [ + "*.html" + ], + "rules": {} + } + ] +} diff --git a/projects/ketcher-wrapper/package.json b/projects/ketcher-wrapper/package.json index b2e0fc60d..893db4983 100644 --- a/projects/ketcher-wrapper/package.json +++ b/projects/ketcher-wrapper/package.json @@ -2,7 +2,7 @@ "name": "ketcher-wrapper", "version": "0.0.1", "peerDependencies": { - "@angular/common": "^6.0.0-rc.0 || ^6.0.0", - "@angular/core": "^6.0.0-rc.0 || ^6.0.0" + "@angular/common": "^13.2.1-rc.0 || ^13.2.1", + "@angular/core": "^13.2.1-rc.0 || ^13.2.1" } } diff --git a/projects/ketcher-wrapper/tslint.json b/projects/ketcher-wrapper/tslint.json deleted file mode 100644 index 36f82781f..000000000 --- a/projects/ketcher-wrapper/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "ncats", - "camelCase" - ], - "component-selector": [ - true, - "element", - "ncats", - "kebab-case" - ] - } -} diff --git a/server.ts b/server.ts index ec4be2ca2..928a34f28 100644 --- a/server.ts +++ b/server.ts @@ -20,12 +20,13 @@ const PORT = process.env.PORT || 4000; const DIST_FOLDER = join(process.cwd(), 'dist'); // * NOTE :: leave this as require() since this file is built Dynamically from webpack -const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main'); +const { AppServerModuleNgFactory, LAZY_MODULE_MAP, ngExpressEngine, provideModuleMap } = require('./dist/server/main'); // Express Engine -import { ngExpressEngine } from '@nguniversal/express-engine'; +//import { ngExpressEngine } from '@nguniversal/express-engine'; // Import module map for lazy loading -import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader'; +//import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader'; +//const { provideModuleMap } = require('@nguniversal/module-map-ngfactory-loader'); app.engine('html', ngExpressEngine({ bootstrap: AppServerModuleNgFactory, diff --git a/src/app/core/admin/admin-objects.model.ts b/src/app/core/admin/admin-objects.model.ts index f1fabbd16..01f492522 100644 --- a/src/app/core/admin/admin-objects.model.ts +++ b/src/app/core/admin/admin-objects.model.ts @@ -25,6 +25,8 @@ export interface DatabaseInfo { driver: string; product: string; connected: boolean; + maxConnectionPool?: number; + activeConnection?: number; latency: number; } @@ -51,8 +53,8 @@ export interface UploadObject { status: string; version?: number; _self?: { - type: string, - url: string + type: string; + url: string; }; } diff --git a/src/app/core/admin/admin.component.html b/src/app/core/admin/admin.component.html index 705a06fbf..a24321d71 100644 --- a/src/app/core/admin/admin.component.html +++ b/src/app/core/admin/admin.component.html @@ -1,6 +1,6 @@
- + Server Status @@ -21,14 +21,16 @@

User Management

- + - Data Management + Data Import -

Data Import

- - + +

Staged Data Import

+ + +
@@ -61,10 +63,17 @@

All Files

+ + + Data Management (Legacy) + +

Bulk Data Import

+ + + +
- - -
+ \ No newline at end of file diff --git a/src/app/core/admin/admin.component.ts b/src/app/core/admin/admin.component.ts index b02b1c4b9..878aed353 100644 --- a/src/app/core/admin/admin.component.ts +++ b/src/app/core/admin/admin.component.ts @@ -1,5 +1,7 @@ import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; +import { MatTabChangeEvent } from '@angular/material/tabs'; +import {Location} from '@angular/common'; @Component({ selector: 'app-admin', @@ -8,21 +10,71 @@ import { ActivatedRoute } from '@angular/router'; }) export class AdminComponent implements OnInit { activeTab: number; + current: string; + lastTab: number; constructor( - private activatedRoute: ActivatedRoute + private activatedRoute: ActivatedRoute, + private router: Router, + private location: Location ) { } ngOnInit() { + + this.activatedRoute.params.subscribe(routeParams => { + this.current = routeParams.function; + switch (this.current) { + case 'cache': this.activeTab = 0; break; + case 'user': this.activeTab = 1; break; + case 'import': this.activeTab = 2; break; + case 'cv': this.activeTab = 3; break; + case 'jobs': this.activeTab = 4; break; + case 'files': this.activeTab = 5; break; + case 'data': this.activeTab = 6; break; + + default: this.activeTab = 0; break; + } + }); const tab = this.activatedRoute.snapshot.queryParams['function'] || 'cache'; - switch (tab) { - case 'cache': this.activeTab = 0; break; - case 'user': this.activeTab = 1; break; - case 'data': this.activeTab = 2; break; - case 'cv': this.activeTab = 3; break; - case 'jobs': this.activeTab = 4; break; - case 'files': this.activeTab = 5; break; - default: this.activeTab = 0; break; - } + } + + onTabChanged(event: MatTabChangeEvent): void { + + let route = 'cache'; + + switch (event.index) { + case 0: + + break; + case 1: + route = 'user'; + break; + case 2: + route = 'import'; + break; + case 3: + route = 'cv'; + break; + case 4: + route = 'jobs'; + break; + case 5: + route = 'files'; + break; + case 6: + route = 'data'; + break; + } + if (this.current !== 'jobs') { + this.current = route; + this.router.navigate(['/admin/' + route] ); + } else { + this.current = route; + this.activeTab = 0; + this.router.navigate(['/admin/' + route] ); + } + + + } } diff --git a/src/app/core/admin/admin.module.ts b/src/app/core/admin/admin.module.ts index 595ea715f..0eb649a91 100644 --- a/src/app/core/admin/admin.module.ts +++ b/src/app/core/admin/admin.module.ts @@ -1,8 +1,23 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { CvManagementComponent } from '@gsrs-core/admin/cv-management/cv-management.component'; -// tslint:disable-next-line:max-line-length -import { MatIconModule, MatButtonModule, MatInputModule, MatFormFieldModule, MatCardModule, MatCheckboxModule, MatTableDataSource, MatTableModule, MatSortModule, MatTooltipModule, MatProgressSpinnerModule, MatTabsModule, MatDialogModule, MatPaginatorModule, MatSelectModule, MatProgressBarModule, MatTreeModule } from '@angular/material'; +// eslint-disable-next-line max-len +import {MatIconModule} from '@angular/material/icon'; +import {MatButtonModule} from '@angular/material/button'; +import {MatInputModule} from '@angular/material/input'; +import {MatCardModule} from '@angular/material/card'; +import {MatCheckboxModule} from '@angular/material/checkbox'; +import {MatTableModule} from '@angular/material/table'; +import {MatSortModule} from '@angular/material/sort'; +import {MatTooltipModule} from '@angular/material/tooltip'; +import {MatTabsModule} from '@angular/material/tabs'; +import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; +import {MatDialogModule, MatDialogRef} from '@angular/material/dialog'; +import {MatPaginatorModule} from '@angular/material/paginator'; +import {MatSelectModule} from '@angular/material/select'; +import {MatTreeModule} from '@angular/material/tree'; +import {MatProgressBarModule} from '@angular/material/progress-bar'; +import {MatFormFieldModule} from '@angular/material/form-field'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { AdminComponent } from '@gsrs-core/admin/admin.component'; import { CvTermDialogComponent } from '@gsrs-core/admin/cv-management/cv-term-dialog/cv-term-dialog.component'; @@ -17,6 +32,24 @@ import { MonitorComponent } from '@gsrs-core/admin/monitor/monitor.component'; import { CanActivateAdmin } from '@gsrs-core/admin/can-activate-admin'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { AllFilesComponent } from '@gsrs-core/admin/all-files/all-files.component'; +import { FragmentWizardComponent } from '@gsrs-core/admin/fragment-wizard/fragment-wizard.component'; +import { StructureEditorModule } from '@gsrs-core/structure-editor'; +import { ImportManagementComponent } from '@gsrs-core/admin/import-management/import-management.component'; +import { MatRadioModule } from '@angular/material/radio'; +import { ImportBrowseComponent } from '@gsrs-core/admin/import-browse/import-browse.component'; +import { MatSidenavModule } from '@angular/material/sidenav'; +import { FacetsManagerModule } from '@gsrs-core/facets-manager'; +import { ImportSummaryComponent } from '@gsrs-core/admin/import-browse/import-summary/import-summary.component'; +import { RouterModule } from '@angular/router'; +import { MatMenuModule } from '@angular/material/menu'; +import { ImportDialogComponent } from '@gsrs-core/admin/import-management/import-dialog/import-dialog.component'; +import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; +import { CvImportComponent } from '@gsrs-core/admin/import-management/cv-import/cv-import.component'; +import { MatChipsModule } from '@angular/material/chips'; +import { BulkActionDialogComponent } from '@gsrs-core/admin/import-browse/bulk-action-dialog/bulk-action-dialog.component'; +import { NamesDisplayStagingPipe } from '@gsrs-core/admin/import-browse/name-display.pipe'; +import { ElementLabelDisplayModule } from '@gsrs-core/utils/element-label-display.module'; +import { TakeImportPipe } from '@gsrs-core/admin/import-browse/take-import.pipe'; @@ -38,13 +71,23 @@ import { AllFilesComponent } from '@gsrs-core/admin/all-files/all-files.componen MatButtonModule, MatCheckboxModule, MatSelectModule, + RouterModule, + MatMenuModule, MatSortModule, MatTooltipModule, MatProgressSpinnerModule, MatDialogModule, MomentModule, MatPaginatorModule, - MatIconModule], + MatIconModule, + MatRadioModule, + MatSidenavModule, + MatChipsModule, + StructureEditorModule, + FacetsManagerModule, + SubstanceImageModule, + ElementLabelDisplayModule, +], declarations: [ CvManagementComponent, ScheduledJobsComponent, @@ -56,7 +99,16 @@ import { AllFilesComponent } from '@gsrs-core/admin/all-files/all-files.componen CacheSummaryComponent, DataManagementComponent, MonitorComponent, - AllFilesComponent + AllFilesComponent, + FragmentWizardComponent, + ImportManagementComponent, + ImportBrowseComponent, + ImportSummaryComponent, + ImportDialogComponent, + CvImportComponent, + BulkActionDialogComponent, + NamesDisplayStagingPipe, + TakeImportPipe ], exports: [ CvManagementComponent, @@ -69,14 +121,33 @@ import { AllFilesComponent } from '@gsrs-core/admin/all-files/all-files.componen CacheSummaryComponent, DataManagementComponent, MonitorComponent, - AllFilesComponent + AllFilesComponent, + FragmentWizardComponent, + ImportManagementComponent, + ImportBrowseComponent, + ImportSummaryComponent, + ImportDialogComponent, + CvImportComponent, + BulkActionDialogComponent + ], entryComponents: [ CvTermDialogComponent, - UserEditDialogComponent + UserEditDialogComponent, + ImportDialogComponent, + CvImportComponent, + BulkActionDialogComponent, + NamesDisplayStagingPipe, + TakeImportPipe + + ], providers: [ - CanActivateAdmin + CanActivateAdmin, + { + provide: MatDialogRef, + useValue: {} + }, ] }) -export class AdminModule { } +export class AdminModule { } \ No newline at end of file diff --git a/src/app/core/admin/admin.service.ts b/src/app/core/admin/admin.service.ts index e933268fd..124685d54 100644 --- a/src/app/core/admin/admin.service.ts +++ b/src/app/core/admin/admin.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { HttpClient, HttpParams, HttpErrorResponse } from '@angular/common/http'; +import { HttpClient, HttpParams, HttpErrorResponse, HttpParameterCodec } from '@angular/common/http'; import { BaseHttpService } from '../base/base-http.service'; import { Observable, Subject, forkJoin, throwError } from 'rxjs'; import { ConfigService } from '../config/config.service'; @@ -9,7 +9,23 @@ import { FacetHttpParams } from '@gsrs-core/facets-manager'; import { ScheduledJob } from '@gsrs-core/admin/scheduled-jobs/scheduled-job.model'; import { Auth } from '@gsrs-core/auth'; import { UserEditObject, UploadObject, DirectoryFile } from '@gsrs-core/admin/admin-objects.model'; +class CustomEncoder implements HttpParameterCodec { + encodeKey(key: string): string { + return encodeURIComponent(key); + } + encodeValue(value: string): string { + return encodeURIComponent(value); + } + + decodeKey(key: string): string { + return decodeURIComponent(key); + } + + decodeValue(value: string): string { + return decodeURIComponent(value); + } +} @Injectable({ providedIn: 'root' @@ -23,6 +39,8 @@ export class AdminService extends BaseHttpService { super(configService); } + + public fetchJobs(): Observable< any > { const url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/`; return this.http.get< any >(`${url}scheduledjobs`); @@ -96,6 +114,7 @@ export class AdminService extends BaseHttpService { } public loadData(form: any) { + console.log(form); const url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/admin/load`; return this.http.post< any >(url, form); } @@ -118,4 +137,226 @@ export class AdminService extends BaseHttpService { public getDownloadLink(name: string): string { return `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/admin/files/${name}`; } + + public getAdapters(): Observable< any > { + const url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/substances/import/adapters`; + return this.http.get< any >(`${url}`); + } + + /* public putAdapter(file: any): Observable< Auth > { + const url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/substances/import`; + return this.http.put< any >(`${url}`, file); + }*/ + + public previewAdapter(id: string, file: any, adapter?: any, limit?: any): Observable< any > { + console.log(file); + let url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/substances/import/${id}/@preview`; + if (limit) { + if(limit === 'all') { + limit = 500000 + } + url += "?limit=" + limit; + } + return this.http.put< any >(`${url}`, file); + } + + public executeAdapter(id: string, file: any, adapter?: any): Observable< any > { + const url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/substances/import/${id}/@execute?adapter=${adapter}`; + return this.http.post< any >(`${url}`, file); + } + + public executeAdapterAsync(id: string, file: any, adapter?: any): Observable< any > { + const url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/substances/import/${id}/@executeasync?adapter=${adapter}`; + return this.http.post< any >(`${url}`, file); + } + + public processingstatus(id: any) { + const url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/substances/stagingArea/processingstatus(${id})`; + return this.http.get< any >(`${url}`); + + } + + //http://localhost:8080/api/v1/substances/import(a446cea4-07ad-4a25-b117-c2e25fee9c9a)/@execute?adapter=SDF + + public postAdapterFile(file: any, adapter?: string, entityType?: string): Observable< UploadObject > { + if (!entityType) { + entityType = 'ix.ginas.models.v1.Substance'; + } + if (!adapter) { + adapter = 'SDF'; + } + adapter = encodeURI(adapter); + + const url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/substances/import?adapter=${adapter}&entityType=${entityType}`; + + return this.http.post< any >(url, file); + } + + + public GetStagedData(index?: any) { + let url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/substances/import/data`; + if(index) { + url += `?skip=${index}`; + } + return this.http.get< any >(`${url}`); + + } + public GetStagedRecord(id:string) { + let url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/substances/stagingArea/${id}`; + + return this.http.get< any >(`${url}`); + + } + + public SearchStagedData(skip: any, facets?: any, term?: any, top?: any, view?: string) { + let params = new FacetHttpParams({encoder: new CustomEncoder()}); + if (facets){ + params = params.appendFacetParams(facets, true); + + } + if (term) { + params = params.append('q',(term)); + } + + if (top) { + params = params.append('top',(top)); + + } + + if (view) { + params = params.append('view',(view)); + } + + const options = { + params: params + }; + let url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/substances/stagingArea/search?skip=${skip}`; + + return this.http.get< any >(url, options); + + } + + public GetSingleUUID() { + let url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/substances/search?top=1`; + return this.http.get< any >(url); + } + + public stagedRecordAction(id:string, record: string, action: string) { + let url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/substances/import/${id}/0/@act?matchedEntityId=${record}&view=internal&persistChangedObject=true`; + let toput = {}; + if (action === 'create') { + toput = { + "processingActions": [ + { + "processingActionClass": "gsrs.dataexchange.processing_actions.CreateProcessingAction" + } + ] + }; + } else if (action === 'merge') { + toput = { + "processingActions": [ + { + "parameters": { + "MergeNames": true, + "MergeCodes": true + }, + "processingActionClass": "gsrs.dataexchange.processing_actions.MergeProcessingAction" + } + ] + } + } else if (action === 'ignore') { + toput = { + "processingActions": [ + { + "processingActionClass": "gsrs.dataexchange.processing_actions.RejectProcessingAction" + } + ] + } + } + return this.http.put< any >(url, toput); + + } + + public stagedRecordMultiAction(records:any, action: string, scrubber?: any) { + console.log(records); + console.log(scrubber); + let url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/substances/stagingArea/@bulkactasync?persistChangedObject=true`; + let toput = { + "stagingAreaRecords": + records + , + "processingActions": [ + { + "parameters": { + + }, "processingActionName": action + } + ] + } + if (scrubber) { + toput.processingActions[0].parameters['scrubberSettings'] = scrubber; + } + console.log(toput); + + return this.http.put< any >(url, toput); + + } + + public stagedRecordSingleAction(id:string, action: string, params?: any, matching?: string) { + let url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/substances/stagingArea/@bulkactasync?persistChangedObject=true`; + let putParam = { "parameters": {}, "processingActionName": action}; + let putRecords = {} + if (action === 'merge') { + putParam.parameters['mergeSettings'] = params; + putRecords = {'id': id, 'matchingID': matching } + } else { + putRecords = {'id': id} + } + let toput = { + stagingAreaRecords: [ + putRecords + ], + "processingActions": [ + putParam + ] + } + + return this.http.put< any >(url, toput); + + } + + getMergeActionSchema() { + let url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/substances/stagingArea/action(merge)/@schema`; + + return this.http.get< any >(url); + + } + + deleteStagedRecord(records: Array) { + let tosend = ""; + for(let i = 0; i< records.length; i++) { + tosend += records[i]; + if(i !== (records.length - 1)) { + tosend += ','; + } + } + let url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/substances/stagingArea/@deletebulk`; + + return this.http.delete< any >(url, { body: tosend }); + + } + + public updateStagingArea(id: string, substance: any) { + let url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/substances/stagingArea/${id}/@update`; + + return this.http.put< any >(url, substance); + + } + + public getImportScrubberSchema() { + let url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/substances/stagingArea/action(Scrub)/@schema`; + + return this.http.get< any >(url); + + } } diff --git a/src/app/core/admin/all-files/all-files.component.scss b/src/app/core/admin/all-files/all-files.component.scss index 35c95c558..dfd2da7bf 100644 --- a/src/app/core/admin/all-files/all-files.component.scss +++ b/src/app/core/admin/all-files/all-files.component.scss @@ -6,10 +6,10 @@ } .log-link { - color: #1565C0; + color: var(--link-color); text-decoration: none; } .description { margin-bottom: 20px; - } \ No newline at end of file + } diff --git a/src/app/core/admin/all-files/all-files.component.ts b/src/app/core/admin/all-files/all-files.component.ts index 4adb23c81..b84a7830c 100644 --- a/src/app/core/admin/all-files/all-files.component.ts +++ b/src/app/core/admin/all-files/all-files.component.ts @@ -2,7 +2,10 @@ import { Component, OnInit } from '@angular/core'; import { AdminService } from '@gsrs-core/admin/admin.service'; import { take } from 'rxjs/operators'; import { NestedTreeControl, FlatTreeControl } from '@angular/cdk/tree'; -import { MatTreeNestedDataSource, MatTreeFlattener, MatTreeFlatDataSource } from '@angular/material'; +import {MatTreeModule} from '@angular/material/tree'; +import {MatTreeNestedDataSource} from '@angular/material/tree'; +import {MatTreeFlattener} from '@angular/material/tree'; +import {MatTreeFlatDataSource} from '@angular/material/tree'; import { DirectoryFile } from '@gsrs-core/admin/admin-objects.model'; import { LoadingService } from '@gsrs-core/loading'; @@ -13,41 +16,16 @@ import { LoadingService } from '@gsrs-core/loading'; }) export class AllFilesComponent implements OnInit { - private _transformer = (node: any, level: number) => { - return { - expandable: !!node.children && node.children.length > 0, - name: node.id, - text: node.text, - level: level, - hasLink: node.hasLink - }; - } - // it is impossible to follow the ordering rule. Doing so results in another tslint error. -/* tslint:disable */ - treeControl = new FlatTreeControl( - node => node.level, node => node.expandable); - - treeFlattener = new MatTreeFlattener( - this._transformer, node => node.level, node => node.expandable, node => node.children); - - dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener); - logFiles: Array< DirectoryFile >; - constructor( - private adminService: AdminService, - private loadingService: LoadingService - ) { - } - hasChild = (_: number, node: FileTreeNode) => node.expandable; -/* tslint:enable */ + /* eslint-enable */ ngOnInit() { this.loadingService.setLoading(true); this.adminService.getFiles().pipe(take(1)).subscribe( result => { - for (let i = 0; i < result.length; i += 1) { - if (result[i].isDir === false) { - result[i].hasLink = this.adminService.getDownloadLink(result[i].id); + for (const r of result) { + if (r.isDir === false) { + r.hasLink = this.adminService.getDownloadLink(r.id); } } - const temp = this.list_to_tree(result); + const temp = this.listToTree(result); this.dataSource.data = temp; this.loadingService.setLoading(false); @@ -55,26 +33,26 @@ export class AllFilesComponent implements OnInit { } - - - - list_to_tree(list) { - const map = {}, roots = []; + listToTree(list) { + const map = {}; + const roots = []; let node; - for (let i = 0; i < list.length; i += 1) { - map[list[i].id] = i; - list[i].children = []; - if (i === 0) { - list[i].order = 'primary'; + let count = 0; + for (const l of list) { + map[l.id] = count; + count++; + l.children = []; + if (count === 1) { + l.order = 'primary'; - } else if (i % 2 === 0) { - list[i].order = 'even'; + } else if (count % 2 === 0) { + l.order = 'even'; } else { - list[i].order = 'odd'; + l.order = 'odd'; } } - for (let i = 0; i < list.length; i += 1) { - node = list[i]; + for (const l of list) { + node = l; if (node.parent !== '#') { list[map[node.parent]].children.push(node); } else { @@ -83,6 +61,32 @@ export class AllFilesComponent implements OnInit { } return roots; } + + private _transformer = (node: any, level: number) => ( + { + expandable: !!node.children && node.children.length > 0, + name: node.id, + text: node.text, + level: level, + hasLink: node.hasLink + } + ); + // it is impossible to follow the ordering rule. Doing so results in another tslint error. +/* eslint-disable */ + treeControl = new FlatTreeControl( + node => node.level, node => node.expandable); + + treeFlattener = new MatTreeFlattener( + this._transformer, node => node.level, node => node.expandable, node => node.children); + + dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener); + logFiles: Array< DirectoryFile >; + constructor( + private adminService: AdminService, + private loadingService: LoadingService + ) { + } + hasChild = (_: number, node: FileTreeNode) => node.expandable; } diff --git a/src/app/core/admin/cache-summary/cache-summary.component.html b/src/app/core/admin/cache-summary/cache-summary.component.html index 5cb3f2c23..60eb96155 100644 --- a/src/app/core/admin/cache-summary/cache-summary.component.html +++ b/src/app/core/admin/cache-summary/cache-summary.component.html @@ -103,6 +103,21 @@ Connected {{db.connected}} + + Max Connections + {{db.maxConnectionPool ? db.maxConnectionPool : 'N/A'}} + + + Active Connections + {{db.activeConnection ? db.activeConnection : 'N/A'}} + + + Connection Usage + + {{ db.activeConnection && db.maxConnectionPool ? + (100-((db.maxConnectionPool - db.activeConnection) / db.maxConnectionPool * 100) | number : '1.1-2') + '%' : 'N/A' + }} + diff --git a/src/app/core/admin/cache-summary/cache-summary.component.scss b/src/app/core/admin/cache-summary/cache-summary.component.scss index 2dbf681d6..84db49509 100644 --- a/src/app/core/admin/cache-summary/cache-summary.component.scss +++ b/src/app/core/admin/cache-summary/cache-summary.component.scss @@ -5,7 +5,7 @@ &:not(:last-child) { - border-bottom: 1px solid rgba(128, 128, 128, .15); + border-bottom: 1px solid var(--nth-child-color-1); } .name { @@ -15,7 +15,7 @@ } &:nth-child(even) { - background-color: rgba(68, 138, 255, .07); + background-color: var(--nth-child-color-2); } } @@ -55,7 +55,7 @@ } .mat-row:nth-child(odd){ - background-color: rgba(68, 138, 255, .07); + background-color: var(--nth-child-color-2); } } @@ -70,4 +70,4 @@ box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 1px -1px, rgba(0, 0, 0, 0.14) 0px 1px 1px 0px, rgba(0, 0, 0, 0.12) 0px 1px 3px 0px; box-sizing:border-box; color:rgba(0, 0, 0, 0.87); -} \ No newline at end of file +} diff --git a/src/app/core/admin/cache-summary/cache-summary.component.ts b/src/app/core/admin/cache-summary/cache-summary.component.ts index 55f6e20cd..669620f70 100644 --- a/src/app/core/admin/cache-summary/cache-summary.component.ts +++ b/src/app/core/admin/cache-summary/cache-summary.component.ts @@ -11,7 +11,7 @@ import * as moment from 'moment'; styleUrls: ['./cache-summary.component.scss'] }) export class CacheSummaryComponent implements OnInit, OnDestroy { - displayedColumns: string[] = ['database', 'driver', 'product', 'latency', 'connected']; + displayedColumns: string[] = ['database', 'driver', 'product', 'latency', 'connected', 'max', 'active', 'usage']; health: HealthInfo; sub: Subscription; runtime = ''; @@ -22,6 +22,7 @@ export class CacheSummaryComponent implements OnInit, OnDestroy { ngOnInit() { this.sub = this.adminService.getEnvironmentHealth().subscribe(response => { this.health = response; + this.setStart(); }); } diff --git a/src/app/core/admin/cv-management/cv-management.component.scss b/src/app/core/admin/cv-management/cv-management.component.scss index 9281a264d..525280e5e 100644 --- a/src/app/core/admin/cv-management/cv-management.component.scss +++ b/src/app/core/admin/cv-management/cv-management.component.scss @@ -58,6 +58,6 @@ .show-button { padding-left: 10px; - color: #1565c0; + color: var(--link-color); font-size:14px; -} \ No newline at end of file +} diff --git a/src/app/core/admin/cv-management/cv-management.component.ts b/src/app/core/admin/cv-management/cv-management.component.ts index 1a89f8229..add92b5b0 100644 --- a/src/app/core/admin/cv-management/cv-management.component.ts +++ b/src/app/core/admin/cv-management/cv-management.component.ts @@ -1,6 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { ControlledVocabularyService, Vocabulary, VocabularyTerm } from '@gsrs-core/controlled-vocabulary'; -import { MatDialog, Sort } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; +import { Sort } from '@angular/material/sort'; import { OverlayContainer } from '@angular/cdk/overlay'; import { CvTermDialogComponent } from '@gsrs-core/admin/cv-management/cv-term-dialog/cv-term-dialog.component'; import { UtilsService } from '@gsrs-core/utils'; @@ -15,16 +16,16 @@ import { DataDictionaryService } from '@gsrs-core/utils/data-dictionary.service' }) export class CvManagementComponent implements OnInit { vocabularies: Array< Vocabulary > = []; - private overlayContainer: HTMLElement; displayedColumns: string[] = ['domain', 'type', 'path', 'terms', 'edit']; filtered: Array< Vocabulary >; vocabType: any = []; downloadHref: SafeUrl; searchControl = new FormControl(); - private searchTimer: any; dictionary: Array< any >; loading: boolean; toggle: Array< boolean > = []; + private searchTimer: any; + private overlayContainer: HTMLElement; constructor(public cvService: ControlledVocabularyService, private dialog: MatDialog, @@ -57,6 +58,9 @@ export class CvManagementComponent implements OnInit { this.searchControl.valueChanges.subscribe(value => { this.filterList(value, this.vocabularies); }, error => { + this.loading = false; + alert('The controlled vocabulary has failed to load from the server' + + (error && error.message ? 'with the following message \n\n' + error.message : '')); }); }); this.downloadHref = this.sanitizer.bypassSecurityTrustUrl('data:text/json;charset=UTF-8,' + @@ -83,12 +87,22 @@ export class CvManagementComponent implements OnInit { } editTerms(vocab: any, index: number): void { + let thisy = window.pageYOffset; + window.scroll({ + top: 0, + left: 0, + behavior: 'auto' }); const dialogRef = this.dialog.open(CvTermDialogComponent, { - data: {'vocabulary': vocab}, + data: {vocabulary: vocab}, width: '1200px' }); this.overlayContainer.style.zIndex = '1002'; + // this.overlayContainer.style.zIndex = '0'; const dialogSubscription = dialogRef.afterClosed().subscribe(response => { + window.scroll({ + top: thisy, + left: 0, + behavior: 'auto' }); this.overlayContainer.style.zIndex = null; if (response ) { // this.vocabularies[index] = response; diff --git a/src/app/core/admin/cv-management/cv-term-dialog/cv-term-dialog.component.html b/src/app/core/admin/cv-management/cv-term-dialog/cv-term-dialog.component.html index c56e31db0..e0ee03b1c 100644 --- a/src/app/core/admin/cv-management/cv-term-dialog/cv-term-dialog.component.html +++ b/src/app/core/admin/cv-management/cv-term-dialog/cv-term-dialog.component.html @@ -5,6 +5,17 @@

+
+
+
+ +
+ +
+
+
+
+ + +
+ + +
+
CV not valid:
+
{{message.messageType}} :{{message.message}}
+ +
diff --git a/src/app/core/admin/cv-management/cv-term-dialog/cv-term-dialog.component.scss b/src/app/core/admin/cv-management/cv-term-dialog/cv-term-dialog.component.scss index 69b68ef33..bf87def14 100644 --- a/src/app/core/admin/cv-management/cv-term-dialog/cv-term-dialog.component.scss +++ b/src/app/core/admin/cv-management/cv-term-dialog/cv-term-dialog.component.scss @@ -11,6 +11,46 @@ } + +.validation { +display:flex; +flex-direction: column; +width: 100%; +margin-bottom:5px; +} + + +.ERROR { + background-color: var(--regular-red-color); + color: var(--error-dialog-color); + background-color: var(--error-dialog-bg-color); + padding: 4px 5px; + margin-right: 5px; + border-radius: 5px; +} + + +.WARNING { + color: var(--warning-dialog-color); + background-color: var(--warning-dialog-bg-color); + padding: 4px 5px; + margin-right: 5px; + border-radius: 5px; +} + +.INFO { + color: var(--regular-black-color); + background-color: var(--regular-lightgray-color); + padding: 4px 5px; + margin-right: 5px; + border-radius: 5px; +} + +.message { + padding-top: 5px; + padding-left: 15px; + +} .content-fix { min-height: 250px; padding: 15px 20px; @@ -47,4 +87,4 @@ .delete-icon { margin-bottom: 20px; -} \ No newline at end of file +} diff --git a/src/app/core/admin/cv-management/cv-term-dialog/cv-term-dialog.component.ts b/src/app/core/admin/cv-management/cv-term-dialog/cv-term-dialog.component.ts index 3f2234e77..172b08b07 100644 --- a/src/app/core/admin/cv-management/cv-term-dialog/cv-term-dialog.component.ts +++ b/src/app/core/admin/cv-management/cv-term-dialog/cv-term-dialog.component.ts @@ -1,7 +1,9 @@ import { Component, OnInit, Input, Inject, ViewChild, ElementRef, AfterViewInit } from '@angular/core'; import { VocabularyTerm, Vocabulary, ControlledVocabularyService } from '@gsrs-core/controlled-vocabulary'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog'; import { ScrollToService } from '@gsrs-core/scroll-to/scroll-to.service'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { FragmentWizardComponent } from '@gsrs-core/admin/fragment-wizard/fragment-wizard.component'; @Component({ selector: 'app-cv-term-dialog', @@ -13,13 +15,18 @@ export class CvTermDialogComponent implements OnInit, AfterViewInit{ vocabulary: Vocabulary; terms: any; message: string; + validationMessages = []; loading = true; + toggled = []; + private overlayContainer: HTMLElement; constructor( public cvService: ControlledVocabularyService, public dialogRef: MatDialogRef, public scrollToService: ScrollToService, + private dialog: MatDialog, + private overlayContainerService: OverlayContainer, @Inject(MAT_DIALOG_DATA) public data: any ) { this.vocabulary = data.vocabulary; @@ -35,9 +42,12 @@ export class CvTermDialogComponent implements OnInit, AfterViewInit{ ngOnInit() { this.loading = false; + this.overlayContainer = this.overlayContainerService.getContainerElement(); + } ngAfterViewInit() { + if (this.vocabulary.vocabularyTermType === 'ix.ginas.models.v1.FragmentControlledVocabulary') { this.terms.forEach(term => { if (term.simplifiedStructure) { @@ -50,9 +60,46 @@ export class CvTermDialogComponent implements OnInit, AfterViewInit{ } } + updateStructure(term: any, index: any) { + this.terms[index] = term; + } + getStructure(structure) { - this.cvService.getStructure(structure).subscribe(response => { - return response; + this.cvService.getStructure(structure).subscribe(response => ( + response + )); + } + + editTerms(term: any, index): void { + // this.dialog.openDialogs.pop(); + // this.overlayContainer.style.zIndex = '1003'; + let thisy = window.pageYOffset; + /* window.scroll({ + top: 0, + left: 0, + behavior: 'auto' });*/ + let dialogConfig = { width: '70%', height: '85%',data: {vocabulary: this.vocabulary, domain: this.vocabulary.domain, term: term, adminPanel: true}, }; + const dialogRef = this.dialog.open(FragmentWizardComponent, dialogConfig); + this.overlayContainer.style.zIndex = '1003'; + + setTimeout(() => { + // this.dialog.openDialogs.pop(); + // this.overlayContainer.style.zIndex = '10003'; + },3000); + const dialogSubscription = dialogRef.afterClosed().subscribe(response => { + window.scroll({ + top: thisy, + left: 0, + behavior: 'auto' }); + // this.overlayContainer.style.zIndex = null; + if (response ) { + this.terms[index].simplifiedStructure = response; + this.terms[index].fragmentStructure = response; + this.terms[index].fragmentSrc = this.cvService.getStructureUrl(response); + this.terms[index].simpleSrc = this.cvService.getStructureUrl(response); + + // this.getVocab(); + } }); } @@ -62,22 +109,70 @@ export class CvTermDialogComponent implements OnInit, AfterViewInit{ term.simpleSrc = this.cvService.getStructureUrl(term.simplifiedStructure); } - + submit(): void { this.vocabulary.terms = this.terms; - this.cvService.addVocabTerm( this.vocabulary).subscribe (response => { + this.validationMessages = []; + this.cvService.validateVocab(this.vocabulary).subscribe(response => { + if(response && response.valid) { + this.cvService.addVocabTerm( this.vocabulary).subscribe (response => { + this.loading = false; + if (response.terms && response.terms.length === this.vocabulary.terms.length) { + alert('vocabulary updated'); + setTimeout(() => { + this.dialogRef.close(response); + }, 200); + } + },error => { + let str = 'Invalid Vocabulary'; + if (error.error && error.error.message) { + str += '\n\n' + error.error.message; + + } + else if(error.message) { + str += '\n\n' + error.message; + } + alert(str); + this.loading = false; + + }); + } else { + if(response && response.validationMessages) { + response.validationMessages.forEach(message => { + this.validationMessages.push(message); + }); + } + } + },error => { + let str = 'Invalid Vocabulary'; + if (error.error && error.error.message) { + str += '\n\n' + error.error.message; + + } + else if(error.message) { + str += '\n\n' + error.message; + } + alert(str); + this.loading = false; + + }); + /* this.cvService.addVocabTerm( this.vocabulary).subscribe (response => { this.loading = false; if (response.terms && response.terms.length === this.vocabulary.terms.length) { alert('vocabulary updated'); - setTimeout(() => {this.dialogRef.close(response); }, 200); + setTimeout(() => { + this.dialogRef.close(response); + }, 200); } else { alert('invalid vocabulary'); } }, error => { alert('invalid vocabulary'); - }); + });*/ + this.loading = false; + } diff --git a/src/app/core/admin/data-management/data-management.component.scss b/src/app/core/admin/data-management/data-management.component.scss index 55a0a7ef5..a7901fc8b 100644 --- a/src/app/core/admin/data-management/data-management.component.scss +++ b/src/app/core/admin/data-management/data-management.component.scss @@ -1,6 +1,6 @@ .italics { font-style: italic; - color: rgba(0, 0, 0, .5); + color: var(--text-color); } .file-name { @@ -27,22 +27,22 @@ height: 17px; margin-bottom: 5px; font-size:12px; - color: black; - -webkit-text-fill-color: white; /* Will override color (regardless of order) */ + color: var(--regular-black-color); + -webkit-text-fill-color: var(--regular-white-color); /* Will override color (regardless of order) */ -webkit-text-stroke-width: 1px; - -webkit-text-stroke-color: black; + -webkit-text-stroke-color: var(--regular-black-color); } .load-fail { // transform: rotate(180deg); margin-top: -22px; ::ng-deep .mat-progress-bar-fill { - background-color: rgb(173, 26, 26); + background-color: var(--error); z-index: 1; } ::ng-deep .mat-progress-bar-fill::after { - background-color: rgb(173, 26, 26); + background-color: var(--error); // z-index: 1; } @@ -51,7 +51,7 @@ ::ng-deep .mat-progress-bar-buffer { - background: rgba(0, 0, 0, 0.01); + background: var(--progress-bar-buffer-bg-color); } } @@ -65,13 +65,13 @@ transform: rotate(180deg); margin-top: -22px; ::ng-deep .mat-progress-bar-fill { - background-color: rgb(173, 26, 26); + background-color: var(--error); z-index: 2; } ::ng-deep .mat-progress-bar-fill::after { - background-color: rgb(173, 26, 26); + background-color: var(--error); z-index: 2; @@ -81,40 +81,40 @@ ::ng-deep .mat-progress-bar-buffer { - background: rgba(0, 0, 0, 0.01); + background: var(--progress-bar-buffer-bg-color); } } .load-success { ::ng-deep .mat-progress-bar-fill { - // background-color: rgb(173, 26, 26); + // background-color: var(--error); z-index: 2; } ::ng-deep .mat-progress-bar-fill::after { - // background-color: rgb(173, 26, 26); + // background-color: var(--error); // z-index: 2; } ::ng-deep .mat-progress-bar-buffer { - background: rgba(0, 0, 0, 0.01); + background: var(--progress-bar-buffer-bg-color); } } .load-success-old { ::ng-deep .mat-progress-bar-fill { - // background-color: rgb(173, 26, 26); + // background-color: var(--error); z-index: 2; } ::ng-deep .mat-progress-bar-fill::after { - // background-color: rgb(173, 26, 26); + // background-color: var(--error); z-index: 2; } ::ng-deep .mat-progress-bar-buffer { - background: rgba(0, 0, 0, 0.01); + background: var(--progress-bar-buffer-bg-color); } } @@ -210,13 +210,13 @@ margin-bottom: auto; transform: scale(-1, 1); ::ng-deep circle { - stroke: rgb(173, 26, 26); + stroke: var(--error); } ::ng-deep .mat-progress-spinner-buffer { - background: rgb(212, 212, 212); + background: var(--progress-spinner-buffer-bg-color); } } @@ -225,11 +225,11 @@ margin-bottom: auto; transform: scale(-1, 1); ::ng-deep circle { - stroke: rgb(173, 26, 26); + stroke: var(--error); } ::ng-deep .mat-progress-spinner-buffer { - background: rgb(212, 212, 212); + background: var(--progress-spinner-buffer-bg-color); } } @@ -243,7 +243,7 @@ margin-bottom: auto; .deleted { margin: auto; padding: 10px; - color: grey; + color: var(--regular-grey-color); font-style: italics; font-size: 18px; } @@ -263,7 +263,7 @@ margin-bottom: auto; .count-cont { text-align: center; - color: white; + color: var(--regular-white-color); margin-top: -22px; font-weight: bold; z-index: 5; diff --git a/src/app/core/admin/data-management/data-management.component.ts b/src/app/core/admin/data-management/data-management.component.ts index 670ade7c4..3795af2d2 100644 --- a/src/app/core/admin/data-management/data-management.component.ts +++ b/src/app/core/admin/data-management/data-management.component.ts @@ -49,9 +49,9 @@ export class DataManagementComponent implements OnInit { this.loadingService.setLoading(false); this.router.navigate(['/monitor/' + response.id]); - }, error => {this.message = 'File could not be uploaded'; - this.loadingService.setLoading(false); - + }, error => { + this.message = 'File could not be uploaded'; + this.loadingService.setLoading(false); }); } diff --git a/src/app/core/admin/fragment-wizard/fragment-wizard.component.html b/src/app/core/admin/fragment-wizard/fragment-wizard.component.html new file mode 100644 index 000000000..6dfbda25f --- /dev/null +++ b/src/app/core/admin/fragment-wizard/fragment-wizard.component.html @@ -0,0 +1,97 @@ + +
+

Add Fragment to CV

+
+
+

Fragment Structure Wizard

+
+
+
+ + + + + + + + + + +
+
+ + + +
+
+ +
+ +
+
+
+ + + +
+
+ +
+ +
+ + +
+
+ + +
+
+
+
+ +
+
+ + +
+
+ +
+
+
+
Click on a form to set the fragment term structure
+
+
+
Set Connection points using "*" atoms in the periodic table element selector.
+
+ + +
+
+
CV not valid:
+
{{message.messageType}} :{{message.message}}
+ +
+
+
{{message}}
+ + + {{privateTerm.fragmentStructure}}-{{privateTerm.description }}--{{privateTerm.value}} + + +
+
+
+ +
+
{{message}}
+ + + +
+
\ No newline at end of file diff --git a/src/app/core/admin/fragment-wizard/fragment-wizard.component.scss b/src/app/core/admin/fragment-wizard/fragment-wizard.component.scss new file mode 100644 index 000000000..abf4f9a15 --- /dev/null +++ b/src/app/core/admin/fragment-wizard/fragment-wizard.component.scss @@ -0,0 +1,111 @@ +.editor-container { + width: 920px; + min-height: 380px; + margin: auto; +} + +.cell { + margin-right:5px; +} + +@media(max-width: 1175px) { + .structure-editor-actions-container { + flex-direction: column; + width: 100%; + } + + .editor-container { + width: 100%; + height: 390px; + } + + .search-actions { + flex-direction: row; + width: 100%; + + } + .two { + width: 50%; + display: flex; + flex-direction: column; + } + + .action-button-container { + width: 50%; + margin: auto; + margin-top: 20px; + } +} + +@media(max-width: 970px) { + .structure-editor-actions-container { + width: 100%; + overflow-x: auto; + } + + .editor-container, .search-actions { + min-width: 633px; + } + + .jsdraw { + .editor-container, .search-actions { + min-width: 710px; + } + } +} + +.form-row { + display:flex; + width: 100%; + flex-direction: row; +} + +::ng-deep table { + z-index: 20000 !important; +} + +.structure-img { + margin-top:-20px; + margin-bottom: -20px; +} + + +.validation { + display:flex; + flex-direction: column; + width: 100%; + } + + + .message { + padding-top: 5px; + padding-left: 15px; + } + + + +.ERROR { + background-color: var(--regular-red-color); + color: var(--error-dialog-color); + background-color: var(--error-dialog-bg-color); + padding: 4px 5px; + margin-right: 5px; + border-radius: 5px; +} + + +.WARNING { +color: var(--warning-dialog-color); +background-color: var(--warning-dialog-bg-color); +padding: 4px 5px; +margin-right: 5px; +border-radius: 5px; +} + +.INFO { +color: var(--regular-black-color); +background-color: var(--regular-lightgray-color); +padding: 4px 5px; +margin-right: 5px; +border-radius: 5px; +} diff --git a/src/app/core/admin/fragment-wizard/fragment-wizard.component.spec.ts b/src/app/core/admin/fragment-wizard/fragment-wizard.component.spec.ts new file mode 100644 index 000000000..8a9e0f4f6 --- /dev/null +++ b/src/app/core/admin/fragment-wizard/fragment-wizard.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FragmentWizardComponent } from './fragment-wizard.component'; + +describe('FragmentWizardComponent', () => { + let component: FragmentWizardComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ FragmentWizardComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(FragmentWizardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/admin/fragment-wizard/fragment-wizard.component.ts b/src/app/core/admin/fragment-wizard/fragment-wizard.component.ts new file mode 100644 index 000000000..3574c917a --- /dev/null +++ b/src/app/core/admin/fragment-wizard/fragment-wizard.component.ts @@ -0,0 +1,410 @@ +import { Component, OnInit, Input, Output, Inject } from '@angular/core'; +import { Editor } from '@gsrs-core/structure-editor'; +import * as _ from 'lodash'; +import { ControlledVocabularyService, VocabularyTerm } from '@gsrs-core/controlled-vocabulary'; +import { LoadingService } from '@gsrs-core/loading'; +import { EventEmitter } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { StructureService } from '@gsrs-core/structure'; +import { OverlayContainer } from '@angular/cdk/overlay'; + + +@Component({ + selector: 'app-fragment-wizard', + templateUrl: './fragment-wizard.component.html', + styleUrls: ['./fragment-wizard.component.scss'] +}) +export class FragmentWizardComponent implements OnInit { + @Output() termUpdated = new EventEmitter(); + private editor: Editor; + @Input() vocab?: any; + connectivity: Array; + dat: any; + domains: any; + forms: Array = []; + term2: any = {value: '', display: ''}; + privateTerm: any = {value: '', display: ''}; + asDialog = false; + vocabulary: any; + message: string; + validationMessages =[]; + adminPanel?: boolean; +smiles?: any; +private overlayContainer: HTMLElement; + + + constructor( + private CVService: ControlledVocabularyService, + private loadingService: LoadingService, + private structureService: StructureService, + public dialogRef: MatDialogRef, + private overlayContainerService: OverlayContainer, + + @Inject(MAT_DIALOG_DATA) public data: any + ) { + this.vocabulary = data.vocabulary; + this.vocab = data.vocabulary.domain; + this.privateTerm.value = data.term; + this.privateTerm.display = data.term; + this.asDialog = true; + this.adminPanel = data.adminPanel; + } + + + @Input() + set term(val: any) { + if (val != null) { + this.privateTerm = val; + } + } + + get standardized(): boolean { + return this.privateTerm; + } + + close() { + this.dialogRef.close(); + } + + save() { + let extant = false; + this.vocabulary.terms.forEach(term => { + if (term.value === this.privateTerm.value) { + extant = true; + } + }); + if (!extant) { + let privateCopy = JSON.parse(JSON.stringify(this.privateTerm)); + delete privateCopy.simpleSrc; + delete privateCopy.fragmentSrc; + this.vocabulary.terms.push(privateCopy); + this.CVService.validateVocab(this.vocabulary).subscribe(response => { + if(response && response.valid) { + this.CVService.addVocabTerm( this.vocabulary).subscribe (response => { + if (response.terms && response.terms.length === this.vocabulary.terms.length) { + this.message = 'Term ' + this.privateTerm.value + ' Added to ' + this.vocabulary.domain + ''; + setTimeout(() => {this.dialogRef.close(this.privateTerm); }, 3000); + } + }, error => { + this.vocabulary.terms.pop(); + let str = 'Server Error'; + if (error.error && error.error.message) { + str += ' - ' + error.error.message; + + } + else if(error.message) { + str += ' - ' + error.message; + } + this.message = str; + + }); + + } else { + if(response.validationMessages) { + response.validationMessages.forEach(message => { + this.validationMessages.push(message); + }); + } + this.vocabulary.terms.pop(); + } + },error => { + console.log(error); + this.vocabulary.terms.pop(); + let str = 'Validation Error'; + if (error.error && error.error.message) { + str += ' - ' + error.error.message; + + } + else if(error.message) { + str += ' - ' + error.message; + } + this.message = str; + + }); + + } else { + this.message = 'Term already exists'; + setTimeout(() => { + this.message = ''; + }, 1000); + } + } + + ngOnInit(): void { + if (this.privateTerm.simplifiedStructure) { + this.privateTerm.simpleSrc = this.CVService.getStructureUrl(this.privateTerm.simplifiedStructure); + } + if (this.privateTerm.fragmentStructure) { + this.privateTerm.fragmentSrc = this.CVService.getStructureUrl(this.privateTerm.fragmentStructure); + } + this.overlayContainer = this.overlayContainerService.getContainerElement(); + + } + + + molvecUpdate(mol: any) { + this.editor.setMolecule(mol); + } + + editorOnLoad(editor: Editor): void { + this.loadingService.setLoading(false); + this.editor = editor; + if(this.privateTerm.value && this.privateTerm.value.fragmentStructure) { + const pos = this.privateTerm.value.fragmentStructure.substring(0, this.privateTerm.value.fragmentStructure.indexOf(' ')); + this.structureService.interpretStructure(pos).subscribe(response => { + if (response.structure && response.structure.molfile) { + + let test = response.structure.molfile; + test = test.replace(/ A /g, ' * '); + this.editor.setMolecule(test); + } + }); + } else { + + } + setTimeout(() => { + // re-adjust z-index after editor messes it up + this.overlayContainer.style.zIndex = '1003'; + + this.overlayContainer.style.zIndex = '10003'; + }); + } + + getCombination(ll, i) { + var cur = ll; + var ret = []; + + for (var i2 = 0; i2 < ll.length; i2++) { + var elm = i % cur.length; + ret.push(cur[elm]); + var rr = []; + + for (var n = 0; n < cur.length; n++) { + if (n !== elm) { + rr.push(cur[n]); + } + } + + cur = rr; + } + + return ret; + } + + fact(i) { + var t = 1; + + for (; i > 1; i--) { + t = t * i; + } + + return t; + } + + forEachCombination(ll, c) { + for (var i = 0; i < this.fact(ll.length); i++) { + c(this.getCombination(ll, i)); + } + } + + + getPossibleSmiles(smi) { + + function getMarkers(smi) { + let temp = smi.replace(/@H/g, '').replace(/[^A-Z*]/g, ''); + var alias: any = { + list: [] + }; + + + + alias.smiles = smi; + alias.stars = []; + + alias.add = function (a) { + alias.list.push(a); + + if (a === '*') { + alias.stars.push(alias.list.length - 1); + } + return alias; + }; + + alias.asAlias = function () { + return '|$' + alias.list.join(';') + '$|'; + }; + + function fact(i) { + var t = 1; + + for (; i > 1; i--) { + t = t * i; + } + + return t; + } + + function getCombination(ll, i) { + var cur = ll; + var ret = []; + + for (var i2 = 0; i2 < ll.length; i2++) { + var elm = i % cur.length; + ret.push(cur[elm]); + var rr = []; + + for (var n = 0; n < cur.length; n++) { + if (n !== elm) { + rr.push(cur[n]); + } + } + + cur = rr; + } + + return ret; + } + + function forEachCombination(ll, c) { + for (var i = 0; i < fact(ll.length); i++) { + c(getCombination(ll, i)); + } + } + + alias.eachForm = function (labs) { + var tot = []; + forEachCombination(labs, function (a) { + for (var i = 0; i < a.length; i++) { + alias.list[alias.stars[i]] = a[i]; + } + + tot.push(alias.asFullSmiles()); + }); + return tot; + }; + + alias.asFullSmiles = function () { + return alias.smiles + ' ' + alias.asAlias(); + }; + + for (var i in temp) { + if (temp[i] === '*') { + alias.add('*'); + } else { + alias.add(''); + } + } + return alias; + } + + return getMarkers(smi); + } + + fragmentType(domain: any, current?: any) { + let selected = null; + if(!current) { + this.domains.forEach(val => { + if (val.domain === domain) { + + selected = val; + } + }); + } else { + selected = this.vocabulary; + } + var rgs = _.chain(selected.terms).map(function (t) { + return t.fragmentStructure; + }).filter(function (t) { + return typeof t !== 'undefined'; + }).map(function (t) { + return t.split(' ')[1]; + }).filter(function (t) { + return typeof t !== 'undefined'; + }).flatMap(function (t) { + return t.replace(/[|]/g, '').replace(/[$]/g, '').split(';'); + }).uniq().filter(function (t) { + return t.indexOf('_') == 0; + }).value(); + + + + + var tt = this.getPossibleSmiles(this.editor.getSmiles()); + let stars = 0; + let dom = ""; + // this.vocab is only used when not editing in admin menu, otherwise full vocabulary is sent since it was fetched earlier + if (this.vocabulary) { + dom = this.vocabulary.domain; + } else { + dom = this.vocab; + } + + + + switch (dom) { + case 'NUCLEIC_ACID_LINKAGE' : stars = 2; break; + case 'NUCLEIC_ACID_BASE' : stars = 1; break; + case 'NUCLEIC_ACID_SUGAR' : stars = 3; break; + case 'AMINO_ACID_RESIDUE' : stars = 2; break; + } + + if (tt.stars.length <= 0) { + alert('No star atoms specified, expecting:' + stars + ' star atoms. Use the star atom selector under the periodic table menu to set'); + return; + } else if (tt.stars.length != stars) { + alert('Expected ' + stars + ' star atoms, but found:' + tt.stars.length); + } + var smilesforms = tt.eachForm(rgs); + this.forms = []; + smilesforms.forEach(form => { + let temp = {'value':form, 'url':this.CVService.getStructureUrl(form)}; + this.forms.push(temp); + }); + + } + + getFragmentCV() { + if (this.editor.getSmiles() && this.editor.getSmiles() !== '') { + + if(!this.vocabulary) { + this.CVService.getFragmentCV().subscribe(data => { + this.dat = {}; + + this.domains = data.content; + + if (this.vocab) { + this.fragmentType(this.vocab); + } + + + }); + } else { + if (this.vocab) { + this.fragmentType(this.vocab, this.vocabulary); + } + } + } else { + this.message = "No Structure Detected in editor"; + setTimeout(() => { + this.message = null; + }, 4000); + } + + + } + checkImg(term: any) { + term.fragmentSrc = this.CVService.getStructureUrlFragment(term.fragmentStructure); + term.simpleSrc = this.CVService.getStructureUrlFragment(term.simplifiedStructure); + + } + setTermStructure(structure) { + this.privateTerm.fragmentStructure = structure; + this.privateTerm.simplifiedStructure = structure.substring(0, structure.indexOf(' '));; + this.checkImg(this.privateTerm); + this.termUpdated.emit(this.privateTerm); + this.forms = []; + if(this.adminPanel) { + this.dialogRef.close(structure); + } + } +} diff --git a/src/app/core/admin/import-browse/bulk-action-dialog/bulk-action-dialog.component.html b/src/app/core/admin/import-browse/bulk-action-dialog/bulk-action-dialog.component.html new file mode 100644 index 000000000..05a511474 --- /dev/null +++ b/src/app/core/admin/import-browse/bulk-action-dialog/bulk-action-dialog.component.html @@ -0,0 +1,69 @@ +
+

Bulk Staging Actions

+
+
+ +
+ + {{total - altStatusCount}} Staged Records, {{total}} Selected Total

+ Bulk actions will only apply records with a 'staged' import status
+ If checked, non 'staged' records can still be deleted after an action is selected
+
+ + {{total}} Selected Records

+
+ + + + + +
+


No Records Selected for bulk action. Click the checkbox next to a record in the results to select

+ +
+ Use Cleaning Options +
+
+ Delete from staging area after Action +
+ + + + + + + + + + + + + + +
Selected + Name {{list.name}}
+ +
+
+ + +

+
+ Progress: {{completedRecordCount}} records processed +
+
+
+ Import Action Successful. +

Selected records deleted from staging area.
+
+ +
+ +
\ No newline at end of file diff --git a/src/app/core/admin/import-browse/bulk-action-dialog/bulk-action-dialog.component.scss b/src/app/core/admin/import-browse/bulk-action-dialog/bulk-action-dialog.component.scss new file mode 100644 index 000000000..834a98c84 --- /dev/null +++ b/src/app/core/admin/import-browse/bulk-action-dialog/bulk-action-dialog.component.scss @@ -0,0 +1,24 @@ +.spinner { + width: 100px !important; +height: 100px !important; +margin: auto; +} + +.action-button { + margin-right: 10px; +font-size: 18px; +} + +.form-row { + display: flex; + width: 100%; + flex-wrap: wrap; +} + +.row { + margin: 15px; +} + +.scrubber-row { + margin: 15px; +} \ No newline at end of file diff --git a/src/app/core/admin/import-browse/bulk-action-dialog/bulk-action-dialog.component.spec.ts b/src/app/core/admin/import-browse/bulk-action-dialog/bulk-action-dialog.component.spec.ts new file mode 100644 index 000000000..516fe11e3 --- /dev/null +++ b/src/app/core/admin/import-browse/bulk-action-dialog/bulk-action-dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BulkActionDialogComponent } from './bulk-action-dialog.component'; + +describe('BulkActionDialogComponent', () => { + let component: BulkActionDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ BulkActionDialogComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(BulkActionDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/admin/import-browse/bulk-action-dialog/bulk-action-dialog.component.ts b/src/app/core/admin/import-browse/bulk-action-dialog/bulk-action-dialog.component.ts new file mode 100644 index 000000000..9afefaf51 --- /dev/null +++ b/src/app/core/admin/import-browse/bulk-action-dialog/bulk-action-dialog.component.ts @@ -0,0 +1,174 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { ImportDialogComponent } from '@gsrs-core/admin/import-management/import-dialog/import-dialog.component'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { LoadingService } from '@gsrs-core/loading'; +import { AdminService } from '@gsrs-core/admin/admin.service'; +import { ConfigService } from '@gsrs-core/config'; + +@Component({ + selector: 'app-bulk-action-dialog', + templateUrl: './bulk-action-dialog.component.html', + styleUrls: ['./bulk-action-dialog.component.scss'] +}) +export class BulkActionDialogComponent implements OnInit { + records: any; + filtered: Array = []; + total: number; + successful = false; + displayedColumns = ['ID', 'name']; + loading = false; + useScrubber = false; + scrubberModel: any; + deleteStaged = true; + altStatusCount = 0; + completedRecordCount = 0; + showMerge = false; + constructor( + + public dialogRef: MatDialogRef, + public loadingService: LoadingService, + private adminService: AdminService, + private configService: ConfigService, + + + @Inject(MAT_DIALOG_DATA) public data: any + ) { + this.records = data.records; + this.scrubberModel = data.scrubberModel; + if(this.scrubberModel) { + this.useScrubber = true; + } + if (this.records) { + this.filtered = []; + this.altStatusCount = 0; + Object.keys(this.records).forEach(record => { + if(this.records[record].checked) { + let temp = {}; + if (this.records[record].substance) { + temp = {"ID": record, "checked": true, "name": this.records[record].substance._name}; + } else { + temp = {"ID": record, "checked": true, "name": this.records[record].name}; + } + this.filtered.push(temp); + /*f (this.records[record].substance && this.records[record].substance._metadata && this.records[record].substance._metadata.importStatus + && this.records[record].substance._metadata.importStatus !== 'staged') { + this.altStatusCount++; + }*/ + } + }); + this.total = this.filtered.length; + } + } + + + + ngOnInit(): void { + if (this.configService.configData && this.configService.configData.stagingArea) { + if (this.configService.configData.stagingArea.mergeAction) { + this.showMerge = this.configService.configData.stagingArea.mergeAction + } + } + } + + deleteStagedRecords(): void { + let toSend = []; + this.filtered.forEach(item => { + if (item.checked) { + toSend.push(item.ID); + } + }); + this.adminService.deleteStagedRecord(toSend).subscribe(response => { + this.successful = true; + this.loading = false; + console.log(response); + }, error => { + console.log(error); + this.successful = true; + this.loading = false; + }) + } + + checked(event: any, list: any) { + list.checked = !list.checked; + if(event.checked) { + this.total++; + } else { + this.total--; + } + } + + doAction(action: string, mergeID?: string) { + let toSend = []; + this.filtered.forEach(item => { + if (item.checked) { + toSend.push({'id':item.ID}); + } + }); + let scrubber = null; + if (this.useScrubber) { + scrubber = this.scrubberModel; + } + + this.loading = true; + this.adminService.stagedRecordMultiAction(toSend, action, scrubber).subscribe(result => { + if (result.jobStatus === 'completed') { + this.successful = true; + this.loading = false; + } else { + setTimeout(() => { + this.processingstatus(result.id); + }, 200); + } + + + }, error => { + alert("Error - see console for details"); + this.loading = false; + + }); + } + + close(param?: string) { + + this.filtered.forEach(record => { + this.records[record.ID].checked = record.checked; + }) + if (this.successful) { + window.location.reload(); + setTimeout(() => { + this.dialogRef.close(); + }, 100); + } + if (param && param == 'save') { + this.dialogRef.close(this.records); + + } else { + this.dialogRef.close(); + + } + } + + processingstatus(id: string): void { + this.adminService.processingstatus(id).subscribe(response => { + if (response.jobStatus === 'completed') { + if(this.deleteStaged) { + this.deleteStagedRecords(); + } else { + this.successful = true; + this.loading = false; + } + + } else { + if (response && response.completedRecordCount) { + this.completedRecordCount = response.completedRecordCount; + } + setTimeout(() => { + this.processingstatus(id); + }, 200); + } + + + }); + } + +} diff --git a/src/app/core/admin/import-browse/import-browse.component.html b/src/app/core/admin/import-browse/import-browse.component.html new file mode 100644 index 000000000..0a431f45f --- /dev/null +++ b/src/app/core/admin/import-browse/import-browse.component.html @@ -0,0 +1,653 @@ + + + + + + + +
+
+ Your search did not return any results. Please try modifying it or + click here to clear all your search criteria. +
+
+ + +
+ + + +
Bulk Search Summary (Full Results)
+
+
+
+
+
+ +
+ Clear + + | Edit + + Bulk Search +
+ +
+
+
+ +
+
+ + +
+
+
+ Search Query:  + {{this.searchTerm}} +
+ + +
+ + +
+
+ +
+
+ + + {{facet.type}}: + + + {{facet.val}} + +
+
+ +
+
+ +
+
+ {{structureSearchTerm && searchType}} Query: +   + + + + +  ≥ {{searchCutoff}} + + +
+
+ + + + + + +
+
+
+
+ Sequence Query: +   + + {{getSequenceDisplay(sequenceSearchTerm)}} + +
+
+ + +
+
+
+
+ +
+ +
+
+

Results below are an incomplete preview

+
+
+ +
+

searching... {{totalSubstances}} matches

+ + +
+

Page will auto-reload when search is complete

+
+
+
+
+ Staged Substances       + New Data Import + +
+ + + + + + + +
+
+
+
+ + + + + + + +
+ + + + +
+ Page: + + + + of {{lastPage}} +
+
+
+
+ + + + +     +     + + + +
+ + +
+
+
+
+
+ + +
+
+
+ +
+
+ + + + + + +
+
+
+
+ + {{substance.structure.stereochemistry}} + +
+
+
+
+ + {{substance.substanceClass | facetDisplay: 'types' | uppercase}} + +
+
+
+
+ + + +
+
+ +
+
+
+ similarity: {{substance._matchContext.similarity.toFixed(3)}} +
+
+ + + + +
+
+ + + + + + + + + + + +
+ +
+ + + + + + Download JSON + + + + Download Molfile + + + Download Molfile + + + Download Fasta + + + + + + Search Structure + + + Search Structure + + + + + + Copy Substance to New Form + + + Copy Definition to New Form + + +
+
+
+
+
+
+
+ + +
+ +
+
+
+ +
+
+
+
+ + +
+
+ \ No newline at end of file diff --git a/src/app/core/admin/import-browse/import-browse.component.scss b/src/app/core/admin/import-browse/import-browse.component.scss new file mode 100644 index 000000000..ac7bb4848 --- /dev/null +++ b/src/app/core/admin/import-browse/import-browse.component.scss @@ -0,0 +1,759 @@ +::ng-deep .mat-expansion-panel-content > .mat-expansion-panel-body { + padding: 0 12px 10px; +} + +.dynamic-container { + width: 100%; + min-height:5px; +} + +::ng-deep .facetView > * { + z-index: 1002 !important; +} + +::ng-deep .facet-value-label { + overflow: auto; + overflow-wrap: break-word; +} + +.smiles-input { + max-width:350px; +} + +.mat-expansion-panel-header { + padding: 0 12px; +} + +.filter-box { + padding: 10px; + + &:not(:last-child) { + border-bottom: 1px solid #d9d9d9; + } +} + +.wildcard-div { + margin: 10px 30px; + width: 20%; + display: inline-flex; +} + +.wildcard { + width: 100%; + height: 50%; + background: var(--regular-transparent-color); + border-top-style: hidden; + border-right-style: hidden; + border-left-style: hidden; + border-bottom-style: groove; + border-color: var(--pale-border-color-rgb-3); +} + +.background-button { + background-color: #ebf1f6; +} + +.button-margin { + margin-right: 0px !important; +} + +.bulk-action-button { + margin-right: 140px; + margin-bottom: 10px; + margin-top: -10px; + font-size: 16px; +} + +.top-button { + font-size: 20px; + margin-left: 150px; +padding-top: 4px; +padding-bottom: 4px; +width: 200px; +background-color: var(--primary-color); +color: white; +} + +.search-parameters { + display: flex; + width: 100%; + flex-wrap: wrap; + + &.center { + margin-top: 10px; + justify-content: center; + } + + & > div { + + padding-left:10px; + padding-right: 2px; + margin-left:5px; + margin-bottom: 5px; + display: flex; + align-items: center; + } + + .actions-container { + margin-left: auto; + flex-shrink: 0; + } + .selected-parameter { + padding-right:5px; + background-color: var(--regular-white-color); + font-size:14px; + } +} + +.controls-container { + display: flex; +} + +.mat-card { + max-width: 928px; + margin-bottom: 20px; +} + +.mat-card, .controls-container { + width: 100%; + box-sizing: border-box; +} + +.controls-container { + display: flex; + align-items: center; + + .mat-paginator { + margin-left: auto; + } +} + +.cards { + + .substance-cards { + display: block; + } + + .substance-table { + display: none; + } + .substance-tiles{ + display: none; + } +} + +.table { + .substance-cards { + display: none; + } + + .substance-table { + display: block; + } + + .substance-tiles{ + display: none; + } +} + +.tiles { + .substance-cards { + display: none; + } + + .substance-table { + display: none; + } + + .substance-tiles{ + display: flex; + } + +} + +.mat-card-title { + display: flex; + box-sizing: border-box; + width: 100%; + flex-direction: row; + align-items: center; + white-space: nowrap; +} + +.mat-card-title { + white-space: normal; + + .substance-name { + color: var(--link-primary-color); + padding-right: 10px; + } + + .approval-id { + font-size: 16px; + color: var(--regular-red-color); + } +} +.table-view-name { + color: var(--link-primary-color); +} + +.tile-title { + height:19px; +} + +::ng-deep .mat-paginator-range-label { + font-weight: 550; +} + +.main-title { + font-size: 26px; + padding-left: 10px; + padding-right: 25px; + font-weight: 550; + padding-top: 10px; + color: var(--primary-color); + width: 285px; +} + +.mat-card-content { + + .mat-chip-list-container { + margin-left: -20px; + margin-bottom: 10px; + } + .mat-chip-list-container-2 { + margin-bottom: 10px; + } +} + +.substance-content { + display: flex; + flex-direction: row; +} + +.tile { + height:350px; + margin:10px; + padding:10px; + width: calc(25% - 20px); + min-width:200px; + max-width:230px; + align-content:center; + overflow:hidden; + text-overflow: ellipsis; +} + +.tile .image-thumbnail { + margin:auto; + margin-bottom:20px; + height:175px; + width:175px; +} + +.tile .structure-container { + width:100%; + padding-right:0px; +} + +.tile-name { + white-space:nowrap; + text-overflow: ellipsis; + overflow: hidden; + width:100%; + height:25px; +} + +.tile-name ::ng-deep { + svg { + margin-bottom: -10px; + } +} + +.substance-tiles{ + flex-flow: row wrap; +} + +.image-other { + width:175px; + height:175px; +} + +.substance-data { + display: flex; + flex-direction: row; + + &:not(:last-child) { + margin-bottom: 15px; + } + + .label { + font-weight: bold; + min-width: 100px; + } +} + +.structure-container { + padding-right: 10px; +} + +.no-results { + text-align: center; + margin-top: 30px; + font-size: 1.2em; +} + +.mat-paginator { + background: var(--regular-transparent-color); +} + +.image-thumbnail{ + height:150px; + width:150px; +} + +.space-bottom { + margin-bottom: 5px; +} + +.exact-matches-container { + margin-top: 80px; +} + +.exact-match-control { + display: flex; + flex-direction: column; + align-content: center; + justify-content: center; + align-items: center; + margin-bottom: 20px; + + button { + margin-top: 15px; + } +} + +.facet-advanced-options-link { + margin-top: 7px; + color: #448aff; +} + +.facet-search-container { + display: flex; + + .mat-form-field { + flex-grow: 1; + } +} + +.facet-search-loading.mat-progress-bar { + margin-top: -18px; + margin-bottom: 1.09em; +} + +.break { + flex-basis: 0%; + height: 0; + width:0px; + } + + .export { + margin: auto; +} + +.export-button { + color: var(--regular-black-color); + border-radius: 4px; +} + +.similarity { + padding-left: 10px; + font-family: Menlo,Monaco,Consolas,"Courier New",monospace; + color: var(--pink-span-color); +} + +.similarity-label { + font-style: italic; +} + +.menu-checkbox:hover { + background-color: var(--regular-white-color); +} +@media(max-width: 1750px) { + + .full-paginator { + width: 100%; + align-content: center; + } + .break { + flex-basis: 100%; + height: 0; + width: auto; + } + + .controls-container { + flex-wrap: wrap; + } + + .export { + margin: auto; + } +} + +@media(max-width: 1100px) { + .controls-container, .search-parameters { + padding-left: 30px; + } + + .controls-container { + flex-wrap: wrap; + } + + .side-nav-content { + padding-top: 10px; + } + + .export { + margin-right:30px; + } +} + +@media(max-width: 730px) { + .mat-card-title { + flex-direction: column; + align-items: flex-start; + + .substance-name { + margin-bottom: 10px; + } + } + + .page-selector { + display:none !important; + } + + .full-paginator { + min-width: 500px !important; + } + .substance-data { + flex-direction: column; + + .label { + margin-bottom: 10px; + } + + .value, .code-system { + padding-left: 20px; + } + } + + .search-parameters { + font-size: .9em; + + .mat-icon-button { + width: 35px; + height: 35px; + line-height: 35px; + } + } + + .side-nav-content { + padding: 5px; + } +} + +.lock-icon { + color: var(--lock-icon-color); +} + +.narrow-search-suggestions-container { + display: flex; + align-content: center; + align-items: center; + margin-bottom: 10px; + justify-content: center; + flex-direction: column; +} + +.flex-row { + width: 100%; + margin: auto; +} + + + +.narrow-search-suggestions { + padding: 12px 20px; + width: auto; + display: flex; + align-content: center; + align-items: center; + + .mat-flat-button { + margin-left: 7px; + } +} + +@media(max-width: 600px) { + + .substance-content { + flex-direction: column; + + .image-thumbnail { + margin: 0 auto; + } + } + + .controls-container { + + ::ng-deep { + + .mat-paginator-range-l.references-link{ + display: inline-flex; + vertical-align: middle; +}abel { + margin: 0 3px 0 0; + } + + .mat-paginator-page-size-select { + margin-left: 0; + } + + .mat-paginator-container { + padding-right: 0; + padding-left: 0; + } + + .mat-paginator-page-size { + margin-right: 4px; + } + + .mat-paginator-range-actions { + + .mat-icon-button { + width: 35px; + height: 35px; + line-height: 35px; + } + } + } + } +} + +@media(max-width: 505px) { + .full-paginator { + min-width: 0px !important; + } + .controls-container { + + ::ng-deep { + + .mat-paginator-page-size-label { + display: none; + } + } + } +} + +.zoom:hover{ + cursor:zoom-in; +} + +.icon-align{ + margin-top: -10px; + vertical-align: bottom; +} + +.ext-link{ + color: var(--link-primary-color); + text-decoration-style: unset; +} + +.sort{ + margin:auto; + margin-bottom: -7px; +} + +.advanced { + padding-left:20px; + color:#448aff; + +} + +.facet-options { + width: 100%; + display:flex; +} + +.tile-button-container { + display: flex; + flex-direction: row; + justify-content: space-evenly +} + +.mat-elevation-z2 { + background-color: var(--regular-white-color); +} + +.more-content { + width:45%; +} + +.advanced-container { + width:55%; + align-content:right; +} + +.selected-container { + font-size:14px; +} + +.selected-label { + padding-right:5px; +} + +.reset-facets-button { + margin-left: 5px; + margin-bottom: 5px; +} + +.selected-parameter { + height:auto; + padding-top: 3px; + padding-bottom: 3px; +} + +.selected-container { + max-width:700px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.selected-label { + max-width:200px; + overflow: hidden; + text-overflow: ellipsis; +} + +.not-icon { + height:18px !important; + width: 18px !important; + vertical-align: bottom; +} +.page-select { + width:100px; +} + +::ng-deep .auto-width{ + ::ng-deep .mat-form-field { + width: auto !important; + } + ::ng-deep .mat-select-value { + max-width: 100%; + width: auto; + } +} + +.page-label { + color:var(--dark-label-color); + display:block; + font-family:Roboto, "Helvetica Neue", sans-serif; + font-size:14px; + padding-right: 12px; +} + +.page-selector { + display: flex; + flex-direction: row; + align-items: center; +margin-left: 20px; + } + + + :host ::ng-deep .mat-paginator-range-label{ + margin: 0 5px 0 5px !important; + + } + + mat-paginator { + font-size:16px; + } + + .page-input { + width: 50px; + font-size:14px; + } + +.bad-page { + color: var(--regular-red-color); +} + +.full-paginator { + display: flex; + min-width: 660px; +} + +.disable-export { + pointer-events:none; + opacity:.5; +} + +.button-link-img { + line-height: 40px; + color: var(--regular-black-color); +} +.red-text { + color: var(--regular-red-color); + font-style: italic; +} +.orange-text { + color: var(--regular-orange-color); + font-style: italic; +} +.sub-search-div { + display: none; // remove to see UI added +} +.sub-search-ref-btn { + padding: 10px; + margin: 0 5px; + background-color: var(--regular-white-color); + box-shadow: 2px 2px var(--regular-lightgray-color); + border: 0.4px solid var(--regular-white-color); + border-radius: 5px; +} +.sub-search-cancel-btn { + padding: 10px; + color: var(--regular-white-color); + background-color: var(--regular-red-color); + box-shadow: 2px 2px var(--regular-lightgray-color); + border: 0.4px solid var(--regular-red-color); + border-radius: 5px; +} +.sub-search-text { + text-align: center; + // margin: 7px; +} +.match-txt { + display: inline; + padding: 0 5px; +} +.sub-search-text-div { + text-align: center; +} +.search-spinner { + width: 15px; + height: 15px; + display: inline-table; + margin: auto; +} +// .mat-progress-spinner circle, .mat-spinner circle { +// stroke: grey; +// } +.search-spinner ::ng-deep .mat-progress-spinner circle, .mat-spinner circle { + stroke: var(--regular-grey-color); + width: 10px !important; + height: 10px !important; + display: inline; +} +.cdk-overlay-backdrop, .cdk-global-overlay-wrapper { + display: none !important; +} diff --git a/src/app/core/admin/import-browse/import-browse.component.spec.ts b/src/app/core/admin/import-browse/import-browse.component.spec.ts new file mode 100644 index 000000000..b1753a913 --- /dev/null +++ b/src/app/core/admin/import-browse/import-browse.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ImportBrowseComponent } from './import-browse.component'; + +describe('ImportBrowseComponent', () => { + let component: ImportBrowseComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ImportBrowseComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ImportBrowseComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/admin/import-browse/import-browse.component.ts b/src/app/core/admin/import-browse/import-browse.component.ts new file mode 100644 index 000000000..f87773fc5 --- /dev/null +++ b/src/app/core/admin/import-browse/import-browse.component.ts @@ -0,0 +1,1294 @@ +import { + Component, + OnInit, + ViewChild, + AfterViewInit, + HostListener, + OnDestroy, + TemplateRef, + Inject, + ComponentFactoryResolver, + ViewChildren, + QueryList +} from '@angular/core'; +import { ActivatedRoute, Router, NavigationExtras, Params } from '@angular/router'; +import * as _ from 'lodash'; +import { MatDialog } from '@angular/material/dialog'; +import { PageEvent } from '@angular/material/paginator'; +import { MatSidenav } from '@angular/material/sidenav'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { Location } from '@angular/common'; +import { StructureService, StructureImageModalComponent } from '@gsrs-core/structure'; +import { Subscription } from 'rxjs'; +import { take } from 'rxjs/operators'; +import { NarrowSearchSuggestion, PagingResponse, searchSortValues, UtilsService } from '@gsrs-core/utils'; +import { FacetParam, Facet, FacetUpdateEvent } from '@gsrs-core/facets-manager'; +import { FacetsManagerService } from '@gsrs-core/facets-manager'; +import { DisplayFacet } from '@gsrs-core/facets-manager/display-facet'; +import { SubstanceTextSearchService } from '@gsrs-core/substance-text-search/substance-text-search.service'; +import { ExportDialogComponent } from '@gsrs-core/substances-browse/export-dialog/export-dialog.component'; +// eslint-disable-next-line max-len +import { BrowseHeaderDynamicSectionDirective } from '@gsrs-core/substances-browse/browse-header-dynamic-section/browse-header-dynamic-section.directive'; +import { DYNAMIC_COMPONENT_MANIFESTS, DynamicComponentManifest } from '@gsrs-core/dynamic-component-loader'; +import { SubstanceBrowseHeaderDynamicContent } from '@gsrs-core/substances-browse/substance-browse-header-dynamic-content.component'; +import { Title } from '@angular/platform-browser'; +import { ControlledVocabularyService } from '@gsrs-core/controlled-vocabulary'; +import { FormControl } from '@angular/forms'; +import { WildcardService } from '@gsrs-core/utils/wildcard.service'; +import { I } from '@angular/cdk/keycodes'; +import { SubstanceDetail, SubstanceName, SubstanceCode, SubstanceService } from '@gsrs-core/substance'; +import { ConfigService } from '@gsrs-core/config'; +import { SubBrowseEmitterService } from '@gsrs-core/substances-browse/sub-browse-emitter.service'; +import { LoadingService } from '@gsrs-core/loading'; +import { MainNotificationService, AppNotification, NotificationType } from '@gsrs-core/main-notification'; +import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; +import { AuthService } from '@gsrs-core/auth'; +import { environment } from '@environment/environment.cbg.prod'; +import { AdminService } from '@gsrs-core/admin/admin.service'; +import { Observable } from 'rxjs'; +import { Subject } from 'rxjs'; +import { BulkActionDialogComponent } from '@gsrs-core/admin/import-browse/bulk-action-dialog/bulk-action-dialog.component'; +import { ImportScrubberComponent } from '@gsrs-core/admin/import-management/import-scrubber/import-scrubber.component'; + +@Component({ + selector: 'app-import-browse', + templateUrl: './import-browse.component.html', + styleUrls: ['./import-browse.component.scss'] +}) +export class ImportBrowseComponent implements OnInit, AfterViewInit, OnDestroy { + private privateSearchTerm?: string; + private privateStructureSearchTerm?: string; + private privateSequenceSearchTerm?: string; + private privateBulkSearchQueryId?: number; + private privateBulkSearchStatusKey?: string; + private privateBulkSearchSummary?: any; + private privateSearchType?: string; + private privateSearchStrategy?: string; + private privateSearchCutoff?: number; + private privateSearchSeqType?: string; + private privateSequenceSearchKey?: string; + + public substances: Array = []; + records: Array = []; + + public exactMatchSubstances: Array; + + searchOnIdentifiers: boolean; + searchEntity: string; + pageIndex: number; + pageSize: number; + test: any; + pageCount: number; + invalidPage = false; + totalSubstances: number; + isLoading = true; + lastPage: number; + etag: string; + privateExport = false; + message: string; + disableExport = false; + isError = false; + isRefresher = false; + @ViewChildren(BrowseHeaderDynamicSectionDirective) dynamicContentContainer: QueryList; + @ViewChild('matSideNavInstance', { static: true }) matSideNav: MatSidenav; + hasBackdrop = false; + view = 'cards'; + displayedColumns: string[] = ['name', 'approvalID', 'names', 'codes', 'actions']; + public smiles: string; + private argsHash?: number; + public order: string; + public sortValues = searchSortValues; + showAudit: boolean; + private overlayContainer: HTMLElement; + private subscriptions: Array = []; + isAdmin = false; + isLoggedIn = false; + showExactMatches = false; + names: { [substanceId: string]: Array } = {}; + codes: { + [substanceId: string]: { + codeSystemNames?: Array + codeSystems?: { [codeSystem: string]: Array } + } + } = {}; + narrowSearchSuggestions?: { [matchType: string]: Array } = {}; + matchTypes?: Array = []; + narrowSearchSuggestionsCount = 0; + private isComponentInit = false; + sequenceID?: string; + searchHashFromAdvanced: string; + + // needed for facets + private privateFacetParams: FacetParam; + rawFacets: Array; + public displayFacets: Array = []; + private isFacetsParamsInit = false; + isCollapsed = true; + exportOptions: Array; + private searchTermHash: number; + isSearchEditable = false; + showDeprecated = true; + codeSystem: any; + previousState: Array = []; + facetViewCategorySelected = 'Default'; + facetDisplayType = 'facetView'; + facetViewCategory: Array = []; + facetViewControl = new FormControl(); + private wildCardText: string; + bulkSearchPanelOpen = false; + substanceList: any; + idMapping: Array< any > = []; + demoResp: any; + matches: Array; + bulkList: any = {}; + + scrubberSchema: any; + scrubberModel: any; + + + constructor( + private activatedRoute: ActivatedRoute, + private substanceService: SubstanceService, + public configService: ConfigService, + public emitService: SubBrowseEmitterService, + private loadingService: LoadingService, + private notificationService: MainNotificationService, + public utilsService: UtilsService, + private router: Router, + private dialog: MatDialog, + public gaService: GoogleAnalyticsService, + public authService: AuthService, + private structureService: StructureService, + private overlayContainerService: OverlayContainer, + private location: Location, + private facetManagerService: FacetsManagerService, + private componentFactoryResolver: ComponentFactoryResolver, + private substanceTextSearchService: SubstanceTextSearchService, + private title: Title, + private cvService: ControlledVocabularyService, + private wildCardService: WildcardService, + private adminService: AdminService, + @Inject(DYNAMIC_COMPONENT_MANIFESTS) private dynamicContentItems: DynamicComponentManifest[], + + ) { + } + + @HostListener('window:popstate', ['$event']) + onPopState(event) { + setTimeout(() => { + if (this.router.url === this.previousState[0]) { + this.ngOnInit(); + } + + }, 50); + } + + saveWildCardText() { + this.wildCardService.getWildCardText(this.wildCardText); + } + + wildCardSearch() { + this.wildCardService.getWildCardText(this.wildCardText); + this.setUpPrivateSearchTerm(); + this.searchSubstances(); + } + + updateBulkList(event: any) { + let checked = event.checked; + let recordId = event.substance._metadata.recordId; + if (this.bulkList[recordId]) { + this.bulkList[recordId].checked = checked; + } else { + this.bulkList[recordId] = {"checked": checked, "substance": event.substance}; + } + + } + + bulkActionDialog() { + const dialogReference = this.dialog.open(BulkActionDialogComponent, { + maxHeight: '85%', + + width: '60%', + data: { 'records': this.bulkList, 'scrubberModel': this.scrubberModel } + }); + + this.overlayContainer.style.zIndex = '1002'; + + const exportSub = dialogReference.afterClosed().subscribe(response => { + if(response) { + this.bulkList = response; + } + this.overlayContainer.style.zIndex = null; + + }); + } + + selectBulk(type?: string) { + if(type && type === 'all') { + this.isLoading = true; + this.loadingService.setLoading(true); + const skip = this.pageIndex * this.pageSize; + const subscription = this.adminService.SearchStagedData(skip, this.privateFacetParams, this.privateSearchTerm, this.totalSubstances, 'selectable') + .subscribe(pagingResponse => { + let start = 0; + let skipped = 0; + let added = 0; + if (pagingResponse.content){ + + + pagingResponse.content.forEach(record => { + if ( record && record.id) { + if (this.bulkList[record.id]) { + if (! this.bulkList[record.id].checked) { + added++; + } + this.bulkList[record.id].checked = true; + } else { + this.bulkList[record.id] = {"checked": true, "name": record.name, "id": record.id}; + added++; + } + } else { + skipped++; + } + }); + } else { + this.isLoading = false; + this.loadingService.setLoading(false); + alert('Error: unable to retrieve staged response content'); + } + this.isLoading = false; + this.loadingService.setLoading(false); + }, error => { + console.log(error); + this.loadingService.setLoading(false); + this.isLoading = false; + alert('Error: unable to retrieve all staged results. See console for error details'); + }); + } else { + this.substances.forEach(record => { + if (record.id && this.bulkList[record.id]) { + this.bulkList[record.id].checked = true; + } else if (record._metadata.recordId && this.bulkList[record._metadata.recordId]) { + this.bulkList[record._metadata.recordId].checked = true; + } else if (record._metadata.recordId) { + this.bulkList[record._metadata.recordId] = {"checked": true, "name": record._name, "id": record._metadata.recordId}; + } + else { + this.bulkList[record.id] = {"checked": true, "name": record.name, "id": record.id}; + } + + }); + } + + + } + + deselectAll() { + Object.keys(this.bulkList).forEach(item => { + this.bulkList[item].checked = false; + }); + } + + openScrubber(templateRef:any, index: number): void { + const dialogref = this.dialog.open(ImportScrubberComponent, { + minHeight: '500px', + width: '800px', + data: { + scrubberSchema: this.scrubberSchema, + scrubberModel: this.scrubberModel + } + }); + this.overlayContainer.style.zIndex = '1002'; + + dialogref.afterClosed().subscribe(result => { + this.overlayContainer.style.zIndex = null; + + if(result) { + this.scrubberModel = result; + } + + }); + } + + ngOnInit() { + this.substances = []; + this.records = []; + + this.adminService.getImportScrubberSchema().subscribe(response => { + this.scrubberSchema = response; + }); + + this.gaService.sendPageView('Staging Area'); + this.cvService.getDomainVocabulary('CODE_SYSTEM').pipe(take(1)).subscribe(response => { + this.codeSystem = response['CODE_SYSTEM'].dictionary; + + }); + this.title.setTitle('Staging Area'); + + this.pageSize = 10; + this.pageIndex = 0; + + this.setUpPrivateSearchTerm(); + + this.privateStructureSearchTerm = this.activatedRoute.snapshot.queryParams['structure_search'] || ''; + this.privateSequenceSearchTerm = this.activatedRoute.snapshot.queryParams['sequence_search'] || ''; + this.privateSequenceSearchKey = this.activatedRoute.snapshot.queryParams['sequence_key'] || ''; + this.privateBulkSearchQueryId = this.activatedRoute.snapshot.queryParams['bulkQID'] || ''; + this.searchOnIdentifiers = (this.activatedRoute.snapshot.queryParams['searchOnIdentifiers']==="true") || false; + this.searchEntity = this.activatedRoute.snapshot.queryParams['searchEntity'] || ''; + + this.privateSearchType = this.activatedRoute.snapshot.queryParams['type'] || ''; + + this.setUpPrivateSearchStrategy(); + + + if (this.activatedRoute.snapshot.queryParams['sequence_key'] && this.activatedRoute.snapshot.queryParams['sequence_key'].length > 9) { + this.sequenceID = this.activatedRoute.snapshot.queryParams['source_id']; + this.privateSequenceSearchTerm = JSON.parse(sessionStorage.getItem('gsrs_search_sequence_' + this.sequenceID)); + } + this.privateSearchCutoff = Number(this.activatedRoute.snapshot.queryParams['cutoff']) || 0; + this.privateSearchSeqType = this.activatedRoute.snapshot.queryParams['seq_type'] || ''; + this.smiles = this.activatedRoute.snapshot.queryParams['smiles'] || ''; + this.order = this.activatedRoute.snapshot.queryParams['order'] || '$root_lastEdited'; + this.view = this.activatedRoute.snapshot.queryParams['view'] || 'cards'; + this.pageSize = parseInt(this.activatedRoute.snapshot.queryParams['pageSize'], null) || 10; + this.searchHashFromAdvanced = this.activatedRoute.snapshot.queryParams['g-search-hash']; + if (this.pageSize > 500) { + this.pageSize = 500; + } + this.pageIndex = parseInt(this.activatedRoute.snapshot.queryParams['pageIndex'], null) || 0; + this.overlayContainer = this.overlayContainerService.getContainerElement(); + const authSubscription = this.authService.getAuth().subscribe(auth => { + if (auth) { + this.isLoggedIn = true; + } else { + this.showDeprecated = true; + } + this.isAdmin = this.authService.hasAnyRoles('Updater', 'SuperUpdater'); + this.showAudit = this.authService.hasRoles('admin'); + + }); + this.facetManagerService.registerGetFacetsHandler(this.substanceService.getStagingFacets ); + + this.subscriptions.push(authSubscription); + this.isComponentInit = true; + this.loadComponent(); + + this.loadFacetViewFromConfig(); + + } + + getStagedRecords(skip?: any) { + this.adminService.GetStagedData(this.pageIndex).subscribe(response => { + this.substanceList = response.content; + this.totalSubstances = response.total; + response.content.forEach(record => { + this.adminService.GetStagedRecord(record.recordId).subscribe( resp => { + this.records.push(resp); + this.idMapping[resp.uuid] = record.recordId; + + }); + }); + }); + } + + setUpPrivateSearchTerm() { + this.privateSearchTerm = this.activatedRoute.snapshot.queryParams['search'] || ''; + if(this.wildCardText && this.wildCardText.length > 0) { + if(this.privateSearchTerm.length > 0) { + this.privateSearchTerm += ' AND "' + this.wildCardText + '"'; + } else { + this.privateSearchTerm += '"' + this.wildCardText + '"'; + } + } + if (this.privateSearchTerm) { + this.searchTermHash = this.utilsService.hashCode(this.privateSearchTerm); + this.isSearchEditable = localStorage.getItem(this.searchTermHash.toString()) != null; + } + } + + setUpPrivateSearchStrategy() { + // Setting privateSearchStrategy so we know what + // search strategy is used, for example so we can + // pass a value for use in cards. + // I think privateSearchType is used differently. + // I see searchType being used for 'similarity' + this.privateSearchStrategy = null; + if(this.privateStructureSearchTerm) { + this.privateSearchStrategy = 'structure'; + } else if(this.privateSequenceSearchTerm) { + this.privateSearchStrategy = 'sequence'; + } else if(this.privateBulkSearchQueryId) { + this.privateSearchStrategy = 'bulk'; + } + } + + ngAfterViewInit() { + const openSubscription = this.matSideNav.openedStart.subscribe(() => { + this.utilsService.handleMatSidenavOpen(1100); + }); + this.subscriptions.push(openSubscription); + const closeSubscription = this.matSideNav.closedStart.subscribe(() => { + this.utilsService.handleMatSidenavClose(); + }); + this.subscriptions.push(closeSubscription); + const dynamicSubscription = this.dynamicContentContainer.changes.subscribe((comps: QueryList) => { + const container = this.dynamicContentContainer.toArray(); + const dynamicContentItemsFlat = this.dynamicContentItems.reduce((acc, val) => acc.concat(val), []) + .filter(item => item.componentType === 'browseHeader'); + if (container[0] != null) { + const viewContainerRef = container[0].viewContainerRef; + viewContainerRef.clear(); + + dynamicContentItemsFlat.forEach(dynamicContentItem => { + const componentFactory = this.componentFactoryResolver.resolveComponentFactory(dynamicContentItem.component); + const componentRef = viewContainerRef.createComponent(componentFactory); + (componentRef.instance).test = 'testing'; + }); + } + }); + this.subscriptions.push(dynamicSubscription); + + } + + ngOnDestroy() { + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + this.facetManagerService.unregisterFacetSearchHandler(); + } + + @HostListener('window:resize', ['$event']) + onResize() { + this.processResponsiveness(); + } + + private loadComponent(): void { + + if (this.isFacetsParamsInit && this.isComponentInit || this.isRefresher) { + + this.searchSubstances(); + + } else { + + // There should be a better way to do this. + this.bulkSearchPanelOpen = + (this.privateSearchTerm ===undefined || this.privateSearchTerm ==='') + && (this.displayFacets && this.displayFacets.length===0); + } + } + + clipboard(value: string) { + document.addEventListener('copy', (e: ClipboardEvent) => { + e.clipboardData.setData('text/plain', (value)); + e.preventDefault(); + document.removeEventListener('copy', null); + }); + document.execCommand('copy'); + } + + changePage(pageEvent: PageEvent) { + + let eventAction; + let eventValue; + + if (this.pageSize !== pageEvent.pageSize) { + eventAction = 'select:page-size'; + eventValue = pageEvent.pageSize; + } else if (this.pageIndex !== pageEvent.pageIndex) { + eventAction = 'icon-button:page-number'; + eventValue = pageEvent.pageIndex + 1; + } + + this.gaService.sendEvent('substancesContent', eventAction, 'pager', eventValue); + + this.pageSize = pageEvent.pageSize; + this.pageIndex = pageEvent.pageIndex; + this.populateUrlQueryParameters(); + + this.searchSubstances(); + } + + customPage(event: any): void { + if (this.validatePageInput(event)) { + this.invalidPage = false; + const newpage = Number(event.target.value) - 1; + this.pageIndex = newpage; + this.gaService.sendEvent('substancesContent', 'select:page-number', 'pager', newpage); + this.populateUrlQueryParameters(); + this.searchSubstances(); + } + } + + validatePageInput(event: any): boolean { + if (event && event.target) { + const newpage = Number(event.target.value); + if (!isNaN(Number(newpage))) { + if ((Number.isInteger(newpage)) && (newpage <= this.lastPage) && (newpage > 0)) { + return true; + } + } + } + return false; + } + // for facets + facetsParamsUpdated(facetsUpdateEvent: FacetUpdateEvent): void { + this.pageIndex = 0; + if (facetsUpdateEvent.deprecated && facetsUpdateEvent.deprecated === true) { + this.showDeprecated = true; + } else { + this.showDeprecated = false; + } + this.privateFacetParams = facetsUpdateEvent.facetParam; + this.displayFacets = facetsUpdateEvent.displayFacets.filter(facet => !(facet.type === 'Deprecated' && facet.bool === false)); + if (!this.isFacetsParamsInit) { + this.isFacetsParamsInit = true; + this.loadComponent(); + } else { + + this.searchSubstances(); + } + } + + facetViewChange(event): void { + this.facetViewCategorySelected = event.value; + } + + openedSortSubstances(event: any) { + if (event) { + this.overlayContainer.style.zIndex = '1002'; + } else { + this.overlayContainer.style.zIndex = '1000'; + } + } + + openedFacetViewChange(event: any) { + if (event) { + this.overlayContainer.style.zIndex = '1002'; + } else { + this.overlayContainer.style.zIndex = '1000'; + } + } + + loadFacetViewFromConfig() { + this.facetViewControl.setValue(this.facetViewCategorySelected); + const facetConf = this.configService.configData.facets && this.configService.configData.facets['substances'] || {}; + facetConf['facetView'].forEach(categoryRow => { + const category = categoryRow['category']; + this.facetViewCategory.push(category); + }); + this.facetViewCategory.push('All'); + } + + // for facets + facetsLoaded(numFacetsLoaded: number) { + if (numFacetsLoaded > 0) { + this.processResponsiveness(); + } else { + this.matSideNav.close(); + } + } + + getRecord(id: string): Observable { + let subject = new Subject(); + let ids = []; + let sources = []; + this.adminService.GetStagedRecord(id).subscribe(response => { + this.idMapping[response.uuid] = id; + response._matches.matches.forEach(match => { + match.matchingRecords.forEach(matchRec => { + if (matchRec.sourceName == 'GSRS' || matchRec.sourceName == 'Staging Area') { + if (!ids[matchRec.recordId.idString]) { + ids[matchRec.recordId.idString] = [matchRec.matchedKey]; + sources[matchRec.recordId.idString] = matchRec.sourceName; + } else { + ids[matchRec.recordId.idString].push(matchRec.matchedKey); + sources[matchRec.recordId.idString] = matchRec.sourceName; + + } + } + + }); + }); + let items = []; + Object.keys(ids).forEach(key => { + let temp = {'ID':key, + 'records':ids[key], + 'source':sources[key] + }; + items.push(temp); + }); + response.matchedRecords = items; + subject.next(response); + + }, error => { + this.idMapping[this.demoResp.uuid] = id; + let response = JSON.parse(JSON.stringify(this.demoResp)); + response._matches.matches.forEach(match => { + match.matchingRecords.forEach(matchRec => { + if (!ids[matchRec.recordId.idString]) { + ids[matchRec.recordId.idString] = [matchRec.matchedKey]; + } else { + ids[matchRec.recordId.idString].push(matchRec.matchedKey); + + } + }); + }); + let items = []; + Object.keys(ids).forEach(key => { + let temp = {'ID':key, + 'records':ids[key]}; + items.push(temp); + }); + + response.matchedRecords = items; + subject.next(response); + + }); + return subject.asObservable(); + } + + organizeMatches() { + this.matches = []; + this.records.forEach(record => { + let ids = []; + record._matches.matches.forEach(match => { + match.matchingRecords.forEach(matchRec => { + if (!ids[matchRec]) { + ids[matchRec] = [matchRec.matchedKey]; + } else { + ids[matchRec].push(matchRec.matchedKey); + } + }); + }); + record.matchedRecords = ids; + }); + } + + searchforIDs() { + // if (this.argsHash == null || this.argsHash !== newArgsHash) { + this.isLoading = true; + this.loadingService.setLoading(true); + const skip = this.pageIndex * this.pageSize; + const subscription = this.adminService.SearchStagedData(skip, this.privateFacetParams, this.privateSearchTerm, this.totalSubstances) + .subscribe(pagingResponse => { console.log(pagingResponse)}); + } + + searchSubstances() { + this.disableExport = false; + const newArgsHash = this.utilsService.hashCode( + this.privateSearchTerm, + this.privateStructureSearchTerm, + this.privateSequenceSearchTerm, + this.privateBulkSearchQueryId, + this.privateSearchCutoff, + this.privateSearchType, + this.privateSearchSeqType, + this.pageSize, + this.order, + this.privateFacetParams, + (this.pageIndex * this.pageSize), + this.showDeprecated + ); + // if (this.argsHash == null || this.argsHash !== newArgsHash) { + this.isLoading = true; + this.loadingService.setLoading(true); + this.argsHash = newArgsHash; + const skip = this.pageIndex * this.pageSize; + const subscription = this.adminService.SearchStagedData(skip, this.privateFacetParams, this.privateSearchTerm, this.pageSize) + .subscribe(pagingResponse => { + this.substances = []; + this.records = []; + if (pagingResponse.total == 0 && pagingResponse.count == 0) { + // alert('Error: response had a count and total of 0, using demonstration data'); + // pagingResponse = this.setDemo2(); + } + this.isError = false; + this.totalSubstances = pagingResponse.total; + // this.pageSize = 10; + if (this.totalSubstances % this.pageSize === 0) { + this.lastPage = (this.totalSubstances / this.pageSize); + } else { + this.lastPage = Math.floor(this.totalSubstances / this.pageSize + 1); + } + + + pagingResponse.content.forEach(entry => { + this.getRecord(entry._metadata.recordId).subscribe(response => { + this.substances.push(response); + this.records.push(response); + }); + }); + this.organizeMatches(); + if (pagingResponse.facets && pagingResponse.facets.length > 0) { + this.rawFacets = pagingResponse.facets; + } + this.narrowSearchSuggestions = {}; + this.matchTypes = []; + this.narrowSearchSuggestionsCount = 0; + this.loadingService.setLoading(false); + + // this.substanceService.setResult(pagingResponse.etag, pagingResponse.content, pagingResponse.total); + }, error => { + this.gaService.sendException('getSubstancesDetails: error from API call'); + const notification: AppNotification = { + message: 'There was an error trying to retrieve substances. Please refresh and try again.', + type: NotificationType.error, + milisecondsToShow: 6000 + }; + this.isError = true; + this.isLoading = false; + this.loadingService.setLoading(false); + /* const pagingResponse = this.setDemo2(); + + this.totalSubstances = pagingResponse.total; + this.pageSize = 10; + if (this.totalSubstances % this.pageSize === 0) { + this.lastPage = (this.totalSubstances / this.pageSize); + } else { + this.lastPage = Math.floor(this.totalSubstances / this.pageSize + 1); + } + this.substances = []; + this.records = []; + pagingResponse.content.forEach(entry => { + this.getRecord(entry.recordId).subscribe(response => { + this.substances.push(response); + this.records.push(response); + console.log(response); + + }); + }); + this.organizeMatches(); + // this.substances = pagingResponse.content; + this.totalSubstances = this.substances.length; + if (pagingResponse.facets && pagingResponse.facets.length > 0) { + this.rawFacets = pagingResponse.facets; + } + this.narrowSearchSuggestions = {}; + this.matchTypes = []; + this.narrowSearchSuggestionsCount = 0;*/ + + }, () => { + subscription.unsubscribe(); + /* + this.substances.forEach(substance => { + this.setSubstanceNames(substance.uuid); + this.setSubstanceCodes(substance.uuid); + });*/ + this.isLoading = false; + this.loadingService.setLoading(this.isLoading); + }); + // } + + } + +sortMatchTypes(a:Array) { + return _.sortBy(a); +} + +searchTermOkforBeginsWithSearch(): boolean { + return (this.privateSearchTerm && !this.utilsService.looksLikeComplexSearchTerm(this.privateSearchTerm)); +} + + + + restricSearh(searchTerm: string): void { + this.privateSearchTerm = searchTerm; + this.searchTermHash = this.utilsService.hashCode(this.privateSearchTerm); + this.isSearchEditable = localStorage.getItem(this.searchTermHash.toString()) != null; + this.populateUrlQueryParameters(); + // this.searchSubstances(); + this.substanceTextSearchService.setSearchValue('main-substance-search', this.privateSearchTerm); + } + + export(url: string, extension: string) { + if (this.authService.getUser() !== '') { + const dialogReference = this.dialog.open(ExportDialogComponent, { + maxHeight: '85%', + + width: '60%', + data: { 'extension': extension } + }); + + this.overlayContainer.style.zIndex = '1002'; + + const exportSub = dialogReference.afterClosed().subscribe(response => { + const name = response.name; + const id = response.id; + this.overlayContainer.style.zIndex = null; + if (name && name !== '') { + // this.loadingService.setLoading(true); + const fullname = name + '.' + extension; + this.authService.startUserDownload(url, this.privateExport, fullname, id).subscribe(response => { + // this.substanceService.getConfigByID(id).subscribe(resp =>{ + // }); + this.loadingService.setLoading(false); + this.loadingService.setLoading(false); + const navigationExtras: NavigationExtras = { + queryParams: { + totalSub: this.totalSubstances + } + }; + const params = { 'total': this.totalSubstances }; + this.router.navigate(['/user-downloads/', response.id]); + }, error => this.loadingService.setLoading(false)); + } + }); + } else { + this.disableExport = true; + } + + } + + setSubstanceNames(substanceId: string): void { + this.substanceService.getSubstanceNames(substanceId).pipe(take(1)).subscribe(names => { + this.names[substanceId] = names; + }, error => { + this.names[substanceId] = []; + }); + } + + setSubstanceCodes(substanceId: string): void { + // this.loadingService.setLoading(true); + this.substanceService.getSubstanceCodes(substanceId).pipe(take(1)).subscribe(codes => { + if (codes && codes.length > 0) { + this.codes[substanceId] = { + codeSystemNames: [], + codeSystems: {} + }; + codes.forEach(code => { + if (this.codes[substanceId].codeSystems[code.codeSystem]) { + this.codes[substanceId].codeSystems[code.codeSystem].push(code); + } else { + this.codes[substanceId].codeSystems[code.codeSystem] = [code]; + this.codes[substanceId].codeSystemNames.push(code.codeSystem); + } + }); + this.codes[substanceId].codeSystemNames = this.sortCodeSystems(this.codes[substanceId].codeSystemNames); + this.codes[substanceId].codeSystemNames.forEach(sysName => { + this.codes[substanceId].codeSystems[sysName] = this.codes[substanceId].codeSystems[sysName].sort((a, b) => { + let test = 0; + if (a.type === 'PRIMARY' && b.type !== 'PRIMARY') { + test = 1; + } else if (a.type !== 'PRIMARY' && b.type === 'PRIMARY') { + test = -1; + } else { + test = 0; + } + return test; + }); + }); + + } + this.loadingService.setLoading(false); + }, error => { + this.loadingService.setLoading(false); + }); + } + + + populateUrlQueryParameters(): void { + + } + + editAdvancedSearch(): void { + const eventLabel = environment.isAnalyticsPrivate ? 'advanced search term' : + `${this.privateSearchTerm}`; + this.gaService.sendEvent('substancesFiltering', 'icon-button:edit-advanced-search', eventLabel); + // ** BEGIN: Store in Local Storage for Advanced Search + // storage searchterm in local storage when going from Browse Substance to Advanced Search (NOT COMING FROM ADVANCED SEARCH) + if (!this.searchHashFromAdvanced) { + const advSearchTerm: Array = []; + advSearchTerm[0] = this.privateSearchTerm; + const queryStatementHashes = []; + const queryStatement = { + condition: '', + queryableProperty: 'Manual Query Entry', + command: 'Manual Query Entry', + commandInputValues: advSearchTerm, + query: this.privateSearchTerm + }; + + // Store in cookies, Category tab (Substance, Application, etc) + const categoryHash = this.utilsService.hashCode('Substance'); + localStorage.setItem(categoryHash.toString(), 'Substance'); + queryStatementHashes.push(categoryHash); + + const queryStatementString = JSON.stringify(queryStatement); + const hash = this.utilsService.hashCode(queryStatementString); + + // Store in cookies, Each Query Statement is stored in separate hash + localStorage.setItem(hash.toString(), queryStatementString); + + // Push Query Statements Hashes in Array + queryStatementHashes.push(hash); + + // Store in cookies, store in Query Hash - Query Statement Hashes Array + const queryStatementHashesString = JSON.stringify(queryStatementHashes); + + localStorage.setItem(this.searchTermHash.toString(), queryStatementHashesString); + } + // ** END: Store in Local Storage for Advanced Search + + const navigationExtras: NavigationExtras = { + queryParams: { + 'g-search-hash': this.searchTermHash + } + }; + + navigationExtras.queryParams['structure'] = this.privateStructureSearchTerm || null; + navigationExtras.queryParams['type'] = this.privateSearchType || null; + + if(this.privateSearchType === 'similarity') { + navigationExtras.queryParams['cutoff'] = this.privateSearchCutoff || 0; + } + this.router.navigate(['/advanced-search'], navigationExtras); + } + + editStructureSearch(): void { + const eventLabel = environment.isAnalyticsPrivate ? 'structure search term' : + `${this.privateStructureSearchTerm}-${this.privateSearchType}-${this.privateSearchCutoff}`; + this.gaService.sendEvent('substancesFiltering', 'icon-button:edit-structure-search', eventLabel); + + const navigationExtras: NavigationExtras = { + queryParams: {} + }; + + navigationExtras.queryParams['structure'] = this.privateStructureSearchTerm || null; + navigationExtras.queryParams['type'] = this.privateSearchType || null; + + if (this.privateSearchType === 'similarity') { + navigationExtras.queryParams['cutoff'] = this.privateSearchCutoff || 0; + } + + this.router.navigate(['/structure-search'], navigationExtras); + } + + clearStructureSearch(): void { + + const eventLabel = environment.isAnalyticsPrivate ? 'structure search term' : + `${this.privateStructureSearchTerm}-${this.privateSearchType}-${this.privateSearchCutoff}`; + this.gaService.sendEvent('substancesFiltering', 'icon-button:clear-structure-search', eventLabel); + + this.privateStructureSearchTerm = ''; + this.privateSearchType = ''; + this.privateSearchCutoff = 0; + this.smiles = ''; + this.pageIndex = 0; + + this.populateUrlQueryParameters(); + // this.searchSubstances(); + } + + editSequenceSearh(): void { + const eventLabel = environment.isAnalyticsPrivate ? 'sequence search term' : + `${this.privateSequenceSearchTerm}-${this.privateSearchType}-${this.privateSearchCutoff}-${this.privateSearchSeqType}`; + this.gaService.sendEvent('substancesFiltering', 'icon-button:edit-sequence-search', eventLabel); + + const navigationExtras: NavigationExtras = { + queryParams: {} + }; + + navigationExtras.queryParams['type'] = this.privateSearchType || null; + navigationExtras.queryParams['cutoff'] = this.privateSearchCutoff || 0; + navigationExtras.queryParams['seq_type'] = this.privateSearchSeqType || null; + sessionStorage.setItem('gsrs_edit_sequence_' + this.sequenceID, JSON.stringify(this.privateSequenceSearchTerm)); + navigationExtras.queryParams['source'] = 'edit'; + navigationExtras.queryParams['source_id'] = this.sequenceID; + + this.router.navigate(['/sequence-search'], navigationExtras); + } + + clearSequenceSearch(): void { + + const eventLabel = environment.isAnalyticsPrivate ? 'sequence search term' : + `${this.privateSequenceSearchTerm}-${this.privateSearchType}-${this.privateSearchCutoff}-${this.privateSearchSeqType}`; + this.gaService.sendEvent('substancesFiltering', 'icon-button:clear-sequence-search', eventLabel); + + this.privateSequenceSearchTerm = ''; + this.privateSequenceSearchKey = ''; + this.privateSearchType = ''; + this.privateSearchCutoff = 0; + this.privateSearchSeqType = ''; + this.pageIndex = 0; + + this.populateUrlQueryParameters(); + // this.searchSubstances(); + } + + editBulkSearch(): void { + const eventLabel = environment.isAnalyticsPrivate ? 'bulk search term' : + `${this.searchEntity}-bulk-search-${this.privateBulkSearchQueryId}`; + this.gaService.sendEvent('substancesFiltering', 'icon-button:edit-bulk-search', eventLabel); + + const navigationExtras: NavigationExtras = { + queryParams: { + bulkQID: this.privateBulkSearchQueryId, + searchOnIdentifiers: this.searchOnIdentifiers, + searchEntity: this.searchEntity + } + }; + this.router.navigate(['/bulk-search'], navigationExtras); + } + + clearBulkSearch(): void { + + const eventLabel = environment.isAnalyticsPrivate ? 'bulk search term' : + `${this.searchEntity}-bulk-search-${this.privateBulkSearchQueryId}`; + this.gaService.sendEvent('substancesFiltering', 'icon-button:clear-bulk-search', eventLabel); + + this.privateBulkSearchQueryId = null; + this.privateBulkSearchSummary = null; + this.searchEntity = ''; + this.searchOnIdentifiers = null; + this.privateSearchType = ''; + this.privateSearchCutoff = 0; + this.smiles = ''; + this.pageIndex = 0; + + this.populateUrlQueryParameters(); + // this.searchSubstances(); + } + + clearSearch(): void { + + const eventLabel = environment.isAnalyticsPrivate ? 'search term' : this.privateSearchTerm; + this.gaService.sendEvent('substancesFiltering', 'icon-button:clear-search', eventLabel); + + this.privateSearchTerm = ''; + this.wildCardText = ''; + this.searchTermHash = null; + this.pageIndex = 0; + + this.populateUrlQueryParameters(); + this.substanceTextSearchService.setSearchValue('main-substance-search'); + this.searchSubstances(); + } + + clearFilters(): void { + // for facets + // Does this facet remove work completely? When I (aw) click RESET button the facet + // is cleared but this.displayFacets still has the value. + this.displayFacets.forEach(displayFacet => { + displayFacet.removeFacet(displayFacet.type, displayFacet.bool, displayFacet.val); + }); + if (this.privateStructureSearchTerm != null && this.privateStructureSearchTerm !== '') { + this.clearStructureSearch(); + } else if ((this.privateSequenceSearchTerm != null && this.privateSequenceSearchTerm !== '') || + (this.privateSequenceSearchKey != null && this.privateSequenceSearchKey !== '')) { + this.clearSequenceSearch(); + } else if (this.privateBulkSearchQueryId != null && this.privateBulkSearchQueryId !== undefined) { + this.clearBulkSearch(); + } else { + this.clearSearch(); + } + this.facetManagerService.clearSelections(); + } + + clickToRefreshPreview() { + this.emitService.setRefresh(true); + this.isRefresher = true; + this.loadComponent(); + } + + clickToCancel() { + this.emitService.setCancel(true); + } + + get searchTerm(): string { + return this.privateSearchTerm; + } + + get structureSearchTerm(): string { + return this.privateStructureSearchTerm; + } + + get sequenceSearchTerm(): string { + return this.privateSequenceSearchTerm; + } + + get bulkSearchSummary(): string { + return this.privateBulkSearchSummary; + } + get bulkSearchQueryId(): number { + return this.privateBulkSearchQueryId; + } + get bulkSearchStatusKey(): string { + return this.privateBulkSearchStatusKey; + } + + get searchType(): string { + return this.privateSearchType; + } + + get searchStrategy(): string { + return this.privateSearchStrategy; + } + + + get searchCutoff(): number { + return this.privateSearchCutoff; + } + + get searchSeqType(): string { + return this.privateSearchSeqType; + } + + private processResponsiveness = () => { + setTimeout(() => { + if (window) { + if (window.innerWidth < 1100) { + this.matSideNav.close(); + this.isCollapsed = true; + this.hasBackdrop = true; + } else { + this.matSideNav.open(); + this.hasBackdrop = false; + } + } + }); + } + + openSideNav() { + this.gaService.sendEvent('substancesFiltering', 'button:sidenav', 'open'); + this.matSideNav.open(); + } + + updateView(event): void { + this.gaService.sendEvent('substancesContent', 'button:view-update', event.value); + this.view = event.value; + } + + + getSequenceDisplay(sequence?: string): string { + if (sequence != null) { + if (sequence.length < 16) { + return sequence; + } else { + return `${sequence.substr(0, 15)}...`; + } + } else { + return ''; + } + } + + openImageModal(substance: any): void { + const eventLabel = environment.isAnalyticsPrivate ? 'substance' : substance._name; + this.gaService.sendEvent('substancesContent', 'link:structure-zoom', eventLabel); + + let data: any; + + if (substance.substanceClass === 'chemical') { + data = { + structure: substance._metadata.recordId, + smiles: substance.structure.smiles, + uuid: substance.uuid, + names: this.names[substance.uuid] + }; + } else { + data = { + structure: substance._metadata.recordId, + names: this.names[substance.uuid] + }; + } + + const dialogRef = this.dialog.open(StructureImageModalComponent, { + width: '650px', + panelClass: 'structure-image-panel', + data: data + }); + + this.overlayContainer.style.zIndex = '1002'; + + const subscription = dialogRef.afterClosed().subscribe(() => { + this.overlayContainer.style.zIndex = null; + subscription.unsubscribe(); + }, () => { + this.overlayContainer.style.zIndex = null; + subscription.unsubscribe(); + }); + } + + getMol(id: string, filename: string): void { + const subscription = this.structureService.downloadMolfile(id).subscribe(response => { + this.downloadFile(response, filename); + subscription.unsubscribe(); + }, error => { + subscription.unsubscribe(); + }); + } + + getFasta(id: string, filename: string): void { + const subscription = this.substanceService.getFasta(id).subscribe(response => { + this.downloadFile(response, filename); + subscription.unsubscribe(); + }, error => { + subscription.unsubscribe(); + }); + } + + downloadFile(response: any, filename: string): void { + const dataType = response.type; + const binaryData = []; + binaryData.push(response); + const downloadLink = document.createElement('a'); + downloadLink.href = window.URL.createObjectURL(new Blob(binaryData, { type: dataType })); + downloadLink.setAttribute('download', filename); + document.body.appendChild(downloadLink); + downloadLink.click(); + } + + sortCodeSystems(codes: Array): Array { + if (this.configService.configData && this.configService.configData.codeSystemOrder && + this.configService.configData.codeSystemOrder.length > 0) { + const order = this.configService.configData.codeSystemOrder; + for (let i = order.length - 1; i >= 0; i--) { + for (let j = 0; j <= codes.length; j++) { + if (order[i] === codes[j]) { + const a = codes.splice(j, 1); // removes the item + codes.unshift(a[0]); // adds it back to the beginning + break; + } + } + } + } + return codes; + } + + showAllRecords(): void { + this.showExactMatches = false; + this.processResponsiveness(); + } + + increaseOverlayZindex(): void { + this.overlayContainer.style.zIndex = '1002'; + } + + decreaseOverlayZindex(): void { + this.overlayContainer.style.zIndex = null; + } + + + downloadJson(id: string) { + this.substanceService.getSubstanceDetails(id).pipe(take(1)).subscribe(response => { + this.downloadFile(JSON.stringify(response), id + '.json'); + }); + + } + + copySmiles(val: string) { + const selBox = document.createElement('textarea'); + selBox.style.position = 'fixed'; + selBox.style.left = '0'; + selBox.style.top = '0'; + selBox.style.opacity = '0'; + selBox.value = val; + document.body.appendChild(selBox); + selBox.focus(); + selBox.select(); + document.execCommand('copy'); + document.body.removeChild(selBox); + } + + +} diff --git a/src/app/core/admin/import-browse/import-summary/import-summary.component.html b/src/app/core/admin/import-browse/import-summary/import-summary.component.html new file mode 100644 index 000000000..e488c848a --- /dev/null +++ b/src/app/core/admin/import-browse/import-summary/import-summary.component.html @@ -0,0 +1,477 @@ + + + + + + + + + +
+ Import Status: + {{substance._metadata.importStatus | uppercase}} +
+
+ + +
+
+
+
+ + {{substance.structure.stereochemistry}} + +
+
+
+
+ + {{substance.substanceClass}} + + +
+
+
+
+ + + +
+
+ +
+
+
+ similarity: {{substance._matchContext.similarity.toFixed(3)}} +
+
+ {{substance.definitionLevel }} DEFINITION +
+ + + + + subunit {{unit.subunitIndex}} ({{unit.sequence && unit.sequence.length || 0}}) similarity search + + + + + + + Download JSON + + + Show Molfile + + + Download Molfile + + + Download Molfile + + + Download Fasta + + + + + + Search Structure + + + Search Structure + + + + + + Advanced Structure + + + + + + Copy Substance to New Form + + + Copy Definition to New Form + + + +
+
+
+
Names:
+
+
Loading
+
+
+
Names:
+
+
+ + + + +
+
See {{names.length - 4}} More
+
+
+
+ + + + + + + +
+
Less
+
+
+
+
Codes:
+
+
+
+ {{(codeSystemVocab && codeSystemVocab[codeSystemName]) ? codeSystemVocab[codeSystemName].display : codeSystemName}} + + :  + + + + + {{codeObject.code.trim()}} + + [{{codeObject.type}}] + + + + {{codeObject.code.trim()}} + [{{codeObject.type}}] + + + , + + + + {{showAll[codeSystemName] ? 'Show Less': 'Show More'}} + +
+
+
See {{codeSystemNames.length - 5}} More
+
+
+
+
+ {{codeSystemName}} + + :  + + + + + {{codeObject.code.trim()}} + + [{{codeObject.type}}] + + + + {{codeObject.code.trim()}} + [{{codeObject.type}}] + + + , + + + + {{showAll[codeSystemName] ? 'Show Less': 'Show More'}} + +
+
+
Less
+
+
+
+
Relationships:
+
+ {{substance._relationships.count}} +
+
+
+
Components:
+
+ {{substance.mixture.components.length}} +
+
+
+
Constituents:
+
+ {{substance.specifiedSubstance.constituents.length}} +
+
+
+
Subunits:
+
+ {{substance.nucleicAcid.subunits.length}} +
+
+
+
Mol. Weight:
+
+ {{substance.structure.mwt | number: rounding}} +
+
+
+
Formula:
+
+ +
+
+
+
Part:
+
+ + + {{part}} + , + + +
+
+
+ +
+
+ Bulk Action Select + +
+
+
+
Created:
+
+ + {{substance.created | date : 'shortDate'}} + +
+
+
+
Created By:
+
+ + {{substance.createdBy }} + +
+
+
+
Import Status:
+
+ + +
+
+
+
Validated By:
+
+ + {{substance.approvedBy }} + +
+
+
+
Validated Date:
+
+ + {{substance.approved| date : 'short'}} + +
+
+
+
Last Modified:
+
+ + {{substance.lastEdited | date : 'shortDate'}} + +
+
+
+
Last Modified By:
+
+ + {{substance.lastEditedBy }} + +
+
+
+
Version:
+
+ + {{substance.version}} + +
+
+
+
+
+
+ + +
+
+ + (matched queries: {{_.join(substance._matchContext.queries, ", ")}}) + +
+ +
+
+

+ Actions: + + + Edit + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
Name + + + + + {{record.ID}} + + Source + + {{record.source}} + + Match Keys + {{item.field}}{{item.count > 1 ? " (" + item.count + " matches)" : ''}}{{last?'':', '}}  
+
+ + +
+
+
+
No matching fields or records found
+
+
+ + + + Validation ({{substance.validations.warnings}} warnings,   + {{substance.validations.errors}} errors ) + +
+
+ + + + + + + + + + + + + + + +
Type {{item.validationType | uppercase}} Message {{item.validationMessage}}
+
+
+
+
+
+ +
+
+
+ + + +

Record Action: {{displayAction}}

+

+     {{message}} + +

+ +
+ + + +
+
\ No newline at end of file diff --git a/src/app/core/admin/import-browse/import-summary/import-summary.component.scss b/src/app/core/admin/import-browse/import-summary/import-summary.component.scss new file mode 100644 index 000000000..3722bd769 --- /dev/null +++ b/src/app/core/admin/import-browse/import-summary/import-summary.component.scss @@ -0,0 +1,288 @@ +.disabled-action-button { + cursor: not-allowed !important; +} + +.disabled-action-button:hover { + cursor: not-allowed !important; +} + +.mat-card { + max-width: 1228px; + margin-bottom: 20px; +} + +.no-matches { + margin: 15px; +} + +.mat-card, .controls-container { + width: 100%; + box-sizing: border-box; +} + +.button-wrapper { + max-width: 170px; +} + +.loading-label { + font-weight: bold; +min-width: 100px; +} + +.match-link { + color: var(--link-primary-color); +} + +.darkgray { + color: gray; +} + +.spinner { + width: 100px !important; +height: 100px !important; +margin: auto; +margin-top: 75px !important; +margin-left: 150px !important; +} + +.action-button { + margin-right: 10px; +font-size: 18px; +} + + + +.action-label { + font-size:16px; + margin-right:15px; + margin-top:15px; +} + +.name-loading { + display: flex; + width: 300px; +} +.show-more { + text-decoration: underline; + color: var(--link-primary-color); + cursor: pointer; +} + +.mat-card-title { + display: flex; + box-sizing: border-box; + width: 100%; + flex-direction: row; + align-items: center; + white-space: nowrap; +} + +.moreLink { + color: var(--link-primary-color); + font-weight: 600; + padding-left: 10px; + font-size: 15px; +} + +.mat-card-title { + white-space: normal; + + .substance-name { + color: var(--link-primary-color); + text-decoration: none; + padding-right: 10px; + } + + .status-label { + font-size:18px; + margin-right: 10px; + } + + .status { + font-size: 24px; + color: var(--pink-span-color); + margin-right: 25px; + } + +} + +.warning-label { + color: var(--warning-dialog-color); + font-weight: 500; + background-color: var(--warning-dialog-bg-color); + padding: 10px; + border-radius: 5px; + + } + + .info-label { + color: black; + font-weight: 500; + background-color: lightgray; + padding: 10px; + border-radius: 5px; + + } + + + .error-label { + color: var(--error-dialog-color); + font-weight: 500; + background-color: var(--error-dialog-bg-color); + padding: 10px; + border-radius: 5px; + } + +.tab-container { + margin-top: 15px; +} + +::ng-deep .mat-tab-label-content { + font-size: 17px !important; +} + +.summary-tab-label { + font-size: 17px; +} + +.substance-content { + display: flex; + flex-direction: row; +} + +.tile .structure-container { + width:100%; + padding-right:0px; +} + +.structure-container { + padding-right: 10px; +} + +.mat-card-content { + + .mat-chip-list-container { + margin-left: -10px; + margin-bottom: 10px; + } +} + +.tile .image-thumbnail { + margin:auto; + margin-bottom:20px; + height:175px; + width:175px; +} + +.image-thumbnail{ + min-height:150px; + min-width:150px; +} + +.zoom:hover{ + cursor:zoom-in; +} + +.substance-data { + display: flex; + flex-direction: row; + + &:not(:last-child) { + margin-bottom: 15px; + } + + .label { + font-weight: bold; + min-width: 100px; + } +} + +.icon-align{ + margin-top: -10px; + vertical-align: bottom; +} + +.ext-link{ + color: var(--link-primary-color); + text-decoration-style: unset; +} + +@media(max-width: 700px) { + .mat-card-title { + flex-direction: column; + align-items: flex-start; + + .substance-name { + margin-bottom: 10px; + } + } + + .substance-data { + flex-direction: column; + + .label { + margin-bottom: 10px; + } + + .value, .code-system { + padding-left: 20px; + } + } +} + +.lock-icon { + color: var(--lock-icon-color); +} + +.validation-container { + margin-top: 20px; +} + +@media(max-width: 600px) { + + .substance-content { + flex-direction: column; + + .image-thumbnail { + margin: 0 auto; + } + } +} + +.definition { + padding: 7px 0px; +} + +.similarity { + padding-left: 10px; + font-family: Menlo,Monaco,Consolas,"Courier New",monospace; + color: var(--pink-span-color); +} + +.similarity-label { + font-style: italic; +} + +.inxight-container { + text-align: right; + padding-bottom: 15px; +} + +.small-icon { + height:20px; + width: 20px; + padding-bottom: 5px; + vertical-align: middle; +} + +.match-context { + display: block; + font-size: small; + color: var(--regular-green-color); + margin-top: 8px; + margin-bottom: 8px; +} + +.space-bottom { + margin-bottom: 5px; +} \ No newline at end of file diff --git a/src/app/core/admin/import-browse/import-summary/import-summary.component.spec.ts b/src/app/core/admin/import-browse/import-summary/import-summary.component.spec.ts new file mode 100644 index 000000000..f76be608d --- /dev/null +++ b/src/app/core/admin/import-browse/import-summary/import-summary.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ImportSummaryComponent } from './import-summary.component'; + +describe('ImportSummaryComponent', () => { + let component: ImportSummaryComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ImportSummaryComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ImportSummaryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/admin/import-browse/import-summary/import-summary.component.ts b/src/app/core/admin/import-browse/import-summary/import-summary.component.ts new file mode 100644 index 000000000..cce1b6b79 --- /dev/null +++ b/src/app/core/admin/import-browse/import-summary/import-summary.component.ts @@ -0,0 +1,533 @@ +import { Component, OnInit, Input, Output, EventEmitter, ComponentFactoryResolver, Inject, ViewChild, TemplateRef } from '@angular/core'; +import { + SubstanceDetail, + SubstanceName, + SubstanceSummary, + SubstanceCode, + SubstanceRelationship, + Subunit +} from '../../../substance/substance.model'; +import { DYNAMIC_COMPONENT_MANIFESTS, DynamicComponentManifest } from '@gsrs-core/dynamic-component-loader'; +import { SafeUrl } from '@angular/platform-browser'; +import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; +import { AuthService } from '@gsrs-core/auth'; +import { SubstanceService } from '@gsrs-core/substance/substance.service'; +import { StructureService } from '@gsrs-core/structure'; +import {Router} from '@angular/router'; +import {Alignment, UtilsService} from '@gsrs-core/utils'; +import { take } from 'rxjs/operators'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { MatDialog, MatDialogRef } from '@angular/material/dialog'; +import { ConfigService } from '@gsrs-core/config'; +import { Vocabulary } from '@gsrs-core/controlled-vocabulary'; +import * as lodash from 'lodash'; +import { CardDynamicSectionDirective } from '@gsrs-core/substances-browse/card-dynamic-section/card-dynamic-section.directive'; +import { AdminService } from '@gsrs-core/admin/admin.service'; +import { LoadingService } from '@gsrs-core/loading'; +import { MergeActionDialogComponent } from '@gsrs-core/admin/import-browse/merge-action-dialog/merge-action-dialog.component'; +import { PageEvent } from '@angular/material/paginator'; +@Component({ + selector: 'app-import-summary', + templateUrl: './import-summary.component.html', + styleUrls: ['./import-summary.component.scss'] +}) +export class ImportSummaryComponent implements OnInit { + private privateSubstance: any; + @Output() openImage = new EventEmitter(); + @Output() doneAction = new EventEmitter< any >(); + @Output() bulkSelect = new EventEmitter < any > (); + privateDummyID: string; + showAudit = false; + isAdmin = false; //this shouldn't be called "isAdmin", it's typically used to mean "canUpdate". Should fix for future devs. + canCreate = false; //meant to allow creating new records + subunits?: Array; + @ViewChild(CardDynamicSectionDirective, {static: true}) dynamicContentContainer: CardDynamicSectionDirective; + codeSystemNames?: Array = []; + @Input() codeSystemVocab?: Vocabulary; + @Input() searchStrategy?: string = ''; + @Input() recordID: string = null; + private codes: Array ; + bulkChecked = false; + displayAction: string; + privateBulkAction: any; + pageSize = 5; + pageIndex = 0; + deleted = false; + + +// @Input() codeSystems?: { [codeSystem: string]: Array }; + alignments?: Array; + inxightLink = false; + inxightUrl: string; + overlayContainer: any; + rounding = '1.0-2'; + showAll = []; + privateCodeSystems?: { [codeSystem: string]: Array }; + privateCodeSystemNames = []; + allPrimary = []; + showLessNames = true; + showLessCodes = true; + privateNames?: Array; + nameLoading = true; + _ = lodash; + codeSystems = []; + displayedColumns = ['name', 'source', 'keys', 'merge']; + displayedColumns2 = ['type', 'message']; + message = ""; + private privateMatches: any; + showMerge = false; + + disabled = false; + performedAction: string; + @ViewChild('infoDialog', { static: true }) infoDialog: TemplateRef; + dialogRef: MatDialogRef; + + + constructor( + public utilsService: UtilsService, + public gaService: GoogleAnalyticsService, + public authService: AuthService, + private substanceService: SubstanceService, + private structureService: StructureService, + private router: Router, + private overlayContainerService: OverlayContainer, + private dialog: MatDialog, + private adminService: AdminService, + private configService: ConfigService, + private loadingService: LoadingService, + @Inject(DYNAMIC_COMPONENT_MANIFESTS) private dynamicContentItems: DynamicComponentManifest[] + ) { } + + + ngOnInit() { + this.getMatchSummary(); + + this.overlayContainer = this.overlayContainerService.getContainerElement(); + + this.authService.hasAnyRolesAsync('Updater', 'SuperUpdater', 'Approver', 'admin').pipe(take(1)).subscribe(response => { + if (response) { + this.isAdmin = response; + } + }); + this.authService.hasAnyRolesAsync('DataEntry', 'SuperDataEntry', 'admin').pipe(take(1)).subscribe(response => { + if (response) { + this.canCreate = response; + } + }); + if (this.substance.protein) { + this.subunits = this.substance.protein.subunits; + this.getAlignments(); + } + if (this.substance.nucleicAcid) { + this.subunits = this.substance.nucleicAcid.subunits; + this.getAlignments(); + } + + if (this.substance.structure && this.substance.structure.formula) { + this.substance.structure.formula = this.structureService.formatFormula(this.substance.structure); + } + + this.matchFieldsToCount(this.substance.matchedRecords); + if (this.configService.configData && this.configService.configData.molWeightRounding) { + this.rounding = '1.0-' + this.configService.configData.molWeightRounding; + } + + if (this.configService.configData && this.configService.configData.stagingArea) { + if (this.configService.configData.stagingArea.mergeAction) { + this.showMerge = this.configService.configData.stagingArea.mergeAction + } + } + // this.privateMatches = JSON.parse(JSON.stringify(this.substance.matchedRecords)).slice(0, 5); + } + + + matchFieldsToCount(matches: any) { + console.log(matches); + matches.forEach(match => { + let newArr: Array = []; + match.records.forEach(record => { + let found = false; + newArr.forEach(val => { + if (val.field && val.field === record) { + val.count++; + found = true; + } + }); + if (!found) { + let temp = {'field': record, 'count': 1}; + newArr.push(temp); + } + }); + match.recordArr = newArr; + }); + } + + + @Input() + + set names(name: any) { + if (typeof(name) != "undefined") { + this.privateNames = name; + this.nameLoading = false; + } + + } + + get names(): any { + return this.privateNames; + } + + @Input() + + set bulkAction(action: boolean){ + this.privateBulkAction = action; + this.bulkChecked = action; + } + + get bulkAction() { + return this.privateBulkAction; + } + + changePage(event: PageEvent) { + this.pageSize = event.pageSize; + this.pageIndex = event.pageIndex; + const skip = event.pageSize * event.pageIndex; + this.privateMatches = JSON.parse(JSON.stringify(this.substance.matchedRecords)).slice(skip, (this.pageSize + skip)); + this.matchFieldsToCount(this.privateMatches); + + this.getMatchSummary(); + + } + + getMatchSummary(skip?: any) { + if (!skip) { + this.privateMatches = JSON.parse(JSON.stringify(this.substance.matchedRecords)).slice(0, 5); + this.matchFieldsToCount(this.privateMatches); + } + + this.privateMatches.forEach(record => { + if (record.source && record.source === 'Staging Area'){ + this.adminService.GetStagedRecord(record.ID).subscribe(response => { + record._name = response._name; + }, error => { + console.log(error); + }) + } else { + this.substanceService.getSubstanceSummary(record.ID).subscribe(response => { + record.uuid = response.uuid; + record._name = response._name; + }, error => { + console.log(error); + }); + } + + }); + + } + + addtoBulkList() { + this.bulkChecked = !this.bulkChecked; + const toEmit = {"checked": this.bulkChecked, + "substance": this.privateSubstance}; + this.bulkSelect.emit(toEmit); + } + + + getApprovalID() { + if (!this.substance.approvalID) { + if (this.substance._approvalIDDisplay && + this.substance._approvalIDDisplay.length === 10 && + this.substance._approvalIDDisplay.indexOf(' ') < 0) { + this.substance.approvalID = this.substance._approvalIDDisplay; + } + } + } + + @Input() + set substance(substance: any) { + if (substance != null) { + this.privateSubstance = substance; + this.codeSystems = substance.codes; + // console.log(substance); + this.codes = substance.codes; + // this.getStructureID(); + if (substance._metadata.importStatus.toUpperCase() === 'MERGED' || substance._metadata.importStatus.toUpperCase() === 'IMPORTED') { + this.disabled = true; + } else { + } + + this.setCodeSystems(); + this.processValidation(); + } + } + + get substance(): any { + return this.privateSubstance; + } + + getStructureID() { + if(this.privateSubstance && this.privateSubstance.structure && this.privateSubstance.structure.molfile) { + this.structureService.interpretStructure(this.privateSubstance.structure.molfile).subscribe(response => { + this.privateSubstance.structureID = response.structure.id; + this.substance.structureID = response.structure.id; + }); + } + } + processValidation() { + let validation = {warnings: 0, errors: 0, validations: []}; + + if(this.privateSubstance._metadata && this.privateSubstance._metadata.validations){ + this.privateSubstance._metadata.validations.forEach (entry => { + if(entry.validationType === 'error') { + validation.errors++; + } else if (entry.validationType === 'warning') { + validation.warnings++; + } + validation.validations.push(entry); + }); + + } + this.privateSubstance.validations = validation; + } + + editRecord() { + + } + + deleteRecord() { + this.adminService.deleteStagedRecord([this.privateSubstance._metadata.recordId]).subscribe(response => { + this.message = "Successfully deleted staging area record data. Closing dialog."; + this.deleted = true; + setTimeout(() => { + this.dialogRef.close(); + }, 1500); + }, error => { + this.message = "There was a problem deleting staging area record data"; + + }) + } + + doAction(action: string, mergeID?: string) { + console.log(action); + this.displayAction = action; + this.loadingService.setLoading(true); + this.adminService.stagedRecordSingleAction(this.privateSubstance._metadata.recordId, action).subscribe(result => { + if (result.jobStatus === 'completed') { + this.loadingService.setLoading(false); + this.doneAction.emit(this.privateSubstance.uuid); + this.message = this.displayAction + " record action completed successfully"; + if (result) { + this.disabled = true; + this.performedAction = action; + } + + this.deleted = false; + this.dialogRef = this.dialog.open(this.infoDialog, + {data: {'message': 'Record successfuly Created', 'action': action}, + width: '600px'}); + const dialogSubscription = this.dialogRef.afterClosed().subscribe(response => { + this.router.navigate(['/admin/staging-area/'], {queryParamsHandling: "preserve"}); + }); + + } else { + setTimeout(() => { + this.processingstatus(result.id, action, result); + }, 200); + } + + }, error => { + this.message = "Error: failed to " + action + " record"; + this.loadingService.setLoading(false); + this.dialogRef = this.dialog.open(this.infoDialog, + {data: {'message': 'Error: failed to', 'action': action}, + width: '600px'}); + const dialogSubscription = this.dialogRef.afterClosed().subscribe(response => { + + }); + }); + } + + processingstatus(id: string, action?: any, result?: any): void { + this.adminService.processingstatus(id).subscribe(response => { + if (response.jobStatus === 'completed') { + this.loadingService.setLoading(false); + this.doneAction.emit(this.privateSubstance.uuid); + this.message = this.displayAction + " record action completed successfully"; + if (result) { + this.disabled = true; + this.performedAction = action; + } + + this.deleted = false; + this.dialogRef = this.dialog.open(this.infoDialog, + {data: {'message': 'Record successfuly Created', 'action': action}, + width: '600px'}); + const dialogSubscription = this.dialogRef.afterClosed().subscribe(response => { + this.router.navigate(['/admin/staging-area/'], {queryParamsHandling: "preserve"}); + }); + } else { + setTimeout(() => { + this.processingstatus(id); + }, 200); + } + + + }); + } + + setCodeSystems() { + if (this.codes && this.codes.length > 0) { + const substanceId = this.substance.uuid; + + this.codes.forEach(code => { + if (this.codeSystems[code.codeSystem]) { + this.codeSystems[code.codeSystem].push(code); + } else { + this.codeSystems[code.codeSystem] = [code]; + this.codeSystemNames.push(code.codeSystem); + } + }); + this.codeSystemNames = this.sortCodeSystems(this.codeSystemNames); + this.codeSystemNames.forEach(sysName => { + this.codeSystems[sysName] = this.codeSystems[sysName].sort((a, b) => { + let test = 0; + if (a.type === 'PRIMARY' && b.type !== 'PRIMARY') { + test = 1; + } else if (a.type !== 'PRIMARY' && b.type === 'PRIMARY') { + test = -1; + } else { + test = 0; + } + return test; + }); + }); + + } + } + + sortCodeSystems(codes: Array): Array { + if (this.configService.configData && this.configService.configData.codeSystemOrder && + this.configService.configData.codeSystemOrder.length > 0) { + const order = this.configService.configData.codeSystemOrder; + for (let i = order.length - 1; i >= 0; i--) { + for (let j = 0; j <= codes.length; j++) { + if (order[i] === codes[j]) { + const a = codes.splice(j, 1); // removes the item + codes.unshift(a[0]); // adds it back to the beginning + break; + } + } + } + } + return codes; + } + + formatCodeSystems() { + // sort() function in substance-browse isn't working... pushing this as alternative to get all primary codes first + this.codeSystemNames.forEach(sysName => { + const testing = []; + this.allPrimary[sysName] = 'true'; + this.codeSystems[sysName].forEach(code => { + if (code.type === 'PRIMARY') { + testing.unshift(code); + } else { + this.allPrimary[sysName] = 'false'; + testing.push(code); + } + }); + this.codeSystems[sysName] = testing; + }); + } + + openImageModal(): void { + this.substance.names = this.privateNames; + this.openImage.emit(this.substance); + } + + editForm(): void { + this.router.navigate(['/substances/' + this.substance.uuid + '/edit']); + } + getFasta(id: string, filename: string): void { + this.substanceService.getFasta(id).subscribe(response => { + this.downloadFile(response, filename); + }); + } + + getMol(id: string, filename: string): void { + this.structureService.downloadMolfile(id).subscribe(response => { + this.downloadFile(response, filename); + }); + } + + downloadFile(response: any, filename: string): void { + const dataType = response.type; + const binaryData = []; + binaryData.push(response); + const downloadLink = document.createElement('a'); + downloadLink.href = window.URL.createObjectURL(new Blob(binaryData, { type: dataType })); + downloadLink.setAttribute('download', filename); + document.body.appendChild(downloadLink); + downloadLink.click(); + } + + downloadJson() { + this.substanceService.getSubstanceDetails(this.substance.uuid).pipe(take(1)).subscribe(response => { + this.downloadFile(JSON.stringify(response), this.substance.uuid + '.json'); + }); + } + + getAlignments(): void { + if (this.substance._matchContext) { + if (this.substance._matchContext.alignments) { + this.alignments = this.substance._matchContext.alignments; + this.alignments.forEach(alignment => { + this.subunits.forEach(subunit => { + if (subunit.uuid === alignment.id) { + alignment.subunitIndex = subunit.subunitIndex; + } + }); + }); + } + } + } + + openMergeModal(selected?: any) { + + let temp = {uuid: this.substance.uuid, recordId: this.substance._metadata.recordId, matches: this.substance.matchedRecords} + if (selected) { + temp['mergeRecord'] = selected; + } + const dialogRef = this.dialog.open(MergeActionDialogComponent, { + minWidth: '50%', + maxWidth: '90%', + minHeight: '600px', + maxHeight: '90%', + data: temp + }); + this.overlayContainer.style.zIndex = '1002'; + + dialogRef.afterClosed().subscribe(result => { + this.overlayContainer.style.zIndex = null; + }); + } + + moreThanNumberCount(names, number) { + if(names && names.length < number) { + return false; + } else { + return true; + } + } + + showMoreLessNames() { + this.showLessNames = !this.showLessNames; + } + + showMoreLessCodes() { + this.showLessCodes = !this.showLessCodes; + } + + + + +} diff --git a/src/app/core/admin/import-browse/merge-action-dialog/merge-action-dialog.component.html b/src/app/core/admin/import-browse/merge-action-dialog/merge-action-dialog.component.html new file mode 100644 index 000000000..1b0b6c1fa --- /dev/null +++ b/src/app/core/admin/import-browse/merge-action-dialog/merge-action-dialog.component.html @@ -0,0 +1,54 @@ +

Staging Record Merge (experimental)

+
+
+ +
Select a record to merge into +
Selected + Record Name
+
+ + {{match._name}} +
+ + +
+
+ Merging with: {{data.mergeRecord._name}} +
+
+
+ +
+ +
+
+ Error:

+
+ {{error}}

+
+ +
+ Fields merged successfully +
+
+ + +
+
+ + +   + +
+
+ + + + + +
+
\ No newline at end of file diff --git a/src/app/core/admin/import-browse/merge-action-dialog/merge-action-dialog.component.scss b/src/app/core/admin/import-browse/merge-action-dialog/merge-action-dialog.component.scss new file mode 100644 index 000000000..4eaabbba5 --- /dev/null +++ b/src/app/core/admin/import-browse/merge-action-dialog/merge-action-dialog.component.scss @@ -0,0 +1,148 @@ +.match-row { + width: 100%; + display: flex; + flex-direction: row; + margin-top: 20px; +} + +.experimental-action-label { + font-weight:500; + margin-left: 25px; +} + +.spinner { + width: 100px !important; +height: 100px !important; +margin: auto; +} + +.dialog-row { + display:flex; + flex-direction: row; + justify-content: flex-start; + align-items: center; +} + +.config-label .mat-focused { + margin-top: -2px; +} + +.name-form { + width: 400px; +} + +.preset { + width: 250px; +} + +.top-button { + margin-left:5px; +} + +.top-button-container { + margin-top: 10px; + margin-left: 50px; +} + +.format { + margin-left: 10px; +} + +.padding { + margin-bottom: 15px; + padding-bottom:5px; + padding-top: 12px; +} + +.mat-warn { + color: var(--regular-red-color); + background-color: white; + border: 1px solid var(--regular-red-color); +} + + +::ng-deep sf-string-widget > div { + margin-left:50px; +} + + +::ng-deep .has-success > ::ng-deep sf-string-widget { + margin-left:20px; + +} + + +::ng-deep sf-string-widget > ::ng-deep .control-label { + min-width: 250px; +display: inline-block; +} + + +.top-label { + display: flex; + flex-direction:row; + width: 100%; +} + +.message { + font-weight: 500; + margin:10px; + font-size:18px; +} + +.message-container { + width:100%; + display:flex; + flex-direction:row; + justify-content: center; +} + +.form-row { + width:100%; + display:flex; + flex-direction:row; +} + +.top-margin { + margin-top:15px; +} + +.right-margin { + margin-right: 10px; +} + + + +::ng-deep .array-remove-button { + color: #4793d1; + background-color: white; + display: inline-block; + white-space: nowrap; + text-decoration: none; + vertical-align: baseline; + text-align: center; + margin-bottom: 8px; + margin-right: 0px; + min-width: 56px; + line-height: 30px; + padding: 0 12px; + border-radius: 4px; + overflow: visible; + } + + ::ng-deep .array-add-button { + background-color: #4793d1; + color: white; + display: inline-block; + white-space: nowrap; + text-decoration: none; + vertical-align: baseline; + text-align: center; + margin-top: 8px; + margin-right: 0px; + min-width: 56px; + line-height: 30px; + padding: 0 12px; + border-radius: 4px; + overflow: visible; + } \ No newline at end of file diff --git a/src/app/core/admin/import-browse/merge-action-dialog/merge-action-dialog.component.spec.ts b/src/app/core/admin/import-browse/merge-action-dialog/merge-action-dialog.component.spec.ts new file mode 100644 index 000000000..32185ac33 --- /dev/null +++ b/src/app/core/admin/import-browse/merge-action-dialog/merge-action-dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MergeActionDialogComponent } from './merge-action-dialog.component'; + +describe('MergeActionDialogComponent', () => { + let component: MergeActionDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ MergeActionDialogComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(MergeActionDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/admin/import-browse/merge-action-dialog/merge-action-dialog.component.ts b/src/app/core/admin/import-browse/merge-action-dialog/merge-action-dialog.component.ts new file mode 100644 index 000000000..652a7c43e --- /dev/null +++ b/src/app/core/admin/import-browse/merge-action-dialog/merge-action-dialog.component.ts @@ -0,0 +1,106 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { AdminService } from '@gsrs-core/admin/admin.service'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; + +@Component({ + selector: 'app-merge-action-dialog', + templateUrl: './merge-action-dialog.component.html', + styleUrls: ['./merge-action-dialog.component.scss'] +}) +export class MergeActionDialogComponent implements OnInit { + mergeSchema: any; + mergeModel = {}; + privateMergeModel = {}; + entity: string; + matches: any; + toMerge: string; + loading = false; + completed = false; + mergeResponse: any; + errors: Array = []; + success = true; + constructor( + private adminService: AdminService, + @Inject(MAT_DIALOG_DATA) public data: any + ) { + console.log(data); + if(data.recordId) { + + this.entity = data.recordId; + this.matches = data.matches; + + } + if(data.mergeRecord) { + this.toMerge = data.mergeRecord.ID; + } + } + + select(entry: any) { + this.toMerge = entry; + } + + ngOnInit(): void { + this.getMergeSchema(); + } + + getMergeSchema() { + this.adminService.getMergeActionSchema().subscribe(response => { + this.mergeSchema = response; + }); + } + + checkValue(event: any) { + this.privateMergeModel = event; + } + + submit() { + this.loading =true; + this.adminService.stagedRecordSingleAction(this.entity, 'merge', this.privateMergeModel, this.toMerge).subscribe(response => { + this.refresh(response.id); + }, error => { + this.loading = false; + }) + } + + restart() { + this.success = true; + this.loading = false; + this.completed = false; + this.mergeResponse = null; + this.errors = []; + this.mergeModel = {}; + this.privateMergeModel = {}; + } + + refresh(id: string): void { + this.adminService.processingstatus(id).subscribe(response => { + if (response.results) { + this.success = response.completeSuccess; + response.results.forEach(result => { + + + if(!this.success) { + let temp = result.message; + if (result.error) { + temp += ". " + result.error; + } + this.errors.push(temp); + } + }); + } + if (response.jobStatus === 'completed') { + + this.loading = false; + this.completed = true; + } else { + setTimeout(() => { + this.refresh(id); + }, 200); + } + + + }); + } + } + + diff --git a/src/app/core/admin/import-browse/name-display.pipe.ts b/src/app/core/admin/import-browse/name-display.pipe.ts new file mode 100644 index 000000000..23be46292 --- /dev/null +++ b/src/app/core/admin/import-browse/name-display.pipe.ts @@ -0,0 +1,24 @@ +import { Pipe, PipeTransform } from '@angular/core'; + + +@Pipe({ + name: 'namesDisplay' + }) + export class NamesDisplayStagingPipe implements PipeTransform { + transform(names: Array): Array { + names = names.slice().sort((a, b) => { + let returned = -1; + if (a.displayName) { + returned = -1; + } else if ( b.displayName === true) { + returned = 1; + } else if (b.preferred === true && a.displayName !== true) { + returned = 1; + } else if (!b.displayName && !a.displayName && a.name > b.name) { + returned = 1; + } + return returned; + }); + return names.slice(0, 4); + } + } \ No newline at end of file diff --git a/src/app/core/admin/import-browse/take-import.pipe.ts b/src/app/core/admin/import-browse/take-import.pipe.ts new file mode 100644 index 000000000..e104985a9 --- /dev/null +++ b/src/app/core/admin/import-browse/take-import.pipe.ts @@ -0,0 +1,16 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'take' +}) +export class TakeImportPipe implements PipeTransform { + + transform(items: Array, num: number): Array { + if (items && items.length && items.length > num) { + return items.slice(0, num); + } else { + return items; + } + } + +} diff --git a/src/app/core/admin/import-management/cv-import/cv-import.component.html b/src/app/core/admin/import-management/cv-import/cv-import.component.html new file mode 100644 index 000000000..109b104ac --- /dev/null +++ b/src/app/core/admin/import-management/cv-import/cv-import.component.html @@ -0,0 +1,22 @@ + +
+ + + + Other (New Value) + Clear selection + + {{term.display}} + + {{privateMod}} ({{domain ? 'not in CV' : 'not in field list'}}) + + + +
+
+ +
diff --git a/src/app/core/admin/import-management/cv-import/cv-import.component.scss b/src/app/core/admin/import-management/cv-import/cv-import.component.scss new file mode 100644 index 000000000..0da2825c5 --- /dev/null +++ b/src/app/core/admin/import-management/cv-import/cv-import.component.scss @@ -0,0 +1,39 @@ +.risen { + margin-top: -15px; +} + + +.custom { + padding-bottom: 10px; + padding-top:10px; + border-bottom: 1px solid var(--box-shadow-color-6); +} + +.deselect { + color: var(--text-color); +} + +.add-term { + margin-left: -7px; + margin-bottom: -10px; +} + +.form-row { + display: flex; + justify-content: space-around; + align-items: flex-end; +} + +.value, .display, .description { + flex-grow: 1; + padding-right: 15px; +} +.cv-field { + width:100%; +} + + +.add-term { + color: var(--secondary-blue); + font-style: italic; +} diff --git a/src/app/core/admin/import-management/cv-import/cv-import.component.ts b/src/app/core/admin/import-management/cv-import/cv-import.component.ts new file mode 100644 index 000000000..9b0598217 --- /dev/null +++ b/src/app/core/admin/import-management/cv-import/cv-import.component.ts @@ -0,0 +1,181 @@ +import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core'; +import {ControlledVocabularyService, VocabularyTerm} from '@gsrs-core/controlled-vocabulary'; +import {SubunitSelectorDialogComponent} from '@gsrs-core/substance-form/subunit-selector-dialog/subunit-selector-dialog.component'; +import {MatDialog} from '@angular/material/dialog'; +import {UtilsService} from '@gsrs-core/utils'; +import {OverlayContainer} from '@angular/cdk/overlay'; +import {Subscription} from 'rxjs'; +import {CvDialogComponent} from '@gsrs-core/substance-form/cv-dialog/cv-dialog.component'; +import {DataDictionaryService} from '@gsrs-core/utils/data-dictionary.service'; +import {AuthService} from '@gsrs-core/auth'; +import { FragmentWizardComponent } from '@gsrs-core/admin/fragment-wizard/fragment-wizard.component'; + +/* + used for any input that uses cv vocabulary to handle custom values after selecting 'other' + */ + +@Component({ + selector: 'app-cv-import', + templateUrl: './cv-import.component.html', + styleUrls: ['./cv-import.component.scss'] +}) +export class CvImportComponent implements OnInit, OnDestroy { + @Input() vocabulary?: any; + @Input() title?: string; + @Input() domain?: string; + @Input() key?: string; + @Input() required?: boolean; + @Input() disable?: boolean; + @Output() + valueChange = new EventEmitter(); + vocabName = ''; + privateMod: any; + dictionary: any; + private overlayContainer: HTMLElement; + private subscriptions: Array = []; + isAdmin: boolean; + + constructor( + public cvService: ControlledVocabularyService, + private dialog: MatDialog, + private utilsService: UtilsService, + private overlayContainerService: OverlayContainer, + private dictionaryService: DataDictionaryService, + private authService: AuthService + ) { } + + ngOnInit() { + if (this.vocabulary) { + this.vocabulary = this.addOtherOption(this.vocabulary, this.privateMod); + } else if (this.key) { + this.dictionary = this.dictionaryService.getDictionaryRow(this.key); + if (!this.title) { + this.title = this.dictionary.fieldName; + } + this.vocabName = this.dictionary.CVDomain; + const cvSubscription = this.cvService.getDomainVocabulary(this.vocabName).subscribe(response => { + this.vocabulary = response[this.vocabName].list; + }); + this.subscriptions.push(cvSubscription); + } else { + this.vocabulary = []; + this.vocabName = this.domain; + const cvSubscription = this.cvService.getDomainVocabulary(this.vocabName).subscribe(response => { + this.vocabulary = response[this.vocabName].list; + }); + this.subscriptions.push(cvSubscription); + + } + this.overlayContainer = this.overlayContainerService.getContainerElement(); + this.isAdmin = this.authService.hasRoles('admin'); + } + + ngOnDestroy() { + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + + @Input() + set model(mod: any) { + this.privateMod = mod; + + } + + get model(): any { + return this.privateMod; + } + + select(event: any): void { + this.privateMod = event; + this.valueChange.emit(this.privateMod); + } + + addOtherOption(vocab: Array, property: string): any { + if ((vocab) && (vocab.some(r => property === r.value))) { + } else { + } + return vocab; + } + + inCV(vocab: Array, property: string): boolean { + if (vocab) { + return vocab.some(r => property === r.value); + } else { + return true; + } + + } + + addToVocab() { + const vocabSubscription = this.cvService.fetchFullVocabulary(this.vocabName).subscribe ( response => { + if (response.content && response.content.length > 0) { + const toPut = response.content[0]; + this.openDialog(toPut, this.privateMod); + } + }); + this.subscriptions.push(vocabSubscription); + } + + updateOrigin(event): void { + if (event && event.value !== '') { + this.privateMod = event.value; + this.valueChange.emit(this.privateMod); + } + } + + openDialog(vocab: any, term: string): void { + let thisy = window.pageYOffset; + window.scroll({ + top: 0, + left: 0, + behavior: 'auto' +}); + + // this.overlayContainer = this.overlayContainerService.getContainerElement(); + if (vocab.domain === 'NUCLEIC_ACID_LINKAGE' || vocab.domain === 'NUCLEIC_ACID_SUGAR'){ + this.overlayContainer.style.zIndex = '1005'; + + let dialogRef = this.dialog.open(FragmentWizardComponent, { + data: {'vocabulary': vocab, 'term': term}, + width: '1040px', + height: '85%' + }); + this.overlayContainer.style.zIndex = '1005'; + const dialogSubscription = dialogRef.afterClosed().subscribe(response => { + window.scroll({ + top: thisy, + left: 0, + behavior: 'auto' + }); + this.overlayContainer.style.zIndex = null; + if (response ) { + this.privateMod = response.display; + this.vocabulary.push(response); + this.valueChange.emit(this.privateMod); + } + }); + this.subscriptions.push(dialogSubscription); + } else { + let dialogRef = this.dialog.open(CvDialogComponent, { + data: {'vocabulary': vocab, 'term': term}, + width: '1040px' + }); + // this.overlayContainer.style.zIndex = '1002'; + const dialogSubscription = dialogRef.afterClosed().subscribe(response => { + // this.overlayContainer.style.zIndex = null; + window.scroll({ + top: thisy, + left: 0, + behavior: 'auto' +}); + if (response ) { + this.privateMod = response.display; + this.vocabulary.push(response); + this.valueChange.emit(this.privateMod); + } + }); + this.subscriptions.push(dialogSubscription); + } + } +} diff --git a/src/app/core/admin/import-management/import-dialog/import-dialog.component.html b/src/app/core/admin/import-management/import-dialog/import-dialog.component.html new file mode 100644 index 000000000..22f659141 --- /dev/null +++ b/src/app/core/admin/import-management/import-dialog/import-dialog.component.html @@ -0,0 +1,194 @@ +

Action Settings

+

{{settingsActive.label}}

+
+ +
+ + + + {{type}} + + + +
+
+
Code System
+
+ +
+
+
+
Code
+
+ +
+
+ +
+
Code Type
+
+ +
+ +
+
+ +
+
+
Name
+
+ + +
+
+
+
Name Type
+
+ +
+
+
+
Language
+
+ +
+
+ +
+ +
+
+
Name
+
+
+
+
Property Type
+
+ +
+ +
+
+
Value Range
+
+ +
+
+
+
Value Units
+ +
+ +
+ + +
+
+ +
+
+
Document Type
+
+ +
+ +
+
+
Citation
+
+ +
+
+
Reference ID
+
+
+
+
UUID
+
+
+ +
+
+ +
+ A standard reference will be added to the substance and assigned to the definition and to any names created. +
+
+ +
+
+
Molfile
+
+
+
+ This value will be pulled from molefile field of your file. Please do not change without a specific reason. +
+ +
+
+
+
Note
+
+ +
+ + +
+ +
+ + +
+ + + +   + + + + \ No newline at end of file diff --git a/src/app/core/admin/import-management/import-dialog/import-dialog.component.scss b/src/app/core/admin/import-management/import-dialog/import-dialog.component.scss new file mode 100644 index 000000000..9dcae8bde --- /dev/null +++ b/src/app/core/admin/import-management/import-dialog/import-dialog.component.scss @@ -0,0 +1,18 @@ +.set-width { + width: 400px; +} + +app-cv-import { + margin-top: -10px; +} + +.label-padding { + margin-top: 5px; +margin-bottom: 10px; +} + +.field-note { + font-size: 15px; + margin-top: 25px; + margin-left: 25px; +} \ No newline at end of file diff --git a/src/app/core/admin/import-management/import-dialog/import-dialog.component.spec.ts b/src/app/core/admin/import-management/import-dialog/import-dialog.component.spec.ts new file mode 100644 index 000000000..6922681d0 --- /dev/null +++ b/src/app/core/admin/import-management/import-dialog/import-dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ImportDialogComponent } from './import-dialog.component'; + +describe('ImportDialogComponent', () => { + let component: ImportDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ImportDialogComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ImportDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/admin/import-management/import-dialog/import-dialog.component.ts b/src/app/core/admin/import-management/import-dialog/import-dialog.component.ts new file mode 100644 index 000000000..7c139a852 --- /dev/null +++ b/src/app/core/admin/import-management/import-dialog/import-dialog.component.ts @@ -0,0 +1,87 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { ControlledVocabularyService } from '@gsrs-core/controlled-vocabulary'; +import { UtilsService } from '@gsrs-core/utils'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { FormControl } from '@angular/forms'; + +@Component({ + selector: 'app-import-dialog', + templateUrl: './import-dialog.component.html', + styleUrls: ['./import-dialog.component.scss'] +}) +export class ImportDialogComponent implements OnInit { + settingsActive: any; + setType: string; + fieldList: Array ; + formControl = new FormControl(); + codeActions: any = { + "codeSystem": "", + "code": "", + "codeType": ""}; + propertyActions: any = { + "name": "", + "propertyType": "", + "valueRange": "", + "valueUnits": "" + }; + nameActions: any = { + "name": "", + "nameType": "", + "lang": "", + "referenceUUIDs": [] + }; + noteActions: any = { + "note":"" + }; + settingTypes = ["Create Name Action", "Create Code Action", "Create Property Action", "Create Note Action"]; + constructor( + public cvService: ControlledVocabularyService, + private utilsService: UtilsService, + public dialogRef: MatDialogRef, + + + @Inject(MAT_DIALOG_DATA) public data: any + ) { + this.settingsActive = data.settingsActive; + this.setType = data.settingsActive.label; + this.fieldList = data.fieldList; + + } + + ngOnInit(): void { + console.log(this.fieldList); + } + + switchAction(action?: any) { + // if (action.value != this.setType) { + if (action.value == "Create Name Action") { + this.settingsActive.actionParameters = this.nameActions; + this.settingsActive.actionName = 'common_name'; + } else if (action.value == "Create Code Action") { + this.settingsActive.actionParameters = this.codeActions; + this.settingsActive.actionName = 'code_import'; + } else if (action.value == "Create Property Action") { + this.settingsActive.actionParameters = this.propertyActions; + this.settingsActive.actionName = 'property_import'; + }else if (action.value == "Create Note Action") { + this.settingsActive.actionParameters = this.noteActions; + this.settingsActive.actionName = 'note_import'; + } + this.settingsActive.label = action.value; + // } + // console.log(this.settingsActive); + + } + updateType(event: any, field: string) { + this.settingsActive.actionParameters[field] = event; + } + + close(send?: string) { + if (send) { + this.dialogRef.close(this.settingsActive); + } else { + this.dialogRef.close(); + } + } + +} diff --git a/src/app/core/admin/import-management/import-management.component.html b/src/app/core/admin/import-management/import-management.component.html new file mode 100644 index 000000000..ad3773811 --- /dev/null +++ b/src/app/core/admin/import-management/import-management.component.html @@ -0,0 +1,281 @@ + +
+ + +
+
+

Step 1: Record File Import

+
+
+
+ +
+
+
{{filename? filename: 'no file chosen'}}
+ +
+ + +
+ + + +
+

+ +
+ +
+ + {{adapter.adapterName}} +
+
+ +
+
+ +
{{adapter.description}}. Supported extensions: + {{ext}}{{!last? ',':''}}
+
+ +
+ +
+ + +
+ +
{{message}}
+   + + + + + + +
+ + +
+ + +
+ + +
+
+
+ + + + + +
+

Step 2: Configure Importer Actions

+

+
+
+ + +
+ Import Action +
+ +
+ Action Details +
+ + +
+ Ignore Field +
+ +
+
+ Preview + +
+ + + +
+
+
+
+ + + +
+ {{action.label? replaceAction(action.label) : "no label"}} +
+ + +
+ +
+
+ + {{action.fileField ? action.fileField + ' field from file' : ''}} + +
+ + +
+
+ +
+
+ + +
+ + + + + + + +
+
+
+ + +
Show     + + {{term}} + +   records +
+ + + +

+
+
+
+
+
+
+ + Record {{previewIndex + 1}} of {{previewTotal}} + +
+
+ + + +
+
+
Names:
+
+ + + + +
+
+
+
+
Codes:
+
+ {{code.codeSystem}}: {{code.code}} +
+
+
+
+
+ Formula: {{preview[previewIndex].data.structure.formula}} +
+
+ Mol Weight: {{preview[previewIndex].data.structure.mwt}} +
+
+
+
+ +
+
+ + + + +
+

+ +
+
+
+
+

Step 3: Record Staging

+
+

Importing {{filename}}

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

+
+ Status: {{executeStatus}}

+ Records Processed: {{completedRecords}} +
+

+
+
+ + + +
+ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/app/core/admin/import-management/import-management.component.scss b/src/app/core/admin/import-management/import-management.component.scss new file mode 100644 index 000000000..27aafad27 --- /dev/null +++ b/src/app/core/admin/import-management/import-management.component.scss @@ -0,0 +1,455 @@ +.italics { + font-style: italic; + color: var(--text-color); +} + +.inline-vertical { + // display: inline; + // vertical-align: middle; + margin-top: auto; + margin-bottom: auto; +} + +.file-name { + display: flex; + text-align: center; + margin-left: 15px; + margin-top: auto; + margin-bottom: auto; +} + +.full-row { + width: 100%; + display: flex; + flex-direction: row; + justify-content: flex-start; + flex-wrap:wrap; + padding: 10px; + + .action-list { + max-width:70%; + width: 900px; + } + + .preview-window { + max-width: 28%; + margin-right:25px; + width: 500px; + } +} + +.spinner { + +} +.action-list { + width: 70%; + +} + +.importer-radio { + display: flex; flex-direction: column; +} + +.half-column { + width: 50%; +} + +.label-value { + width: 100%; + .label { + width:30%; + } + .value-container { + width: 66%; + display:flex; + flex-wrap: wrap; + + .value { + width: 100%; + } + } + + .ignore-container { + width: 15% + } +} + +.preview-container { + margin-left: auto; +margin-right: auto; +padding-left: 7%; +} + +.top-button-row { + width: 100%; + +display: flex; +flex-direction: row; +align-items: center; + justify-content: center; + +.top-button { + font-size: 20px; +margin-right: 5px; +padding-top: 4px; +padding-bottom: 4px; +width: 200px; +} +} +.progress-container { + width: 100%; + display: flex; + flex-direction: column; + margin-bottom: 12px; + + .load-progress { + width: 100%; + } + + .bar-label { + font-size: 14px; + padding-bottom: 3px; + } +} + +.spinner { + width: 100px !important; + height: 100px !important; + margin: auto; + margin-top: 75px !important; + margin-left: 150px !important; + mat-progress-spinner { + margin: auto; + width: 100px !important; + height: 100px !important; + } +} + +.set-width-label { + min-width: 430px; +} + +.load-progress { + height: 17px; + margin-bottom: 5px; + font-size:12px; + color: var(--regular-black-color); + -webkit-text-fill-color: var(--regular-white-color); /* Will override color (regardless of order) */ + -webkit-text-stroke-width: 1px; + -webkit-text-stroke-color: var(--regular-black-color); +} + +.load-fail { + // transform: rotate(180deg); + margin-top: -22px; + ::ng-deep .mat-progress-bar-fill { + background-color: var(--error); + z-index: 1; + + } + ::ng-deep .mat-progress-bar-fill::after { + background-color: var(--error); + // z-index: 1; + + } + + + + + ::ng-deep .mat-progress-bar-buffer { + background: var(--progress-bar-buffer-bg-color); + } +} + +.loading-spinner { + width: 70px; + mat-progress-spinner { + margin: auto; + } +} +.load-fail-old { + transform: rotate(180deg); + margin-top: -22px; + ::ng-deep .mat-progress-bar-fill { + background-color: var(--error); + z-index: 2; + + + } + ::ng-deep .mat-progress-bar-fill::after { + background-color: var(--error); + z-index: 2; + + + } + + + + + ::ng-deep .mat-progress-bar-buffer { + background: var(--progress-bar-buffer-bg-color); + } +} + +.load-success { + ::ng-deep .mat-progress-bar-fill { + // background-color: var(--error); + z-index: 2; + + } + ::ng-deep .mat-progress-bar-fill::after { + // background-color: var(--error); + // z-index: 2; + + } + ::ng-deep .mat-progress-bar-buffer { + background: var(--progress-bar-buffer-bg-color); + } +} + + +.load-success-old { + ::ng-deep .mat-progress-bar-fill { + // background-color: var(--error); + z-index: 2; + + } + ::ng-deep .mat-progress-bar-fill::after { + // background-color: var(--error); + z-index: 2; + + } + ::ng-deep .mat-progress-bar-buffer { + background: var(--progress-bar-buffer-bg-color); + } +} + +.progress-container { + width: 100%; + display: flex; + flex-direction: column; + + .load-progress { + width: 100%; + } + + .bar-label { + font-size: 14px; + } +} + + + +.spinner-container { + display: flex; + flex-direction: row; + + .spinner-labels { + + } + + .spinner-row { + width: 33%; + flex-direction: column; + + .spinner-labels { + display: flex; + flex-direction: row; + + + .spinner { + width: 50px; + } + } + } +} + +.count-label { +margin-top: auto; +margin-bottom: auto; +} + +.loader-container { + width: 100%; + margin: 10px; +} + +.lower-body { + width: 100%; + display:flex; + flex-direction: row; +} + +.stat-container { + width: 30%; + margin:10px; + min-width: 250px; +} + +.label-value{ + width: 100%; + display:flex; + flex-direction: row; + padding-bottom: 10px; + + .label { + width: 25%; + min-width: 125px; + font-weight: 500; + } + + .value { + width: 69%; + } +} + +.example-section { + display: flex; + align-content: center; + align-items: center; + height: 60px; + } + +.preview-limit { + width: 60px; + font-weight: 500; +} + .mirror { + margin-top: -105px; + transform: scale(-1, 1); + + ::ng-deep circle { + stroke: var(--error); + } + + + + ::ng-deep .mat-progress-spinner-buffer { + background: var(--progress-spinner-buffer-bg-color); + } + } + + .mirror-test { + margin-left: -125px; + transform: scale(-1, 1); + + ::ng-deep circle { + stroke: var(--error); + } + + ::ng-deep .mat-progress-spinner-buffer { + background: var(--progress-spinner-buffer-bg-color); + } +} + + + .overlap { + ::ng-deep .mat-progress-bar-fill::after { + z-index: 2; + } + } + + .deleted { + margin: auto; + padding: 10px; + color: var(--regular-grey-color); + font-style: italics; + font-size: 18px; + } + + + .label-row { + width: 100%; + display: flex; + flex-direction: row; + justify-content: left; + + .count-row-label { + margin-left: auto; + margin-right: auto; + } + } + + .count-cont { + text-align: center; + color: var(--regular-white-color); + margin-top: -22px; + font-weight: bold; + z-index: 5; + font-size: 15px; + } + + .file-row { + width: 75%; + height: 40px; + display: flex; + flex-direction: row; + } + + .options-row { + width: 25%; + display: flex; + flex-direction: column; + } + + .bottom { + padding-top: 50px; + } + + + .substance-data { + flex-direction: column; + + .label { + margin-bottom: 10px; + } + + .value, .code-system { + padding-left: 20px; + } +} + +.step2-header { + width: 100%; + display:flex; + font-size: 18px; +} + +.step2-actions-container { + width: 70%; + justify-content: space-between; +} + +.step2-preview-container { + width: 30%; +} + +.actions-container { + +} + +.step2-button { + font-weight: 500; + font-size: 16px; + margin-right: 20px; +} + +.ignore-col { + width: 125px; +padding-left: 40px; +} + + +.details-col { + min-width: 100px; +} + +.label-col { + min-width: 200px; + padding-left: 25px; +} + +.action-row { + padding-top: 5px; + padding-bottom: 5px; +} \ No newline at end of file diff --git a/src/app/core/admin/import-management/import-management.component.spec.ts b/src/app/core/admin/import-management/import-management.component.spec.ts new file mode 100644 index 000000000..497ee3857 --- /dev/null +++ b/src/app/core/admin/import-management/import-management.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ImportManagementComponent } from './import-management.component'; + +describe('ImportManagementComponent', () => { + let component: ImportManagementComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ImportManagementComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ImportManagementComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/admin/import-management/import-management.component.ts b/src/app/core/admin/import-management/import-management.component.ts new file mode 100644 index 000000000..27772d2c5 --- /dev/null +++ b/src/app/core/admin/import-management/import-management.component.ts @@ -0,0 +1,450 @@ +import { Component, OnInit } from '@angular/core'; +import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms'; +import { AdminService } from '@gsrs-core/admin/admin.service'; +import { take } from 'rxjs/operators'; +import { Router, ActivatedRoute, NavigationExtras } from '@angular/router'; +import { LoadingService } from '@gsrs-core/loading'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { MatDialog, MatDialogRef, MatDialogModule, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { StructureService, StructureImageModalComponent } from '@gsrs-core/structure'; +import { ImportDialogComponent } from '@gsrs-core/admin/import-management/import-dialog/import-dialog.component'; +import { isString } from 'util'; +import { ImportScrubberComponent } from '@gsrs-core/admin/import-management/import-scrubber/import-scrubber.component'; + + +@Component({ + selector: 'app-import-management', + templateUrl: './import-management.component.html', + styleUrls: ['./import-management.component.scss'] +}) +export class ImportManagementComponent implements OnInit { +demo: any; +uploadForm: FormGroup; +filename: string; +fileType: string; +audit = false; +processing = false; +message: string; +adapterSettings: any = null; +adapterKey: string; +settingsIndex: number; +settingsActive: any; +step = 1; +private overlayContainer: HTMLElement; +postResp: any; +save = false; +preview: any; +fileID: string; +previewDemo: any; +previewIndex = 0; +previewTotal = 0; +previewLoading = false; +previewLimit = 10; +previewLimitList = [1, 10, 100, 'all']; +toIgnore = []; +fieldList: Array; +extension: string; +executeStatus: string; +executeID:string; +completedRecords: any; +executeLoading = false; +scrubberSchema: any; +scrubberModel: any; +uuidInt = 1; +constructor( + public formBuilder: FormBuilder, + public adminService: AdminService, + private router: Router, + private route: ActivatedRoute, + private loadingService: LoadingService, + private overlayContainerService: OverlayContainer, + private dialog: MatDialog, + private structureService: StructureService + + + + +) { } + +setAdapter(event?: any) { + this.adapterKey = event.value; + + this.demo.forEach(val => { + if (val.adapterKey === event.value) { + this.adapterSettings = val.parameters; + } + }); + +} + +createFieldList(values: Array): void { +this.fieldList = []; + values.forEach(item => { + let field = ""; + if (item.fieldName) { + field = item.fieldName; + } else if(isString(item)) { + field = item; + + } + // field = field.replace(/\(/g,"{").replace(/\)/g,"}"); + let temp = {"value":'{{'+field+'}}', "display": field}; + // temp.value = '{{'+field+'}}'; + //temp.display = field; + this.fieldList.push(temp); + }); + +} + +openScrubber(templateRef:any, index: number): void { + this.save = false; + this.settingsActive = this.postResp.adapterSettings.actions[index]; + + + + const dialogref = this.dialog.open(ImportScrubberComponent, { + minHeight: '500px', + width: '800px', + data: { + scrubberSchema: this.scrubberSchema, + scrubberModel: this.scrubberModel + } + }); + this.overlayContainer.style.zIndex = '1002'; + + dialogref.afterClosed().subscribe(result => { + this.overlayContainer.style.zIndex = null; + + if(result) { + this.scrubberModel = result; + } + + }); +} + +openAction(templateRef:any, index: number): void { + this.save = false; + this.settingsActive = this.postResp.adapterSettings.actions[index]; + + + + const dialogref = this.dialog.open(ImportDialogComponent, { + minHeight: '500px', + width: '800px', + data: { + settingsActive: JSON.parse(JSON.stringify(this.postResp.adapterSettings.actions[index])), + fieldList: this.fieldList + } + }); + this.overlayContainer.style.zIndex = '1002'; + + dialogref.afterClosed().subscribe(result => { + this.overlayContainer.style.zIndex = null; + + if(result) { + this.postResp.adapterSettings.actions[index] = result; + } + + }); +} + +changePreview(direction: string) { + if (direction === 'back') { + this.previewIndex-=1; + } else { + this.previewIndex += 1; + } + if (this.preview[this.previewIndex].data.structure && this.preview[this.previewIndex].data.structure.molfile) { + this.structureService.interpretStructure(this.preview[this.previewIndex].data.structure.molfile).subscribe(response => { + this.preview[this.previewIndex].data.structureID = response.structure.id; + }); + } else { + } + +} + +ngOnInit() { + this.overlayContainer = this.overlayContainerService.getContainerElement(); + + + this.uploadForm = this.formBuilder.group({ + file: [''], + fileType: ['SDF'] }); + this.fileType = 'SDF'; + + this.adminService.getAdapters().subscribe(result => { + if(result) { + // this.setDemo(); + this.demo = result; + } else { + alert('adapters set but invalid response'); + } + this.getExtensions(); + + }, error => { + alert('failed to fetch adapters. error in console'); + this.getExtensions(); + + + }); + + +} + + close(param?: string) { + if (!param) { + param = null; + } else { + this.save = true; + } + this.dialog.closeAll(); + } + + getExtensions() { + let extArr = []; + + this.demo.forEach(entry => { + entry.fileExtensions.forEach(ext => { + if (!extArr.includes(ext)) { + extArr.push(ext); + } + }); + }); + this.extension = ""; + for (let i = 0; i < extArr.length; i++) { + this.extension += "." + extArr[i]; + if (i !== extArr.length - 1) { + this.extension += ", "; + } + } + } + + toggleIgnore(action: any, index: any) { + if (action.actionName && action.actionName == 'no-op') { + action.actionName = this.toIgnore[index].actionName; + } else { + action.actionName = 'no-op'; + } + } + + onSubmit() { + const formData = new FormData(); + this.loadingService.setLoading(true); + + formData.append('file', this.uploadForm.get('file').value); + formData.append('file-type', this.fileType); + this.adminService.postAdapterFile(formData, this.adapterKey).pipe(take(1)).subscribe(response => { + console.log(response); + this.loadingService.setLoading(false); + this.step = 3; + this.postResp = response; + this.fileID = response.id; + this.toIgnore = this.postResp.adapterSettings && this.postResp.adapterSettings.actions? JSON.parse(JSON.stringify(this.postResp.adapterSettings.actions)) : null; + + if (this.postResp.adapterSchema && this.postResp.adapterSchema.fields) { + this.createFieldList(this.postResp.adapterSchema.fields); + } else if (this.postResp.adapterSchema && this.postResp.adapterSchema['SDF Fields']) { + this.createFieldList(this.postResp.adapterSchema['SDF Fields']); + } + + this.callPreview(); + }, error => { + this.message = 'File could not be uploaded'; + alert('error in upload call, continuing with non-api demo. Error in console'); + this.step = 3; + this.fileID = this.postResp.id; + this.loadingService.setLoading(false); + if (this.postResp.adapterSchema && this.postResp.adapterSchema.fields) { + this.createFieldList(this.postResp.adapterSchema.fields); + } else if (this.postResp.adapterSchema && this.postResp.adapterSchema['SDF Fields']) { + this.createFieldList(this.postResp.adapterSchema['SDF Fields']); + } + }); + } + + putSettings(): void { + this.loadingService.setLoading(true); + this.step = 4; + let tosend = JSON.parse(JSON.stringify(this.postResp)); + this.adminService.executeAdapterAsync(this.fileID, tosend).subscribe(response => { + this.executeStatus = response.jobStatus; + this.loadingService.setLoading(false); + this.executeLoading = true; + + this.processingstatus(response.id); + }, error => { + this.loadingService.setLoading(false); + console.log(error); + alert('Error: see console log for server error'); + + }); + + } + + processingstatus(id: string): void { + this.adminService.processingstatus(id).subscribe(response => { + this.executeStatus = response.statusMessage; + this.completedRecords = response.completedRecordCount; + if (response.jobStatus === 'completed') { + this.executeLoading = false; + } else { + setTimeout(() => { + this.processingstatus(id); + }, 200); + } + + + }); + } + + + addRef() { + + let temp = { + "actionName": "public_reference", + "actionParameters": { + "docType": "", + "citation": "", + "referenceID": "", + "uuid": "[[UUID_"+this.uuidInt+"]]", + "publicDomain": false, + "tags":[] + }, + "label": "Create Reference" + } + this.uuidInt++; + this.postResp.adapterSettings.actions.push(temp); + this.openAction(null, this.postResp.adapterSettings.actions.length-1); + } + + + +onFileSelect(event): void { + if (event.target.files.length > 0) { + const file = event.target.files[0]; + this.filename = file.name; + let extension = file.name.split('.'); + extension = extension[extension.length - 1]; + + if(this.demo) { + this.demo.forEach(val => { + val.fileExtensions.forEach(ext => { + if (ext.toUpperCase() == extension.toUpperCase()) { + this.adapterSettings = val.parameters; + this.adapterKey = val.adapterKey; + } + }); + + }); + } + + this.uploadForm.get('file').setValue(file); + + } +} + +openInput(): void { + document.getElementById('fileInput').click(); +} + +stagingArea(sendFile?: boolean): void { + let navigationExtras: NavigationExtras = {queryParams: {}}; + + if(sendFile) { + const newtest = this.postResp.filename.replaceAll(".", "!."); + + navigationExtras.queryParams = {'facets': 'Source*' + newtest.replace(/^.*[\\\/]/, '') + '.true'}; + + } + this.router.navigate(['admin/staging-area'], navigationExtras); + +} + +ImportReload(): void { + const currentUrl = this.router.url; + this.router.navigate(['/admin/import']); +} + + +callPreview(): void { + + const formData = new FormData(); + // this.loadingService.setLoading(true); + this.previewLoading = true; + this.previewIndex = 0; + + formData.append('file', this.uploadForm.get('file').value); + formData.append('file-type', this.fileType); + this.preview = []; + let tosend = JSON.parse(JSON.stringify(this.postResp)); + this.adminService.previewAdapter(this.fileID, tosend, this.adapterKey, this.previewLimit ).pipe(take(1)).subscribe(response => { + this.preview = []; + this.previewLoading = false; + response.dataPreview.forEach(entry => { + if (entry.data) { + this.preview.push(entry); + this.previewTotal = this.preview.length; + } + + }); + if (this.preview[0].data.structure) { + this.structureService.interpretStructure(this.preview[0].data.structure.molfile).subscribe(response => { + this.preview[0].data.structureID = response.structure.id; + }); + } + this.previewLoading = false; + + + }, error => { + console.log(error); + + this.preview = []; + + this.previewLoading = false; + + }); +} + + + +replaceAction(s: string) { + return s && s.replace(' Action',''); +} + +openImageModal(preview: any): void { + + let data: any; + + if (preview.substanceClass === 'chemical') { + data = { + structure: preview.structureID, + uuid: preview.uuid, + names: preview.names + }; + } else { + data = { + structure: preview.structureID, + names: preview.names + }; + } + + const dialogRef = this.dialog.open(StructureImageModalComponent, { + width: '670px', + height: '670px', + panelClass: 'structure-image-panel', + data: data + }); + + this.overlayContainer.style.zIndex = '1002'; + + const subscription = dialogRef.afterClosed().subscribe(() => { + this.overlayContainer.style.zIndex = null; + subscription.unsubscribe(); + }, () => { + this.overlayContainer.style.zIndex = null; + subscription.unsubscribe(); + }); +} + + +} diff --git a/src/app/core/admin/import-management/import-scrubber/import-scrubber.component.html b/src/app/core/admin/import-management/import-scrubber/import-scrubber.component.html new file mode 100644 index 000000000..749bc1e54 --- /dev/null +++ b/src/app/core/admin/import-management/import-scrubber/import-scrubber.component.html @@ -0,0 +1,9 @@ +

Data Cleaning / Scrubbing

+
+ +
+
+ +   + +
\ No newline at end of file diff --git a/src/app/core/admin/import-management/import-scrubber/import-scrubber.component.scss b/src/app/core/admin/import-management/import-scrubber/import-scrubber.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/core/admin/import-management/import-scrubber/import-scrubber.component.spec.ts b/src/app/core/admin/import-management/import-scrubber/import-scrubber.component.spec.ts new file mode 100644 index 000000000..4de4721d8 --- /dev/null +++ b/src/app/core/admin/import-management/import-scrubber/import-scrubber.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ImportScrubberComponent } from './import-scrubber.component'; + +describe('ImportScrubberComponent', () => { + let component: ImportScrubberComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ImportScrubberComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ImportScrubberComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/admin/import-management/import-scrubber/import-scrubber.component.ts b/src/app/core/admin/import-management/import-scrubber/import-scrubber.component.ts new file mode 100644 index 000000000..7684f14a8 --- /dev/null +++ b/src/app/core/admin/import-management/import-scrubber/import-scrubber.component.ts @@ -0,0 +1,52 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { AdminService } from '@gsrs-core/admin/admin.service'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; + +@Component({ + selector: 'app-import-scrubber', + templateUrl: './import-scrubber.component.html', + styleUrls: ['./import-scrubber.component.scss'] +}) +export class ImportScrubberComponent implements OnInit { + + scrubberSchema: any; + scrubberModel = {}; + privateMergeModel = {}; + entity: string; + matches: any; + toMerge: string; + loading = false; + completed = false; + mergeResponse: any; + errors: Array = []; + success = true; + constructor( + private adminService: AdminService, + public dialogRef: MatDialogRef, + + @Inject(MAT_DIALOG_DATA) public data: any + ) { + this.scrubberSchema = data.scrubberSchema; + this.scrubberModel = data.scrubberModel; + } + + select(entry: any) { + this.toMerge = entry.uuid; + } + + ngOnInit(): void { + } + + + checkValue(event: any) { + this.privateMergeModel = event; + } + + submit() { + this.dialogRef.close(this.privateMergeModel); + } + + + } + + diff --git a/src/app/core/admin/monitor/monitor.component.scss b/src/app/core/admin/monitor/monitor.component.scss index 6de07b2b9..042d37e93 100644 --- a/src/app/core/admin/monitor/monitor.component.scss +++ b/src/app/core/admin/monitor/monitor.component.scss @@ -1,6 +1,6 @@ .italics { font-style: italic; - color: rgba(0, 0, 0, .5); + color: var(--text-color); } .file-name { @@ -31,22 +31,22 @@ height: 17px; margin-bottom: 5px; font-size:12px; - color: black; - -webkit-text-fill-color: white; /* Will override color (regardless of order) */ + color: var(--regular-black-color); + -webkit-text-fill-color: var(--regular-white-color); /* Will override color (regardless of order) */ -webkit-text-stroke-width: 1px; - -webkit-text-stroke-color: black; + -webkit-text-stroke-color: var(--regular-black-color); } .load-fail { // transform: rotate(180deg); margin-top: -22px; ::ng-deep .mat-progress-bar-fill { - background-color: rgb(173, 26, 26); + background-color: var(--error); z-index: 1; } ::ng-deep .mat-progress-bar-fill::after { - background-color: rgb(173, 26, 26); + background-color: var(--error); // z-index: 1; } @@ -55,7 +55,7 @@ ::ng-deep .mat-progress-bar-buffer { - background: rgba(0, 0, 0, 0.01); + background: var(--progress-bar-buffer-bg-color); } } @@ -67,13 +67,13 @@ transform: rotate(180deg); margin-top: -22px; ::ng-deep .mat-progress-bar-fill { - background-color: rgb(173, 26, 26); + background-color: var(--error); z-index: 2; } ::ng-deep .mat-progress-bar-fill::after { - background-color: rgb(173, 26, 26); + background-color: var(--error); z-index: 2; @@ -83,40 +83,40 @@ ::ng-deep .mat-progress-bar-buffer { - background: rgba(0, 0, 0, 0.01); + background: var(--progress-bar-buffer-bg-color); } } .load-success { ::ng-deep .mat-progress-bar-fill { - // background-color: rgb(173, 26, 26); + // background-color: var(--error); z-index: 2; } ::ng-deep .mat-progress-bar-fill::after { - // background-color: rgb(173, 26, 26); + // background-color: var(--error); // z-index: 2; } ::ng-deep .mat-progress-bar-buffer { - background: rgba(0, 0, 0, 0.01); + background: var(--progress-bar-buffer-bg-color); } } .load-success-old { ::ng-deep .mat-progress-bar-fill { - // background-color: rgb(173, 26, 26); + // background-color: var(--error); z-index: 2; } ::ng-deep .mat-progress-bar-fill::after { - // background-color: rgb(173, 26, 26); + // background-color: var(--error); z-index: 2; } ::ng-deep .mat-progress-bar-buffer { - background: rgba(0, 0, 0, 0.01); + background: var(--progress-bar-buffer-bg-color); } } @@ -214,13 +214,13 @@ margin-bottom: auto; transform: scale(-1, 1); ::ng-deep circle { - stroke: rgb(173, 26, 26); + stroke: var(--error); } ::ng-deep .mat-progress-spinner-buffer { - background: rgb(212, 212, 212); + background: var(--progress-spinner-buffer-bg-color); } } @@ -229,11 +229,11 @@ margin-bottom: auto; transform: scale(-1, 1); ::ng-deep circle { - stroke: rgb(173, 26, 26); + stroke: var(--error); } ::ng-deep .mat-progress-spinner-buffer { - background: rgb(212, 212, 212); + background: var(--progress-spinner-buffer-bg-color); } } @@ -257,7 +257,7 @@ margin-bottom: auto; .deleted { margin: auto; padding: 10px; - color: grey; + color: var(--regular-grey-color); font-style: italics; font-size: 18px; } @@ -272,7 +272,7 @@ margin-bottom: auto; .count-row-label { margin-left: auto; margin-right: auto; - text-shadow: 1px 0 0 #000, 0 -1px 0 #000, 0 1px 0 #000, -1px 0 0 #000; + text-shadow: 1px 0 0 var(--text-shadow-color), 0 -1px 0 var(--text-shadow-color), 0 1px 0 var(--text-shadow-color), -1px 0 0 var(--text-shadow-color); white-space: nowrap; } } @@ -283,7 +283,7 @@ margin-bottom: auto; .count-cont { text-align: center; - color: white; + color: var(--regular-white-color); margin-top: -22px; font-weight: 500; z-index: 5; diff --git a/src/app/core/admin/monitor/monitor.component.ts b/src/app/core/admin/monitor/monitor.component.ts index 63f180b52..e52990ef4 100644 --- a/src/app/core/admin/monitor/monitor.component.ts +++ b/src/app/core/admin/monitor/monitor.component.ts @@ -70,7 +70,7 @@ export class MonitorComponent implements OnInit, OnDestroy { clearJob(): void { const navigationExtras: NavigationExtras = { - queryParams: { 'function': 'data' } + queryParams: { function: 'data' } }; this.router.navigate(['/admin'], navigationExtras); } @@ -91,7 +91,6 @@ export class MonitorComponent implements OnInit, OnDestroy { }); } else { this.monitor = false; - console.log('no monitor'); } }, error => { this.message = 'invalid Job ID'; @@ -159,7 +158,9 @@ export class MonitorComponent implements OnInit, OnDestroy { case '...': this.ellipses = '.'; break; default: this.ellipses = '.'; break; } - setTimeout(() => {this.changeEllipses(); }, 1000); + setTimeout(() => { + this.changeEllipses(); + }, 1000); } else { this.ellipses = ''; } diff --git a/src/app/core/admin/scheduled-jobs/scheduled-job/scheduled-job.component.ts b/src/app/core/admin/scheduled-jobs/scheduled-job/scheduled-job.component.ts index d7d11f2a3..d6dfa7f77 100644 --- a/src/app/core/admin/scheduled-jobs/scheduled-job/scheduled-job.component.ts +++ b/src/app/core/admin/scheduled-jobs/scheduled-job/scheduled-job.component.ts @@ -101,7 +101,7 @@ export class ScheduledJobComponent implements OnInit, OnDestroy { this.monitor = false; } -disable (job: any) { +disable(job: any) { this.adminService.runJob(job['@disable']).pipe(take(1)).subscribe( response => { this.refresh(); }); @@ -118,7 +118,9 @@ execute(job: any) { this.adminService.runJob(job['@execute']).pipe(take(1)).subscribe( response => { this.refresh(true); }, error => { - setTimeout(() => {this.refresh(); } ); + setTimeout(() => { + this.refresh(); + } ); }); } cancel(job: any) { diff --git a/src/app/core/admin/scheduled-jobs/scheduled-jobs.component.scss b/src/app/core/admin/scheduled-jobs/scheduled-jobs.component.scss index 1233de617..122e30df1 100644 --- a/src/app/core/admin/scheduled-jobs/scheduled-jobs.component.scss +++ b/src/app/core/admin/scheduled-jobs/scheduled-jobs.component.scss @@ -8,7 +8,7 @@ .job-container { padding:10px; margin: 20px; - border: 1px solid rgba(0, 0, 0, .1); + border: 1px solid var(--border-color); } diff --git a/src/app/core/admin/user-management/user-edit-dialog/user-edit-dialog.component.html b/src/app/core/admin/user-management/user-edit-dialog/user-edit-dialog.component.html index 97c8a01fa..362c1f677 100644 --- a/src/app/core/admin/user-management/user-edit-dialog/user-edit-dialog.component.html +++ b/src/app/core/admin/user-management/user-edit-dialog/user-edit-dialog.component.html @@ -1,3 +1,4 @@ +
{{message}}

Add User @@ -11,7 +12,7 @@

- User Name + Username
@@ -69,8 +70,6 @@

-
{{message}}
-

diff --git a/src/app/core/admin/user-management/user-edit-dialog/user-edit-dialog.component.scss b/src/app/core/admin/user-management/user-edit-dialog/user-edit-dialog.component.scss index e9128cfb6..96ab6f836 100644 --- a/src/app/core/admin/user-management/user-edit-dialog/user-edit-dialog.component.scss +++ b/src/app/core/admin/user-management/user-edit-dialog/user-edit-dialog.component.scss @@ -53,6 +53,14 @@ .message { margin: auto; + text-align: center; + padding: 10px; +} + +.error-msg { + background: var(--regular-red-color); + color: var(--regular-white-color); + border: 1px solid var(--regular-black-color); } .box-label { @@ -84,4 +92,4 @@ .submit-message { margin: auto; font-size: 20px; -} \ No newline at end of file +} diff --git a/src/app/core/admin/user-management/user-edit-dialog/user-edit-dialog.component.ts b/src/app/core/admin/user-management/user-edit-dialog/user-edit-dialog.component.ts index afe5a5801..6df7ff184 100644 --- a/src/app/core/admin/user-management/user-edit-dialog/user-edit-dialog.component.ts +++ b/src/app/core/admin/user-management/user-edit-dialog/user-edit-dialog.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, Inject } from '@angular/core'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { AdminService } from '@gsrs-core/admin/admin.service'; import { isString } from 'util'; import { IfStmt } from '@angular/compiler'; @@ -13,8 +13,10 @@ import { UserEditObject } from '@gsrs-core/admin/admin-objects.model'; styleUrls: ['./user-edit-dialog.component.scss'] }) export class UserEditDialogComponent implements OnInit { + userLoggedIn: any; user: any; userID: string; + userHasAdminRole: boolean; originalName: string; newPassword: string; newUser = false; @@ -26,6 +28,7 @@ export class UserEditDialogComponent implements OnInit { groups: Array< any >; submitted = false; response: any; + isError: boolean = false; roles = [ {name: 'Query', hasRole: false}, {name: 'DataEntry', hasRole: false}, @@ -42,6 +45,8 @@ export class UserEditDialogComponent implements OnInit { ) { this.user = data.user; this.userID = data.userID; + this.submitted = data.submission; + this.userLoggedIn = this.authService.getUser(); } ngOnInit() { @@ -50,6 +55,7 @@ export class UserEditDialogComponent implements OnInit { this.originalName = this.user.username; this.loading = false; this.newUser = false; + this.userHasAdminRole = this.checkIfUserHasAdminRole(this.user.roles); this.adminService.getGroups().pipe(take(1)).subscribe( response => { this.groups = []; response.forEach( grp => { @@ -69,6 +75,7 @@ export class UserEditDialogComponent implements OnInit { this.originalName = resp.user.username; this.loading = false; this.newUser = false; + this.userHasAdminRole = this.checkIfUserHasAdminRole(this.user.roles); this.adminService.getGroups().pipe(take(1)).subscribe( response => { this.groups = []; response.forEach( grp => { @@ -84,7 +91,9 @@ export class UserEditDialogComponent implements OnInit { }); } else { this.newUser = true; + this.userHasAdminRole = false; this.user = {groups: [], roles: [], user: {}}; + this.user.active = true; this.loading = false; this.adminService.getGroups().pipe(take(1)).subscribe( response => { this.groups = []; @@ -106,6 +115,16 @@ export class UserEditDialogComponent implements OnInit { }); } + checkIfUserHasAdminRole(roles): boolean { + let toReturn = false; + roles.forEach(role => { + if(role.toLowerCase() === 'admin') { + toReturn = true; + } + }); + return toReturn; + } + checkGroups(): void { this.groups.forEach(group => { this.user.groups.forEach(element => { @@ -118,8 +137,10 @@ export class UserEditDialogComponent implements OnInit { saveChanges(): void { if (this.changePassword && this.newPassword !== '' ) { + this.isError = true; this.message = 'Cancel or submit new password to save other changes'; } else { + this.isError = false; const rolesArr = []; this.roles.forEach(role => { if (role.hasRole) { @@ -138,30 +159,50 @@ export class UserEditDialogComponent implements OnInit { groups.push(this.newGroup); } const userEditObj: UserEditObject = { - 'username': this.user.user.username, - 'isAdmin': this.user.user.admin, - 'isActive': this.user.active, - 'email': this.user.user.email || null, - 'roles': rolesArr, - 'groups' : groups, + username: this.user.user.username, + isAdmin: this.user.user.admin, + isActive: this.user.active, + email: this.user.user.email || null, + roles: rolesArr, + groups: groups }; - this.adminService.editUser(userEditObj, this.userID).pipe(take(1)).subscribe(response => { - if (response && response.user) { - this.successfulChange(response); - } else { - this.message = 'Unable to edit user'; - } - }, error => { - this.message = 'Unable to edit user'; - if (error.error && isString(error.error) ) { - this.message = error; + if(this.userLoggedIn === this.user.user.username) { // if userLoggedIn is making changes to their account + if((this.userHasAdminRole !== this.checkIfUserHasAdminRole(rolesArr)) + || !this.user.active) { // user is trying to remove their admin role or make themselves inactive + if (confirm('Setting your own account as inactive or removing admin role are significant changes. ARE YOU SURE YOU WANT TO PROCEED?')) { + this.editUser(userEditObj); + } + } else { // safe changes + this.editUser(userEditObj); } - }); + } else { // not userloggedin's acct + this.editUser(userEditObj); + } } } + editUser(userEditObj): void { + this.adminService.editUser(userEditObj, this.userID).pipe(take(1)).subscribe(response => { + if (response && response.user) { + this.isError = false; + this.successfulChange(response); + } else { + this.isError = true; + this.message = 'Unable to edit user'; + } + }, error => { + this.isError = true; + this.message = 'Unable to edit user'; + if (error.error) { + this.isError = true; + this.message = error; + } + }); + } + addUser(): void { + this.isError = false; if (this.newPassword === this.newPasswordConfirm) { const rolesArr = []; this.roles.forEach(role => { @@ -179,25 +220,42 @@ export class UserEditDialogComponent implements OnInit { groups.push(this.newGroup); } const userEditObj: UserEditObject = { - 'username': this.user.user.username, - 'isAdmin': this.user.user.admin, - 'isActive': this.user.active, - 'email': this.user.user.email || null, - 'roles': rolesArr, - 'groups' : groups, - 'password': this.newPassword + username: this.user.user.username, + isAdmin: this.user.user.admin, + isActive: this.user.active, + email: this.user.user.email || null, + roles: rolesArr, + groups: groups, + password: this.newPassword }; this.adminService.addUser(userEditObj).pipe(take(1)).subscribe(response => { + this.message = ''; if (response && response.user) { this.successfulChange(response); } }, error => { - if (error.error && isString(error.error) ) { - this.message = error.error; + if (error.error) { + this.isError = true; + this.message = 'ERROR: '; + let dummytext = 'This user either already exists or there was a server problem updating the record'; + this.message += error.error.message === undefined ? + dummytext : error.error.message.split(':')[1]; } + this.adminService.getUserByName(this.user.user.username).pipe(take(1)).subscribe(response => { + let userIsActive = false; + userIsActive = response.active; + if(userIsActive) { + this.message += '. This user is active.'; + } else { + this.message += '. This user is NOT active.'; + } + + }, err => { + }); }); } else { + this.isError = true; this.message = 'passwords do not match'; } } @@ -216,18 +274,23 @@ export class UserEditDialogComponent implements OnInit { validatePassword(): void { if (this.newPassword !== this.newPasswordConfirm) { + this.isError = true; this.message = 'Error: passwords do not match'; this.newPassword = ''; this.newPasswordConfirm = ''; } else { + this.isError = false; if ( this.authService.getUser === this.user.identifier ) { this.adminService.changeMyPassword('', this.newPassword, this.user.id).pipe(take(1)).subscribe(response => { + this.isError = false; this.changePassword = !this.changePassword; this.message = 'Password updated successfully'; }, error => { - if (error.error && isString(error.error) ) { + if (error.error) { + this.isError = true; this.message = 'Error - ' + error.error; } else { + this.isError = true; this.newPassword = ''; this.newPasswordConfirm = ''; this.changePassword = !this.changePassword; @@ -237,11 +300,14 @@ export class UserEditDialogComponent implements OnInit { } else { this.adminService.changePassword( this.newPassword, this.user.id).pipe(take(1)).subscribe(response => { this.changePassword = !this.changePassword; + this.isError = false; this.message = 'Password updated successfully'; }, error => { - if (error.error && isString(error.error) ) { + if (error.error) { + this.isError = true; this.message = 'Error: ' + error.error; } else { + this.isError = true; this.newPassword = ''; this.newPasswordConfirm = ''; this.changePassword = !this.changePassword; diff --git a/src/app/core/admin/user-management/user-management.component.html b/src/app/core/admin/user-management/user-management.component.html index 73dcb5aa6..ace9ec02c 100644 --- a/src/app/core/admin/user-management/user-management.component.html +++ b/src/app/core/admin/user-management/user-management.component.html @@ -8,6 +8,25 @@
+
+ + +
+
+
+ +
+ + + + + +
@@ -21,19 +40,19 @@
- -
-
- -
- - - - - -
- + + + + + - + @@ -71,4 +90,4 @@ - \ No newline at end of file + diff --git a/src/app/core/admin/user-management/user-management.component.scss b/src/app/core/admin/user-management/user-management.component.scss index 78d66f6c1..f8087ca51 100644 --- a/src/app/core/admin/user-management/user-management.component.scss +++ b/src/app/core/admin/user-management/user-management.component.scss @@ -33,4 +33,24 @@ .small-icon { height: 20px; line-height: 20px; -} \ No newline at end of file +} +.dropdown-content { + display: none; + box-shadow: 0px 8px 16px 0px var(--box-shadow-color); +} +.dropdown-content a { + font-size: 12px; + color: var(--regular-black-color);; + display: block; + padding: 12px 16px; + width: 170px; +} +.dropdown-content a:hover { + background-color: var(--lightgray-color); +} +.show { + display: block !important; +} +.hide { + display: none !important; +} diff --git a/src/app/core/admin/user-management/user-management.component.ts b/src/app/core/admin/user-management/user-management.component.ts index 59a88c6dd..5708ad943 100644 --- a/src/app/core/admin/user-management/user-management.component.ts +++ b/src/app/core/admin/user-management/user-management.component.ts @@ -1,6 +1,9 @@ import { Component, OnInit, ViewChild } from '@angular/core'; import { User, Auth, AuthService } from '@gsrs-core/auth'; -import { MatDialog, Sort, MatTableDataSource, PageEvent } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; +import { Sort } from '@angular/material/sort'; +import { MatTableDataSource } from '@angular/material/table'; +import { PageEvent } from '@angular/material/paginator'; import { OverlayContainer } from '@angular/cdk/overlay'; import { UserEditDialogComponent } from '@gsrs-core/admin/user-management/user-edit-dialog/user-edit-dialog.component'; import { AdminService } from '@gsrs-core/admin/admin.service'; @@ -8,6 +11,7 @@ import { UtilsService } from '@gsrs-core/utils'; import { DataSource } from '@angular/cdk/table'; import { FormControl } from '@angular/forms'; import {MatPaginator} from '@angular/material/paginator'; +import { TouchSequence } from 'selenium-webdriver'; @Component({ selector: 'app-user-management', @@ -15,6 +19,7 @@ import {MatPaginator} from '@angular/material/paginator'; styleUrls: ['./user-management.component.scss'] }) export class UserManagementComponent implements OnInit { + @ViewChild(MatPaginator, {static: true}) paginator: MatPaginator; userID: number; alert: string; filtered = new MatTableDataSource(); @@ -23,14 +28,15 @@ export class UserManagementComponent implements OnInit { showAll = false; showInactive = false; private overlayContainer: HTMLElement; - displayedColumns: string[] = ['name', 'email', 'created', 'modified', 'active']; + displayedColumns: string[] = ['checkbox', 'name', 'email', 'created', 'modified', 'active']; page = 0; pageSize = 10000; paged: Array< any >; users: Array< any > = []; - private searchTimer: any; lastSort: Sort; - @ViewChild(MatPaginator, {static: true}) paginator: MatPaginator; + checkedList: Array = []; + showHideActions: boolean = false; + private searchTimer: any; constructor( private dialog: MatDialog, @@ -52,6 +58,75 @@ constructor( }); } +toggleActions() { + this.showHideActions = !this.showHideActions; +} + +selectUnselectAll(): void { + if(this.checkedList.length === this.paged.length) { // all are selected, need to deselect all + this.checkedList = []; + } else { // select all + this.checkedList = []; + for(let user of this.paged) { + this.checkedList.push(user.user.username); + } + } +} + +isInCheckList(username): boolean { + let toReturn = false; + for(let users of this.checkedList) { + if(users === username) { + toReturn = true; + } + } + return toReturn; +} + +getPagedIndex(username): number { + let toReturn = 0; + this.paged.forEach((user, index) => { + if(user.user.username === username) { + toReturn = index; + } + }); + return toReturn; +} + +markSelected(status): void { + let extraWarning = ''; + if(status === 'inactive') { + extraWarning = '. If the user is inactive that user won\'t be able to log in until they\'ve been reactivated' + } + if (confirm('Please confirm you would like to set the selected accounts to ' + status + extraWarning + + '. \n NOTE: Selecting up to 50 at a time may take some time and so is not adviseable.')) { + switch(status) { + case 'inactive': + this.setSelectedToInactive(); + break; + } + } +} + +setSelectedToInactive(): void { + for(let user of this.checkedList) { + let index = this.getPagedIndex(user); + this.deleteUsers(user, index); + } +} + +checkListToggle(username): void { + if(this.checkedList.includes(username)) { + this.checkedList.forEach((user, index) => { + if(user === username) { + this.checkedList.splice(index, 1); + } + }) + } else { + this.checkedList.push(username); + } +} + showAllUsers(): void { this.loading = true; this.showAll = true; @@ -121,7 +196,7 @@ showInactiveUsers(): void { editUser(userID: any, index: number): void { const dialogRef = this.dialog.open(UserEditDialogComponent, { - data: {'userID': userID}, + data: {userID: userID, submission: false}, width: '800px', autoFocus: false, disableClose: true @@ -140,18 +215,10 @@ showInactiveUsers(): void { } // change both dataSource and original source to avoid making an API call after every edit -updateLocalData(response: any, index?: number, id?: number, username?: string, ) { - this.users.forEach( current => { - if (index) { - if (current.index === response.index) { - current = response; - } - } else { - if (current.id === response.id) { - current = response; - } - } - }); +updateLocalData(response: any, index?: number, id?: number, username?: string ) { + let i = this.users.findIndex(x => x.id === response.id); + let u = this.users[i]; + this.users[i] = response; if (index) { const backup = this.filtered.data; backup[index] = response; @@ -162,19 +229,55 @@ updateLocalData(response: any, index?: number, id?: number, username?: string, ) deleteUser(username: string, index: number): void { + if (confirm('Are you sure you want to set this user to inactive? If the user is inactive that user won\'t be able to log in until they\'ve been reactivated.')) { this.adminService.deleteUser(username).subscribe( response => { - if (response) { - this.updateLocalData(response, index, null, username); + const dialogRef = this.dialog.open(UserEditDialogComponent, { + data: {userID: response.id, submission: true}, + width: '800px', + autoFocus: false, + disableClose: true + }); + this.overlayContainer.style.zIndex = '1002'; + const dialogSubscription = dialogRef.afterClosed().subscribe(response1 => { + this.overlayContainer.style.zIndex = null; + if (response) { + this.updateLocalData(response, index, null, username); const backup = this.filtered.data; - backup[index] = response; - this.filtered.data = backup; - } + backup[index] = response; + this.filtered.data = backup; + this.pageChange(); + } + }); + }); + } + } + + // identical to deleteUser but no warning since warning is combined in mark selected + // only used when user selects multiple users to set inactive + deleteUsers(username: string, index: number): void { + this.adminService.deleteUser(username).subscribe( response => { + const dialogRef = this.dialog.open(UserEditDialogComponent, { + data: {userID: response.id, submission: true}, + width: '800px', + autoFocus: false, + disableClose: true + }); + this.overlayContainer.style.zIndex = '1002'; + const dialogSubscription = dialogRef.afterClosed().subscribe(response1 => { + this.overlayContainer.style.zIndex = null; + if (response) { + this.updateLocalData(response, index, null, username); + const backup = this.filtered.data; + backup[index] = response; + this.filtered.data = backup; + } + }); }); } addUser(): void { const dialogRef = this.dialog.open(UserEditDialogComponent, { - data: {'type': 'add'}, + data: {type: 'add', submission: false}, width: '800px', autoFocus: false, disableClose: true diff --git a/src/app/core/app-dynamic-component-manifests.ts b/src/app/core/app-dynamic-component-manifests.ts index ebe8b4a14..c4d5b13bf 100644 --- a/src/app/core/app-dynamic-component-manifests.ts +++ b/src/app/core/app-dynamic-component-manifests.ts @@ -171,6 +171,18 @@ export const dynamicComponentManifests: LazyLoadedComponentManifest[] = [ loadChildren: () => import('./substance-details/substance-mixture-source/substance-mixture-source.module') .then(m => m.SubstanceMixtureSourceModule), }, + { + componentId: 'substance-mixture-parent', + path: 'substance-mixture-parent', + loadChildren: () => import('./substance-details/substance-mixture-parent/substance-mixture-parent.module') + .then(m => m.SubstanceMixtureParentModule), + }, + { + componentId: 'substance-hierarchy', + path: 'substance-hierarchy', + loadChildren: () => import('./substance-details/substance-hierarchy/substance-hierarchy.module') + .then(m => m.SubstanceHierarchyModule), + }, { componentId: 'substance-history', path: 'substance-history', @@ -183,6 +195,12 @@ export const dynamicComponentManifests: LazyLoadedComponentManifest[] = [ loadChildren: () => import('./substance-details/substance-ssg-parent-substance/substance-ssg-parent-substance.module') .then(m => m.SubstanceSsgParentSubstanceModule), }, + { + componentId: 'substance-ssg1-parent', + path: 'substance-ssg1-parent', + loadChildren: () => import('./substance-details/substance-ssg1-parent/substance-ssg1-parent.module') + .then(m => m.SubstanceSsg1ParentModule), + }, { componentId: 'substance-ssg-grade', path: 'substance-ssg-grade', @@ -195,6 +213,12 @@ export const dynamicComponentManifests: LazyLoadedComponentManifest[] = [ loadChildren: () => import('./substance-details/substance-ssg-definition/substance-ssg-definition.module') .then(m => m.SubstanceSsgDefinitionModule), }, + { + componentId: 'substance-dependencies-image', + path: 'substance-dependencies-image', + loadChildren: () => import('./substance-details/substance-dependencies-image/substance-dependencies-image.module') + .then(m => m.SubstanceDependenciesImageModule), + }, { componentId: 'substance-form-definition', path: 'substance-form-definition', @@ -330,13 +354,15 @@ export const dynamicComponentManifests: LazyLoadedComponentManifest[] = [ { componentId: 'substance-form-structurally-diverse-source', path: 'substance-form-structurally-diverse-source', - loadChildren: () => import('./substance-form/structurally-diverse/substance-form-structurally-diverse-source/substance-form-structurally-diverse-source.module') + loadChildren: () => + import('./substance-form/structurally-diverse/substance-form-structurally-diverse-source/substance-form-structurally-diverse-source.module') .then(m => m.SubstanceFormStructurallyDiverseSourceModule) }, { componentId: 'substance-form-structurally-diverse-organism', path: 'substance-form-structurally-diverse-organism', - loadChildren: () => import('./substance-form/structurally-diverse/substance-form-structurally-diverse-organism/substance-form-structurally-diverse-organism.module') + loadChildren: () => + import('./substance-form/structurally-diverse/substance-form-structurally-diverse-organism/substance-form-structurally-diverse-organism.module') .then(m => m.SubstanceFormStructurallyDiverseOrganismModule) }, { @@ -386,5 +412,23 @@ export const dynamicComponentManifests: LazyLoadedComponentManifest[] = [ path: 'ssg-definition-form', loadChildren: () => import('./substance-form/ssg-definition-form/ssg-definition-form.module') .then(m => m.SsgDefinitionFormModule) + }, + { + componentId: 'substance-form-ssg4m-process', + path: 'ssg4m-process-form', + loadChildren: () => import('./substance-ssg4m/ssg4m-process/substance-form-ssg4m-process.module') + .then(m => m.SubstanceSsg4mProcessModule) + }, + { + componentId: 'substance-form-ssg2-manufacturing', + path: 'substance-form-ssg2-manufacturing', + loadChildren: () => import('./substance-ssg2/ssg2-manufacturing/ssg2-manufacturing.module') + .then(m => m.Ssg2ManufacturingModule) + }, + { + componentId: 'substance-form-ssg2-overview', + path: 'substance-form-ssg2-overview', + loadChildren: () => import('./substance-ssg2/ssg2-overview-form/ssg2-overview-form.module') + .then(m => m.Ssg2OverviewFormModule) } ]; diff --git a/src/app/core/app-routing.module.ts b/src/app/core/app-routing.module.ts index f8ab8af56..c28b77618 100644 --- a/src/app/core/app-routing.module.ts +++ b/src/app/core/app-routing.module.ts @@ -3,6 +3,7 @@ import { Routes, RouterModule } from '@angular/router'; import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; import { BaseComponent } from './base/base.component'; import { HomeComponent } from './home/home.component'; +import { RegistrarsComponent } from './registrars/registrars.component'; import { SubstancesBrowseComponent } from './substances-browse/substances-browse.component'; import { StructureSearchComponent } from './structure-search/structure-search.component'; import { SubstanceDetailsComponent } from './substance-details/substance-details.component'; @@ -19,6 +20,9 @@ import { UserDownloadsComponent } from '@gsrs-core/auth/user-downloads/user-down import { MonitorComponent } from '@gsrs-core/admin/monitor/monitor.component'; import { CanActivateAdmin } from '@gsrs-core/admin/can-activate-admin'; import { CanActivateAdminPage } from './admin/can-activate-admin-page'; +import { UnauthorizedComponent } from '@gsrs-core/unauthorized/unauthorized.component'; +import { SubstanceSsg4ManufactureFormComponent } from './substance-ssg4m/substance-ssg4m-form.component'; +import { ImportBrowseComponent } from '@gsrs-core/admin/import-browse/import-browse.component'; const childRoutes: Routes = [ { @@ -29,15 +33,21 @@ const childRoutes: Routes = [ path: 'home', component: HomeComponent }, + { + path: 'unauthorized', + component: UnauthorizedComponent + }, { path: 'browse-substance', component: SubstancesBrowseComponent }, + { + path: 'registrars', + component: RegistrarsComponent, + }, { path: 'substances/register', - component: SubstanceFormComponent, - canActivate: [CanRegisterSubstanceForm], - canDeactivate: [CanDeactivateSubstanceFormGuard] + component: SubstanceFormComponent }, { path: 'substances/register/:type', @@ -65,6 +75,11 @@ const childRoutes: Routes = [ path: 'sequence-search', component: SequenceSearchComponent }, + { + path: 'staging', + component: ImportBrowseComponent + + }, { path: 'login', component: LoginComponent @@ -78,7 +93,21 @@ const childRoutes: Routes = [ { path: 'admin', component: AdminComponent, - canActivate: [CanActivateAdmin] + canActivate: [CanActivateAdmin], + + }, + + { + path: 'admin/staging-area', + component: ImportBrowseComponent, + pathMatch: 'full' + + }, + { + path: 'admin/:function', + component: AdminComponent, + canActivate: [CanActivateAdmin], + }, { @@ -97,7 +126,6 @@ const childRoutes: Routes = [ { path: 'user-downloads/:id', component: UserDownloadsComponent - } ]; @@ -114,7 +142,10 @@ const routes: Routes = [ ]; @NgModule({ - imports: [RouterModule.forRoot(routes)], + imports: [RouterModule.forRoot(routes, { + // onSameUrlNavigation: 'ignore', // default behavior, ingnores same route reload + onSameUrlNavigation: 'reload' +})], exports: [RouterModule] }) export class AppRoutingModule { } diff --git a/src/app/core/app.component.ts b/src/app/core/app.component.ts index b8e3f964a..a37a08821 100644 --- a/src/app/core/app.component.ts +++ b/src/app/core/app.component.ts @@ -1,6 +1,6 @@ import {Component, OnInit} from '@angular/core'; import {DomSanitizer, Title} from '@angular/platform-browser'; -import { MatIconRegistry } from '@angular/material'; +import { MatIconRegistry } from '@angular/material/icon'; import { GoogleAnalyticsService } from './google-analytics/google-analytics.service'; import {Router, NavigationStart} from '@angular/router'; @@ -23,6 +23,10 @@ export class AppComponent { 'chevron_right', sanitizer.bypassSecurityTrustResourceUrl('assets/icons/baseline-chevron_right-24px.svg')); + iconRegistry.addSvgIcon( + 'chevron_left', + sanitizer.bypassSecurityTrustResourceUrl('assets/icons/baseline-chevron_left-24px.svg')); + iconRegistry.addSvgIcon( 'search', sanitizer.bypassSecurityTrustResourceUrl('assets/icons/baseline-search-24px.svg')); @@ -181,7 +185,6 @@ export class AppComponent { iconRegistry.addSvgIcon( 'alarm-on', sanitizer.bypassSecurityTrustResourceUrl('assets/icons/baseline-alarm_on-24px.svg')); - // precisionFDA Icons iconRegistry.addSvgIcon( 'pfda_home', @@ -198,6 +201,18 @@ export class AppComponent { iconRegistry.addSvgIcon( 'pfda_profile', sanitizer.bypassSecurityTrustResourceUrl('assets/icons/pfda/profile.svg')); + iconRegistry.addSvgIcon( + 'view-list', + sanitizer.bypassSecurityTrustResourceUrl('assets/icons/baseline-view_list-24px.svg')); + iconRegistry.addSvgIcon( + 'glasses', + sanitizer.bypassSecurityTrustResourceUrl('assets/icons/baseline-glasses-24px.svg')); + iconRegistry.addSvgIcon( + 'paste', + sanitizer.bypassSecurityTrustResourceUrl('assets/icons/baseline-paste-24px.svg')); + iconRegistry.addSvgIcon( + 'help', + sanitizer.bypassSecurityTrustResourceUrl('assets/icons/help-outline-24px.svg')); } } diff --git a/src/app/core/app.module.ts b/src/app/core/app.module.ts index 8c6864754..6aea37b90 100644 --- a/src/app/core/app.module.ts +++ b/src/app/core/app.module.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ import { BrowserModule } from '@angular/platform-browser'; import { NgModule, APP_INITIALIZER, ErrorHandler } from '@angular/core'; import { HttpClientModule, HttpClientJsonpModule, HTTP_INTERCEPTORS } from '@angular/common/http'; @@ -22,8 +23,10 @@ import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatTableModule } from '@angular/material/table'; import { MatPaginatorModule } from '@angular/material/paginator'; import { MatSelectModule } from '@angular/material/select'; +import {MatRadioModule} from '@angular/material/radio'; import { MatSliderModule } from '@angular/material/slider'; import { MatDialogModule } from '@angular/material/dialog'; +import { MatGridListModule } from '@angular/material/grid-list'; import { MatListModule } from '@angular/material/list'; import { MatMenuModule } from '@angular/material/menu'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; @@ -31,11 +34,11 @@ import { MatTooltipModule } from '@angular/material/tooltip'; import { OverlayModule } from '@angular/cdk/overlay'; import { MatBottomSheetModule } from '@angular/material/bottom-sheet'; import { MatProgressBarModule } from '@angular/material/progress-bar'; - import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; import { BaseComponent } from './base/base.component'; import { PfdaToolbarComponent } from './base/pfda-toolbar/pfda-toolbar.component'; import { HomeComponent } from './home/home.component'; +import { RegistrarsComponent } from './registrars/registrars.component'; import { SubstancesBrowseComponent } from './substances-browse/substances-browse.component'; import { configServiceFactory } from './config/config.factory'; import { ConfigService } from './config/config.service'; @@ -52,7 +55,9 @@ import { TakePipe } from './utils/take.pipe'; import { EnvironmentModule } from '../../environments/environment'; import { SubstanceTextSearchModule } from './substance-text-search/substance-text-search.module'; import { StructureImageModalComponent } from './structure/structure-image-modal/structure-image-modal.component'; -import { MatTabsModule, MatNativeDateModule } from '@angular/material'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatNativeDateModule } from '@angular/material/core'; import { SequenceSearchComponent } from './sequence-search/sequence-search.component'; import { TrackLinkEventDirective } from './google-analytics/track-link-event/track-link-event.directive'; import { SubstanceCardsModule } from './substance-details/substance-cards.module'; @@ -75,13 +80,46 @@ import { GuidedSearchModule } from './guided-search/guided-search.module'; import { CanActivateAdminPage } from '@gsrs-core/admin/can-activate-admin-page'; import { ExportDialogComponent } from '@gsrs-core/substances-browse/export-dialog/export-dialog.component'; import { NamesDisplayPipe } from '@gsrs-core/utils/names-display-order.pipe'; -// tslint:disable-next-line:max-line-length +// eslint-disable-next-line max-len import { BrowseHeaderDynamicSectionDirective } from '@gsrs-core/substances-browse/browse-header-dynamic-section/browse-header-dynamic-section.directive'; import { SubstanceHistoryDialogComponent } from '@gsrs-core/substance-history-dialog/substance-history-dialog.component'; import { SubstanceEditImportDialogComponent } from '@gsrs-core/substance-edit-import-dialog/substance-edit-import-dialog.component'; import { CodeDisplayModule } from '@gsrs-core/utils/code-display.module'; import { GlobalErrorHandler } from '@gsrs-core/error-handler/error-handler'; +import { ShowMolfileDialogComponent } from +'@gsrs-core/substances-browse/substance-summary-card/show-molfile-dialog/show-molfile-dialog.component'; +import { SubstanceStatusPipe } from '@gsrs-core/utils/substance-status.pipe'; +import { UnauthorizedComponent } from '@gsrs-core/unauthorized/unauthorized.component'; +import { SubstanceSsg4mModule } from './substance-ssg4m/substance-ssg4m.module'; +import { SubstanceSsg4mProcessModule } from './substance-ssg4m/ssg4m-process/substance-form-ssg4m-process.module'; +import { Ssg4mSitesModule } from './substance-ssg4m/ssg4m-sites/ssg4m-sites.module'; +import { Ssg4mStagesModule } from './substance-ssg4m/ssg4m-stages/substance-form-ssg4m-stages.module'; +import { SubstanceFormSsg4mStartingMaterialsModule } from './substance-ssg4m/ssg4m-starting-materials/substance-form-ssg4m-starting-materials.module'; +import { SubstanceSsg2Module } from './substance-ssg2/substance-ssg2.module'; +import { Ssg2ManufacturingModule } from './substance-ssg2/ssg2-manufacturing/ssg2-manufacturing.module'; +import { CustomCheckboxWidgetComponent } from '@gsrs-core/substances-browse/export-dialog/custom-checkbox-widget/custom-checkbox-widget.component'; +import { + WidgetRegistry, + DefaultWidgetRegistry, + SchemaFormModule, +} from "ngx-schema-form"; +import { MyWidgetRegistry } from '@gsrs-core/substances-browse/export-dialog/custom-checkbox-widget/custom-checkbox-registry'; +import { CustomTextWidgetComponent } from '@gsrs-core/substances-browse/export-dialog/custom-text-widget/custom-text-widget.component'; +import { CustomMultiselectWidgetComponent } from '@gsrs-core/substances-browse/custom-multiselect-widget/custom-multiselect-widget.component'; +import { CustomSelectWidgetComponent } from '@gsrs-core/substances-browse/export-dialog/custom-select-widget/custom-select-widget.component'; +import { CustomRadioWidgetComponent } from '@gsrs-core/substances-browse/export-dialog/custom-radio-widget/custom-radio-widget.component'; +import { CustomMultiCheckboxWidgetComponent } from '@gsrs-core/substances-browse/export-dialog/custom-multi-checkbox-widget/custom-multi-checkbox-widget.component'; +import { CustomTextareaWidgetComponent } from '@gsrs-core/substances-browse/export-dialog/custom-textarea-widget/custom-textarea-widget.component'; +import { BulkSearchModule } from '@gsrs-core/bulk-search/bulk-search.module'; +import { RegisterComponent } from './register/register.component'; +import { PwdRecoveryComponent } from './pwd-recovery/pwd-recovery.component'; +import { ElementLabelDisplayModule } from './utils/element-label-display.module'; +import { MergeActionDialogComponent } from '@gsrs-core/admin/import-browse/merge-action-dialog/merge-action-dialog.component'; +import { UserQueryListDialogComponent } from '@gsrs-core/bulk-search/user-query-list-dialog/user-query-list-dialog.component'; +import { ListCreateDialogComponent } from '@gsrs-core/substances-browse/list-create-dialog/list-create-dialog.component'; +import { ImportScrubberComponent } from '@gsrs-core/admin/import-management/import-scrubber/import-scrubber.component'; +import { PrivacyStatementModule } from './privacy-statement/privacy-statement.module'; @NgModule({ declarations: [ AppComponent, @@ -89,9 +127,11 @@ import { GlobalErrorHandler } from '@gsrs-core/error-handler/error-handler'; BaseComponent, PfdaToolbarComponent, HomeComponent, + UnauthorizedComponent, SubstancesBrowseComponent, StructureSearchComponent, SubstanceDetailsComponent, + RegistrarsComponent, TakePipe, SequenceSearchComponent, TrackLinkEventDirective, @@ -104,7 +144,23 @@ import { GlobalErrorHandler } from '@gsrs-core/error-handler/error-handler'; ExportDialogComponent, SubstanceEditImportDialogComponent, SubstanceHistoryDialogComponent, - NamesDisplayPipe + ShowMolfileDialogComponent, + NamesDisplayPipe, + SubstanceStatusPipe, + CustomCheckboxWidgetComponent, + CustomTextWidgetComponent, + CustomMultiselectWidgetComponent, + CustomSelectWidgetComponent, + CustomRadioWidgetComponent, + CustomMultiCheckboxWidgetComponent, + CustomTextareaWidgetComponent, + RegisterComponent, + PwdRecoveryComponent, + MergeActionDialogComponent, + UserQueryListDialogComponent, + ListCreateDialogComponent, + ImportScrubberComponent + ], imports: [ BrowserModule.withServerTransition({ appId: 'gsrs' }), @@ -132,10 +188,12 @@ import { GlobalErrorHandler } from '@gsrs-core/error-handler/error-handler'; MatTableModule, MatPaginatorModule, MatSelectModule, + MatRadioModule, MatSliderModule, MatDialogModule, StructureEditorModule, FileSelectModule, + MatGridListModule, MatListModule, DynamicComponentLoaderModule.forRoot(dynamicComponentManifests), ScrollToModule, @@ -160,13 +218,26 @@ import { GlobalErrorHandler } from '@gsrs-core/error-handler/error-handler'; MatNativeDateModule, AdminModule, FacetsManagerModule, - CodeDisplayModule + CodeDisplayModule, + SubstanceSsg4mModule, + SubstanceSsg4mProcessModule, + Ssg4mSitesModule, + MatProgressSpinnerModule, + Ssg4mStagesModule, + SubstanceFormSsg4mStartingMaterialsModule, + SubstanceSsg2Module, + Ssg2ManufacturingModule, + SchemaFormModule.forRoot(), + BulkSearchModule, + ElementLabelDisplayModule, + PrivacyStatementModule ], providers: [ { provide: ErrorHandler, useClass: GlobalErrorHandler }, + { provide: WidgetRegistry, useClass: DefaultWidgetRegistry }, CanActivateAdminPage, ConfigService, { @@ -175,7 +246,8 @@ import { GlobalErrorHandler } from '@gsrs-core/error-handler/error-handler'; deps: [ConfigService], multi: true }, - { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } + { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }, + {provide: WidgetRegistry, useClass: MyWidgetRegistry} ], bootstrap: [AppComponent], entryComponents: [ @@ -183,6 +255,24 @@ import { GlobalErrorHandler } from '@gsrs-core/error-handler/error-handler'; ExportDialogComponent, SubstanceEditImportDialogComponent, SubstanceHistoryDialogComponent, + ShowMolfileDialogComponent, + CustomCheckboxWidgetComponent, + CustomTextWidgetComponent, + CustomMultiselectWidgetComponent, + CustomSelectWidgetComponent, + CustomRadioWidgetComponent, + CustomMultiCheckboxWidgetComponent, + CustomTextareaWidgetComponent, + MergeActionDialogComponent, + UserQueryListDialogComponent, + ListCreateDialogComponent, + ImportScrubberComponent + ], + exports: [ + StructureEditorModule, + NameResolverModule, + NamesDisplayPipe, + SubstanceStatusPipe ] }) export class AppModule {} diff --git a/src/app/core/app.server.module.ts b/src/app/core/app.server.module.ts index 226064315..f085173db 100644 --- a/src/app/core/app.server.module.ts +++ b/src/app/core/app.server.module.ts @@ -1,6 +1,6 @@ import { NgModule } from '@angular/core'; import { ServerModule } from '@angular/platform-server'; -import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader'; +//import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader'; import { AppModule } from './app.module'; import { AppComponent } from './app.component'; @@ -11,7 +11,7 @@ import { UniversalInterceptor } from './config/universal.interceptor'; imports: [ AppModule, ServerModule, - ModuleMapLoaderModule + //ModuleMapLoaderModule ], providers: [ { diff --git a/src/app/core/assets/data/adverseevent_dictionary.json b/src/app/core/assets/data/adverseevent_dictionary.json new file mode 100644 index 000000000..74a5b2358 --- /dev/null +++ b/src/app/core/assets/data/adverseevent_dictionary.json @@ -0,0 +1,66 @@ +{ + "Adverse Event":{ + "lucenePath":"root_adverseEvent", + "description":"PT Term", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Adverse_Event" + }, + "PT Term":{ + "lucenePath":"root_ptTerm", + "description":"PT Term", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"PT_Term" + }, + "Prim SOC":{ + "lucenePath":"root_primSoc", + "description":"Prim SOC", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Prim_SOC" + }, + "DME Reactions":{ + "lucenePath":"root_dmeReactions", + "description":"DME Reactions", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"DME_Reactions" + }, + "PT Term DME Meddra":{ + "lucenePath":"root_ptTermMeddra", + "description":"PT Term DME Meddra", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"PTTerm_Meddra" + }, + "Species":{ + "lucenePath":"root_species", + "description":"Species", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Species" + }, + "Ingredient Name":{ + "lucenePath":"root_name", + "description":"Ingredient Name", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Ingredient_Name" + }, + "Substance Key":{ + "lucenePath":"root_substanceKey", + "description":"Substance Key", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + } +} \ No newline at end of file diff --git a/src/app/core/assets/data/application_dictionary.json b/src/app/core/assets/data/application_dictionary.json new file mode 100644 index 000000000..69056cb1a --- /dev/null +++ b/src/app/core/assets/data/application_dictionary.json @@ -0,0 +1,298 @@ +{ + "Amount":{ + "lucenePath":"root_applicationProductList_amount", + "description":"Amount", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Application Deprecated":{ + "lucenePath":"root_deprecated", + "description":"Deprecated", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Application Number":{ + "lucenePath":"root_appNumber", + "description":"Application Number", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Application_Number" + }, + "Application Record Create Date":{ + "lucenePath":"root_creationDate", + "description":"Record Create Date", + "type":"timestamp", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Application Record Created By":{ + "lucenePath":"root_createdBy", + "description":"Record Created By", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Application Record Last Edited":{ + "lucenePath":"root_lastModifiedDate", + "description":"Record Last Edited", + "type":"timestamp", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Application Record Last Edited By":{ + "lucenePath":"root_modifiedBy", + "description":"Record Last Edited By", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Application Status":{ + "lucenePath":"root_status", + "description":"Application Status", + "type":"string", + "cvDomain":"APPLICATION_STATUS", + "priority":"x", + "suggest":null + }, + "Application Sub Type":{ + "lucenePath":"root_appSubType", + "description":"Application Sub Type", + "type":"string", + "cvDomain":"APPLICATION_SUB_TYPE", + "priority":"x", + "suggest":null + }, + "Application Type":{ + "lucenePath":"root_appType", + "description":"Application Type", + "type":"string", + "cvDomain":"APPLICATION_TYPE", + "priority":"x", + "suggest":null + }, + "Center":{ + "lucenePath":"root_center", + "description":"In Application, lists of centers such as CDER, CBER, CFSAN, etc.", + "type":"string", + "cvDomain":"CENTER", + "priority":"x", + "suggest":null + }, + "Division Class Description":{ + "lucenePath":"root_divisionClassDesc", + "description":"Division Class Description", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Division_Class_Description" + }, + "Dosage Form":{ + "lucenePath":"root_applicationProductList_dosageForm", + "description":"Dosage Form", + "type":"string", + "cvDomain":"DOSAGE_FORM", + "priority":"x", + "suggest":null + }, + "External Title":{ + "lucenePath":"root_title", + "description":"External Title coming from other source", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":"External Title" + }, + "Has Indications":{ + "lucenePath":"root_hasIndications", + "description":"Has Indications", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Has Ingredients":{ + "lucenePath":"root_hasIngredients", + "description":"Has Ingredients", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Has Products":{ + "lucenePath":"root_hasProducts", + "description":"Has Products", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Indication":{ + "lucenePath":"root_applicationIndicationList_indication", + "description":"Indication", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Indication" + }, + "Ingredient Create Date":{ + "lucenePath":"root_applicationProductList_applicationIngredientList_creationDate", + "description":"Ingredient Create Date", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Ingredient Created By":{ + "lucenePath":"root_applicationProductList_applicationIngredientList_createdBy", + "description":"Ingredient Created By", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Ingredient Last Edited":{ + "lucenePath":"root_applicationProductList_applicationIngredientList_lastModifiedDate", + "description":"Ingredient Last Edited", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Ingredient Last Edited By":{ + "lucenePath":"root_applicationProductList_applicationIngredientList_modifiedBy", + "description":"Ingredient Last Edited By", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Ingredient Name":{ + "lucenePath":"Ingredient Name", + "description":"Ingredient Name", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Ingredient_Name" + }, + "Ingredient Type":{ + "lucenePath":"root_applicationProductList_applicationIngredientList_ingredientType", + "description":"Ingredient Type", + "type":"string", + "cvDomain":"INGREDIENT_TYPE", + "priority":"x", + "suggest":null + }, + "Non Proprietary Name":{ + "lucenePath":"root_nonProprietaryName", + "description":"Non Proprietary Name", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Product Name":{ + "lucenePath":"root_applicationProductList_applicationProductNameList_productName", + "description":"Product Name", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Product_Name" + }, + "Product Name Type":{ + "lucenePath":"root_applicationProductList_applicationProductNameList_productNameType", + "description":"Product Name Type", + "type":"string", + "cvDomain":"PROD_PRODUCT_NAME_TYPE", + "priority":"x", + "suggest":null + }, + "Provenance (GSRS)":{ + "lucenePath":"root_provenance", + "description":"Provenance (GSRS)", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Provenance_(GSRS)" + }, + "Public Domain":{ + "lucenePath":"root_publicDomain", + "description":"Public Domain", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Route of Administration":{ + "lucenePath":"root_applicationProductList_routeAdmin", + "description":"Route of Administration for Application", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Sponsor Name":{ + "lucenePath":"root_sponsorName", + "description":"Sponsor Name", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Sponsor_Name" + }, + "Status Date":{ + "lucenePath":"root_statusDate", + "description":"Status Date of Application", + "type":"Date", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Status Date Year":{ + "lucenePath":"root_statusDateYear", + "description":"Status Date Year", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Submit Date":{ + "lucenePath":"root_submitDate", + "description":"Submit Date of Application", + "type":"Date", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Submit Date Year":{ + "lucenePath":"root_submitDateYear", + "description":"Submit Date Year", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Substance Key":{ + "lucenePath":"root_applicationProductList_applicationIngredientList_substanceKey", + "description":"Substance Key", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Title":{ + "lucenePath":"root_title", + "description":"Title", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Title" + } +} \ No newline at end of file diff --git a/src/app/core/assets/data/cteu_dictionary.json b/src/app/core/assets/data/cteu_dictionary.json new file mode 100644 index 000000000..b7a3fe928 --- /dev/null +++ b/src/app/core/assets/data/cteu_dictionary.json @@ -0,0 +1,306 @@ +{ + "Clinical Trial Europe Competent Authority Decision":{ + "lucenePath":"root_competentAuthorityDecision", + "description":"Competent authority decision", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Competent Authority Decision Date":{ + "lucenePath":"root_competentAuthorityDecisionDate", + "description":"Competent authority decision date", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Country":{ + "lucenePath":"root_country", + "description":"Country where the trial takes place", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Creation Date":{ + "lucenePath":"root_creationDate", + "description":"A UNIX time stamp indicating date that record was created in GSRS", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Date First Entered DB":{ + "lucenePath":"root_dateFirstEnteredDb", + "description":"Date first entered into source's database", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Deprecated ":{ + "lucenePath":"root_getDeprecated", + "description":"Indexed value indicating whether the record is deprecated or not", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Drug Id":{ + "lucenePath":"root_clinicalTrialEuropeProductList_clinicalTrialEuropeDrugList_id", + "description":"The table row id of a substance", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Drug Substance Key":{ + "lucenePath":"root_clinicalTrialEuropeProductList_clinicalTrialEuropeDrugList_substanceKey", + "description":"A substance key value identifying a GSRS substance", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Drug Substance Key Type":{ + "lucenePath":"root_clinicalTrialEuropeProductList_clinicalTrialEuropeDrugList_substanceKeyType", + "description":"The substance key type (such as a UUID)", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Ethics Com Opinion App":{ + "lucenePath":"root_ethicsComOpinionApp", + "description":"Ethics Committee opinion of the trial application", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Ethics Com Opinion Date":{ + "lucenePath":"root_ethicsComOpinionDate", + "description":"Ethics Committee opinion of the trial date", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Ethics Com Opinion Reason":{ + "lucenePath":"root_ethicsComOpinionReason", + "description":"Ethics Committee opinion of the trial reason", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Kind":{ + "lucenePath":"root_kind", + "description":"The data source of the trial, in this case \"EUROPE\"", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Last Modified Date":{ + "lucenePath":"root_lastModifiedDate", + "description":"A UNIX time stamp indicating date that record was last modified in GSRS", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe MedDra Class Code":{ + "lucenePath":"root_clinicalTrialEuropeMeddraList_meddraClassCode", + "description":"The MedDra class code", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe MedDra Id":{ + "lucenePath":"root_clinicalTrialEuropeMeddraList_id", + "description":"The table row id of the MedDra classification", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe MedDra System Organ Class":{ + "lucenePath":"root_clinicalTrialEuropeMeddraList_meddraSystemOrganClass", + "description":"The MedDra System Organ Class", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe MedDra Term":{ + "lucenePath":"root_clinicalTrialEuropeMeddraList_meddraTerm", + "description":"The MedDra Term", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe MedDra Version":{ + "lucenePath":"root_clinicalTrialEuropeMeddraList_meddraVersion", + "description":"The MedDra classification version number", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Medical Condition Investigated":{ + "lucenePath":"root_clinicalTrialEuropeMedicalList_medicalCondInvesigated", + "description":"Medical condition investigated", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Medical Condition Investigated EZ":{ + "lucenePath":"root_clinicalTrialEuropeMedicalList_medicalCondInvesigatedEz", + "description":"Medical condition investigated easily understood language", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Medical Condition Therapy Area":{ + "lucenePath":"root_clinicalTrialEuropeMedicalList_medicalCondTherapyArea", + "description":"The therapeutic area of the condition", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Medical Id":{ + "lucenePath":"root_clinicalTrialEuropeMedicalList_medicalCondInvesigated", + "description":"The table row id of the medical condition", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe National Competent Authority":{ + "lucenePath":"root_nationalCompetentAuthority", + "description":"National competent authority", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Product Id":{ + "lucenePath":"root_clinicalTrialEuropeProductList_id", + "description":"The table row id in the list of products", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Product Imp Pharmaceutical Form":{ + "lucenePath":"root_clinicalTrialEuropeProductList_pharmaceuticalForm", + "description":"Pharmaceutical Form of the product", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Product Imp Role":{ + "lucenePath":"root_clinicalTrialEuropeProductList_impRole", + "description":"Role of the product in the given imp section", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Product Imp Routes Admin":{ + "lucenePath":"root_clinicalTrialEuropeProductList_impRoutesAdmin", + "description":"Route of administration of the product in the given imp section", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Product Imp Section":{ + "lucenePath":"root_clinicalTrialEuropeProductList_impSection", + "description":"Investigational medicinal product section number", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Product Ingredient Name":{ + "lucenePath":"root_clinicalTrialEuropeProductList_prodIngredName", + "description":"Name of ingredient investigated", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Product Name":{ + "lucenePath":"root_clinicalTrialEuropeProductList_productName", + "description":"Name of product investigated", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Product Trade Name":{ + "lucenePath":"root_clinicalTrialEuropeProductList_tradeName", + "description":"Trade name of product investigated", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Sponsor Name":{ + "lucenePath":"root_sponsorName", + "description":"Sponsor name of the trial", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Title":{ + "lucenePath":"root_title", + "description":"The title of the trial", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Trial Number":{ + "lucenePath":"root_trialNumber", + "description":"The string id of the trial, assigned by the source", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Trial Results":{ + "lucenePath":"root_trialResults", + "description":"Summary of trial results", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Trial Status":{ + "lucenePath":"root_trialStatus", + "description":"Status of the trial", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial Europe Url":{ + "lucenePath":"root_url", + "description":"The URL of the trial at its source", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + } +} \ No newline at end of file diff --git a/src/app/core/assets/data/ctus_dictionary.json b/src/app/core/assets/data/ctus_dictionary.json new file mode 100644 index 000000000..c34e385b1 --- /dev/null +++ b/src/app/core/assets/data/ctus_dictionary.json @@ -0,0 +1,394 @@ +{ + "Clinical Trial US Acronym":{ + "lucenePath":"root_acronym", + "description":"Acronyms used in the trial, pipe delimited", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Age Groups":{ + "lucenePath":"root_ageGroups", + "description":"Age groups of participants in the trial", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Age Groups (index values)":{ + "lucenePath":"root_getAgeGroupIndexing", + "description":"A list of age groups of participants", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Completion Date":{ + "lucenePath":"root_completionDate", + "description":"A UNIX timestamp indicating the completion date, at the source", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Conditions":{ + "lucenePath":"root_conditions", + "description":"The health conditions the trial addresses, pipe delimited", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Conditions (index values)":{ + "lucenePath":"root_getConditionsIndexing", + "description":"A list of conditions indicated in the trial", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":"Conditions" + }, + "Clinical Trial US Creation Date":{ + "lucenePath":"root_creationDate", + "description":"A UNIX timestamp indicating the date the record was created in GSRS", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Deprecated":{ + "lucenePath":"root_getDeprecated", + "description":"String indicating whether or not study is \"Deprecated\" in GSRS", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Drug Id":{ + "lucenePath":"root_clinicalTrialUSDrug_id", + "description":"The table row id in the list of a Clinical Trial Drug record", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Drug Protected Match":{ + "lucenePath":"root_clinicalTrialUSDrug_protectedMatch", + "description":"A Boolean indicating whether the substance match is proprietary information", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Drug Substance Key":{ + "lucenePath":"root_clinicalTrialUSDrug_substanceKey", + "description":"A substance key value identifying a GSRS substance", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Drug Substance Key Type":{ + "lucenePath":"root_clinicalTrialUSDrug_trialNumber", + "description":"The substance key type (such as UUID)", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Enrollment":{ + "lucenePath":"root_enrollment", + "description":"Number of participants in trial", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US First Received":{ + "lucenePath":"root_firstReceived", + "description":"A UNIX timestamp indicating the date first received, at the source", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US First Received Year":{ + "lucenePath":"root_getFirstPostedYear", + "description":"First received year at the source", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Funded Bys":{ + "lucenePath":"root_fundedBys", + "description":"Funders of the trial, pipe delimited", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US GSRS Matching Complete":{ + "lucenePath":"root_gsrsMatchingComplete", + "description":"A Boolean indicating whether GSRS curators consider that substances in the trial have been fully matched, to the extent possible", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Gender":{ + "lucenePath":"root_gender", + "description":"Gender(s) of participants in the trial (Male, Female, All, or null)", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Has Substances":{ + "lucenePath":"root_getHasSubstances", + "description":"String indicating whether or not any substances from the trial have been matched", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Internal Version":{ + "lucenePath":"root_internalVersion", + "description":"A bookkeeping internal version number assigned by GSRS", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Intervention":{ + "lucenePath":"root_intervention", + "description":"The interventions studied in the trial (drug, procedure, etc.)", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Intervention Type (index values)":{ + "lucenePath":"root_getInterventionTypeIndexing", + "description":"A list of interventions types indicated in the trial", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Interventions (index values)":{ + "lucenePath":"root_getInterventionIndexing", + "description":"A list of interventions indicated in the trial", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Kind":{ + "lucenePath":"root_kind", + "description":"The data source of the trial, in this case \"US\"", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Last Modified Date":{ + "lucenePath":"root_lastModifiedDate", + "description":"A UNIX timestamp indicating the date the record was last modified in GSRS", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Last Updated":{ + "lucenePath":"root_lastUpdated", + "description":"A UNIX timestamp indicating the date last updated, at the source", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Last Updated Year":{ + "lucenePath":"root_getLastUpdatedYear", + "description":"The year the trial was last updated at the source", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Last Verified":{ + "lucenePath":"root_lastVerified", + "description":"A UNIX timestamp indicating the last verified date, at the source", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Locations":{ + "lucenePath":"root_locations", + "description":"Locations where the trial took place, pipe delimited", + "type":"String", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Matching Complete":{ + "lucenePath":"root_getCTMatchingComplete", + "description":"String indicating whether or not substance matching is complete", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Other Ids":{ + "lucenePath":"root_otherIds", + "description":"Other study IDs noted in the trial, pipe delimited", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Outcome Measures":{ + "lucenePath":"root_outcomeMeasures", + "description":"Outcome measures of the trial, pipe delimited", + "type":"String", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Outcome Measures (index values)":{ + "lucenePath":"root_getOutcomeMeasureIndexing", + "description":"A list of outcome measures indicated in the trial", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Phases":{ + "lucenePath":"root_phases", + "description":"The phases of the trial, pipe delimited", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Primary Completion Date":{ + "lucenePath":"root_primaryCompletionDate", + "description":"A UNIX timestamp indicating the primary completion date, at the source", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Primary Completion Year":{ + "lucenePath":"root_getPrimaryCompletionYear", + "description":"Primary completion year at the source", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Recruitment":{ + "lucenePath":"root_recruitment", + "description":"The recruitment status of the trial", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Results First Received":{ + "lucenePath":"root_resultsFirstReceived", + "description":"A UNIX timestamp indicating when the trial was first received at the source", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Sponsor":{ + "lucenePath":"root_sponsor", + "description":"The sponsors of the trial, pipe delimited", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Sponsors (index values)":{ + "lucenePath":"root_getSponsorIndexing", + "description":"A list of sponsors indicated in the trial", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":"Sponsors" + }, + "Clinical Trial US Start Date":{ + "lucenePath":"root_startDate", + "description":"A UNIX timestamp indicating the start date of the trial, at the source", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Status":{ + "lucenePath":"root_status", + "description":"Trial status at the source", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Study Designs":{ + "lucenePath":"root_studyDesigns", + "description":"Study designs, pipe delimited", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Study Results":{ + "lucenePath":"root_studyResults", + "description":"String indicating if results are available or not", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Study Types":{ + "lucenePath":"root_studyTypes", + "description":"Study types of the trial, pipe delimited", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Study Types (index values)":{ + "lucenePath":"root_getStudyTypesIndexing", + "description":"A list of study types indicated in the trial", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Title":{ + "lucenePath":"root_title", + "description":"The title of the trial", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Trial Number":{ + "lucenePath":"root_trialNumber", + "description":"The string id of the trial, assigned by the source", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Clinical Trial US Url":{ + "lucenePath":"root_url", + "description":"The URL of the trial at its source", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + } +} \ No newline at end of file diff --git a/src/app/core/assets/data/product_dictionary.json b/src/app/core/assets/data/product_dictionary.json new file mode 100644 index 000000000..5f41a63ba --- /dev/null +++ b/src/app/core/assets/data/product_dictionary.json @@ -0,0 +1,546 @@ +{ + "Application Number":{ + "lucenePath":"root_appNumber", + "description":"Application Number for Product", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Compose Product Name":{ + "lucenePath":"root_composeProductName", + "description":"Composed Product Name", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Ingredient Average":{ + "lucenePath":"root_productManufactureItems_productLots_productIngredients_average", + "description":"Ingredient Average", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Ingredient Basis of Strength Substance Key":{ + "lucenePath":"root_productManufactureItems_productLots_productIngredients_basisOfStrengthSubstanceKey", + "description":"Ingredient Basis of Strength Substance Key", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Ingredient Basis of Strength Substance Key Type":{ + "lucenePath":"root_productManufactureItems_productLots_productIngredients_basisOfStrengthSubstanceKeyType", + "description":"Ingredient Basis of Strength Substance Key Type", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Ingredient Grade":{ + "lucenePath":"root_productManufactureItems_productLots_productIngredients_grade", + "description":"Ingredient Grade", + "type":"string", + "cvDomain":"PROD_GRADE", + "priority":null, + "suggest":null + }, + "Ingredient High":{ + "lucenePath":"root_productManufactureItems_productLots_productIngredients_high", + "description":"Ingredient High", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Ingredient Ingredient Type":{ + "lucenePath":"root_productManufactureItems_productLots_productIngredients_ingredientType", + "description":"Ingredient Ingredient Type", + "type":"string", + "cvDomain":"INGREDIENT_TYPE", + "priority":null, + "suggest":null + }, + "Ingredient Location":{ + "lucenePath":"root_productManufactureItems_productLots_productIngredients_ingredientLocation", + "description":"Ingredient Location", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Ingredient Lot No":{ + "lucenePath":"root_productManufactureItems_productLots_productIngredients_ingredLotNo", + "description":"Ingredient Lot No", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Ingredient Low":{ + "lucenePath":"root_productManufactureItems_productLots_productIngredients_low", + "description":"Ingredient Low", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Ingredient Manufacturer":{ + "lucenePath":"root_productManufactureItems_productLots_productIngredients_manufacturer", + "description":"Ingredient Manufacturer", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Ingredient Notes":{ + "lucenePath":"root_productManufactureItems_productLots_productIngredients_notes", + "description":"Ingredient Notes", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Ingredient Release Characteristic":{ + "lucenePath":"root_productManufactureItems_productLots_productIngredients_releaseCharacteristic", + "description":"Ingredient Release Characteristic", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Ingredient Substance Key":{ + "lucenePath":"root_productManufactureItems_productLots_productIngredients_substanceKey", + "description":"Ingredient Substance Key", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Ingredient Substance Key Type":{ + "lucenePath":"root_productManufactureItems_productLots_productIngredients_substanceKeyType", + "description":"Ingredient Substance Key Type", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Ingredient Unit":{ + "lucenePath":"root_productManufactureItems_productLots_productIngredients_unit", + "description":"Ingredient Unit", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Non Proprietary Name":{ + "lucenePath":"root_nonProprietaryName", + "description":"Non Proprietary Name", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Pharmacedical Dosage Form":{ + "lucenePath":"root_pharmacedicalDosageForm", + "description":"Pharmaceutical Form of the product", + "type":"string", + "cvDomain":"PROD_PHARMACEDICAL_DOSAGE_FORM", + "priority":null, + "suggest":null + }, + "Product Amount":{ + "lucenePath":"root_productManufactureItems_amount", + "description":"Product Amount", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Application Type":{ + "lucenePath":"root_appType", + "description":"Application Type for Product", + "type":"string", + "cvDomain":"APPLICATION_TYPE", + "priority":null, + "suggest":null + }, + "Product Code":{ + "lucenePath":"root_productCodes_productCode", + "description":"Product Term", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Code Adress":{ + "lucenePath":"root_productCompanies_companyAddress", + "description":"Product Code Adress", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Code City":{ + "lucenePath":"root_productCompanies_companyCity", + "description":"Product Code City", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Code Code":{ + "lucenePath":"root_productCompanies_companyCode", + "description":"Product Company Code", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Code Country Code":{ + "lucenePath":"root_productCodes_countryCode", + "description":"Product Code Country Code", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Code Type":{ + "lucenePath":"root_productCodes_productCodeType", + "description":"Product Code Type", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Code Zipcode":{ + "lucenePath":"root_productCompanies_companyZip", + "description":"Product Code zipcode", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Color Name":{ + "lucenePath":"root_productManufactureItems_charColor", + "description":"Product Color Name", + "type":"string", + "cvDomain":"PROD_CHARACTER_COLOR", + "priority":null, + "suggest":null + }, + "Product Company Code Type":{ + "lucenePath":"root_productCompanies_companyCodeType", + "description":"Product Company Code Type", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Company Country":{ + "lucenePath":"root_productCompanies_companyCountry", + "description":"Product Company Country", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Company Name":{ + "lucenePath":"root_productCompanies_companyName", + "description":"Product Company Name", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Company Role":{ + "lucenePath":"root_productCompanies_companyRole", + "description":"Product Company Role", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Company State":{ + "lucenePath":"root_productCompanies_companyState", + "description":"Product Company State", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Country Code":{ + "lucenePath":"root_countryCode", + "description":"Country Code", + "type":"string", + "cvDomain":"PROD_COUNTRY_CODE", + "priority":null, + "suggest":null + }, + "Product Created By":{ + "lucenePath":"root_createdBy", + "description":"Created By for Product", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Created Date":{ + "lucenePath":"root_creationDate", + "description":"Created Date for Product", + "type":"Date", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Dosage Form":{ + "lucenePath":"root_productManufactureItems_dosageForm", + "description":"Product Dosage Form", + "type":"string", + "cvDomain":"DOSAGE_FORM", + "priority":null, + "suggest":null + }, + "Product Flavor Name":{ + "lucenePath":"root_productManufactureItems_charFlavor", + "description":"Product Flavor Name", + "type":"string", + "cvDomain":"PROD_CHARACTER_FLAVOR", + "priority":null, + "suggest":null + }, + "Product Imprint Text":{ + "lucenePath":"root_productManufactureItems_charImprintText", + "description":"Product Imprint Text", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Language":{ + "lucenePath":"root_language", + "description":"Language", + "type":"string", + "cvDomain":"LANGUAGE", + "priority":null, + "suggest":null + }, + "Product Lot Expiry Date":{ + "lucenePath":"root_productManufactureItems_productLots_expiryDate", + "description":"Product Lot Expiry Date", + "type":"Date", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Lot Manufacture Date":{ + "lucenePath":"root_productManufactureItems_productLots_manufactureDate", + "description":"Product Lot Manufacture Date", + "type":"Date", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Lot No":{ + "lucenePath":"root_productManufactureItems_productLots_lotNo", + "description":"Product Lot No", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Lot Size":{ + "lucenePath":"root_productManufactureItems_productLots_lotSize", + "description":"Product Lot Size", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Manufacture Code":{ + "lucenePath":"root_productManufactureItems_manufactureCode", + "description":"Product Manufacture Code", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Manufacture Code Type":{ + "lucenePath":"root_productManufactureItems_manufactureCodeType", + "description":"Product Manufacture Code Type", + "type":"string", + "cvDomain":"PROD_MANUFACTURE_CODE_TYPE", + "priority":null, + "suggest":null + }, + "Product Modified By":{ + "lucenePath":"root_modifiedBy", + "description":"Modified By for Product", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Modify Date":{ + "lucenePath":"root_lastModifiedDate", + "description":"Last Modified Date for Product", + "type":"Date", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Name":{ + "lucenePath":"root_productNames_productName", + "description":"Product Name", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Name Type":{ + "lucenePath":"root_productNames_productNameType", + "description":"Product Name Type", + "type":"string", + "cvDomain":"PROD_PRODUCT_NAME_TYPE", + "priority":null, + "suggest":null + }, + "Product Proprietary Name":{ + "lucenePath":"root_proprietaryName", + "description":"Proprietary Name", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Provenance":{ + "lucenePath":"root_provenance", + "description":"Provenance", + "type":"string", + "cvDomain":"PROD_PROVENANCE", + "priority":null, + "suggest":null + }, + "Product Public Domain":{ + "lucenePath":"root_public domain", + "description":"Public Domain", + "type":"string", + "cvDomain":"PUBLIC_DOMAIN", + "priority":null, + "suggest":null + }, + "Product Route of Administration":{ + "lucenePath":"root_routeAdmin", + "description":"Route of Administration", + "type":"string", + "cvDomain":"PROD_ROUTE_OF_ADMIN", + "priority":null, + "suggest":null + }, + "Product Scoring":{ + "lucenePath":"root_productManufactureItems_charNumFragments", + "description":"Product Scoring", + "type":"string", + "cvDomain":"PROD_CHARACTER_FRAGMENTS", + "priority":null, + "suggest":null + }, + "Product Shape Name":{ + "lucenePath":"root_productManufactureItems_charShape", + "description":"Product Shape Name", + "type":"string", + "cvDomain":"PROD_CHARACTER_SHAPE", + "priority":null, + "suggest":null + }, + "Product Size":{ + "lucenePath":"root_productManufactureItems_charSize", + "description":"Product Size", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Source":{ + "lucenePath":"root_source", + "description":"Product Source", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Source Type":{ + "lucenePath":"root_sourceType", + "description":"Source Type", + "type":"string", + "cvDomain":"PROD_SOURCE_TYPE", + "priority":null, + "suggest":null + }, + "Product Status":{ + "lucenePath":"root_status", + "description":"Product Status", + "type":"string", + "cvDomain":"PROD_STATUS", + "priority":null, + "suggest":null + }, + "Product Term":{ + "lucenePath":"root_productNames_productTermAndParts_productTerm", + "description":"Product Term", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Term Part":{ + "lucenePath":"root_productNames_productTermAndParts_productTermPart", + "description":"Product Term Part", + "type":"string", + "cvDomain":"PROD_TERM_PART", + "priority":null, + "suggest":null + }, + "Product Type":{ + "lucenePath":"root_productType", + "description":"Product Type", + "type":"string", + "cvDomain":"PROD_PRODUCT_TYPE", + "priority":null, + "suggest":null + }, + "Product Unit":{ + "lucenePath":"root_productManufactureItems_unit", + "description":"Product Unit", + "type":"string", + "cvDomain":"PROD_UNIT", + "priority":null, + "suggest":null + }, + "Release Characteristic":{ + "lucenePath":"root_releaseCharacteristic", + "description":"Release Characteristic", + "type":"string", + "cvDomain":"PROD_RELEASE_CHARACTERISTIC", + "priority":null, + "suggest":null + }, + "Strength Characteristic":{ + "lucenePath":"root_strengthCharacteristic", + "description":"Strength Characteristic", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Unit of Presentation":{ + "lucenePath":"root_unitPresentation", + "description":"Unit of Presentation", + "type":"string", + "cvDomain":"PROD_UNIT_PRESENTATION", + "priority":null, + "suggest":null + } +} \ No newline at end of file diff --git a/src/app/core/assets/data/productall_dictionary.json b/src/app/core/assets/data/productall_dictionary.json new file mode 100644 index 000000000..bba77f639 --- /dev/null +++ b/src/app/core/assets/data/productall_dictionary.json @@ -0,0 +1,258 @@ +{ + "Active Moiety":{ + "lucenePath":"root_productIngredientAllList_activeMoietyName", + "description":"Active Moiety", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Active_Moiety" + }, + "Application Type Number":{ + "lucenePath":"root_appTypeNumber", + "description":"Application Type Number", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Application_Type_Number" + }, + "Company Country":{ + "lucenePath":"root_productCompanyAllList_countryWithoutCode", + "description":"Company Country", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Company_Country" + }, + "Dosage Form Name":{ + "lucenePath":"root_productIngredientAllList_dosageFormName", + "description":"Dosage Form Name", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Dosage_Form_Name" + }, + "FEI Number":{ + "lucenePath":"root_productCompanyAllList_feiNumber", + "description":"FEI Number", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "From Table":{ + "lucenePath":"root_fromTable", + "description":"From Table", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Ingredient Approval ID":{ + "lucenePath":"root_productIngredientAllList_substanceApprovalId", + "description":"Ingredient Approval ID", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Ingredient Name":{ + "lucenePath":"Ingredient Name", + "description":"Ingredient Name", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Ingredient_Name" + }, + "Ingredient Type":{ + "lucenePath":"root_productIngredientAllList_ingredientType", + "description":"Ingredient Type", + "type":"string", + "cvDomain":"INGREDIENT_TYPE", + "priority":"x", + "suggest":null + }, + "Is Listed":{ + "lucenePath":"root_isListed", + "description":"Is Listed", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Labeler City":{ + "lucenePath":"root_productCompanyAllList_city", + "description":"City", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Labeler DUNS Number":{ + "lucenePath":"root_productCompanyAllList_labelerDuns", + "description":"Labeler DUNS Number", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Labeler Name":{ + "lucenePath":"root_productCompanyAllList_labelerName", + "description":"Labeler Name", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Labeler_Name" + }, + "Labeler State":{ + "lucenePath":"root_productCompanyAllList_state", + "description":"State", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Marketing Category Name":{ + "lucenePath":"root_marketingCategoryName", + "description":"Marketing Category Name", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Marketing_Category_Name" + }, + "Nonproprietary Name":{ + "lucenePath":"root_nonProprietaryName", + "description":"Nonproprietary Name", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Nonproprietary_Name" + }, + "Product Application Number":{ + "lucenePath":"root_appNumber", + "description":"Product Application Number", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Product Application Type":{ + "lucenePath":"root_appType", + "description":"Product Application Type", + "type":"string", + "cvDomain":"APPLICATION_TYPE", + "priority":"x", + "suggest":null + }, + "Product Deprecated":{ + "lucenePath":"root_deprecated", + "description":"Product Deprecated", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product Entity Link Substances":{ + "lucenePath":"root_entity_link_substances", + "description":"Entity Link Substances related to Substance Module", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Product ID":{ + "lucenePath":"root_productNDC", + "description":"Product ID", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Product_ID" + }, + "Product Name":{ + "lucenePath":"root_productNameAllList_productName", + "description":"Product Name", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Product_Name" + }, + "Product Source":{ + "lucenePath":"root_source", + "description":"Product Source", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Product Status":{ + "lucenePath":"root_status", + "description":"Status", + "type":"string", + "cvDomain":"PROD_STATUS", + "priority":"x", + "suggest":null + }, + "Product Type":{ + "lucenePath":"root_productType", + "description":"Product Type", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Product_Type" + }, + "Provenance":{ + "lucenePath":"root_provenance", + "description":"Provenance", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Provenance" + }, + "Route of Admin":{ + "lucenePath":"root_routeName", + "description":"Route of Admin", + "type":"string", + "cvDomain":"PROD_ROUTE_OF_ADMIN", + "priority":"x", + "suggest":null + }, + "Source Type":{ + "lucenePath":"root_sourceType", + "description":"Source Type", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Substance Key":{ + "lucenePath":"root_productIngredientAllList_substanceKey", + "description":"Substance Key", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Unit Presentation":{ + "lucenePath":"root_unitPresentation", + "description":"Unit Presentation", + "type":"string", + "cvDomain":"PROD_UNIT_PRESENTATION", + "priority":"x", + "suggest":null + }, + "Product Record Create Date":{ + "lucenePath":"root_creationDate", + "description":"Record Create Date", + "type":"timestamp", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Product Record Last Edited":{ + "lucenePath":"root_lastModifiedDate", + "description":"Record Last Edited", + "type":"timestamp", + "cvDomain":null, + "priority":"x", + "suggest":null + } +} \ No newline at end of file diff --git a/src/app/core/assets/data/substance_dictionary.json b/src/app/core/assets/data/substance_dictionary.json index 5fa492ea6..7b37ba888 100644 --- a/src/app/core/assets/data/substance_dictionary.json +++ b/src/app/core/assets/data/substance_dictionary.json @@ -1,4497 +1,5226 @@ { - "Agent Modification Access": { - "lucenePath": "root_modifications_agentModifications_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Agent Modification Amount Access": { - "lucenePath": "root_modifications_agentModifications_amount_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Agent Modification Amount Average": { - "lucenePath": "D_root_modifications_agentModifications_amount_average", - "description": "The amount's numeric average if present.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Agent Modification Amount Created": { - "lucenePath": "root_modifications_agentModifications_amount_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Agent Modification Amount Created By": { - "lucenePath": "root_modifications_agentModifications_amount_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Agent Modification Amount Deprecated": { - "lucenePath": "root_modifications_agentModifications_amount_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Agent Modification Amount High": { - "lucenePath": "D_root_modifications_agentModifications_amount_high", - "description": "The highest numerical value likely for the average amount. (highest average).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Agent Modification Amount High Limit": { - "lucenePath": "D_root_modifications_agentModifications_amount_highLimit", - "description": "The highest allowable numeric value usable for the amount. (highest limit).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Agent Modification Amount Last Edited": { - "lucenePath": "root_modifications_agentModifications_amount_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Agent Modification Amount Last Edited By": { - "lucenePath": "root_modifications_agentModifications_amount_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Agent Modification Amount Low": { - "lucenePath": "D_root_modifications_agentModifications_amount_low", - "description": "The lowest numerical value likely for the average amount. (lowest average).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Agent Modification Amount Low Limit": { - "lucenePath": "D_root_modifications_agentModifications_amount_lowLimit", - "description": "The lowest allowable numeric value usable for the amount. (lowest limit).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Agent Modification Amount Non-Numeric Value": { - "lucenePath": "root_modifications_agentModifications_amount_nonNumericValue", - "description": "A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Agent Modification Amount Type": { - "lucenePath": "root_modifications_agentModifications_amount_type", - "description": "The type of the amount specified ('mol ratio'. 'degree of polymerization', 'weight ratio', etc.).", - "type": "string", - "cvDomain": "AMOUNT_TYPE", - "priority": null - }, - "Agent Modification Amount UUID": { - "lucenePath": "root_modifications_agentModifications_amount_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Agent Modification Amount Units": { - "lucenePath": "root_modifications_agentModifications_amount_units", - "description": "The amount's unit of measurement.", - "type": "string", - "cvDomain": "AMOUNT_UNIT", - "priority": null - }, - "Agent Modification Created": { - "lucenePath": "root_modifications_agentModifications_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Agent Modification Created By": { - "lucenePath": "root_modifications_agentModifications_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Agent Modification Deprecated": { - "lucenePath": "root_modifications_agentModifications_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Agent Modification Last Edited": { - "lucenePath": "root_modifications_agentModifications_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Agent Modification Last Edited By": { - "lucenePath": "root_modifications_agentModifications_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Agent Modification Modification Group": { - "lucenePath": "root_modifications_agentModifications_modificationGroup", - "description": "A key specifying how this modification relates to other modifications. All modifications sharing a group key are considered to be occuring as part of a single process or event, or descriptive of the same change to the substance.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Agent Modification Process": { - "lucenePath": "root_modifications_agentModifications_agentModificationProcess", - "description": "The process used for the agent modification.", - "type": "string", - "cvDomain": "AGENT_MODIFICATION_PROCESS", - "priority": "x" - }, - "Agent Modification Role": { - "lucenePath": "root_modifications_agentModifications_agentModificationRole", - "description": "The role of the agent in the modification process.", - "type": "string", - "cvDomain": "ROLE", - "priority": null - }, - "Agent Modification Type": { - "lucenePath": "root_modifications_agentModifications_agentModificationType", - "description": "The type of agent modification.", - "type": "string", - "cvDomain": "AGENT_MODIFICATION_TYPE", - "priority": null - }, - "Agent Modification UUID": { - "lucenePath": "root_modifications_agentModifications_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Agent Substance Access": { - "lucenePath": "root_modifications_agentModifications_agentSubstance_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Agent Substance Approval ID": { - "lucenePath": "root_modifications_agentModifications_agentSubstance_approvalID", - "description": "The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Agent Substance Created": { - "lucenePath": "root_modifications_agentModifications_agentSubstance_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Agent Substance Created By": { - "lucenePath": "root_modifications_agentModifications_agentSubstance_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Agent Substance Deprecated": { - "lucenePath": "root_modifications_agentModifications_agentSubstance_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Agent Substance Last Edited": { - "lucenePath": "root_modifications_agentModifications_agentSubstance_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Agent Substance Last Edited By": { - "lucenePath": "root_modifications_agentModifications_agentSubstance_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Agent Substance Preferred Name": { - "lucenePath": "root_modifications_agentModifications_agentSubstance_refPname", - "description": "The \"Priority Name\" (usually the display name) of the record being referenced.", - "type": "string", - "cvDomain": null, - "priority": "x" - }, - "Agent Substance Refuuid": { - "lucenePath": "root_modifications_agentModifications_agentSubstance_refuuid", - "description": "The UUID of the related record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Agent Substance Substance Class": { - "lucenePath": "root_modifications_agentModifications_agentSubstance_substanceClass", - "description": "This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Agent Substance UUID": { - "lucenePath": "root_modifications_agentModifications_agentSubstance_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Any Name": { - "lucenePath": "root_names_name", - "description": "The literal string text of a name.", - "type": "string", - "cvDomain": null, - "priority": "x" - }, - "Approval ID": { - "lucenePath": "root_approvalID", - "description": "The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Code Access": { - "lucenePath": "root_codes_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Code Comments": { - "lucenePath": "root_codes_codeText", - "description": "Any comments regarding the relationship.", - "type": "string", - "cvDomain": null, - "priority": "x" - }, - "Code Created": { - "lucenePath": "root_codes_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Code Created By": { - "lucenePath": "root_codes_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Code Deprecated": { - "lucenePath": "root_codes_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Code Last Edited": { - "lucenePath": "root_codes_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Code Last Edited By": { - "lucenePath": "root_codes_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Code Literal": { - "lucenePath": "root_codes_code", - "description": "The literal code value.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Code System": { - "lucenePath": "root_codes_codeSystem", - "description": "The orginizational system which defines the code meaning (e.g. the originating database or classification system).", - "type": "string", - "cvDomain": "CODE_SYSTEM", - "priority": "x" - }, - "Code Text": { - "lucenePath": "root_codes_comments", - "description": "text field for codes", - "type": "string", - "cvDomain": null, - "priority": "x" - }, - "Code Type": { - "lucenePath": "root_codes_type", - "description": "The type of the code (e.g. \"PRIMARY\" for a primary code, \"SECONDARY\" for a secondary code, and \"SUPERSEDED\" for a code which has been superseded by another code, etc).", - "type": "string", - "cvDomain": "CODE_TYPE", - "priority": "x" - }, - "Code URL": { - "lucenePath": "root_codes_url", - "description": "The url to further information regarding this code.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Code UUID": { - "lucenePath": "root_codes_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Constituent Amount Access": { - "lucenePath": "root_specifiedSubstance_constituents_amount_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Constituent Amount Average": { - "lucenePath": "D_root_specifiedSubstance_constituents_amount_average", - "description": "The amount's numeric average if present.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Constituent Amount Created": { - "lucenePath": "root_specifiedSubstance_constituents_amount_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Constituent Amount Created By": { - "lucenePath": "root_specifiedSubstance_constituents_amount_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Constituent Amount Deprecated": { - "lucenePath": "root_specifiedSubstance_constituents_amount_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Constituent Amount High": { - "lucenePath": "D_root_specifiedSubstance_constituents_amount_high", - "description": "The highest numerical value likely for the average amount. (highest average).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Constituent Amount High Limit": { - "lucenePath": "D_root_specifiedSubstance_constituents_amount_highLimit", - "description": "The highest allowable numeric value usable for the amount. (highest limit).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Constituent Amount Last Edited": { - "lucenePath": "root_specifiedSubstance_constituents_amount_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Constituent Amount Last Edited By": { - "lucenePath": "root_specifiedSubstance_constituents_amount_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Constituent Amount Low": { - "lucenePath": "D_root_specifiedSubstance_constituents_amount_low", - "description": "The lowest numerical value likely for the average amount. (lowest average).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Constituent Amount Low Limit": { - "lucenePath": "D_root_specifiedSubstance_constituents_amount_lowLimit", - "description": "The lowest allowable numeric value usable for the amount. (lowest limit).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Constituent Amount Non-Numeric Value": { - "lucenePath": "root_specifiedSubstance_constituents_amount_nonNumericValue", - "description": "A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Constituent Amount Type": { - "lucenePath": "root_specifiedSubstance_constituents_amount_type", - "description": "The type of the amount specified ('mol ratio'. 'degree of polymerization', 'weight ratio', etc.).", - "type": "string", - "cvDomain": "AMOUNT_TYPE", - "priority": null - }, - "Constituent Amount UUID": { - "lucenePath": "root_specifiedSubstance_constituents_amount_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Constituent Amount Units": { - "lucenePath": "root_specifiedSubstance_constituents_amount_units", - "description": "The amount's unit of measurement.", - "type": "string", - "cvDomain": "AMOUNT_UNIT", - "priority": null - }, - "Constituent Role": { - "lucenePath": "root_specifiedSubstance_constituents_role", - "description": "The role the constituent plays in the substance (marker substance, adjuvant, constituent, solvent, etc.).", - "type": "string", - "cvDomain": "CONSTITUENT_ROLE", - "priority": null - }, - "Constituent Substance Access": { - "lucenePath": "root_specifiedSubstance_constituents_substance_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Constituent Substance Approval ID": { - "lucenePath": "root_specifiedSubstance_constituents_substance_approvalID", - "description": "The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Constituent Substance Created": { - "lucenePath": "root_specifiedSubstance_constituents_substance_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Constituent Substance Created By": { - "lucenePath": "root_specifiedSubstance_constituents_substance_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Constituent Substance Deprecated": { - "lucenePath": "root_specifiedSubstance_constituents_substance_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Constituent Substance Last Edited": { - "lucenePath": "root_specifiedSubstance_constituents_substance_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Constituent Substance Last Edited By": { - "lucenePath": "root_specifiedSubstance_constituents_substance_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Constituent Substance Refuuid": { - "lucenePath": "root_specifiedSubstance_constituents_substance_refuuid", - "description": "The UUID of the related record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Constituent Substance Substance Class": { - "lucenePath": "root_specifiedSubstance_constituents_substance_substanceClass", - "description": "This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Constituent Substance Substance Name": { - "lucenePath": "root_specifiedSubstance_constituents_substance_refPname", - "description": "The \"Priority Name\" (usually the display name) of the record being referenced.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Constituent Substance UUID": { - "lucenePath": "root_specifiedSubstance_constituents_substance_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Count Amount Access": { - "lucenePath": "root_moieties_countAmount_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Count Amount Average": { - "lucenePath": "D_root_moieties_countAmount_average", - "description": "The amount's numeric average if present.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Count Amount Created": { - "lucenePath": "root_moieties_countAmount_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Count Amount Created By": { - "lucenePath": "root_moieties_countAmount_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Count Amount Deprecated": { - "lucenePath": "root_moieties_countAmount_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Count Amount High": { - "lucenePath": "D_root_moieties_countAmount_high", - "description": "The highest numerical value likely for the average amount. (highest average).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Count Amount Last Edited": { - "lucenePath": "root_moieties_countAmount_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Count Amount Last Edited By": { - "lucenePath": "root_moieties_countAmount_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Count Amount Low": { - "lucenePath": "D_root_moieties_countAmount_low", - "description": "The lowest numerical value likely for the average amount. (lowest average).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Count Amount Non-Numeric Value": { - "lucenePath": "root_moieties_countAmount_nonNumericValue", - "description": "A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Count Amount Type": { - "lucenePath": "root_moieties_countAmount_type", - "description": "The type of moiety count amount ('Mol ration', 'weight ratio', etc.)", - "type": "string", - "cvDomain": "AMOUNT_TYPE", - "priority": null - }, - "Count Amount UUID": { - "lucenePath": "root_moieties_countAmount_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Count Amount Units": { - "lucenePath": "root_moieties_countAmount_units", - "description": "The amount's unit of measurement.", - "type": "string", - "cvDomain": "AMOUNT_UNIT", - "priority": null - }, - "Display Structure Access": { - "lucenePath": "root_polymer_displayStructure_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Display Structure Charge": { - "lucenePath": "root_polymer_displayStructure_charge", - "description": "The net charge of the structure.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Display Structure Count": { - "lucenePath": "root_polymer_displayStructure_count", - "description": "The exact number of times that the structure is repeated.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Display Structure Created": { - "lucenePath": "root_polymer_displayStructure_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Display Structure Created By": { - "lucenePath": "root_polymer_displayStructure_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Display Structure Defined Stereo": { - "lucenePath": "root_polymer_displayStructure_definedStereo", - "description": "A count of defined stereocenters.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Display Structure Deprecated": { - "lucenePath": "root_polymer_displayStructure_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Display Structure Digest": { - "lucenePath": "root_polymer_displayStructure_digest", - "description": "A hash of the raw structural information encoded.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Display Structure Ez Centers": { - "lucenePath": "root_polymer_displayStructure_ezCenters", - "description": "A count of E\/Z, or absolute double bond stereochemistry centers.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Display Structure Formula": { - "lucenePath": "root_polymer_displayStructure_formula", - "description": "The chemical formula of the record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Display Structure Last Edited": { - "lucenePath": "root_polymer_displayStructure_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Display Structure Last Edited By": { - "lucenePath": "root_polymer_displayStructure_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Display Structure Optical Activity": { - "lucenePath": "root_polymer_displayStructure_opticalActivity", - "description": "The optical activity or rotation of the material. \"(+)\" and \"(-)\" are used for optically active materials where activity is known and significantly useful for definition. \"(+\/-)\" is used for racemic mixtures.", - "type": "string", - "cvDomain": "OPTICAL_ACTIVITY", - "priority": null - }, - "Display Structure Stereo Centers": { - "lucenePath": "root_polymer_displayStructure_stereoCenters", - "description": "A count of possible stereocenters.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Display Structure Stereochemistry": { - "lucenePath": "root_polymer_displayStructure_stereochemistry", - "description": "The structure's type of stereochemistry (absolute, achiral, racemic, etc.).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Display Structure UUID": { - "lucenePath": "root_polymer_classification_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Idealized Structure Access": { - "lucenePath": "root_polymer_idealizedStructure_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Idealized Structure Charge": { - "lucenePath": "root_polymer_idealizedStructure_charge", - "description": "The net charge of the structure.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Idealized Structure Count": { - "lucenePath": "root_polymer_idealizedStructure_count", - "description": "The exact number of times that the structure is repeated.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Idealized Structure Created": { - "lucenePath": "root_polymer_idealizedStructure_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Idealized Structure Created By": { - "lucenePath": "root_polymer_idealizedStructure_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Idealized Structure Defined Stereo": { - "lucenePath": "root_polymer_idealizedStructure_definedStereo", - "description": "A count of defined stereocenters.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Idealized Structure Deprecated": { - "lucenePath": "root_polymer_idealizedStructure_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Idealized Structure Digest": { - "lucenePath": "root_polymer_idealizedStructure_digest", - "description": "A hash of the raw structural information encoded.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Idealized Structure Ez Centers": { - "lucenePath": "root_polymer_idealizedStructure_ezCenters", - "description": "A count of E\/Z, or absolute double bond stereochemistry centers.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Idealized Structure Formula": { - "lucenePath": "root_polymer_idealizedStructure_formula", - "description": "The chemical formula of the record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Idealized Structure Last Edited": { - "lucenePath": "root_polymer_idealizedStructure_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Idealized Structure Last Edited By": { - "lucenePath": "root_polymer_idealizedStructure_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Idealized Structure Optical Activity": { - "lucenePath": "root_polymer_idealizedStructure_opticalActivity", - "description": "The optical activity or rotation of the material. \"(+)\" and \"(-)\" are used for optically active materials where activity is known and significantly useful for definition. \"(+\/-)\" is used for racemic mixtures.", - "type": "string", - "cvDomain": "OPTICAL_ACTIVITY", - "priority": null - }, - "Idealized Structure Stereo Centers": { - "lucenePath": "root_polymer_idealizedStructure_stereoCenters", - "description": "A count of possible stereocenters.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Idealized Structure Stereochemistry": { - "lucenePath": "root_polymer_idealizedStructure_stereochemistry", - "description": "The structure's type of stereochemistry (absolute, achiral, racemic, etc.).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Linkage": { - "lucenePath": "root_nucleicAcid_linkages_linkage", - "description": "The linkage itself which is used. This is a short-hand code for a defined structural linkage fragment found within the GSRS controlled vocabulary.", - "type": "string", - "cvDomain": "NUCLEIC_ACID_LINKAGE", - "priority": "x" - }, - "Linkage Access": { - "lucenePath": "root_nucleicAcid_linkages_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Linkage Created": { - "lucenePath": "root_nucleicAcid_linkages_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Linkage Created By": { - "lucenePath": "root_nucleicAcid_linkages_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Linkage Deprecated": { - "lucenePath": "root_nucleicAcid_linkages_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Linkage Last Edited": { - "lucenePath": "root_nucleicAcid_linkages_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Linkage Last Edited By": { - "lucenePath": "root_nucleicAcid_linkages_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Linkage UUID": { - "lucenePath": "root_nucleicAcid_linkages_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mediator Substance Access": { - "lucenePath": "root_relationships_mediatorSubstance_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Mediator Substance Approval ID": { - "lucenePath": "root_relationships_mediatorSubstance_approvalID", - "description": "The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mediator Substance Created": { - "lucenePath": "root_relationships_mediatorSubstance_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Mediator Substance Created By": { - "lucenePath": "root_relationships_mediatorSubstance_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mediator Substance Deprecated": { - "lucenePath": "root_relationships_mediatorSubstance_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Mediator Substance Last Edited": { - "lucenePath": "root_relationships_mediatorSubstance_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Mediator Substance Last Edited By": { - "lucenePath": "root_relationships_mediatorSubstance_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mediator Substance Preferred Name": { - "lucenePath": "root_relationships_mediatorSubstance_refPname", - "description": "The \"Priority Name\" (usually the display name) of the record being referenced.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mediator Substance Refuuid": { - "lucenePath": "root_relationships_mediatorSubstance_refuuid", - "description": "The UUID of the related record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mediator Substance Substance Class": { - "lucenePath": "root_relationships_mediatorSubstance_substanceClass", - "description": "This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mediator Substance UUID": { - "lucenePath": "root_relationships_mediatorSubstance_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture Access": { - "lucenePath": "root_mixture_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Mixture Component Access": { - "lucenePath": "root_mixture_components_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Mixture Component ApprovalID": { - "lucenePath": "root_mixture_components_substance_approvalID", - "description": "The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture Component Created": { - "lucenePath": "root_mixture_components_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Mixture Component Created By": { - "lucenePath": "root_mixture_components_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture Component Deprecated": { - "lucenePath": "root_mixture_components_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Mixture Component Last Edited": { - "lucenePath": "root_mixture_components_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Mixture Component Last Edited By": { - "lucenePath": "root_mixture_components_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture Component Name": { - "lucenePath": "root_mixture_components_substance_refPname", - "description": "The \"Priority Name\" (usually the display name) of the record being referenced.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture Component Substance Access": { - "lucenePath": "root_mixture_components_substance_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Mixture Component Substance Created": { - "lucenePath": "root_mixture_components_substance_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Mixture Component Substance Created By": { - "lucenePath": "root_mixture_components_substance_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture Component Substance Deprecated": { - "lucenePath": "root_mixture_components_substance_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Mixture Component Substance Last Edited": { - "lucenePath": "root_mixture_components_substance_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Mixture Component Substance Last Edited By": { - "lucenePath": "root_mixture_components_substance_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture Component Substance Refuuid": { - "lucenePath": "root_mixture_components_substance_refuuid", - "description": "The UUID of the related record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture Component Substance Substance Class": { - "lucenePath": "root_mixture_components_substance_substanceClass", - "description": "This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture Component Substance UUID": { - "lucenePath": "root_mixture_components_substance_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture Component Type": { - "lucenePath": "root_mixture_components_type", - "description": "The component type relative to it's mixture ('may be present (any of)', 'must be present (all of)', etc.).", - "type": "string", - "cvDomain": "MIXTURE_TYPE", - "priority": null - }, - "Mixture Component UUID": { - "lucenePath": "root_mixture_components_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture Created": { - "lucenePath": "root_mixture_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Mixture Created By": { - "lucenePath": "root_mixture_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture Deprecated": { - "lucenePath": "root_mixture_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Mixture Last Edited": { - "lucenePath": "root_mixture_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Mixture Last Edited By": { - "lucenePath": "root_mixture_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture Parent Substance Access": { - "lucenePath": "root_mixture_parentSubstance_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Mixture Parent Substance Approval ID": { - "lucenePath": "root_mixture_parentSubstance_approvalID", - "description": "The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture Parent Substance Created": { - "lucenePath": "root_mixture_parentSubstance_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Mixture Parent Substance Created By": { - "lucenePath": "root_mixture_parentSubstance_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture Parent Substance Deprecated": { - "lucenePath": "root_mixture_parentSubstance_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Mixture Parent Substance Last Edited": { - "lucenePath": "root_mixture_parentSubstance_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Mixture Parent Substance Last Edited By": { - "lucenePath": "root_mixture_parentSubstance_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture Parent Substance Preferred Name": { - "lucenePath": "root_mixture_parentSubstance_refPname", - "description": "The \"Priority Name\" (usually the display name) of the record being referenced.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture Parent Substance Refuuid": { - "lucenePath": "root_mixture_parentSubstance_refuuid", - "description": "The UUID of the related record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture Parent Substance Substance Class": { - "lucenePath": "root_mixture_parentSubstance_substanceClass", - "description": "This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture Parent Substance UUID": { - "lucenePath": "root_mixture_parentSubstance_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mixture UUID": { - "lucenePath": "root_mixture_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Modification Access": { - "lucenePath": "root_modifications_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Modification Created": { - "lucenePath": "root_modifications_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Modification Created By": { - "lucenePath": "root_modifications_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Modification Deprecated": { - "lucenePath": "root_modifications_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Modification Last Edited": { - "lucenePath": "root_modifications_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Modification Last Edited By": { - "lucenePath": "root_modifications_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Modification UUID": { - "lucenePath": "root_modifications_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Moiety Access": { - "lucenePath": "root_moieties_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Moiety Atropisomerism": { - "lucenePath": "root_moieties_structure_atropisomerism", - "description": "Whether or not the record has atropisomerism, or has a chirality where a hindered rotation about a single bond as a resolt of steric or electronic constraints.", - "type": "string", - "cvDomain": "ATROPISOMERISM", - "priority": null - }, - "Moiety Charge": { - "lucenePath": "root_moieties_structure_charge", - "description": "The net charge of the structure.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Moiety Count": { - "lucenePath": "root_moieties_structure_count", - "description": "The exact number of times that the structure is repeated.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Moiety Created": { - "lucenePath": "root_moieties_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Moiety Created By": { - "lucenePath": "root_moieties_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Moiety Defined Stereo": { - "lucenePath": "root_moieties_structure_definedStereo", - "description": "A count of defined stereocenters.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Moiety Deprecated": { - "lucenePath": "root_moieties_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Moiety Digest": { - "lucenePath": "root_moieties_structure_digest", - "description": "A hash of the raw structural information encoded.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Moiety Ez Centers": { - "lucenePath": "root_moieties_structure_ezCenters", - "description": "A count of E\/Z, or absolute double bond stereochemistry centers.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Moiety Formula": { - "lucenePath": "root_moieties_structure_formula", - "description": "The chemical formula of the record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Moiety Last Edited": { - "lucenePath": "root_moieties_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Moiety Last Edited By": { - "lucenePath": "root_moieties_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Moiety Mwt": { - "lucenePath": "D_root_moieties_structure_mwt", - "description": "The molecular weight of the structure.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Moiety Optical Activity": { - "lucenePath": "root_moieties_structure_opticalActivity", - "description": "The optical activity or rotation of the material. \"(+)\" and \"(-)\" are used for optically active materials where activity is known and significantly useful for definition. \"(+\/-)\" is used for racemic mixtures.", - "type": "string", - "cvDomain": "OPTICAL_ACTIVITY", - "priority": null - }, - "Moiety Stereo Centers": { - "lucenePath": "root_moieties_structure_stereoCenters", - "description": "A count of possible stereocenters.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Moiety Stereo Comments": { - "lucenePath": "root_moieties_structure_stereoComments", - "description": "Some textually descriptive information about specific stereochemistry that cannot adequately be captured elsewhere.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Moiety Stereochemistry": { - "lucenePath": "root_moieties_stereoChemistry", - "description": "The structure's type of stereochemistry (absolute, achiral, racemic, etc.).", - "type": "string", - "cvDomain": "STEREOCHEMISTRY_TYPE", - "priority": null - }, - "Moiety UUID": { - "lucenePath": "root_moieties_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Mol Formula": { - "lucenePath": "root_structure_formula", - "description": "The chemical formula of the record.", - "type": "string", - "cvDomain": null, - "priority": "x" - }, - "Molecular Fragment Access": { - "lucenePath": "root_modifications_structuralModifications_molecularFragment_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Molecular Fragment Approval ID": { - "lucenePath": "root_modifications_structuralModifications_molecularFragment_approvalID", - "description": "The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Molecular Fragment Created": { - "lucenePath": "root_modifications_structuralModifications_molecularFragment_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Molecular Fragment Created By": { - "lucenePath": "root_modifications_structuralModifications_molecularFragment_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Molecular Fragment Deprecated": { - "lucenePath": "root_modifications_structuralModifications_molecularFragment_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Molecular Fragment Last Edited": { - "lucenePath": "root_modifications_structuralModifications_molecularFragment_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Molecular Fragment Last Edited By": { - "lucenePath": "root_modifications_structuralModifications_molecularFragment_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Molecular Fragment Preferred Name": { - "lucenePath": "root_modifications_structuralModifications_molecularFragment_refPname", - "description": "The \"Priority Name\" (usually the display name) of the record being referenced.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Molecular Fragment Refuuid": { - "lucenePath": "root_modifications_structuralModifications_molecularFragment_refuuid", - "description": "The UUID of the related record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Molecular Fragment Substance Class": { - "lucenePath": "root_modifications_structuralModifications_molecularFragment_substanceClass", - "description": "This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Molecular Fragment UUID": { - "lucenePath": "root_modifications_structuralModifications_molecularFragment_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Monomer Access": { - "lucenePath": "root_polymer_monomers_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Monomer Amount Access": { - "lucenePath": "root_polymer_monomers_amount_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Monomer Amount Average": { - "lucenePath": "D_root_polymer_monomers_amount_average", - "description": "The amount's numeric average if present.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Monomer Amount Created": { - "lucenePath": "root_polymer_monomers_amount_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Monomer Amount Created By": { - "lucenePath": "root_polymer_monomers_amount_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Monomer Amount Deprecated": { - "lucenePath": "root_polymer_monomers_amount_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Monomer Amount High": { - "lucenePath": "D_root_polymer_monomers_amount_high", - "description": "The highest numerical value likely for the average amount. (highest average).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Monomer Amount High Limit": { - "lucenePath": "D_root_polymer_monomers_amount_highLimit", - "description": "The highest allowable numeric value usable for the amount. (highest limit).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Monomer Amount Last Edited": { - "lucenePath": "root_polymer_monomers_amount_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Monomer Amount Last Edited By": { - "lucenePath": "root_polymer_monomers_amount_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Monomer Amount Low": { - "lucenePath": "D_root_polymer_monomers_amount_low", - "description": "The lowest numerical value likely for the average amount. (lowest average).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Monomer Amount Low Limit": { - "lucenePath": "D_root_polymer_monomers_amount_lowLimit", - "description": "The lowest allowable numeric value usable for the amount. (lowest limit).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Monomer Amount Non-Numeric Value": { - "lucenePath": "root_polymer_monomers_amount_nonNumericValue", - "description": "A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Monomer Amount Type": { - "lucenePath": "root_polymer_monomers_amount_type", - "description": "The type of the amount specified ('mol ratio'. 'degree of polymerization', 'weight ratio', etc.).", - "type": "string", - "cvDomain": "AMOUNT_TYPE", - "priority": null - }, - "Monomer Amount UUID": { - "lucenePath": "root_polymer_monomers_amount_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Monomer Amount Units": { - "lucenePath": "root_polymer_monomers_amount_units", - "description": "The amount's unit of measurement.", - "type": "string", - "cvDomain": "AMOUNT_UNIT", - "priority": null - }, - "Monomer Approval ID": { - "lucenePath": "root_polymer_monomers_monomerSubstance_approvalID", - "description": "The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Monomer Created": { - "lucenePath": "root_polymer_monomers_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Monomer Created By": { - "lucenePath": "root_polymer_monomers_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Monomer Defining": { - "lucenePath": "root_polymer_monomers_defining", - "description": "A flag for whether or not the monomer \/ starting material is considered canonically defining for the polymer.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Monomer Deprecated": { - "lucenePath": "root_polymer_monomers_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Monomer Last Edited": { - "lucenePath": "root_polymer_monomers_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Monomer Last Edited By": { - "lucenePath": "root_polymer_monomers_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Monomer Substance Access": { - "lucenePath": "root_polymer_monomers_monomerSubstance_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Monomer Substance Created": { - "lucenePath": "root_polymer_monomers_monomerSubstance_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Monomer Substance Created By": { - "lucenePath": "root_polymer_monomers_monomerSubstance_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Monomer Substance Deprecated": { - "lucenePath": "root_polymer_monomers_monomerSubstance_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Monomer Substance Last Edited": { - "lucenePath": "root_polymer_monomers_monomerSubstance_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Monomer Substance Last Edited By": { - "lucenePath": "root_polymer_monomers_monomerSubstance_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Monomer Substance Name": { - "lucenePath": "root_polymer_monomers_monomerSubstance_refPname", - "description": "The \"Priority Name\" (usually the display name) of the record being referenced.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Monomer Substance Refuuid": { - "lucenePath": "root_polymer_monomers_monomerSubstance_refuuid", - "description": "The UUID of the related record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Monomer Substance Substance Class": { - "lucenePath": "root_polymer_monomers_monomerSubstance_substanceClass", - "description": "This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Monomer Substance UUID": { - "lucenePath": "root_polymer_monomers_monomerSubstance_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Monomer Type": { - "lucenePath": "root_polymer_monomers_type", - "description": "The chemical's type or role in polymerization ('initiator', 'monomer', 'starting material', etc.).", - "type": "string", - "cvDomain": "MONOMER_TYPE", - "priority": null - }, - "Monomer UUID": { - "lucenePath": "root_polymer_monomers_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Name Access": { - "lucenePath": "root_names_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Name Created": { - "lucenePath": "root_names_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Name Created By": { - "lucenePath": "root_names_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Name Deprecated": { - "lucenePath": "root_names_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Name Display Name": { - "lucenePath": "root_names_displayName", - "description": "True if this is to be the displayed name for the substance.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Name Domains": { - "lucenePath": "root_names_domains_term", - "description": "The list of domains that this name is used in (e.g. \"drug\", \"cosmetic\", etc).", - "type": "string", - "cvDomain": "NAME_DOMAIN", - "priority": null - }, - "Name Languages": { - "lucenePath": "root_names_languages_term", - "description": "The list of languages that use that name as written (ISO 639-1 codes).", - "type": "string", - "cvDomain": "LANGUAGE", - "priority": null - }, - "Name Last Edited": { - "lucenePath": "root_names_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Name Last Edited By": { - "lucenePath": "root_names_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Name Name Jurisdiction": { - "lucenePath": "root_names_nameJurisdiction_term", - "description": "The list of jurisdictions where that name is used.", - "type": "string", - "cvDomain": "JURISDICTION", - "priority": null - }, - "Name Preferred": { - "lucenePath": "root_names_preferred", - "description": "True if this is a \"preferred name\" for the substance (each substance may have many).", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Name Type Code": { - "lucenePath": "root_names_type", - "description": "The type of the name (e.g. \"bn\" for \"brand name\", \"cn\" for \"common name\", and \"sys\" for \"systematic name\", etc).", - "type": "string", - "cvDomain": "NAME_TYPE", - "priority": "x" - }, - "Name UUID": { - "lucenePath": "root_names_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Naming Organization": { - "lucenePath": "root_names_nameOrgs_nameOrg", - "description": "A string representing the naming organization which has designated the name as official in some regard. (e.g. \"USAN\", \"INN\").", - "type": "string", - "cvDomain": "NAME_ORG", - "priority": "x" - }, - "Naming Organization Access": { - "lucenePath": "root_names_nameOrgs_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Naming Organization Created": { - "lucenePath": "root_names_nameOrgs_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Naming Organization Created By": { - "lucenePath": "root_names_nameOrgs_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Naming Organization Deprecated": { - "lucenePath": "root_names_nameOrgs_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Naming Organization Deprecated Date": { - "lucenePath": "root_names_nameOrgs_deprecatedDate", - "description": "A UNIX timestamp for when the naming organization deprecated the name's status (if it has been deprecated).", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Naming Organization Last Edited": { - "lucenePath": "root_names_nameOrgs_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Naming Organization Last Edited By": { - "lucenePath": "root_names_nameOrgs_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Naming Organization UUID": { - "lucenePath": "root_names_nameOrgs_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Note": { - "lucenePath": "root_notes_note", - "description": "The literal text of a note comment for a record. These is typically used to capture some orienting descriptive information, qualifications, or systematic notes on validation.", - "type": "string", - "cvDomain": null, - "priority": "x" - }, - "Note Access": { - "lucenePath": "root_notes_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Note Created": { - "lucenePath": "root_notes_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Note Created By": { - "lucenePath": "root_notes_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Note Deprecated": { - "lucenePath": "root_notes_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Note Last Edited": { - "lucenePath": "root_notes_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Note Last Edited By": { - "lucenePath": "root_notes_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Nucleic Acid Access": { - "lucenePath": "root_nucleicAcid_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Nucleic Acid Created": { - "lucenePath": "root_nucleicAcid_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Nucleic Acid Created By": { - "lucenePath": "root_nucleicAcid_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Nucleic Acid Deprecated": { - "lucenePath": "root_nucleicAcid_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Nucleic Acid Last Edited": { - "lucenePath": "root_nucleicAcid_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Nucleic Acid Last Edited By": { - "lucenePath": "root_nucleicAcid_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Nucleic Acid Sequence Origin": { - "lucenePath": "root_nucleicAcid_sequenceOrigin", - "description": "The original source of the amino acid sequence. This is a general category which, for organisms, typically specifies a useful general abstract taxonic level with increasing granularity as it approaches humans (e.g. \"bacteria\",\"mouse\", \"mouse chimeric\", \"human\").", - "type": "string", - "cvDomain": "NUCLEIC_ACID_SEQUENCE_ORIGIN", - "priority": null - }, - "Nucleic Acid Sequence Type": { - "lucenePath": "root_nucleicAcid_sequenceType", - "description": "The type of sequence being specified (e.g. \"incomplete\", \"complete\").", - "type": "string", - "cvDomain": "SEQUENCE_TYPE", - "priority": null - }, - "Nucleic Acid Sub Type": { - "lucenePath": "root_nucleicAcid_nucleicAcidSubType", - "description": "The sub-type of nucleic acid.", - "type": "string", - "cvDomain": "NUCLEIC_ACID_SUBTYPE", - "priority": null - }, - "Nucleic Acid Subunit Access": { - "lucenePath": "root_nucleicAcid_subunits_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Nucleic Acid Subunit Created": { - "lucenePath": "root_nucleicAcid_subunits_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Nucleic Acid Subunit Created By": { - "lucenePath": "root_nucleicAcid_subunits_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Nucleic Acid Subunit Deprecated": { - "lucenePath": "root_nucleicAcid_subunits_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Nucleic Acid Subunit Last Edited": { - "lucenePath": "root_nucleicAcid_subunits_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Nucleic Acid Subunit Last Edited By": { - "lucenePath": "root_nucleicAcid_subunits_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Nucleic Acid Subunit Sequence": { - "lucenePath": "root_nucleicAcid_subunits_sequence", - "description": "The amino acid sequence as a string of 1-letter amino acids, from N-term to C-term. Lower-case letters represent D-amino acids, while upper-case are the standard L-amino acids.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Nucleic Acid Subunit Subunit Index": { - "lucenePath": "root_nucleicAcid_subunits_subunitIndex", - "description": "The index of the subunit being specified.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Nucleic Acid Subunit UUID": { - "lucenePath": "root_nucleicAcid_subunits_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Nucleic Acid Type": { - "lucenePath": "root_nucleicAcid_nucleicAcidType", - "description": "The type of nucleic acid.", - "type": "string", - "cvDomain": "NUCLEIC_ACID_TYPE", - "priority": null - }, - "Nucleic Acid UUID": { - "lucenePath": "root_nucleicAcid_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Organism Author": { - "lucenePath": "root_structurallyDiverse_organismAuthor", - "description": "The author of the organism's scientific name.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Organism Family": { - "lucenePath": "root_structurallyDiverse_organismFamily", - "description": "The organism's taxonomic family.", - "type": "string", - "cvDomain": null, - "priority": "x" - }, - "Organism Genus": { - "lucenePath": "root_structurallyDiverse_organismGenus", - "description": "The organism's taxonomic genus.", - "type": "string", - "cvDomain": null, - "priority": "x" - }, - "Organism Species": { - "lucenePath": "root_structurallyDiverse_organismSpecies", - "description": "The organism's species scientific name excluding the genus, known as the specific epithet.", - "type": "string", - "cvDomain": null, - "priority": "x" - }, - "Other Link Access": { - "lucenePath": "root_protein_otherLinks_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Other Link Created": { - "lucenePath": "root_protein_otherLinks_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Other Link Created By": { - "lucenePath": "root_protein_otherLinks_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Other Link Deprecated": { - "lucenePath": "root_protein_otherLinks_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Other Link Last Edited": { - "lucenePath": "root_protein_otherLinks_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Other Link Last Edited By": { - "lucenePath": "root_protein_otherLinks_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Other Link Linkagetype": { - "lucenePath": "root_protein_otherLinks_linkageType", - "description": "The type of linkage which connects the residues in a protein.", - "type": "string", - "cvDomain": "OTHER_LINKAGE_TYPE", - "priority": null - }, - "Other Link UUID": { - "lucenePath": "root_protein_otherLinks_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Parameter Access": { - "lucenePath": "root_properties_parameters_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Parameter Created": { - "lucenePath": "root_properties_parameters_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Parameter Created By": { - "lucenePath": "root_properties_parameters_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Parameter Deprecated": { - "lucenePath": "root_properties_parameters_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Parameter Last Edited": { - "lucenePath": "root_properties_parameters_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Parameter Last Edited By": { - "lucenePath": "root_properties_parameters_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Parameter Type": { - "lucenePath": "root_properties_parameters_type", - "description": "The type of parameter specified, (e.g. 'CHEMICAL', ''PHYSICAL', 'ENZYMATIC', or other).", - "type": "string", - "cvDomain": null, - "priority": "x" - }, - "Parameter UUID": { - "lucenePath": "root_properties_parameters_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Parameter Value Access": { - "lucenePath": "root_properties_parameters_value_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Parameter Value Average": { - "lucenePath": "D_root_properties_parameters_value_average", - "description": "The amount's numeric average if present.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Parameter Value Created": { - "lucenePath": "root_properties_parameters_value_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Parameter Value Created By": { - "lucenePath": "root_properties_parameters_value_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Parameter Value Deprecated": { - "lucenePath": "root_properties_parameters_value_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Parameter Value High": { - "lucenePath": "D_root_properties_parameters_value_high", - "description": "The highest numerical value likely for the average amount. (highest average).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Parameter Value Last Edited": { - "lucenePath": "root_properties_parameters_value_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Parameter Value Last Edited By": { - "lucenePath": "root_properties_parameters_value_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Parameter Value Low": { - "lucenePath": "D_root_properties_parameters_value_low", - "description": "The lowest numerical value likely for the average amount. (lowest average).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Parameter Value Non-Numeric Value": { - "lucenePath": "root_properties_parameters_value_nonNumericValue", - "description": "A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Parameter Value Type": { - "lucenePath": "root_properties_parameters_value_type", - "description": "The type of the property parameter (e.g. 'CHEMICAL', ''PHYSICAL', 'ENZYMATIC', or other).", - "type": "string", - "cvDomain": "AMOUNT_TYPE", - "priority": null - }, - "Parameter Value UUID": { - "lucenePath": "root_properties_parameters_value_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Parameter Value Units": { - "lucenePath": "root_properties_parameters_value_units", - "description": "The amount's unit of measurement.", - "type": "string", - "cvDomain": "AMOUNT_UNIT", - "priority": null - }, - "Physical Modification Access": { - "lucenePath": "root_modifications_physicalModifications_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Physical Modification Amount Access": { - "lucenePath": "root_modifications_physicalModifications_parameters_amount_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Physical Modification Amount Average": { - "lucenePath": "D_root_modifications_physicalModifications_parameters_amount_average", - "description": "The amount's numeric average if present.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Physical Modification Amount Created": { - "lucenePath": "root_modifications_physicalModifications_parameters_amount_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Physical Modification Amount Created By": { - "lucenePath": "root_modifications_physicalModifications_parameters_amount_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Physical Modification Amount Deprecated": { - "lucenePath": "root_modifications_physicalModifications_parameters_amount_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Physical Modification Amount High Limit": { - "lucenePath": "D_root_modifications_physicalModifications_parameters_amount_highLimit", - "description": "The highest allowable numeric value usable for the amount. (highest limit).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Physical Modification Amount Last Edited": { - "lucenePath": "root_modifications_physicalModifications_parameters_amount_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Physical Modification Amount Last Edited By": { - "lucenePath": "root_modifications_physicalModifications_parameters_amount_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Physical Modification Amount Low Limit": { - "lucenePath": "D_root_modifications_physicalModifications_parameters_amount_lowLimit", - "description": "The lowest allowable numeric value usable for the amount. (lowest limit).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Physical Modification Amount Non-Numeric Value": { - "lucenePath": "root_modifications_physicalModifications_parameters_amount_nonNumericValue", - "description": "A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Physical Modification Amount Type": { - "lucenePath": "root_modifications_physicalModifications_parameters_amount_type", - "description": "The type of the amount specified ('mol ratio'. 'degree of polymerization', 'weight ratio', 'other' etc.).", - "type": "string", - "cvDomain": "AMOUNT_TYPE", - "priority": null - }, - "Physical Modification Amount UUID": { - "lucenePath": "root_modifications_physicalModifications_parameters_amount_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Physical Modification Amount Units": { - "lucenePath": "root_modifications_physicalModifications_parameters_amount_units", - "description": "The amount's unit of measurement.", - "type": "string", - "cvDomain": "AMOUNT_UNIT", - "priority": null - }, - "Physical Modification Created": { - "lucenePath": "root_modifications_physicalModifications_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Physical Modification Created By": { - "lucenePath": "root_modifications_physicalModifications_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Physical Modification Deprecated": { - "lucenePath": "root_modifications_physicalModifications_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Physical Modification Last Edited": { - "lucenePath": "root_modifications_physicalModifications_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Physical Modification Last Edited By": { - "lucenePath": "root_modifications_physicalModifications_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Physical Modification Modification Group": { - "lucenePath": "root_modifications_physicalModifications_modificationGroup", - "description": "A key specifying how this modification relates to other modifications. All modifications sharing a group key are considered to be occuring as part of a single process or event, or descriptive of the same change to the substance.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Physical Modification Parameter Access": { - "lucenePath": "root_modifications_physicalModifications_parameters_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Physical Modification Parameter Created": { - "lucenePath": "root_modifications_physicalModifications_parameters_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Physical Modification Parameter Created By": { - "lucenePath": "root_modifications_physicalModifications_parameters_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Physical Modification Parameter Deprecated": { - "lucenePath": "root_modifications_physicalModifications_parameters_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Physical Modification Parameter Last Edited": { - "lucenePath": "root_modifications_physicalModifications_parameters_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Physical Modification Parameter Last Edited By": { - "lucenePath": "root_modifications_physicalModifications_parameters_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Physical Modification Parameter Parameter Name": { - "lucenePath": "root_modifications_physicalModifications_parameters_parameterName", - "description": "The name of the paramater which qualifies \/ conditions the property's measurement or domain of applicability. .", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Physical Modification Parameter UUID": { - "lucenePath": "root_modifications_physicalModifications_parameters_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Physical Modification Physical Modification Role": { - "lucenePath": "root_modifications_physicalModifications_physicalModificationRole", - "description": "The role that the physical modification has in the process of the modification.", - "type": "string", - "cvDomain": "PHYSICAL_MODIFICATION_ROLE", - "priority": null - }, - "Physical Modification UUID": { - "lucenePath": "root_modifications_physicalModifications_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Polymer Access": { - "lucenePath": "root_polymer_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Polymer Class": { - "lucenePath": "root_polymer_classification_polymerClass", - "description": "A general classication of the polymer type (e.g. \"homopolymer\", \"copolymer\").", - "type": "string", - "cvDomain": "POLYMER_CLASS", - "priority": "x" - }, - "Polymer Classification Access": { - "lucenePath": "root_polymer_classification_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Polymer Classification Created": { - "lucenePath": "root_polymer_classification_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Polymer Classification Created By": { - "lucenePath": "root_polymer_classification_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Polymer Classification Deprecated": { - "lucenePath": "root_polymer_classification_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Polymer Classification Last Edited": { - "lucenePath": "root_polymer_classification_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Polymer Classification Last Edited By": { - "lucenePath": "root_polymer_classification_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Polymer Classification Polymer Subclass": { - "lucenePath": "root_polymer_classification_polymerSubclass_term", - "description": "The set of subclasses describing the polymer (e.g. \"cross-link\", \"graft\").", - "type": "string", - "cvDomain": "POLYMER_SUBCLASS", - "priority": null - }, - "Polymer Classification Source Type": { - "lucenePath": "root_polymer_classification_sourceType", - "description": "The type of material used as the source for the polymer. If from a biological or natural source, further reference to the source material will be needed.", - "type": "string", - "cvDomain": "POLYMER_SOURCE_TYPE", - "priority": null - }, - "Polymer Classification UUID": { - "lucenePath": "root_notes_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Polymer Created": { - "lucenePath": "root_polymer_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Polymer Created By": { - "lucenePath": "root_polymer_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Polymer Deprecated": { - "lucenePath": "root_polymer_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Polymer Geometry": { - "lucenePath": "root_polymer_classification_polymerGeometry", - "description": "A general classification of the polymer geometry (e.g. \"linear\", \"branch\", \"network\").", - "type": "string", - "cvDomain": "POLYMER_GEOMETRY", - "priority": null - }, - "Polymer Last Edited": { - "lucenePath": "root_polymer_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Polymer Last Edited By": { - "lucenePath": "root_polymer_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Polymer Parent Substance Access": { - "lucenePath": "root_polymer_classification_parentSubstance_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Polymer Parent Substance Approval ID": { - "lucenePath": "root_polymer_classification_parentSubstance_approvalID", - "description": "The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Polymer Parent Substance Created": { - "lucenePath": "root_polymer_classification_parentSubstance_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Polymer Parent Substance Created By": { - "lucenePath": "root_polymer_classification_parentSubstance_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Polymer Parent Substance Deprecated": { - "lucenePath": "root_polymer_classification_parentSubstance_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Polymer Parent Substance Last Edited": { - "lucenePath": "root_polymer_classification_parentSubstance_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Polymer Parent Substance Last Edited By": { - "lucenePath": "root_polymer_classification_parentSubstance_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Polymer Parent Substance Preferred Name": { - "lucenePath": "root_polymer_classification_parentSubstance_refPname", - "description": "The \"Priority Name\" (usually the display name) of the record being referenced.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Polymer Parent Substance Refuuid": { - "lucenePath": "root_polymer_classification_parentSubstance_refuuid", - "description": "The UUID of the related record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Polymer Parent Substance Substance Class": { - "lucenePath": "root_polymer_classification_parentSubstance_substanceClass", - "description": "This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Polymer Parent Substance UUID": { - "lucenePath": "root_polymer_classification_parentSubstance_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Polymer UUID": { - "lucenePath": "root_polymer_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Property Access": { - "lucenePath": "root_properties_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Property Created": { - "lucenePath": "root_properties_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Property Created By": { - "lucenePath": "root_properties_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Property Defining": { - "lucenePath": "root_properties_defining", - "description": "A flag for whether or not the property is considered canonically defining for the substance definition.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Property Deprecated": { - "lucenePath": "root_properties_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Property Last Edited": { - "lucenePath": "root_properties_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Property Last Edited By": { - "lucenePath": "root_properties_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Property Name": { - "lucenePath": "root_properties_name", - "description": "The literal string text of a name.", - "type": "string", - "cvDomain": "PROPERTY_NAME", - "priority": "x" - }, - "Property Property Type": { - "lucenePath": "root_properties_propertyType", - "description": "The type of property: chemical, enzymatic, physical, or other.", - "type": "string", - "cvDomain": "PROPERTY_TYPE", - "priority": null - }, - "Property Type": { - "lucenePath": "root_properties_type", - "description": "The type of the name (e.g. \"bn\" for \"brand name\", \"cn\" for \"common name\", and \"sys\" for \"systematic name\", etc).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Property UUID": { - "lucenePath": "root_properties_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Protein Access": { - "lucenePath": "root_protein_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Protein Created": { - "lucenePath": "root_protein_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Protein Created By": { - "lucenePath": "root_protein_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Protein Deprecated": { - "lucenePath": "root_protein_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Protein Glycosylation Access": { - "lucenePath": "root_protein_glycosylation_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Protein Glycosylation Created": { - "lucenePath": "root_protein_glycosylation_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Protein Glycosylation Created By": { - "lucenePath": "root_protein_glycosylation_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Protein Glycosylation Deprecated": { - "lucenePath": "root_protein_glycosylation_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Protein Glycosylation Last Edited": { - "lucenePath": "root_protein_glycosylation_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Protein Glycosylation Last Edited By": { - "lucenePath": "root_protein_glycosylation_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Protein Glycosylation Type": { - "lucenePath": "root_protein_glycosylation_glycosylationType", - "description": "The type of glycosylation pattern found on the protein. Here, this is a very general term meant to capture the organism-level pattern that the glycosylation roughly corresponds to. (e.g. \"mouse\", \"human\", \"porcine\").", - "type": "string", - "cvDomain": "GLYCOSYLATION_TYPE", - "priority": "x" - }, - "Protein Glycosylation UUID": { - "lucenePath": "root_protein_glycosylation_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Protein Last Edited": { - "lucenePath": "root_protein_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Protein Last Edited By": { - "lucenePath": "root_protein_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Protein Protein Subtype": { - "lucenePath": "root_protein_proteinSubType", - "description": "A comma-separated set of subtypes descriptive of the protein.", - "type": "string", - "cvDomain": "PROTEIN_SUBTYPE", - "priority": "x" - }, - "Protein Sequence": { - "lucenePath": "root_protein_subunits_sequence", - "description": "The amino acid sequence as a string of 1-letter amino acids, from N-term to C-term. Lower-case letters represent D-amino acids, while upper-case are the standard L-amino acids.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Protein Sequence Origin": { - "lucenePath": "root_protein_sequenceOrigin", - "description": "The original source of the amino acid sequence. This is a general category which, for organisms, typically specifies a useful general abstract taxonic level with increasing granularity as it approaches humans (e.g. \"bacteria\",\"mouse\", \"mouse chimeric\", \"human\").", - "type": "string", - "cvDomain": "SEQUENCE_ORIGIN", - "priority": null - }, - "Protein Sequence Type": { - "lucenePath": "root_protein_sequenceType", - "description": "The type of sequence being specified (e.g. \"incomplete\", \"complete\").", - "type": "string", - "cvDomain": "SEQUENCE_TYPE", - "priority": null - }, - "Protein Subunit Access": { - "lucenePath": "root_protein_subunits_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Protein Subunit Created": { - "lucenePath": "root_protein_subunits_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Protein Subunit Created By": { - "lucenePath": "root_protein_subunits_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Protein Subunit Deprecated": { - "lucenePath": "root_protein_subunits_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Protein Subunit Last Edited": { - "lucenePath": "root_protein_subunits_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Protein Subunit Last Edited By": { - "lucenePath": "root_protein_subunits_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Protein Subunit Subunit Index": { - "lucenePath": "root_protein_subunits_subunitIndex", - "description": "The index of the subunit being specified.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Protein Subunit UUID": { - "lucenePath": "root_protein_subunits_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Protein UUID": { - "lucenePath": "root_protein_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Protein type": { - "lucenePath": "root_protein_proteinType", - "description": "A general typing of the protein.", - "type": "string", - "cvDomain": "PROTEIN_TYPE", - "priority": "x" - }, - "Record Access": { - "lucenePath": "root_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Record Approved By": { - "lucenePath": "root_approvedBy", - "description": "A string representing a user or entity which approved the assignment of UNII, or approval ID.", - "type": "string", - "cvDomain": null, - "priority": "x" - }, - "Record Change Reason": { - "lucenePath": "root_changeReason", - "description": "A description of the reason for the last change to the record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Record Created": { - "lucenePath": "root_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": "x" - }, - "Record Created By": { - "lucenePath": "root_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": "x" - }, - "Record Definition Level": { - "lucenePath": "root_definitionLevel", - "description": "The level of completeness and specificity of the definition (e.g. \"complete\", \"incomplete\", \"representative\").", - "type": "string", - "cvDomain": null, - "priority": "x" - }, - "Record Definition Type": { - "lucenePath": "root_definitionType", - "description": "The type of definition (\"primary\" or \"alternative\"). Primary definitions are the main descriptive form of the substance definition, while \"alternative\" definitions may be seen as definitional \"synonyms\" of some primary record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Record Deprecated": { - "lucenePath": "root_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": "x" - }, - "Record Last Edited": { - "lucenePath": "root_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": "x" - }, - "Record Last Edited By": { - "lucenePath": "root_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": "x" - }, - "Record Status": { - "lucenePath": "root_status", - "description": "The current record status (e.g. \"pending\", \"approved\"). This is not a regulatory status, but simply the status of record validation and curation. .", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Reference Access": { - "lucenePath": "root_references_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Reference Created": { - "lucenePath": "root_references_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Reference Created By": { - "lucenePath": "root_references_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Reference Deprecated": { - "lucenePath": "root_references_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Reference Document Date": { - "lucenePath": "root_references_documentDate", - "description": "The date that the document or reference was obtained \/ accessed.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Reference ID": { - "lucenePath": "root_references_id", - "description": "The identifier of the structural element itself (this is still a UUID).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Reference Last Edited": { - "lucenePath": "root_references_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Reference Last Edited By": { - "lucenePath": "root_references_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Reference Public Domain": { - "lucenePath": "root_references_publicDomain", - "description": "A marking of whether the reference itself is known to be publicly accessible.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Reference Tags": { - "lucenePath": "root_references_tags_term", - "description": "A collection of strings which mark the reference as having specific properties (nomenclature, spectra, definition, etc).", - "type": "string", - "cvDomain": "DOCUMENT_COLLECTION", - "priority": null - }, - "Reference Text \/ Citation": { - "lucenePath": "root_references_citation", - "description": "The reference text or citation.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Reference Type": { - "lucenePath": "root_references_docType", - "description": "The type of reference or document represented (e.g. \"INN\", \"Journal Article\", etc).", - "type": "string", - "cvDomain": "DOCUMENT_TYPE", - "priority": "x" - }, - "Reference URL": { - "lucenePath": "root_references_url", - "description": "The url to further information regarding this reference.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Reference UUID": { - "lucenePath": "root_references_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Reference Uploaded File": { - "lucenePath": "root_references_uploadedFile", - "description": "the file path of the associated files.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Related Substance Access": { - "lucenePath": "root_relationships_relatedSubstance_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Related Substance Approval ID": { - "lucenePath": "root_relationships_relatedSubstance_approvalID", - "description": "The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Related Substance Created": { - "lucenePath": "root_relationships_relatedSubstance_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Related Substance Created By": { - "lucenePath": "root_relationships_relatedSubstance_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Related Substance Deprecated": { - "lucenePath": "root_relationships_relatedSubstance_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Related Substance Last Edited": { - "lucenePath": "root_relationships_relatedSubstance_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Related Substance Last Edited By": { - "lucenePath": "root_relationships_relatedSubstance_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Related Substance Name": { - "lucenePath": "root_relationships_relatedSubstance_refPname", - "description": "The \"Priority Name\" (usually the display name) of the record being referenced.", - "type": "string", - "cvDomain": null, - "priority": "x" - }, - "Related Substance Refuuid": { - "lucenePath": "root_relationships_relatedSubstance_refuuid", - "description": "The UUID of the related record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Related Substance Substance Class": { - "lucenePath": "root_relationships_relatedSubstance_substanceClass", - "description": "This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Related Substance UUID": { - "lucenePath": "root_relationships_relatedSubstance_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Relationship Access": { - "lucenePath": "root_relationships_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Relationship Amount Access": { - "lucenePath": "root_relationships_amount_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Relationship Amount Average": { - "lucenePath": "D_root_relationships_amount_average", - "description": "The amount's numeric average if present.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Relationship Amount Created": { - "lucenePath": "root_relationships_amount_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Relationship Amount Created By": { - "lucenePath": "root_relationships_amount_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Relationship Amount Deprecated": { - "lucenePath": "root_relationships_amount_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Relationship Amount High": { - "lucenePath": "D_root_relationships_amount_high", - "description": "The highest numerical value likely for the average amount. (highest average).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Relationship Amount High Limit": { - "lucenePath": "D_root_relationships_amount_highLimit", - "description": "The highest allowable numeric value usable for the amount. (highest limit).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Relationship Amount Last Edited": { - "lucenePath": "root_relationships_amount_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Relationship Amount Last Edited By": { - "lucenePath": "root_relationships_amount_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Relationship Amount Low": { - "lucenePath": "D_root_relationships_amount_low", - "description": "The lowest numerical value likely for the average amount. (lowest average).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Relationship Amount Low Limit": { - "lucenePath": "D_root_relationships_amount_lowLimit", - "description": "The lowest allowable numeric value usable for the amount. (lowest limit).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Relationship Amount Non-Numeric Value": { - "lucenePath": "root_relationships_amount_nonNumericValue", - "description": "A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Relationship Amount Type": { - "lucenePath": "root_relationships_amount_type", - "description": "The type of the amount specified ('mol ratio'. 'degree of polymerization', 'weight ratio', etc.).", - "type": "string", - "cvDomain": "AMOUNT_TYPE", - "priority": null - }, - "Relationship Amount UUID": { - "lucenePath": "root_relationships_amount_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Relationship Amount Units": { - "lucenePath": "root_relationships_amount_units", - "description": "The amount's unit of measurement.", - "type": "string", - "cvDomain": "AMOUNT_UNIT", - "priority": null - }, - "Relationship Comments": { - "lucenePath": "root_relationships_comments", - "description": "Any comments regarding the relationship.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Relationship Created": { - "lucenePath": "root_relationships_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Relationship Created By": { - "lucenePath": "root_relationships_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Relationship Deprecated": { - "lucenePath": "root_relationships_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Relationship Interaction Type": { - "lucenePath": "root_relationships_interactionType", - "description": "The type of interaction which occurs between the two specified substance records, if applicable.", - "type": "string", - "cvDomain": "INTERACTION_TYPE", - "priority": null - }, - "Relationship Last Edited": { - "lucenePath": "root_relationships_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": "x" - }, - "Relationship Last Edited By": { - "lucenePath": "root_relationships_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": "x" - }, - "Relationship Originator UUID": { - "lucenePath": "root_relationships_originatorUuid", - "description": "The UUID of the equivalent inverted relationship (if present) on the other record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Relationship Qualification": { - "lucenePath": "root_relationships_qualification", - "description": "Any qualifier needed to describe the relationship \/ interaction between the two records.", - "type": "string", - "cvDomain": "QUALIFICATION", - "priority": null - }, - "Relationship Type": { - "lucenePath": "root_relationships_type", - "description": "The type of relationship (e.g. 'parent -> salt solvate', 'active moiety', 'inhibitor -> 'target', 'toxin -> conjugate' etc.).", - "type": "string", - "cvDomain": "RELATIONSHIP_TYPE", - "priority": "X" - }, - "Relationship UUID": { - "lucenePath": "root_relationships_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "SRU Access": { - "lucenePath": "root_polymer_structuralUnits_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "SRU Amount Access": { - "lucenePath": "root_polymer_structuralUnits_amount_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "SRU Amount Average": { - "lucenePath": "D_root_polymer_structuralUnits_amount_average", - "description": "The amount's numeric average if present.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "SRU Amount Created": { - "lucenePath": "root_polymer_structuralUnits_amount_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "SRU Amount Created By": { - "lucenePath": "root_polymer_structuralUnits_amount_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "SRU Amount Deprecated": { - "lucenePath": "root_polymer_structuralUnits_amount_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "SRU Amount High": { - "lucenePath": "D_root_polymer_structuralUnits_amount_high", - "description": "The highest numerical value likely for the average amount. (highest average).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "SRU Amount High Limit": { - "lucenePath": "D_root_polymer_structuralUnits_amount_highLimit", - "description": "The highest allowable numeric value usable for the amount. (highest limit).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "SRU Amount Last Edited": { - "lucenePath": "root_polymer_structuralUnits_amount_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "SRU Amount Last Edited By": { - "lucenePath": "root_polymer_structuralUnits_amount_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "SRU Amount Low": { - "lucenePath": "D_root_polymer_structuralUnits_amount_low", - "description": "The lowest numerical value likely for the average amount. (lowest average).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "SRU Amount Low Limit": { - "lucenePath": "D_root_polymer_structuralUnits_amount_lowLimit", - "description": "The lowest allowable numeric value usable for the amount. (lowest limit).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "SRU Amount Non-Numeric Value": { - "lucenePath": "root_polymer_structuralUnits_amount_nonNumericValue", - "description": "A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "SRU Amount Type": { - "lucenePath": "root_polymer_structuralUnits_amount_type", - "description": "The type of the amount specified ('mol ratio'. 'degree of polymerization', 'weight ratio', etc.)", - "type": "string", - "cvDomain": "AMOUNT_TYPE", - "priority": null - }, - "SRU Amount UUID": { - "lucenePath": "root_polymer_structuralUnits_amount_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "SRU Amount Units": { - "lucenePath": "root_polymer_structuralUnits_amount_units", - "description": "The amount's unit of measurement.", - "type": "string", - "cvDomain": "AMOUNT_UNIT", - "priority": null - }, - "SRU Attachment Count": { - "lucenePath": "root_polymer_structuralUnits_attachmentCount", - "description": "A count of the attachment points allowed for the given structural unit", - "type": "number", - "cvDomain": null, - "priority": null - }, - "SRU Created": { - "lucenePath": "root_polymer_structuralUnits_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "SRU Created By": { - "lucenePath": "root_polymer_structuralUnits_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "SRU Deprecated": { - "lucenePath": "root_polymer_structuralUnits_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "SRU Label": { - "lucenePath": "root_polymer_structuralUnits_label", - "description": "A label for the structural unit, typically a sequential capital letter starting with \"A\" (\"A\",\"B\",\"C\", etc)", - "type": "string", - "cvDomain": null, - "priority": null - }, - "SRU Last Edited": { - "lucenePath": "root_polymer_structuralUnits_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "SRU Last Edited By": { - "lucenePath": "root_polymer_structuralUnits_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "SRU Structure": { - "lucenePath": "root_polymer_structuralUnits_structure", - "description": "The molfile-format structure of the structural unit", - "type": "string", - "cvDomain": null, - "priority": null - }, - "SRU Type": { - "lucenePath": "root_polymer_structuralUnits_type", - "description": "The type of structural unit specified, describing whether it is an end group, SRU, or fragment.", - "type": "string", - "cvDomain": "POLYMER_SRU_TYPE", - "priority": null - }, - "Source Material Class": { - "lucenePath": "root_structurallyDiverse_sourceMaterialClass", - "description": "The class of source material (e.g. \"organism\" or \"mineral\").", - "type": "string", - "cvDomain": "SOURCE_MATERIAL_CLASS", - "priority": null - }, - "Source Material Source Material State": { - "lucenePath": "root_structurallyDiverse_sourceMaterialState", - "description": "A textual catagorization of what state the material is in, if needed (e.g. \"live\", \"killed\").", - "type": "string", - "cvDomain": "SOURCE_MATERIAL_STATE", - "priority": "x" - }, - "Source Material Type": { - "lucenePath": "root_structurallyDiverse_sourceMaterialType", - "description": "The type of source material. This is a general category which, for organisms, typically specifies a useful general abstract taxonic level with increasing granularity as it approaches humans (e.g. \"plant\", \"fungus\", \"mammal\", \"primate\", \"human\").", - "type": "string", - "cvDomain": "SOURCE_MATERIAL_TYPE", - "priority": "x" - }, - "SpecifiedSubstance Access": { - "lucenePath": "root_specifiedSubstance_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "SpecifiedSubstance Created": { - "lucenePath": "root_specifiedSubstance_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "SpecifiedSubstance Created By": { - "lucenePath": "root_specifiedSubstance_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "SpecifiedSubstance Deprecated": { - "lucenePath": "root_specifiedSubstance_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "SpecifiedSubstance Last Edited": { - "lucenePath": "root_specifiedSubstance_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "SpecifiedSubstance Last Edited By": { - "lucenePath": "root_specifiedSubstance_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "SpecifiedSubstance UUID": { - "lucenePath": "root_specifiedSubstance_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (m) Access": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesMaternalOrganism_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "St. Div. Hybrid Parent (m) Approval ID": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesMaternalOrganism_approvalID", - "description": "The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (m) Created": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesMaternalOrganism_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (m) Created By": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesMaternalOrganism_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (m) Deprecated": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesMaternalOrganism_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (m) Last Edited": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesMaternalOrganism_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (m) Last Edited By": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesMaternalOrganism_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (m) Refuuid": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesMaternalOrganism_refuuid", - "description": "The UUID of the related record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (m) Substance Class": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesMaternalOrganism_substanceClass", - "description": "This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (m) Substance Name": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesMaternalOrganism_refPname", - "description": "The \"Priority Name\" (usually the display name) of the record being referenced.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (m) UUID": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesMaternalOrganism_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (p) Access": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesPaternalOrganism_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "St. Div. Hybrid Parent (p) Approval ID": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesPaternalOrganism_approvalID", - "description": "The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (p) Created": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesPaternalOrganism_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (p) Created By": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesPaternalOrganism_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (p) Deprecated": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesPaternalOrganism_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (p) Last Edited": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesPaternalOrganism_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (p) Last Edited By": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesPaternalOrganism_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (p) Refuuid": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesPaternalOrganism_refuuid", - "description": "The UUID of the related record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (p) Substance Class": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesPaternalOrganism_substanceClass", - "description": "This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (p) Substance Name": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesPaternalOrganism_refPname", - "description": "The \"Priority Name\" (usually the display name) of the record being referenced.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent (p) UUID": { - "lucenePath": "root_structurallyDiverse_hybridSpeciesPaternalOrganism_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent Access": { - "lucenePath": "root_structurallyDiverse_parentSubstance_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "St. Div. Hybrid Parent Created": { - "lucenePath": "root_structurallyDiverse_parentSubstance_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent Created By": { - "lucenePath": "root_structurallyDiverse_parentSubstance_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent Deprecated": { - "lucenePath": "root_structurallyDiverse_parentSubstance_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent Last Edited": { - "lucenePath": "root_structurallyDiverse_parentSubstance_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent Last Edited By": { - "lucenePath": "root_structurallyDiverse_parentSubstance_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent Refuuid": { - "lucenePath": "root_structurallyDiverse_parentSubstance_refuuid", - "description": "The UUID of the related record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent Substance Class": { - "lucenePath": "root_structurallyDiverse_parentSubstance_substanceClass", - "description": "This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Hybrid Parent UUID": { - "lucenePath": "root_structurallyDiverse_parentSubstance_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Parent Approval ID": { - "lucenePath": "root_structurallyDiverse_parentSubstance_approvalID", - "description": "The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "St. Div. Parent Substance Name": { - "lucenePath": "root_structurallyDiverse_parentSubstance_refPname", - "description": "The \"Priority Name\" (usually the display name) of the record being referenced.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Structural Modification Access": { - "lucenePath": "root_modifications_structuralModifications_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Structural Modification Created": { - "lucenePath": "root_modifications_structuralModifications_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Structural Modification Created By": { - "lucenePath": "root_modifications_structuralModifications_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Structural Modification Deprecated": { - "lucenePath": "root_modifications_structuralModifications_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Structural Modification Extent": { - "lucenePath": "root_modifications_structuralModifications_extent", - "description": "The type of extent for the structural modification. \"Complete\" if the extent is considered to have 100% occupancy \/ replacement, \"partial\" otherwise.", - "type": "string", - "cvDomain": "EXTENT_TYPE", - "priority": null - }, - "Structural Modification Extent Amount Access": { - "lucenePath": "root_modifications_structuralModifications_extentAmount_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Structural Modification Extent Amount Average": { - "lucenePath": "D_root_modifications_structuralModifications_extentAmount_average", - "description": "The amount's numeric average if present.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Structural Modification Extent Amount Created": { - "lucenePath": "root_modifications_structuralModifications_extentAmount_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Structural Modification Extent Amount Created By": { - "lucenePath": "root_modifications_structuralModifications_extentAmount_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Structural Modification Extent Amount Deprecated": { - "lucenePath": "root_modifications_structuralModifications_extentAmount_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Structural Modification Extent Amount High": { - "lucenePath": "D_root_modifications_structuralModifications_extentAmount_high", - "description": "The highest numerical value likely for the average amount. (highest average).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Structural Modification Extent Amount High Limit": { - "lucenePath": "D_root_modifications_structuralModifications_extentAmount_highLimit", - "description": "The highest allowable numeric value usable for the amount. (highest limit).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Structural Modification Extent Amount Last Edited": { - "lucenePath": "root_modifications_structuralModifications_extentAmount_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Structural Modification Extent Amount Last Edited By": { - "lucenePath": "root_modifications_structuralModifications_extentAmount_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Structural Modification Extent Amount Low": { - "lucenePath": "D_root_modifications_structuralModifications_extentAmount_low", - "description": "The lowest numerical value likely for the average amount. (lowest average).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Structural Modification Extent Amount Low Limit": { - "lucenePath": "D_root_modifications_structuralModifications_extentAmount_lowLimit", - "description": "The lowest allowable numeric value usable for the amount. (lowest limit).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Structural Modification Extent Amount Non-Numeric Value": { - "lucenePath": "root_modifications_structuralModifications_extentAmount_nonNumericValue", - "description": "A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Structural Modification Extent Amount Type": { - "lucenePath": "root_modifications_structuralModifications_extentAmount_type", - "description": "The type of amount recorded for a modification of partial extent.", - "type": "string", - "cvDomain": "AMOUNT_TYPE", - "priority": null - }, - "Structural Modification Extent Amount UUID": { - "lucenePath": "root_modifications_structuralModifications_extentAmount_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Structural Modification Extent Amount Units": { - "lucenePath": "root_modifications_structuralModifications_extentAmount_units", - "description": "The amount's unit of measurement.", - "type": "string", - "cvDomain": "AMOUNT_UNIT", - "priority": null - }, - "Structural Modification Last Edited": { - "lucenePath": "root_modifications_structuralModifications_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Structural Modification Last Edited By": { - "lucenePath": "root_modifications_structuralModifications_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Structural Modification Locationtype": { - "lucenePath": "root_modifications_structuralModifications_locationType", - "description": "The type of location specification for a residue modified in a structural modification. (e.g. \"site-specific\" or \"residue-specific\").", - "type": "string", - "cvDomain": "LOCATION_TYPE", - "priority": null - }, - "Structural Modification Modification Group": { - "lucenePath": "root_modifications_structuralModifications_modificationGroup", - "description": "A key specifying how this modification relates to other modifications. All modifications sharing a group key are considered to be occuring as part of a single process or event, or descriptive of the same change to the substance.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Structural Modification Residuemodified": { - "lucenePath": "root_modifications_structuralModifications_residueModified", - "description": "The residue which was modified for the structural modification (necessary if the modification is residue specific instead of site-specific).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Structural Modification Structural Modification Type": { - "lucenePath": "root_modifications_structuralModifications_structuralModificationType", - "description": "The type of structural modification.", - "type": "string", - "cvDomain": "STRUCTURAL_MODIFICATION_TYPE", - "priority": null - }, - "Structural Modification UUID": { - "lucenePath": "root_modifications_structuralModifications_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Structurally Diverse Access": { - "lucenePath": "root_structurallyDiverse_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": "x" - }, - "Structurally Diverse Created": { - "lucenePath": "root_structurallyDiverse_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Structurally Diverse Created By": { - "lucenePath": "root_structurallyDiverse_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": "x" - }, - "Structurally Diverse Deprecated": { - "lucenePath": "root_structurallyDiverse_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Structurally Diverse Developmental Stage": { - "lucenePath": "root_structurallyDiverse_developmentalStage", - "description": "The developmental stage of the organism, when necessary to describe the material. (e.g. \"adult\", \"larva\", etc).", - "type": "string", - "cvDomain": "DEVELOPMENTAL_STAGE", - "priority": null - }, - "Structurally Diverse Fraction Material Type": { - "lucenePath": "root_structurallyDiverse_fractionMaterialType", - "description": "A general material type of the fraction which is isolated in a structurally diverse substance (e.g. \"oils\", \"cells\", etc).", - "type": "string", - "cvDomain": "FRACTION_MATERIAL_TYPE", - "priority": "x" - }, - "Structurally Diverse Fraction Name": { - "lucenePath": "root_structurallyDiverse_fractionName", - "description": "A specific name for the fraction which is isolated in a structirally diverse substance (e.g. \"low molecular weight oils\").", - "type": "string", - "cvDomain": null, - "priority": "x" - }, - "Structurally Diverse Infraspecific Name": { - "lucenePath": "root_structurallyDiverse_infraSpecificName", - "description": "The part of the organisms name that is more specific than the species rank.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Structurally Diverse Infraspecific type": { - "lucenePath": "root_structurallyDiverse_infraSpecificType", - "description": "The type of rank of the infra specific name such as variety, subspecies, or forma. .", - "type": "string", - "cvDomain": "INFRA_SPECIFIC_TYPE", - "priority": null - }, - "Structurally Diverse Last Edited": { - "lucenePath": "root_structurallyDiverse_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Structurally Diverse Last Edited By": { - "lucenePath": "root_structurallyDiverse_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Structurally Diverse Part": { - "lucenePath": "root_structurallyDiverse_part_term", - "description": "The contiguous physical part(s) of the source material that are isolated in this substance (e.g. \"flower\", \"fruit\", \"stem\", \"bone\"). Full organisms receive the part of \"Whole\".", - "type": "string", - "cvDomain": "PART", - "priority": "x" - }, - "Structurally Diverse Partlocation": { - "lucenePath": "root_structurallyDiverse_partLocation", - "description": "The location of the part(s), if necessary to explain the context of the part.", - "type": "string", - "cvDomain": "PART_LOCATION", - "priority": null - }, - "Structurally Diverse UUID": { - "lucenePath": "root_structurallyDiverse_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Structure Access": { - "lucenePath": "root_structure_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Structure Charge": { - "lucenePath": "root_structure_charge", - "description": "The net charge of the structure.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Structure Count": { - "lucenePath": "root_structure_count", - "description": "The exact number of times that the structure is repeated.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Structure Created": { - "lucenePath": "root_structure_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Structure Created By": { - "lucenePath": "root_structure_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Structure Defined Stereo": { - "lucenePath": "root_structure_definedStereo", - "description": "A count of defined stereocenters.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Structure Deprecated": { - "lucenePath": "root_structure_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Structure Digest": { - "lucenePath": "root_structure_digest", - "description": "A hash of the raw structural information encoded.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Structure Ez Centers": { - "lucenePath": "root_structure_ezCenters", - "description": "A count of E\/Z, or absolute double bond stereochemistry centers.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Structure Has Atropisomerism": { - "lucenePath": "root_structure_atropisomerism", - "description": "Whether or not the record has atropisomerism, or has a chirality where a hindered rotation about a single bond as a resolt of steric or electronic constraints.", - "type": "string", - "cvDomain": "ATROPISOMERISM", - "priority": null - }, - "Structure Last Edited": { - "lucenePath": "root_structure_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Structure Last Edited By": { - "lucenePath": "root_structure_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Structure Stereochemistry": { - "lucenePath": "root_structure_stereoChemistry", - "description": "The structure's type of stereochemistry (absolute, achiral, racemic, etc.).", - "type": "string", - "cvDomain": "STEREOCHEMISTRY_TYPE", - "priority":"x" - } - , - "Structure Optical Activity": { - "lucenePath": "root_structure_opticalActivity", - "cvDomain": "OPTICAL_ACTIVITY", - "type": "string", - "priority": "x", - "description": "The optical activity or rotation of the material. '(+)' and '(-)' are used for optically active materials where activity is known and significantly useful for definition. '(+/-)' is used for racemic mixtures." - }, - "Sugar": { - "lucenePath": "root_nucleicAcid_sugars_sugar", - "description": "The sugar itself which is used. This is a short-hand code for a defined structural sugar fragment found within the GSRS controlled vocabulary.", - "type": "string", - "cvDomain": null, - "priority": "x" - }, - "Sugar Access": { - "lucenePath": "root_nucleicAcid_sugars_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Sugar Created": { - "lucenePath": "root_nucleicAcid_sugars_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Sugar Created By": { - "lucenePath": "root_nucleicAcid_sugars_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Sugar Deprecated": { - "lucenePath": "root_nucleicAcid_sugars_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Sugar Last Edited": { - "lucenePath": "root_nucleicAcid_sugars_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Sugar Last Edited By": { - "lucenePath": "root_nucleicAcid_sugars_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Sugar UUID": { - "lucenePath": "root_nucleicAcid_sugars_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Value Access": { - "lucenePath": "root_properties_value_access", - "description": "Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", - "type": "string", - "cvDomain": "ACCESS_GROUP", - "priority": null - }, - "Value Average": { - "lucenePath": "D_root_properties_value_average", - "description": "The amount's numeric average if present.", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Value Created": { - "lucenePath": "root_properties_value_created", - "description": "A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Value Created By": { - "lucenePath": "root_properties_value_createdBy", - "description": "A string representing a user or entity which originally created the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Value Deprecated": { - "lucenePath": "root_properties_value_deprecated", - "description": "A boolean marking for whether the element or record is considered deprecated.", - "type": "boolean", - "cvDomain": null, - "priority": null - }, - "Value High": { - "lucenePath": "D_root_properties_value_high", - "description": "The highest numerical value likely for the average amount. (highest average).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Value High Limit": { - "lucenePath": "D_root_properties_value_highLimit", - "description": "The highest allowable numeric value usable for the amount. (highest limit).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Value Last Edited": { - "lucenePath": "root_properties_value_lastEdited", - "description": "A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", - "type": "timestamp", - "cvDomain": null, - "priority": null - }, - "Value Last Edited By": { - "lucenePath": "root_properties_value_lastEditedBy", - "description": "A string representing a user or entity which last edited the record (or element).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Value Low": { - "lucenePath": "D_root_properties_value_low", - "description": "The lowest numerical value likely for the average amount. (lowest average).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Value Low Limit": { - "lucenePath": "D_root_properties_value_lowLimit", - "description": "The lowest allowable numeric value usable for the amount. (lowest limit).", - "type": "number", - "cvDomain": null, - "priority": null - }, - "Value Non-Numeric Value": { - "lucenePath": "root_properties_value_nonNumericValue", - "description": "A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Value Type": { - "lucenePath": "root_properties_value_type", - "description": "The type of property specified, (e.g. 'CHEMICAL', ''PHYSICAL', 'ENZYMATIC', or other).", - "type": "string", - "cvDomain": "AMOUNT_TYPE", - "priority": null - }, - "Value UUID": { - "lucenePath": "root_properties_value_uuid", - "description": "A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", - "type": "string", - "cvDomain": null, - "priority": null - }, - "Value Units": { - "lucenePath": "root_properties_value_units", - "description": "The amount's unit of measurement.", - "type": "string", - "cvDomain": "AMOUNT_UNIT", - "priority": null + "Agent Modification Access":{ + "lucenePath":"root_modifications_agentModifications_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Agent Modification Amount Access":{ + "lucenePath":"root_modifications_agentModifications_amount_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Agent Modification Amount Average":{ + "lucenePath":"D_root_modifications_agentModifications_amount_average", + "description":"The amount's numeric average if present.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Modification Amount Created By":{ + "lucenePath":"root_modifications_agentModifications_amount_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Modification Amount Created Date":{ + "lucenePath":"root_modifications_agentModifications_amount_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Modification Amount Deprecated":{ + "lucenePath":"root_modifications_agentModifications_amount_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Modification Amount High":{ + "lucenePath":"D_root_modifications_agentModifications_amount_high", + "description":"The highest numerical value likely for the average amount. (highest average).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Modification Amount High Limit":{ + "lucenePath":"D_root_modifications_agentModifications_amount_highLimit", + "description":"The highest allowable numeric value usable for the amount. (highest limit).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Modification Amount Last Edited By":{ + "lucenePath":"root_modifications_agentModifications_amount_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Modification Amount Last Edited Date":{ + "lucenePath":"root_modifications_agentModifications_amount_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Modification Amount Low":{ + "lucenePath":"D_root_modifications_agentModifications_amount_low", + "description":"The lowest numerical value likely for the average amount. (lowest average).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Modification Amount Low Limit":{ + "lucenePath":"D_root_modifications_agentModifications_amount_lowLimit", + "description":"The lowest allowable numeric value usable for the amount. (lowest limit).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Modification Amount Non-Numeric Value":{ + "lucenePath":"root_modifications_agentModifications_amount_nonNumericValue", + "description":"A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Modification Amount Type":{ + "lucenePath":"root_modifications_agentModifications_amount_type", + "description":"The type of the amount specified ('mol ratio'. 'degree of polymerization', 'weight ratio', etc.).", + "type":"string", + "cvDomain":"AMOUNT_TYPE", + "priority":null, + "suggest":null + }, + "Agent Modification Amount UUID":{ + "lucenePath":"root_modifications_agentModifications_amount_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Modification Amount Units":{ + "lucenePath":"root_modifications_agentModifications_amount_units", + "description":"The amount's unit of measurement.", + "type":"string", + "cvDomain":"AMOUNT_UNIT", + "priority":null, + "suggest":null + }, + "Agent Modification Created By":{ + "lucenePath":"root_modifications_agentModifications_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Modification Created Date":{ + "lucenePath":"root_modifications_agentModifications_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Modification Deprecated":{ + "lucenePath":"root_modifications_agentModifications_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Modification Last Edited By":{ + "lucenePath":"root_modifications_agentModifications_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Modification Last Edited Date":{ + "lucenePath":"root_modifications_agentModifications_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Modification Modification Group":{ + "lucenePath":"root_modifications_agentModifications_modificationGroup", + "description":"A key specifying how this modification relates to other modifications. All modifications sharing a group key are considered to be occuring as part of a single process or event, or descriptive of the same change to the substance.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Modification Process":{ + "lucenePath":"root_modifications_agentModifications_agentModificationProcess", + "description":"The process used for the agent modification.", + "type":"string", + "cvDomain":"AGENT_MODIFICATION_PROCESS", + "priority":"x", + "suggest":null + }, + "Agent Modification Role":{ + "lucenePath":"root_modifications_agentModifications_agentModificationRole", + "description":"The role of the agent in the modification process.", + "type":"string", + "cvDomain":"ROLE", + "priority":"x", + "suggest":null + }, + "Agent Modification Type":{ + "lucenePath":"root_modifications_agentModifications_agentModificationType", + "description":"The type of agent modification.", + "type":"string", + "cvDomain":"AGENT_MODIFICATION_TYPE", + "priority":"x", + "suggest":null + }, + "Agent Modification UUID":{ + "lucenePath":"root_modifications_agentModifications_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Substance Access":{ + "lucenePath":"root_modifications_agentModifications_agentSubstance_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Agent Substance Approval ID":{ + "lucenePath":"root_modifications_agentModifications_agentSubstance_approvalID", + "description":"The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Substance Created By":{ + "lucenePath":"root_modifications_agentModifications_agentSubstance_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Substance Created Date":{ + "lucenePath":"root_modifications_agentModifications_agentSubstance_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Substance Deprecated":{ + "lucenePath":"root_modifications_agentModifications_agentSubstance_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Substance Last Edited By":{ + "lucenePath":"root_modifications_agentModifications_agentSubstance_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Substance Last Edited Date":{ + "lucenePath":"root_modifications_agentModifications_agentSubstance_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Substance Preferred Name":{ + "lucenePath":"root_modifications_agentModifications_agentSubstance_refPname", + "description":"The \"Priority Name\" (usually the display name) of the record being referenced.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Agent Substance RefUUID":{ + "lucenePath":"root_modifications_agentModifications_agentSubstance_refuuid", + "description":"The UUID of the related record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Substance Substance Class":{ + "lucenePath":"root_modifications_agentModifications_agentSubstance_substanceClass", + "description":"This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Agent Substance UUID":{ + "lucenePath":"root_modifications_agentModifications_agentSubstance_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Any Name":{ + "lucenePath":"root_names_name", + "description":"The literal string text of a name.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Name" + }, + "Approval ID":{ + "lucenePath":"root_approvalID", + "description":"The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Approval_ID" + }, + "BDNUM":{ + "lucenePath":"root_codes_BDNUM", + "description":"The BDNUM code associated with the record. This field is not restricted to primary codes, but includes all code types which have the BDNUM codesystem.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"BDNUM" + }, + "CAS RN":{ + "lucenePath":"root_codes_CAS", + "description":"The CAS RN code associated with the record. This field is not restricted to primary codes, but includes all code types which have the CAS RN codesystem.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"CAS" + }, + "Chemical Structure Last Edited By":{ + "lucenePath":"root_structure_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Chemical Structure Last Edited Date":{ + "lucenePath":"root_structure_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Chemical Structure Molecular Weight":{ + "lucenePath":"root_structure_mwt", + "description":"The molecular weight of the structure.", + "type":"number", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Chemical Structure Optical Activity":{ + "lucenePath":"root_structure_opticalActivity", + "description":"The optical activity or rotation of the material. \"(+)\" and \"(-)\" are used for optically active materials where activity is known and significantly useful for definition. \"(+\/-)\" is used for racemic mixtures.", + "type":"string", + "cvDomain":"OPTICAL_ACTIVITY", + "priority":null, + "suggest":null + }, + "Chemical Structure SMILES":{ + "lucenePath":"root_structure_smiles", + "description":"The chemical structure in SMILES format.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Chemical Structure Stereo Centers":{ + "lucenePath":"root_structure_stereoCenters", + "description":"A count of possible stereocenters.", + "type":"number", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Chemical Structure Stereo Comments":{ + "lucenePath":"root_structure_stereoComments", + "description":"Some textually descriptive information about specific stereochemistry that cannot adequately be captured elsewhere.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Chemical Structure Stereochemistry":{ + "lucenePath":"root_structure_stereoChemistry", + "description":"The structure's type of stereochemistry (absolute, achiral, racemic, etc.).", + "type":"string", + "cvDomain":"STEREOCHEMISTRY_TYPE", + "priority":"x", + "suggest":null + }, + "Code Access":{ + "lucenePath":"root_codes_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Code Comments":{ + "lucenePath":"root_codes_comments", + "description":"Any comments regarding the code. Usage note: in current GSRS verion 3.0 and below, comments values shown in UI are actually stored as codeText.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Code Created By":{ + "lucenePath":"root_codes_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Code Created Date":{ + "lucenePath":"root_codes_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Code Deprecated":{ + "lucenePath":"root_codes_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Code Details Text":{ + "lucenePath":"root_codes_codeText", + "description":"Code Text, often used for showing hierarchy. Usage note: in current GSRS verion 3.0 and below, codeText values shown in UI are actually stored as comments.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Code Last Edited By":{ + "lucenePath":"root_codes_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Code Last Edited Date":{ + "lucenePath":"root_codes_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Code System":{ + "lucenePath":"root_codes_codeSystem", + "description":"The orginizational system which defines the code meaning (e.g. the originating database or classification system).", + "type":"string", + "cvDomain":"CODE_SYSTEM", + "priority":"x", + "suggest":null + }, + "Code Type":{ + "lucenePath":"root_codes_type", + "description":"The type of the code (e.g. \"PRIMARY\" for a primary code, \"SECONDARY\" for a secondary code, and \"SUPERSEDED\" for a code which has been superseded by another code, etc).", + "type":"string", + "cvDomain":"CODE_TYPE", + "priority":"x", + "suggest":null + }, + "Code URL":{ + "lucenePath":"root_codes_url", + "description":"The url to further information regarding this code.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Code UUID":{ + "lucenePath":"root_codes_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Code Value":{ + "lucenePath":"root_codes_code", + "description":"The literal code value.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Code" + }, + "Count Amount Access":{ + "lucenePath":"root_moieties_countAmount_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Count Amount Average":{ + "lucenePath":"D_root_moieties_countAmount_average", + "description":"The amount's numeric average if present.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Count Amount Created By":{ + "lucenePath":"root_moieties_countAmount_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Count Amount Created Date":{ + "lucenePath":"root_moieties_countAmount_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Count Amount Deprecated":{ + "lucenePath":"root_moieties_countAmount_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Count Amount High":{ + "lucenePath":"D_root_moieties_countAmount_high", + "description":"The highest numerical value likely for the average amount. (highest average).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Count Amount Last Edited By":{ + "lucenePath":"root_moieties_countAmount_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Count Amount Last Edited Date":{ + "lucenePath":"root_moieties_countAmount_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Count Amount Low":{ + "lucenePath":"D_root_moieties_countAmount_low", + "description":"The lowest numerical value likely for the average amount. (lowest average).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Count Amount Non-Numeric Value":{ + "lucenePath":"root_moieties_countAmount_nonNumericValue", + "description":"A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Count Amount Type":{ + "lucenePath":"root_moieties_countAmount_type", + "description":"The type of moiety count amount ('Mol ration', 'weight ratio', etc.)", + "type":"string", + "cvDomain":"AMOUNT_TYPE", + "priority":null, + "suggest":null + }, + "Count Amount UUID":{ + "lucenePath":"root_moieties_countAmount_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Count Amount Units":{ + "lucenePath":"root_moieties_countAmount_units", + "description":"The amount's unit of measurement.", + "type":"string", + "cvDomain":"AMOUNT_UNIT", + "priority":null, + "suggest":null + }, + "Display Name":{ + "lucenePath":"root_Display Name", + "description":"The Display Name (preferred term) of the substance record.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":"Display Name" + }, + "Display Structure UUID":{ + "lucenePath":"root_polymer_classification_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Access":{ + "lucenePath":"root_specifiedSubstance_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Amount Access":{ + "lucenePath":"root_specifiedSubstance_constituents_amount_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Amount Average":{ + "lucenePath":"D_root_specifiedSubstance_constituents_amount_average", + "description":"The amount's numeric average if present.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Amount Created By":{ + "lucenePath":"root_specifiedSubstance_constituents_amount_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Amount Created Date":{ + "lucenePath":"root_specifiedSubstance_constituents_amount_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Amount Deprecated":{ + "lucenePath":"root_specifiedSubstance_constituents_amount_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Amount High":{ + "lucenePath":"D_root_specifiedSubstance_constituents_amount_high", + "description":"The highest numerical value likely for the average amount. (highest average).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Amount High Limit":{ + "lucenePath":"D_root_specifiedSubstance_constituents_amount_highLimit", + "description":"The highest allowable numeric value usable for the amount. (highest limit).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Amount Last Edited By":{ + "lucenePath":"root_specifiedSubstance_constituents_amount_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Amount Last Edited Date":{ + "lucenePath":"root_specifiedSubstance_constituents_amount_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Amount Low":{ + "lucenePath":"D_root_specifiedSubstance_constituents_amount_low", + "description":"The lowest numerical value likely for the average amount. (lowest average).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Amount Low Limit":{ + "lucenePath":"D_root_specifiedSubstance_constituents_amount_lowLimit", + "description":"The lowest allowable numeric value usable for the amount. (lowest limit).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Amount Non-Numeric Value":{ + "lucenePath":"root_specifiedSubstance_constituents_amount_nonNumericValue", + "description":"A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Amount Type":{ + "lucenePath":"root_specifiedSubstance_constituents_amount_type", + "description":"The type of the amount specified ('mol ratio'. 'degree of polymerization', 'weight ratio', etc.).", + "type":"string", + "cvDomain":"AMOUNT_TYPE", + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Amount UUID":{ + "lucenePath":"root_specifiedSubstance_constituents_amount_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Amount Units":{ + "lucenePath":"root_specifiedSubstance_constituents_amount_units", + "description":"The amount's unit of measurement.", + "type":"string", + "cvDomain":"AMOUNT_UNIT", + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Role":{ + "lucenePath":"root_specifiedSubstance_constituents_role", + "description":"The role the constituent plays in the substance (marker substance, adjuvant, constituent, solvent, etc.).", + "type":"string", + "cvDomain":"CONSTITUENT_ROLE", + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Substance Access":{ + "lucenePath":"root_specifiedSubstance_constituents_substance_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Substance Approval ID":{ + "lucenePath":"root_specifiedSubstance_constituents_substance_approvalID", + "description":"The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Substance Created By":{ + "lucenePath":"root_specifiedSubstance_constituents_substance_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Substance Created Date":{ + "lucenePath":"root_specifiedSubstance_constituents_substance_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Substance Deprecated":{ + "lucenePath":"root_specifiedSubstance_constituents_substance_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Substance Last Edited By":{ + "lucenePath":"root_specifiedSubstance_constituents_substance_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Substance Last Edited Date":{ + "lucenePath":"root_specifiedSubstance_constituents_substance_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Substance RefUUID":{ + "lucenePath":"root_specifiedSubstance_constituents_substance_refuuid", + "description":"The UUID of the related record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Substance Substance Class":{ + "lucenePath":"root_specifiedSubstance_constituents_substance_substanceClass", + "description":"This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Constituent Substance Substance Name":{ + "lucenePath":"root_specifiedSubstance_constituents_substance_refPname", + "description":"The \"Priority Name\" (usually the display name) of the record being referenced.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Group 1 Specified Substance Constituent Substance UUID":{ + "lucenePath":"root_specifiedSubstance_constituents_substance_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Created By":{ + "lucenePath":"root_specifiedSubstance_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Created Date":{ + "lucenePath":"root_specifiedSubstance_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Deprecated":{ + "lucenePath":"root_specifiedSubstance_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Last Edited By":{ + "lucenePath":"root_specifiedSubstance_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance Last Edited Date":{ + "lucenePath":"root_specifiedSubstance_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Group 1 Specified Substance UUID":{ + "lucenePath":"root_specifiedSubstance_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Idealized Structure Access":{ + "lucenePath":"root_polymer_idealizedStructure_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Idealized Structure Charge":{ + "lucenePath":"root_polymer_idealizedStructure_charge", + "description":"The net charge of the structure.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Idealized Structure Count":{ + "lucenePath":"root_polymer_idealizedStructure_count", + "description":"The exact number of times that the structure is repeated.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Idealized Structure Created By":{ + "lucenePath":"root_polymer_idealizedStructure_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Idealized Structure Created Date":{ + "lucenePath":"root_polymer_idealizedStructure_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Idealized Structure Defined Stereo":{ + "lucenePath":"root_polymer_idealizedStructure_definedStereo", + "description":"A count of defined stereocenters.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Idealized Structure Deprecated":{ + "lucenePath":"root_polymer_idealizedStructure_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Idealized Structure Digest":{ + "lucenePath":"root_polymer_idealizedStructure_digest", + "description":"A hash of the raw structural information encoded.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Idealized Structure Ez Centers":{ + "lucenePath":"root_polymer_idealizedStructure_ezCenters", + "description":"A count of E\/Z, or absolute double bond stereochemistry centers.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Idealized Structure Formula":{ + "lucenePath":"root_polymer_idealizedStructure_formula", + "description":"The chemical formula of the record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Idealized Structure Last Edited By":{ + "lucenePath":"root_polymer_idealizedStructure_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Idealized Structure Last Edited Date":{ + "lucenePath":"root_polymer_idealizedStructure_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Idealized Structure Optical Activity":{ + "lucenePath":"root_polymer_idealizedStructure_opticalActivity", + "description":"The optical activity or rotation of the material. \"(+)\" and \"(-)\" are used for optically active materials where activity is known and significantly useful for definition. \"(+\/-)\" is used for racemic mixtures.", + "type":"string", + "cvDomain":"OPTICAL_ACTIVITY", + "priority":null, + "suggest":null + }, + "Idealized Structure Stereo Centers":{ + "lucenePath":"root_polymer_idealizedStructure_stereoCenters", + "description":"A count of possible stereocenters.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Idealized Structure Stereochemistry":{ + "lucenePath":"root_polymer_idealizedStructure_stereochemistry", + "description":"The structure's type of stereochemistry (absolute, achiral, racemic, etc.).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mediator Substance Access":{ + "lucenePath":"root_relationships_mediatorSubstance_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Mediator Substance Approval ID":{ + "lucenePath":"root_relationships_mediatorSubstance_approvalID", + "description":"The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mediator Substance Created By":{ + "lucenePath":"root_relationships_mediatorSubstance_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mediator Substance Created Date":{ + "lucenePath":"root_relationships_mediatorSubstance_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mediator Substance Deprecated":{ + "lucenePath":"root_relationships_mediatorSubstance_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mediator Substance Last Edited By":{ + "lucenePath":"root_relationships_mediatorSubstance_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mediator Substance Last Edited Date":{ + "lucenePath":"root_relationships_mediatorSubstance_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mediator Substance Preferred Name":{ + "lucenePath":"root_relationships_mediatorSubstance_refPname", + "description":"The \"Priority Name\" (usually the display name) of the record being referenced.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mediator Substance RefUUID":{ + "lucenePath":"root_relationships_mediatorSubstance_refuuid", + "description":"The UUID of the related record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mediator Substance Substance Class":{ + "lucenePath":"root_relationships_mediatorSubstance_substanceClass", + "description":"This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mediator Substance UUID":{ + "lucenePath":"root_relationships_mediatorSubstance_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Access":{ + "lucenePath":"root_mixture_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Mixture Component Access":{ + "lucenePath":"root_mixture_components_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Mixture Component ApprovalID":{ + "lucenePath":"root_mixture_components_substance_approvalID", + "description":"The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Component Created By":{ + "lucenePath":"root_mixture_components_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Component Created Date":{ + "lucenePath":"root_mixture_components_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Component Deprecated":{ + "lucenePath":"root_mixture_components_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Component Last Edited By":{ + "lucenePath":"root_mixture_components_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Component Last Edited Date":{ + "lucenePath":"root_mixture_components_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Component Name":{ + "lucenePath":"root_mixture_components_substance_refPname", + "description":"The \"Priority Name\" (usually the display name) of the record being referenced.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Mixture Component Substance Access":{ + "lucenePath":"root_mixture_components_substance_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Mixture Component Substance Created By":{ + "lucenePath":"root_mixture_components_substance_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Component Substance Created Date":{ + "lucenePath":"root_mixture_components_substance_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Component Substance Deprecated":{ + "lucenePath":"root_mixture_components_substance_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Component Substance Last Edited By":{ + "lucenePath":"root_mixture_components_substance_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Component Substance Last Edited Date":{ + "lucenePath":"root_mixture_components_substance_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Component Substance RefUUID":{ + "lucenePath":"root_mixture_components_substance_refuuid", + "description":"The UUID of the related record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Component Substance Substance Class":{ + "lucenePath":"root_mixture_components_substance_substanceClass", + "description":"This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Component Substance UUID":{ + "lucenePath":"root_mixture_components_substance_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Component Type":{ + "lucenePath":"root_mixture_components_type", + "description":"The component type relative to it's mixture ('may be present (any of)', 'must be present (all of)', etc.).", + "type":"string", + "cvDomain":"MIXTURE_TYPE", + "priority":null, + "suggest":null + }, + "Mixture Component UUID":{ + "lucenePath":"root_mixture_components_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Created By":{ + "lucenePath":"root_mixture_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Created Date":{ + "lucenePath":"root_mixture_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Deprecated":{ + "lucenePath":"root_mixture_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Last Edited By":{ + "lucenePath":"root_mixture_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Last Edited Date":{ + "lucenePath":"root_mixture_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Parent Substance Access":{ + "lucenePath":"root_mixture_parentSubstance_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Mixture Parent Substance Approval ID":{ + "lucenePath":"root_mixture_parentSubstance_approvalID", + "description":"The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Parent Substance Created By":{ + "lucenePath":"root_mixture_parentSubstance_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Parent Substance Created Date":{ + "lucenePath":"root_mixture_parentSubstance_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Parent Substance Deprecated":{ + "lucenePath":"root_mixture_parentSubstance_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Parent Substance Last Edited By":{ + "lucenePath":"root_mixture_parentSubstance_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Parent Substance Last Edited Date":{ + "lucenePath":"root_mixture_parentSubstance_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Parent Substance Preferred Name":{ + "lucenePath":"root_mixture_parentSubstance_refPname", + "description":"The \"Priority Name\" (usually the display name) of the record being referenced.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Parent Substance RefUUID":{ + "lucenePath":"root_mixture_parentSubstance_refuuid", + "description":"The UUID of the related record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Parent Substance Substance Class":{ + "lucenePath":"root_mixture_parentSubstance_substanceClass", + "description":"This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture Parent Substance UUID":{ + "lucenePath":"root_mixture_parentSubstance_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Mixture UUID":{ + "lucenePath":"root_mixture_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Modification Access":{ + "lucenePath":"root_modifications_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Modification Created By":{ + "lucenePath":"root_modifications_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Modification Created Date":{ + "lucenePath":"root_modifications_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Modification Deprecated":{ + "lucenePath":"root_modifications_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Modification Last Edited By":{ + "lucenePath":"root_modifications_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Modification Last Edited Date":{ + "lucenePath":"root_modifications_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Modification UUID":{ + "lucenePath":"root_modifications_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Moiety Access":{ + "lucenePath":"root_moieties_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Moiety Atropisomerism":{ + "lucenePath":"root_moieties_atropisomerism", + "description":"Whether or not the record has atropisomerism, or has a chirality where a hindered rotation about a single bond as a resolt of steric or electronic constraints.", + "type":"string", + "cvDomain":"ATROPISOMERISM", + "priority":null, + "suggest":null + }, + "Moiety Charge":{ + "lucenePath":"root_moieties_charge", + "description":"The net charge of the structure.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Moiety Count":{ + "lucenePath":"root_moieties_count", + "description":"The exact number of times that the structure is repeated.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Moiety Created By":{ + "lucenePath":"root_moieties_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Moiety Created Date":{ + "lucenePath":"root_moieties_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Moiety Defined Stereo":{ + "lucenePath":"root_moieties_definedStereo", + "description":"A count of defined stereocenters.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Moiety Deprecated":{ + "lucenePath":"root_moieties_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Moiety Digest":{ + "lucenePath":"root_moieties_digest", + "description":"A hash of the raw structural information encoded.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Moiety Ez Centers":{ + "lucenePath":"root_moieties_ezCenters", + "description":"A count of E\/Z, or absolute double bond stereochemistry centers.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Moiety Formula":{ + "lucenePath":"root_moieties_formula", + "description":"The chemical formula of the record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Moiety Last Edited By":{ + "lucenePath":"root_moieties_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Moiety Last Edited Date":{ + "lucenePath":"root_moieties_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Moiety Molecular Weight":{ + "lucenePath":"D_root_moieties_mwt", + "description":"The molecular weight of the structure.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Moiety Optical Activity":{ + "lucenePath":"root_moieties_opticalActivity", + "description":"The optical activity or rotation of the material. \"(+)\" and \"(-)\" are used for optically active materials where activity is known and significantly useful for definition. \"(+\/-)\" is used for racemic mixtures.", + "type":"string", + "cvDomain":"OPTICAL_ACTIVITY", + "priority":null, + "suggest":null + }, + "Moiety Stereo Centers":{ + "lucenePath":"root_moieties_stereoCenters", + "description":"A count of possible stereocenters.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Moiety Stereo Comments":{ + "lucenePath":"root_moieties_stereoComments", + "description":"Some textually descriptive information about specific stereochemistry that cannot adequately be captured elsewhere.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Moiety Stereochemistry":{ + "lucenePath":"root_moieties_stereoChemistry", + "description":"The structure's type of stereochemistry (absolute, achiral, racemic, etc.).", + "type":"string", + "cvDomain":"STEREOCHEMISTRY_TYPE", + "priority":null, + "suggest":null + }, + "Moiety UUID":{ + "lucenePath":"root_moieties_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Molecular Formula":{ + "lucenePath":"root_structure_formula", + "description":"The chemical formula of the record.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Molecular Fragment Access":{ + "lucenePath":"root_modifications_structuralModifications_molecularFragment_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Molecular Fragment Approval ID":{ + "lucenePath":"root_modifications_structuralModifications_molecularFragment_approvalID", + "description":"The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Molecular Fragment Created By":{ + "lucenePath":"root_modifications_structuralModifications_molecularFragment_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Molecular Fragment Created Date":{ + "lucenePath":"root_modifications_structuralModifications_molecularFragment_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Molecular Fragment Deprecated":{ + "lucenePath":"root_modifications_structuralModifications_molecularFragment_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Molecular Fragment Last Edited By":{ + "lucenePath":"root_modifications_structuralModifications_molecularFragment_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Molecular Fragment Last Edited Date":{ + "lucenePath":"root_modifications_structuralModifications_molecularFragment_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Molecular Fragment Preferred Name":{ + "lucenePath":"root_modifications_structuralModifications_molecularFragment_refPname", + "description":"The \"Priority Name\" (usually the display name) of the record being referenced.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Molecular Fragment RefUUID":{ + "lucenePath":"root_modifications_structuralModifications_molecularFragment_refuuid", + "description":"The UUID of the related record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Molecular Fragment Substance Class":{ + "lucenePath":"root_modifications_structuralModifications_molecularFragment_substanceClass", + "description":"This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Molecular Fragment UUID":{ + "lucenePath":"root_modifications_structuralModifications_molecularFragment_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Access":{ + "lucenePath":"root_polymer_monomers_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Monomer Amount Access":{ + "lucenePath":"root_polymer_monomers_amount_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Monomer Amount Average":{ + "lucenePath":"D_root_polymer_monomers_amount_average", + "description":"The amount's numeric average if present.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Amount Created By":{ + "lucenePath":"root_polymer_monomers_amount_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Amount Created Date":{ + "lucenePath":"root_polymer_monomers_amount_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Amount Deprecated":{ + "lucenePath":"root_polymer_monomers_amount_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Amount High":{ + "lucenePath":"D_root_polymer_monomers_amount_high", + "description":"The highest numerical value likely for the average amount. (highest average).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Amount High Limit":{ + "lucenePath":"D_root_polymer_monomers_amount_highLimit", + "description":"The highest allowable numeric value usable for the amount. (highest limit).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Amount Last Edited By":{ + "lucenePath":"root_polymer_monomers_amount_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Amount Last Edited Date":{ + "lucenePath":"root_polymer_monomers_amount_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Amount Low":{ + "lucenePath":"D_root_polymer_monomers_amount_low", + "description":"The lowest numerical value likely for the average amount. (lowest average).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Amount Low Limit":{ + "lucenePath":"D_root_polymer_monomers_amount_lowLimit", + "description":"The lowest allowable numeric value usable for the amount. (lowest limit).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Amount Non-Numeric Value":{ + "lucenePath":"root_polymer_monomers_amount_nonNumericValue", + "description":"A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Amount Type":{ + "lucenePath":"root_polymer_monomers_amount_type", + "description":"The type of the amount specified ('mol ratio'. 'degree of polymerization', 'weight ratio', etc.).", + "type":"string", + "cvDomain":"AMOUNT_TYPE", + "priority":null, + "suggest":null + }, + "Monomer Amount UUID":{ + "lucenePath":"root_polymer_monomers_amount_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Amount Units":{ + "lucenePath":"root_polymer_monomers_amount_units", + "description":"The amount's unit of measurement.", + "type":"string", + "cvDomain":"AMOUNT_UNIT", + "priority":null, + "suggest":null + }, + "Monomer Approval ID":{ + "lucenePath":"root_polymer_monomers_monomerSubstance_approvalID", + "description":"The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Created By":{ + "lucenePath":"root_polymer_monomers_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Created Date":{ + "lucenePath":"root_polymer_monomers_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Defining":{ + "lucenePath":"root_polymer_monomers_defining", + "description":"A flag for whether or not the monomer \/ starting material is considered canonically defining for the polymer.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Deprecated":{ + "lucenePath":"root_polymer_monomers_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Last Edited By":{ + "lucenePath":"root_polymer_monomers_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Last Edited Date":{ + "lucenePath":"root_polymer_monomers_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Substance Access":{ + "lucenePath":"root_polymer_monomers_monomerSubstance_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Monomer Substance Created By":{ + "lucenePath":"root_polymer_monomers_monomerSubstance_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Substance Created Date":{ + "lucenePath":"root_polymer_monomers_monomerSubstance_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Substance Deprecated":{ + "lucenePath":"root_polymer_monomers_monomerSubstance_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Substance Last Edited By":{ + "lucenePath":"root_polymer_monomers_monomerSubstance_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Substance Last Edited Date":{ + "lucenePath":"root_polymer_monomers_monomerSubstance_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Substance Name":{ + "lucenePath":"root_polymer_monomers_monomerSubstance_refPname", + "description":"The \"Priority Name\" (usually the display name) of the record being referenced.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Monomer Substance RefUUID":{ + "lucenePath":"root_polymer_monomers_monomerSubstance_refuuid", + "description":"The UUID of the related record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Substance Substance Class":{ + "lucenePath":"root_polymer_monomers_monomerSubstance_substanceClass", + "description":"This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Substance UUID":{ + "lucenePath":"root_polymer_monomers_monomerSubstance_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Monomer Type":{ + "lucenePath":"root_polymer_monomers_type", + "description":"The chemical's type or role in polymerization ('initiator', 'monomer', 'starting material', etc.).", + "type":"string", + "cvDomain":"MONOMER_TYPE", + "priority":null, + "suggest":null + }, + "Monomer UUID":{ + "lucenePath":"root_polymer_monomers_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Name Access":{ + "lucenePath":"root_names_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Name Created By":{ + "lucenePath":"root_names_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Name Created Date":{ + "lucenePath":"root_names_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Name Deprecated":{ + "lucenePath":"root_names_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Name Display Name":{ + "lucenePath":"root_names_displayName", + "description":"True if this is to be the displayed name for the substance.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Name Domains":{ + "lucenePath":"root_names_domains_term", + "description":"The list of domains that this name is used in (e.g. \"drug\", \"cosmetic\", etc).", + "type":"string", + "cvDomain":"NAME_DOMAIN", + "priority":null, + "suggest":null + }, + "Name Languages":{ + "lucenePath":"root_names_languages_term", + "description":"The list of languages that use that name as written (ISO 639-1 codes).", + "type":"string", + "cvDomain":"LANGUAGE", + "priority":null, + "suggest":null + }, + "Name Last Edited By":{ + "lucenePath":"root_names_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Name Last Edited Date":{ + "lucenePath":"root_names_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Name Name Jurisdiction":{ + "lucenePath":"root_names_nameJurisdiction_term", + "description":"The list of jurisdictions where that name is used.", + "type":"string", + "cvDomain":"JURISDICTION", + "priority":null, + "suggest":null + }, + "Name Preferred":{ + "lucenePath":"root_names_preferred", + "description":"True if this is a \"preferred name\" for the substance (each substance may have many).", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Name Type Code":{ + "lucenePath":"root_names_type", + "description":"The type of the name (e.g. \"bn\" for \"brand name\", \"cn\" for \"common name\", and \"sys\" for \"systematic name\", etc).", + "type":"string", + "cvDomain":"NAME_TYPE", + "priority":"x", + "suggest":null + }, + "Name UUID":{ + "lucenePath":"root_names_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Naming Organization":{ + "lucenePath":"root_names_nameOrgs_nameOrg", + "description":"A string representing the naming organization which has designated the name as official in some regard. (e.g. \"USAN\", \"INN\").", + "type":"string", + "cvDomain":"NAME_ORG", + "priority":"x", + "suggest":null + }, + "Naming Organization Access":{ + "lucenePath":"root_names_nameOrgs_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Naming Organization Created By":{ + "lucenePath":"root_names_nameOrgs_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Naming Organization Created Date":{ + "lucenePath":"root_names_nameOrgs_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Naming Organization Deprecated":{ + "lucenePath":"root_names_nameOrgs_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Naming Organization Deprecated Date":{ + "lucenePath":"root_names_nameOrgs_deprecatedDate", + "description":"A UNIX timestamp for when the naming organization deprecated the name's status (if it has been deprecated).", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Naming Organization Last Edited By":{ + "lucenePath":"root_names_nameOrgs_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Naming Organization Last Edited Date":{ + "lucenePath":"root_names_nameOrgs_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Naming Organization UUID":{ + "lucenePath":"root_names_nameOrgs_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Note":{ + "lucenePath":"root_notes_note", + "description":"The literal text of a note comment for a record. These is typically used to capture some orienting descriptive information, qualifications, or systematic notes on validation.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Note Access":{ + "lucenePath":"root_notes_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Note Created By":{ + "lucenePath":"root_notes_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Note Created Date":{ + "lucenePath":"root_notes_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Note Deprecated":{ + "lucenePath":"root_notes_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Note Last Edited By":{ + "lucenePath":"root_notes_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Note Last Edited Date":{ + "lucenePath":"root_notes_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Access":{ + "lucenePath":"root_nucleicAcid_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Nucleic Acid Created By":{ + "lucenePath":"root_nucleicAcid_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Created Date":{ + "lucenePath":"root_nucleicAcid_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Deprecated":{ + "lucenePath":"root_nucleicAcid_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Last Edited By":{ + "lucenePath":"root_nucleicAcid_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Last Edited Date":{ + "lucenePath":"root_nucleicAcid_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Linkage":{ + "lucenePath":"root_nucleicAcid_linkages_linkage", + "description":"The linkage itself which is used. This is a short-hand code for a defined structural linkage fragment found within the GSRS controlled vocabulary.", + "type":"string", + "cvDomain":"NUCLEIC_ACID_LINKAGE", + "priority":"x", + "suggest":null + }, + "Nucleic Acid Linkage Access":{ + "lucenePath":"root_nucleicAcid_linkages_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Nucleic Acid Linkage Created By":{ + "lucenePath":"root_nucleicAcid_linkages_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Linkage Created Date":{ + "lucenePath":"root_nucleicAcid_linkages_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Linkage Deprecated":{ + "lucenePath":"root_nucleicAcid_linkages_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Linkage Last Edited By":{ + "lucenePath":"root_nucleicAcid_linkages_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Linkage Last Edited Date":{ + "lucenePath":"root_nucleicAcid_linkages_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Linkage UUID":{ + "lucenePath":"root_nucleicAcid_linkages_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Sequence Origin":{ + "lucenePath":"root_nucleicAcid_sequenceOrigin", + "description":"The original source of the amino acid sequence. This is a general category which, for organisms, typically specifies a useful general abstract taxonic level with increasing granularity as it approaches humans (e.g. \"bacteria\",\"mouse\", \"mouse chimeric\", \"human\").", + "type":"string", + "cvDomain":"NUCLEIC_ACID_SEQUENCE_ORIGIN", + "priority":null, + "suggest":null + }, + "Nucleic Acid Sequence Type":{ + "lucenePath":"root_nucleicAcid_sequenceType", + "description":"The type of sequence being specified (e.g. \"incomplete\", \"complete\").", + "type":"string", + "cvDomain":"SEQUENCE_TYPE", + "priority":null, + "suggest":null + }, + "Nucleic Acid Sub Type":{ + "lucenePath":"root_nucleicAcid_nucleicAcidSubType", + "description":"The sub-type of nucleic acid.", + "type":"string", + "cvDomain":"NUCLEIC_ACID_SUBTYPE", + "priority":null, + "suggest":null + }, + "Nucleic Acid Subunit Access":{ + "lucenePath":"root_nucleicAcid_subunits_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Nucleic Acid Subunit Created By":{ + "lucenePath":"root_nucleicAcid_subunits_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Subunit Created Date":{ + "lucenePath":"root_nucleicAcid_subunits_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Subunit Deprecated":{ + "lucenePath":"root_nucleicAcid_subunits_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Subunit Last Edited By":{ + "lucenePath":"root_nucleicAcid_subunits_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Subunit Last Edited Date":{ + "lucenePath":"root_nucleicAcid_subunits_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Subunit Sequence":{ + "lucenePath":"root_nucleicAcid_subunits_sequence", + "description":"The amino acid sequence as a string of 1-letter amino acids, from N-term to C-term. Lower-case letters represent D-amino acids, while upper-case are the standard L-amino acids.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Subunit Subunit Index":{ + "lucenePath":"root_nucleicAcid_subunits_subunitIndex", + "description":"The index of the subunit being specified.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Subunit UUID":{ + "lucenePath":"root_nucleicAcid_subunits_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Sugar":{ + "lucenePath":"root_nucleicAcid_sugars_sugar", + "description":"The sugar itself which is used. This is a short-hand code for a defined structural sugar fragment found within the GSRS controlled vocabulary.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Nucleic Acid Sugar Access":{ + "lucenePath":"root_nucleicAcid_sugars_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Nucleic Acid Sugar Created By":{ + "lucenePath":"root_nucleicAcid_sugars_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Sugar Created Date":{ + "lucenePath":"root_nucleicAcid_sugars_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Sugar Deprecated":{ + "lucenePath":"root_nucleicAcid_sugars_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Sugar Last Edited By":{ + "lucenePath":"root_nucleicAcid_sugars_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Sugar Last Edited Date":{ + "lucenePath":"root_nucleicAcid_sugars_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Sugar UUID":{ + "lucenePath":"root_nucleicAcid_sugars_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Nucleic Acid Type":{ + "lucenePath":"root_nucleicAcid_nucleicAcidType", + "description":"The type of nucleic acid.", + "type":"string", + "cvDomain":"NUCLEIC_ACID_TYPE", + "priority":null, + "suggest":null + }, + "Nucleic Acid UUID":{ + "lucenePath":"root_nucleicAcid_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Organism Author":{ + "lucenePath":"root_structurallyDiverse_organismAuthor", + "description":"The author of the organism's scientific name.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Organism Family":{ + "lucenePath":"root_structurallyDiverse_organismFamily", + "description":"The organism's taxonomic family.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Organism Genus":{ + "lucenePath":"root_structurallyDiverse_organismGenus", + "description":"The organism's taxonomic genus.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Organism Species":{ + "lucenePath":"root_structurallyDiverse_organismSpecies", + "description":"The organism's species scientific name excluding the genus, known as the specific epithet.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Other Link Access":{ + "lucenePath":"root_protein_otherLinks_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Other Link Created By":{ + "lucenePath":"root_protein_otherLinks_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Other Link Created Date":{ + "lucenePath":"root_protein_otherLinks_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Other Link Deprecated":{ + "lucenePath":"root_protein_otherLinks_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Other Link Last Edited By":{ + "lucenePath":"root_protein_otherLinks_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Other Link Last Edited Date":{ + "lucenePath":"root_protein_otherLinks_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Other Link Linkagetype":{ + "lucenePath":"root_protein_otherLinks_linkageType", + "description":"The type of linkage which connects the residues in a protein.", + "type":"string", + "cvDomain":"OTHER_LINKAGE_TYPE", + "priority":null, + "suggest":null + }, + "Other Link UUID":{ + "lucenePath":"root_protein_otherLinks_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Access":{ + "lucenePath":"root_modifications_physicalModifications_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Physical Modification Amount Access":{ + "lucenePath":"root_modifications_physicalModifications_parameters_amount_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Physical Modification Amount Average":{ + "lucenePath":"D_root_modifications_physicalModifications_parameters_amount_average", + "description":"The amount's numeric average if present.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Amount Created By":{ + "lucenePath":"root_modifications_physicalModifications_parameters_amount_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Amount Created Date":{ + "lucenePath":"root_modifications_physicalModifications_parameters_amount_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Amount Deprecated":{ + "lucenePath":"root_modifications_physicalModifications_parameters_amount_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Amount High Limit":{ + "lucenePath":"D_root_modifications_physicalModifications_parameters_amount_highLimit", + "description":"The highest allowable numeric value usable for the amount. (highest limit).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Amount Last Edited By":{ + "lucenePath":"root_modifications_physicalModifications_parameters_amount_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Amount Last Edited Date":{ + "lucenePath":"root_modifications_physicalModifications_parameters_amount_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Amount Low Limit":{ + "lucenePath":"D_root_modifications_physicalModifications_parameters_amount_lowLimit", + "description":"The lowest allowable numeric value usable for the amount. (lowest limit).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Amount Non-Numeric Value":{ + "lucenePath":"root_modifications_physicalModifications_parameters_amount_nonNumericValue", + "description":"A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Amount Type":{ + "lucenePath":"root_modifications_physicalModifications_parameters_amount_type", + "description":"The type of the amount specified ('mol ratio'. 'degree of polymerization', 'weight ratio', 'other' etc.).", + "type":"string", + "cvDomain":"AMOUNT_TYPE", + "priority":null, + "suggest":null + }, + "Physical Modification Amount UUID":{ + "lucenePath":"root_modifications_physicalModifications_parameters_amount_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Amount Units":{ + "lucenePath":"root_modifications_physicalModifications_parameters_amount_units", + "description":"The amount's unit of measurement.", + "type":"string", + "cvDomain":"AMOUNT_UNIT", + "priority":null, + "suggest":null + }, + "Physical Modification Created By":{ + "lucenePath":"root_modifications_physicalModifications_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Created Date":{ + "lucenePath":"root_modifications_physicalModifications_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Deprecated":{ + "lucenePath":"root_modifications_physicalModifications_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Last Edited By":{ + "lucenePath":"root_modifications_physicalModifications_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Last Edited Date":{ + "lucenePath":"root_modifications_physicalModifications_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Modification Group":{ + "lucenePath":"root_modifications_physicalModifications_modificationGroup", + "description":"A key specifying how this modification relates to other modifications. All modifications sharing a group key are considered to be occuring as part of a single process or event, or descriptive of the same change to the substance.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Parameter Access":{ + "lucenePath":"root_modifications_physicalModifications_parameters_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Physical Modification Parameter Created By":{ + "lucenePath":"root_modifications_physicalModifications_parameters_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Parameter Created Date":{ + "lucenePath":"root_modifications_physicalModifications_parameters_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Parameter Deprecated":{ + "lucenePath":"root_modifications_physicalModifications_parameters_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Parameter Last Edited By":{ + "lucenePath":"root_modifications_physicalModifications_parameters_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Parameter Last Edited Date":{ + "lucenePath":"root_modifications_physicalModifications_parameters_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Parameter Parameter Name":{ + "lucenePath":"root_modifications_physicalModifications_parameters_parameterName", + "description":"The name of the paramater which qualifies \/ conditions the property's measurement or domain of applicability. .", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Physical Modification Parameter UUID":{ + "lucenePath":"root_modifications_physicalModifications_parameters_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Physical Modification Physical Modification Role":{ + "lucenePath":"root_modifications_physicalModifications_physicalModificationRole", + "description":"The role that the physical modification has in the process of the modification.", + "type":"string", + "cvDomain":"PHYSICAL_MODIFICATION_ROLE", + "priority":"x", + "suggest":null + }, + "Physical Modification UUID":{ + "lucenePath":"root_modifications_physicalModifications_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Access":{ + "lucenePath":"root_polymer_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Polymer Class":{ + "lucenePath":"root_polymer_classification_polymerClass", + "description":"A general classication of the polymer type (e.g. \"homopolymer\", \"copolymer\").", + "type":"string", + "cvDomain":"POLYMER_CLASS", + "priority":"x", + "suggest":null + }, + "Polymer Classification Access":{ + "lucenePath":"root_polymer_classification_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Polymer Classification Created By":{ + "lucenePath":"root_polymer_classification_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Classification Created Date":{ + "lucenePath":"root_polymer_classification_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Classification Deprecated":{ + "lucenePath":"root_polymer_classification_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Classification Last Edited By":{ + "lucenePath":"root_polymer_classification_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Classification Last Edited Date":{ + "lucenePath":"root_polymer_classification_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Classification Polymer Subclass":{ + "lucenePath":"root_polymer_classification_polymerSubclass_term", + "description":"The set of subclasses describing the polymer (e.g. \"cross-link\", \"graft\").", + "type":"string", + "cvDomain":"POLYMER_SUBCLASS", + "priority":"x", + "suggest":null + }, + "Polymer Classification Source Type":{ + "lucenePath":"root_polymer_classification_sourceType", + "description":"The type of material used as the source for the polymer. If from a biological or natural source, further reference to the source material will be needed.", + "type":"string", + "cvDomain":"POLYMER_SOURCE_TYPE", + "priority":null, + "suggest":null + }, + "Polymer Classification UUID":{ + "lucenePath":"root_notes_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Created By":{ + "lucenePath":"root_polymer_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Created Date":{ + "lucenePath":"root_polymer_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Deprecated":{ + "lucenePath":"root_polymer_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Display Structure Access":{ + "lucenePath":"root_polymer_displayStructure_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Polymer Display Structure Charge":{ + "lucenePath":"root_polymer_displayStructure_charge", + "description":"The net charge of the structure.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Display Structure Count":{ + "lucenePath":"root_polymer_displayStructure_count", + "description":"The exact number of times that the structure is repeated.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Display Structure Created By":{ + "lucenePath":"root_polymer_displayStructure_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Display Structure Created Date":{ + "lucenePath":"root_polymer_displayStructure_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Display Structure Defined Stereo":{ + "lucenePath":"root_polymer_displayStructure_definedStereo", + "description":"A count of defined stereocenters.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Display Structure Deprecated":{ + "lucenePath":"root_polymer_displayStructure_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Display Structure Digest":{ + "lucenePath":"root_polymer_displayStructure_digest", + "description":"A hash of the raw structural information encoded.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Display Structure Ez Centers":{ + "lucenePath":"root_polymer_displayStructure_ezCenters", + "description":"A count of E\/Z, or absolute double bond stereochemistry centers.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Display Structure Formula":{ + "lucenePath":"root_polymer_displayStructure_formula", + "description":"The chemical formula of the record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Display Structure Last Edited By":{ + "lucenePath":"root_polymer_displayStructure_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Display Structure Last Edited Date":{ + "lucenePath":"root_polymer_displayStructure_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Display Structure Optical Activity":{ + "lucenePath":"root_polymer_displayStructure_opticalActivity", + "description":"The optical activity or rotation of the material. \"(+)\" and \"(-)\" are used for optically active materials where activity is known and significantly useful for definition. \"(+\/-)\" is used for racemic mixtures.", + "type":"string", + "cvDomain":"OPTICAL_ACTIVITY", + "priority":null, + "suggest":null + }, + "Polymer Display Structure Stereo Centers":{ + "lucenePath":"root_polymer_displayStructure_stereoCenters", + "description":"A count of possible stereocenters.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Display Structure Stereochemistry":{ + "lucenePath":"root_polymer_displayStructure_stereochemistry", + "description":"The structure's type of stereochemistry (absolute, achiral, racemic, etc.).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Geometry":{ + "lucenePath":"root_polymer_classification_polymerGeometry", + "description":"A general classification of the polymer geometry (e.g. \"linear\", \"branch\", \"network\").", + "type":"string", + "cvDomain":"POLYMER_GEOMETRY", + "priority":"x", + "suggest":null + }, + "Polymer Last Edited By":{ + "lucenePath":"root_polymer_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Last Edited Date":{ + "lucenePath":"root_polymer_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Parent Substance Access":{ + "lucenePath":"root_polymer_classification_parentSubstance_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Polymer Parent Substance Approval ID":{ + "lucenePath":"root_polymer_classification_parentSubstance_approvalID", + "description":"The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Parent Substance Created By":{ + "lucenePath":"root_polymer_classification_parentSubstance_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Parent Substance Created Date":{ + "lucenePath":"root_polymer_classification_parentSubstance_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Parent Substance Deprecated":{ + "lucenePath":"root_polymer_classification_parentSubstance_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Parent Substance Last Edited By":{ + "lucenePath":"root_polymer_classification_parentSubstance_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Parent Substance Last Edited Date":{ + "lucenePath":"root_polymer_classification_parentSubstance_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Parent Substance Preferred Name":{ + "lucenePath":"root_polymer_classification_parentSubstance_refPname", + "description":"The \"Priority Name\" (usually the display name) of the record being referenced.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Parent Substance RefUUID":{ + "lucenePath":"root_polymer_classification_parentSubstance_refuuid", + "description":"The UUID of the related record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Parent Substance Substance Class":{ + "lucenePath":"root_polymer_classification_parentSubstance_substanceClass", + "description":"This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer Parent Substance UUID":{ + "lucenePath":"root_polymer_classification_parentSubstance_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Polymer UUID":{ + "lucenePath":"root_polymer_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Access":{ + "lucenePath":"root_properties_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Property Created By":{ + "lucenePath":"root_properties_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Created Date":{ + "lucenePath":"root_properties_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Defining":{ + "lucenePath":"root_properties_defining", + "description":"A flag for whether or not the property is considered canonically defining for the substance definition.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Deprecated":{ + "lucenePath":"root_properties_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Last Edited By":{ + "lucenePath":"root_properties_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Last Edited Date":{ + "lucenePath":"root_properties_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Name":{ + "lucenePath":"root_properties_name", + "description":"The literal string text of a name.", + "type":"string", + "cvDomain":"PROPERTY_NAME", + "priority":"x", + "suggest":null + }, + "Property Parameter Access":{ + "lucenePath":"root_properties_parameters_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Property Parameter Created By":{ + "lucenePath":"root_properties_parameters_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Parameter Created Date":{ + "lucenePath":"root_properties_parameters_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Parameter Deprecated":{ + "lucenePath":"root_properties_parameters_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Parameter Last Edited By":{ + "lucenePath":"root_properties_parameters_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Parameter Last Edited Date":{ + "lucenePath":"root_properties_parameters_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Parameter Type":{ + "lucenePath":"root_properties_parameters_type", + "description":"The type of parameter specified, (e.g. 'CHEMICAL', ''PHYSICAL', 'ENZYMATIC', or other).", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Property Parameter UUID":{ + "lucenePath":"root_properties_parameters_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Parameter Value Access":{ + "lucenePath":"root_properties_parameters_value_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Property Parameter Value Average":{ + "lucenePath":"D_root_properties_parameters_value_average", + "description":"The amount's numeric average if present.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Parameter Value Created By":{ + "lucenePath":"root_properties_parameters_value_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Parameter Value Created Date":{ + "lucenePath":"root_properties_parameters_value_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Parameter Value Deprecated":{ + "lucenePath":"root_properties_parameters_value_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Parameter Value High":{ + "lucenePath":"D_root_properties_parameters_value_high", + "description":"The highest numerical value likely for the average amount. (highest average).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Parameter Value Last Edited By":{ + "lucenePath":"root_properties_parameters_value_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Parameter Value Last Edited Date":{ + "lucenePath":"root_properties_parameters_value_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Parameter Value Low":{ + "lucenePath":"D_root_properties_parameters_value_low", + "description":"The lowest numerical value likely for the average amount. (lowest average).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Parameter Value Non-Numeric Value":{ + "lucenePath":"root_properties_parameters_value_nonNumericValue", + "description":"A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Parameter Value Type":{ + "lucenePath":"root_properties_parameters_value_type", + "description":"The type of the property parameter (e.g. 'CHEMICAL', ''PHYSICAL', 'ENZYMATIC', or other).", + "type":"string", + "cvDomain":"AMOUNT_TYPE", + "priority":null, + "suggest":null + }, + "Property Parameter Value UUID":{ + "lucenePath":"root_properties_parameters_value_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Parameter Value Units":{ + "lucenePath":"root_properties_parameters_value_units", + "description":"The amount's unit of measurement.", + "type":"string", + "cvDomain":"AMOUNT_UNIT", + "priority":null, + "suggest":null + }, + "Property Property Type":{ + "lucenePath":"root_properties_propertyType", + "description":"The type of property: chemical, enzymatic, physical, or other.", + "type":"string", + "cvDomain":"PROPERTY_TYPE", + "priority":null, + "suggest":null + }, + "Property Type":{ + "lucenePath":"root_properties_type", + "description":"The type of the name (e.g. \"bn\" for \"brand name\", \"cn\" for \"common name\", and \"sys\" for \"systematic name\", etc).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property UUID":{ + "lucenePath":"root_properties_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Value Access":{ + "lucenePath":"root_properties_value_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Property Value Average":{ + "lucenePath":"D_root_properties_value_average", + "description":"The amount's numeric average if present.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Value Created By":{ + "lucenePath":"root_properties_value_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Value Created Date":{ + "lucenePath":"root_properties_value_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Value Deprecated":{ + "lucenePath":"root_properties_value_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Value High":{ + "lucenePath":"D_root_properties_value_high", + "description":"The highest numerical value likely for the average amount. (highest average).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Value High Limit":{ + "lucenePath":"D_root_properties_value_highLimit", + "description":"The highest allowable numeric value usable for the amount. (highest limit).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Value Last Edited By":{ + "lucenePath":"root_properties_value_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Value Last Edited Date":{ + "lucenePath":"root_properties_value_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Value Low":{ + "lucenePath":"D_root_properties_value_low", + "description":"The lowest numerical value likely for the average amount. (lowest average).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Value Low Limit":{ + "lucenePath":"D_root_properties_value_lowLimit", + "description":"The lowest allowable numeric value usable for the amount. (lowest limit).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Value Non-Numeric Property Value":{ + "lucenePath":"root_properties_value_nonNumericValue", + "description":"A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Value Type":{ + "lucenePath":"root_properties_value_type", + "description":"The type of property specified, (e.g. 'CHEMICAL', ''PHYSICAL', 'ENZYMATIC', or other).", + "type":"string", + "cvDomain":"AMOUNT_TYPE", + "priority":null, + "suggest":null + }, + "Property Value UUID":{ + "lucenePath":"root_properties_value_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Property Value Units":{ + "lucenePath":"root_properties_value_units", + "description":"The amount's unit of measurement.", + "type":"string", + "cvDomain":"AMOUNT_UNIT", + "priority":null, + "suggest":null + }, + "Protein Access":{ + "lucenePath":"root_protein_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Protein Created By":{ + "lucenePath":"root_protein_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Protein Created Date":{ + "lucenePath":"root_protein_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Protein Deprecated":{ + "lucenePath":"root_protein_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Protein Glycosylation Access":{ + "lucenePath":"root_protein_glycosylation_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Protein Glycosylation Created By":{ + "lucenePath":"root_protein_glycosylation_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Protein Glycosylation Created Date":{ + "lucenePath":"root_protein_glycosylation_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Protein Glycosylation Deprecated":{ + "lucenePath":"root_protein_glycosylation_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Protein Glycosylation Last Edited By":{ + "lucenePath":"root_protein_glycosylation_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Protein Glycosylation Last Edited Date":{ + "lucenePath":"root_protein_glycosylation_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Protein Glycosylation Type":{ + "lucenePath":"root_protein_glycosylation_glycosylationType", + "description":"The type of glycosylation pattern found on the protein. Here, this is a very general term meant to capture the organism-level pattern that the glycosylation roughly corresponds to. (e.g. \"mouse\", \"human\", \"porcine\").", + "type":"string", + "cvDomain":"GLYCOSYLATION_TYPE", + "priority":"x", + "suggest":null + }, + "Protein Glycosylation UUID":{ + "lucenePath":"root_protein_glycosylation_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Protein Last Edited By":{ + "lucenePath":"root_protein_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Protein Last Edited Date":{ + "lucenePath":"root_protein_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Protein Protein Subtype":{ + "lucenePath":"root_protein_proteinSubType", + "description":"A comma-separated set of subtypes descriptive of the protein.", + "type":"string", + "cvDomain":"PROTEIN_SUBTYPE", + "priority":"x", + "suggest":null + }, + "Protein Sequence":{ + "lucenePath":"root_protein_subunits_sequence", + "description":"The amino acid sequence as a string of 1-letter amino acids, from N-term to C-term. Lower-case letters represent D-amino acids, while upper-case are the standard L-amino acids.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Protein Sequence Origin":{ + "lucenePath":"root_protein_sequenceOrigin", + "description":"The original source of the amino acid sequence. This is a general category which, for organisms, typically specifies a useful general abstract taxonic level with increasing granularity as it approaches humans (e.g. \"bacteria\",\"mouse\", \"mouse chimeric\", \"human\").", + "type":"string", + "cvDomain":"SEQUENCE_ORIGIN", + "priority":null, + "suggest":null + }, + "Protein Sequence Type":{ + "lucenePath":"root_protein_sequenceType", + "description":"The type of sequence being specified (e.g. \"incomplete\", \"complete\").", + "type":"string", + "cvDomain":"SEQUENCE_TYPE", + "priority":null, + "suggest":null + }, + "Protein Subunit Access":{ + "lucenePath":"root_protein_subunits_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Protein Subunit Created By":{ + "lucenePath":"root_protein_subunits_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Protein Subunit Created Date":{ + "lucenePath":"root_protein_subunits_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Protein Subunit Deprecated":{ + "lucenePath":"root_protein_subunits_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Protein Subunit Last Edited By":{ + "lucenePath":"root_protein_subunits_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Protein Subunit Last Edited Date":{ + "lucenePath":"root_protein_subunits_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Protein Subunit Subunit Index":{ + "lucenePath":"root_protein_subunits_subunitIndex", + "description":"The index of the subunit being specified.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Protein Subunit UUID":{ + "lucenePath":"root_protein_subunits_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Protein Type":{ + "lucenePath":"root_protein_proteinType", + "description":"A general typing of the protein.", + "type":"string", + "cvDomain":"PROTEIN_TYPE", + "priority":"x", + "suggest":null + }, + "Protein UUID":{ + "lucenePath":"root_protein_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Record Access":{ + "lucenePath":"root_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Record Approved By":{ + "lucenePath":"root_approvedBy", + "description":"A string representing a user or entity which approved the assignment of UNII, or approval ID.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Record Change Reason":{ + "lucenePath":"root_changeReason", + "description":"A description of the reason for the last change to the record.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Record Created By":{ + "lucenePath":"root_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Record Created Date":{ + "lucenePath":"root_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Record Definition Level":{ + "lucenePath":"root_definitionLevel", + "description":"The level of completeness and specificity of the definition (e.g. \"complete\", \"incomplete\", \"representative\").", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Record Definition Type":{ + "lucenePath":"root_definitionType", + "description":"The type of definition (\"primary\" or \"alternative\"). Primary definitions are the main descriptive form of the substance definition, while \"alternative\" definitions may be seen as definitional \"synonyms\" of some primary record.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Record Deprecated":{ + "lucenePath":"root_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Record Last Edited By":{ + "lucenePath":"root_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Record Last Edited Date":{ + "lucenePath":"root_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Record Status":{ + "lucenePath":"root_status", + "description":"The current record status (e.g. \"pending\", \"approved\"). This is not a regulatory status, but simply the status of record validation and curation. .", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Record Tags":{ + "lucenePath":"root_tags", + "description":"A collection of strings which mark the reference as having specific properties (nomenclature, spectra, definition, etc).", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Record Version":{ + "lucenePath":"root_version", + "description":"The version of the substance being viewed. Increments with every saved edit.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Reference Access":{ + "lucenePath":"root_references_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Reference Created By":{ + "lucenePath":"root_references_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Reference Created Date":{ + "lucenePath":"root_references_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Reference Deprecated":{ + "lucenePath":"root_references_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Reference Document Date":{ + "lucenePath":"root_references_documentDate", + "description":"The date that the document or reference was obtained \/ accessed.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Reference ID":{ + "lucenePath":"root_references_id", + "description":"The identifier of the structural element itself (this is still a UUID).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Reference Last Edited By":{ + "lucenePath":"root_references_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Reference Last Edited Date":{ + "lucenePath":"root_references_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Reference Public Domain":{ + "lucenePath":"root_references_publicDomain", + "description":"A marking of whether the reference itself is known to be publicly accessible.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Reference Tags":{ + "lucenePath":"root_references_tags_term", + "description":"A collection of strings which mark the reference as having specific properties (nomenclature, spectra, definition, etc).", + "type":"string", + "cvDomain":"DOCUMENT_COLLECTION", + "priority":null, + "suggest":null + }, + "Reference Text \/ Citation":{ + "lucenePath":"root_references_citation", + "description":"The reference text or citation.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Reference Type":{ + "lucenePath":"root_references_docType", + "description":"The type of reference or document represented (e.g. \"INN\", \"Journal Article\", etc).", + "type":"string", + "cvDomain":"DOCUMENT_TYPE", + "priority":"x", + "suggest":null + }, + "Reference URL":{ + "lucenePath":"root_references_url", + "description":"The url to further information regarding this reference.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Reference UUID":{ + "lucenePath":"root_references_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Reference Uploaded File":{ + "lucenePath":"root_references_uploadedFile", + "description":"the file path of the associated files.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Related Substance Access":{ + "lucenePath":"root_relationships_relatedSubstance_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Related Substance Approval ID":{ + "lucenePath":"root_relationships_relatedSubstance_approvalID", + "description":"The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Related Substance Created By":{ + "lucenePath":"root_relationships_relatedSubstance_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Related Substance Created Date":{ + "lucenePath":"root_relationships_relatedSubstance_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Related Substance Deprecated":{ + "lucenePath":"root_relationships_relatedSubstance_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Related Substance Last Edited By":{ + "lucenePath":"root_relationships_relatedSubstance_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Related Substance Last Edited Date":{ + "lucenePath":"root_relationships_relatedSubstance_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Related Substance Name":{ + "lucenePath":"root_relationships_relatedSubstance_refPname", + "description":"The \"Priority Name\" (usually the display name) of the record being referenced.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Related Substance RefUUID":{ + "lucenePath":"root_relationships_relatedSubstance_refuuid", + "description":"The UUID of the related record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Related Substance Substance Class":{ + "lucenePath":"root_relationships_relatedSubstance_substanceClass", + "description":"This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Related Substance UUID":{ + "lucenePath":"root_relationships_relatedSubstance_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Access":{ + "lucenePath":"root_relationships_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Relationship Amount Access":{ + "lucenePath":"root_relationships_amount_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Relationship Amount Average":{ + "lucenePath":"D_root_relationships_amount_average", + "description":"The amount's numeric average if present.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Amount Created By":{ + "lucenePath":"root_relationships_amount_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Amount Created Date":{ + "lucenePath":"root_relationships_amount_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Amount Deprecated":{ + "lucenePath":"root_relationships_amount_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Amount High":{ + "lucenePath":"D_root_relationships_amount_high", + "description":"The highest numerical value likely for the average amount. (highest average).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Amount High Limit":{ + "lucenePath":"D_root_relationships_amount_highLimit", + "description":"The highest allowable numeric value usable for the amount. (highest limit).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Amount Last Edited By":{ + "lucenePath":"root_relationships_amount_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Amount Last Edited Date":{ + "lucenePath":"root_relationships_amount_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Amount Low":{ + "lucenePath":"D_root_relationships_amount_low", + "description":"The lowest numerical value likely for the average amount. (lowest average).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Amount Low Limit":{ + "lucenePath":"D_root_relationships_amount_lowLimit", + "description":"The lowest allowable numeric value usable for the amount. (lowest limit).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Amount Non-Numeric Value":{ + "lucenePath":"root_relationships_amount_nonNumericValue", + "description":"A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Amount Type":{ + "lucenePath":"root_relationships_amount_type", + "description":"The type of the amount specified ('mol ratio'. 'degree of polymerization', 'weight ratio', etc.).", + "type":"string", + "cvDomain":"AMOUNT_TYPE", + "priority":null, + "suggest":null + }, + "Relationship Amount UUID":{ + "lucenePath":"root_relationships_amount_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Amount Units":{ + "lucenePath":"root_relationships_amount_units", + "description":"The amount's unit of measurement.", + "type":"string", + "cvDomain":"AMOUNT_UNIT", + "priority":null, + "suggest":null + }, + "Relationship Comments":{ + "lucenePath":"root_relationships_comments", + "description":"Any comments regarding the relationship.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Created By":{ + "lucenePath":"root_relationships_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Created Date":{ + "lucenePath":"root_relationships_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Deprecated":{ + "lucenePath":"root_relationships_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Interaction Type":{ + "lucenePath":"root_relationships_interactionType", + "description":"The type of interaction which occurs between the two specified substance records, if applicable.", + "type":"string", + "cvDomain":"INTERACTION_TYPE", + "priority":null, + "suggest":null + }, + "Relationship Last Edited By":{ + "lucenePath":"root_relationships_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Last Edited Date":{ + "lucenePath":"root_relationships_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Originator UUID":{ + "lucenePath":"root_relationships_originatorUuid", + "description":"The UUID of the equivalent inverted relationship (if present) on the other record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Relationship Qualification":{ + "lucenePath":"root_relationships_qualification", + "description":"Any qualifier needed to describe the relationship \/ interaction between the two records.", + "type":"string", + "cvDomain":"QUALIFICATION", + "priority":null, + "suggest":null + }, + "Relationship Type":{ + "lucenePath":"root_relationships_type", + "description":"The type of relationship (e.g. 'parent -> salt solvate', 'active moiety', 'inhibitor -> 'target', 'toxin -> conjugate' etc.).", + "type":"string", + "cvDomain":"RELATIONSHIP_TYPE", + "priority":null, + "suggest":null + }, + "Relationship UUID":{ + "lucenePath":"root_relationships_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Access":{ + "lucenePath":"root_polymer_structuralUnits_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "SRU Amount Access":{ + "lucenePath":"root_polymer_structuralUnits_amount_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "SRU Amount Average":{ + "lucenePath":"D_root_polymer_structuralUnits_amount_average", + "description":"The amount's numeric average if present.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Amount Created By":{ + "lucenePath":"root_polymer_structuralUnits_amount_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Amount Created Date":{ + "lucenePath":"root_polymer_structuralUnits_amount_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Amount Deprecated":{ + "lucenePath":"root_polymer_structuralUnits_amount_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Amount High":{ + "lucenePath":"D_root_polymer_structuralUnits_amount_high", + "description":"The highest numerical value likely for the average amount. (highest average).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Amount High Limit":{ + "lucenePath":"D_root_polymer_structuralUnits_amount_highLimit", + "description":"The highest allowable numeric value usable for the amount. (highest limit).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Amount Last Edited By":{ + "lucenePath":"root_polymer_structuralUnits_amount_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Amount Last Edited Date":{ + "lucenePath":"root_polymer_structuralUnits_amount_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Amount Low":{ + "lucenePath":"D_root_polymer_structuralUnits_amount_low", + "description":"The lowest numerical value likely for the average amount. (lowest average).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Amount Low Limit":{ + "lucenePath":"D_root_polymer_structuralUnits_amount_lowLimit", + "description":"The lowest allowable numeric value usable for the amount. (lowest limit).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Amount Non-Numeric Value":{ + "lucenePath":"root_polymer_structuralUnits_amount_nonNumericValue", + "description":"A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Amount Type":{ + "lucenePath":"root_polymer_structuralUnits_amount_type", + "description":"The type of the amount specified ('mol ratio'. 'degree of polymerization', 'weight ratio', etc.)", + "type":"string", + "cvDomain":"AMOUNT_TYPE", + "priority":null, + "suggest":null + }, + "SRU Amount UUID":{ + "lucenePath":"root_polymer_structuralUnits_amount_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Amount Units":{ + "lucenePath":"root_polymer_structuralUnits_amount_units", + "description":"The amount's unit of measurement.", + "type":"string", + "cvDomain":"AMOUNT_UNIT", + "priority":null, + "suggest":null + }, + "SRU Attachment Count":{ + "lucenePath":"root_polymer_structuralUnits_attachmentCount", + "description":"A count of the attachment points allowed for the given structural unit", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Created By":{ + "lucenePath":"root_polymer_structuralUnits_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Created Date":{ + "lucenePath":"root_polymer_structuralUnits_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Deprecated":{ + "lucenePath":"root_polymer_structuralUnits_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Label":{ + "lucenePath":"root_polymer_structuralUnits_label", + "description":"A label for the structural unit, typically a sequential capital letter starting with \"A\" (\"A\",\"B\",\"C\", etc)", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Last Edited By":{ + "lucenePath":"root_polymer_structuralUnits_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Last Edited Date":{ + "lucenePath":"root_polymer_structuralUnits_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Structure":{ + "lucenePath":"root_polymer_structuralUnits_structure", + "description":"The molfile-format structure of the structural unit", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "SRU Type":{ + "lucenePath":"root_polymer_structuralUnits_type", + "description":"The type of structural unit specified, describing whether it is an end group, SRU, or fragment.", + "type":"string", + "cvDomain":"POLYMER_SRU_TYPE", + "priority":null, + "suggest":null + }, + "Source Material Class":{ + "lucenePath":"root_structurallyDiverse_sourceMaterialClass", + "description":"The class of source material (e.g. \"organism\" or \"mineral\").", + "type":"string", + "cvDomain":"SOURCE_MATERIAL_CLASS", + "priority":null, + "suggest":null + }, + "Source Material Source Material State":{ + "lucenePath":"root_structurallyDiverse_sourceMaterialState", + "description":"A textual catagorization of what state the material is in, if needed (e.g. \"live\", \"killed\").", + "type":"string", + "cvDomain":"SOURCE_MATERIAL_STATE", + "priority":"x", + "suggest":null + }, + "Source Material Type":{ + "lucenePath":"root_structurallyDiverse_sourceMaterialType", + "description":"The type of source material. This is a general category which, for organisms, typically specifies a useful general abstract taxonic level with increasing granularity as it approaches humans (e.g. \"plant\", \"fungus\", \"mammal\", \"primate\", \"human\").", + "type":"string", + "cvDomain":"SOURCE_MATERIAL_TYPE", + "priority":"x", + "suggest":null + }, + "St. Div. Hybrid Parent (m) Access":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesMaternalOrganism_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (m) Approval ID":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesMaternalOrganism_approvalID", + "description":"The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (m) Created By":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesMaternalOrganism_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (m) Created Date":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesMaternalOrganism_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (m) Deprecated":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesMaternalOrganism_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (m) Last Edited By":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesMaternalOrganism_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (m) Last Edited Date":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesMaternalOrganism_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (m) RefUUID":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesMaternalOrganism_refuuid", + "description":"The UUID of the related record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (m) Substance Class":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesMaternalOrganism_substanceClass", + "description":"This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (m) Substance Name":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesMaternalOrganism_refPname", + "description":"The \"Priority Name\" (usually the display name) of the record being referenced.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (m) UUID":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesMaternalOrganism_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (p) Access":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesPaternalOrganism_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (p) Approval ID":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesPaternalOrganism_approvalID", + "description":"The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (p) Created By":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesPaternalOrganism_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (p) Created Date":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesPaternalOrganism_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (p) Deprecated":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesPaternalOrganism_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (p) Last Edited By":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesPaternalOrganism_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (p) Last Edited Date":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesPaternalOrganism_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (p) RefUUID":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesPaternalOrganism_refuuid", + "description":"The UUID of the related record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (p) Substance Class":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesPaternalOrganism_substanceClass", + "description":"This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (p) Substance Name":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesPaternalOrganism_refPname", + "description":"The \"Priority Name\" (usually the display name) of the record being referenced.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent (p) UUID":{ + "lucenePath":"root_structurallyDiverse_hybridSpeciesPaternalOrganism_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent Access":{ + "lucenePath":"root_structurallyDiverse_parentSubstance_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent Created By":{ + "lucenePath":"root_structurallyDiverse_parentSubstance_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent Created Date":{ + "lucenePath":"root_structurallyDiverse_parentSubstance_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent Deprecated":{ + "lucenePath":"root_structurallyDiverse_parentSubstance_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent Last Edited By":{ + "lucenePath":"root_structurallyDiverse_parentSubstance_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent Last Edited Date":{ + "lucenePath":"root_structurallyDiverse_parentSubstance_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent RefUUID":{ + "lucenePath":"root_structurallyDiverse_parentSubstance_refuuid", + "description":"The UUID of the related record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent Substance Class":{ + "lucenePath":"root_structurallyDiverse_parentSubstance_substanceClass", + "description":"This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Hybrid Parent UUID":{ + "lucenePath":"root_structurallyDiverse_parentSubstance_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Parent Approval ID":{ + "lucenePath":"root_structurallyDiverse_parentSubstance_approvalID", + "description":"The Unique Ingredient Identifier (UNII, aka \"Approval ID\") is a unique ID generated for each substance record.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "St. Div. Parent Substance Name":{ + "lucenePath":"root_structurallyDiverse_parentSubstance_refPname", + "description":"The \"Priority Name\" (usually the display name) of the record being referenced.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Access":{ + "lucenePath":"root_modifications_structuralModifications_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Structural Modification Created By":{ + "lucenePath":"root_modifications_structuralModifications_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Created Date":{ + "lucenePath":"root_modifications_structuralModifications_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Deprecated":{ + "lucenePath":"root_modifications_structuralModifications_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Extent":{ + "lucenePath":"root_modifications_structuralModifications_extent", + "description":"The type of extent for the structural modification. \"Complete\" if the extent is considered to have 100% occupancy \/ replacement, \"partial\" otherwise.", + "type":"string", + "cvDomain":"EXTENT_TYPE", + "priority":null, + "suggest":null + }, + "Structural Modification Extent Amount Access":{ + "lucenePath":"root_modifications_structuralModifications_extentAmount_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Structural Modification Extent Amount Average":{ + "lucenePath":"D_root_modifications_structuralModifications_extentAmount_average", + "description":"The amount's numeric average if present.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Extent Amount Created By":{ + "lucenePath":"root_modifications_structuralModifications_extentAmount_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Extent Amount Created Date":{ + "lucenePath":"root_modifications_structuralModifications_extentAmount_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Extent Amount Deprecated":{ + "lucenePath":"root_modifications_structuralModifications_extentAmount_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Extent Amount High":{ + "lucenePath":"D_root_modifications_structuralModifications_extentAmount_high", + "description":"The highest numerical value likely for the average amount. (highest average).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Extent Amount High Limit":{ + "lucenePath":"D_root_modifications_structuralModifications_extentAmount_highLimit", + "description":"The highest allowable numeric value usable for the amount. (highest limit).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Extent Amount Last Edited By":{ + "lucenePath":"root_modifications_structuralModifications_extentAmount_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Extent Amount Last Edited Date":{ + "lucenePath":"root_modifications_structuralModifications_extentAmount_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Extent Amount Low":{ + "lucenePath":"D_root_modifications_structuralModifications_extentAmount_low", + "description":"The lowest numerical value likely for the average amount. (lowest average).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Extent Amount Low Limit":{ + "lucenePath":"D_root_modifications_structuralModifications_extentAmount_lowLimit", + "description":"The lowest allowable numeric value usable for the amount. (lowest limit).", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Extent Amount Non-Numeric Value":{ + "lucenePath":"root_modifications_structuralModifications_extentAmount_nonNumericValue", + "description":"A textual description of a value which can not be quantified numerically. For example, \"solubility\" may be described quantitatively or it may be described with certain textual categories. Non-numeric values are meant to specify qualitative values and other values not easily described by numbers or ranges of numbers.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Extent Amount Type":{ + "lucenePath":"root_modifications_structuralModifications_extentAmount_type", + "description":"The type of amount recorded for a modification of partial extent.", + "type":"string", + "cvDomain":"AMOUNT_TYPE", + "priority":null, + "suggest":null + }, + "Structural Modification Extent Amount UUID":{ + "lucenePath":"root_modifications_structuralModifications_extentAmount_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Extent Amount Units":{ + "lucenePath":"root_modifications_structuralModifications_extentAmount_units", + "description":"The amount's unit of measurement.", + "type":"string", + "cvDomain":"AMOUNT_UNIT", + "priority":null, + "suggest":null + }, + "Structural Modification Last Edited By":{ + "lucenePath":"root_modifications_structuralModifications_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Last Edited Date":{ + "lucenePath":"root_modifications_structuralModifications_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Locationtype":{ + "lucenePath":"root_modifications_structuralModifications_locationType", + "description":"The type of location specification for a residue modified in a structural modification. (e.g. \"site-specific\" or \"residue-specific\").", + "type":"string", + "cvDomain":"LOCATION_TYPE", + "priority":"x", + "suggest":null + }, + "Structural Modification Modification Group":{ + "lucenePath":"root_modifications_structuralModifications_modificationGroup", + "description":"A key specifying how this modification relates to other modifications. All modifications sharing a group key are considered to be occuring as part of a single process or event, or descriptive of the same change to the substance.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Residuemodified":{ + "lucenePath":"root_modifications_structuralModifications_residueModified", + "description":"The residue which was modified for the structural modification (necessary if the modification is residue specific instead of site-specific).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structural Modification Structural Modification Type":{ + "lucenePath":"root_modifications_structuralModifications_structuralModificationType", + "description":"The type of structural modification.", + "type":"string", + "cvDomain":"STRUCTURAL_MODIFICATION_TYPE", + "priority":"x", + "suggest":null + }, + "Structural Modification UUID":{ + "lucenePath":"root_modifications_structuralModifications_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structurally Diverse Access":{ + "lucenePath":"root_structurallyDiverse_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":"x", + "suggest":null + }, + "Structurally Diverse Created By":{ + "lucenePath":"root_structurallyDiverse_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Structurally Diverse Created Date":{ + "lucenePath":"root_structurallyDiverse_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structurally Diverse Deprecated":{ + "lucenePath":"root_structurallyDiverse_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structurally Diverse Developmental Stage":{ + "lucenePath":"root_structurallyDiverse_developmentalStage", + "description":"The developmental stage of the organism, when necessary to describe the material. (e.g. \"adult\", \"larva\", etc).", + "type":"string", + "cvDomain":"DEVELOPMENTAL_STAGE", + "priority":null, + "suggest":null + }, + "Structurally Diverse Fraction Material Type":{ + "lucenePath":"root_structurallyDiverse_fractionMaterialType", + "description":"A general material type of the fraction which is isolated in a structurally diverse substance (e.g. \"oils\", \"cells\", etc).", + "type":"string", + "cvDomain":"FRACTION_MATERIAL_TYPE", + "priority":"x", + "suggest":null + }, + "Structurally Diverse Fraction Name":{ + "lucenePath":"root_structurallyDiverse_fractionName", + "description":"A specific name for the fraction which is isolated in a structirally diverse substance (e.g. \"low molecular weight oils\").", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Structurally Diverse Infraspecific Name":{ + "lucenePath":"root_structurallyDiverse_infraSpecificName", + "description":"The part of the organisms name that is more specific than the species rank.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structurally Diverse Infraspecific type":{ + "lucenePath":"root_structurallyDiverse_infraSpecificType", + "description":"The type of rank of the infra specific name such as variety, subspecies, or forma. .", + "type":"string", + "cvDomain":"INFRA_SPECIFIC_TYPE", + "priority":null, + "suggest":null + }, + "Structurally Diverse Last Edited By":{ + "lucenePath":"root_structurallyDiverse_lastEditedBy", + "description":"A string representing a user or entity which last edited the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structurally Diverse Last Edited Date":{ + "lucenePath":"root_structurallyDiverse_lastEdited", + "description":"A UNIX timestamp marking when the record was last edited (or sometimes exported) to form the record (or element) in its current state.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structurally Diverse Part":{ + "lucenePath":"root_structurallyDiverse_part_term", + "description":"The contiguous physical part(s) of the source material that are isolated in this substance (e.g. \"flower\", \"fruit\", \"stem\", \"bone\"). Full organisms receive the part of \"Whole\".", + "type":"string", + "cvDomain":"PART", + "priority":"x", + "suggest":null + }, + "Structurally Diverse Partlocation":{ + "lucenePath":"root_structurallyDiverse_partLocation", + "description":"The location of the part(s), if necessary to explain the context of the part.", + "type":"string", + "cvDomain":"PART_LOCATION", + "priority":null, + "suggest":null + }, + "Structurally Diverse UUID":{ + "lucenePath":"root_structurallyDiverse_uuid", + "description":"A Universally Unique Identifier (also sometimes called a GUID) for the element. Most GSRS elements have a uuid to be used for tracking, processing and referencing a record (and any element of that record).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structure Access":{ + "lucenePath":"root_structure_access", + "description":"Marking the record or element as belonging to certain groups for use in tracking which groups should receive visibility, export and\/or inclusion in specific reports.", + "type":"string", + "cvDomain":"ACCESS_GROUP", + "priority":null, + "suggest":null + }, + "Structure Charge":{ + "lucenePath":"root_structure_charge", + "description":"The net charge of the structure.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structure Count":{ + "lucenePath":"root_structure_count", + "description":"The exact number of times that the structure is repeated.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structure Created By":{ + "lucenePath":"root_structure_createdBy", + "description":"A string representing a user or entity which originally created the record (or element).", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structure Created Date":{ + "lucenePath":"root_structure_created", + "description":"A UNIX timestamp marking when the record (or element) was created (or sometimes exported) in its original form.", + "type":"timestamp", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structure Defined Stereo":{ + "lucenePath":"root_structure_definedStereo", + "description":"A count of defined stereocenters.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structure Deprecated":{ + "lucenePath":"root_structure_deprecated", + "description":"A boolean marking for whether the element or record is considered deprecated.", + "type":"boolean", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structure Digest":{ + "lucenePath":"root_structure_digest", + "description":"A hash of the raw structural information encoded.", + "type":"string", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structure Ez Centers":{ + "lucenePath":"root_structure_ezCenters", + "description":"A count of E\/Z, or absolute double bond stereochemistry centers.", + "type":"number", + "cvDomain":null, + "priority":null, + "suggest":null + }, + "Structure Has Atropisomerism":{ + "lucenePath":"root_structure_atropisomerism", + "description":"Whether or not the record has atropisomerism, or has a chirality where a hindered rotation about a single bond as a resolt of steric or electronic constraints.", + "type":"string", + "cvDomain":"ATROPISOMERISM", + "priority":null, + "suggest":null + }, + "Structure InChIKey":{ + "lucenePath":"root_structure_inchikey", + "description":"The InChIKey associated with the chemical structure.", + "type":"string", + "cvDomain":null, + "priority":"x", + "suggest":null + }, + "Substance Class":{ + "lucenePath":"root_substanceClass", + "description":"This value wall always be \"reference\" if it is intended as a real reference and \"mention\" if it is a place-holder. However, it can also specify a full substance class, in which case the whole record reference object is meant to be a full entire record rather than a reference.", + "type":"string", + "cvDomain":"SUBSTANCE_CLASS", + "priority":"x", + "suggest":null } } \ No newline at end of file diff --git a/src/app/core/assets/html/privacy-statement.html b/src/app/core/assets/html/privacy-statement.html new file mode 100644 index 000000000..bdedcc083 --- /dev/null +++ b/src/app/core/assets/html/privacy-statement.html @@ -0,0 +1,5 @@ +

Privacy Statement

+ +

Content line 1.

+ +

Content line 2.

\ No newline at end of file diff --git a/src/app/core/assets/icons/baseline-chevron_left-24px.svg b/src/app/core/assets/icons/baseline-chevron_left-24px.svg new file mode 100644 index 000000000..56c2d1f46 --- /dev/null +++ b/src/app/core/assets/icons/baseline-chevron_left-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/core/assets/icons/baseline-glasses-24px.svg b/src/app/core/assets/icons/baseline-glasses-24px.svg new file mode 100644 index 000000000..960bdd89e --- /dev/null +++ b/src/app/core/assets/icons/baseline-glasses-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/core/assets/icons/baseline-paste-24px.svg b/src/app/core/assets/icons/baseline-paste-24px.svg new file mode 100644 index 000000000..4749bbf83 --- /dev/null +++ b/src/app/core/assets/icons/baseline-paste-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/core/assets/icons/chevron_left.svg b/src/app/core/assets/icons/chevron_left.svg new file mode 100644 index 000000000..51f6a6178 --- /dev/null +++ b/src/app/core/assets/icons/chevron_left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/core/assets/icons/glasses.svg b/src/app/core/assets/icons/glasses.svg new file mode 100644 index 000000000..19c91f9e3 --- /dev/null +++ b/src/app/core/assets/icons/glasses.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/core/assets/icons/help-outline-24px.svg b/src/app/core/assets/icons/help-outline-24px.svg new file mode 100644 index 000000000..d2a8d0217 --- /dev/null +++ b/src/app/core/assets/icons/help-outline-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/core/assets/images/gsrs-logo.svg b/src/app/core/assets/images/gsrs-logo.svg index d0cd34810..1c23a5b06 100644 --- a/src/app/core/assets/images/gsrs-logo.svg +++ b/src/app/core/assets/images/gsrs-logo.svg @@ -1,50 +1,105 @@ - - - -
+
+
+ +
+
diff --git a/src/app/core/auth/login/login.component.scss b/src/app/core/auth/login/login.component.scss index 57101101d..324574730 100644 --- a/src/app/core/auth/login/login.component.scss +++ b/src/app/core/auth/login/login.component.scss @@ -1,6 +1,6 @@ .login-container { width: 100%; - display: flex; + //display: flex; align-items: center; justify-content: center; padding: 86px 20px 20px 20px; @@ -9,7 +9,8 @@ .mat-card { max-width: 400px; width: 100%; - box-sizing: border-box; + //box-sizing: border-box; + //position: absolute; } .login-form { @@ -23,3 +24,14 @@ display: flex; justify-content: flex-end; } +.reg-link> button { + padding: 10px 15px; + border: 1px solid var(--primary-color); + background-color: var(--primary-color); + color: var(--regular-white-color); + border-radius: 5px; +} +.button-div { + margin: auto; + text-align: center; +} diff --git a/src/app/core/auth/login/login.component.ts b/src/app/core/auth/login/login.component.ts index daba624d2..51ab02016 100644 --- a/src/app/core/auth/login/login.component.ts +++ b/src/app/core/auth/login/login.component.ts @@ -1,11 +1,18 @@ -import { Component, OnInit, OnDestroy, HostListener } from '@angular/core'; +import { Component, OnInit, OnDestroy, HostListener, Inject } from '@angular/core'; import { AuthService } from '../auth.service'; +import { ConfigService } from '@gsrs-core/config/config.service'; import { FormGroup, FormControl, Validators } from '@angular/forms'; import { Router, ActivatedRoute } from '@angular/router'; import { LoadingService } from '../../loading/loading.service'; import { MainNotificationService } from '../../main-notification/main-notification.service'; import { AppNotification, NotificationType } from '../../main-notification/notification.model'; +import {MatDialog, MatDialogConfig} from "@angular/material/dialog"; import { Subscription } from 'rxjs'; +import { RegisterComponent } from '../../register/register.component'; +import { DOCUMENT } from '@angular/common'; +import { LoadedComponents } from '@gsrs-core/config' +import * as _ from 'lodash'; +import {sprintf} from "sprintf-js"; @Component({ selector: 'app-login', @@ -15,22 +22,46 @@ import { Subscription } from 'rxjs'; export class LoginComponent implements OnInit, OnDestroy { isLoaded = false; isLoading = true; + loadedComponents: LoadedComponents; loginForm = new FormGroup({ username: new FormControl('', Validators.required), password: new FormControl('', Validators.required) }); private subscriptions: Array = []; + private newuserinfo = {}; + private emailFormUserRegConf: any = undefined; + public emailFormUserRegActive: boolean = false; constructor( private authService: AuthService, + private configService: ConfigService, private router: Router, private loadingService: LoadingService, private mainNotificationService: MainNotificationService, - private activatedRoute: ActivatedRoute + private activatedRoute: ActivatedRoute, + private dialog: MatDialog, + @Inject(DOCUMENT) private document: Document ) { } + @HostListener('keyup', ['$event']) + onKeyup(event: KeyboardEvent) { + if (event.code && event.code.toLowerCase() === 'enter') { + this.login(); + } + } + ngOnInit() { this.loadingService.setLoading(true); + // As we develop more types of userRegistration, make a utility service to provide configuration information. + this.loadedComponents = this.configService.configData.loadedComponents || null; + if(typeof this.configService.configData.userRegistration !== "undefined" ) { + this.emailFormUserRegConf = + this.configService.configData.userRegistration.configurations?.emailForm; + if(typeof this.emailFormUserRegConf !== "undefined" ) { + this.emailFormUserRegActive = this.emailFormUserRegConf?.active; + } + } + const subscription = this.authService.getAuth().subscribe(auth => { this.loadingService.setLoading(false); if (auth) { @@ -54,13 +85,6 @@ export class LoginComponent implements OnInit, OnDestroy { }); } - @HostListener('keyup', ['$event']) - onKeyup(event: KeyboardEvent) { - if (event.code && event.code.toLowerCase() === 'enter') { - this.login(); - } - } - login() { if (this.loginForm.valid) { this.loadingService.setLoading(true); @@ -90,4 +114,51 @@ export class LoginComponent implements OnInit, OnDestroy { } } + register() { + const dialogConfig = new MatDialogConfig(); + dialogConfig.disableClose = true; + dialogConfig.autoFocus = true; + const dialogRef = this.dialog.open(RegisterComponent, dialogConfig); + const c = this.emailFormUserRegConf; + const confOk =( + c && + c.userAdminRecipientEmails && + c.instanceApplicationName && + c.instanceApplicationSubjectTag && + c.registrationAccessEmailSubjectTemplate && + c.registrationAccessEmailBodyTemplate + ); + if (!confOk) { + alert("ERROR: the 'emailForm' registration configuration is missing values."); + return; + } + + dialogRef.afterClosed().subscribe( + data => { + if(data) { + this.newuserinfo = data; + const recipients = c.userAdminRecipientEmails.join(","); + let text = sprintf(c.registrationAccessEmailBodyTemplate, c.instanceApplicationName, data.username, data.email); + const linefeed = "%0D%0A"; + text = text.replace(/\n/g, linefeed); + const subject = sprintf(c.registrationAccessEmailSubjectTemplate, c.instanceApplicationSubjectTag); + this.document.location = "mailto:"+recipients +"?subject=" + (subject) + "&body=" + (text); + console.log('Generating mailto email ...'); + } else { + console.log('Request registration form closed without data submission.'); + } + } + ); + } + + /* + callPwdPage(type: string) { + const dialogConfig = new MatDialogConfig(); + dialogConfig.disableClose = true; + dialogConfig.autoFocus = true; + dialogConfig.data = type; + const dialogRef = this.dialog.open(PwdRecoveryComponent, dialogConfig); + dialogRef.afterClosed().subscribe(); + } + */ } diff --git a/src/app/core/auth/user-downloads/download-monitor/download-monitor.component.html b/src/app/core/auth/user-downloads/download-monitor/download-monitor.component.html index 09a3645ff..065e72d03 100644 --- a/src/app/core/auth/user-downloads/download-monitor/download-monitor.component.html +++ b/src/app/core/auth/user-downloads/download-monitor/download-monitor.component.html @@ -3,10 +3,10 @@
{{download.displayFilename}}
- Status: + Status: {{download.status}}
-
+
@@ -33,13 +33,13 @@
Export complete
- +
- +
- + - +
- Started: + Started: {{download.startedHuman}}
- Ended: + Ended: {{download.finishedHuman}}
-
+
+ diff --git a/src/app/core/auth/user-downloads/download-monitor/download-monitor.component.scss b/src/app/core/auth/user-downloads/download-monitor/download-monitor.component.scss index 5d7238480..832e03892 100644 --- a/src/app/core/auth/user-downloads/download-monitor/download-monitor.component.scss +++ b/src/app/core/auth/user-downloads/download-monitor/download-monitor.component.scss @@ -152,7 +152,7 @@ .query-link { text-decoration:none; - color: #1565C0; + color: var(--link-color); } .middle-button { @@ -182,4 +182,4 @@ .facet-container { display: flex; flex-direction: column; -} \ No newline at end of file +} diff --git a/src/app/core/auth/user-downloads/download-monitor/download-monitor.component.ts b/src/app/core/auth/user-downloads/download-monitor/download-monitor.component.ts index a79b4c863..db1a84d34 100644 --- a/src/app/core/auth/user-downloads/download-monitor/download-monitor.component.ts +++ b/src/app/core/auth/user-downloads/download-monitor/download-monitor.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { Component, OnInit, Input, Output, EventEmitter, OnDestroy } from '@angular/core'; import { AuthService } from '@gsrs-core/auth/auth.service'; import * as moment from 'moment'; import { take } from 'rxjs/operators'; @@ -10,7 +10,7 @@ import { NavigationExtras } from '@angular/router'; templateUrl: './download-monitor.component.html', styleUrls: ['./download-monitor.component.scss'] }) -export class DownloadMonitorComponent implements OnInit { +export class DownloadMonitorComponent implements OnInit, OnDestroy { @Input() id: string; @Input() fromRoute?: boolean; @Output() deletedEmitter = new EventEmitter(); @@ -22,6 +22,7 @@ export class DownloadMonitorComponent implements OnInit { facetArray = []; displayOrder: string; type?: string; + killed = false; constructor( private authService: AuthService ) { } @@ -30,28 +31,39 @@ export class DownloadMonitorComponent implements OnInit { this.refresh(); } - refresh(spawn?: boolean) { - this.authService.getUpdateStatus(this.id).pipe(take(1)).subscribe( response => { - this.download = response; - if (response.originalQuery) { - this.processQuery(response.originalQuery); - } + refresh(stop?: boolean) { + if (!stop) { + this.authService.getUpdateStatus(this.id).pipe(take(1)).subscribe(response => { + // console.log((this.exists? this.exists : 't') + '---' + this.download.status); + this.download = response; + if (response.originalQuery) { + this.processQuery(response.originalQuery); + } - this.exists = true; - if (this.download.started) { - this.download.startedHuman = moment(this.download.started).fromNow(); - } - if (this.download.finished) { - this.download.finishedHuman = moment(this.download.finished).fromNow(); - } + this.exists = true; + if (this.download.started) { + this.download.startedHuman = moment(this.download.started).fromNow(); + } + if (this.download.finished) { + this.download.finishedHuman = moment(this.download.finished).fromNow(); + } if (this.download.status === 'RUNNING' || this.download.status === 'PREPARING' || this.download.status === 'INITIALIZED') { - setTimeout(() => { - this.refresh(true); - }, 400); + if (!this.killed) { + setTimeout(() => { + this.refresh(); + }, 1400); + } } - }, error => { - this.exists = false; - }); + }, error => { + this.exists = false; + }); + } + } + + ngOnDestroy() { + this.killed = true; + this.exists = false; + this.refresh(true); } cancel() { @@ -68,62 +80,65 @@ export class DownloadMonitorComponent implements OnInit { deleteDownload() { this.authService.deleteDownload(this.download.removeUrl.url).pipe(take(1)).subscribe(response => { - this.deleted = true; + this.deleted = true; }); } processQuery(url: string) { if (url.indexOf('status(') < 0) { this.browseLink = true; - if (url.indexOf('v1/productmainall') > 0) { + if (url.indexOf('v1/productsall') > 0) { this.type = 'product'; - } else if (url.indexOf('v1/applicationssrs') > 0) { + } else if ((url.indexOf('v1/applications') > 0) || (url.indexOf('v1/applicationsall') > 0)) { this.type = 'application'; + } else if + ((url.indexOf('v1/adverseeventpt') > 0) || (url.indexOf('v1/adverseeventdme') > 0) || (url.indexOf('v1/adverseeventcvm') > 0)) { + this.type = 'adverseevent'; } else { this.type = 'browse'; } url = url.split('?')[1]; - const urlParams = new URLSearchParams(url); - this.facetArray = []; - const facets = urlParams.getAll('facet'); - facets.forEach(str => { + const urlParams = new URLSearchParams(url); + this.facetArray = []; + const facets = urlParams.getAll('facet'); + facets.forEach(str => { const facet = str.split('/'); let bool = 'true'; - if ( facet[0].indexOf('!') > -1) { + if (facet[0].indexOf('!') > -1) { bool = 'false'; facet[0] = facet[0].slice(1, facet[0].length); } - let exists = false; - facet[1] = encodeURIComponent(facet[1]); - const value = facet[1] + '.' + bool; - this.facetArray.forEach(entry => { - if (entry.facet === facet[0]) { - // build valuestring for queryParams and values for template display - entry.valueString = entry.valueString + '+' + value; - entry.values.push(bool === 'false' ? 'NOT ' + facet[1] : facet[1]); - exists = true; - } - }); - if (exists === false) { - this.facetArray.push( - {'facet': facet[0], 'valueString': value, 'values': [bool === 'false' ? 'NOT ' + facet[1] : facet[1] ]} - ); + let exists = false; + facet[1] = encodeURIComponent(facet[1]); + const value = facet[1] + '.' + bool; + this.facetArray.forEach(entry => { + if (entry.facet === facet[0]) { + // build valuestring for queryParams and values for template display + entry.valueString = entry.valueString + '+' + value; + entry.values.push(bool === 'false' ? 'NOT ' + facet[1] : facet[1]); + exists = true; } - }); + }); + if (exists === false) { + this.facetArray.push( + { facet: facet[0], valueString: value, values: [bool === 'false' ? 'NOT ' + facet[1] : facet[1]] } + ); + } + }); if (urlParams.has('q')) { this.parameters['search'] = urlParams.get('q'); } if (urlParams.has('order')) { - this.parameters['order'] = urlParams.get('order'); + this.parameters['order'] = urlParams.get('order'); let order = urlParams.get('order'); - if ( order.charAt(0) === '$') { + if (order.charAt(0) === '$') { order = order.slice(1, order.length); order = order.replace('root_', '') + ' - descending'; } - if ( order.charAt(0) === '^') { + if (order.charAt(0) === '^') { order = order.slice(1, order.length); order = order.replace('root_', '') + ' - ascending'; } @@ -132,7 +147,7 @@ export class DownloadMonitorComponent implements OnInit { if (this.facetArray.length > 0) { let facetVal = ''; - for (let i = 0; i < this.facetArray.length; i ++) { + for (let i = 0; i < this.facetArray.length; i++) { const object = this.facetArray[i]; facetVal += object.facet + '*' + object.valueString; if (i < (this.facetArray.length - 1)) { @@ -141,7 +156,7 @@ export class DownloadMonitorComponent implements OnInit { } this.parameters['facets'] = facetVal; } + } } -} } diff --git a/src/app/core/auth/user-downloads/user-downloads.component.scss b/src/app/core/auth/user-downloads/user-downloads.component.scss index ae2cd3f2a..dbebd1ac0 100644 --- a/src/app/core/auth/user-downloads/user-downloads.component.scss +++ b/src/app/core/auth/user-downloads/user-downloads.component.scss @@ -16,7 +16,7 @@ .job-container { padding:10px; margin: 20px; - border: 1px solid rgba(0, 0, 0, .1); + border: 1px solid var(--border-color); } @@ -49,5 +49,5 @@ .a-link { text-decoration:none; - color: #1565C0; - } \ No newline at end of file + color: var(--link-color); + } diff --git a/src/app/core/auth/user-profile/user-profile.component.html b/src/app/core/auth/user-profile/user-profile.component.html index ca0bce3ae..bef085047 100644 --- a/src/app/core/auth/user-profile/user-profile.component.html +++ b/src/app/core/auth/user-profile/user-profile.component.html @@ -1,90 +1,94 @@
-

User Profile

-
- {{ user.user.username}} -
-
- -
- - -
-
-
Email:
-
{{user.user.email}}
-
-
-
- Roles: -
-
- - {{role}}{{!islast? ', ':''}} - -
-
-
-
- Groups: -
-
- - {{group.name}}{{!islast? ', ':''}} - -
+

User Profile

+
+ {{ user.user.username}}
- - - -
+
+ +
-
-
- -
- - + +
+
+
Email:
+
{{user.user.email}}
+
+
+
+ Roles: +
+
+ + {{role}}{{!islast? ', ':''}} + +
-
- -
- - - +
+
+ Groups: +
+
+ + {{group.name}}{{!islast? ', ':''}} +
-
- - - +
+ + + +
+
+
+
+ +
+ +
+
+
- - - + + +
- -
- -
-
-
+ + + +
+
+ + + +
+ + +
+ +
+ +
+
+
+ {{this.message}} +
-
- {{this.message}} -
- - - - - - \ No newline at end of file + + + + + + + \ No newline at end of file diff --git a/src/app/core/auth/user-profile/user-profile.component.ts b/src/app/core/auth/user-profile/user-profile.component.ts index 7fd301ae5..888289017 100644 --- a/src/app/core/auth/user-profile/user-profile.component.ts +++ b/src/app/core/auth/user-profile/user-profile.component.ts @@ -5,7 +5,9 @@ import { AdminService } from '@gsrs-core/admin/admin.service'; import { take } from 'rxjs/operators'; import { isString } from 'util'; import { Router } from '@angular/router'; -import { MatDialogRef } from '@angular/material'; +import { MatDialogRef, MatDialog } from '@angular/material/dialog'; +import { SubstanceDraftsComponent } from '@gsrs-core/substance-form/substance-drafts/substance-drafts.component'; +import { ConfigService } from '@gsrs-core/config'; @Component({ selector: 'app-user-profile', @@ -13,6 +15,17 @@ import { MatDialogRef } from '@angular/material'; styleUrls: ['./user-profile.component.scss'] }) export class UserProfileComponent implements OnInit { + + /* + + To prevent the change password button from showing add this to the configuration + + "userProfile": { + "showChangeUserPasswordButton": false + }, + + */ + user: Auth; newPassword = ''; oldPassword = ''; @@ -20,24 +33,36 @@ export class UserProfileComponent implements OnInit { changePassword = false; message = ''; loading = false; + showChangeUserPasswordButton: boolean; + constructor( + public configService: ConfigService, private authService: AuthService, private adminService: AdminService, private router: Router, public dialogRef: MatDialogRef, + private dialog: MatDialog, + ) { } ngOnInit() { + this.showChangeUserPasswordButton = this.configService.configData?.userProfile?.showChangeUserPasswordButton; + if (!this.showChangeUserPasswordButton && this.showChangeUserPasswordButton===false) { + this.showChangeUserPasswordButton=false; + } else { + this.showChangeUserPasswordButton=true; + } this.authService.getAuth().pipe(take(1)).subscribe( response => { this.user = response; }); } viewDownloads(): void { - this.router.navigate(['/user-downloads']); setTimeout(() => { this.dialogRef.close(); - }, 400); + }); + this.router.navigate(['/user-downloads']); + } validatePassword(): void { @@ -70,4 +95,39 @@ export class UserProfileComponent implements OnInit { } } + +close() { + this.dialog.closeAll(); +} + +viewDrafts(): void { + const dialogRef = this.dialog.open(SubstanceDraftsComponent, { + maxHeight: '85%', + width: '70%', + data: {profile: true} + }); + // this.overlayContainer.style.zIndex = '1002'; + + dialogRef.afterClosed().subscribe(response => { + // this.overlayContainer.style.zIndex = null; + + + /* if (response) { + + const read = response.substance; + if (response.uuid && response.uuid != 'register'){ + const url = '/substances/' + response.uuid + '/edit?action=import'; + this.router.navigateByUrl(url, { state: { record: response.substance } }); + } else { + setTimeout(() => { + // this.overlayContainer.style.zIndex = null; + this.router.onSameUrlNavigation = 'reload'; + this.router.navigateByUrl('/substances/register?action=import', { state: { record: response.json } }); + + }, 1000); + } + }*/ + + }); +} } diff --git a/src/app/core/base/base.component.html b/src/app/core/base/base.component.html index 387904ffc..0a89e8a48 100644 --- a/src/app/core/base/base.component.html +++ b/src/app/core/base/base.component.html @@ -1,4 +1,4 @@ - + + + +
+ + + + +
+ + + + + + +
- -
- - - Classic Site - - +
diff --git a/src/app/core/base/base.component.scss b/src/app/core/base/base.component.scss index 60264cfbf..8dca2150c 100644 --- a/src/app/core/base/base.component.scss +++ b/src/app/core/base/base.component.scss @@ -1,7 +1,7 @@ @import '../../../styles/variables'; .version { - color: #182c4d; + color: var(--version-text-color); font-size: 10pt; line-height: 10px; text-align: right; @@ -31,8 +31,9 @@ width: 25px !important; } - -.main-button { +.top-button { + padding-top: 10px; + padding-bottom: 10px; } .user-button { margin-bottom:-5px; @@ -107,7 +108,6 @@ } - .mat-toolbar { position: fixed; top: 0; @@ -200,8 +200,60 @@ // } // } + + @media(min-width: $nav-breaking-point) { .top-search { min-width: 500px; } + .menu-button :hover { + background-color: rgb(71, 147, 209) !important; + } +} +.extra-item { + display: inline-block; } + +// hides extra browse (for half 1920 to save space) +@media(max-width: 1500px ) { + .extra-browse { + display: none !important; + } +} + +// hides all but main menu +@media(max-width: 1350px ) { + .extra-item { + display: none !important; + } +} + + + + @media(max-width: 1610px ) { + .add-at-low-res{ + display: inline !important; + } + + .hide-at-low-res{ + display: none !important; + } + } + + @media(min-width: 1610px ) { + .add-at-low-res{ + display: none !important; + } + + .hide-at-low-res{ + display: inline-block; + } + } + + @media(max-width: 1650px ) { + .extra-item-2 { + display: none !important; + } + } + + diff --git a/src/app/core/base/base.component.ts b/src/app/core/base/base.component.ts index 3b603ff72..68e2ba44e 100644 --- a/src/app/core/base/base.component.ts +++ b/src/app/core/base/base.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, ViewEncapsulation, HostListener, OnDestroy } from '@angular/core'; -import { Router, RouterEvent, NavigationEnd, NavigationExtras, ActivatedRoute, NavigationStart, ResolveEnd, ParamMap } from '@angular/router'; +import { Router, RouterEvent, NavigationExtras, ActivatedRoute, NavigationStart, ResolveEnd, ParamMap } from '@angular/router'; import { Environment } from '../../../environments/environment.model'; import { AuthService } from '../auth/auth.service'; import { Auth } from '../auth/auth.model'; @@ -8,15 +8,21 @@ import { ConfigService } from '../config/config.service'; import { OverlayContainer } from '@angular/cdk/overlay'; import { LoadingService } from '../loading/loading.service'; import { HighlightedSearchActionComponent } from '../highlighted-search-action/highlighted-search-action.component'; -import { MatBottomSheet, MatBottomSheetRef, MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; +import { MatBottomSheet, MatBottomSheetRef } from '@angular/material/bottom-sheet'; import { Observable, Subscription } from 'rxjs'; import { UserProfileComponent } from '@gsrs-core/auth/user-profile/user-profile.component'; import { SubstanceTextSearchService } from '@gsrs-core/substance-text-search/substance-text-search.service'; -import { NavItem } from '../config/config.model'; +import { NavItem, LoadedComponents } from '../config/config.model'; import { UtilsService } from '@gsrs-core/utils'; import { take } from 'rxjs/operators'; import * as moment from 'moment'; import { SubstanceEditImportDialogComponent } from '@gsrs-core/substance-edit-import-dialog/substance-edit-import-dialog.component'; +import { WildcardService } from '@gsrs-core/utils/wildcard.service'; +import { SubstanceDraftsComponent } from '@gsrs-core/substance-form/substance-drafts/substance-drafts.component'; +import {sprintf} from "sprintf-js"; +import { BulkSearchService } from '@gsrs-core/bulk-search/service/bulk-search.service'; +import { UserQueryListDialogComponent } from '@gsrs-core/bulk-search/user-query-list-dialog/user-query-list-dialog.component'; @Component({ selector: 'app-base', @@ -30,16 +36,9 @@ export class BaseComponent implements OnInit, OnDestroy { auth?: Auth; environment: Environment; searchValue: string; - private overlayContainer: HTMLElement; - private bottomSheetOpenTimer: any; - private bottomSheetRef: MatBottomSheetRef; - private bottomSheetCloseTimer: any; - private selectedText: string; - private subscriptions: Array = []; baseDomain: string; classicLinkPath: string; classicLinkQueryParamsString: string; - private classicLinkQueryParams = {}; isAdmin = false; contactEmail: string; version?: string; @@ -48,6 +47,20 @@ export class BaseComponent implements OnInit, OnDestroy { clasicBaseHref: string; navItems: Array; customToolbarComponent: string = ''; + canRegister = false; + registerNav: Array; + searchNav: Array; + adverseEventShinyHomepageDisplay = false; + loadedComponents: LoadedComponents; + private overlayContainer: HTMLElement; + private bottomSheetOpenTimer: any; + private bottomSheetRef: MatBottomSheetRef; + private bottomSheetCloseTimer: any; + private selectedText: string; + private subscriptions: Array = []; + private wildCardText: string; + private classicLinkQueryParams = {}; + showHeaderBar = 'true'; constructor( private router: Router, @@ -60,22 +73,112 @@ export class BaseComponent implements OnInit, OnDestroy { private dialog: MatDialog, private substanceTextSearchService: SubstanceTextSearchService, private utilsService: UtilsService, + private wildCardService: WildcardService ) { - this.classicLinkPath = this.configService.environment.clasicBaseHref; - this.classicLinkQueryParamsString = ''; - this.contactEmail = this.configService.configData.contactEmail; - this.clasicBaseHref = this.configService.environment.clasicBaseHref; - this.navItems = this.configService.configData.navItems; - this.customToolbarComponent = this.configService.configData.customToolbarComponent; + + this.wildCardService.wildCardObservable.subscribe((data) => { + this.wildCardText = data; + }); + } + + @HostListener('document:mouseup', ['$event']) + @HostListener('document:keyup', ['$event']) + // @HostListener('document:selectionchange', ['$event']) + onKeyUp(event: Event) { + let text = ''; + let selection: Selection; + let range: Range; + let selectionStart: number; + let selectionEnd: number; + const activeEl: HTMLInputElement = document.activeElement as HTMLInputElement; + + if (activeEl != null) { + const activeElTagName = activeEl ? activeEl.tagName.toLowerCase() : null; + if ( + (activeElTagName === 'textarea') || (activeElTagName === 'input' && + /^(?:text|search|password|tel|url)$/i.test(activeEl.type)) && + (typeof activeEl.selectionStart === 'number') + ) { + selectionStart = activeEl.selectionStart; + selectionEnd = activeEl.selectionEnd; + text = activeEl.value.slice(selectionStart, selectionEnd); + } else if (window.getSelection) { + selection = window.getSelection(); + // ###### why just chrome? + if (selection.rangeCount > 0) { + range = selection.getRangeAt(0); + } + text = selection.toString(); + } + + clearTimeout(this.bottomSheetOpenTimer); + + if (text && text !== this.selectedText) { + this.selectedText = text; + /* this.bottomSheetOpenTimer = setTimeout(() => { + const subscription = this.openSearchBottomSheet(text).subscribe(() => { + setTimeout(() => { + if (selection != null && range != null) { + selection.removeAllRanges(); + selection.addRange(range); + } else if (selectionStart != null) { + activeEl.focus(); + activeEl.selectionStart = selectionStart; + activeEl.selectionEnd = selectionEnd; + } + }); + subscription.unsubscribe(); + }, () => { + subscription.unsubscribe(); + }, () => { + this.selectedText = ''; + subscription.unsubscribe(); + }); + }, 600);*/ + } + } } ngOnInit() { + this.showHeaderBar = this.activatedRoute.snapshot.queryParams['header'] || 'true'; + this.loadedComponents = this.configService.configData.loadedComponents || null; + + this.classicLinkPath = this.configService.environment.clasicBaseHref; + this.clasicBaseHref = this.configService.environment.clasicBaseHref; + this.classicLinkQueryParamsString = ''; + this.contactEmail = this.configService.configData.contactEmail || null; + this.navItems = this.configService.configData.navItems || null; + + let notempty = false; + if (this.loadedComponents) { + if (this.loadedComponents.applications) { + notempty = true; + } else if (this.loadedComponents.clinicaltrials) { + notempty = true; + } else if (this.loadedComponents.adverseevents) { + notempty = true; + } else if (this.loadedComponents.impurities) { + notempty = true; + } else if (this.loadedComponents.products) { + notempty = true; + } else if (this.loadedComponents.ssg4m) { + notempty = true; + } + if (!notempty) { + this.loadedComponents = null; + } + } const roleSubscription = this.authService.hasRolesAsync('Admin').subscribe(response => { this.isAdmin = response; }); this.subscriptions.push(roleSubscription); + const regSubscription = + this.authService.hasAnyRolesAsync('Admin', 'Updater', 'SuperUpdater', 'DataEntry', 'SuperDataEntry').subscribe(response => { + this.canRegister = response; + }); + this.subscriptions.push(regSubscription); this.baseDomain = this.configService.configData.apiUrlDomain; this.utilsService.getBuildInfo().pipe(take(1)).subscribe(buildInfo => { @@ -83,7 +186,33 @@ export class BaseComponent implements OnInit, OnDestroy { this.versionTooltipMessage = `V${this.version}`; this.versionTooltipMessage += ` built on ${moment(buildInfo.buildTime).utc().format('ddd MMM D YYYY HH:mm:SS z')}`; }); + this.navItems.forEach(item => { + if (item.display === 'Register') { + this.registerNav = item.children; + } + if (item.display === 'Search') { + this.searchNav = item.children; + } + }); + if (this.loadedComponents) { + for(let i = this.navItems.length - 1; i >= 0; i--) { + if (this.navItems[i].children) { + for (let j = this.navItems[i].children.length - 1; j >= 0; j--) { + if (this.navItems[i].children[j].component) { + if (!this.loadedComponents[this.navItems[i].children[j].component]) { + this.navItems[i].children.splice(j, 1); + } + } + } + } + if (this.navItems[i].component) { + if (!this.loadedComponents[this.navItems[i].component]) { + this.navItems.splice(i, 1); + } + } + } +} this.overlayContainer = this.overlayContainerService.getContainerElement(); let urlPath = this.router.routerState.snapshot.url.split('?')[0]; @@ -100,15 +229,26 @@ export class BaseComponent implements OnInit, OnDestroy { }); this.subscriptions.push(paramsSubscription); + const authSubscription2 = this.authService.checkAuth().subscribe(auth => { + }, error => { + if (error.status === 403 && (this.router.url.split('?')[0] !== '/login' && this.router.url.split('?')[0] !== '/unauthorized')) { + this.loadingService.setLoading(false); + this.router.navigate(['/unauthorized']); + } + }); + this.subscriptions.push(authSubscription2); + const authSubscription = this.authService.getAuth().subscribe(auth => { this.auth = auth; + }, error => { }); + this.subscriptions.push(authSubscription); this.environment = this.configService.environment; this.appId = this.environment.appId; - this.logoSrcPath = `${this.environment.baseHref || '/'}assets/images/gsrs-logo.svg`; + this.logoSrcPath = `${this.environment.baseHref || ''}assets/images/gsrs-logo.svg`; const routerSubscription = this.router.events.subscribe((event: RouterEvent) => { if (event instanceof ResolveEnd) { @@ -160,15 +300,15 @@ export class BaseComponent implements OnInit, OnDestroy { } processSubstanceSearch(searchValue: string) { + this.wildCardService.getTopSearchBoxText(searchValue); this.navigateToSearchResults(searchValue); } navigateToSearchResults(searchTerm: string) { const navigationExtras: NavigationExtras = { - queryParams: searchTerm ? { 'search': searchTerm } : null + queryParams: searchTerm ? { search: searchTerm } : null }; - this.router.navigate(['/browse-substance'], navigationExtras); } @@ -180,64 +320,6 @@ export class BaseComponent implements OnInit, OnDestroy { this.overlayContainer.style.zIndex = null; } - @HostListener('document:mouseup', ['$event']) - @HostListener('document:keyup', ['$event']) - // @HostListener('document:selectionchange', ['$event']) - onKeyUp(event: Event) { - let text = ''; - let selection: Selection; - let range: Range; - let selectionStart: number; - let selectionEnd: number; - const activeEl: HTMLInputElement = document.activeElement as HTMLInputElement; - - if (activeEl != null) { - const activeElTagName = activeEl ? activeEl.tagName.toLowerCase() : null; - if ( - (activeElTagName === 'textarea') || (activeElTagName === 'input' && - /^(?:text|search|password|tel|url)$/i.test(activeEl.type)) && - (typeof activeEl.selectionStart === 'number') - ) { - selectionStart = activeEl.selectionStart; - selectionEnd = activeEl.selectionEnd; - text = activeEl.value.slice(selectionStart, selectionEnd); - } else if (window.getSelection) { - selection = window.getSelection(); - // ###### why just chrome? - if (selection.rangeCount > 0) { - range = selection.getRangeAt(0); - } - text = selection.toString(); - } - - clearTimeout(this.bottomSheetOpenTimer); - - if (text && text !== this.selectedText) { - this.selectedText = text; - /* this.bottomSheetOpenTimer = setTimeout(() => { - const subscription = this.openSearchBottomSheet(text).subscribe(() => { - setTimeout(() => { - if (selection != null && range != null) { - selection.removeAllRanges(); - selection.addRange(range); - } else if (selectionStart != null) { - activeEl.focus(); - activeEl.selectionStart = selectionStart; - activeEl.selectionEnd = selectionEnd; - } - }); - subscription.unsubscribe(); - }, () => { - subscription.unsubscribe(); - }, () => { - this.selectedText = ''; - subscription.unsubscribe(); - }); - }, 600);*/ - } - } - } - openSearchBottomSheet(searchTerm: string): Observable { return new Observable(observer => { @@ -281,6 +363,28 @@ export class BaseComponent implements OnInit, OnDestroy { }); } + transformMailToPath(item: NavItem) { + if(item?.kind && item?.mailToPath) { + let subject =''; + let email =''; + if(item.kind==='contact-us') { + email = this.contactEmail; + } + if(item?.queryParams) { + if(item?.queryParams?.subject) { + subject = item.queryParams.subject; + } + } + const part1 = sprintf(item.mailToPath, email); + let part2 =''; + if(subject) { + part2 = 'subject='+subject; + } + return part1+'?'+part2; + } + return ''; + } + setClassicLinkPath(path: string): void { const basePath = this.clasicBaseHref; @@ -378,4 +482,72 @@ export class BaseComponent implements OnInit, OnDestroy { } + viewLists(list?: string): void { + let data = {view: 'all'}; + if (list) { + data.view = 'single'; + data['activeName'] = list.split(':')[1]; + } + const dialogRef = this.dialog.open(UserQueryListDialogComponent, { + width: '850px', + autoFocus: false, + data: data + + }); + this.overlayContainer.style.zIndex = '1002'; + + const dialogSubscription = dialogRef.afterClosed().pipe(take(1)).subscribe(response => { + if (response) { + this.overlayContainer.style.zIndex = null; + } + }); + } + + logout() { + this.authService.logout(); + setTimeout(() => { + if (this.configService.configData && this.configService.configData.logoutRedirectUrl){ + window.location.href = this.configService.configData.logoutRedirectUrl; + } else { + this.router.navigate(['/home']); + } + }, 1200); + } + + viewDrafts(): void { + const dialogRef = this.dialog.open(SubstanceDraftsComponent, { + maxHeight: '85%', + width: '70%', + data: {view: 'user'} + }); + this.overlayContainer.style.zIndex = '1002'; + + dialogRef.afterClosed().subscribe(response => { + this.overlayContainer.style.zIndex = null; + + + if (response) { + this.loadingService.setLoading(true); + // console.log(response.json); + + const read = response.substance; + + if (response.uuid && response.uuid != 'register'){ + const url = '/substances/' + response.uuid + '/edit?action=import&source=draft'; + this.router.navigateByUrl(url, { state: { record: response.substance } }); + } else { + setTimeout(() => { + // this.overlayContainer.style.zIndex = null; + this.router.onSameUrlNavigation = 'reload'; + let url = '/substances/register/' + response.substance.substanceClass + '?action=import' + this.router.navigateByUrl(url, { state: { record: response.substance } }); + + }, 500); + } + } + + + }); + } + } diff --git a/src/app/core/bulk-search/bulk-query.component.html b/src/app/core/bulk-search/bulk-query.component.html new file mode 100644 index 000000000..e75ae1606 --- /dev/null +++ b/src/app/core/bulk-search/bulk-query.component.html @@ -0,0 +1,41 @@ +
+ +

Bulk Query

+
+
+ + + Identifiers + All Fields + +
+
+ + + + + + {{entity.title}} + + + +
+
+ +
+ + +
+
+
+
+
+
+
diff --git a/src/app/core/bulk-search/bulk-query.component.scss b/src/app/core/bulk-search/bulk-query.component.scss new file mode 100644 index 000000000..13cb95c5d --- /dev/null +++ b/src/app/core/bulk-search/bulk-query.component.scss @@ -0,0 +1,66 @@ +.query-content-container { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 65px 5px 0 5px; +} + +.mat-card { + margin-top: 15px; + box-sizing: border-box; + max-width: 1170px; + overflow-y: auto; +} + +.mat-radio-group { + margin-top: 5px; + margin-bottom: 10px; +} + +.mat-radio-button { + margin-right: 10px; +} + +.editor-container { + width: 920px; + min-height: 460px; + height: 390px; +} + +.query-actions { + + button { + float: right; + display: block; + } +} + + +.query-actions { + flex-direction: row; + width: 100%; + +} +.two { + width: auto; + display: flex; + flex-direction: column; +} +.three { + width: auto; + display: flex; + flex-direction: column; +} + + +@media(max-width: 1175px) { } + +@media(max-width: 970px) { + + .editor-container, .query-actions { + width: 100%; + } +} + diff --git a/src/app/core/bulk-search/bulk-query.component.ts b/src/app/core/bulk-search/bulk-query.component.ts new file mode 100644 index 000000000..32ef2dc0a --- /dev/null +++ b/src/app/core/bulk-search/bulk-query.component.ts @@ -0,0 +1,255 @@ +import { + Component, + OnInit, + OnDestroy, + ViewChild, + AfterViewInit, +} from '@angular/core'; +import { NavigationExtras, Router, ActivatedRoute } from '@angular/router'; +import { FormControl } from '@angular/forms'; +import { Subscription, Observable } from 'rxjs'; +import { ConfigService, LoadedComponents } from '@gsrs-core/config'; +import { AppNotification, NotificationType } from '@gsrs-core/main-notification'; +import { MainNotificationService } from '@gsrs-core/main-notification'; +import { LoadingService } from '@gsrs-core/loading'; +import { TextInputFormComponent } from '@gsrs-core/utils/text-input-form/text-input-form.component'; +import { AuthService } from '../../core/auth/auth.service'; +import { BulkSearchService } from './service/bulk-search.service'; +import { BulkQuery } from './bulk-query.model'; +import { BulkSearch } from './bulk-search.model'; +import * as lodash from 'lodash'; + + @Component({ + selector: 'app-bulk-query', + templateUrl: './bulk-query.component.html', + styleUrls: ['./bulk-query.component.scss'] + }) + +export class BulkQueryComponent implements OnInit, OnDestroy, AfterViewInit { + _ = lodash; + loadedComponents: LoadedComponents; + showAudit: boolean; + isAdmin = false; + isLoggedIn = false; + showDeprecated = false; + queryText: string; + _bulkQuery: BulkQuery; + _bulkQueryIdAfterSubmit: number; + _bulkQueryIdOnLoad: number; + _bulkSearchIdOnLoad: number; + _bulkSearchKeyOnLoad: string; + _bulkSearch: BulkSearch; + _bulkSearchResults: any; + _bulkSearchResultKey: string; + searchOnIdentifiers: boolean = true; + query: string; + isError = false; + isLoading = false; + displayProperties: Array; + displayPropertiesCommon: Array; + facetViewControl = new FormControl(); + structureEditor: string; + anchorElement: HTMLAnchorElement; + smiles: string; + mol: string; + height = 0; + width = 0; + textInputFormPlaceholder = 'ASPIRIN\n50-00-0\nroot_names_name:\"^parsley\$"\nR6Q3791S76\n1cf410f9-3eeb-41ed-ab69-eeb5076901e5\n'; + // eslint-disable-next-line @typescript-eslint/member-ordering + @ViewChild(TextInputFormComponent, {static:true}) textInputForm: TextInputFormComponent; + + showSpinner = false; + navigationExtrasFacet: NavigationExtras = { + queryParams: {} + }; + searchEntityControl = new FormControl(); + searchEntities: any; + private subscriptions: Array = []; + private searchEntity: string; + + constructor( + private loadingService: LoadingService, + public authService: AuthService, + private notificationService: MainNotificationService, + private bulkSearchService: BulkSearchService, + private configService: ConfigService, + private route: ActivatedRoute, + private router: Router + ) { } + + ngOnInit() { + + this.loadingService.setLoading(true); + this.showSpinner = true; // Start progress spinner + + // Get configration values to hide/show Modules + // this.loadedComponents = this.configService.configData.loadedComponents || null; + // if (this.loadedComponents) { + // if (this.loadedComponents.applications) { + // } + // } + + this.showSpinner = false; // Stop progress spinner + this.loadingService.setLoading(false); + const bsConfig = this.configService.configData?.bulkSearch; + this.searchEntities = bsConfig?.entities; + + if (!this.searchEntities) { + this.searchEntities = [{name: 'substances', title: 'Substances'}]; + } + + const authSubscription = this.authService.getAuth().subscribe(auth => { + if (auth) { + this.isLoggedIn = true; + } else { + this.showDeprecated = false; + } + this.isAdmin = this.authService.hasAnyRoles('Updater', 'SuperUpdater'); + this.showAudit = this.authService.hasRoles('admin'); + }); + this.setSearchEntity('substances'); + this.checkBulkQueryIdParameterOnLoad(); + } + + ngOnDestroy() { + this.subscriptions.forEach(subscription => { + if (subscription) { + subscription.unsubscribe(); + } + }); + } + + ngAfterViewInit() { + + } +submitText() { + this.queryText = this.textInputForm.textControl.value; + this.postOrPutBulkQueryAndNavigateToBrowse(); +} + + bulkSearchSubmit(): void { + const eventLabel = 'Bulk search submit `${this.searchEntity}`'; + // this.gaService.sendEvent('Application Filtering', 'icon-button:bulk-search-submit', eventLabel); + } + + checkBulkQueryIdParameterOnLoad() { + // We need to get the query id from the URL query parameter, then extract the queries + // from the response. Queries come back as array, The number query terms in the + // array is determined by `top`, so we may need to process multiple responses to get + // the full list of queries for editing in the textarea. + const top = 10000; + let skip = 0; + const subscriptions = []; + + // Part A: get url query param values + subscriptions[0] = this.route.queryParams.subscribe(params => { + if(params.bulkQID !== null && params.bulkQID!==undefined) { + this._bulkQueryIdOnLoad = params.bulkQID; + + if(params.searchOnIdentifiers && params.searchOnIdentifiers === "false") { + this.searchOnIdentifiers = false; + } else { + this.searchOnIdentifiers = true; + } + this.setSearchEntity(params.searchEntity); + const observables: Array> = []; + const texts: string[] = []; + // Part B: load queries terms + subscriptions[1] = this.bulkSearchService.getBulkQuery( + this.searchEntity, + this._bulkQueryIdOnLoad, + top, + skip + ) + .subscribe((bulkQuery) => { + if(bulkQuery.queries) { + texts.push(bulkQuery.queries.join('\n')); + const left = bulkQuery.total - bulkQuery.count; + // Turning this off and using big top value for now. + const off = true; + if(!off && left>0) { + const x = Math.floor(left/top); + for (let i = 1; i <= x; i++) { + skip = skip + top; + const observable = this.bulkSearchService + .getBulkQuery(this.searchEntity, this._bulkQueryIdOnLoad, top, skip); + observables.push(observable); + const s = observable.subscribe((bulkQuery1) => { + texts.push(bulkQuery1.queries.join('\n')); + subscriptions.push(s); + }); + } + } + } + }, + (error) => { + console.log( 'Error getting bulk search status results data.'); + }, + () => { + // completed + // Join all values in the texts array and insert into form text area. + this.textInputForm.textControl.setValue(texts.join('')); + }); + // Part B End + } // if params + }, + () => { + subscriptions.forEach( + (o) => { + o.unsubscribe(); + }); + }); + // Part A (Params) end) + + } + + + postOrPutBulkQueryAndNavigateToBrowse() { + // This assumes we post/put the query and launch the search FROM the browse page. + this.loadingService.setLoading(true); + const s1 = this.bulkSearchService.postOrPutBulkQuery( + // this._bulkQueryIdOnLoad, + this.searchEntity, + this.queryText + ) + .subscribe(bulkQuery => { + this.isError = false; + this._bulkQuery = bulkQuery; + this._bulkQueryIdAfterSubmit = bulkQuery.id; + const navigationExtras: NavigationExtras = { + queryParams: { + // eslint-disable-next-line @typescript-eslint/naming-convention + bulkQID: this._bulkQueryIdAfterSubmit, + searchOnIdentifiers: this.searchOnIdentifiers, + searchEntity: this.searchEntity + } + }; + this.router.navigate(['/browse-substance'], navigationExtras); + }, error => { + console.log('Error trying to post/put a bulk query.'); + const notification: AppNotification = { + message: 'Error trying to post/put a bulk query.', + milisecondsToShow: 6000 + }; + this.isError = true; + this.isLoading = false; + this.loadingService.setLoading(this.isLoading); + this.notificationService.setNotification(notification); + }, () => { + s1.unsubscribe(); + this.isLoading = false; + this.loadingService.setLoading(this.isLoading); + } + ); + } + + setSearchEntity(value: string) { + this.searchEntity = value; + this.searchEntityControl.setValue(value); + } + + searchEntitySelected($event) { + this.setSearchEntity($event.value); + } + +} diff --git a/src/app/core/bulk-search/bulk-query.model.ts b/src/app/core/bulk-search/bulk-query.model.ts new file mode 100644 index 000000000..d781652f2 --- /dev/null +++ b/src/app/core/bulk-search/bulk-query.model.ts @@ -0,0 +1,8 @@ +export interface BulkQuery { + id: number; + total: number; + count: number; + top: number; + skip: number; + queries: Array; +} \ No newline at end of file diff --git a/src/app/core/bulk-search/bulk-search-results-summary/substances/bulk-search-results-summary.component.html b/src/app/core/bulk-search/bulk-search-results-summary/substances/bulk-search-results-summary.component.html new file mode 100644 index 000000000..744cdbba5 --- /dev/null +++ b/src/app/core/bulk-search/bulk-search-results-summary/substances/bulk-search-results-summary.component.html @@ -0,0 +1,73 @@ +
+
+

Bulk Search Summary (Full Results)

+
+ + +
+ All Queries: {{totalQueries}}     + Matched: {{totalQueriesMatch}}     + Unmatched: {{totalQueriesUnMatch}} +
+ +
+ + Showing all queries + + + Showing {{qFilteredTotal}} matched queries (show all) + + + Showing {{qFilteredTotal}} unmatched queries (show all) + +
+ +
+
+ +
+
+ +
+ (Un)Select All + + User Name @@ -51,7 +70,7 @@ Modified {{user.user.modified | date:'medium'}} {{user.modified | date:'medium'}} Active/ Set Inactive
+ + + + + + + + + + + + + + + + + + + +
{{displayedColumnNames['searchTerm']}}{{element.searchTerm}}{{displayedColumnNames['displayName']}} + + {{element.displayName}} + + + + {{element.displayName}} + + (no name) + + + {{element.displayName}} + + {{displayedColumnNames['matches']}}{{element.matches}}{{displayedColumnNames['displayCode']}}{{element.displayCode}}
+ + + + +
\ No newline at end of file diff --git a/src/app/core/bulk-search/bulk-search-results-summary/substances/bulk-search-results-summary.component.scss b/src/app/core/bulk-search/bulk-search-results-summary/substances/bulk-search-results-summary.component.scss new file mode 100644 index 000000000..23bd6e48f --- /dev/null +++ b/src/app/core/bulk-search/bulk-search-results-summary/substances/bulk-search-results-summary.component.scss @@ -0,0 +1,69 @@ +.wrapper { + margin-bottom: 20px; + width: 90%; + margin-left: auto; + margin-right: auto; +} + +.summary-title { + font-size: 16px; + font-weight: 550; + color: var(--lighter-title-color); + margin-bottom: 3px; + float: left; +} + +.export { + float: right; + margin-top: -10px; + font-size: small; +} + +.export-button { + color: var(--regular-black-color); + border-radius: 4px; +} + +.statistics { + margin-top: 6px; + margin-top: 5px; + font-weight: 500; +} + +/* Doesn't work? */ +.mat-expansion-panel-header, +.mat-expansion-panel-header.mat-expanded { + height: 30px !important; +} + +.linkish { + color: var(--link-primary-color); + cursor: pointer; +} + +table th, table td { + text-align: left; + justify-content: flex-end; +} + +table tr th:nth-child(1), table tr td:nth-child(1) { + overflow-wrap: anywhere; + width: 35% !important; +} + +table tr th:nth-child(2), table tr td:nth-child(2) { + overflow-wrap: anywhere; + width: 35% !important; +} + +table tr td:nth-child(3) { + text-align: center; + padding: 5px 5px 5px 5px ; + justify-content: flex-end; +} + +table tr th:nth-child(3) { + text-align: center; + padding: 5px 5px 5px 5px ; + justify-content: flex-end; +} diff --git a/src/app/core/bulk-search/bulk-search-results-summary/substances/bulk-search-results-summary.component.spec.ts b/src/app/core/bulk-search/bulk-search-results-summary/substances/bulk-search-results-summary.component.spec.ts new file mode 100644 index 000000000..b119e8543 --- /dev/null +++ b/src/app/core/bulk-search/bulk-search-results-summary/substances/bulk-search-results-summary.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BulkSearchResultsSummaryComponent } from './bulk-search-results-summary.component'; + +describe('BulkSearchResultsSummaryComponent', () => { + let component: BulkSearchResultsSummaryComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ BulkSearchResultsSummaryComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(BulkSearchResultsSummaryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/bulk-search/bulk-search-results-summary/substances/bulk-search-results-summary.component.ts b/src/app/core/bulk-search/bulk-search-results-summary/substances/bulk-search-results-summary.component.ts new file mode 100644 index 000000000..5bb95151b --- /dev/null +++ b/src/app/core/bulk-search/bulk-search-results-summary/substances/bulk-search-results-summary.component.ts @@ -0,0 +1,464 @@ +import { Component, OnInit, ViewChild, Input, AfterViewInit, OnChanges } from '@angular/core'; +import { MatTable, MatTableDataSource } from '@angular/material/table'; +import { RecordOverview } from '@gsrs-core/bulk-search/bulk-search.model'; +import { AuthService } from '@gsrs-core/auth'; +import { LoadingService } from '@gsrs-core/loading'; +import { BulkSearchService } from '@gsrs-core/bulk-search/service/bulk-search.service'; +import { MainNotificationService } from '@gsrs-core/main-notification'; +import { ConfigService } from '@gsrs-core/config'; +import { MatPaginator, PageEvent } from '@angular/material/paginator'; +import { NavigationExtras, Router } from '@angular/router'; +import { Location } from '@angular/common'; +import { interval, Subscription, switchMap, takeWhile } from 'rxjs'; +import { MatSort, Sort} from '@angular/material/sort'; + +@Component({ + selector: 'app-bulk-search-results-summary[context][key]', + templateUrl: './bulk-search-results-summary.component.html', + styleUrls: ['./bulk-search-results-summary.component.scss'] +}) + +export class BulkSearchResultsSummaryComponent implements OnInit, AfterViewInit, OnChanges { +/* + showTitle -- Show the table header title + [summary] -- expects any if this is provided then loadSummary should be false. + loadSummary -- if true will run a data load procedure in the component + context -- expects the entity CONTEXT being browsed (e.g. substances) + [key] -- the bulk search results key + [isCollapsed] -- hide the summary if true. +*/ + @Input() key: string = null; + @Input() context = 'substances'; + + // if false, then we expect the summary to be passed as a parameter + @Input() loadSummary = true; + @Input() showTitle = true; + @Input() isCollapsed = false; + @ViewChild(MatTable, {static: false}) table: MatTable; // initialize + @ViewChild('paginator') paginator: MatPaginator; +  @ViewChild(MatSort) sort: MatSort = new MatSort(); + // https://code-maze.com/angular-material-table/ + + qPageSize: number; + qPageIndex: number; + qSort: string; + qFilter: string; + qFilteredTotal: number; + recordOverviewsShownOnPage: number; + recordOverviews: Array = []; + totalRecordOverviews: number; + totalQueries: number; + totalQueriesMatch: number; + totalQueriesUnMatch: number; + + isLoggedIn = false; + showDeprecated: boolean; + isAdmin = false; + showAudit = false; + isPolling = true; + + private pollingInterval = 2500; + private displayCodeHeader: string; + private defaultDisplayCodeHeader = 'Code'; + private defaultIdHeader = 'Id'; + + + + sortValues: Array = [ + { + 'value': '^searchTerm', + 'display': 'Search Term Ascending ' + }, + { + 'value': '$searchTerm', + 'display': 'Search Term Descending' + }, + { + 'value': '^records_length', + 'display': 'Matches Ascending' + }, + { + 'value': '$records_length', + 'display': 'Matches Descending' + } + ]; + + filterValues: Array = [ + { + 'value': '', + 'display': 'No filter' + }, + { + 'value': 'records_length:0', + 'display': 'No matches' + }, + { + 'value': 'records_length:1', + 'display': 'One match' + }, + { + 'value': 'records_length>0', + 'display': 'One or more matches' + }, + { + 'value': 'records_length>1', + 'display': 'More than one match' + } + ]; + + displayedColumns: string[] = [ + 'searchTerm', + 'displayName', + 'matches', + 'displayCode' + ]; + + displayedColumnNames = { + searchTerm: 'Search Term', + displayName: 'Display Name', + matches: 'Matches', + displayCode: 'Code' // replace with the value in the first row of displayCodeName that has data or "Code" + }; + + dataSource = new MatTableDataSource(this.recordOverviews); + + + private _summary: any = null; + private _summaryForDownload: any = null; + + + constructor( + private loadingService: LoadingService, + public authService: AuthService, + private notificationService: MainNotificationService, + private bulkSearchService: BulkSearchService, + private configService: ConfigService, + private router: Router, + private location: Location + ) { + // const data:any = JSON.parse(``); + // this._summary = data.summary; + } + get summary(): Array { + return this._summary; + } + + @Input() set summary(value: Array) { + this.summary=value; + } + + ngAfterViewInit() { + this.dataSource.paginator = this.paginator; + this.dataSource.sort = this.sort; + } + + ngOnInit(): void { + + + const authSubscription = this.authService.getAuth().subscribe(auth => { + if (auth) { + this.isLoggedIn = true; + } else { + this.showDeprecated = false; + } + this.isAdmin = this.authService.hasAnyRoles('Updater', 'SuperUpdater'); + this.showAudit = this.authService.hasRoles('admin'); + }); + + this.qPageSize = 10; + this.qPageIndex = 0; + if (this.loadSummary) { + this.pollUntillCompleted(); + } + } + + ngOnChanges() { + // This approach was tested early on but not later on in developemnt. + if (!this.loadSummary) { + if(this._summary.queries) { + this.summaryToRecordOverviews(); + } + } + } + + changePage(pageEvent: PageEvent) { + let eventAction: any; + let eventValue: any; + if (this.qPageSize !== pageEvent.pageSize) { + eventAction = 'select:page-size'; + eventValue = pageEvent.pageSize; + } else if (this.qPageIndex !== pageEvent.pageIndex) { + eventAction = 'icon-button:page-number'; + eventValue = pageEvent.pageIndex + 1; + } + // this.gaService.sendEvent('substancesContent', eventAction, 'pager', eventValue); + this.qPageSize = pageEvent.pageSize; + this.qPageIndex = pageEvent.pageIndex; + this.getBulkSearchStatusResults(); + } + + // see substances code + populateUrlQueryParameters(): void { + const navigationExtras: NavigationExtras = { + queryParams: {} + }; + + navigationExtras.queryParams['qSort'] = this.qSort; + navigationExtras.queryParams['qFilter'] = this.qFilter; + navigationExtras.queryParams['qTop'] = this.qPageSize; + navigationExtras.queryParams['qSkip'] = this.qPageIndex * this.qPageSize; + + const urlTree = this.router.createUrlTree([], { + queryParams: navigationExtras.queryParams, + queryParamsHandling: 'merge', + preserveFragment: true + }); + this.location.go(urlTree.toString()); + } + + // see substances code + clearSearch(): void { + // const eventLabel = environment.isAnalyticsPrivate ? 'search term' : this.privateSearchTerm; + // this.gaService.sendEvent('substancesFiltering', 'icon-button:clear-search', eventLabel); + // this.privateSearchTerm = ''; + this.qPageIndex = 0; + this.populateUrlQueryParameters(); + this.getBulkSearchStatusResults(); + } + + pollUntillCompleted() { + interval(this.pollingInterval) + .pipe( + switchMap(() =>this.bulkSearchService.getBulkSearchStatus(this.key)), + takeWhile(( response ) => { + if (response?.finished === true) { + return false; + } + return true; + }) + ).subscribe( + _ => {}, + _ => {}, + () => { + this.isPolling = false; + this.getBulkSearchStatusResults(); + } + ); + } + + sortData(sort: Sort) { + this.qSort = ''; + if (sort.active === 'searchTerm') { + if (sort.direction === 'asc') { + this.qSort = this.sortValues[0].value; + } else if (sort.direction === 'desc') { + this.qSort = this.sortValues[1].value; + } + } else if (sort.active==='matches') { + if (sort.direction === 'asc') { + this.qSort = this.sortValues[2].value; + } else if (sort.direction === 'desc') { + this.qSort = this.sortValues[3].value; + } + } + this.getBulkSearchStatusResults(); + } + + setAndFilterData(qFilter: string) { + this.qFilter=qFilter; + this.qPageIndex = 0; + this.getBulkSearchStatusResults(); + } + + getBulkSearchStatusResults() { + const qSkip = this.qPageIndex * this.qPageSize; + const subscription = this.bulkSearchService.getBulkSearchStatusResults( + this.key, + 0, // we don't need content here just the summaries + 0, + this.qPageSize, + qSkip, + this.qSort, + this.qFilter + ) + .subscribe(bulkSearchResults => { + if (!this.key) { + console.log('Warning, key is null or undefined in getBulkSearchStatusResults.'); + } + if (!this.context) { + console.log('Warning, context is null or undefined in getBulkSearchStatusResults.'); + } + if(bulkSearchResults?.summary) { + this._summary = bulkSearchResults.summary; + this.totalQueries = this._summary.qTotal; + this.qFilteredTotal = this._summary.qFilteredTotal; + this.totalQueriesMatch = this._summary.qMatchTotal; + this.totalQueriesUnMatch = this._summary.qUnMatchTotal; + + this.summaryToRecordOverviews(); + if(this.displayCodeHeader=='') { + this.displayedColumnNames['displayCode'] = + this.defaultDisplayCodeHeader; + } else { + this.displayedColumnNames['displayCode'] = + this.displayCodeHeader; + } + if(this.table) { + this.table.dataSource = this.recordOverviews; + this.recordOverviewsShownOnPage = this.recordOverviews.length; + this.table.renderRows(); + } + } + }, error => { + console.log('Error getting bulk search results in summary component.'); + }, () => { + subscription.unsubscribe(); + }); + } + +summaryToRecordOverviews() { + + this.displayCodeHeader = ''; + this.recordOverviews = []; + this._summary.queries.forEach( q => { + const o: RecordOverview = {} as RecordOverview; + o.searchTerm = q.searchTerm; + o.modifiedSearchTerm = q.modifiedSearchTerm; + if (q.records) { + o.matches = q.records.length; + if(q.records.length === 0) { + o.displayName = '(no match)'; + o.id = '(no match)'; + o.displayCode = '(no match)'; + o.displayCodeName = '(no match)'; + } else + if(q.records.length === 1) { + o.displayName = q.records[0].displayName; + o.id = q.records[0].id;; + o.displayCode = q.records[0].displayCode; + o.displayCodeName = q.records[0].displayCodeName; + // get this for summary table header + if(this.displayCodeHeader==='') { + this.displayCodeHeader = q.records[0].displayCodeName; + } + } else + if (q.records.length>1) { + o.displayName = 'multiple'; + o.id = 'multiple'; + o.displayCode = 'multiple'; + o.displayCodeName = 'multiple'; + } + } + this.recordOverviews.push(o); + }); + this.recordOverviews = this.recordOverviews; +} + + makeTsvTextFromSummaryQueries(json: any): string { + + // searchTerm|matches|displayName|id|idName|displayCode|displayCodeName; + + // replace header for id with the first idName found. + // replace header for displayCode with the first displayCodeName found. + + let _displayCodeHeader = ''; + let _displayCodeHeaderFound = false; + let _idHeader = ''; + let _idHeaderFound = false; + + let tsvText = ''; + + json.queries.forEach(q => { + if (q.records.length === 0) { + tsvText += + (q['searchTerm']||'')+'\t' + +q.records.length+'\t' + +''+'\t' + +''+'\t' + +''+"\n"; + } else { + q.records.forEach( r => { + if(!_displayCodeHeaderFound && r['displayCodeName']) { + _displayCodeHeader = r['displayCodeName']; + _displayCodeHeaderFound = true; + } + if(!_idHeaderFound && r['idName']) { + _idHeader = r['idName']; + _idHeaderFound = true; + } + tsvText += + (q['searchTerm']||'')+'\t' + +(q.records.length)+'\t' + +(r['displayName']||'')+'\t' + +(r['id']||'')+'\t' + +(r['displayCode']||'')+"\n"; + }); + + } + }); + + if(!_displayCodeHeaderFound) { + _displayCodeHeader = this.defaultDisplayCodeHeader; + } + if(!_idHeaderFound) { + _idHeader = this.defaultIdHeader; + } + + const tsvHeaders = + 'searchTerm'+'\t' + +'matches'+'\t' + +'displayName'+'\t' + +_idHeader+'\t' + +_displayCodeHeader+"\n"; + + + return tsvHeaders + tsvText; + } + + getBulkSearchStatusResultsSummaryForDownload(): void { + let filename = ''; + let context: string; + let finished = false; + const qTop = 20000; + const qSkip = 0; + + let s2: Subscription; + const s1 = this.bulkSearchService.getBulkSearchStatus(this.key).subscribe(response1 => { + context = response1.context; + finished = response1.finished; + if (context === undefined) { + alert('Context (entity type) must be defined in the JSON response.'); + } else if (finished !== true) { + alert('The seach is not yet finshed. Please try clicking again after a time.'); + } else { + // top and skip are zero because we don't need the content array for the download. + s2 = this.bulkSearchService.getBulkSearchStatusResults(this.key, 0, 0, qTop, qSkip) + .subscribe(response2 => { + this._summaryForDownload = response2.summary; + filename = context + '-bulk-search-summary-' + this.key + '.txt'; + this.downloadSummaryQueriesFile(response2, filename); + }, error => { + console.log('Error downloading file in getBulkSearchStatusResultsSummaryForDownload.'); + } + ); + } + }, + ()=> { + s1.unsubscribe(); + s2.unsubscribe(); + } + ); + } + + downloadSummaryQueriesFile(response: any, filename: string): void { + const tsvText = this.makeTsvTextFromSummaryQueries(this._summaryForDownload); + const dataType = response.type; + // const binaryData = []; + // binaryData.push(response); + const downloadLink = document.createElement('a'); + downloadLink.href = window.URL.createObjectURL(new Blob([tsvText], { type: dataType })); + downloadLink.setAttribute('download', filename); + document.body.appendChild(downloadLink); + downloadLink.click(); + } +} diff --git a/src/app/core/bulk-search/bulk-search-results-summary/substances/data.json b/src/app/core/bulk-search/bulk-search-results-summary/substances/data.json new file mode 100644 index 000000000..b2a429653 --- /dev/null +++ b/src/app/core/bulk-search/bulk-search-results-summary/substances/data.json @@ -0,0 +1,107 @@ +{ + "summary": { + "qTop":100, + "qSkip":0, + "qTotal":2, + "totalMatched":1, + "totalUnmatched":1, + "queries": [{ + "searchTerm": "faketerm", + "records": [] + }, { + "searchTerm": "sodium chloride", + "records": [{ + "id": "306d24b9-a6b8-4091-8024-02f9ec24b705", + "displayName": "SODIUM CHLORIDE", + "displayCode": "451W47IQ8X", + "displayCodeName": "UNII" + }, { + "id": "bd953285-a7a4-2965-cc7d-73bdceb7abd0", + "displayName": "CAVROTOLIMOD SODIUM", + "displayCode": "Y50W8NUZ1O", + "displayCodeName": "UNII" + }, { + "id": "79dbcc59-e887-40d1-a0e3-074379b755e4", + "displayName": "SODIUM ACETATE", + "displayCode": "4550K0SC9B", + "displayCodeName": "UNII" + }, { + "id": "5b611b0d-b798-45ed-ba02-6f0a2f85986b", + "displayName": "POTASSIUM CHLORIDE", + "displayCode": "660YQ98I10", + "displayCodeName": "UNII" + }, { + "id": "90e9191d-1a81-4a53-b7ee-560bf9e68109", + "displayName": "SODIUM GLUCONATE", + "displayCode": "R6Q3791S76", + "displayCodeName": "UNII" + }, { + "id": "302cedcc-895f-421c-acf4-1348bbdb31f4", + "displayName": "MAGNESIUM CHLORIDE", + "displayCode": "02F3473H9O", + "displayCodeName": "UNII" + }, { + "id": "e92bc4ad-250a-4eef-8cd7-0b0b1e3b6cf0", + "displayName": "THIOFLAVIN S2", + "displayCode": "FDK4QJ64TS", + "displayCodeName": "UNII" + }, { + "id": "0d1371fc-904f-45e9-b073-ba55dacc4f30", + "displayName": "THIOFLAVIN S1", + "displayCode": "2R5VJA8RQB", + "displayCodeName": "UNII" + }] + }, { + "searchTerm": "sodium gluconate", + "records": [{ + "id": "90e9191d-1a81-4a53-b7ee-560bf9e68109", + "displayName": "SODIUM GLUCONATE", + "displayCode": "R6Q3791S76", + "displayCodeName": "UNII" + }, { + "id": "bd953285-a7a4-2965-cc7d-73bdceb7abd0", + "displayName": "CAVROTOLIMOD SODIUM", + "displayCode": "Y50W8NUZ1O", + "displayCodeName": "UNII" + }, { + "id": "306d24b9-a6b8-4091-8024-02f9ec24b705", + "displayName": "SODIUM CHLORIDE", + "displayCode": "451W47IQ8X", + "displayCodeName": "UNII" + }, { + "id": "79dbcc59-e887-40d1-a0e3-074379b755e4", + "displayName": "SODIUM ACETATE", + "displayCode": "4550K0SC9B", + "displayCodeName": "UNII" + }] + }, { + "searchTerm": "potasium chloride", + "records": [{ + "id": "5b611b0d-b798-45ed-ba02-6f0a2f85986b", + "displayName": "POTASSIUM CHLORIDE", + "displayCode": "660YQ98I10", + "displayCodeName": "UNII" + }, { + "id": "306d24b9-a6b8-4091-8024-02f9ec24b705", + "displayName": "SODIUM CHLORIDE", + "displayCode": "451W47IQ8X", + "displayCodeName": "UNII" + }, { + "id": "302cedcc-895f-421c-acf4-1348bbdb31f4", + "displayName": "MAGNESIUM CHLORIDE", + "displayCode": "02F3473H9O", + "displayCodeName": "UNII" + }, { + "id": "e92bc4ad-250a-4eef-8cd7-0b0b1e3b6cf0", + "displayName": "THIOFLAVIN S2", + "displayCode": "FDK4QJ64TS", + "displayCodeName": "UNII" + }, { + "id": "0d1371fc-904f-45e9-b073-ba55dacc4f30", + "displayName": "THIOFLAVIN S1", + "displayCode": "2R5VJA8RQB", + "displayCodeName": "UNII" + }] + }] + }} + \ No newline at end of file diff --git a/src/app/core/bulk-search/bulk-search.component.html b/src/app/core/bulk-search/bulk-search.component.html new file mode 100644 index 000000000..188c808d7 --- /dev/null +++ b/src/app/core/bulk-search/bulk-search.component.html @@ -0,0 +1,4 @@ +
Bulk Search Summary
+
+ +
\ No newline at end of file diff --git a/src/app/core/bulk-search/bulk-search.component.scss b/src/app/core/bulk-search/bulk-search.component.scss new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/src/app/core/bulk-search/bulk-search.component.scss @@ -0,0 +1 @@ + diff --git a/src/app/core/bulk-search/bulk-search.component.ts b/src/app/core/bulk-search/bulk-search.component.ts new file mode 100644 index 000000000..86f12a656 --- /dev/null +++ b/src/app/core/bulk-search/bulk-search.component.ts @@ -0,0 +1,209 @@ + +// This should probably be deleted. + +import { + Component, + OnInit, + OnDestroy, + ViewChild, +} from '@angular/core'; +import { NavigationExtras,ActivatedRoute } from '@angular/router'; +import { Subscription } from 'rxjs'; +import { ConfigService, LoadedComponents } from '@gsrs-core/config'; +import { AppNotification, NotificationType } from '@gsrs-core/main-notification'; +import { MainNotificationService } from '@gsrs-core/main-notification'; +import { LoadingService } from '@gsrs-core/loading'; +import { AuthService } from '../../core/auth/auth.service'; +import { BulkSearchService } from './service/bulk-search.service'; +import { BulkSearch } from './bulk-search.model'; + + @Component({ + selector: 'app-bulk-search', + templateUrl: './bulk-search.component.html', + styleUrls: ['./bulk-search.component.scss'] + }) + export class BulkSearchComponent implements OnInit, OnDestroy { + loadedComponents: LoadedComponents; + showAudit: boolean; + isAdmin = false; + isLoggedIn = false; + showDeprecated = false; + queryText: string; + context: 'substances'; + _bulkSearch: BulkSearch; + _bulkSearchResults: any; + bulkQID: number; + searchOnIdentifiers: boolean; + bulkSearchResultsTop = 10; + bulkSearchResultsSkip = 0; + bulkSearchResultsQTop = 10; + bulkSearchResultsQSkip = 0; + isError = false; + isLoading = false; + anchorElement: HTMLAnchorElement; + showSpinner = false; + navigationExtrasFacet: NavigationExtras = { + queryParams: {} + }; + + private subscriptions: Array = []; + private searchType: string; + private searchEntity: string; + + constructor( + private loadingService: LoadingService, + public authService: AuthService, + private notificationService: MainNotificationService, + private bulkSearchService: BulkSearchService, + private configService: ConfigService, + private route: ActivatedRoute + ) { + } + + ngOnInit() { + + this.loadingService.setLoading(true); + this.showSpinner = true; // Start progress spinner + + // Get configration values to hide/show Modules + this.loadedComponents = this.configService.configData.loadedComponents || null; + + if (this.loadedComponents) { + if (this.loadedComponents.applications) { + } + } + + this.showSpinner = false; // Stop progress spinner + this.loadingService.setLoading(false); + + const authSubscription = this.authService.getAuth().subscribe(auth => { + if (auth) { + this.isLoggedIn = true; + } else { + this.showDeprecated = false; + } + this.isAdmin = this.authService.hasAnyRoles('Updater', 'SuperUpdater'); + this.showAudit = this.authService.hasRoles('admin'); + }); + + this.route.queryParams.subscribe(params => { + // this.loadingService.setLoading(true); + // this.isError = false; + this.bulkQID = params.bulkQID; + let subscription2: Subscription; + this.bulkSearchService.getBulkSearch( + this.context, + this.bulkQID, + this.searchOnIdentifiers + ).subscribe(bulkSearch => { + this._bulkSearch = bulkSearch; + subscription2 = this.bulkSearchService.getBulkSearchStatusResults( + this._bulkSearch.key, + this.bulkSearchResultsTop, + this.bulkSearchResultsSkip, + this.bulkSearchResultsQTop, + this.bulkSearchResultsQSkip + ).subscribe(bulkSearchResults => { + this._bulkSearchResults = bulkSearchResults; + }); + }); + }); + } +/* +, + error => { + const notification: AppNotification = { + message: 'There was an error trying to get a bulk search results.', + type: NotificationType.error, + milisecondsToShow: 6000 + }; + this.isError = true; + this.isLoading = false; + this.loadingService.setLoading(this.isLoading); + this.notificationService.setNotification(notification); + }, + () => { + this.isLoading = false; + this.loadingService.setLoading(this.isLoading); + } + +*/ + + + + ngOnDestroy() { + this.subscriptions.forEach(subscription => { + if (subscription) { + subscription.unsubscribe(); + } + }); + } + + + + + + getBulkSearch(context: string, id: number) { + this.loadingService.setLoading(true); + const subscription = this.bulkSearchService.getBulkSearch( + context, + id + ) + .subscribe(bulkSearch => { + this.isError = false; + this._bulkSearch = bulkSearch; + }, error => { + const notification: AppNotification = { + message: 'There was an error trying to get a bulk search result.', + type: NotificationType.error, + milisecondsToShow: 6000 + }; + this.isError = true; + this.isLoading = false; + this.loadingService.setLoading(this.isLoading); + this.notificationService.setNotification(notification); + }, () => { + subscription.unsubscribe(); + this.isLoading = false; + this.loadingService.setLoading(this.isLoading); + }); + } + + getBulkSearchStatusResults( + key: string, + top: number, + skip: number, + qTop: number, + qSkip: number + ) { + this.loadingService.setLoading(true); + const subscription = this.bulkSearchService.getBulkSearchStatusResults( + key, + top, + skip, + qTop, + qSkip + ) + .subscribe(bulkSearchResults => { + this.isError = false; + this._bulkSearchResults = bulkSearchResults; + }, error => { + const notification: AppNotification = { + message: 'There was an error trying to get a bulk search results.', + type: NotificationType.error, + milisecondsToShow: 6000 + }; + this.isError = true; + this.isLoading = false; + this.loadingService.setLoading(this.isLoading); + this.notificationService.setNotification(notification); + }, () => { + subscription.unsubscribe(); + this.isLoading = false; + this.loadingService.setLoading(this.isLoading); + }); + } + + + + } diff --git a/src/app/core/bulk-search/bulk-search.model.ts b/src/app/core/bulk-search/bulk-search.model.ts new file mode 100644 index 000000000..69cdd4030 --- /dev/null +++ b/src/app/core/bulk-search/bulk-search.model.ts @@ -0,0 +1,41 @@ +export interface BulkSearch { + start: string; + id: number; + key: string; + count: number; + status: number; + finished: number; + determined: string; + generatingUrl: string; + url: string; +} + +export interface Summary { + searchTerm: string; + records: Array; + key: string; + count: number; + status: number; + finished: number; + determined: string; + generatingUrl: string; + url: string; +} + +export interface Record { + id: string; + displayName: string; + displayCode: string; + displayCodeName: string; +} + +export interface RecordOverview { + searchTerm?: string; + modifiedSearchTerm?: string; + displayName?: string; + id?: string; + matches?: number; + displayCode?: string; + displayCodeName?: string; +} + diff --git a/src/app/core/bulk-search/bulk-search.module.ts b/src/app/core/bulk-search/bulk-search.module.ts new file mode 100644 index 000000000..92e74b0f6 --- /dev/null +++ b/src/app/core/bulk-search/bulk-search.module.ts @@ -0,0 +1,128 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Router, Routes, RouterModule } from '@angular/router'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { BulkSearchService } from './service/bulk-search.service'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { MatSidenavModule } from '@angular/material/sidenav'; +import { MatCardModule } from '@angular/material/card'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatBadgeModule } from '@angular/material/badge'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatTableModule } from '@angular/material/table'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatSelectModule } from '@angular/material/select'; +import { MatSliderModule } from '@angular/material/slider'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatListModule } from '@angular/material/list'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatButtonToggleModule } from '@angular/material/button-toggle'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { OverlayModule } from '@angular/cdk/overlay'; +import { MatBottomSheetModule } from '@angular/material/bottom-sheet'; +import { FacetsManagerModule } from '@gsrs-core/facets-manager'; +import { SubstanceFormModule } from '../../core/substance-form/substance-form.module'; +import { BulkSearchComponent } from '@gsrs-core/bulk-search/bulk-search.component'; +import { BulkQueryComponent } from '@gsrs-core/bulk-search/bulk-query.component'; +import { NameResolverModule } from '@gsrs-core/name-resolver/name-resolver.module'; +import { MatDatepickerModule } from '@angular/material/datepicker'; +import { StructureModule } from '@gsrs-core/structure/structure.module'; +import { TextInputFormComponent } from '@gsrs-core/utils/text-input-form/text-input-form.component'; +import { FileUploadFormComponent } from '@gsrs-core/bulk-search/file-upload-form/file-upload-form.component'; +import { BulkSearchResultsSummaryComponent } from './bulk-search-results-summary/substances/bulk-search-results-summary.component'; +import {MatRadioModule} from '@angular/material/radio'; +import { MatSortModule } from '@angular/material/sort'; + +const bulkSearchRoutes: Routes = [ + { + path: 'bulk-search', + component: BulkQueryComponent + }, + { + path: 'bulk-search-results', + component: BulkSearchComponent + } +]; + +@NgModule({ + imports: [ + CommonModule, + RouterModule.forChild(bulkSearchRoutes), + MatToolbarModule, + MatSidenavModule, + MatCardModule, + MatAutocompleteModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + MatIconModule, + MatChipsModule, + MatBadgeModule, + MatExpansionModule, + MatCheckboxModule, + MatTableModule, + MatPaginatorModule, + MatSortModule, + MatSelectModule, + MatRadioModule, + MatSliderModule, + MatDialogModule, + MatListModule, + MatMenuModule, + MatButtonToggleModule, + MatTooltipModule, + MatTabsModule, + MatBottomSheetModule, + MatProgressSpinnerModule, + MatDatepickerModule, + FormsModule, + ReactiveFormsModule, + OverlayModule, + SubstanceFormModule, + FacetsManagerModule, + NameResolverModule, + StructureModule + ], + declarations: [ + BulkQueryComponent, + BulkSearchComponent, + TextInputFormComponent, + FileUploadFormComponent, + BulkSearchResultsSummaryComponent + ], + exports: [ + BulkQueryComponent, + BulkSearchComponent, + TextInputFormComponent, + FileUploadFormComponent, + BulkSearchResultsSummaryComponent + ] +}) + +export class BulkSearchModule { + constructor(router: Router) { + bulkSearchRoutes.forEach(route => { + router.config[0].children.push(route); + }); + } + + static forRoot(): ModuleWithProviders { + return { + ngModule: BulkSearchModule, + providers: [ + BulkSearchService + ] + }; + } + +} + + diff --git a/src/app/core/bulk-search/file-upload-form/file-upload-form.component.html b/src/app/core/bulk-search/file-upload-form/file-upload-form.component.html new file mode 100644 index 000000000..9d6b945b7 --- /dev/null +++ b/src/app/core/bulk-search/file-upload-form/file-upload-form.component.html @@ -0,0 +1,47 @@ +
+
+
+ +
+
+
{{filename? filename: 'no file chosen'}}
+ +
+ +
+
+ + + + Text (.txt) + + + Text (.tsv) + + + + +
+
+ + +
+
+
+ +
+ +
{{message}}
+ +
+ + +
+ + +
+
+
+
+ +
\ No newline at end of file diff --git a/src/app/core/bulk-search/file-upload-form/file-upload-form.component.scss b/src/app/core/bulk-search/file-upload-form/file-upload-form.component.scss new file mode 100644 index 000000000..55a0a7ef5 --- /dev/null +++ b/src/app/core/bulk-search/file-upload-form/file-upload-form.component.scss @@ -0,0 +1,288 @@ +.italics { + font-style: italic; + color: rgba(0, 0, 0, .5); +} + +.file-name { + display: flex; + text-align: center; + margin-left: 15px; + margin-top: auto; + margin-bottom: auto; +} + +.full-row { + width: 100%; + display: flex; + flex-direction: row; + justify-content: flex-start; + padding: 10px; +} + +.spinner { + +} + +.load-progress { + height: 17px; + margin-bottom: 5px; + font-size:12px; + color: black; + -webkit-text-fill-color: white; /* Will override color (regardless of order) */ + -webkit-text-stroke-width: 1px; + -webkit-text-stroke-color: black; +} + +.load-fail { + // transform: rotate(180deg); + margin-top: -22px; + ::ng-deep .mat-progress-bar-fill { + background-color: rgb(173, 26, 26); + z-index: 1; + + } + ::ng-deep .mat-progress-bar-fill::after { + background-color: rgb(173, 26, 26); + // z-index: 1; + + } + + + + + ::ng-deep .mat-progress-bar-buffer { + background: rgba(0, 0, 0, 0.01); + } +} + +.loading-spinner { + width: 70px; + mat-progress-spinner { + margin: auto; + } +} +.load-fail-old { + transform: rotate(180deg); + margin-top: -22px; + ::ng-deep .mat-progress-bar-fill { + background-color: rgb(173, 26, 26); + z-index: 2; + + + } + ::ng-deep .mat-progress-bar-fill::after { + background-color: rgb(173, 26, 26); + z-index: 2; + + + } + + + + + ::ng-deep .mat-progress-bar-buffer { + background: rgba(0, 0, 0, 0.01); + } +} + +.load-success { + ::ng-deep .mat-progress-bar-fill { + // background-color: rgb(173, 26, 26); + z-index: 2; + + } + ::ng-deep .mat-progress-bar-fill::after { + // background-color: rgb(173, 26, 26); + // z-index: 2; + + } + ::ng-deep .mat-progress-bar-buffer { + background: rgba(0, 0, 0, 0.01); + } +} + + +.load-success-old { + ::ng-deep .mat-progress-bar-fill { + // background-color: rgb(173, 26, 26); + z-index: 2; + + } + ::ng-deep .mat-progress-bar-fill::after { + // background-color: rgb(173, 26, 26); + z-index: 2; + + } + ::ng-deep .mat-progress-bar-buffer { + background: rgba(0, 0, 0, 0.01); + } +} + +.progress-container { + width: 100%; + display: flex; + flex-direction: column; + + .load-progress { + width: 100%; + } + + .bar-label { + font-size: 14px; + } +} + + + +.spinner-container { + display: flex; + flex-direction: row; + + .spinner-labels { + + } + + .spinner-row { + width: 33%; + flex-direction: column; + + .spinner-labels { + display: flex; + flex-direction: row; + + + .spinner { + width: 50px; + } + } + } +} + +.count-label { +margin-top: auto; +margin-bottom: auto; +} + +.loader-container { + width: 70%; + margin: 10px; +} + +.lower-body { + width: 100%; + display:flex; + flex-direction: row; +} + +.stat-container { + width: 30%; + margin:10px; + min-width: 250px; +} + +.label-value{ + width: 100%; + display:flex; + flex-direction: row; + padding-bottom: 10px; + + .label { + width: 50%; + min-width: 125px; + font-weight: 500; + } + + .value { + width: 50%; + } +} + +.example-section { + display: flex; + align-content: center; + align-items: center; + height: 60px; + } + + + .mirror { + margin-top: -105px; + transform: scale(-1, 1); + + ::ng-deep circle { + stroke: rgb(173, 26, 26); + } + + + + ::ng-deep .mat-progress-spinner-buffer { + background: rgb(212, 212, 212); + } + } + + .mirror-test { + margin-left: -125px; + transform: scale(-1, 1); + + ::ng-deep circle { + stroke: rgb(173, 26, 26); + } + + ::ng-deep .mat-progress-spinner-buffer { + background: rgb(212, 212, 212); + } +} + + + .overlap { + ::ng-deep .mat-progress-bar-fill::after { + z-index: 2; + } + } + + .deleted { + margin: auto; + padding: 10px; + color: grey; + font-style: italics; + font-size: 18px; + } + + + .label-row { + width: 100%; + display: flex; + flex-direction: row; + justify-content: left; + + .count-row-label { + margin-left: auto; + margin-right: auto; + } + } + + .count-cont { + text-align: center; + color: white; + margin-top: -22px; + font-weight: bold; + z-index: 5; + font-size: 15px; + } + + .file-row { + width: 75%; + height: 40px; + display: flex; + flex-direction: row; + } + + .options-row { + width: 25%; + display: flex; + flex-direction: column; + } + + .bottom { + padding-top: 50px; + } diff --git a/src/app/core/bulk-search/file-upload-form/file-upload-form.component.spec.ts b/src/app/core/bulk-search/file-upload-form/file-upload-form.component.spec.ts new file mode 100644 index 000000000..322d6ce5d --- /dev/null +++ b/src/app/core/bulk-search/file-upload-form/file-upload-form.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FileUploadFormComponent } from './file-upload-form.component'; + +describe('FileUploadFormComponent', () => { + let component: FileUploadFormComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ FileUploadFormComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FileUploadFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/bulk-search/file-upload-form/file-upload-form.component.ts b/src/app/core/bulk-search/file-upload-form/file-upload-form.component.ts new file mode 100644 index 000000000..23f8e62bc --- /dev/null +++ b/src/app/core/bulk-search/file-upload-form/file-upload-form.component.ts @@ -0,0 +1,66 @@ +import { Component, OnInit } from '@angular/core'; +import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms'; +import { AdminService } from '@gsrs-core/admin/admin.service'; +import { take } from 'rxjs/operators'; +import * as moment from 'moment'; +import { Router, ActivatedRoute } from '@angular/router'; +import { UploadObject } from '@gsrs-core/admin/admin-objects.model'; +import { LoadingService } from '@gsrs-core/loading'; + +@Component({ + selector: 'app-file-upload-form', + templateUrl: './file-upload-form.component.html', + styleUrls: ['./file-upload-form.component.scss'] +}) +export class FileUploadFormComponent implements OnInit { + uploadForm: FormGroup; + filename: string; + fileType: string; + audit = false; + processing = false; + message: string; + constructor( + public formBuilder: FormBuilder, + public adminService: AdminService, + private router: Router, + private route: ActivatedRoute, + private loadingService: LoadingService + + ) { } + + ngOnInit() { + this.uploadForm = this.formBuilder.group({ + file: [''], + fileType: ['TEXT'], + audit: [false] + }); + this.fileType = 'TEXT'; + } + + onSubmit() { + const formData = new FormData(); + this.loadingService.setLoading(true); + formData.append('file-name', this.uploadForm.get('file').value); + formData.append('file-type', this.fileType); + + this.adminService.loadData(formData).pipe(take(1)).subscribe(response => { + this.loadingService.setLoading(false); + this.router.navigate(['/monitor/' + response.id]); + }, error => { + this.message = 'File could not be uploaded'; + this.loadingService.setLoading(false); + }); +} + + onFileSelect(event): void { + if (event.target.files.length > 0) { + const file = event.target.files[0]; + this.filename = file.name; + this.uploadForm.get('file').setValue(file); + } + } + + openInput(): void { + document.getElementById('fileInput').click(); + } +} diff --git a/src/app/core/bulk-search/service/bulk-search.service.ts b/src/app/core/bulk-search/service/bulk-search.service.ts new file mode 100644 index 000000000..363b0587c --- /dev/null +++ b/src/app/core/bulk-search/service/bulk-search.service.ts @@ -0,0 +1,194 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpParams } from '@angular/common/http'; +import { Observable, } from 'rxjs'; +import { ConfigService } from '@gsrs-core/config'; +import { BaseHttpService } from '@gsrs-core/base'; +import { BulkQuery } from '../bulk-query.model'; +import { BulkSearch } from '../bulk-search.model'; +import { Subject } from 'rxjs'; + +@Injectable( + { providedIn: 'root' } +) + +export class BulkSearchService extends BaseHttpService { + + totalRecords: 0; + baseHref: ''; + public listEmitter = new Subject(); + + + constructor( + public http: HttpClient, + public configService: ConfigService + ) { + super(configService); + } + + getBaseHref(): string { + return this.configService.environment.baseHref; + } + + postOrPutBulkQuery( + // id: number, + context: string, + queryText: string + ): Observable { + // NOTE PUTs are resulting in errors during search turning off for now. + // All new queries are getting a new bulkQID + const url = this.configService.configData.apiBaseUrl + 'api/v1/'+context+'/@bulkQuery'; + let params = {}; + // if (id !== null && id !== undefined) { params['id'] = id }; + const options = { + // eslint-disable-next-line object-shorthand + params: params, + type: 'JSON', + headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'Content-type': 'text/plain' + } + }; + // if (id !== null && id !== undefined) { + // return this.http.put(url, queryText, options); + // } + return this.http.post(url, queryText, options); + + } + + getBulkQuery( + context: string, + id: number, + top: number = 10, + skip: number = 0 + ): Observable { + const url = this.configService.configData.apiBaseUrl + 'api/v1/'+context+'/@bulkQuery'; + const options = { + // eslint-disable-next-line object-shorthand + params: { top: top, skip: skip }, + type: 'JSON', + headers: {} + }; + return this.http.get(url+'?id='+id, options); + } + + getBulkSearch( + context: string, + id: number, + searchOnIdentifiers: boolean = false + ): Observable { + const url = this.configService.configData.apiBaseUrl + 'api/v1/'+context+'/bulkSearch'; + let params = new HttpParams(); + params = params.append('bulkQID', id); + params = params.append('searchOnIdentifiers', searchOnIdentifiers); + + params.append('simpleSearchOnly', null); + const options = { + // eslint-disable-next-line object-shorthand + params: params, + type: 'JSON', + headers: {} + }; + return this.http.get(url, options); + } + + getBulkSearchStatus( + key: string, + ): Observable { + const url = this.configService.configData.apiBaseUrl + 'api/v1/status/'+key; + // let params = new HttpParams(); + const options = { + type: 'JSON', + headers: {} + }; + return this.http.get(url, options); + } + + getBulkSearchStatusResults( + key: string, + top?: number, + skip?: number, + qTop?: number, + qSkip?: number, + qSort: string='', + qFilter: string='' + ): Observable { + const url = this.configService.configData.apiBaseUrl + 'api/v1/status/'+key+'/results'; + // let params = new HttpParams(); + const options = { + // eslint-disable-next-line object-shorthand + params: {top: top, skip: skip, qTop: qTop, qSkip: qSkip, qSort: qSort, qFilter: qFilter}, + type: 'JSON', + headers: {} + }; + return this.http.get(url, options); + } + + + saveBulkSearch(list: string, name: string, etag?: string) { + const url = this.apiBaseUrl + `substances/@userList/keys?listName=${name}`; + + return this.http.post(url, list); + } + + saveBulkSearchEtag(list: string, name: string, etag: string) { + // save search results as a list by etag + const url = this.apiBaseUrl + `substances/@userList/etag/${etag}?listName=${name}`; + + return this.http.post(url, null); + } + + getSaveBulkListStatus(id: string) { + // get the status of a call to add a new list. + const url = this.apiBaseUrl + `substances/@userList/status/${id}`; + return this.http.get(url); + } + + getBulkSearchLists() { + const url = this.apiBaseUrl + `substances/@userLists/currentUser`; + return this.http.get(url); + } + + getUserBulkSearchLists(name: string) { + // Get any users all saved lists. + const url = this.apiBaseUrl + `substances/@userLists/otherUser?name=${name}`; + return this.http.get(url); + } + + getSingleBulkSearchList(name: string, user?: string) { + // Get the keys and other fields of a list. default to active user if not specified + let url = this.apiBaseUrl + `substances/@userList/${name}`; + + if(user && user !== null) { + url = this.apiBaseUrl + `substances/@userList/${user}/${name}`; + + } + return this.http.get(url); + } + + editKeysBulkSearchLists(name: string, list: string, operation: string) { + // Add or remove keys from a list + const url = this.apiBaseUrl + `substances/@userList/currentUser?keys=${list}&listName=${name}&operation=${operation}`; + return this.http.put(url, list); + } + + editEtagBulkSearchLists(name: string, etag: string, operation: string) { + // Add or remove keys from a list + const url = this.apiBaseUrl + `substances/@userList/currentUser/etag/${etag}?listName=${name}&operation=${operation}`; + return this.http.put(url, null); + } + + deleteBulkSearchList(name: string) { + // Delete a list from current user + const url = this.apiBaseUrl + `substances/@userList/currentUser?listName=${name}`; + return this.http.delete(url); + } + + deleteUserBulkSearchList(listName: string, userName: string) { + // Delete a list from any user + const url = this.apiBaseUrl + `substances/@userList/otherUser?listName=${listName}&userName=${userName}`; + return this.http.delete(url); + } + + + +} diff --git a/src/app/core/bulk-search/user-query-list-dialog/user-query-list-dialog.component.html b/src/app/core/bulk-search/user-query-list-dialog/user-query-list-dialog.component.html new file mode 100644 index 000000000..f70c1593b --- /dev/null +++ b/src/app/core/bulk-search/user-query-list-dialog/user-query-list-dialog.component.html @@ -0,0 +1,182 @@ +
+ +
+

Saved Lists {{setUser ? ' - ' + setUser : ''}}

+ +
+
+

Add List

+
+
+

Import List

+ Return to lists view +
+ +
+

Users

+
+
+

Saved Lists: {{activeName}} + +

+ +
+
+
+ +
+
+
+ Enter a list name to save browse results to

+     + +
+

+
+ Or select an existing list to append to
+ + Select a List + + + {{list}} + + + +     + +
+
+
+ + +
+
+ +
+
+ + + + + + + + + + + + + + + + +
Name {{list.identifier}} ID {{role}}   View
+ +
+ + +
+


+
+ Select an exported .json saved list file to import into a new form. +
+

+
+
+
+
+
{{filename? filename: 'no file chosen'}}
+ + +
+

+ +

+
+ + +
+
+ No lists were found +
+ + + + + + + + + + + + + + + + + +
Delete Name {{list}} View
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + +
Delete UUID {{list.key}} Name {{list.displayName? list.displayName : 'N/A'}} Code {{list.displayCode? list.displayCode : 'N/A'}} Code System {{list.displayCodeSystem? list.displayCodeSystem : 'N/A'}}
+ + +
+ +
+
+
{{message}}
+
+

+             This dialog can safely be closed and the process will continue in the background.
+ +       View new list + +
+
+
+
+ + +
\ No newline at end of file diff --git a/src/app/core/bulk-search/user-query-list-dialog/user-query-list-dialog.component.scss b/src/app/core/bulk-search/user-query-list-dialog/user-query-list-dialog.component.scss new file mode 100644 index 000000000..47ce4310d --- /dev/null +++ b/src/app/core/bulk-search/user-query-list-dialog/user-query-list-dialog.component.scss @@ -0,0 +1,111 @@ +.link { + color: var(--primary-color); + + font-size: 18px; + text-decoration: none; +} + +.loading-spinner { + width: 70px; + mat-progress-spinner { + margin: auto; + } +} + + +.file-import { + width: 100%; +} + +.file-row { + width: 100%; +} + +.full-row-start { + width: 100%; + display: flex; + flex-direction: row; + justify-content: flex-start; + padding: 15px; +} + + +.italics { + font-style: italic; + color: var(--text-color); +} + +.file-name { + display: flex; + text-align: center; + margin-left: 15px; + margin-top: auto; + margin-bottom: auto; + font-size: 15px; + color: var(--deprecated-color); +} + +.single-header { + margin-bottom:10px; + padding-right:25px; +} + +.primary-color { + color: var(--primary-color); +} + +.form-row { + display: flex; + width: 100%; +} + +.spinner-container { + display: flex; + flex-direction: row; + + .spinner-labels { + + } + + .spinner-row { + width: 33%; + flex-direction: column; + + .spinner-labels { + display: flex; + flex-direction: row; + + + .spinner { + width: 50px; + } + } + } +} + +.mat-mini-fab { + position: absolute; + right: 17px; + top: 8px; + background-color: var(--mat-icon-button-bg-color); + color: var(--label-color); + width: 35px; + height: 35px; + + &:not(:first-child) { + margin-top: 3px; + } + + ::ng-deep .mat-button-wrapper { + padding: 0; + } +} + +::ng-deep { + + .mat-dialog-container { + // padding: 5px; + position: relative; + // overflow: hidden; + } + } \ No newline at end of file diff --git a/src/app/core/bulk-search/user-query-list-dialog/user-query-list-dialog.component.spec.ts b/src/app/core/bulk-search/user-query-list-dialog/user-query-list-dialog.component.spec.ts new file mode 100644 index 000000000..26f51a1a9 --- /dev/null +++ b/src/app/core/bulk-search/user-query-list-dialog/user-query-list-dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UserQueryListDialogComponent } from './user-query-list-dialog.component'; + +describe('UserQueryListDialogComponent', () => { + let component: UserQueryListDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ UserQueryListDialogComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(UserQueryListDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/bulk-search/user-query-list-dialog/user-query-list-dialog.component.ts b/src/app/core/bulk-search/user-query-list-dialog/user-query-list-dialog.component.ts new file mode 100644 index 000000000..5080d7e11 --- /dev/null +++ b/src/app/core/bulk-search/user-query-list-dialog/user-query-list-dialog.component.ts @@ -0,0 +1,457 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { BulkSearchService } from '@gsrs-core/bulk-search/service/bulk-search.service'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { Subscription } from 'rxjs'; +import { take } from 'rxjs/operators'; +import { AdminService } from '@gsrs-core/admin/admin.service'; +import { NavigationExtras, Router } from '@angular/router'; +import { AuthService } from '@gsrs-core/auth'; +import { PageEvent } from '@angular/material/paginator'; +import { SubstanceService } from '@gsrs-core/substance'; +import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +@Component({ + selector: 'app-user-query-list-dialog', + templateUrl: './user-query-list-dialog.component.html', + styleUrls: ['./user-query-list-dialog.component.scss'] +}) +export class UserQueryListDialogComponent implements OnInit { + lists: Array = []; + view = 'all'; + active: any; + filtered: any; + activeName: string; + pagesize = 10; + page = 0; + userLists: Array; + displayedColumns: string[] = ['delete', 'name', 'view']; + displayedColumns2: string[] = ['delete', 'key', 'name', 'code', 'codeSystem']; + displayedColumns3: string[] = ['identifier', 'roles', 'action']; + + message = ''; + etag?: string; + listName?: string; + listName2?: string; + showAddButtons = false; + loading = false; + status: string; + loadID: string; + users = []; + setUser: string; + identifier: string; + isAdmin = false; + etagIDs = []; + uniqueRecords = []; + disabled = false; + loaded = false; + filename: string; + importedList: any; + pastedJson: any; + viewCreated = false; + downloadJsonHref: SafeUrl; + refreshing = false; + + constructor( + private bulkSearchService: BulkSearchService, + public dialogRef: MatDialogRef, + private adminService: AdminService, + private substanceService: SubstanceService, + private router: Router, + private authService: AuthService, + private sanitizer: DomSanitizer, + + @Inject(MAT_DIALOG_DATA) public data: any + ) { + this.view = data.view || 'all'; + this.activeName = data.activeName || null; + this.etag = data.etag || null; + this.userLists = data.lists || null; + + } + + ngOnInit(): void { + this.substanceService.getAllByEtag(this.etag).subscribe(result => { + if(result.content) { + result.content.forEach(record => { + this.etagIDs.push(record.uuid); + }) + } + + }); + this.authService.checkAuth().subscribe(response => { + this.identifier = response.identifier; + response.roles.forEach(role => { + if (role === 'Admin') { + this.isAdmin = true; + } + }); + }); + this.getUserLists(); + if (this.view === 'single') { + this.useDraft(this.activeName); + + } + } + + viewLists(): void { + this.getUserLists(); + this.showAddButtons = false; + this.view = "all"; + this.message =""; + } + + addList(): void { + this.refreshing = false; + + let found = false; + this.lists.forEach(item => { + if (item === this.listName) { + found = true; + } + }); + if (!found) { + this.loading = true; + this.bulkSearchService.saveBulkSearchEtag(null, this.listName, this.etag).subscribe( response => { + this.loadID = response.id; + setTimeout(() => { + + this.refresh('add'); + }, 100); + this.message = "Status: sending request"; + this.showAddButtons = true; + }, error => { + this.message = "Error: There was a problem adding a new list"; + this.loading = false; + + }) + } else { + this.message = "Cannot add: This list name is already used"; + this.loading = false; + } + } + + checkList(): void { + this.bulkSearchService.getSingleBulkSearchList(this.listName2).subscribe(result => { + let tosend = []; + result.lists.forEach(list => { + tosend.push(list.key); + }) + if (this.etagIDs.length > 0) { + this.compareLists(tosend); + } + + }); + } + + compareLists(list: any) { + this.uniqueRecords = this.etagIDs.filter(obj => { return list.indexOf(obj) == -1; }); + if (this.uniqueRecords.length == 0) { + this.message = "NOTICE: All records already exist in selected list"; + this.disabled = true; + } else { + this.disabled = false; + this.message = "Add " + this.uniqueRecords.length + " records to list."; + if (this.uniqueRecords.length < this.etagIDs.length) { + this.message += " Ignore " + (this.etagIDs.length - this.uniqueRecords.length) + " duplicates"; + } + } + } + + appendList(): void { + this.refreshing = false; + + let found = false; + this.lists.forEach(item => { + if (item === this.listName) { + found = true; + } + }); + if (!found) { + this.loading = true; + + this.bulkSearchService.editEtagBulkSearchLists(this.listName2, this.etag, 'add').subscribe( response => { + this.loadID = response.id; + setTimeout(() => { + this.refresh('append'); + }, 100); + this.message = "Status: sending request"; + this.showAddButtons = true; + }, error => { + this.message = "Error: There was a problem adding a new list"; + }); + } else { + this.message = "Cannot add: This list name is already used"; + } + + } + + useUser(name: string) { + this.refreshing = false; + + this.viewCreated = false; + this.setUser = null; + this.loaded = false; + this.bulkSearchService.getUserBulkSearchLists(name).subscribe(response => { + this.view = "all"; + this.lists = response.lists; + this.setUser = name; + }); + } + + pageChange(pageEvent?: PageEvent): void { + this.refreshing = false; + + if (pageEvent != null) { + + let eventAction; + let eventValue; + + if (this.pagesize !== pageEvent.pageSize) { + eventAction = 'select:page-size'; + eventValue = pageEvent.pageSize; + } else if (this.page !== pageEvent.pageIndex) { + eventAction = 'icon-button:page-number'; + eventValue = pageEvent.pageIndex + 1; + } + + + this.page = pageEvent.pageIndex; + this.pagesize = pageEvent.pageSize; + } + + this.filtered = []; + const startIndex = this.page * this.pagesize; + for (let i = startIndex; i < (startIndex + this.pagesize); i++) { + if (this.active.lists[i] != null) { + this.filtered.push(this.active.lists[i]); + } else { + break; + } + } + } + + browseView() { + let navigationExtras: NavigationExtras = {queryParams: {}}; + + const newtest = this.activeName; + navigationExtras.queryParams = {'facets': 'User List*' + this.identifier.replace(/[.]/g, '!.') + ':' + newtest.replace(/^.*[\\\/]/, '') + '.true'}; + + this.router.navigate(['/browse-substance'], navigationExtras); + this.dialogRef.close(); + } + + refresh(type: string) { + this.bulkSearchService.getSaveBulkListStatus(this.loadID).pipe(take(1)).subscribe(response => { + + this.status = response.status; + this.refreshing = true; + console.log(response); + this.message = "Status: " + this.status; + if (this.status === 'Completed.') { + this.loading = false; + this.refreshing = false; + + } else { + setTimeout(() => { + this.refresh(type); + }, 100); + } + }, error => { + setTimeout(() => { + this.refresh(type); + console.log(error); + }, 1000); + }) + } + + getUserLists(): void { + this.refreshing = false; + this.bulkSearchService.getBulkSearchLists().subscribe(result => { + this.bulkSearchService.listEmitter.next(result.lists); + this.lists = result.lists; + this.setUser = null; + }, error => { + console.log(error); + + this.message = "Error: Fetching lists failed, see error in console"; + + }); + } + deleteList(list: string) { + this.message = ''; + if (confirm("Are you sure you want to delete this list?")){ + this.bulkSearchService.deleteBulkSearchList(list).subscribe(result => { + this.getUserLists(); + }, error => { + console.log(error); + this.message = "Error: Delete list failed. See browser console"; + + }); + } + } + + deleteListEntry(entry: any) { + let copy = JSON.parse(JSON.stringify(this.active)); + copy.lists = copy.lists.filter(item => { + return entry.key !== item.key; + }); + + let send = ''; + for (let i = 0; i < copy.lists.length; i++) { + send += copy.lists[i].key; + if (i < (copy.lists.length - 1)) { + send += ','; + } + } + this.message = ""; + this.bulkSearchService.editKeysBulkSearchLists(this.activeName, entry.key, 'remove').subscribe(response => { + this.active = copy; + this.filtered = JSON.parse(JSON.stringify(copy.lists)).slice(0, 10); + this.pagesize = 10; + this.page = 0; + }, error => { + console.log(error); + this.message = "Error: Failed to delete. See error in console"; + }); + } + + demo() { + this.bulkSearchService.saveBulkSearchEtag('90e9191d-1a81-4a53-b7ee-560bf9e68109', 'testList', 'f6fe09ff7ae9fab1').subscribe(response => { + }); + } + + getUsers() { + this.refreshing = false; + this.viewCreated = false; + this.loaded = false; + this.view = 'users'; + this.adminService.getAllUsers().subscribe(response => { + this.users = response; + }) + } + + useDraft(draft: any) { + this.refreshing = false; + this.viewCreated = false; + this.message = ''; + this.activeName = draft; + this.bulkSearchService.getSingleBulkSearchList(draft, this.setUser).subscribe(result => { + this.active = result; + this.filtered = JSON.parse(JSON.stringify(result.lists)).slice(0, 10); + this.pagesize = 10; + this.page = 0; + const uri = this.sanitizer.bypassSecurityTrustUrl('data:text/json;charset=UTF-8,' + encodeURIComponent(JSON.stringify(result.lists))); + this.downloadJsonHref = uri; + + this.view = 'single'; + }, error => { + this.message = "Error: Fetching list failed. See error in console"; + this.view = 'single'; + this.active = { + "top": 1000, + "skip": 0, + "lists": [ + + ] + } + }); + } + + uploadFile(event) { + this.refreshing = false; + this.viewCreated = false; + if (event.target.files.length !== 1) { + this.message = 'No file selected'; + this.loaded = false; + } else { + const file = event.target.files[0]; + this.filename = file.name; + const reader = new FileReader(); + reader.onloadend = (e) => { + const response = reader.result.toString().replace('\t',''); + if (this.jsonValid(response)) { + const read = JSON.parse(response); + this.pastedJson = response; + this.loaded = true; + this.importedList = response; + this.message = ''; + + } else { + this.message = 'Error: Invalid file format'; + this.loaded = false; + } + }; + reader.readAsText(event.target.files[0]); + + // this.uploaded = true; + } + } + + useFile() { + this.loading =true; + this.refreshing = false; + this.viewCreated = false; + if (this.loaded && this.pastedJson) { + const read = JSON.parse(this.pastedJson); + if (!read[0]['key']) { + this.message = 'Error: Invalid JSON format'; + this.loaded = false; + this.loading =true; + } else { + this.loaded = true; + this.importedList = read; + const mapped = read.map(x=> x.key).join(','); + this.bulkSearchService.saveBulkSearch( mapped, this.listName, 'add').subscribe(response => { + this.getUserLists(); + this.loading = false; + this.message = "List Successfully Created"; + this.viewCreated = true; + }, error => { + this.loading = false; + }) + // this.pastedJson.forEach() + this.message = ''; + } + } + } + + useCreated() { + this.view = "single"; + this.viewCreated = false; + this.refreshing = false; + this.loaded = false; + this.useDraft(this.listName); + } + + + checkLoaded() { + this.loaded = true; + try { + JSON.parse(this.pastedJson); + this.message = ''; + } catch (e) { + this.message = 'Error: Invalid JSON format in pasted string'; + this.loaded = false; + } +} + + + openInput(): void { + document.getElementById('fileInput').click(); + } + + jsonValid(file: any): boolean { + try { + JSON.parse(file); + } catch (e) { + console.log(e); + return false; + } + return true; + } + + close(){ + this.dialogRef.close(); + } + + +} diff --git a/src/app/core/bulkQuery/bulk-query.service.ts b/src/app/core/bulkQuery/bulk-query.service.ts new file mode 100644 index 000000000..b88c01737 --- /dev/null +++ b/src/app/core/bulkQuery/bulk-query.service.ts @@ -0,0 +1,46 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpParams, HttpClientJsonpModule, HttpParameterCodec } from '@angular/common/http'; +import { interval, Observable, Observer, Subject } from 'rxjs'; +import { ConfigService } from '../config/config.service'; +import { BaseHttpService } from '../base/base-http.service'; +import { + SubstanceSummary, + SubstanceDetail, + SubstanceEdit, + SubstanceName, + SubstanceCode, + SubstanceRelationship, + SubstanceRelated, + SubstanceReference +} from '../substance/substance.model'; +import { PagingResponse, ShortResult } from '../utils/paging-response.model'; +import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +import { FacetParam } from '../facets-manager/facet.model'; +import { FacetHttpParams } from '../facets-manager/facet-http-params'; +import { UtilsService } from '../utils/utils.service'; +import { switchMap, map, catchError, takeWhile } from 'rxjs/operators'; +import { ValidationResults} from '@gsrs-core/substance-form/substance-form.model'; +import {Facet, FacetQueryResponse} from '@gsrs-core/facets-manager'; +import { StructuralUnit } from '@gsrs-core/substance'; +import {HierarchyNode} from '@gsrs-core/substances-browse/substance-hierarchy/hierarchy.model'; +import { stringify } from 'querystring'; + +export class BulkQueryService extends BaseHttpService { + constructor( + public http: HttpClient, + public configService: ConfigService, + private sanitizer: DomSanitizer, + private utilsService: UtilsService, + ) { + super(configService); + } + + + + saveBulkSearch(list: string, user: string) { + const url = this.apiBaseUrl + 'substances/@bulkQueryResultList'; + list = '5b611b0d-b798-45ed-ba02-6f0a2f85986b,302cedcc-895f-421c-acf4-1348bbdb31f4,79dbcc59-e887-40d1-a0e3-074379b755e4,0e65128d-05e2-4b89-bc68-30a1c555fc2d'; + return this.http.post(url, list); + } + +} diff --git a/src/app/core/config/config.factory.ts b/src/app/core/config/config.factory.ts index bad9de5a4..c19dc2af8 100644 --- a/src/app/core/config/config.factory.ts +++ b/src/app/core/config/config.factory.ts @@ -1,6 +1,7 @@ import { ConfigService } from './config.service'; import { environment } from '../../../environments/environment'; +// eslint-disable-next-line ban-types export function configServiceFactory(startupService: ConfigService): Function { return () => startupService.load(environment); } diff --git a/src/app/core/config/config.json b/src/app/core/config/config.json index c43ee1b41..9afdb6e32 100644 --- a/src/app/core/config/config.json +++ b/src/app/core/config/config.json @@ -1,535 +1,869 @@ { - "version": "2.7.1", + "version": "3.0.3", + "contactEmail": "ncatsgsrs@mail.nih.gov", + "displayMatchApplication": "false", + "adverseEventShinyHomepageDisplay": "false", + "adverseEventShinySubstanceNameDisplay": "false", + "adverseEventShinyAdverseEventDisplay": "false", + "bannerMessage": null, + "showNameStandardizeButton": true, + "advancedSearchFacetDisplay": false, + "approvalCodeName":"UNII", + "primaryCode": "BDNUM", + "filteredDuplicationCodes": [ "BDNUM", "FDA UNII" ], + "typeaheadFields": ["Standardized_Name", "Display_Name", "CAS", "Name", "Approval_ID" ], + "loadedComponents": null, + "usefulLinks": [ + {"title": "GSRSFind Excel tools", + "description": "GSRSFind is an add-in for Microsoft Excel that gives a user access to data in GSRS for both search/browse and creation/modification purposes. (Note: write access requires valid credentials!)
GSRSFind can augment an existing spreadsheet with data from GSRS. For example, you can add chemical structures to a column of names or identifiers.", + "href": "https://gsrs.ncats.nih.gov/downloads/GinasExcelTools.zip", + "imageFile": "xlsx.png", + "templateDescription": "Click to download GSRSFind Excel tools.zip"}, + {"title": "GSRS User Guide", + "description": "The GSRS user guide is comprehensive set of instructions for searching and registering new substances of all types in GSRS.", + "href": "https://gsrs.ncats.nih.gov/downloads/Substance%20Registration%20-%20October%202019.docx", + "imageFile": "guide.png", + "templateDescription": "Click to download GSRS User Guide.docx"}, + {"title": "GSRS Data Dictionary", + "description": "The GSRS Data Dictionary is a comprehensive spreadsheet of all properties of the substance object used by GSRS. You will find descriptions, relative paths, data types, and more for every property present in the GSRS substance model.", + "href": "https://gsrs.ncats.nih.gov/downloads/GSRS_data_dictionary_11-20-19.xlsx", + "imageFile": "dictionary.png", + "templateDescription": "Click to download GSRS Data Dictionary.xlsx"}, + {"title": "GSRS API Documentation", + "description": "Swagger documentation of the GSRS API calls with working examples and implementation notes.", + "href": "https://gsrs.ncats.nih.gov/#/api#chemical%20search", + "imageFile": "API.png"}, + {"title": "GSRS JSON schema", + "description": "This file contains a JSON formatted schema used by GSRS for recording substances to help visualize the substance object described by the GSRS Data Dictionary.", + "href": "https://gsrs.ncats.nih.gov/downloads/GSRS_schema_2_0_0.json", + "imageFile": "json.png", + "templateDescription": "Click to download GSRS JSON schema.json"}, + {"title": "ISO 11238 Standard", + "description": "ISO 11238 is the standard that all substance data used by GSRS conforms to. ISO 11238 Substance Identification (SubID) provides an information model to define and identify substances within medicinal products or substances used for medicinal purposes.", + "href": "https://www.fda.gov/industry/fda-resources-data-standards/substance-identification", + "imageFile": "iso2.png"} + ], "substanceDetailsCards": [ - { - "card": "substance-overview", - "title": "overview" - }, - { - "card": "substance-primary-definition", - "title": "primary definition", - "filters": [ - { - "filterName": "equals", - "propertyToCheck": "definitionType", - "value": "ALTERNATIVE" - } - ] - }, - { - "card": "substance-alternative-definition", - "type": "SUBSTANCE->SUB_ALTERNATE", - "title": "variant concepts", - "filters": [ - { - "filterName": "exists", - "propertyToCheck": "relationships" - }, - { - "filterName": "substanceRelationships", - "value": "SUBSTANCE->SUB_ALTERNATE" - } - ] - }, - { - "card": "structure-details", - "title": "structure", - "filters": [ - { - "filterName": "equals", - "propertyToCheck": "substanceClass", - "value": "chemical|polymer" - }, - { - "filterName": "exists", - "propertyToCheck": "structure" - } - ] - }, - { - "card": "substance-names", - "title": "names", - "filters": [ - { - "filterName": "exists", - "propertyToCheck": "names" - } - ] - }, - { - "card": "substance-codes", - "type": "classification", - "filters": [ - { - "filterName": "exists", - "propertyToCheck": "codes" - }, - { - "filterName": "substanceCodes", - "value": "classification" - } - ] - }, - { - "card": "substance-codes", - "type": "identifiers", - "filters": [ - { - "filterName": "exists", - "propertyToCheck": "codes" - }, - { - "filterName": "substanceCodes", - "value": "identifiers" - } - ] - }, - { - "card": "substance-subunits", - "title": "subunits", - "filters": [ - { - "filterName": "equals", - "propertyToCheck": "substanceClass", - "value": "protein" - }, - { - "filterName": "exists", - "propertyToCheck": "protein.subunits" - } - ] - }, - { - "card": "substance-subunits", - "title": "subunits", - "filters": [ + { + "card": "substance-overview", + "title": "Overview" + }, + { + "card": "substance-primary-definition", + "title": "Primary Definition", + "filters": [ + { + "filterName": "equals", + "propertyToCheck": "definitionType", + "value": "ALTERNATIVE" + } + ] + }, + { + "card": "substance-alternative-definition", + "type": "SUBSTANCE->SUB_ALTERNATE", + "title": "Alternative Definitions", + "filters": [ + { + "filterName": "exists", + "propertyToCheck": "relationships" + }, + { + "filterName": "substanceRelationships", + "value": "SUBSTANCE->SUB_ALTERNATE" + } + ] + }, + { + "card": "substance-relationships", + "type": "SUB_CONCEPT->SUBSTANCE", + "title": "Subconcepts Variants", + "filters": [ + { + "filterName": "exists", + "propertyToCheck": "relationships" + }, + { + "filterName": "substanceRelationships", + "value": "SUB_CONCEPT->SUBSTANCE" + } + ] + }, + { + "card": "substance-concept-definition", + "title": "Concept Definition", + "filters": [ + { + "filterName": "equals", + "propertyToCheck": "substanceClass", + "value": "concept" + } + ] + }, + { + "card": "substance-mixture-parent", + "title": "Found in Mixtures", + "filters": [ + { + "filterName": "exists", + "propertyToCheck": "$$mixtureParents" + } + ] + }, + { + "card": "substance-ssg1-parent", + "title": "Found in G1SS", + "filters": [ + { + "filterName": "exists", + "propertyToCheck": "$$constituentParents" + } + ] + }, + { "card": "substance-hierarchy", + "title": "Substance Hierarchy" + }, + { + "card": "structure-details", + "title": "Chemical Structure", + "filters": [ + { + "filterName": "equals", + "propertyToCheck": "substanceClass", + "value": "chemical|polymer" + }, + { + "filterName": "exists", + "propertyToCheck": "structure" + } + ] + }, + { + "card": "substance-moieties", + "title": "Chemical Moieties", + "filters": [ + { + "filterName": "equals", + "propertyToCheck": "substanceClass", + "value": "chemical" + }, + { + "filterName": "exists", + "propertyToCheck": "moieties" + } + ] + }, + { + "card": "substance-subunits", + "title": "Protein Subunits", + "filters": [ + { + "filterName": "equals", + "propertyToCheck": "substanceClass", + "value": "protein" + }, + { + "filterName": "exists", + "propertyToCheck": "protein.subunits" + } + ] + }, + { + "card": "substance-glycosylation", + "title": "Protein Glycosylation", + "filters": [ + { + "filterName": "equals", + "propertyToCheck": "substanceClass", + "value": "protein" + }, + { + "filterName": "anyExists", + "propertyToCheck": "protein.glycosylation.glycosylationType|protein.glycosylation.CGlycosylationSites|protein.glycosylation.NGlycosylationSites|protein.glycosylation.OGlycosylationSites" + } + ] + }, + { + "card": "substance-disulfide-links", + "title": "Protein Disulfide Links", + "filters": [ + { + "filterName": "equals", + "propertyToCheck": "substanceClass", + "value": "protein" + }, + { + "filterName": "exists", + "propertyToCheck": "protein.disulfideLinks" + } + ] + }, + { + "card": "substance-other-links", + "title": "Protein Other Links", + "filters": [ + { + "filterName": "equals", + "propertyToCheck": "substanceClass", + "value": "protein" + }, + { + "filterName": "exists", + "propertyToCheck": "protein.otherLinks" + } + ] + }, + { + "card": "substance-subunits", + "title": "Nuceic Acid Subunits", + "filters": [ + { + "filterName": "equals", + "propertyToCheck": "substanceClass", + "value": "nucleicAcid" + }, + { + "filterName": "exists", + "propertyToCheck": "nucleicAcid.subunits" + } + ] + }, + { + "card": "substance-na-sugars", + "title": "Nucleic Acid Sugars", + "filters": [ + { + "filterName": "equals", + "propertyToCheck": "substanceClass", + "value": "nucleicAcid" + }, + { + "filterName": "exists", + "propertyToCheck": "nucleicAcid.sugars" + } + ] + }, + { + "card": "substance-na-linkages", + "title": "Nucleic Acid Linkages", + "filters": [ + { + "filterName": "equals", + "propertyToCheck": "substanceClass", + "value": "nucleicAcid" + }, + { + "filterName": "exists", + "propertyToCheck": "nucleicAcid.linkages" + } + ] + }, + { + "card": "substance-polymer-structure", + "title": "Polymer Display Structure", + "filters": [ + { + "filterName": "equals", + "propertyToCheck": "substanceClass", + "value": "polymer" + } + ] + }, + { + "card": "substance-monomers", + "title": "Polymer Monomers and Starting Materials", + "filters": [ + { + "filterName": "equals", + "propertyToCheck": "substanceClass", + "value": "polymer" + }, + { + "filterName": "exists", + "propertyToCheck": "polymer.monomers" + } + ] + }, + { + "card": "substance-structural-units", + "title": "Polymer Structural Units", + "filters": [ + { + "filterName": "equals", + "propertyToCheck": "substanceClass", + "value": "polymer" + }, + { + "filterName": "exists", + "propertyToCheck": "polymer.structuralUnits" + } + ] + }, + { + "card": "substance-mixture-source", + "title": "Mixture Source", + "filters": [ + { + "filterName": "equals", + "propertyToCheck": "substanceClass", + "value": "mixture" + }, + { + "filterName": "exists", + "propertyToCheck": "mixture.parentSubstance" + } + ] + }, + { + "card": "substance-mixture-components", + "title": "Mixture Components", + "filters": [ + { + "filterName": "equals", + "propertyToCheck": "substanceClass", + "value": "mixture" + }, + { + "filterName": "exists", + "propertyToCheck": "mixture.components" + } + ] + }, + { + "card": "substance-constituents", + "title": "G1 Specified Substance Constituents", + "filters": [ + { + "filterName": "equals", + "propertyToCheck": "substanceClass", + "value": "specifiedSubstanceG1" + }, + { + "filterName": "exists", + "propertyToCheck": "specifiedSubstance.constituents" + } + ] + }, + { + "card": "substance-modifications", + "title": "Substance Modifications", + "filters": [ + { + "filterName": "anyExists", + "propertyToCheck": "modifications.structuralModifications|modifications.physicalModifications|modifications.agentModifications" + } + ] + }, + { + "card": "substance-ssg-parent-substance", + "title": "G3 Specified Substance Parent", + "filters": [ + { + "filterName": "exists", + "propertyToCheck": "specifiedSubstanceG3.parentSubstance" + } + ] + }, + { + "card": "substance-ssg-definition", + "title": "G3 Specified Substance Definition", + "filters": [ + { + "filterName": "exists", + "propertyToCheck": "specifiedSubstanceG3.definition" + } + ] + }, + { + "card": "substance-ssg-grade", + "title": "G3 Specified Substance Grade", + "filters": [ + { + "filterName": "exists", + "propertyToCheck": "specifiedSubstanceG3.grade" + } + ] + }, + { + "card": "substance-names", + "title": "Names and Synonyms", + "filters": [ + { + "filterName": "exists", + "propertyToCheck": "names" + } + ] + }, + { + "card": "substance-codes", + "type": "Codes - Classifications", + "filters": [ + { + "filterName": "exists", + "propertyToCheck": "codes" + }, + { + "filterName": "substanceCodes", + "value": "classification" + } + ] + }, + { + "card": "substance-codes", + "type": "Codes - Identifiers", + "filters": [ + { + "filterName": "exists", + "propertyToCheck": "codes" + }, + { + "filterName": "substanceCodes", + "value": "identifiers" + } + ] + }, + { + "card": "substance-properties", + "title": "Characteristic Attributes", + "filters": [ + { + "filterName": "exists", + "propertyToCheck": "properties" + } + ] + }, + { + "card": "substance-relationships", + "type": "ACTIVE MOIETY", + "title": "Relationships: Active Moiety", + "filters": [ + { + "filterName": "exists", + "propertyToCheck": "relationships" + }, + { + "filterName": "substanceRelationships", + "value": "ACTIVE MOIETY" + } + ] + }, + { + "card": "substance-relationships", + "type": "METABOLITE", + "title": "Relationships: Metabolites", + "filters": [ + { + "filterName": "exists", + "propertyToCheck": "relationships" + }, + { + "filterName": "substanceRelationships", + "value": "METABOLITE" + } + ] + }, + { + "card": "substance-relationships", + "type": "IMPURITY", + "title": "Relationships: Impurities", + "filters": [ + { + "filterName": "exists", + "propertyToCheck": "relationships" + }, + { + "filterName": "substanceRelationships", + "value": "IMPURITY" + } + ] + }, + { + "card": "substance-relationships", + "type": "CONSTITUENT", + "title": "Relationships: Constitents", + "filters": [ + { + "filterName": "exists", + "propertyToCheck": "relationships" + }, + { + "filterName": "substanceRelationships", + "value": "CONSTITUENT" + } + ] + }, + { + "card": "substance-relationships", + "type": "RELATIONSHIPS", + "title": "Relationships", + "filters": [ + { + "filterName": "exists", + "propertyToCheck": "relationships" + }, + { + "filterName": "substanceRelationships", + "value": [ + "METABOLITE", + "IMPURITY", + "ACTIVE MOIETY", + "CONSTITUENT", + "SUB_CONCEPT->SUBSTANCE" + ] + } + ] + }, + { + "card": "substance-notes", + "title": "Notes", + "filters": [ + { + "filterName": "exists", + "propertyToCheck": "notes" + } + ] + }, + { + "card": "substance-references", + "title": "References", + "filters": [ + { + "filterName": "exists", + "propertyToCheck": "references" + } + ] + }, + { + "card": "substance-audit-info", + "title": "Audit Information" + }, + { + "card": "substance-history", + "title": "Record History", + "filters": [ + { + "filterName": "hasCredentials", + "propertyToCheck": "admin" + } + ] + } + ], + "facets": { + "substances": { + "default": [ + "Deprecated", + "Record Status", + "Substance Class", + "GInAS Tag", + "Code System", + "ATC Level 1", + "ATC Level 2", + "ATC Level 3", + "ATC Level 4", + "DME Reactions", + "Moiety Type", + "Molecular Weight", + "SubstanceStereochemistry", + "Relationships", + "Record Level Access", + "Display Name Level Access", + "Definition Level Access", + "Protein Type", + "Protein Subtype", + "modified" + ], + "admin": [ + "Record Created By", + "Record Last Edited By", + "Record Create Date", + "Record Last Edited", + "root_lastEdited", + "root_created", + "root_lastEditedBy", + "root_approved", + "Approved By", + "Material Type", + "Family", + "Parts" + ], + "facetView": [ { - "filterName": "equals", - "propertyToCheck": "substanceClass", - "value": "nucleicAcid" + "category": "Default", + "facets": ["Deprecated", "Record Status", "Substance Class", "GInAS Tag", "GInAS Domain", "Code System", "ATC Level 1", "ATC Level 2", "ATC Level 3", "ATC Level 4", "WHO-SDG Level 1", "WHO-SDG Level 2", "DME Reactions", "Application Center", "Application Type", "Application Status", "Material Type", "Family", "Parts", "Moiety Type", "Molecular Weight", "SubstanceStereochemistry", "root_approved", "Approved By", "root_created", "Record Created By", "root_lastEdited", "root_lastEditedBy", "Record Level Access", "Display Name Level Access", "Definition Level Access", "Protein Type", "Product Ingredient Type", "Product Type", "Product Dosage Form", "Definition Level", "Clinical Trial Count", "Relationships"] }, { - "filterName": "exists", - "propertyToCheck": "nucleicAcid.subunits" - } - ] - }, - { - "card": "substance-glycosylation", - "title": "glycosylation", - "filters": [ - { - "filterName": "equals", - "propertyToCheck": "substanceClass", - "value": "protein" + "category": "Record Data", + "facets": ["Deprecated", "Record Status", "Substance Class", "Definition Type", "Definition Level", "GInAS Tag", "GInAS Domain", "GInAS Language", "GInAS Name Jurisdiction", "Code System", "root_approved", "Approved By", "root_created", "Record Created By", "root_lastEdited", "root_lastEditedBy", "Record Level Access", "Display Name Level Access", "Definition Level Access", "Validation", "Reference Type", "GInAS Document Tag", "Relationships"] }, { - "filterName": "anyExists", - "propertyToCheck": "protein.glycosylation.glycosylationType|protein.glycosylation.CGlycosylationSites|protein.glycosylation.NGlycosylationSites|protein.glycosylation.OGlycosylationSites" - } - ] - }, - { - "card": "substance-disulfide-links", - "title": "disulfide links", - "filters": [ - { - "filterName": "equals", - "propertyToCheck": "substanceClass", - "value": "protein" + "category": "User Data", + "facets": ["Record Created By", "Approved By", "root_lastEditedBy"] }, { - "filterName": "exists", - "propertyToCheck": "protein.disulfideLinks" - } - ] - }, - { - "card": "substance-other-links", - "title": "other links", - "filters": [ - { - "filterName": "equals", - "propertyToCheck": "substanceClass", - "value": "protein" + "category": "Chemistry", + "facets": ["Record Status", "Substance Class", "Code System", "GInAS Tag", "Moiety Type", "Molecular Weight", "SubstanceStereochemistry", "Molecular Formula", "Polymer Class", "GInAS Subclass", "Polymer Geometry"] }, { - "filterName": "exists", - "propertyToCheck": "protein.otherLinks" - } - ] - }, - { - "card": "substance-relationships", - "type": "IMPURITY", - "title": "impurities", - "filters": [ - { - "filterName": "exists", - "propertyToCheck": "relationships" + "category": "Proteins, DNA and RNA", + "facets": ["Record Status", "Substance Class", "Code System", "GInAS Tag", "Protein Type", "Modifications", "Glycosylation Type", "Protein Subtype", "Linkage Type", "Nucleic Acid Subtype", "Sequence Origin", "Sequence Type", "Molecular Weight"] }, { - "filterName": "substanceRelationships", - "value": "IMPURITY" - } - ] - }, - { - "card": "substance-relationships", - "type": "METABOLITE", - "title": "metabolites", - "filters": [ - { - "filterName": "exists", - "propertyToCheck": "relationships" + "category": "Organisms", + "facets": ["Record Status", "Substance Class", "Code System", "GInAS Tag", "Material Class", "Material Type", "Modifications", "Parts", "Family", "Genus", "Species", "Author"] }, { - "filterName": "substanceRelationships", - "value": "METABOLITE" + "category": "Codes", + "facets": ["ATCC", "BDNUM", "BIOLOGIC SUBSTANCE CLASSIFICATION CODE", "CAS", "CAYMAN", "CERES", "CFR", "CFSAN PSEUDO CAS", "ChEMBL", "CLINICAL_TRIALS.GOV", "Code System", "CODEX ALIMENTARIUS (GSFA)", "COSMETIC INGREDIENT REVIEW (CIR)", "DASH INDICATION", "DEA NO.", "DME Reactions", "DRUG BANK", "DRUG CENTRAL", "DSLD", "EC (ENZYME CLASS)", "EC SCIENTIFIC COMMITTEE ON CONSUMER SAFETY OPINION", "ECHA (EC/EINECS)", "EMA ASSESSMENT REPORTS", "EMA VETERINARY ASSESSMENT REPORTS", "EPA CompTox", "EPA PESTICIDE CODE", "EU CLINICAL TRIALS REGISTER", "EU FOOD ADDITIVES", "EU-Orphan Drug", "EVMPD", "FARM SUBSTANCE ID", "FDA ORPHAN DRUG", "FDA UNII", "Food Contact Sustance Notif, (FCN No.)", "GENE", "GRIN", "HEALTH -CANADA NHP INGREDIENT MONOGRAPH", "HEALTH-CANADA NHP INGREDIENT RECORD", "HSDB", "IARC", "INCB IDS CODE", "INN", "INS", "ITIS", "IUPHAR", "JAPANESE REVIEW", "JECFA EVALUATION", "JECFA MONOGRAPH", "JMPR-PESTICIDE RESIDUE", "KEGG", "LactMed", "LIVERTOX", "LOINC", "MANUFACTURER PRODUCT INFORMATION", "MERCK INDEX", "MESH", "MIRBASE", "MPNS", "NCBI TAXONOMY", "NCI_THESAURUS", "NDF-RT", "NSC", "Other", "PFAF", "PHAROS", "PROTEIN ID", "PUBCHEM", "RXCUI", "STARI", "SUPERSEDED_BD_NUM", "SWGDRUG", "UCSF-FDA TRANSPORTAL", "UNII", "UNIPROT", "USDA PLANTS", "USP_CATALOG", "USP-HMC", "WEB RESOURCE", "WHO INTERNATIONAL PHARMACOPEIA", "WHO INTERNATIONAL PHARMACPOEIA", "WHO-ATC", "WHO-ESSENTIAL MEDICINES LIST", "WHO-SDG", "WHO-SDG Level 1", "WHO-SDG Level 2", "WHO-VATC", "WIKIPEDIA", "YELLOW LIST"] } ] - }, + } + }, + "codeSystemOrder": [ + "BDNUM", + "CAS", + "WHO-ATC", + "EVMPD", + "NCI" + ], + "homeHeader": "Global Substance Registration System - GSRS", + "homeContents": "GSRS has been developed to assist regulators in managing substance information. It was designed to facilitate global monitoring of human and animal medicinal, food, tobacco, and cosmetic products. GSRS provides unique substance identifiers consistent with the ISO 11238 standard. FDA’s GSRS specifically generates the Unique Ingredient Identifiers (UNIIs) used in electronic listing and other regulatory activities.", + "relationshipsVisualizationUri": "", + "navItems": [ { - "card": "substance-relationships", - "type": "ACTIVE MOIETY", - "title": "active moiety", - "filters": [ - { - "filterName": "exists", - "propertyToCheck": "relationships" - }, - { - "filterName": "substanceRelationships", - "value": "ACTIVE MOIETY" - } - ] + "display": "Browse Substances", + "path": "browse-substance", + "order": 10 }, { - "card": "substance-relationships", - "type": "CONSTITUENT", - "title": "constituents", - "filters": [ + "display": "Search", + "children": [ { - "filterName": "exists", - "propertyToCheck": "relationships" + "component": "", + "display": "Advanced Search", + "path": "advanced-search", + "order": 180 }, { - "filterName": "substanceRelationships", - "value": "CONSTITUENT" - } - ] - }, - { - "card": "substance-relationships", - "type": "SUB_CONCEPT->SUBSTANCE", - "title": "variant concepts", - "filters": [ - { - "filterName": "exists", - "propertyToCheck": "relationships" + "component": "", + "display": "Bulk Search", + "path": "bulk-search", + "order": 190 }, { - "filterName": "substanceRelationships", - "value": "SUB_CONCEPT->SUBSTANCE" - } - ] - }, - { - "card": "substance-relationships", - "type": "RELATIONSHIPS", - "title": "relationships", - "filters": [ - { - "filterName": "exists", - "propertyToCheck": "relationships" + "component": "", + "display": "Structure Search", + "path": "structure-search", + "order": 200 }, { - "filterName": "substanceRelationships", - "value": [ - "METABOLITE", - "IMPURITY", - "ACTIVE MOIETY", - "CONSTITUENT", - "SUB_CONCEPT->SUBSTANCE" - ] + "component": "", + "display": "Sequence Search", + "path": "sequence-search", + "order": 210 } ] - }, + }, { - "card": "substance-notes", - "title": "notes", - "filters": [ + "display": "Help", + "order": 60, + "children": [ { - "filterName": "exists", - "propertyToCheck": "notes" - } - ] - }, - { - "card": "substance-audit-info", - "title": "audit info" - }, - { - "card": "substance-references", - "title": "references", - "filters": [ - { - "filterName": "exists", - "propertyToCheck": "references" - } - ] - }, - { - "card": "substance-moieties", - "title": "moieties", - "filters": [ - { - "filterName": "equals", - "propertyToCheck": "substanceClass", - "value": "chemical" + "display": "User Manual", + "href": "https://gsrs.ncats.nih.gov/downloads/Substance%20Registration%20-%20October%202019.docx", + "order": 10 }, { - "filterName": "exists", - "propertyToCheck": "moieties" + "component": "", + "kind": "contact-us", + "display": "Email GSRS Support", + "mailToPath": "mailto:%s", + "queryParams": {"subject" : "Support request"}, + "order": 30 } ] }, { - "card": "substance-concept-definition", - "title": "concept definition", - "filters": [ - { - "filterName": "equals", - "propertyToCheck": "substanceClass", - "value": "concept" - } - ] - }, + "component": "", + "kind": "contact-us", + "display": "Contact Us", + "mailToPath": "mailto:%s", + "queryParams": {"subject" : "Support request"}, + "order": 30 + } + + ], + "substanceSelectorProperties": [ + "root_names_name", + "root_approvalID", + "root_codes_BDNUM", + "root_codes_CAS", + "root_codes_ECHA\\ \\(EC\\/EINECS\\)" + ], + "homeDynamicLinks": [ { - "card": "substance-polymer-structure", - "title": "display structure", - "filters": [ - { - "filterName": "equals", - "propertyToCheck": "substanceClass", - "value": "polymer" - } - ] + "display": "Chemicals", + "facetName": "Substance Class", + "facetValue":"chemical" }, { - "card": "substance-monomers", - "title": "monomers", - "filters": [ - { - "filterName": "equals", - "propertyToCheck": "substanceClass", - "value": "polymer" - }, - { - "filterName": "exists", - "propertyToCheck": "polymer.monomers" - } - ] + "display": "Polymers", + "facetName": "Substance Class", + "facetValue":"polymer" }, { - "card": "substance-structural-units", - "title": "Structural Units", - "filters": [ - { - "filterName": "equals", - "propertyToCheck": "substanceClass", - "value": "polymer" - }, - { - "filterName": "exists", - "propertyToCheck": "polymer.structuralUnits" - } - ] + "display": "Structurally Diverse", + "facetName": "Substance Class", + "facetValue":"structurallyDiverse" }, { - "card": "substance-mixture-components", - "title": "mixture components", - "filters": [ - { - "filterName": "equals", - "propertyToCheck": "substanceClass", - "value": "mixture" - }, - { - "filterName": "exists", - "propertyToCheck": "mixture.components" - } - ] + "display": "Proteins", + "facetName": "Substance Class", + "facetValue":"protein" }, { - "card": "substance-constituents", - "title": "specified substance constituents", - "filters": [ - { - "filterName": "equals", - "propertyToCheck": "substanceClass", - "value": "specifiedSubstanceG1" - }, - { - "filterName": "exists", - "propertyToCheck": "specifiedSubstance.constituents" - } - ] + "display": "Nucleic Acids", + "facetName": "Substance Class", + "facetValue":"nucleicAcid" }, { - "card": "substance-mixture-source", - "title": "mixture source", - "filters": [ - { - "filterName": "equals", - "propertyToCheck": "substanceClass", - "value": "mixture" - }, - { - "filterName": "exists", - "propertyToCheck": "mixture.parentSubstance" - } - ] - }, + "display": "Concepts", + "facetName": "Substance Class", + "facetValue":"concept" + } + ], + "registrarDynamicLinks": [ { - "card": "substance-modifications", - "title": "substance modifications", - "filters": [ - { - "filterName": "anyExists", - "propertyToCheck": "modifications.structuralModifications|modifications.physicalModifications|modifications.agentModifications" - } - ] + "display": "Chemicals", + "facets":[{ + "facetName": "Substance Class", + "facetValue":"chemical" + }] }, { - "card": "substance-na-sugars", - "title": "sugars", - "filters": [ - { - "filterName": "equals", - "propertyToCheck": "substanceClass", - "value": "nucleicAcid" - }, - { - "filterName": "exists", - "propertyToCheck": "nucleicAcid.sugars" - } - ] + "display": "Polymers", + "facets":[{ + "facetName": "Substance Class", + "facetValue":"polymer" + }] }, { - "card": "substance-na-linkages", - "title": "linkages", - "filters": [ - { - "filterName": "equals", - "propertyToCheck": "substanceClass", - "value": "nucleicAcid" - }, - { - "filterName": "exists", - "propertyToCheck": "nucleicAcid.linkages" - } - ] + "display": "Structurally Diverse", + "facets":[{ + "facetName": "Substance Class", + "facetValue":"structurallyDiverse" + }] + }, { - "card": "substance-properties", - "title": "characteristic attributes", - "filters": [ - { - "filterName": "exists", - "propertyToCheck": "properties" - } - ] + "display": "Proteins", + "facets":[{ + "facetName": "Substance Class", + "facetValue":"protein" + }] + }, { - "card": "substance-history", - "title": "history", - "filters": [ - { - "filterName": "hasCredentials", - "propertyToCheck": "admin" - } - ] + "display": "Nucleic Acids", + "facets":[{ + "facetName": "Substance Class", + "facetValue":"nucleicAcid" + }] + }, { - "card": "substance-ssg-definition", - "title": "definition", - "filters": [ - { - "filterName": "exists", - "propertyToCheck": "specifiedSubstanceG3.definition" - } - ] - }, - { - "card": "substance-ssg-parent-substance", - "title": "parent substance", - "filters": [ - { - "filterName": "exists", - "propertyToCheck": "specifiedSubstanceG3.parentSubstance" - } - ] + "display": "Concepts", + "facets":[{ + "facetName": "Substance Class", + "facetValue":"concept" + }] + }, { - "card": "substance-ssg-grade", - "title": "grade", - "filters": [ - { - "filterName": "exists", - "propertyToCheck": "specifiedSubstanceG3.grade" - } - ] + "display": "SSG1", + "facets":[{ + "facetName": "Substance Class", + "facetValue":"specifiedSubstanceG1" + }] + } ], - "facets": { - "substances": { - "default": [ - "Deprecated", - "Record Status", - "Substance Class", - "GInAS Tag", - "Code System", - "ATC Level 1", - "ATC Level 2", - "ATC Level 3", - "ATC Level 4", - "DME Reactions", - "Moiety Type", - "Molecular Weight", - "SubstanceStereochemistry", - "Relationships", - "Record Level Access", - "Display Name Level Access", - "Definition Level Access", - "Protein Type", - "Protein Subtype", - "modified" - ], - "admin": [ - "Record Created By", - "root_lastEdited", - "root_lastEditedBy", - "root_approved", - "Approved By", - "Material Type", - "Family", - "Parts" - ] + "registrarDynamicLinks2": [ + { + "display": "Pending Chemicals", + "facets":[{ + "facetName": "Substance Class", + "facetValue":"chemical" + }, + {"facetName": "Record Status", + "facetValue":"pending"} + ] + }, + { + "display": "Pending Polymers", + "facets":[{ + "facetName": "Substance Class", + "facetValue":"polymer" + }, + {"facetName": "Record Status", + "facetValue":"pending"}] + }, + { + "display": "Pending Structurally Diverse", + "facets":[{ + "facetName": "Substance Class", + "facetValue":"structurallyDiverse" + }, + {"facetName": "Record Status", + "facetValue":"pending"}] + + }, + { + "display": "Pending Proteins", + "facets":[{ + "facetName": "Substance Class", + "facetValue":"protein" + }, + {"facetName": "Record Status", + "facetValue":"pending"}] + + }, + { + "display": "Pending Nucleic Acids", + "facets":[{ + "facetName": "Substance Class", + "facetValue":"nucleicAcid" + }, + {"facetName": "Record Status", + "facetValue":"pending"}] + + }, + { + "display": "Pending Concepts", + "facets":[{ + "facetName": "Substance Class", + "facetValue":"concept" + }, + {"facetName": "Record Status", + "facetValue":"pending"}] + }, + { + "display": "Pending SSG1", + "facets":[{ + "facetName": "Substance Class", + "facetValue":"specifiedSubstanceG1" + }, + {"facetName": "Record Status", + "facetValue":"pending"}] + } - }, - "codeSystemOrder": [ - "BDNUM", - "CAS", - "WHO-ATC", - "EVMPD", - "NCI" ], - "substanceSelectorProperties": [ - "root_names_name", - "root_approvalID", - "root_codes_BDNUM", - "root_codes_CAS", - "root_codes_ECHA\\ \\(EC\/EINECS\\)" - ] -} \ No newline at end of file + "substance":{ + "linking":{ + "keyType":{ + "default":"BDNUM", + "orgDisplayKeyType":"BDNUM", + "clinicalTrialKeyType":"UUID" + } + } + } +} diff --git a/src/app/core/config/config.model.ts b/src/app/core/config/config.model.ts index 8e5d6f289..7ef252644 100644 --- a/src/app/core/config/config.model.ts +++ b/src/app/core/config/config.model.ts @@ -1,18 +1,25 @@ export interface Config { apiBaseUrl?: string; + gsrsHomeBaseUrl?: string; + apiSSG4mBaseUrl?: string; apiUrlDomain?: string; + logoutRedirectUrl?: string; googleAnalyticsId?: string; version?: string; buildDateTime?: string; substanceDetailsCards?: Array; facets?: { [name: string]: { - [permission: string]: Array - } + [permission: string]: Array; + }; }; codeSystemOrder?: Array; + homeHeader?: string; + homeContents?: string; contactEmail?: string; defaultCodeSystem?: string; + primaryCode?: string; + typeaheadFields?: Array; navItems?: Array; substanceSelectorProperties?: Array; displayMatchApplication?: string; @@ -22,6 +29,9 @@ export interface Config { adverseEventShinyHomepageURL?: string; adverseEventShinySubstanceNameURL?: string; adverseEventShinyAdverseEventURL?: string; + // eslint-disable-next-line member-delimiter-style + FAERSDashboardAdverseEventUrl?: string; + advancedSearchFacetDisplay?: boolean; facetDisplay?: Array; relationshipsVisualizationUri?: string; customToolbarComponent?: string; @@ -29,6 +39,44 @@ export interface Config { disableReferenceDocumentUpload?: boolean; externalSiteWarning?: ExternalSiteWarning; pfdaBaseUrl?: string; + homeDynamicLinks?: Array; + registrarDynamicLinks?: Array; + registrarDynamicLinks2?: Array; + bannerMessage?: string; + substance?: any; + showOldLinks?: boolean; + loadedComponents?: LoadedComponents; + showNameStandardizeButton?: boolean; + molWeightRounding?: number; + usefulLinks?: Array; + approvalCodeName?: string; + approvalType?: string; + ssg4Form?: string; + filteredDuplicationCodes?: Array; + autoSaveWait?: number; + authenticateAs?: AuthenticateAs; + userRegistration?: any + elementLabelDisplay?:any; + bulkSearch?: any + useDataUrl?: any; + userProfile?: any; + stagingArea?: StagingAreaSettings; + privacyStatement: string; + appId?: string; +} + +export interface StagingAreaSettings { + mergeAction?: boolean; +} + +export interface LoadedComponents { + applications?: boolean; + products?: boolean; + clinicaltrials?: boolean; + adverseevents?: boolean; + impurities?: boolean; + ssg4m?: boolean; + userRegistration?: boolean; } export interface SubstanceDetailsCard { @@ -45,14 +93,37 @@ export interface SubstanceCardFilterParameters { value?: any; propertyInArray?: string; order?: number; + countMinimum?: number; + countMaximum?: number; } export interface NavItem { display: string; + id?:string; + kind?: string; path?: string; externalPath?: string; + mailToPath?: string; + queryParams?: any; order?: number; children?: Array; + component?: string; +} + +export interface UsefulLink { + title: string; + description: string; + href: string; + imageFile: string; + linkHref: string; + templateDescription?: string; +} + +export interface AuthenticateAs { + apiUsername?: string, + apiPassword?: string, + apiKey?: string, + apiToken?: string; } export interface SessionExpirationWarning { diff --git a/src/app/core/config/config.service.ts b/src/app/core/config/config.service.ts index 2015cbda4..eb740a940 100644 --- a/src/app/core/config/config.service.ts +++ b/src/app/core/config/config.service.ts @@ -11,8 +11,26 @@ export class ConfigService { private _configData: Config; private _environment: Environment; + // these are a set of callback methods which can be registered + // to trigger on data load. These are each called exactly once + // after a successful load, and are used to resolve promises from + // the afterLoad method + private _triggers: Array = []; + constructor(private http: HttpClient) { } + get configData(): Config { + return this._configData; + } + + set configData(configData: Config) { + this._configData = configData; + } + + get environment(): Environment { + return this._environment; + } + // This is the method you want to call at bootstrap // Important: It should return a Promise load(environment: Environment): Promise { @@ -20,7 +38,7 @@ export class ConfigService { this._configData = null; const configFilePath = environment.configFileLocation ? - environment.configFileLocation : `${environment.baseHref || '/'}assets/data/config.json`; + environment.configFileLocation : `${environment.baseHref || ''}assets/data/config.json`; return this.http .get(configFilePath) @@ -50,9 +68,11 @@ export class ConfigService { const filteredNavItems = config.navItems.filter(navItem => { if (navItem.children != null && navItem.children.length > 0) { let isNotExisting = true; + // eslint-disable-next-line prefer-for-of for (let i = 0; i < navItemsCopy.length; i++) { if (navItemsCopy[i].display === navItem.display && navItemsCopy[i].children != null) { navItemsCopy[i].children = navItemsCopy[i].children.concat(navItem.children); + // eslint-disable-next-line arrow-body-style navItemsCopy[i].children.sort((a, b) => { return a.order - b.order; }); @@ -72,19 +92,37 @@ export class ConfigService { } config.navItems = navItemsCopy; this._configData = config; + //this tells the service to resolve any outstanding Promises for loaded data + this.executeOnLoadTriggers(); }) .catch((err: any) => Promise.resolve()); } - get configData(): Config { - return this._configData; + // this is the method that gets by the load process itself and is called + // once after the config service is loaded, going through each registered + // trigger and executing it, then clearing the trigger list. It shouldn't be + // called more than once. + executeOnLoadTriggers() { + this._triggers.map(cb => cb()); + this._triggers.length = 0; } - set configData(configData: Config) { - this._configData = configData; - } + // this returns a Promise to return the configData after loading, + // effectively giving a callback for when the config service is + // fully loaded. If the service is already loaded it returns + // a promise which resolves immediately. + afterLoad(): Promise { + if (this._configData) { + return new Promise((resolve, reject) => { + resolve(this._configData); + }); + } else { + return new Promise((resolve, reject) => { + this._triggers.push(() => { + resolve(this._configData); + }); + }); + } + }; - get environment(): Environment { - return this._environment; - } } diff --git a/src/app/core/config/nav-items.constant.ts b/src/app/core/config/nav-items.constant.ts index eb303935a..e65eb82b1 100644 --- a/src/app/core/config/nav-items.constant.ts +++ b/src/app/core/config/nav-items.constant.ts @@ -1,21 +1,6 @@ import { NavItem } from '@gsrs-core/config'; export const navItems: Array = [ - { - display: 'Browse Substances', - path: 'browse-substance', - order: 10 - }, - { - display: 'Structure Search', - path: 'structure-search', - order: 20 - }, - { - display: 'Sequence Search', - path: 'sequence-search', - order: 30 - }, { display: 'Register', order: 40, diff --git a/src/app/core/controlled-vocabulary/controlled-vocabulary.service.ts b/src/app/core/controlled-vocabulary/controlled-vocabulary.service.ts index fac687d34..f9a5bd370 100644 --- a/src/app/core/controlled-vocabulary/controlled-vocabulary.service.ts +++ b/src/app/core/controlled-vocabulary/controlled-vocabulary.service.ts @@ -94,6 +94,93 @@ export class ControlledVocabularyService extends BaseHttpService { }); } + getStructure(structure: string) { + const url = this.baseUrl + 'render?structure=' + structure + '&size=150&standardize=true'; + return this.http.get(url); + } + getStructureUrl(structure: string) { + structure = structure.replace(/[;]/g, '%3B') + .replace(/[#]/g, '%23') + .replace(/[+]/g, '%2B') + .replace(/[|]/g, '%7C'); + const url = this.baseUrl + 'render?structure=' + structure + '&size=150&standardize=true'; + return url; + } + + getStructureUrlFragment(structure: string) { + structure = structure.replace(/%/g, "%25").replace(/#/g, "%23").replace(/[;]/g, "%3B").replace(/[+]/g, "%2B"); + const url = this.baseUrl + 'render?structure=' + structure + '&size=150&standardize=true'; + return url; + } + + + search(domain: string, query: string): Observable> { + return new Observable(observer => { + const subscription = this.getDomainVocabulary(domain).subscribe(response => { + const filteredTerms = response[domain].list.filter(term => term.value.toLowerCase().indexOf(query.toLowerCase()) > -1); + let sortedTerms = []; + + if (filteredTerms != null && filteredTerms.length) { + sortedTerms = filteredTerms.sort((termA, termB) => { + if (termA < termB) { + return -1; + } + if (termA > termB) { + return 1; + } + return 0; + }); + } + + observer.next(sortedTerms); + subscription.unsubscribe(); + }); + }); + } + + public fetchFullVocabulary(domain: string): Observable { + + const url = `${this.apiBaseUrl}vocabularies/search`; + let params = new HttpParams(); + params = params.append('top', '100000'); + + let domainLuceneQuery = ''; + const responseDomainVocabulary: VocabularyDictionary = {}; + this.vocabularyLoadingIndicators[domain] = true; + if (this.vocabularySubject[domain] == null) { + this.vocabularySubject[domain] = new Subject(); + } + + responseDomainVocabulary[domain] = { + dictionary: {}, + list: [] + }; + + domainLuceneQuery += `root_domain:${domain}`; + params = params.append('q', domainLuceneQuery); + const options = { + params: params + }; + return this.http.get>(url, options); + } + + public validateVocab(vocab: any): Observable { + const url = `${this.apiBaseUrl}vocabularies/@validate`; + return this.http.post( url, vocab); + } + + + public addVocabTerm(vocab: any): Observable { + const url = `${this.apiBaseUrl}vocabularies`; + return this.http.put( url, vocab); + } + + public getFragmentCV(): Observable { + const url = `${this.apiBaseUrl}vocabularies/search?facet=ix.Class/ix.ginas.models.v1.FragmentControlledVocabulary`; + return this.http.get(url); + + } + private fetchVocabulariesFromServer(...domainArgs: Array): Observable { const url = `${this.apiBaseUrl}vocabularies/search`; @@ -148,8 +235,8 @@ export class ControlledVocabularyService extends BaseHttpService { dictionary: {} }; } - - singleDomainVocabulary[vocabulary.domain].list = vocabulary.terms.sort(function(a, b) { + // eslint-disable-next-line prefer-arrow-functions + singleDomainVocabulary[vocabulary.domain].list = vocabulary.terms.filter(term => (term.hidden === false && term.deprecated === false)).sort(function(a, b) { const termA = (a.display && a.display.toUpperCase()) || (a.value && a.value.toUpperCase()) || ''; const termB = (b.display && b.display.toUpperCase()) || (b.value && b.value.toUpperCase()) || ''; if (termA < termB) { @@ -162,7 +249,7 @@ export class ControlledVocabularyService extends BaseHttpService { }); - vocabulary.terms.forEach(vocabularyTerm => { + vocabulary.terms.filter(term => (term.hidden === false && term.deprecated === false)).forEach(vocabularyTerm => { singleDomainVocabulary[vocabulary.domain].dictionary[vocabularyTerm.value] = vocabularyTerm; }); } @@ -203,73 +290,4 @@ export class ControlledVocabularyService extends BaseHttpService { }) ); } - - getStructure(structure: string) { - const url = this.baseUrl + 'render?structure=' + structure + '&size=150&standardize=true'; - return this.http.get(url); - } - getStructureUrl(structure: string) { - structure = structure.replace(/[;]/g, '%3B') - .replace(/[#]/g, '%23') - .replace(/[+]/g, '%2B') - .replace(/[|]/g, '%7C'); - const url = this.baseUrl + 'render?structure=' + structure + '&size=150&standardize=true'; - return url; - } - - - search(domain: string, query: string): Observable> { - return new Observable(observer => { - const subscription = this.getDomainVocabulary(domain).subscribe(response => { - const filteredTerms = response[domain].list.filter(term => term.value.toLowerCase().indexOf(query.toLowerCase()) > -1); - let sortedTerms = []; - - if (filteredTerms != null && filteredTerms.length) { - sortedTerms = filteredTerms.sort((termA, termB) => { - if (termA < termB) { - return -1; - } - if (termA > termB) { - return 1; - } - return 0; - }); - } - - observer.next(sortedTerms); - subscription.unsubscribe(); - }); - }); - } - - public fetchFullVocabulary(domain: string): Observable { - - const url = `${this.apiBaseUrl}vocabularies/search`; - let params = new HttpParams(); - params = params.append('top', '100000'); - - let domainLuceneQuery = ''; - const responseDomainVocabulary: VocabularyDictionary = {}; - this.vocabularyLoadingIndicators[domain] = true; - if (this.vocabularySubject[domain] == null) { - this.vocabularySubject[domain] = new Subject(); - } - - responseDomainVocabulary[domain] = { - dictionary: {}, - list: [] - }; - - domainLuceneQuery += `root_domain:${domain}`; - params = params.append('q', domainLuceneQuery); - const options = { - params: params - }; - return this.http.get>(url, options); - } - - public addVocabTerm(vocab: any): Observable { - const url = `${this.apiBaseUrl}vocabularies`; - return this.http.put( url, vocab); - } } diff --git a/src/app/core/controlled-vocabulary/vocabulary.model.ts b/src/app/core/controlled-vocabulary/vocabulary.model.ts index 0d5e6903c..f25688a4b 100644 --- a/src/app/core/controlled-vocabulary/vocabulary.model.ts +++ b/src/app/core/controlled-vocabulary/vocabulary.model.ts @@ -30,7 +30,7 @@ export interface VocabularyTerm { export interface VocabularyDictionary { [domain: string]: { - dictionary?: { [vocabularyValue: string]: VocabularyTerm }, - list?: Array + dictionary?: { [vocabularyValue: string]: VocabularyTerm }; + list?: Array; }; } diff --git a/src/app/core/dynamic-component-loader/dynamic-component-loader.module.ts b/src/app/core/dynamic-component-loader/dynamic-component-loader.module.ts index b8d3a4cc5..91a62e550 100644 --- a/src/app/core/dynamic-component-loader/dynamic-component-loader.module.ts +++ b/src/app/core/dynamic-component-loader/dynamic-component-loader.module.ts @@ -21,7 +21,7 @@ export class DynamicComponentLoaderModule { static forRoot( lazyLoadedManifests: LazyLoadedComponentManifest[] = [], dynamicManifests: DynamicComponentManifest[] = [] - ): ModuleWithProviders { + ): ModuleWithProviders { return { ngModule: DynamicComponentLoaderModule, providers: [ @@ -34,7 +34,7 @@ export class DynamicComponentLoaderModule { ], }; } - static forModule(manifest: LazyLoadedComponentManifest): ModuleWithProviders { + static forModule(manifest: LazyLoadedComponentManifest): ModuleWithProviders { return { ngModule: DynamicComponentLoaderModule, providers: [ @@ -46,7 +46,7 @@ export class DynamicComponentLoaderModule { ], }; } - static forChild(component: Type): ModuleWithProviders { + static forChild(component: Type): ModuleWithProviders { return { ngModule: DynamicComponentLoaderModule, providers: [ diff --git a/src/app/core/dynamic-component-loader/dynamic-component-loader.service.ts b/src/app/core/dynamic-component-loader/dynamic-component-loader.service.ts index 4e3b6b42a..62a39b558 100644 --- a/src/app/core/dynamic-component-loader/dynamic-component-loader.service.ts +++ b/src/app/core/dynamic-component-loader/dynamic-component-loader.service.ts @@ -4,7 +4,8 @@ import { Injectable, Injector, NgModuleFactory, - Compiler + Compiler, + createNgModuleRef } from '@angular/core'; import { from, Observable, throwError, of } from 'rxjs'; import { mergeMap } from 'rxjs/operators'; @@ -26,32 +27,14 @@ export class DynamicComponentLoader { ) { } - /** - * Get the value as an observable - * - * @template T - * @param {(T | NgModuleFactory | Promise | Observable)} value - * @returns - * @memberof LibConfigService - */ - private _wrapIntoObservable(value: T | NgModuleFactory | Promise | Observable) { - if (value instanceof Observable) { - return value; - } else if (value instanceof Promise) { - return from(value); - } else { - return of(value); - } - } - /** * Retrieve a ComponentFactory, based on the specified componentId * (defined in the DynamicComponentManifest array). * * @template T - * @param {string} componentId - * @param {Injector} [injector] - * @returns {Observable>} + * @param componentId + * @param injector + * @returns * @memberof DynamicComponentLoader */ getComponentFactory(componentId: string, injector?: Injector): Observable> { @@ -69,26 +52,26 @@ export class DynamicComponentLoader { } return this._wrapIntoObservable(path()).pipe(mergeMap((t: any) => { - let moduleFactory = null; + // let moduleFactory = null; const offlineMode = this.compiler instanceof Compiler; // true means AOT enalbed compiler (Prod build), false means JIT enabled compiler (Dev build) - moduleFactory = offlineMode ? t : this.compiler.compileModuleSync(t); - return this.loadFactory(moduleFactory, componentId, injector); + // moduleFactory = offlineMode ? t : this.compiler.compileModuleSync(t); + return this.loadFactory(t, componentId, injector); })); } - /** + /** * Load the factory object * * @template T - * @param {NgModuleFactory} ngModuleFactory - * @param {string} componentId - * @param {Injector} [injector] - * @returns {Promise>} + * @param ngModuleFactory + * @param componentId + * @param injector + * @returns * @memberof DynamicComponentLoader */ - loadFactory(ngModuleFactory: NgModuleFactory, componentId: string, injector?: Injector): Promise> { - const moduleRef = ngModuleFactory.create(injector || this.injector); + loadFactory(module: any, componentId: string, injector?: Injector): Promise> { + const moduleRef = createNgModuleRef(module, injector || this.injector); const dynamicComponentType = moduleRef.injector.get(DYNAMIC_COMPONENT, null); if (!dynamicComponentType) { const dynamicModule: LazyLoadedComponentManifest = moduleRef.injector.get(DYNAMIC_MODULE, null); @@ -124,4 +107,22 @@ export class DynamicComponentLoader { return Promise.resolve(moduleRef.componentFactoryResolver.resolveComponentFactory(dynamicComponentType)); } + + /** + * Get the value as an observable + * + * @template T + * @param value + * @returns + * @memberof LibConfigService + */ + private _wrapIntoObservable(value: T | NgModuleFactory | Promise | Observable) { + if (value instanceof Observable) { + return value; + } else if (value instanceof Promise) { + return from(value); + } else { + return of(value); + } + } } diff --git a/src/app/core/error-handler/error-handler.ts b/src/app/core/error-handler/error-handler.ts index 91424d0dc..b107822ae 100644 --- a/src/app/core/error-handler/error-handler.ts +++ b/src/app/core/error-handler/error-handler.ts @@ -2,13 +2,14 @@ import { ErrorHandler, Injectable } from '@angular/core'; @Injectable() export class GlobalErrorHandler implements ErrorHandler { - + handleError(error: any): void { const chunkFailedMessage = /Loading chunk/; if (chunkFailedMessage.test(error.message)) { - if (confirm('There was a network error while fetching files, would you like to refresh?')) { + /* // replaced by function in substance.form.component which checks loaded component count + if (confirm('There was a network error while fetching files, would you like to refresh?')) { window.location.reload(true); - } + }*/ } else { console.error(error); } diff --git a/src/app/core/expand-details/expand-details.directive.ts b/src/app/core/expand-details/expand-details.directive.ts index d5a744542..b559387bc 100644 --- a/src/app/core/expand-details/expand-details.directive.ts +++ b/src/app/core/expand-details/expand-details.directive.ts @@ -16,14 +16,6 @@ export class ExpandDetailsDirective implements AfterViewInit, OnDestroy { private renderer: Renderer2 ) { } - ngAfterViewInit() { - } - - ngOnDestroy() { - clearTimeout(this.timerToExpand); - clearTimeout(this.timerToCollapse); - } - @ContentChild(ExpandableDetailsDirective, {static: false}) set expandableDetails(expandableDetails: ExpandableDetailsDirective) { this.expandableDetailsElement = expandableDetails.element.nativeElement; @@ -72,6 +64,14 @@ export class ExpandDetailsDirective implements AfterViewInit, OnDestroy { } } + ngAfterViewInit() { + } + + ngOnDestroy() { + clearTimeout(this.timerToExpand); + clearTimeout(this.timerToCollapse); + } + private expandDetails(): void { this.renderer.addClass(this.expandableDetailsElement, 'details-expanded'); this.renderer.removeClass(this.expandableDetailsElement, 'details-collapsed'); diff --git a/src/app/core/facets-manager/facet-display.pipe.ts b/src/app/core/facets-manager/facet-display.pipe.ts index 1193ef667..1fd0afcac 100644 --- a/src/app/core/facets-manager/facet-display.pipe.ts +++ b/src/app/core/facets-manager/facet-display.pipe.ts @@ -1,16 +1,32 @@ import { Pipe, PipeTransform } from '@angular/core'; -import { ConfigService } from '@gsrs-core/config'; +import { ConfigService } from '../config/config.service'; @Pipe({ name: 'facetDisplay' }) export class FacetDisplayPipe implements PipeTransform { - constructor(public configService: ConfigService) { - } + constructor( + public configService: ConfigService + ) { } transform(name: any, args?: any): any { + //TODO: move this snippet to the constructor to be run only once + let codeTerm = 'UNII'; + if (this.configService.configData && this.configService.configData.approvalCodeName) { + codeTerm = this.configService.configData.approvalCodeName; + } + + + if (args) { + if (args === 'userList') { + if (name.includes(':')){ + return name.split(':')[1]; + } else { + return name; + } + } if (args === 'types') { if (name === 'structurallyDiverse') { return 'Structurally Diverse'; @@ -25,7 +41,7 @@ export class FacetDisplayPipe implements PipeTransform { } } else if (args === 'status') { if (name === 'approved') { - return 'Validated (UNII)'; + return 'Validated (' + codeTerm + ')'; } else if (name === 'non-approved') { return 'non-Validated'; } @@ -47,13 +63,13 @@ export class FacetDisplayPipe implements PipeTransform { return 'Stereochemistry'; } if (name === 'root_lastEdited') { - return 'Last Edited'; + return 'Last Edited Date'; } if (name === 'root_approved') { - return 'Last Validated'; + return 'Last Validated Date'; } - if (name === 'root_approved') { - return 'Last Validated'; + if (name === 'root_created') { + return 'Created Date'; } if (name === 'Approved By') { return 'Validated By'; @@ -67,8 +83,27 @@ export class FacetDisplayPipe implements PipeTransform { if (name === 'GInAS Tag') { return 'Source Tag'; } - + + if (name === 'GInAS Domain') { + return 'Domain'; + } + if (args === 'relationships') { + if (name.indexOf('->') >= 0) { + let temp = name.split ('->'); + if (temp[0].trim() === 'PARENT') { + return temp[1].trim() + ' (PARENT)'; + } else { + return temp[0].trim() + ' -> ' + temp[1].trim(); + } + } + } + if (name === 'root_submitDate') { + return 'Submit Date'; + } + return name.trim(); } + + } diff --git a/src/app/core/facets-manager/facet-filter.pipe.ts b/src/app/core/facets-manager/facet-filter.pipe.ts index b9b2198d9..1693912a1 100644 --- a/src/app/core/facets-manager/facet-filter.pipe.ts +++ b/src/app/core/facets-manager/facet-filter.pipe.ts @@ -11,6 +11,7 @@ export class FacetFilterPipe implements PipeTransform { return items; } searchText = searchText.toLowerCase(); + // eslint-disable-next-line arrow-body-style return items.filter( it => { // the need to check the label is why this can't be a more general pipe return it.label.toLowerCase().indexOf(searchText) > -1; diff --git a/src/app/core/facets-manager/facet-http-params.ts b/src/app/core/facets-manager/facet-http-params.ts index 10ecc2df0..5b1a2abdd 100644 --- a/src/app/core/facets-manager/facet-http-params.ts +++ b/src/app/core/facets-manager/facet-http-params.ts @@ -46,7 +46,7 @@ export class FacetHttpParams extends HttpParams { } appendDictionary(params: { - [name: string]: string + [name: string]: string; }): FacetHttpParams { let clone = new FacetHttpParams({ fromString: super.toString() , encoder: new CustomEncoder()}); if (params != null) { diff --git a/src/app/core/facets-manager/facets-manager.component.html b/src/app/core/facets-manager/facets-manager.component.html index e83105081..ee7a4f2fb 100644 --- a/src/app/core/facets-manager/facets-manager.component.html +++ b/src/app/core/facets-manager/facets-manager.component.html @@ -1,96 +1,148 @@ -Show Deprecated Records +Show Deprecated Records + +
+ + + + + - + + {{facet.name | facetDisplay}} -
-
- - - - - -
- -
- -
-
- - - - -
-
- - {{value.label}} - - - {{value.label | facetDisplay: 'types'}} - - - {{value.label | facetDisplay: 'status'}} - - - {{value.label | codeSystemDisplay | async }} - + +
+ +
+
+ + + + +
-
-
- {{value.count}} + +
+ +
+
+ + + + +
+
+ + {{value.label}} + + + {{value.label | facetDisplay: 'userList'}} + + + {{value.label | facetDisplay: 'types'}} + + + {{value.label | facetDisplay: 'status'}} + + + {{value.label | codeSystemDisplay | async }} + + + {{value.label | relationshipDisplay | async }} + +
+
+
+ {{value.count}} +
+
+ + + {{value.count}} +
+ + + + + + +
+ - -
- - - - More...{{searchText[facet.name] && searchText[facet.name].value || ''}} - - - Show Less {{searchText[facet.name] &&searchText[facet.name].value || ''}} + - -
- - All Match - -
- - + + + + +
+
+ + All Match + +
+ + + +
- \ No newline at end of file + + + +
+ \ No newline at end of file diff --git a/src/app/core/facets-manager/facets-manager.component.scss b/src/app/core/facets-manager/facets-manager.component.scss index 9107198fe..3226c440e 100644 --- a/src/app/core/facets-manager/facets-manager.component.scss +++ b/src/app/core/facets-manager/facets-manager.component.scss @@ -6,6 +6,10 @@ } } +.strikethrough { + text-decoration: line-through; +} + .facet-search-loading.mat-progress-bar { margin-top: -18px; margin-bottom: 1.09em; @@ -29,7 +33,7 @@ padding: 0 3px; max-width: 150px; overflow: hidden; - color: #404040; + color: var(--label-color); white-space: normal; } @@ -37,34 +41,53 @@ padding: 0 0 0 3px; overflow: hidden; font-weight: 500; + + .number-fix { + padding-top: 5px; + } + + .button-fix { + max-height: 20px; + max-width: 50px; + } } } +.count-fix { + display: flex; +} + +.user-list-button { + padding: 0px 5px; + margin-left: 15px; + color: var(--link-primary-color); +} + .include { ::ng-deep .mat-checkbox-frame { - border-color: #80CBC4; + border-color: var(--include-checkbox-border-color); } ::ng-deep &.mat-checkbox-indeterminate.mat-accent .mat-checkbox-background, ::ng-deep &.mat-checkbox-checked.mat-accent .mat-checkbox-background { - background-color: #26A69A; + background-color: var(--include-checkbox-bg-color); } } .exclude { margin-left: 5px; ::ng-deep .mat-checkbox-frame { - border-color: #EF9A9A; + border-color: var(--exclude-checkbox-border-color); } ::ng-deep &.mat-checkbox-indeterminate.mat-accent .mat-checkbox-background, ::ng-deep &.mat-checkbox-checked.mat-accent .mat-checkbox-background { - background-color: #EF5350; + background-color: var(--exclude-checkbox-bg-color); } } .show-more { - color:#448aff; + color: var(--link-primary-color); } .show-more:hover { @@ -73,7 +96,7 @@ .facet-advanced-options-link { margin-top: 7px; - color: #448aff; + color: var(--link-primary-color); } .facet-actions { @@ -97,7 +120,7 @@ .deprecated { font-size: 14px; margin-left: 25px; - color: rgba(0, 0, 0, .70); + color: var(--deprecated-color); margin-bottom: 15px; ::ng-deep .mat-checkbox-inner-container { @@ -109,5 +132,3 @@ margin-bottom: 10px; } } - - diff --git a/src/app/core/facets-manager/facets-manager.component.ts b/src/app/core/facets-manager/facets-manager.component.ts index ae9aebab2..9a84bd85c 100644 --- a/src/app/core/facets-manager/facets-manager.component.ts +++ b/src/app/core/facets-manager/facets-manager.component.ts @@ -13,6 +13,8 @@ import { Environment } from 'src/environments/environment.model'; import { Location } from '@angular/common'; import { DisplayFacet } from './display-facet'; import { MatCheckboxChange } from '@angular/material/checkbox'; +import { UserQueryListDialogComponent } from '@gsrs-core/bulk-search/user-query-list-dialog/user-query-list-dialog.component'; +import { MatDialogRef, MatDialog } from '@angular/material/dialog'; @Component({ selector: 'app-facets-manager', @@ -20,29 +22,37 @@ import { MatCheckboxChange } from '@angular/material/checkbox'; styleUrls: ['./facets-manager.component.scss'] }) export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit { + @Output() facetsParamsUpdated = new EventEmitter(); + @Output() facetsLoaded = new EventEmitter(); + @Input() includeFacetSearch = false; + @Input() calledFrom = 'default'; + @Input() panelExpanded = false; facetString: string; public facets: Array; - private privateRawFacets: Array; public displayFacets: Array = []; - private privateFacetParams: FacetParam; public facetBuilder: FacetParam; - searchText: { [faceName: string]: { value: string, isLoading: boolean } } = {}; - private facetSearchChanged = new Subject<{ index: number, query: any }>(); - private activeSearchedFaced: Facet; - private facetsAuthSubscription: Subscription; - private subscriptions: Array = []; - @Output() facetsParamsUpdated = new EventEmitter(); + searchText: { [faceName: string]: { value: string; isLoading: boolean; } } = {}; showAudit: boolean; - private facetsConfig: { [permission: string]: Array }; toggle: Array = []; - @Output() facetsLoaded = new EventEmitter(); - private environment: Environment; - @Input() includeFacetSearch = false; showDeprecated = false; loggedIn = false; hideDeprecatedCheckbox = false; previousState: Array = []; previousFacets: Array = []; + _facetDisplayType = 'default'; + _facetViewCategorySelected: string; + _configName: string; + _facetNameText: string; + urlSearch: string; + stagingFacets: any; + private privateFacetParams: FacetParam; + private privateRawFacets: Array; + private facetSearchChanged = new Subject<{ index: number; query: any; facets?: any; }>(); + private activeSearchedFaced: Facet; + private facetsAuthSubscription: Subscription; + private subscriptions: Array = []; + private facetsConfig: { [permission: string]: Array }; + private environment: Environment; constructor( private activatedRoute: ActivatedRoute, @@ -53,7 +63,8 @@ export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit private gaService: GoogleAnalyticsService, private router: Router, private location: Location, - private facetManagerService: FacetsManagerService + private facetManagerService: FacetsManagerService, + private dialog: MatDialog ) { this.privateFacetParams = {}; this.facetBuilder = {}; @@ -73,32 +84,74 @@ export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit @HostListener('window:popstate', ['$event']) onPopState(event) { - setTimeout(() => { - if (this.router.url === this.previousState[0]) { - if(this.router.url === '/browse-substance') { - this.privateFacetParams = {}; - } else { - this.privateFacetParams = this.previousFacets[0]; - this.facetBuilder = {}; - } - this.ngOnInit(); - } + setTimeout(() => { + if (this.router.url === this.previousState[0]) { + if (this.router.url === '/browse-substance') { + this.privateFacetParams = {}; + } else { + this.privateFacetParams = this.previousFacets[0]; + this.facetBuilder = {}; + } + this.ngOnInit(); + } }, 50); } + @Input() + set rawFacets(facets: Array) { + this.privateRawFacets = facets || []; + this.populateFacets(); + } + + @Input() + set configName(configName: string) { + this.facetsConfig = this.configService.configData.facets && this.configService.configData.facets[configName] || {}; + this._configName = configName; + if (configName === 'applications' || configName === 'clinicaltrialsus' || configName === 'products' + || configName === 'adverseeventpt' || configName === 'adverseeventdme' || configName === 'adverseeventcvm' || configName === 'staging') { + this.hideDeprecatedCheckbox = true; + } else { + this.hideDeprecatedCheckbox = false; + } + if(this.calledFrom === 'staging') { + this.hideDeprecatedCheckbox = true; + this.stagingFacets = this.configService.configData.facets[configName]; + } + this.populateFacets(); + } + + @Input() + set facetDisplayType(facetDisplayType: string) { + this._facetDisplayType = facetDisplayType; + this.populateFacets(); + } + + @Input() + set facetViewCategorySelected(facetViewCategorySelected: string) { + this._facetViewCategorySelected = facetViewCategorySelected; + this.populateFacets(); + } + + @Input() + set facetNameText(facetNameText: string) { + this._facetNameText = facetNameText; + this.populateFacets(); + } ngOnInit() { this.facetString = this.activatedRoute.snapshot.queryParams['facets'] || ''; const show = this.activatedRoute.snapshot.queryParams['showDeprecated'] || 'false'; - if ( show === 'true') { + if (show === 'true') { this.showDeprecated = true; } this.facetsFromParams(); this.setDisplayFacets(); - this.facetsParamsUpdated.emit({ facetParam: this.privateFacetParams, + this.facetsParamsUpdated.emit({ + facetParam: this.privateFacetParams, displayFacets: this.displayFacets, - deprecated: this.showDeprecated }); + deprecated: this.showDeprecated + }); const deleteEventSubscription = this.facetManagerService.clearSelectionsEvent.subscribe(() => { this.clearFacetSelection(); }); @@ -118,13 +171,17 @@ export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit } ngAfterViewInit() { + this.urlSearch = this.activatedRoute.snapshot.queryParams['search'] || null; if (this.includeFacetSearch) { const facetSearchSubscription = this.facetSearchChanged.pipe( debounceTime(500), distinctUntilChanged(), switchMap(event => { const facet = this.facets[event.index]; - return this.facetsService.getFacetsHandler(facet, event.query).pipe(take(1)); + if (!facet._self) { + facet._self = this.facetManagerService.generateSelfUrl('stagingArea', facet.name); + } + return this.facetsService.getFacetsHandler(facet, event.query, null, this.privateFacetParams, this.urlSearch).pipe(take(1)); }) ).subscribe(response => { this.activeSearchedFaced.values = this.activeSearchedFaced.values.filter(value => { @@ -132,8 +189,8 @@ export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit let isInSearhResults = false; - for (let i = 0; i < response.content.length; i++) { - if (response.content[i].label === value.label) { + for (const r of response.content) { + if (r.label === value.label) { isInSearhResults = true; break; } @@ -145,7 +202,6 @@ export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit || this.privateFacetParams[this.activeSearchedFaced.name].params[value.label] === false)) { removeFacet = false; } - return !removeFacet; }); this.activeSearchedFaced.values = this.activeSearchedFaced.values.concat(response.content); @@ -157,29 +213,11 @@ export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit } } - @Input() - set rawFacets(facets: Array) { - this.privateRawFacets = facets || []; - this.populateFacets(); - } - - @Input() - set configName(configName: string) { - this.facetsConfig = this.configService.configData.facets && this.configService.configData.facets[configName] || {}; - if ( configName === 'applications' || configName === 'ctclinicaltrial' || configName === 'products') { - this.hideDeprecatedCheckbox = true; - } else { - this.hideDeprecatedCheckbox = false; - } - this.populateFacets(); - } - - facetsFromParams() { if (this.facetString !== '') { const categoryArray = this.escapedSplit(this.facetString, ','); - for (let i = 0; i < (categoryArray.length); i++) { - const categorySplit = this.escapedSplit(categoryArray[i],'*'); + for (const c of categoryArray) { + const categorySplit = this.escapedSplit(c, '*'); const category = categorySplit[0]; const fieldsArr = this.escapedSplit(categorySplit[1], '+'); const params: { [facetValueLabel: string]: boolean } = {}; @@ -187,8 +225,8 @@ export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit let isAllMatch = false; let hasExcludeOption = false; let includeOptionsLength = 0; - for (let j = 0; j < fieldsArr.length; j++) { - const field = this.escapedSplit(fieldsArr[j], '.'); + for (const f of fieldsArr) { + const field = this.escapedSplit(f, '.'); field[0] = this.decodeValue(decodeURIComponent(field[0])); if (field[0] === 'is_all_match') { isAllMatch = true; @@ -225,9 +263,10 @@ export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit this.showDeprecated = !this.showDeprecated; setTimeout(() => { this.populateUrlQueryParameters(this.showDeprecated); - this.facetsParamsUpdated.emit({ facetParam: this.privateFacetParams, displayFacets: this.displayFacets, - deprecated: this.showDeprecated }); - + this.facetsParamsUpdated.emit({ + facetParam: this.privateFacetParams, displayFacets: this.displayFacets, + deprecated: this.showDeprecated + }); }); } @@ -241,43 +280,120 @@ export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit const facetsCopy = this.privateRawFacets.slice(); const newFacets = []; this.showAudit = this.authService.hasRoles('admin'); - const facetKeys = Object.keys(this.facetsConfig) || []; - - facetKeys.forEach(facetKey => { - if (this.facetsConfig[facetKey].length - && (facetKey === 'default' || this.authService.hasRoles(facetKey))) { - this.facetsConfig[facetKey].forEach(facet => { - for (let facetIndex = 0; facetIndex < facetsCopy.length; facetIndex++) { - this.toggle[facetIndex] = true; - if (facet === facetsCopy[facetIndex].name) { - if (facetsCopy[facetIndex].values != null && facetsCopy[facetIndex].values.length) { - let hasValues = false; - for (let valueIndex = 0; valueIndex < facetsCopy[facetIndex].values.length; valueIndex++) { - if (facetsCopy[facetIndex].values[valueIndex].count) { - hasValues = true; - break; + let facetKeys = Object.keys(this.facetsConfig) || []; + if (this._facetDisplayType) { + if (this._facetDisplayType === 'default' || this.calledFrom === 'staging') { + facetKeys.forEach(facetKey => { + if (this.facetsConfig[facetKey].length + && (facetKey === 'default' || this.authService.hasRoles(facetKey) || (facetKey === 'staging' && this.calledFrom === 'staging'))) { + this.facetsConfig[facetKey].forEach(facet => { + for (let facetIndex = 0; facetIndex < facetsCopy.length; facetIndex++) { + this.toggle[facetIndex] = true; + if (facet === facetsCopy[facetIndex].name) { + + // Facet Name Search + let facetNameTextFound = true; + if ((this._facetNameText) && (this._facetNameText.length > 0)) { + facetNameTextFound = false; + if (facetsCopy[facetIndex].name.toLowerCase().indexOf(this._facetNameText.toLowerCase().trim()) > -1) { + facetNameTextFound = true; + } } - } - if (hasValues) { - if (this.showDeprecated === false && facet === 'Deprecated') { - } else { - const facetToAdd = facetsCopy.splice(facetIndex, 1); - facetIndex--; - newFacets.push(facetToAdd[0]); - this.searchText[facetToAdd[0].name] = { value: '', isLoading: false }; + if (facetNameTextFound === true) { + if (facetsCopy[facetIndex].values != null && facetsCopy[facetIndex].values.length) { + let hasValues = false; + for (let valueIndex = 0; valueIndex < facetsCopy[facetIndex].values.length; valueIndex++) { + if (facetsCopy[facetIndex].values[valueIndex].count) { + hasValues = true; + break; + } + } + + if (hasValues) { + if (this.showDeprecated === false && facet === 'Deprecated') { + } else { + const facetToAdd = facetsCopy.splice(facetIndex, 1); + facetIndex--; + newFacets.push(facetToAdd[0]); + this.searchText[facetToAdd[0].name] = { value: '', isLoading: false }; + } + } + } } + + break; } } - break; - } + }); } }); + } else if (this._facetDisplayType === 'facetView' && this._facetViewCategorySelected !== 'All') { + if (this._configName && this._configName === 'substances') { + this.facetsConfig['facetView'].forEach(categoryRow => { + const category = categoryRow['category']; + const categoryFacets = categoryRow['facets']; + if (category === this._facetViewCategorySelected) { + categoryFacets.forEach(facet => { + for (let facetIndex = 0; facetIndex < facetsCopy.length; facetIndex++) { + this.toggle[facetIndex] = true; + if (facet === facetsCopy[facetIndex].name) { + // Facet Name Search + let facetNameTextFound = true; + if ((this._facetNameText) && (this._facetNameText.length > 0)) { + facetNameTextFound = false; + if (facetsCopy[facetIndex].name.toLowerCase().indexOf(this._facetNameText.toLowerCase().trim()) > -1) { + facetNameTextFound = true; + } + } + + if (facetNameTextFound === true) { + if (facetsCopy[facetIndex].values != null && facetsCopy[facetIndex].values.length) { + let hasValues = false; + for (let valueIndex = 0; valueIndex < facetsCopy[facetIndex].values.length; valueIndex++) { + if (facetsCopy[facetIndex].values[valueIndex].count) { + hasValues = true; + break; + } + } + + if (hasValues) { + if (this.showDeprecated === false && facet === 'Deprecated') { + } else { + const facetToAdd = facetsCopy.splice(facetIndex, 1); + facetIndex--; + newFacets.push(facetToAdd[0]); + this.searchText[facetToAdd[0].name] = { value: '', isLoading: false }; + } + } + } + } + + break; + } + } + }); + } + }); + } + } else { // Show ALL Facets + for (let facetIndex = 0; facetIndex < facetsCopy.length; facetIndex++) { + this.searchText[facetsCopy[facetIndex].name] = { value: '', isLoading: false }; + newFacets.push(facetsCopy[facetIndex]); + } } + } + // Set any facets being used to filter results to the top of the facet display + Object.keys(this.privateFacetParams).forEach(key => { + const position = newFacets.map(object => object.name).indexOf(key); + if (position > 0 ) { + newFacets.unshift(newFacets.splice(position, 1)[0]); + } }); - + console.log(newFacets); this.facets = newFacets; + this.setShowAdvancedFacetStates(); this.facetsLoaded.emit(this.facets.length); this.cleanFacets(); this.setDisplayFacets(); @@ -323,19 +439,38 @@ export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit } } + setShowAdvancedFacetStates() { + // When there is at least one facet param value that is false (aka red/negative) + // set the advancedState to true. This is run so the user doesn't get confused + // by negative selections not showing on page reload. + + this.facets.forEach( f => { + if (this.privateFacetParams[f.name] && this.privateFacetParams[f.name].params) { + Object.keys(this.privateFacetParams[f.name].params).every(sub => { + if (this.privateFacetParams[f.name].params[sub] !== undefined + && this.privateFacetParams[f.name].params[sub] === false + ) { + f.$showAdvanced = true; + return false; + } + return true; + }); + } + }); + } + populateUrlQueryParameters(deprecated?: boolean): void { const navigationExtras: NavigationExtras = { queryParams: {} }; - const catArr = []; let facetString = ''; for (const key of Object.keys(this.privateFacetParams)) { if (this.privateFacetParams[key] !== undefined && this.privateFacetParams[key].hasSelections === true && !(this.showDeprecated && key === 'Deprecated' && this.privateFacetParams[key] !== undefined && - this.privateFacetParams[key].params && - this.privateFacetParams[key].params['Deprecated'] === true)) { + this.privateFacetParams[key].params && + this.privateFacetParams[key].params['Deprecated'] === true)) { const cat = this.privateFacetParams[key]; const valArr = []; for (const subkey of Object.keys(cat.params)) { @@ -351,13 +486,13 @@ export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit const newHash = this.utilsService.hashCode(paramsString, this.privateFacetParams[key].isAllMatch.toString()); this.privateFacetParams[key].currentStateHash = newHash; this.privateFacetParams[key].isUpdated = false; + } } - } facetString = catArr.join(','); if (facetString !== '') { navigationExtras.queryParams['facets'] = facetString; } - if (this.showDeprecated) { + if (this.showDeprecated) { navigationExtras.queryParams['showDeprecated'] = 'true'; } else { navigationExtras.queryParams['showDeprecated'] = null; @@ -372,27 +507,26 @@ export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit this.location.go(urlTree.toString()); } - - private escapedSplit(value: string, delim: string){ - return value.match(new RegExp('((.|^)*?([^!]|^))([' + delim + ']|$)','g')) - .map(d=>d.replace(new RegExp('[' +delim + ']$','g'),'')); + + private escapedSplit(value: string, delim: string) { + return value.match(new RegExp('((.|^)*?([^!]|^))([' + delim + ']|$)', 'g')) + .map(d => d.replace(new RegExp('[' + delim + ']$', 'g'), '')); } - - private encodeValue(facetValue: string){ - let encFV = facetValue.replace('!','!@'); - encFV = encFV.replace(/[.]/g,'!.'); - encFV = encFV.replace(/[\+]/g,'!+'); - encFV = encFV.replace(/[,]/g,'!,'); - encFV = encFV.replace(/[\*]/g,'!*'); + + private encodeValue(facetValue: string) { + let encFV = facetValue.replace('!', '!@'); + encFV = encFV.replace(/[.]/g, '!.'); + encFV = encFV.replace(/[\+]/g, '!+'); + encFV = encFV.replace(/[,]/g, '!,'); + encFV = encFV.replace(/[\*]/g, '!*'); return encFV; } - - private decodeValue(encFV: string){ - let decFV = encFV.replace(/!([^@])/g,"$1").replace(/[!][@]/g,'!'); + private decodeValue(encFV: string) { + const decFV = encFV.replace(/!([^@])/g, '$1').replace(/[!][@]/g, '!'); return decFV; } - + private applyFacetsFilter(facetName: string) { const eventLabel = this.environment.isAnalyticsPrivate ? 'facet' : `${facetName}`; let eventValue = 0; @@ -404,9 +538,11 @@ export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit this.gaService.sendEvent('substancesFiltering', 'button:apply-facet', eventLabel, eventValue); this.populateUrlQueryParameters(); this.setDisplayFacets(); - this.facetsParamsUpdated.emit({ facetParam: this.privateFacetParams, - displayFacets: this.displayFacets, - deprecated: this.showDeprecated}); + this.facetsParamsUpdated.emit({ + facetParam: this.privateFacetParams, + displayFacets: this.displayFacets, + deprecated: this.showDeprecated + }); } removeFacet(type: string, bool: boolean, val: string): void { @@ -431,7 +567,6 @@ export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit let paramsString: string; let isAllMatchString: string; - if (this.privateFacetParams[facetName] == null) { this.privateFacetParams[facetName] = { params: {}, @@ -478,10 +613,19 @@ export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit isAllMatchString = this.privateFacetParams[facetName].isAllMatch.toString(); const newHash = this.utilsService.hashCode(paramsString, isAllMatchString); this.privateFacetParams[facetName].isUpdated = newHash !== this.privateFacetParams[facetName].currentStateHash; + + // Pass which facet is selected when calling from Advanced Search. + if (this.calledFrom && this.calledFrom === 'advancedsearch') { + this.setDisplayFacets(); + this.facetsParamsUpdated.emit({ + facetParam: this.privateFacetParams, + displayFacets: this.displayFacets, + deprecated: this.showDeprecated + }); + } } updateAllMatch(facetName: string): void { - const eventLabel = this.environment.isAnalyticsPrivate ? 'facet' : `${facetName}`; const eventValue = this.privateFacetParams[facetName].isAllMatch ? 1 : 0; this.gaService.sendEvent('substancesFiltering', `check:facet-all_match`, eventLabel, eventValue); @@ -544,10 +688,15 @@ export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit moreFacets(index: number, facet: Facet) { this.facets[index].$isLoading = true; + // This check for _self should be temporary while it's incorporation in the staging area is complete. + // If no meta fields exist, generate what they are expected to look like if (facet.$next == null) { - facet.$next = facet._self.replace('fskip=0', 'fskip=10'); + if (!facet._self) { + facet._self = this.facetManagerService.generateSelfUrl('stagingArea', facet.name); + } + facet.$next = facet._self.replace('fskip=0', 'fskip=10'); } - this.facetManagerService.getFacetsHandler(this.facets[index], '', facet.$next).pipe(take(1)).subscribe(resp => { + this.facetManagerService.getFacetsHandler(this.facets[index], '', facet.$next, this.privateFacetParams, this.urlSearch).pipe(take(1)).subscribe(resp => { this.facets[index].$next = resp.nextPageUri; this.facets[index].$previous = resp.previousPageUri; this.facets[index].values = this.facets[index].values.concat(resp.content); @@ -562,7 +711,7 @@ export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit lessFacets(index: number) { this.facets[index].$isLoading = true; const nextUrl = this.facets[index].$next; - this.facetManagerService.getFacetsHandler(this.facets[index], null, null).pipe(take(1)).subscribe(response => { + this.facetManagerService.getFacetsHandler(this.facets[index], null, null, this.privateFacetParams, this.urlSearch).pipe(take(1)).subscribe(response => { this.facets[index].values = response.content; this.facets[index].$fetched = response.content; this.facets[index].$next = response.nextPageUri; @@ -576,7 +725,7 @@ export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit filterFacets(index: number, searchTerm: string, faceName: string): void { this.searchText[faceName].isLoading = true; this.activeSearchedFaced = this.facets[index]; - this.facetSearchChanged.next({ index: index, query: searchTerm }); + this.facetSearchChanged.next({ index: index, query: searchTerm, facets: this.privateFacetParams }); } clearFacetSearch(index: number, facetName: string): void { @@ -588,4 +737,25 @@ export class FacetsManagerComponent implements OnInit, OnDestroy, AfterViewInit return this.privateFacetParams; } -} \ No newline at end of file + editList(list?: string): void { + let data = {view: 'all'}; + if (list) { + data.view = 'single'; + data['activeName'] = list.split(':')[1]; + } + console.log(data); + const dialogRef = this.dialog.open(UserQueryListDialogComponent, { + width: '850px', + autoFocus: false, + data: data + + }); + // this.overlayContainer.style.zIndex = '1002'; + + const dialogSubscription = dialogRef.afterClosed().pipe(take(1)).subscribe(response => { + if (response) { + // this.overlayContainer.style.zIndex = null; + } + }); + } +} diff --git a/src/app/core/facets-manager/facets-manager.module.ts b/src/app/core/facets-manager/facets-manager.module.ts index af7fb3457..ef208b744 100644 --- a/src/app/core/facets-manager/facets-manager.module.ts +++ b/src/app/core/facets-manager/facets-manager.module.ts @@ -1,15 +1,13 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FacetsManagerComponent } from './facets-manager.component'; -import { - MatExpansionModule, - MatFormFieldModule, - MatCheckboxModule, - MatIconModule, - MatProgressBarModule, - MatInputModule, - MatButtonModule -} from '@angular/material'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatIconModule } from '@angular/material/icon'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatButtonModule } from '@angular/material/button'; +import { MatInputModule } from '@angular/material/input'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatFormFieldModule } from '@angular/material/form-field'; import { FormsModule } from '@angular/forms'; import { FacetDisplayPipe } from './facet-display.pipe'; import { FacetFilterPipe } from './facet-filter.pipe'; diff --git a/src/app/core/facets-manager/facets-manager.service.ts b/src/app/core/facets-manager/facets-manager.service.ts index 44719e1f6..afaa2a024 100644 --- a/src/app/core/facets-manager/facets-manager.service.ts +++ b/src/app/core/facets-manager/facets-manager.service.ts @@ -10,7 +10,7 @@ import { Facet, FacetQueryResponse } from './facet.model'; providedIn: 'root' }) export class FacetsManagerService extends BaseHttpService { - getFacetsHandler: (facet: Facet, searchTerm?: string, nextUrl?: string) => Observable; + getFacetsHandler: (facet: Facet, searchTerm?: string, nextUrl?: string, otherFacets?: any, pageQuery?: string) => Observable; private clearSelectionsSubject = new Subject(); constructor( @@ -21,7 +21,7 @@ export class FacetsManagerService extends BaseHttpService { super(configService); } - registerGetFacetsHandler(handler: (facet: Facet, searchTerm?: string, nextUrl?: string) => Observable): void { + registerGetFacetsHandler(handler: (facet: Facet, searchTerm?: string, nextUrl?: string, otherFacets?: any, pageQuery?: string) => Observable): void { this.getFacetsHandler = handler; } @@ -36,4 +36,11 @@ export class FacetsManagerService extends BaseHttpService { clearSelections(): void { this.clearSelectionsSubject.next(); } + + //note: use only if there is an issue with _self or other meta facet properties. + generateSelfUrl(entity?: string, field?: string) { + return this.apiBaseUrl + 'substances/' + (entity? entity : '') + + '/search/@facets?defaultField=identifiers&wait=false&skip=0&fskip=0' + + (field ? ('&field=' + field.replace(' ', '+')) : ''); + } } diff --git a/src/app/core/gsrs.module.ts b/src/app/core/gsrs.module.ts index 27fec310e..1a8ad6a95 100644 --- a/src/app/core/gsrs.module.ts +++ b/src/app/core/gsrs.module.ts @@ -8,7 +8,7 @@ import { CommonModule } from '@angular/common'; declarations: [] }) export class GsrsModule { - static forRoot(): ModuleWithProviders { + static forRoot(): ModuleWithProviders { return { ngModule: GsrsModule }; diff --git a/src/app/core/guided-search/guided-search.component.scss b/src/app/core/guided-search/guided-search.component.scss index f2bc1b316..50a19652f 100644 --- a/src/app/core/guided-search/guided-search.component.scss +++ b/src/app/core/guided-search/guided-search.component.scss @@ -65,8 +65,8 @@ .query-value { padding: 10px 15px; - background-color: #ECECEC; - border: solid 1px #DBDBDB; + background-color: var(--query-bg-color); + border: solid 1px var(--query-border-color); border-radius: 2px; min-height: 69px; } @@ -86,4 +86,4 @@ .query-builder-actions { margin: 10px 0 15px 0; -} \ No newline at end of file +} diff --git a/src/app/core/guided-search/guided-search.module.ts b/src/app/core/guided-search/guided-search.module.ts index 815409bf2..ad2423e81 100644 --- a/src/app/core/guided-search/guided-search.module.ts +++ b/src/app/core/guided-search/guided-search.module.ts @@ -1,15 +1,13 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { GuidedSearchComponent } from './guided-search.component'; -import { - MatCardModule, - MatInputModule, - MatAutocompleteModule, - MatSelectModule, - MatTooltipModule, - MatButtonModule, - MatIconModule -} from '@angular/material'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatButtonModule } from '@angular/material/button'; +import { MatInputModule } from '@angular/material/input'; +import { MatSelectModule } from '@angular/material/select'; +import { MatCardModule } from '@angular/material/card'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatDatepickerModule } from '@angular/material/datepicker'; import { QueryStatementComponent } from './query-statement/query-statement.component'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; diff --git a/src/app/core/guided-search/query-statement/query-statement.component.scss b/src/app/core/guided-search/query-statement/query-statement.component.scss index f48c73e65..2b8795bc6 100644 --- a/src/app/core/guided-search/query-statement/query-statement.component.scss +++ b/src/app/core/guided-search/query-statement/query-statement.component.scss @@ -51,10 +51,10 @@ justify-content: flex-end; a { - color: #448aff; + color: var(--link-primary-color); } } .mat-error { - color: red; -} \ No newline at end of file + color: var(--regular-red-color); +} diff --git a/src/app/core/guided-search/query-statement/query-statement.component.ts b/src/app/core/guided-search/query-statement/query-statement.component.ts index ee37fbc1b..42f8e4e0f 100644 --- a/src/app/core/guided-search/query-statement/query-statement.component.ts +++ b/src/app/core/guided-search/query-statement/query-statement.component.ts @@ -194,6 +194,9 @@ export class QueryStatementComponent implements OnInit, AfterViewInit, OnDestroy this.selectedLucenePath = this.selectedLucenePath + ':'; } this.selectedQueryablePropertyType = this._queryableDictionary[queryableProperty].type; + + /* Commenting for now. At this moment will use Exact Match, Contains, and Starts With. April, 2020 */ + /* this.commandOptions = Object.keys( this.typeCommandOptions[this._queryableDictionary[queryableProperty].type] ).filter(option => { @@ -213,6 +216,7 @@ export class QueryStatementComponent implements OnInit, AfterViewInit, OnDestroy } return 0; }); + */ } setCvOptions(cvDomain: string): void { diff --git a/src/app/core/guided-search/query-statement/query-statement.model.ts b/src/app/core/guided-search/query-statement/query-statement.model.ts index 9b1ea893e..821c6410a 100644 --- a/src/app/core/guided-search/query-statement/query-statement.model.ts +++ b/src/app/core/guided-search/query-statement/query-statement.model.ts @@ -1,4 +1,4 @@ -import { MatDatepickerInputEvent } from '@angular/material'; +import { MatDatepickerInputEvent } from '@angular/material/datepicker'; export interface QueryStatement { condition?: string; diff --git a/src/app/core/guided-search/query-statement/type-command-options.constant.ts b/src/app/core/guided-search/query-statement/type-command-options.constant.ts index 0edbf826e..af73c3415 100644 --- a/src/app/core/guided-search/query-statement/type-command-options.constant.ts +++ b/src/app/core/guided-search/query-statement/type-command-options.constant.ts @@ -67,10 +67,12 @@ export const typeCommandOptions: CommandTypesDict = { } ] }, - 'the following contained phrase, which must be found as written (no partial words)': { + // 'the following contained phrase, which must be found as written (no partial words)': { + 'Exact Match': { commandInputs: [ { type: 'text', + example: 'Example: aspirin sodium', constructQuery: ( queryValue: string, condition: string, @@ -78,11 +80,14 @@ export const typeCommandOptions: CommandTypesDict = { lucenePath: string, eventEmitter: EventEmitter ) => { - const query = queryValue.trim() && `${condition}${lucenePath}"${queryValue.trim()}"` || ''; + if (queryValue) { + queryValue = queryValue.replace(/['"]+/g, ''); + } + const query = queryValue.trim() && `${condition}${lucenePath}"^${queryValue.trim()}$"` || ''; eventEmitter.emit({ condition: condition, queryableProperty: queryableProperty, - command: 'the following contained phrase, which must be found as written (no partial words)', + command: 'Exact Match', commandInputValues: [queryValue], query: query }); @@ -120,7 +125,81 @@ export const typeCommandOptions: CommandTypesDict = { } ] }, - 'a WORD that contains': { + // 'a WORD that contains': { + 'Contains': { + commandInputs: [ + { + type: 'text', + example: 'Example: sodium', + constructQuery: ( + queryValue: string, + condition: string, + queryableProperty: string, + lucenePath: string, + eventEmitter: EventEmitter + ) => { + if (queryValue) { + // Remove single and double quotes + queryValue = queryValue.replace(/['"]+/g, ''); + // Put slash \\ in front of AND in the text for scape + queryValue = queryValue.replace(/[ ]AND[ ]/g, ' \\\\AND '); + // Put slash \\ in front of OR in the text for scape + queryValue = queryValue.replace(/[ ]OR[ ]/g, ' \\\\OR '); + // Put slash \\ in front of OR in the text for scape + queryValue = queryValue.replace(/[ ]NOT[ ]/g, ' \\\\NOT '); + queryValue = queryValue.replace(/^NOT[ ]/g, '\\\\NOT '); + + //TODO: Fix the underlying issues on backend with this eventually + // Replace hypen - with space for scape + queryValue = queryValue.replace(/[-]+/g, ' '); + // Remove dot . for scape + queryValue = queryValue.replace(/[.]+/g, ''); + // Remove parentheses ( for scape + queryValue = queryValue.replace(/[()]+/g, ' ').trim(); + // remove commas that are before spaces + queryValue = queryValue.replace(/[,][ ]/g, ' '); + } + const query = queryValue.trim() && `${condition}${lucenePath}"*${queryValue.trim()}*"` || ''; + eventEmitter.emit({ + condition: condition, + queryableProperty: queryableProperty, + command: 'Contains', + commandInputValues: [queryValue], + query: query + }); + } + } + ] + }, + // 'a WORD that starts with': { + 'Starts With': { + commandInputs: [ + { + type: 'text', + example: 'Example: aspir', + constructQuery: ( + queryValue: string, + condition: string, + queryableProperty: string, + lucenePath: string, + eventEmitter: EventEmitter + ) => { + if (queryValue) { + queryValue = queryValue.replace(/['"]+/g, ''); + } + const query = queryValue.trim() && `${condition}${lucenePath}"^${queryValue.trim()}*"` || ''; + eventEmitter.emit({ + condition: condition, + queryableProperty: queryableProperty, + command: 'Starts With', + commandInputValues: [queryValue], + query: query + }); + } + } + ] + }, + 'Ends With': { commandInputs: [ { type: 'text', @@ -131,11 +210,11 @@ export const typeCommandOptions: CommandTypesDict = { lucenePath: string, eventEmitter: EventEmitter ) => { - const query = queryValue.trim() && `${condition}${lucenePath}*${queryValue.trim()}*` || ''; + const query = queryValue.trim() && `${condition}${lucenePath}"*${queryValue.trim()}$"` || ''; eventEmitter.emit({ condition: condition, queryableProperty: queryableProperty, - command: 'a WORD that contains', + command: 'Ends With', commandInputValues: [queryValue], query: query }); @@ -143,10 +222,12 @@ export const typeCommandOptions: CommandTypesDict = { } ] }, - 'a WORD that starts with': { + // 'the following contained phrase, which must be found as written (no partial words)': { + 'Manual Query Entry': { commandInputs: [ { type: 'text', + example: 'Example: aspirin sodium', constructQuery: ( queryValue: string, condition: string, @@ -154,11 +235,15 @@ export const typeCommandOptions: CommandTypesDict = { lucenePath: string, eventEmitter: EventEmitter ) => { - const query = queryValue.trim() && `${condition}${lucenePath}${queryValue.trim()}*` || ''; + /* if (queryValue) { + queryValue = queryValue.replace(/['"]+/g, ''); + } */ + const query = queryValue.trim(); + // const query = queryValue.trim() && `${condition}${lucenePath}"^${queryValue.trim()}$"` || ''; eventEmitter.emit({ condition: condition, queryableProperty: queryableProperty, - command: 'a WORD that contains', + command: 'Manual Query Entry', commandInputValues: [queryValue], query: query }); @@ -486,7 +571,8 @@ export const typeCommandOptions: CommandTypesDict = { } ] }, - 'number that begins with': { + //'number that begins with': { + 'Greater Than': { commandInputs: [ { type: 'number', @@ -501,7 +587,8 @@ export const typeCommandOptions: CommandTypesDict = { eventEmitter.emit({ condition: condition, queryableProperty: queryableProperty, - command: 'number that begins with', + // command: 'number that begins with', + command: 'Greater Than', commandInputValues: [queryValue], query: query }); @@ -509,7 +596,8 @@ export const typeCommandOptions: CommandTypesDict = { } ] }, - 'number that ends with': { + //'number that ends with': { + 'Less Than': { commandInputs: [ { type: 'number', @@ -524,7 +612,8 @@ export const typeCommandOptions: CommandTypesDict = { eventEmitter.emit({ condition: condition, queryableProperty: queryableProperty, - command: 'number that ends with', + // command: 'number that ends with', + command: 'Less Than', commandInputValues: [queryValue], query: query }); diff --git a/src/app/core/guided-search/queryable-substance-dictionary.model.ts b/src/app/core/guided-search/queryable-substance-dictionary.model.ts index bf796403e..db7f7c359 100644 --- a/src/app/core/guided-search/queryable-substance-dictionary.model.ts +++ b/src/app/core/guided-search/queryable-substance-dictionary.model.ts @@ -1,5 +1,5 @@ import { EventEmitter } from '@angular/core'; -import { MatDatepickerInputEvent } from '@angular/material'; +import { MatDatepickerInputEvent } from '@angular/material/datepicker'; import { QueryStatement } from './query-statement/query-statement.model'; export interface QueryableSubstanceDictionary { @@ -9,6 +9,7 @@ export interface QueryableSubstanceDictionary { type: string; cvDomain?: string; priority?: string; + suggest?: string; }; } @@ -26,6 +27,7 @@ export interface Command { export interface CommandInput { type?: string; + example?: string; constructQuery?: ( queryValue: string | Date | number, condition: string, diff --git a/src/app/core/highlighted-search-action/highlighted-search-action.component.ts b/src/app/core/highlighted-search-action/highlighted-search-action.component.ts index d51c7b1a7..796e8a485 100644 --- a/src/app/core/highlighted-search-action/highlighted-search-action.component.ts +++ b/src/app/core/highlighted-search-action/highlighted-search-action.component.ts @@ -1,6 +1,5 @@ import { Component, OnInit, Inject } from '@angular/core'; -import { MAT_BOTTOM_SHEET_DATA } from '@angular/material'; -import { MatBottomSheetRef } from '@angular/material'; +import { MAT_BOTTOM_SHEET_DATA, MatBottomSheetRef } from '@angular/material/bottom-sheet'; import { ActivatedRoute, Router, NavigationExtras } from '@angular/router'; @Component({ diff --git a/src/app/core/home/home.component.html b/src/app/core/home/home.component.html index fcc39d65d..0292397d5 100644 --- a/src/app/core/home/home.component.html +++ b/src/app/core/home/home.component.html @@ -1,307 +1,373 @@ - -
- -
- Welcome to the new user interface for GSRS 2.5 and onwards. Please be aware that you can return to the classic view by - clicking - the white button on the top right. Please send any feedback to {{contactEmail}}. -
-
-
- - -
- - - - Search - - - - -

- - Search GSRS

- - - Browse All Substances - - - - - - - Structure Search - - - - - Sequence Search - - - - - Guided Search - - - - - - - - Browse All Applications - - - - - Browse All Products - - - - - Browse All Clinical Trials - - - - - - Structure Search - - - - - Sequence Search - - - - - - Advanced Search - - - - - - - - Product - - - - - - - - Application - - - - - - - - - Clinical Trial - - - - - - - - - Biomarker - - - - - - - - - Indication/Sponsor - - - - - - - +
+ + - Adverse Event - - - - - - - - - Register (Login to Register) - - -
+ + + + +
+ + + + + + + + + +
+
+ first display image +
+
+ second display image +
+
+ third display image +
+
+ fourth display image +
+
+ +
+

{{homeHeader}}

+
{{homeContents}}
+ +
+
+ + +
+ \ No newline at end of file +
+
+ +
+ {{link.display}} +
+
+ {{link.total | number:'1.0':'en-US'}} +
+
+
+
+
+
+
+ + +

Helpful Resources

+
+ + + + +
+ GSRS Helpful Resource +
+
+
+
+
+ + + + +
+ + +
+
+ +
+
+ \ No newline at end of file diff --git a/src/app/core/home/home.component.scss b/src/app/core/home/home.component.scss index 72e3b8588..70d1dea18 100644 --- a/src/app/core/home/home.component.scss +++ b/src/app/core/home/home.component.scss @@ -23,7 +23,6 @@ .register-header { font-size: 16px; font-weight: 400; -color: rgba(0,0,0,.54); } .register-card-content { @@ -38,6 +37,7 @@ color: rgba(0,0,0,.54); margin-top: 10px; margin-right: 20px; min-width: 275px; + margin-left: 10px; .main-list, .other-list { padding-top: 0px; @@ -57,7 +57,57 @@ color: rgba(0,0,0,.54); } .left-search { - margin-top: 10px; +} + +.image-row { + justify-content: space-evenly; + max-width: 1200px; + margin: auto; +} + +.display-image { + max-height: 225px; + max-width: 281px; + height: 100%; + width:100%; +} + +.image-container { + width: 24%; +} + +.quick-links { + color: var(--primary-color); + font-weight: 550; + margin-left: 30%; + font-size: 23px; +} + +.resource-row { + margin-left: 15px; + margin-right: 15px; +} + +.temprow { + width: 45%; + margin-top: 0px; + margin-bottom: 0px; +} + +.test { + margin: auto; + margin-bottom: 15px; + width: 100%; + +} + +.row3 { + display: flex; + flex-direction: row; + margin: auto; + max-width: 1220px; + margin-top: 15px; + } .shrunken-icon { @@ -65,6 +115,34 @@ color: rgba(0,0,0,.54); padding-left: 5px; } +.mat-list-base .mat-list-item.mat-list-item-with-avatar { + height: 52px; +} + + +.right-link { + margin-left:10px; +} + +.left-link { + margin-right: 10px; +} +.button-modal { + justify-content: center; + align-content: center; + height: 115px; + padding-top: 50px; +} + +.modal-button { + font-size: 18px; + color: var(--primary-color); +} +.trial-button { + color: var(--regular-white-color); + font-size: 18px; +} + .closed-welcome-padding { height:60px !important; width:100%; @@ -72,20 +150,40 @@ color: rgba(0,0,0,.54); .mat-list-base { position: relative; + padding-top: 0px; +} + +.resource-link { + color: var(--primary-color) !important; + font-weight:550; + margin-top: 5px; +} + +.resource-desc { + padding-left: 15px; + padding-bottom: 10px; } +.panel-header { + font-size: 18px; + font-weight: 550px; +} +.subtest { + position: relative !important; + padding: 2px; +} a.mat-list-item { - color: #1565c0; + color: var(--link-color); } .mat-list-base .mat-list-item .mat-list-icon { - color: rgba(0, 0, 0, 0.87); + color: var(--mat-list-color); } .big-button { font-size: 19px !important; - color: white !important; - background-color: #4793d1 !important; + color: var(--regular-white-color) !important; + background-color: var(--primary-color) !important; padding: 3px 14px !important; } @@ -96,14 +194,24 @@ a.mat-list-item { .big-button { font-size: 19px !important; - color: white !important; - background-color: #4793d1 !important; + color: var(--regular-white-color) !important; + background-color: var(--primary-color) !important; padding: 3px 14px !important; } } +.banner-main { + width: calc(100% - 50px); +} + +.topMargin { + margin-top: 75px !important; +} .welcome-banner { - margin-top: 75px; + margin-top: 0px; + width: 100%; + display: flex; + flex-direction: row; .close-container { display: flex; @@ -116,3 +224,829 @@ a.mat-list-item { mat-list-item { height: 52px; } + +.home-card { + margin-bottom: 15px; +} + +.padding { + padding-bottom: 5px; + padding-bottom: 5px; + margin-left: 61px; + width: 90% !important; +} + +.home { + font-weight: 500; + font-size: 27px; + color: var(--primary-color); +} + +.spaced { + padding:5px; +} + +.main-search { + margin-left: 10%; + margin-right: 10%; + max-width: 100%; + min-width: 80%; +} + + + +.row { + line-height: 24px; + padding-bottom: 10px; + padding-top: 10px; +} + +.flex-row { + display: flex; + width:100%; + flex-direction: row; + + .equal-width { + flex: 1 0 0; + } +} + +.label { + min-width: 200px; + margin-left: 70px; +} +.value { + min-width: 50px; +} + +.data { + margin: auto; + font-weight:550; + font-size: 20px; + padding: 10px; + padding-bottom: 15px; + +} + + + +.main-container { + display: flex; + flex-direction: row; +} + +.main-page { + margin: auto; +} + +.side-bar { + width: 325px; + margin-top: 0px; + padding: 0px; + +} + +.img-container { + /* padding-left: 30px;*/ +} + +.divide-test { + padding: 4px; + position: relative !important; +} + +.test { + justify-content: space-evenly; + display: flex; + flex-direction: row; + flex-wrap: wrap; +} + +.custom-wrap:hover { + text-decoration: underline; + background-color: var(--hover-bg-color); + cursor: pointer; +} + +.custom-wrap { + width: 33%; +} + +@media(max-width: 1000px) { + .custom-wrap { + width: 50%; + } + +} + + +@media (max-width: 700px) { + .label { + margin-left:5px; + min-width: 160px; + + } +} + +.entry { + color:var(--link-color); + display: flex; + padding:5px; + margin: auto; +} + +.entry:hover { + text-decoration: underline; + background-color: var(--hover-bg-color); + cursor: pointer; +} + +.entry:hover div { + text-decoration: underline; + /*background-color: var(--hover-bg-color);*/ +} + +.half-column { + width:28%; +display:flex; +flex-direction: column; +font-size: 18px; + + + + + color { + color:var(--link-color); + } +} +.even { + flex-direction: row; + flex-grow: 1; + flex-wrap: wrap; +} + +.third { + width: 33%; + margin-bottom:20px; +} + +.img-responsive { + height: 170px; +} + +.para { + margin-top:10px; + margin-bottom: 10px; +} + + +.mat-sidenav { + width: 365px; +} + +mat-sidenav { + width: 365px; + +} + + + + + + + + + + + + + + + + + + + + + + +::ng-deep .mat-expansion-panel-content > .mat-expansion-panel-body { + padding: 0 12px 10px; +} + +.dynamic-container { + width: 100%; + min-height:5px; +} + +.mat-expansion-panel-header { + padding: 0 12px; +} + +.filter-box { + padding: 10px; + + &:not(:last-child) { + border-bottom: 1px solid #d9d9d9; + } +} + +.search-parameters { + display: flex; + width: 100%; + flex-wrap: wrap; + + &.center { + margin-top: 10px; + justify-content: center; + } + + & > div { + + padding-left:10px; + padding-right: 2px; + margin-left:5px; + margin-bottom: 5px; + display: flex; + align-items: center; + } + + .actions-container { + margin-left: auto; + flex-shrink: 0; + } +.selected-parameter { + padding-right:5px; + background-color: var(--regular-white-color); + font-size:14px; +} +} + +.controls-container { + display: flex; +} + +.mat-card { + max-width: 1220px; + margin-bottom: 20px; +} + +.mat-card, .controls-container { + width: 100%; + box-sizing: border-box; +} + +.controls-container { + display: flex; + align-items: center; + + .mat-paginator { + margin-left: auto; + } +} + +.cards { + + .substance-cards { + display: block; + } + + .substance-table { + display: none; + } + .substance-tiles{ + display: none; + } +} + +.table { + .substance-cards { + display: none; + } + + .substance-table { + display: block; + } + + .substance-tiles{ + display: none; + } +} + +.tiles { +.substance-cards { + display: none; +} + +.substance-table { + display: none; +} + +.substance-tiles{ + display: flex; +} + +} + +.mat-card-title { + display: flex; + box-sizing: border-box; + width: 100%; + flex-direction: row; + align-items: center; + white-space: nowrap; +} + +.mat-card-title { + white-space: normal; + + .substance-name { + color: #448aff; + padding-right: 10px; + } + + .approval-id { + font-size: 16px; + color: var(--regular-red-color); + } +} +.table-view-name { +color: #448aff; +} + +.tile-title { +height:19px; +} + +::ng-deep .mat-paginator-range-label { + font-weight: 550; +} + +.main-title { + font-size: 26px; + padding-left: 10px; + padding-right: 25px; + font-weight: 550; + padding-top: 10px; + color: var(--primary-color); + width: 285px; +} + +.mat-card-content { + + .mat-chip-list-container { + margin-left: -20px; + margin-bottom: 10px; + } + .mat-chip-list-container-2 { + margin-bottom: 10px; + } +} + +.substance-content { + display: flex; + flex-direction: row; +} + +.tile { +height:300px; +margin:10px; +padding:10px; +width: calc(25% - 20px); +min-width:200px; +max-width:230px; +align-content:center; +overflow:hidden; +text-overflow: ellipsis; +} + +.tile .image-thumbnail { +margin:auto; +margin-bottom:20px; +height:175px; +width:175px; +} + +.tile .structure-container { +width:100%; +padding-right:0px; +} + +.tile-name { +white-space:nowrap; +text-overflow: ellipsis; +overflow: hidden; +width:100%; +height:25px; +} + +.substance-tiles{ +flex-flow: row wrap; +} + +.image-other { +width:175px; +height:175px; +} + +.substance-data { + display: flex; + flex-direction: row; + + &:not(:last-child) { + margin-bottom: 15px; + } + + .label { + font-weight: bold; + min-width: 100px; + } +} + +.structure-container { + padding-right: 10px; +} + +.no-results { + text-align: center; + margin-top: 30px; + font-size: 1.2em; +} + +.mat-paginator { + background: var(--regular-transparent-color); +} + +.image-thumbnail{ +height:150px; +width:150px; +} + +.space-bottom { + margin-bottom: 5px; +} + +.exact-matches-container { + margin-top: 80px; +} + +.exact-match-control { + display: flex; + flex-direction: column; + align-content: center; + justify-content: center; + align-items: center; + margin-bottom: 20px; + + button { + margin-top: 15px; + } +} + +.facet-advanced-options-link { + margin-top: 7px; + color: #448aff; +} + +.facet-search-container { + display: flex; + + .mat-form-field { + flex-grow: 1; + } +} + +.facet-search-loading.mat-progress-bar { + margin-top: -18px; + margin-bottom: 1.09em; +} + +.break { + flex-basis: 0%; + height: 0; + width:0px; +} + +.export { + margin: auto; +} + +.export-button { + color: var(--regular-black-color); + border-radius: 4px; +} + +.menu-checkbox:hover { + background-color: var(--regular-white-color); +} +@media(max-width: 1615px) { + + .full-paginator { + width: 100%; + align-content: center; + } + .break { + flex-basis: 100%; + height: 0; + width: auto; + } + + .controls-container { + flex-wrap: wrap; + } + + .export { + margin: auto; + } +} + +@media(max-width: 1100px) { + .controls-container, .search-parameters { + padding-left: 30px; + } + + .controls-container { + flex-wrap: wrap; + } + + .side-nav-content { + padding-top: 10px; + } + + .export { + margin-right:30px; + } +} + +@media(max-width: 730px) { + .mat-card-title { + flex-direction: column; + align-items: flex-start; + + .substance-name { + margin-bottom: 10px; + } + } + + .page-selector { + display:none !important; + } + + .full-paginator { + min-width: 500px !important; + } + .substance-data { + flex-direction: column; + + .label { + margin-bottom: 10px; + } + + .value, .code-system { + padding-left: 20px; + } + } + + .search-parameters { + font-size: .9em; + + .mat-icon-button { + width: 35px; + height: 35px; + line-height: 35px; + } + } + + .side-nav-content { + padding: 5px; + } +} + +.lock-icon { + color: #f0ad4e; +} + +.narrow-search-suggestions-container { + display: flex; + align-content: center; + align-items: center; + margin-bottom: 10px; + justify-content: center; +} + +.narrow-search-suggestions { + padding: 12px 20px; + width: auto; + display: flex; + align-content: center; + align-items: center; + + .mat-flat-button { + margin-left: 7px; + } +} + +@media(max-width: 600px) { + + .substance-content { + flex-direction: column; + + .image-thumbnail { + margin: 0 auto; + } + } + + .controls-container { + + ::ng-deep { + + .mat-paginator-range-l.references-link{ +display: inline-flex; +vertical-align: middle; +}abel { + margin: 0 3px 0 0; + } + + .mat-paginator-page-size-select { + margin-left: 0; + } + + .mat-paginator-container { + padding-right: 0; + padding-left: 0; + } + + .mat-paginator-page-size { + margin-right: 4px; + } + + .mat-paginator-range-actions { + + .mat-icon-button { + width: 35px; + height: 35px; + line-height: 35px; + } + } + } + } +} + +@media(max-width: 505px) { + .full-paginator { + min-width: 0px !important; + } + .controls-container { + + ::ng-deep { + + .mat-paginator-page-size-label { + display: none; + } + } + } +} + +.zoom:hover{ +cursor:zoom-in; +} + +.icon-align{ +margin-top: -10px; +vertical-align: bottom; +} + +.ext-link{ +color: #448aff; +text-decoration-style: unset; +} + +.sort{ +margin:auto; +margin-bottom: -7px; +} + +.advanced { +padding-left:20px; +color:#448aff; + +} + +.facet-options { +width: 100%; +display:flex; +} + +.tile-button-container { + display: flex; + flex-direction: row; + justify-content: space-evenly +} + +.more-content { +width:45%; +} + +.advanced-container { +width:55%; +align-content:right; +} + +.selected-container { +font-size:14px; +} + +.selected-label { +padding-right:5px; +} + +.reset-facets-button { + margin-left: 5px; + margin-bottom: 5px; +} + +.selected-parameter { +height:auto; +padding-top: 3px; +padding-bottom: 3px; +} + +.selected-container { +max-width:700px; +overflow: hidden; +text-overflow: ellipsis; +white-space: nowrap; +} +.selected-label { +max-width:200px; +overflow: hidden; +text-overflow: ellipsis; +} + +.not-icon { +height:18px !important; +width: 18px !important; +vertical-align: bottom; +} +.page-select { + width:100px; +} + +::ng-deep .auto-width{ + ::ng-deep .mat-form-field { + width: auto !important; + } + ::ng-deep .mat-select-value { + max-width: 100%; + width: auto; + } +} + +.page-label { + color:var(--dark-label-color); + display:block; + font-family:Roboto, "Helvetica Neue", sans-serif; + font-size:14px; + padding-right: 12px; +} + +.page-selector { + display: flex; + flex-direction: row; + align-items: center; +margin-left: 20px; +} + + +:host ::ng-deep .mat-paginator-range-label{ + margin: 0 5px 0 5px !important; + +} + +mat-paginator { + font-size:16px; +} + +.page-input { + width: 50px; + font-size:14px; +} + +.bad-page { + color: var(--regular-red-color); +} + +.full-paginator { + display: flex; + min-width: 660px; +} + +.disable-export { + pointer-events:none; +opacity:.5; +} + +.button-link-img { + line-height: 40px; + color: var(--regular-black-color); +} diff --git a/src/app/core/home/home.component.ts b/src/app/core/home/home.component.ts index 03a1a1761..e8c9a9c11 100644 --- a/src/app/core/home/home.component.ts +++ b/src/app/core/home/home.component.ts @@ -1,30 +1,57 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, ViewChild, AfterViewInit, HostListener } from '@angular/core'; import { GoogleAnalyticsService } from '../google-analytics/google-analytics.service'; -import { ConfigService } from '@gsrs-core/config'; +import { ConfigService, LoadedComponents } from '@gsrs-core/config'; import { Environment } from 'src/environments/environment.model'; import { AuthService } from '@gsrs-core/auth'; -import { Router } from '@angular/router'; +import { Router, NavigationExtras } from '@angular/router'; +import { SubstanceService } from '@gsrs-core/substance'; +import { take } from 'rxjs/operators'; +import { MatDialog } from '@angular/material/dialog'; +import { MatSidenav } from '@angular/material/sidenav'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { UtilsService } from '@gsrs-core/utils'; +import { UsefulLink } from '../config/config.model'; + @Component({ selector: 'app-home', templateUrl: './home.component.html', styleUrls: ['./home.component.scss'] }) -export class HomeComponent implements OnInit { +export class HomeComponent implements OnInit, AfterViewInit { environment: Environment; baseDomain: string; isAuthenticated = false; contactEmail: string; + homeHeader: string; + homeContents: string; isClosedWelcomeMessage = true; imageLoc: any; appId: string; + customLinks: Array; + total: string; + isCollapsed = true; + hasBackdrop = false; + bannerMessage?: string; + usefulLinks?: Array; + + + // these may be necessary due to a strange quirk + // of angular and ngif + searchValue: string; + loadedComponents: LoadedComponents; + + + private overlayContainer: HTMLElement; + @ViewChild('matSideNavInstance', { static: true }) matSideNav: MatSidenav; + browseAll: string; application: string; chemicon: string; clasicBaseHref: string; - //Config for Adverse Event on Shiny Server + // Config for Adverse Event on Shiny Server adverseEventShinyHomepageDisplay = false; adverseEventShinyHomepageURL: string; @@ -32,7 +59,12 @@ export class HomeComponent implements OnInit { private gaService: GoogleAnalyticsService, private configService: ConfigService, private authService: AuthService, - private router: Router + private substanceService: SubstanceService, + private router: Router, + private dialog: MatDialog, + private overlayContainerService: OverlayContainer, + public utilsService: UtilsService, + ) { this.contactEmail = this.configService.configData.contactEmail; this.clasicBaseHref = this.configService.environment.clasicBaseHref; @@ -40,19 +72,116 @@ export class HomeComponent implements OnInit { ngOnInit() { this.environment = this.configService.environment; - this.application = `${this.configService.environment.baseHref || '/'}assets/icons/home/icon_application.png`; - this.browseAll = `${this.configService.environment.baseHref || '/'}assets/icons/home/icon_browseall.png`; - this.chemicon = `${this.configService.environment.baseHref || '/'}assets/icons/home/icon_registersubstance.png`; + this.application = `${this.configService.environment.baseHref || ''}assets/icons/home/icon_application.png`; + this.browseAll = `${this.configService.environment.baseHref || ''}assets/icons/home/icon_browseall.png`; + this.chemicon = `${this.configService.environment.baseHref || ''}assets/icons/home/icon_registersubstance.png`; + this.appId = this.configService.environment.appId; + this.bannerMessage = this.configService.configData.bannerMessage || null; + this.usefulLinks = this.configService.configData.usefulLinks || []; + this.homeHeader = this.configService.configData.homeHeader || null; + this.homeContents = this.configService.configData.homeContents || null; + this.loadedComponents = this.configService.configData.loadedComponents || null; + // this code cause memory errors in the build process + /*let notempty = false; + for (const property in this.loadedComponents) { + if (this.loadedComponents[property] === true) { + notempty = true; + } + } + if (!notempty) { + this.loadedComponents = null; + }*/ + + let notempty = false; + if (this.loadedComponents) { + if (this.loadedComponents.applications) { + notempty = true; + } else if (this.loadedComponents.clinicaltrials) { + notempty = true; + } else if (this.loadedComponents.adverseevents) { + notempty = true; + } else if (this.loadedComponents.impurities) { + notempty = true; + } else if (this.loadedComponents.products) { + notempty = true; + } + + if (!notempty) { + this.loadedComponents = null; + } + } + + this.imageLoc = `${this.environment.baseHref || ''}assets/images/home/`; + this.authService.hasAnyRolesAsync('DataEntry', 'SuperDataEntry', 'Admin').subscribe(response => { this.isAuthenticated = response; }); this.gaService.sendPageView(`Home`); this.baseDomain = this.configService.configData.apiUrlDomain; - this.isClosedWelcomeMessage = localStorage.getItem('isClosedWelcomeMessage') === 'true'; + this.customLinks = this.configService.configData.homeDynamicLinks || []; + this.customLinks.forEach (link => { + link.total = 0; + const searchStr = `${link.facetName}:${link.facetValue}`; + this.substanceService.searchSingleFacetSimpleCount(link.facetName, link.facetValue).pipe(take(1)).subscribe( response => { + if (response){ + link.total = Number(response.total); + } else { + link.total = 0; + } + }); + }); + this.substanceService.getRecordCount().subscribe( response => { + this.total = response; + }); + // this.isClosedWelcomeMessage = localStorage.getItem('isClosedWelcomeMessage') === 'false'; + this.isClosedWelcomeMessage = false; this.getAdverseEventShinyConfig(); + this.overlayContainer = this.overlayContainerService.getContainerElement(); + + } +ngAfterViewInit(){ + this.processResponsiveness(); + const openSubscription = this.matSideNav.openedStart.subscribe(() => { + this.utilsService.handleMatSidenavOpen(1100); + }); + const closeSubscription = this.matSideNav.closedStart.subscribe(() => { + this.utilsService.handleMatSidenavClose(); + }); + +} + +@HostListener('window:resize', ['$event']) +onResize() { + this.processResponsiveness(); +} + +private processResponsiveness = () => { + setTimeout(() => { + if (window) { + if (window.innerWidth < 1100) { + this.matSideNav.close(); + this.isCollapsed = true; + this.hasBackdrop = true; + } else { + this.matSideNav.open(); + this.hasBackdrop = false; + } + } + }); +} + openSideNav() { + this.gaService.sendEvent('substancesFiltering', 'button:sidenav', 'open'); + this.matSideNav.open(); + } + + routeToCustom(link) { + const navigationExtras: NavigationExtras = { + queryParams: { 'facets': link.facetName + '*' + link.facetValue + '.true' } + }; + this.router.navigate(['/browse-substance'], navigationExtras); } closeWelcomeMessage(): void { @@ -64,6 +193,45 @@ export class HomeComponent implements OnInit { this.router.navigate(['/browse-substance']); } + openModal(templateRef, tile:UsefulLink) { + + const dialogRef = this.dialog.open(templateRef, { + height: '200px', + width: '400px', + data: { + href: tile.href, + templateDescription: tile.templateDescription + } + }); + this.overlayContainer.style.zIndex = '1002'; + + dialogRef.afterClosed().subscribe(result => { + this.overlayContainer.style.zIndex = null; + }); + } + + + increaseMenuZindex(): void { + this.overlayContainer.style.zIndex = '1001'; + } + + removeZindex(): void { + this.overlayContainer.style.zIndex = null; + } + + processSubstanceSearch(searchValue: string) { + this.navigateToSearchResults(searchValue); + } + + navigateToSearchResults(searchTerm: string) { + + const navigationExtras: NavigationExtras = { + queryParams: searchTerm ? { 'search': searchTerm } : null + }; + + this.router.navigate(['/browse-substance'], navigationExtras); + } + getAdverseEventShinyConfig(): void { if (this.configService.configData) { if (this.configService.configData.adverseEventShinyHomepageDisplay && this.configService.configData.adverseEventShinyHomepageDisplay !== null) { diff --git a/src/app/core/loading/loading.module.ts b/src/app/core/loading/loading.module.ts index bbb26e0ef..6c48a7c07 100644 --- a/src/app/core/loading/loading.module.ts +++ b/src/app/core/loading/loading.module.ts @@ -3,11 +3,13 @@ import { CommonModule } from '@angular/common'; import { LoadingComponent } from './loading/loading.component'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { LoadingOverlayComponent } from './loading-overlay/loading-overlay.component'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; @NgModule({ imports: [ CommonModule, - MatProgressBarModule + MatProgressBarModule, + MatProgressSpinnerModule ], declarations: [ LoadingComponent, diff --git a/src/app/core/loading/loading/loading.component.html b/src/app/core/loading/loading/loading.component.html index 91c1f30cf..8d268ae14 100644 --- a/src/app/core/loading/loading/loading.component.html +++ b/src/app/core/loading/loading/loading.component.html @@ -2,3 +2,9 @@ mode="buffer" *ngIf="isLoading"> + + + + diff --git a/src/app/core/loading/loading/loading.component.scss b/src/app/core/loading/loading/loading.component.scss index 433a2c4f6..eaa8502d9 100644 --- a/src/app/core/loading/loading/loading.component.scss +++ b/src/app/core/loading/loading/loading.component.scss @@ -4,4 +4,14 @@ top: 0; left: 0; right: 0; +} + + + +.mat-progress-spinner { + z-index: 1003; + position: fixed; + top: 50%; + left: calc(50% - 25px); +/* transform: translate(-50%, -50%); */ } \ No newline at end of file diff --git a/src/app/core/main-notification/main-notification/main-notification.component.scss b/src/app/core/main-notification/main-notification/main-notification.component.scss index bead19301..c4362dcc2 100644 --- a/src/app/core/main-notification/main-notification/main-notification.component.scss +++ b/src/app/core/main-notification/main-notification/main-notification.component.scss @@ -23,17 +23,17 @@ } &.default { - background-color: #448aff; - color: white; + background-color: var(--link-primary-color); + color: var(--regular-white-color); } &.success { - background-color: #A7FFEB; - color: #262626; + background-color: var(--notif-success-bg-color); + color: var(dark-grey-color); } &.error { - background-color: #E57373; - color: black; + background-color: var(--notif-error-bg-color); + color: var(--regular-black-color); } -} \ No newline at end of file +} diff --git a/src/app/core/name-resolver/name-resolver-dialog.component.ts b/src/app/core/name-resolver/name-resolver-dialog.component.ts index 56405dd97..dfea94736 100644 --- a/src/app/core/name-resolver/name-resolver-dialog.component.ts +++ b/src/app/core/name-resolver/name-resolver-dialog.component.ts @@ -1,5 +1,5 @@ import {Component, Inject, OnInit} from '@angular/core'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { GoogleAnalyticsService } from '../google-analytics/google-analytics.service'; @Component({ diff --git a/src/app/core/name-resolver/name-resolver.component.html b/src/app/core/name-resolver/name-resolver.component.html index 914b0423a..dfb01789a 100644 --- a/src/app/core/name-resolver/name-resolver.component.html +++ b/src/app/core/name-resolver/name-resolver.component.html @@ -3,7 +3,7 @@ - @@ -23,7 +23,7 @@

- GSRS duplicate
+ GSRS Record
{{ name._name}}
-
\ No newline at end of file +
diff --git a/src/app/core/name-resolver/name-resolver.component.scss b/src/app/core/name-resolver/name-resolver.component.scss index a38877270..ab7b9dedc 100644 --- a/src/app/core/name-resolver/name-resolver.component.scss +++ b/src/app/core/name-resolver/name-resolver.component.scss @@ -23,4 +23,11 @@ .mat-form-field { width: 500px; max-width: 100%; +} + + + +.search ::ng-deep .mat-form-field-infix { + border-top-width: 1px !important; + border-bottom-width: 1px !important; } \ No newline at end of file diff --git a/src/app/core/privacy-statement/privacy-statement.component.html b/src/app/core/privacy-statement/privacy-statement.component.html new file mode 100644 index 000000000..f12729fc8 --- /dev/null +++ b/src/app/core/privacy-statement/privacy-statement.component.html @@ -0,0 +1,9 @@ +
+ + + + +
+
+
+
\ No newline at end of file diff --git a/src/app/core/privacy-statement/privacy-statement.component.scss b/src/app/core/privacy-statement/privacy-statement.component.scss new file mode 100644 index 000000000..5de5a2c35 --- /dev/null +++ b/src/app/core/privacy-statement/privacy-statement.component.scss @@ -0,0 +1,16 @@ +.content-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 65px 5px 0 5px; +} + +.mat-card { + margin-top: 15px; + display: flex; + flex-direction: column; + box-sizing: border-box; + max-width: 800px; + width: 100%; +} diff --git a/src/app/core/privacy-statement/privacy-statement.component.ts b/src/app/core/privacy-statement/privacy-statement.component.ts new file mode 100644 index 000000000..a77833683 --- /dev/null +++ b/src/app/core/privacy-statement/privacy-statement.component.ts @@ -0,0 +1,43 @@ +import { HttpClient } from '@angular/common/http'; +import { Component, OnInit } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; +import { ConfigService } from '@gsrs-core/config'; + +// tried this: +// import html from '../assets/data/privacy-policy.html'; +// this.htmlText = html; +// but it requires a webpack loader, not sure what that is. +// if using this strategy, need to create/add to src/typings.d.ts file with this: +// declare module '*html' +// { +// const value:string; +// export default value +// } + +@Component({ + selector: 'app-privacy-statement', + templateUrl: './privacy-statement.component.html', + styleUrls: ['./privacy-statement.component.scss'] +}) +export class PrivacyStatementComponent implements OnInit { + htmlText; + constructor( + private http:HttpClient, + private sanitizer:DomSanitizer, + private configService: ConfigService + ){ } + ngOnInit(){ + const privacyStatement = this.configService.configData.privacyStatement; + if(privacyStatement) { + // if used, privacyStatement should be a json encoded string in config.json + this.htmlText = this.sanitizer.bypassSecurityTrustHtml(privacyStatement); + } else { + // if used overwrite this file with a simple html version of your privacy statement. + this.http.get('assets/html/privacy-statement.html',{responseType:'text'}).subscribe(result=>{ + this.htmlText = this.sanitizer.bypassSecurityTrustHtml(result); + }, error => { + this.htmlText = "Error fetching page content"; + }); + } + } +} \ No newline at end of file diff --git a/src/app/core/privacy-statement/privacy-statement.module.ts b/src/app/core/privacy-statement/privacy-statement.module.ts new file mode 100644 index 000000000..1438f8476 --- /dev/null +++ b/src/app/core/privacy-statement/privacy-statement.module.ts @@ -0,0 +1,32 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { PrivacyStatementComponent } from './privacy-statement.component'; +import { MatSidenavModule } from '@angular/material/sidenav'; +import { MatCardModule } from '@angular/material/card'; +import { Router, Routes, RouterModule } from '@angular/router'; + +const privacyStatmentRoutes: Routes = [ + { + path: 'privacy-statement', + component: PrivacyStatementComponent + }, +]; + +@NgModule({ + declarations: [PrivacyStatementComponent], + imports: [ + CommonModule, + MatSidenavModule, + MatCardModule + ], + exports: [PrivacyStatementComponent] +}) + +export class PrivacyStatementModule { + constructor(router: Router) { + privacyStatmentRoutes.forEach(route => { + router.config[0].children.push(route); + }); +} + +} \ No newline at end of file diff --git a/src/app/core/pwd-recovery/pwd-recovery.component.html b/src/app/core/pwd-recovery/pwd-recovery.component.html new file mode 100644 index 000000000..ef59f4a78 --- /dev/null +++ b/src/app/core/pwd-recovery/pwd-recovery.component.html @@ -0,0 +1,18 @@ +
+ X + + + Enter email associated with the account: + + +
+ + + +
+
+ + + +
+
diff --git a/src/app/core/pwd-recovery/pwd-recovery.component.scss b/src/app/core/pwd-recovery/pwd-recovery.component.scss new file mode 100644 index 000000000..0c7c6ad0c --- /dev/null +++ b/src/app/core/pwd-recovery/pwd-recovery.component.scss @@ -0,0 +1,32 @@ +.container { + width: 100%; + //display: flex; + align-items: center; + justify-content: center; + //padding: 86px 20px 20px 20px; +} + +.mat-card { + max-width: 400px; + width: 100%; + box-sizing: border-box; + box-shadow: none !important; +} + +.form { + display: flex; + flex-direction: column; + width: 100%; + padding-top: 20px; +} + +.mat-card-actions { + display: flex; + justify-content: flex-end; +} + +.exitBtn { + float: right; + width: 1px; + cursor: pointer; +} diff --git a/src/app/core/pwd-recovery/pwd-recovery.component.spec.ts b/src/app/core/pwd-recovery/pwd-recovery.component.spec.ts new file mode 100644 index 000000000..36906c05d --- /dev/null +++ b/src/app/core/pwd-recovery/pwd-recovery.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PwdRecoveryComponent } from './pwd-recovery.component'; + +describe('PwdRecoveryComponent', () => { + let component: PwdRecoveryComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ PwdRecoveryComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(PwdRecoveryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/pwd-recovery/pwd-recovery.component.ts b/src/app/core/pwd-recovery/pwd-recovery.component.ts new file mode 100644 index 000000000..97f0535b1 --- /dev/null +++ b/src/app/core/pwd-recovery/pwd-recovery.component.ts @@ -0,0 +1,34 @@ +import { Component, Inject, OnInit, Optional } from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; + +@Component({ + selector: 'app-pwd-recovery', + templateUrl: './pwd-recovery.component.html', + styleUrls: ['./pwd-recovery.component.scss'] +}) +export class PwdRecoveryComponent implements OnInit { + + pwdForm = new FormGroup({ + email: new FormControl('', Validators.required) + }); + + constructor( + private dialogRef: MatDialogRef, + @Optional() @Inject(MAT_DIALOG_DATA) public data: string + ) { + console.log('data:::', data); + } + + ngOnInit(): void { + } + + exit() { + this.dialogRef.close(); + } + + pwdRecovery() { + this.exit(); + } + +} diff --git a/src/app/core/references-manager/references-manager.component.html b/src/app/core/references-manager/references-manager.component.html index f9c511790..1f3ed21a3 100644 --- a/src/app/core/references-manager/references-manager.component.html +++ b/src/app/core/references-manager/references-manager.component.html @@ -24,7 +24,10 @@ Tags - {{tag}} + + {{tag}} hide + {{ref.tags.length > 0? ref.tags[0]:null}} more... + @@ -41,6 +44,16 @@ + + Access + + + + + + + + diff --git a/src/app/core/references-manager/references-manager.component.scss b/src/app/core/references-manager/references-manager.component.scss index 8ac478323..19d9dbf97 100644 --- a/src/app/core/references-manager/references-manager.component.scss +++ b/src/app/core/references-manager/references-manager.component.scss @@ -1,5 +1,12 @@ .mat-row:nth-child(odd){ - background-color:#f9f9f9; + background-color: var(--ref-manager-mat-row-nth-child-color); +} + + +.morelink { + color: var(--link-color); + padding-left:10px; + } diff --git a/src/app/core/references-manager/references-manager.component.ts b/src/app/core/references-manager/references-manager.component.ts index b28dccf8a..faf4e4cf6 100644 --- a/src/app/core/references-manager/references-manager.component.ts +++ b/src/app/core/references-manager/references-manager.component.ts @@ -15,7 +15,8 @@ export class ReferencesManagerComponent implements OnInit { @Input() references: Array; subRef: Array; matchedRef: SubstanceReference[] = []; - displayedColumns: string[] = ['index', 'citation', 'docType', 'tags', 'files', 'lastEdited']; + showmore = false; + displayedColumns: string[] = ['index', 'citation', 'docType', 'tags', 'files', 'lastEdited', 'access']; constructor(private substanceService: SubstanceService) { } diff --git a/src/app/core/references-manager/references-manager.module.ts b/src/app/core/references-manager/references-manager.module.ts index 28ff6b21a..601bf8c1c 100644 --- a/src/app/core/references-manager/references-manager.module.ts +++ b/src/app/core/references-manager/references-manager.module.ts @@ -1,13 +1,17 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import {MatTableModule, MatIconModule} from '@angular/material'; +import {MatTooltipModule} from '@angular/material/tooltip'; +import {MatIconModule} from '@angular/material/icon'; +import {MatTableModule} from '@angular/material/table'; + import {ReferencesManagerComponent} from './references-manager.component'; @NgModule({ imports: [ CommonModule, MatTableModule, - MatIconModule + MatIconModule, + MatTooltipModule ], declarations: [ReferencesManagerComponent], diff --git a/src/app/core/register/register.component.html b/src/app/core/register/register.component.html new file mode 100644 index 000000000..6e20f9fe0 --- /dev/null +++ b/src/app/core/register/register.component.html @@ -0,0 +1,41 @@ +
+ X + + + Enter the following to request registration or resolve login/password issue: + + +
+ + + + + + +
+
+ + + +
+ +
+ + diff --git a/src/app/core/register/register.component.scss b/src/app/core/register/register.component.scss new file mode 100644 index 000000000..76458f100 --- /dev/null +++ b/src/app/core/register/register.component.scss @@ -0,0 +1,33 @@ +.reg-container { + width: 100%; + //display: flex; + align-items: center; + justify-content: center; + //padding: 86px 20px 20px 20px; +} + +.mat-card { + max-width: 400px; + width: 100%; + box-sizing: border-box; + box-shadow: none !important; +} + +.reg-form { + display: flex; + flex-direction: column; + width: 100%; + padding-top: 20px; +} + +.mat-card-actions { + display: flex; + justify-content: flex-end; +} + +.exitBtn { + float: right; + width: 1px; + cursor: pointer; +} + diff --git a/src/app/core/register/register.component.spec.ts b/src/app/core/register/register.component.spec.ts new file mode 100644 index 000000000..f6db869d5 --- /dev/null +++ b/src/app/core/register/register.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RegisterComponent } from './register.component'; + +describe('RegisterComponent', () => { + let component: RegisterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ RegisterComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(RegisterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/register/register.component.ts b/src/app/core/register/register.component.ts new file mode 100644 index 000000000..5cff68539 --- /dev/null +++ b/src/app/core/register/register.component.ts @@ -0,0 +1,38 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { MatDialogRef } from '@angular/material/dialog'; + +@Component({ + selector: 'app-register', + templateUrl: './register.component.html', + styleUrls: ['./register.component.scss'] +}) +export class RegisterComponent implements OnInit { + + regForm = new FormGroup({ + username: new FormControl('', Validators.required), + email: new FormControl('', Validators.required) + }); + + constructor( + private fb: FormBuilder, + private dialogRef: MatDialogRef + ) { } + + ngOnInit() { + this.regForm.controls.email.markAsTouched(); + // this.regForm = this.fb.group({ + // username: '', + // email: '' + // }) + } + + register() { + this.dialogRef.close(this.regForm.value); + } + + exit() { + this.dialogRef.close(); + } + +} diff --git a/src/app/core/registrars/registrars.component.html b/src/app/core/registrars/registrars.component.html new file mode 100644 index 000000000..759e5a1c7 --- /dev/null +++ b/src/app/core/registrars/registrars.component.html @@ -0,0 +1,292 @@ + diff --git a/src/app/core/registrars/registrars.component.scss b/src/app/core/registrars/registrars.component.scss new file mode 100644 index 000000000..af93a43ab --- /dev/null +++ b/src/app/core/registrars/registrars.component.scss @@ -0,0 +1,301 @@ +.middle-content-container { + display: flex; + justify-content: center; + padding-top: 0px; + margin-top: 75px; +} + +.button-container { + display:flex; + justify-content: center; + margin-top: 15px; +} + +.custom-column { + display: flex; + flex-direction: column; + width: 100%; +} +.search-section { + margin: 0 20px 0 20px; + width: 300px; +} + +.entry2 { + width: 100%; + display: flex; + flex-direction: row; +} + +.label2 { + width: 75%; +} + +.value2 { + width:25%; +} + +.register-section { + margin: 0 20px 0 10px; + width: auto; +} + +.register-header { + font-size: 16px; + font-weight: 400; +} + +.register-card-content { + display: flex; + + .register-other { + margin-top: 10px; + margin-left: 20px; + } + + .register-main { + margin-top: 10px; + margin-right: 20px; + min-width: 275px; + margin-left: 10px; + + .main-list, .other-list { + padding-top: 0px; + } + } +} + +.old-icon { + height: 28px; + width: 30px; + padding-right: 7px; + margin-bottom: -5px; +} + +.divider-vertical { + margin-top: 65px; +} + +.left-search { + margin-top: 10px; +} + +.shrunken-icon { + width: 20px; + padding-left: 5px; +} + +.closed-welcome-padding { + height:60px !important; + width:100%; +} + +.mat-list-base { + position: relative; +} + +.subtest { + position: relative !important; + padding: 2px; +} +a.mat-list-item { + color: var(--link-color); +} + +.mat-list-base .mat-list-item .mat-list-icon { + color: var(--mat-list-color); +} + +.mat-list-base .mat-list-item.mat-list-item-with-avatar { + height: 48px !important; +} +.big-button { + font-size: 19px !important; + color: var(--regular-white-color) !important; + background-color: var(--primary-color) !important; + padding: 3px 14px !important; +} + +.button-container { + margin-left:auto; + margin-right: auto; + margin-top: 15px; + + .big-button { + font-size: 19px !important; + color: var(--regular-white-color) !important; + background-color: var(--primary-color) !important; + padding: 3px 14px !important; + } +} + +.welcome-banner { + margin-top: 75px; + + .close-container { + display: flex; + justify-content: flex-end; + color: #296ca3; + } + } + + +mat-list-item { + height: 52px; +} + +.home-card { + margin-bottom: 15px; +} + +.padding { + padding-bottom: 5px; + padding-bottom: 5px; + margin-left: 61px; + width: 90% !important; +} + +.home { + font-weight: 500; + font-size: 27px; + color: var(--primary-color); +} + +.spaced { + padding:5px; +} + +.main-search { + margin-left: 10%; + margin-right: 10%; + max-width: 100%; + min-width: 80%; +} + + + +.row { + line-height: 24px; + padding-bottom: 10px; + padding-top: 10px; +} + +.flex-row { + display: flex; + width:100%; + flex-direction: row; + + .equal-width { + flex: 1 0 0; + } +} + +.label { + min-width: 250px; +} +.value { + min-width: 50px; +} + +.data { + margin: auto; + font-weight:550; + font-size: 20px; + padding: 10px; + padding-bottom: 15px; + +} + + + +.main-container { + display: flex; + flex-direction: row; +} + +.main-page { + margin: auto; +} + +.side-bar { + width: 350px; + margin-top: 75px; + padding: 0px; + +} + +.img-container { + padding-left: 30px; +} + +.divide-test { + padding: 4px; + position: relative !important; +} + +.test { + justify-content: space-evenly; + display: flex; + flex-direction: row; + flex-wrap: wrap; +} + +.custom-wrap:hover { + text-decoration: underline; + background-color: var(--hover-bg-color); + cursor: pointer; +} + +.custom-wrap { +} + +.custom-header { + margin-top: 32px; +} + +.entry { + color:var(--link-color); + display: flex; + padding:5px; + margin: auto; +} + +.entry:hover { + /* text-decoration: underline; + background-color: var(--hover-bg-color); + cursor: pointer;*/ +} + +.entry:hover div { + text-decoration: underline; + /*background-color: var(--hover-bg-color);*/ +} + +.half-column { + width:28%; + display:flex; + flex-direction: column; + font-size: 18px; + + color { + color:var(--link-color); + } +} +.even { + flex-direction: row; + flex-grow: 1; +} + +.third { + width: 33%; + margin-bottom:20px; +} + +.img-responsive { + height: 170px; +} + +.para { + margin-top:10px; + margin-bottom: 10px; +} diff --git a/src/app/core/registrars/registrars.component.spec.ts b/src/app/core/registrars/registrars.component.spec.ts new file mode 100644 index 000000000..0b34cd207 --- /dev/null +++ b/src/app/core/registrars/registrars.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RegistrarsComponent } from './registrars.component'; + +describe('RegistrarsComponent', () => { + let component: RegistrarsComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ RegistrarsComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(RegistrarsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/registrars/registrars.component.ts b/src/app/core/registrars/registrars.component.ts new file mode 100644 index 000000000..89eac17f0 --- /dev/null +++ b/src/app/core/registrars/registrars.component.ts @@ -0,0 +1,140 @@ +import { Component, OnInit } from '@angular/core'; +import { GoogleAnalyticsService } from '../google-analytics/google-analytics.service'; +import { ConfigService, LoadedComponents } from '@gsrs-core/config'; +import { Environment } from 'src/environments/environment.model'; +import { AuthService } from '@gsrs-core/auth'; +import { Router, NavigationExtras } from '@angular/router'; +import { SubstanceService } from '@gsrs-core/substance'; +import { take } from 'rxjs/operators'; +@Component({ + selector: 'app-registrars', + templateUrl: './registrars.component.html', + styleUrls: ['./registrars.component.scss'] +}) +export class RegistrarsComponent implements OnInit { + + environment: Environment; + baseDomain: string; + isAuthenticated = false; + contactEmail: string; + isClosedWelcomeMessage = true; + imageLoc: any; + appId: string; + customLinks1: Array; + customLinks2: Array; + + browseAll: string; + application: string; + chemicon: string; + clasicBaseHref: string; + loadedComponents: LoadedComponents; + + // Config for Adverse Event on Shiny Server + adverseEventShinyHomepageDisplay = false; + adverseEventShinyHomepageURL: string; + + constructor( + private gaService: GoogleAnalyticsService, + private configService: ConfigService, + private authService: AuthService, + private substanceService: SubstanceService, + private router: Router + ) { + this.contactEmail = this.configService.configData.contactEmail; + this.clasicBaseHref = this.configService.environment.clasicBaseHref; + } + + ngOnInit() { + this.environment = this.configService.environment; + this.application = `${this.configService.environment.baseHref || ''}assets/icons/home/icon_application.png`; + this.browseAll = `${this.configService.environment.baseHref || ''}assets/icons/home/icon_browseall.png`; + this.chemicon = `${this.configService.environment.baseHref || ''}assets/icons/home/icon_registersubstance.png`; + this.loadedComponents = this.configService.configData.loadedComponents || null; + this.appId = this.configService.environment.appId; + + this.authService.hasAnyRolesAsync('DataEntry', 'SuperDataEntry', 'Admin').subscribe(response => { + this.isAuthenticated = response; + }); + this.gaService.sendPageView(`Home`); + this.baseDomain = this.configService.configData.apiUrlDomain; + this.customLinks1 = this.configService.configData.registrarDynamicLinks; + this.customLinks2 = this.configService.configData.registrarDynamicLinks2; + + this.customLinks1.forEach (link => { + let str = ''; + for (let i = 0; i < link.facets.length; i++) { + + if (i === 0) { + str = 'facet=' + link.facets[i].facetName + '/' + link.facets[i].facetValue; + } else { + str += '&facet=' + link.facets[i].facetName + '/' + link.facets[i].facetValue; + } + } + this.substanceService.searchFromString(str).pipe(take(1)).subscribe( response => { + link.total = response.total; + }); + }); + this.customLinks2.forEach (link => { + let str = ''; + for (let i = 0; i < link.facets.length; i++) { + + if (i === 0) { + str = 'facet=' + link.facets[i].facetName + '/' + link.facets[i].facetValue; + } else { + str += '&facet=' + link.facets[i].facetName + '/' + link.facets[i].facetValue; + } + } + this.substanceService.searchFromString(str).pipe(take(1)).subscribe( response => { + link.total = Number(response.total); + }); + }); + this.isClosedWelcomeMessage = localStorage.getItem('isClosedWelcomeMessage') === 'true'; + this.processFacets(); + } + + routeToCustom(link) { + for (let i = 0; i < link.facets.length; i++) { + let string = ''; + if (i === 0) { + string = 'facets:' + link.facetName + '*' + link.facetValue + '.true'; + } else { + string += ',' + link.facetName + '*' + link.facetValue + '.true'; + } + } + const navigationExtras: NavigationExtras = { + queryParams: { 'facets': link.facetName + '*' + link.facetValue + '.true' } + }; + this.router.navigate(['/browse-substance'], navigationExtras); + } + + processFacets() { + this.customLinks1.forEach(link => { + let str = ''; + for (let i = 0; i < link.facets.length; i++) { + + if (i === 0) { + str += '' + link.facets[i].facetName + '*' + link.facets[i].facetValue + '.true'; + } else { + str += ',' + link.facets[i].facetName + '*' + link.facets[i].facetValue + '.true'; + } + } + + link.queryParams = str; + }); + + this.customLinks2.forEach(link => { + let str = ''; + for (let i = 0; i < link.facets.length; i++) { + + if (i === 0) { + str += '' + link.facets[i].facetName + '*' + link.facets[i].facetValue + '.true'; + } else { + str += ',' + link.facets[i].facetName + '*' + link.facets[i].facetValue + '.true'; + } + } + + link.queryParams = str; + }); + } + +} diff --git a/src/app/core/scroll-to/scroll-to-registration.class.ts b/src/app/core/scroll-to/scroll-to-registration.class.ts index 204817580..17aab5306 100644 --- a/src/app/core/scroll-to/scroll-to-registration.class.ts +++ b/src/app/core/scroll-to/scroll-to-registration.class.ts @@ -36,7 +36,7 @@ export class ScrollToRegistration { unregister(): void { this.triggerElement.removeEventListener('click', this.scrollToEventHandler); - this.registrationTerminatedSubject.next(); + this.registrationTerminatedSubject.next(0); } inactivateRegistration(): void { diff --git a/src/app/core/sequence-search/sequence-search.component.html b/src/app/core/sequence-search/sequence-search.component.html index 42fdcc96a..df275dbe7 100644 --- a/src/app/core/sequence-search/sequence-search.component.html +++ b/src/app/core/sequence-search/sequence-search.component.html @@ -23,9 +23,6 @@ Global Alignment Match - - Local Alignment Match - diff --git a/src/app/core/sequence-search/sequence-search.component.scss b/src/app/core/sequence-search/sequence-search.component.scss index 375d96571..09a25700a 100644 --- a/src/app/core/sequence-search/sequence-search.component.scss +++ b/src/app/core/sequence-search/sequence-search.component.scss @@ -22,7 +22,7 @@ .mat-error { display: block; - background-color: white; + background-color: var(--regular-white-color); padding: 0 2px; } } @@ -33,7 +33,7 @@ box-sizing: border-box; width: 100%; height: 300px; - border: solid 1px rgba(0, 0, 0, 0.42); + border: solid 1px var(--textarea-dark-border-color); border-radius: 3px; padding: 10px; } @@ -55,5 +55,5 @@ .error-message { text-align: center; font-size: 16px; - color: red; + color: var(--regular-red-color); } diff --git a/src/app/core/structure-editor/structure-editor.component.html b/src/app/core/structure-editor/structure-editor.component.html index 2259d45e9..9372d76ac 100644 --- a/src/app/core/structure-editor/structure-editor.component.html +++ b/src/app/core/structure-editor/structure-editor.component.html @@ -2,25 +2,44 @@ - +
-
- Load an image by pasting a copied image into the canvas with ctrl + v, or dragging a local image file + +
+ Load an image by pasting a copied image into the canvas with ctrl + v, or dragging a local image file +
+ +
+ +
+
+
-
{{canvasMessage}}
- +
+ + + + + + \ No newline at end of file diff --git a/src/app/core/structure-editor/structure-editor.component.scss b/src/app/core/structure-editor/structure-editor.component.scss index b5e7a84e2..29230ee7d 100644 --- a/src/app/core/structure-editor/structure-editor.component.scss +++ b/src/app/core/structure-editor/structure-editor.component.scss @@ -16,6 +16,11 @@ flex-grow: 1; } + +.options { + margin-right: 10px; +} + #canvas-container { width:100%; text-align:center; @@ -23,7 +28,7 @@ .canvas-message { font-weight:bold; - color:red; + color:var(--regular-red-color); } .canvas-label { @@ -49,4 +54,4 @@ .higher { z-index: 9988 !important; -} \ No newline at end of file +} diff --git a/src/app/core/structure-editor/structure-editor.component.ts b/src/app/core/structure-editor/structure-editor.component.ts index d5dbf2170..9b92391e2 100644 --- a/src/app/core/structure-editor/structure-editor.component.ts +++ b/src/app/core/structure-editor/structure-editor.component.ts @@ -44,10 +44,10 @@ export class StructureEditorComponent implements OnInit, AfterViewInit, OnDestro public context: CanvasRenderingContext2D; public canvasCopy: HTMLCanvasElement; private jsdrawScriptUrls = [ - `${environment.baseHref || '/'}assets/dojo/dojo.js`, - `${environment.baseHref || '/'}assets/jsdraw/Scilligence.JSDraw2.Pro.js`, - `${environment.baseHref || '/'}assets/jsdraw/Scilligence.JSDraw2.Resources.js`, - `${environment.baseHref || '/'}assets/jsdraw/JSDraw.extensions.js` + `${environment.baseHref || ''}assets/dojo/dojo.js`, + `${environment.baseHref || ''}assets/jsdraw/Scilligence.JSDraw2.Pro.js`, + `${environment.baseHref || ''}assets/jsdraw/Scilligence.JSDraw2.Resources.js`, + `${environment.baseHref || ''}assets/jsdraw/JSDraw.extensions.js` ]; ketcherFilePath: string; @@ -106,7 +106,7 @@ export class StructureEditorComponent implements OnInit, AfterViewInit, OnDestro window.addEventListener('drop', this.preventDrag); window.addEventListener('paste', this.checkPaste); - this.ketcherFilePath = `${environment.baseHref || '/'}assets/ketcher/ketcher.html`; + this.ketcherFilePath = `${environment.baseHref || ''}assets/ketcher/ketcher.html`; this.structureEditor = environment.structureEditor; @@ -245,8 +245,18 @@ export class StructureEditorComponent implements OnInit, AfterViewInit, OnDestro } cleanStructure() { - const smiles = this.editor.getSmiles(); + const molfile = this.editor.getMolfile(); + if (molfile != null && molfile !== '') { + this.structureService.interpretStructure(molfile).pipe(take(1)).subscribe(response => { + if (response && response.structure && response.structure.smiles) { + this.cleanStructureSmiles(response.structure.smiles); + } + }); + } + } + + cleanStructureSmiles(smiles: string) { if (smiles != null && smiles !== '') { this.structureService.interpretStructure(smiles).pipe(take(1)).subscribe(response => { if (response && response.structure && response.structure.molfile) { @@ -256,4 +266,16 @@ export class StructureEditorComponent implements OnInit, AfterViewInit, OnDestro } } + + standardize(standard: string): void { + this.loadingService.setLoading(true); + const mol = this.editor.getMolfile(); + this.structureService.interpretStructure(mol, '', standard).pipe(take(1)).subscribe((response: any) => { + if (response && response.structure && response.structure.molfile) { + this.editor.setMolecule(response.structure.molfile); + } + this.loadingService.setLoading(false); + }, () => {this.loadingService.setLoading(false); }); + } + } diff --git a/src/app/core/structure-editor/structure-editor.module.ts b/src/app/core/structure-editor/structure-editor.module.ts index e45b903f4..0c2f0f81e 100644 --- a/src/app/core/structure-editor/structure-editor.module.ts +++ b/src/app/core/structure-editor/structure-editor.module.ts @@ -14,16 +14,20 @@ import { MatProgressBarModule } from '@angular/material/progress-bar'; import { StructureEditorComponent } from './structure-editor.component'; import {DragDropPasteDirective} from '@gsrs-core/substance-form/structure/drag-drop-paste.component'; import { StructureModule } from '@gsrs-core/structure/structure.module'; +import { MatSelectModule } from '@angular/material/select'; +import { MatMenuModule } from '@angular/material/menu'; @NgModule({ imports: [ CommonModule, MatFormFieldModule, MatInputModule, + MatSelectModule, MatButtonModule, MatIconModule, MatDialogModule, MatProgressBarModule, + MatMenuModule, ReactiveFormsModule, KetcherWrapperModule, JsdrawWrapperModule, diff --git a/src/app/core/structure-search/structure-search.component.html b/src/app/core/structure-search/structure-search.component.html index c9c4a362f..a629d18b2 100644 --- a/src/app/core/structure-search/structure-search.component.html +++ b/src/app/core/structure-search/structure-search.component.html @@ -1,49 +1,60 @@
- -
-
- -
-
-
- - - - Substructure - - - Similarity - - - Exact - - - Flex - - - -
-
Similarity cutoff (tanimoto)
- -
{{similarityCutoff}}
-
-
-
- + +

Structure Search

+
+
+
-
- +
+
+ + + + Substructure + + + Similarity + + + Exact + + + Flex + + + +
+
+ +
+
+ +
+
+
+
Similarity cutoff (tanimoto)
+ +
{{similarityCutoff}}
+
+
+
+
+ +
+
+ +
+
+ +
-
-
-
-
-

- Get Structure From Name -

- -
- -
+
+

+ Get Structure From Name +

+ +
+
+
diff --git a/src/app/core/structure-search/structure-search.component.scss b/src/app/core/structure-search/structure-search.component.scss index 50ba9fb2b..eff113772 100644 --- a/src/app/core/structure-search/structure-search.component.scss +++ b/src/app/core/structure-search/structure-search.component.scss @@ -1,4 +1,5 @@ .search-content-container { + width: 100%; display: flex; flex-direction: column; align-items: center; @@ -15,6 +16,11 @@ white-space: nowrap; } +.clean-button { + margin-top: -15px; + } + + .mat-card { margin-top: 15px; box-sizing: border-box; @@ -49,7 +55,7 @@ align-items: flex-start; padding-left: 15px; margin-left: 10px; - border-left: 1px solid #b3b3b3; + border-left: 1px solid var(--textarea-light-border-color); .mat-slider-horizontal { width: 100%; @@ -85,44 +91,19 @@ .search-actions { flex-direction: row; - border-left: none; - padding-top: 10px; - padding-left: 0; - margin-left: 0; - - .one { - order: 1; - } - - .export-button { - margin-left: 7px; - } - - .two { - order: 2; - margin-left: auto; - display: flex; - padding-right: 10px; - - .similarity-cutoff { - margin: 0; - margin-top: 1px; - } - } + width: 100%; - .three { - order: 3; - } + } + .two { + width: 50%; + display: flex; + flex-direction: column; } .action-button-container { - width: auto; - margin-top: 0; - align-self: flex-start; - - button { - width: auto; - } + width: 50%; + margin: auto; + margin-top: 20px; } } diff --git a/src/app/core/structure-search/structure-search.component.ts b/src/app/core/structure-search/structure-search.component.ts index 3b1303bf0..663d5b83c 100644 --- a/src/app/core/structure-search/structure-search.component.ts +++ b/src/app/core/structure-search/structure-search.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit, AfterViewInit, Renderer2, ViewChild, OnDestroy } from '@angular/core'; import { NavigationExtras, Router, ActivatedRoute } from '@angular/router'; import { InterpretStructureResponse } from '../structure/structure-post-response.model'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; import { StructureImportComponent } from '../structure/structure-import/structure-import.component'; import { Editor } from '../structure-editor/structure.editor.model'; import { LoadingService } from '../loading/loading.service'; @@ -22,6 +22,7 @@ import * as _ from 'lodash'; export class StructureSearchComponent implements OnInit, AfterViewInit, OnDestroy { private editor: Editor; private searchType: string; + _searchtype: string; similarityCutoff?: number; showSimilarityCutoff = false; searchTypeControl = new FormControl(); @@ -41,6 +42,7 @@ export class StructureSearchComponent implements OnInit, AfterViewInit, OnDestro ) { this.searchType = 'substructure'; + this._searchtype ='substructure'; } ngOnInit() { @@ -74,6 +76,8 @@ export class StructureSearchComponent implements OnInit, AfterViewInit, OnDestro } if (params.has('type')) { this.searchType = params.get('type'); + this._searchtype = params.get('type'); + } if (this.searchType === 'similarity') { @@ -95,6 +99,15 @@ export class StructureSearchComponent implements OnInit, AfterViewInit, OnDestro }, () => {}); } + standardize(standard: string): void { + const mol = this.editor.getMolfile(); + this.structureService.interpretStructure(mol, '', standard).subscribe((response: InterpretStructureResponse) => { + if (response && response.structure && response.structure.molfile) { + this.editor.setMolecule(response.structure.molfile); + } + }, () => {}); + } + private navigateToBrowseSubstance(structureSearchTerm: string, smiles?: string): void { @@ -132,6 +145,8 @@ export class StructureSearchComponent implements OnInit, AfterViewInit, OnDestro searchTypeSelected(event): void { this.searchType = event.value; + this._searchtype = event.value; + this.gaService.sendEvent('structureSearch', 'select:search-type', this.searchType); diff --git a/src/app/core/structure/structure-export/structure-export.component.ts b/src/app/core/structure/structure-export/structure-export.component.ts index 46c2c0752..8c15f1d8a 100644 --- a/src/app/core/structure/structure-export/structure-export.component.ts +++ b/src/app/core/structure/structure-export/structure-export.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, Inject } from '@angular/core'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { FormControl } from '@angular/forms'; @Component({ diff --git a/src/app/core/structure/structure-image-modal/structure-image-modal.component.html b/src/app/core/structure/structure-image-modal/structure-image-modal.component.html index 0e2f08ae7..6feabfc58 100644 --- a/src/app/core/structure/structure-image-modal/structure-image-modal.component.html +++ b/src/app/core/structure/structure-image-modal/structure-image-modal.component.html @@ -3,8 +3,41 @@ - - +
+ + + {{approvalID}} + +
+
+ +
+
+ + + {{displayName}} + +
+
  • @@ -13,15 +46,33 @@
-

{{smiles}}

+
- -

{{inchi}}

+ + +
+
+ + + + Details + Edit + + + +
+ +
+
--> \ No newline at end of file diff --git a/src/app/core/structure/structure-image-modal/structure-image-modal.component.scss b/src/app/core/structure/structure-image-modal/structure-image-modal.component.scss index f089e6981..5299020bd 100644 --- a/src/app/core/structure/structure-image-modal/structure-image-modal.component.scss +++ b/src/app/core/structure/structure-image-modal/structure-image-modal.component.scss @@ -5,14 +5,15 @@ .mat-dialog-container { padding: 5px; position: relative; + overflow: hidden; } - + .mat-dialog-content { margin: 0; padding: 0; max-height: 100%; } - + .mat-tab-body-content { padding-left: 10px; padding-right: 10px; @@ -25,12 +26,41 @@ img { height: auto; } +.icon-super { + vertical-align: top; +height: 20px; +} + + +.button-row { + margin: 10px 5px; + width:100%; + display: inline-flex; + align-items: flex-end; + flex-direction: row; +} + +.modal-button { + font-size:15px; + margin-right: 10px; +} + +.structure-image-content>div { + max-width: 500px; + margin: auto; +} +/* this is a hack to try to add a scroll bar sometimes */ +mat-dialog-container div[mat-dialog-content] { + overflow: auto; + height: 100%; +} + .mat-mini-fab { position: absolute; right: 17px; top: 8px; - background-color: rgba(242, 242, 242, .85); - color: #404040; + background-color: var(--mat-icon-button-bg-color); + color: var(--label-color); width: 35px; height: 35px; @@ -42,3 +72,24 @@ img { padding: 0; } } + +.approval { + position: relative; + top: 30px; + width: 80%; + margin: 0 auto; + text-align: center; +} + +.displayname { + width: 80%; + position: relative; + margin: 0 auto; + text-align: center; +} + +.viewrecord { + color: var(--link-primary-color); + font-weight: bold; + font-size: 20px; +} diff --git a/src/app/core/structure/structure-image-modal/structure-image-modal.component.spec.ts b/src/app/core/structure/structure-image-modal/structure-image-modal.component.spec.ts index b09817948..31cf232f6 100644 --- a/src/app/core/structure/structure-image-modal/structure-image-modal.component.spec.ts +++ b/src/app/core/structure/structure-image-modal/structure-image-modal.component.spec.ts @@ -1,9 +1,9 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { StructureImageModalComponent } from './structure-image-modal.component'; -import { MatTabsModule } from '@angular/material'; +import { MatTabsModule } from '@angular/material/tabs'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ConfigService } from '../../config/config.service'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MatDialogRefStub } from '../../../testing/mat-dialog-ref-stub'; import { UtilsService } from '../../utils/utils.service'; import { UtilsServiceStub } from '../../../testing/utils-service-stub'; diff --git a/src/app/core/structure/structure-image-modal/structure-image-modal.component.ts b/src/app/core/structure/structure-image-modal/structure-image-modal.component.ts index 0a7f295a2..244bfce08 100644 --- a/src/app/core/structure/structure-image-modal/structure-image-modal.component.ts +++ b/src/app/core/structure/structure-image-modal/structure-image-modal.component.ts @@ -1,8 +1,10 @@ import { Component, Inject, OnInit } from '@angular/core'; import { UtilsService } from '../../utils/utils.service'; -import {MAT_DIALOG_DATA, MatDialogRef, } from '@angular/material'; +import {MAT_DIALOG_DATA, MatDialogRef, } from '@angular/material/dialog'; import { SafeUrl } from '@angular/platform-browser'; import { StructureService } from '../structure.service'; +import { Router } from '@angular/router'; +import { ConfigService } from '@gsrs-core/config'; @Component({ selector: 'app-structure-image-modal', @@ -13,21 +15,37 @@ export class StructureImageModalComponent implements OnInit { structure: string; smiles: string; inchi: string; + inchiKey: string; + approvalID: string; + uuid: string; + displayName: string; names: string[] = []; + showSelector =false; + showSubstanceSelector = false; + gsrsHomeBaseUrl = ''; + constructor( + private configService: ConfigService, private utilsService: UtilsService, private structureService: StructureService, public dialogRef: MatDialogRef, + private router: Router, @Inject(MAT_DIALOG_DATA) public data: any ) { } ngOnInit() { + // Load Frontend Home URL from config + this.getHomepageUrl(); + this.structure = (this.data && this.data.structure) ? this.data.structure : null; if (this.data.smiles) { this.smiles = this.data.smiles; - this.structureService.getInchi(this.data.uuid).subscribe(inchi => { + this.structureService.getOtherInchi(this.data.uuid).subscribe(inchi => { this.inchi = inchi; }); + this.structureService.getInchi(this.data.uuid).subscribe(inchiKey=> { + this.inchiKey = inchiKey; + }); } if (this.data && this.data.names && this.data.names.length) { for (const name of this.data.names) { @@ -36,10 +54,77 @@ export class StructureImageModalComponent implements OnInit { } } } + if (this.data && this.data.approvalID) { + this.approvalID = this.data.approvalID; + } + if (this.data && this.data.uuid) { + this.uuid = this.data.uuid; + } + if (this.data && this.data.displayName) { + this.displayName = this.data.displayName; + } + + if (this.data.component && this.data.component === 'selector') { + this.showSelector = true; + } + + if (this.data.component && this.data.component === 'substanceSelector') { + this.showSubstanceSelector = true; + } } dismissDialog(): void { this.dialogRef.close(); } + + + + + + setMolfile(): void { + + this.dialogRef.close('molfile'); + } + + selectSubstance(): void { + this.dialogRef.close('select'); + + } + + gotoDetails(): void { + let url = ''; + // this.dialogRef.close(); + if (this.configService.configData && this.configService.configData.gsrsHomeBaseUrl) { + url = this.configService.configData.gsrsHomeBaseUrl + '/substances/' + this.uuid; + + } else { + url = this.router.serializeUrl( + this.router.createUrlTree(['/substances/' + this.uuid]) + ); + } + window.open(url, '_blank'); + } + + gotoEdit(): void { + let url = ''; + // this.dialogRef.close(); + if (this.configService.configData && this.configService.configData.gsrsHomeBaseUrl) { + url = this.configService.configData.gsrsHomeBaseUrl + '/substances/' + this.uuid + '/edit'; + + } else { + url = this.router.serializeUrl( + this.router.createUrlTree(['/substances/' + this.uuid + '/edit']) + ); + } + window.open(url, '_blank'); + + } + + + + getHomepageUrl() { + // Get GSRS Frontend URL fron config + this.gsrsHomeBaseUrl = this.configService.configData && this.configService.configData.gsrsHomeBaseUrl || ''; + } } diff --git a/src/app/core/structure/structure-import/structure-import.component.scss b/src/app/core/structure/structure-import/structure-import.component.scss index ffd1d96eb..30729451d 100644 --- a/src/app/core/structure/structure-import/structure-import.component.scss +++ b/src/app/core/structure/structure-import/structure-import.component.scss @@ -10,7 +10,7 @@ textarea { box-sizing: border-box; width: 100%; height: 50vh; - border: solid 1px #b3b3b3; + border: solid 1px var(--textarea-light-border-color); border-radius: 2px; padding: 5px; margin: 0; @@ -32,17 +32,17 @@ textarea { border-bottom-right-radius: 2px; &.default { - background-color: rgba(68, 138, 255, .4); - color: #595959; + background-color: var(--structure-import-default-bg-color); + color: var(--structure-import-color); } &.success { - background-color: rgba(167, 255, 235, .4); - color: #595959; + background-color: var(--structure-import-success-bg-color); + color: var(--structure-import-color); } &.error { - background-color: rgba(229, 115, 115, .4); - color: #595959; + background-color: var(--structure-import-error-bg-color); + color: var(--structure-import-color); } -} \ No newline at end of file +} diff --git a/src/app/core/structure/structure-import/structure-import.component.ts b/src/app/core/structure/structure-import/structure-import.component.ts index 78e61f54f..b6a418270 100644 --- a/src/app/core/structure/structure-import/structure-import.component.ts +++ b/src/app/core/structure/structure-import/structure-import.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, Inject } from '@angular/core'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { FormControl } from '@angular/forms'; import { GoogleAnalyticsService } from '../../google-analytics/google-analytics.service'; import { StructureService } from '../structure.service'; diff --git a/src/app/core/structure/structure.module.ts b/src/app/core/structure/structure.module.ts index cd659000c..fa5ac36b4 100644 --- a/src/app/core/structure/structure.module.ts +++ b/src/app/core/structure/structure.module.ts @@ -3,11 +3,15 @@ import { CommonModule } from '@angular/common'; import { StructureExportComponent } from './structure-export/structure-export.component'; import { StructureImageModalComponent } from './structure-image-modal/structure-image-modal.component'; import { StructureImportComponent } from './structure-import/structure-import.component'; -import { MatIconModule, MatTabsModule, MatProgressBarModule, MatButtonModule } from '@angular/material'; +import {MatProgressBarModule} from '@angular/material/progress-bar'; +import {MatIconModule} from '@angular/material/icon'; +import {MatTabsModule} from '@angular/material/tabs'; +import {MatButtonModule} from '@angular/material/button'; import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { Router, NavigationExtras, RouterModule } from '@angular/router'; import { FileSelectModule } from 'file-select'; - +import { MatTooltipModule } from '@angular/material/tooltip'; @NgModule({ declarations: [ @@ -19,11 +23,13 @@ import { FileSelectModule } from 'file-select'; CommonModule, MatIconModule, MatTabsModule, + MatTooltipModule, SubstanceImageModule, FormsModule, ReactiveFormsModule, MatProgressBarModule, MatButtonModule, + RouterModule, FileSelectModule ], exports: [ diff --git a/src/app/core/structure/structure.service.ts b/src/app/core/structure/structure.service.ts index 8ad9a0752..3d3ae5161 100644 --- a/src/app/core/structure/structure.service.ts +++ b/src/app/core/structure/structure.service.ts @@ -1,13 +1,12 @@ import { Injectable } from '@angular/core'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; import { ConfigService } from '../config/config.service'; -import { Observable } from 'rxjs'; +import { Observable, timeout } from 'rxjs'; import { HttpClient, HttpParams } from '@angular/common/http'; import { SubstanceDetail, SubstanceStructure, SubstanceMoiety } from '../substance/substance.model'; import { ResolverResponse } from './structure-post-response.model'; import { InterpretStructureResponse } from './structure-post-response.model'; import { ControlledVocabularyService } from '@gsrs-core/controlled-vocabulary'; - @Injectable({ providedIn: 'root' }) @@ -34,9 +33,9 @@ export class StructureService { const url = `${this.configService.configData.apiBaseUrl}img/${id}.mol`; return this.http.get(url, {responseType: 'blob' as 'json'}); } - + //TODO: this is the inchikey, should be renamed getInchi(id: string): Observable { - const url = `${this.configService.configData.apiBaseUrl}api/v1/substances(${id})/structure!$inchikey()`; + const url = `${this.configService.configData.apiBaseUrl}api/v1/substances(${id})/$structure!$inchikey()`; return this.http.get(url, {responseType: 'text'}); } @@ -45,16 +44,16 @@ export class StructureService { return this.http.get(url, {responseType: 'text'}); } - + //TODO: this is the full inchi, should be renamed getOtherInchi(id: string): Observable { // get the other half of the inchi - const url = `${this.configService.configData.apiBaseUrl}api/v1/substances(${id})/structure!$inchi()`; + const url = `${this.configService.configData.apiBaseUrl}api/v1/substances(${id})/$structure!$inchi()`; return this.http.get(url, {responseType: 'text'}); } resolveName(name: string): Observable { - const url = `${this.configService.configData.apiBaseUrl}resolve/${name}`; - return this.http.get(url); + const url = `${this.configService.configData.apiBaseUrl}resolve?name=${encodeURIComponent(name)}`; + return this.http.get(url).pipe(timeout(15000)); } formatFormula(structure: SubstanceStructure ):string { @@ -91,13 +90,13 @@ export class StructureService { return this.http.get(url, options); } - interpretStructure(mol: string): Observable { - const url = `${this.configService.configData.apiBaseUrl}api/v1/substances/interpretStructure`; + interpretStructure(mol: string, mode?: string, standardize?: string ): Observable { + const url = `${this.configService.configData.apiBaseUrl}api/v1/substances/interpretStructure?mode=${mode ? mode:''}&standardize=${(standardize ? standardize:'')}`; return this.http.post(url, mol); } molvec(file: any): Observable { - const url = `${this.configService.configData.apiBaseUrl}api/v1/foo/ocrStructure`; + const url = `${this.configService.configData.apiBaseUrl}api/v1/substances/ocrStructure`; return this.http.post(url, file); } diff --git a/src/app/core/substance-details/structure-details/structure-details.component.html b/src/app/core/substance-details/structure-details/structure-details.component.html index cb85782f6..6927bd995 100644 --- a/src/app/core/substance-details/structure-details/structure-details.component.html +++ b/src/app/core/substance-details/structure-details/structure-details.component.html @@ -11,7 +11,7 @@
Molecular Weight
-
{{structure.mwt}}
+
{{structure.mwt | number: rounding}}
Optical Activity
@@ -39,14 +39,14 @@
- + + +
@@ -109,6 +111,8 @@

Systematic Names:

- + + +
\ No newline at end of file diff --git a/src/app/core/substance-details/structure-details/structure-details.component.scss b/src/app/core/substance-details/structure-details/structure-details.component.scss index bcc968980..e93b598e6 100644 --- a/src/app/core/substance-details/structure-details/structure-details.component.scss +++ b/src/app/core/substance-details/structure-details/structure-details.component.scss @@ -9,18 +9,20 @@ .structure-img { width: 100%; height: auto; - margin-top:-12%; - margin-bottom: -2%; cursor: zoom-in; } } .structure-img-big { width: 100%; - height: auto; + height: 500px !important; } +.blue-font ::ng-deep sub { + margin-bottom: -10px; +} + .content-fix { min-height: 250px; padding: 15px 20px; @@ -54,17 +56,17 @@ } .black-pill, .gray-pill { - color: white; + color: var(--regular-white-color); padding: 1px 7px; border-radius: 11px; } .black-pill { - background-color: black; + background-color: var(--regular-black-color); } .gray-pill { - background-color: gray; + background-color: var(--regular-grey-color); } .name { diff --git a/src/app/core/substance-details/structure-details/structure-details.component.ts b/src/app/core/substance-details/structure-details/structure-details.component.ts index f5267facb..1b3f12d77 100644 --- a/src/app/core/substance-details/structure-details/structure-details.component.ts +++ b/src/app/core/substance-details/structure-details/structure-details.component.ts @@ -8,8 +8,9 @@ import { UtilsService } from '../../utils/utils.service'; import { GoogleAnalyticsService } from '../../google-analytics/google-analytics.service'; import {Subject} from 'rxjs'; import { take } from 'rxjs/operators'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; import { OverlayContainer } from '@angular/cdk/overlay'; +import { ConfigService } from '@gsrs-core/config'; @Component({ selector: 'app-structure-details', @@ -32,6 +33,7 @@ export class StructureDetailsComponent extends SubstanceCardBase implements OnIn showNames = false; searchHref: string; private overlayContainer: HTMLElement; + rounding = '1.0-2'; constructor( private utilService: UtilsService, @@ -39,7 +41,8 @@ export class StructureDetailsComponent extends SubstanceCardBase implements OnIn public gaService: GoogleAnalyticsService, private sanitizer: DomSanitizer, private dialog: MatDialog, - private overlayContainerService: OverlayContainer + private overlayContainerService: OverlayContainer, + private configService: ConfigService ) { super(); } @@ -47,8 +50,12 @@ export class StructureDetailsComponent extends SubstanceCardBase implements OnIn ngOnInit() { if (this.substance != null) { + this.getSysNames(); this.structure = this.substance.structure; + if(this.substance.$$source && this.substance.$$source === 'staging') { + this.structure.id = this.substance.uuid; + } if (this.structure.smiles) { this.structureService.getInchi(this.substance.uuid).pipe(take(1)).subscribe(inchi => { this.inchi = inchi.replace(/\"/g, ''); @@ -65,6 +72,9 @@ export class StructureDetailsComponent extends SubstanceCardBase implements OnIn this.searchHref = 'structure-search?structure=' + this.structure.id; } this.overlayContainer = this.overlayContainerService.getContainerElement(); + if (this.configService.configData && this.configService.configData.molWeightRounding) { + this.rounding = '1.0-' + this.configService.configData.molWeightRounding; + } } @@ -84,8 +94,8 @@ export class StructureDetailsComponent extends SubstanceCardBase implements OnIn openModal(templateRef) { const dialogRef = this.dialog.open(templateRef, { - height: '80%', - width: '80%' + width: '650px', + panelClass: 'structure-image-panel', }); this.overlayContainer.style.zIndex = '1002'; diff --git a/src/app/core/substance-details/structure-details/structure-details.module.ts b/src/app/core/substance-details/structure-details/structure-details.module.ts index 9a1e8a1b2..2bd536493 100644 --- a/src/app/core/substance-details/structure-details/structure-details.module.ts +++ b/src/app/core/substance-details/structure-details/structure-details.module.ts @@ -2,7 +2,8 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { StructureDetailsComponent } from './structure-details.component'; import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; -import {MatIconModule, MatListModule} from '@angular/material'; +import { MatListModule } from '@angular/material/list'; +import { MatIconModule } from '@angular/material/icon'; import {ReferencesManagerModule} from '../../references-manager/references-manager.module'; import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; diff --git a/src/app/core/substance-details/substance-card-base.ts b/src/app/core/substance-details/substance-card-base.ts index 1e6b37daa..53d80510d 100644 --- a/src/app/core/substance-details/substance-card-base.ts +++ b/src/app/core/substance-details/substance-card-base.ts @@ -1,6 +1,7 @@ import { SubstanceDetail } from '../substance/substance.model'; -import { Output, EventEmitter } from '@angular/core'; +import { Output, EventEmitter, Injectable } from '@angular/core'; +@Injectable() export abstract class SubstanceCardBase { substance: SubstanceDetail; title: string; diff --git a/src/app/core/substance-details/substance-cards-filter.model.ts b/src/app/core/substance-details/substance-cards-filter.model.ts index 56d54a9b9..3e949e5cd 100644 --- a/src/app/core/substance-details/substance-cards-filter.model.ts +++ b/src/app/core/substance-details/substance-cards-filter.model.ts @@ -2,7 +2,6 @@ import { InjectionToken } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { SubstanceDetail } from '../substance/substance.model'; import { SubstanceCardFilterParameters } from '../config/config.model'; -import { SubstanceCardFilter } from './substance-cards-filter.model'; import { Observable } from 'rxjs'; import {AuthService} from '@gsrs-core/auth/auth.service'; diff --git a/src/app/core/substance-details/substance-cards-filters.constant.ts b/src/app/core/substance-details/substance-cards-filters.constant.ts index 3d378bc34..fa8b771a3 100644 --- a/src/app/core/substance-details/substance-cards-filters.constant.ts +++ b/src/app/core/substance-details/substance-cards-filters.constant.ts @@ -23,6 +23,10 @@ export const substanceCardsFilters: Array = [ name: 'anyExists', filter: anyExistsFilter }, + { + name: 'countFilter', + filter: countFilter + }, { name: 'substanceCodes', filter: substanceCodesFilter @@ -34,6 +38,10 @@ export const substanceCardsFilters: Array = [ { name: 'hasCredentials', filter: credentialsFilter + }, + { + name: 'isInGroups', + filter: groupFilter } ]; @@ -70,7 +78,7 @@ export function equalsInArrayFilter( let isApproved = false; if (filter.value != null && filter.propertyToCheck != null && filter.propertyInArray != null) { for (let i = 0; i < substance[filter.propertyToCheck].length; i++) { - if ((substance[filter.propertyToCheck][i][filter.propertyInArray]) === filter.value) { + if ((substance[filter.propertyToCheck][i][filter.propertyInArray]) == filter.value) { isApproved = true; break; } @@ -121,6 +129,29 @@ export function anyExistsFilter( }); } +export function countFilter( + substance: SubstanceDetail, + filter: SubstanceCardFilterParameters +): Observable { + return new Observable(observer => { + let isApproved = false; + let countMin = (filter.countMinimum!=null)?filter.countMinimum:1; + let countMax = (filter.countMaximum!=null)?filter.countMaximum:9999999; + + if (filter.propertyToCheck != null) { + const evaluatedProperty = getEvaluatedProperty(substance, filter.propertyToCheck); + if (evaluatedProperty != null + && (Object.prototype.toString.call(evaluatedProperty) === '[object Array]' + && (evaluatedProperty.length) + && (evaluatedProperty.length >= countMin && evaluatedProperty.length <= countMax))) { + isApproved = true; + } + } + observer.next(isApproved); + observer.complete(); + }); +} + export function substanceCodesFilter( substance: SubstanceDetail, filter: SubstanceCardFilterParameters @@ -131,7 +162,7 @@ export function substanceCodesFilter( if (substance.codes && substance.codes.length > 0) { for (let i = 0; i < substance.codes.length; i++) { - if (substance.codes[i].comments && substance.codes[i].comments.indexOf('|') > -1 && filter.value === 'classification') { + if (substance.codes[i]._isClassification && filter.value === 'classification') { isApproved = true; break; } else if (filter.value === 'identifiers') { @@ -201,3 +232,22 @@ export function substanceRelationshipsFilter( }); } +export function groupFilter( + substance: SubstanceDetail, + filter: SubstanceCardFilterParameters, + http: HttpClient, + auth: AuthService + ): Observable { + return new Observable(observer => { + + let isApproved = false; + if (filter.propertyToCheck != null) { + if (auth.isInGroups(filter.propertyToCheck) === true) { + isApproved = true; + } + } + observer.next(isApproved); + observer.complete(); + }); +} + diff --git a/src/app/core/substance-details/substance-cards.module.ts b/src/app/core/substance-details/substance-cards.module.ts index 297cdd870..855c4c440 100644 --- a/src/app/core/substance-details/substance-cards.module.ts +++ b/src/app/core/substance-details/substance-cards.module.ts @@ -7,7 +7,7 @@ import { SubstanceCardsService } from './substance-cards.service'; @NgModule() export class SubstanceCardsModule { - static forRoot(filters: Array): ModuleWithProviders { + static forRoot(filters: Array): ModuleWithProviders { return { ngModule: SubstanceCardsModule, providers: [ diff --git a/src/app/core/substance-details/substance-cards.service.ts b/src/app/core/substance-details/substance-cards.service.ts index ab850a7ac..5fb37d808 100644 --- a/src/app/core/substance-details/substance-cards.service.ts +++ b/src/app/core/substance-details/substance-cards.service.ts @@ -19,7 +19,7 @@ export class SubstanceCardsService { public auth: AuthService ) { } - getSubstanceDetailsPropertiesAsync(substance: SubstanceDetail): Observable { + getSubstanceDetailsPropertiesAsync(substance: SubstanceDetail, source?: string): Observable { return new Observable(observer => { const registeredFilters = this.filters.reduce((acc, val) => acc.concat(val), []); const configCards = this.configService.configData.substanceDetailsCards; @@ -33,6 +33,10 @@ export class SubstanceCardsService { card.type, order ); + // hard filter cards incompatible with staging area records (revisit if staging model changes significantly) + if (source && source === 'staging' && (card.card === 'substance-history' || card.card === 'substance-audit-info' || card.card === 'substance-hierarchy' || card.card === 'fda-substance-product')) { + observer.next(null); + } else { if (card.filters && card.filters.length) { const filterResolver = new FilterResolver(substance, card.filters, registeredFilters, this.http, this.auth); filterResolver.resolve().subscribe(response => { @@ -45,6 +49,7 @@ export class SubstanceCardsService { } else { observer.next(substanceDetailsProperty); } + } }); } diff --git a/src/app/core/substance-details/substance-codes/codeSearchPipe.ts b/src/app/core/substance-details/substance-codes/codeSearchPipe.ts new file mode 100644 index 000000000..2cdac5119 --- /dev/null +++ b/src/app/core/substance-details/substance-codes/codeSearchPipe.ts @@ -0,0 +1,13 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'forwardSlash' +}) +export class forwardSlash implements PipeTransform { + + transform(value: any): any { + value = value.replace("/", " "); + return value; + } + +} diff --git a/src/app/core/substance-details/substance-codes/substance-codes.component.html b/src/app/core/substance-details/substance-codes/substance-codes.component.html index 62db1dfa3..66f698ac7 100644 --- a/src/app/core/substance-details/substance-codes/substance-codes.component.html +++ b/src/app/core/substance-details/substance-codes/substance-codes.component.html @@ -5,7 +5,10 @@
- + + {{showHideFilterText}} + +
@@ -14,7 +17,7 @@ + [queryParams]='{ search: "root_codes_comments:\"" + (node | forwardSlash) + "\""}'> {{node}} @@ -23,7 +26,11 @@ - + @@ -39,10 +46,54 @@ + + + + + + + + + + + + + + + - + + + + + + + + + +
Classification Tree Code System {{code.codeSystem | codeSystemDisplay | async}} {{code.codeSystem | codeSystemDisplay | async}} + + + + {{code.type}} +
+ + + +
+
{{code.codeSystem | codeSystemDisplay | async}} +
+ + + +
+
+ {{code.code}} + {{code.code}} + +
+ + + + {{typeFilter.value ? typeFilter.value : ''}} + + Select Type Filter + {{type.display}} + + +
+
{{code.type}} Comments Comments - +

Code Comments

@@ -57,7 +108,7 @@

Code Comments

- +
@@ -69,7 +120,9 @@

Code Comments

References - +

References

@@ -78,16 +131,30 @@

References

- +
+ +
+ +
+
- diff --git a/src/app/core/substance-details/substance-codes/substance-codes.component.scss b/src/app/core/substance-details/substance-codes/substance-codes.component.scss index 03b747c9f..7ecb79224 100644 --- a/src/app/core/substance-details/substance-codes/substance-codes.component.scss +++ b/src/app/core/substance-details/substance-codes/substance-codes.component.scss @@ -7,9 +7,22 @@ .a-link { text-decoration:none; - color: #1565C0; + color: var(--link-color); } +.disabled-btn { + color: var(--box-shadow-color-5); + background-color: var(--box-shadow-color-3); + cursor: pointer; + &:hover { + cursor: pointer !important; + } +} + +.disabled-btn:hover { + cursor: pointer !important; + +} .form-row { width:100%; @@ -24,4 +37,9 @@ width:90%; } padding-bottom: 25px; -} \ No newline at end of file +} + +.show-filter-span { + display: block; + float: right; +} diff --git a/src/app/core/substance-details/substance-codes/substance-codes.component.ts b/src/app/core/substance-details/substance-codes/substance-codes.component.ts index 1b06078db..d1e374d33 100644 --- a/src/app/core/substance-details/substance-codes/substance-codes.component.ts +++ b/src/app/core/substance-details/substance-codes/substance-codes.component.ts @@ -1,12 +1,13 @@ import {AfterViewInit, Component, OnInit} from '@angular/core'; import { SubstanceCardBaseFilteredList } from '../substance-card-base-filtered-list'; -import {SubstanceCode, SubstanceDetail} from '../../substance/substance.model'; -import {MatDialog} from '@angular/material'; +import {SubstanceCode, SubstanceDetail, SubstanceName, TableFilterDDModel} from '../../substance/substance.model'; +import {MatDialog} from '@angular/material/dialog'; import { GoogleAnalyticsService } from '../../google-analytics/google-analytics.service'; import {Subject} from 'rxjs'; -import {Sort} from '@angular/material'; +import {Sort} from '@angular/material/sort'; import { OverlayContainer } from '@angular/cdk/overlay'; import {UtilsService} from '@gsrs-core/utils'; +import { FormControl } from '@angular/forms'; @Component({ selector: 'app-substance-codes', @@ -18,6 +19,13 @@ export class SubstanceCodesComponent extends SubstanceCardBaseFilteredList = []; displayedColumns: string[]; substanceUpdated = new Subject(); + hideFilters = true; + showHideFilterText = 'Show Filter'; + displayedFilterColumns: string[]; + codeSystemFilter = new FormControl(); + typeFilter = new FormControl(); + codeFilter = new FormControl(); + typeFilterOptions: Array = []; private overlayContainer: HTMLElement; constructor( @@ -34,30 +42,100 @@ export class SubstanceCodesComponent extends SubstanceCardBaseFilteredList { + if (code.url) { + code.url = code.url.trim(); + } + }); this.filtered = this.codes; this.pageChange(); this.searchControl.valueChanges.subscribe(value => { this.filterList(value, this.codes); }, error => { - console.log(error); + // console.log(error); }); + this.getTypeFilterOptions(); } else { this.filtered = []; } } }); + + // if (this.type === 'identifiers') { + this.pageSize = 10; + // } + this.overlayContainer = this.overlayContainerService.getContainerElement(); + this.codeSystemFilter.valueChanges.subscribe((codeSystemFilterValue) => { + this.filterTable(); + }); + this.codeFilter.valueChanges.subscribe((codeFilterValue) => { + this.filterTable(); + }); + this.typeFilter.valueChanges.subscribe((typeFilterValue) => { + this.filterTable(); + }); + } + + filterTable(type?:string) { + const csFilter = this.codeSystemFilter.value === null ? '' : this.codeSystemFilter.value; + const cFilter = this.codeFilter.value === null ? '' : this.codeFilter.value; + const tFilter = this.typeFilter.value === null ? '' : this.typeFilter.value; + this.filtered = []; + for(let n of this.codes) { + if((n.codeSystem.toLowerCase().includes(csFilter.toLowerCase())) && + (n.code.toLowerCase().includes(cFilter.toLowerCase())) && + (n.type.toLowerCase().includes(tFilter.toLowerCase()))) { + this.filtered.push(n); + } + } + + this.pageChange(); + } + + toggleFilter() { + this.hideFilters = !this.hideFilters; + if(this.hideFilters) { + this.showHideFilterText = 'Show Filter'; + } else { + this.showHideFilterText = 'Hide Filter'; + } + } + + getTypeFilterValue(value) { + for(let l of this.typeFilterOptions) { + if(l.display === value) { + return l; + } + } } + + getTypeFilterOptions() { + for(let n of this.codes) { + let oneType = n.type; + let value: TableFilterDDModel = { + value: oneType, + display: oneType + } + if (this.typeFilterOptions.filter(e => e.value === oneType).length > 0) { + } else { + this.typeFilterOptions.push(value); + } + } + } + sortData(sort: Sort) { const data = this.codes.slice(); if (!sort.active || sort.direction === '') { @@ -67,9 +145,11 @@ export class SubstanceCodesComponent extends SubstanceCardBaseFilteredList { const isAsc = sort.direction === 'asc'; - return this.utilsService.compare(a[sort.active], b[sort.active], isAsc); + return this.utilsService.compare(a[sort.active] ? a[sort.active].toString().toUpperCase() : null, b[sort.active] ? b[sort.active].toString().toUpperCase() : null, isAsc); }); this.pageChange(); + + } ngAfterViewInit(): void { @@ -78,9 +158,9 @@ export class SubstanceCodesComponent extends SubstanceCardBaseFilteredList 0) { this.substance.codes.forEach(code => { - if (code.comments && code.comments.indexOf('|') > -1 && this.type === 'classification') { + if (code._isClassification && this.type === 'Codes - Classifications') { this.codes.push(code); - } else if (this.type === 'identifiers') { + } else if (!code._isClassification && this.type === 'Codes - Identifiers') { this.codes.push(code); } }); @@ -88,10 +168,28 @@ export class SubstanceCodesComponent extends SubstanceCardBaseFilteredList { return comments.split('|'); } + resetFilters() { + this.pageChange(); + this.searchControl.setValue(''); + this.codeFilter.setValue(''); + this.codeSystemFilter.setValue(''); + this.typeFilter.setValue(''); + } + openModal(templateRef) { this.gaService.sendEvent(this.analyticsEventCategory, 'button', 'references view'); @@ -106,5 +204,9 @@ export class SubstanceCodesComponent extends SubstanceCardBaseFilteredList - This is a Definitional Concept of -
-
+ This record is a subconcept of +
+
diff --git a/src/app/core/substance-details/substance-concept-definition/substance-concept-definition.component.scss b/src/app/core/substance-details/substance-concept-definition/substance-concept-definition.component.scss index 627a1f9d8..ddc5d2d6a 100644 --- a/src/app/core/substance-details/substance-concept-definition/substance-concept-definition.component.scss +++ b/src/app/core/substance-details/substance-concept-definition/substance-concept-definition.component.scss @@ -9,3 +9,34 @@ padding-right:10px; max-width:250px; } + +.thumb-row { + display:flex; + flex-direction: row; + flex-wrap: wrap; + width:100%; +} + + +.thumb-col{ + max-width:400px; + min-width: 25%; + padding-bottom: 25px; + } + + + .image-icon { + width: 150px; + height: auto; + } + + .substance-icon-container{ + text-align:center; + padding-left:10px; + padding-right:10px; + max-width:250px; + } + + .wrap{ + flex-wrap: wrap; + } \ No newline at end of file diff --git a/src/app/core/substance-details/substance-concept-definition/substance-concept-definition.component.ts b/src/app/core/substance-details/substance-concept-definition/substance-concept-definition.component.ts index 14fe2321e..4781257da 100644 --- a/src/app/core/substance-details/substance-concept-definition/substance-concept-definition.component.ts +++ b/src/app/core/substance-details/substance-concept-definition/substance-concept-definition.component.ts @@ -29,6 +29,7 @@ export class SubstanceConceptDefinitionComponent extends SubstanceCardBase imple } private getConceptDefinition(): void { + this.definitions = []; if (this.substance.relationships && this.substance.relationships.length > 0) { this.substance.relationships.forEach(relationship => { if (relationship.type === 'SUBSTANCE->SUB_CONCEPT') { diff --git a/src/app/core/substance-details/substance-constituents/substance-constituents.component.html b/src/app/core/substance-details/substance-constituents/substance-constituents.component.html index b1152cc26..f9b4d827a 100644 --- a/src/app/core/substance-details/substance-constituents/substance-constituents.component.html +++ b/src/app/core/substance-details/substance-constituents/substance-constituents.component.html @@ -44,7 +44,7 @@

References

- +
diff --git a/src/app/core/substance-details/substance-constituents/substance-constituents.component.scss b/src/app/core/substance-details/substance-constituents/substance-constituents.component.scss index 82994a328..302072aab 100644 --- a/src/app/core/substance-details/substance-constituents/substance-constituents.component.scss +++ b/src/app/core/substance-details/substance-constituents/substance-constituents.component.scss @@ -34,12 +34,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .form-row { diff --git a/src/app/core/substance-details/substance-constituents/substance-constituents.component.ts b/src/app/core/substance-details/substance-constituents/substance-constituents.component.ts index 006caf87b..44d2ba768 100644 --- a/src/app/core/substance-details/substance-constituents/substance-constituents.component.ts +++ b/src/app/core/substance-details/substance-constituents/substance-constituents.component.ts @@ -6,7 +6,7 @@ import {Subject} from 'rxjs'; import { SubstanceCardBaseFilteredList } from '@gsrs-core/substance-details'; import {GoogleAnalyticsService} from '@gsrs-core/google-analytics'; import { OverlayContainer } from '@angular/cdk/overlay'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; @Component({ selector: 'app-substance-constituents', @@ -63,4 +63,7 @@ export class SubstanceConstituentsComponent extends SubstanceCardBaseFilteredLis this.overlayContainer.style.zIndex = null; }); } + close() { + this.dialog.closeAll(); + } } diff --git a/src/app/core/substance-details/substance-dependencies-image/substance-dependencies-image.component.html b/src/app/core/substance-details/substance-dependencies-image/substance-dependencies-image.component.html new file mode 100644 index 000000000..8912e7dbc --- /dev/null +++ b/src/app/core/substance-details/substance-dependencies-image/substance-dependencies-image.component.html @@ -0,0 +1,65 @@ +
+
Dependencies
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Related Substance + + Structure +
+
+ + + +
+
+
Relationship Type {{mod.relationshipType}} Interaction Type {{mod.interactionType}} Mediator Substance + + Comments {{mod.comments}}
+ + + \ No newline at end of file diff --git a/src/app/core/substance-details/substance-dependencies-image/substance-dependencies-image.component.scss b/src/app/core/substance-details/substance-dependencies-image/substance-dependencies-image.component.scss new file mode 100644 index 000000000..fe78c560e --- /dev/null +++ b/src/app/core/substance-details/substance-dependencies-image/substance-dependencies-image.component.scss @@ -0,0 +1,44 @@ +.divcenter { + text-align: center; + margin-bottom: 10px; +} + +.image-icon { + width: 50%; + height: 50% !important; +} + +.zoom:hover{ + cursor: zoom-in; +} + +.width95px { + width: 95px; +} + +.width110px { + width: 110px; +} + +::ng-deep { + + .structure-image-panel { + + .mat-dialog-container { + padding: 5px; + position: relative; + overflow: hidden; + } + + .mat-dialog-content { + margin: 0; + padding: 0; + max-height: 100%; + } + + .mat-tab-body-content { + padding-left: 10px; + padding-right: 10px; + } + } +} \ No newline at end of file diff --git a/src/app/core/substance-details/substance-dependencies-image/substance-dependencies-image.component.spec.ts b/src/app/core/substance-details/substance-dependencies-image/substance-dependencies-image.component.spec.ts new file mode 100644 index 000000000..67f0ebd26 --- /dev/null +++ b/src/app/core/substance-details/substance-dependencies-image/substance-dependencies-image.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SubstanceDependenciesImageComponent } from './substance-dependencies-image.component'; + +describe('SubstanceDisplayImageComponent', () => { + let component: SubstanceDependenciesImageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ SubstanceDependenciesImageComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(SubstanceDependenciesImageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-details/substance-dependencies-image/substance-dependencies-image.component.ts b/src/app/core/substance-details/substance-dependencies-image/substance-dependencies-image.component.ts new file mode 100644 index 000000000..b76ecabe4 --- /dev/null +++ b/src/app/core/substance-details/substance-dependencies-image/substance-dependencies-image.component.ts @@ -0,0 +1,97 @@ +import { AfterViewInit, Component, OnInit } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { SubstanceCardBase } from '../substance-card-base'; +import { SubstanceDetail, SubstanceReference } from '../../substance/substance.model'; +import { SubstanceDependenciesImageNode } from './substance-dependencies-image.model'; +import { SubstanceService } from '@gsrs-core/substance/substance.service'; +import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; +import { AuthService } from '@gsrs-core/auth'; +import { SubstanceCardBaseFilteredList } from '@gsrs-core/substance-details'; +import { StructureImageModalComponent } from '@gsrs-core/structure'; + +@Component({ + selector: 'app-substance-dependencies-image', + templateUrl: './substance-dependencies-image.component.html', + styleUrls: ['./substance-dependencies-image.component.scss'] +}) + +export class SubstanceDependenciesImageComponent extends SubstanceCardBaseFilteredList implements OnInit { + references: Array = []; + displayedColumns: string[] = ['relatedSubstance', 'structure', 'relationshipType', 'interactionType', 'mediatorSubtance', 'comments']; + private overlayContainer: HTMLElement; + displayImagetag: string; + dependencies: Array; + uuid: string; + + constructor( + private substanceService: SubstanceService, + private authService: AuthService, + public gaService: GoogleAnalyticsService, + private overlayContainerService: OverlayContainer, + private dialog: MatDialog + ) { super(gaService); } + + ngOnInit() { + this.overlayContainer = this.overlayContainerService.getContainerElement(); + + this.uuid = this.substance.uuid; + + /* + this.substanceService.getDependencies(this.uuid).subscribe(response => { + if (response) { + this.dependencies = response; + this.filtered = response; + } + }, error => { + }); + */ + + this.getSubstanceRelationships(); + } + + getSubstanceRelationships() { + let relationship = this.substance.relationships; + + if (relationship.length > 0) { + this.dependencies = []; + relationship.forEach(element => { + if (element != null) { + if (element.qualification && element.qualification === 'DEPENDENCY') { + let data: any; + data = { + relatedSubstance: element.relatedSubstance, + relationshipType: element.type, + interactionType: element.interactionType, + role: 'Relationship', + mediatorSubstance: element.mediatorSubstance, + comments: element.comments + }; + this.dependencies.push(data); + } + } + }); + } + this.filtered = this.dependencies; + } + + openImageModal(uuid: string) { + const dialogRef = this.dialog.open(StructureImageModalComponent, { + height: '90%', + width: '650px', + panelClass: 'structure-image-panel', + data: { structure: uuid } + }); + + this.overlayContainer.style.zIndex = '1002'; + + const subscription = dialogRef.afterClosed().subscribe(() => { + this.overlayContainer.style.zIndex = null; + subscription.unsubscribe(); + }, () => { + this.overlayContainer.style.zIndex = null; + subscription.unsubscribe(); + }); + } + +} diff --git a/src/app/core/substance-details/substance-dependencies-image/substance-dependencies-image.model.ts b/src/app/core/substance-details/substance-dependencies-image/substance-dependencies-image.model.ts new file mode 100644 index 000000000..a1ed6f28e --- /dev/null +++ b/src/app/core/substance-details/substance-dependencies-image/substance-dependencies-image.model.ts @@ -0,0 +1,10 @@ +import {SubstanceRelated} from '@gsrs-core/substance'; + +export interface SubstanceDependenciesImageNode { + relatedSubstance?: SubstanceRelated; + structure?: string; + relationshipType?: string; + interactionType?: string; + mediatorSubstance?: SubstanceRelated; + comments?: string; +} diff --git a/src/app/core/substance-details/substance-dependencies-image/substance-dependencies-image.module.ts b/src/app/core/substance-details/substance-dependencies-image/substance-dependencies-image.module.ts new file mode 100644 index 000000000..278d369d8 --- /dev/null +++ b/src/app/core/substance-details/substance-dependencies-image/substance-dependencies-image.module.ts @@ -0,0 +1,46 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SubstanceDependenciesImageComponent } from './substance-dependencies-image.component'; +import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; +import { MatTableModule } from '@angular/material/table'; +import { CdkTableModule } from '@angular/cdk/table'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatInputModule } from '@angular/material/input'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { MatTreeModule } from '@angular/material/tree'; +import { RouterModule } from '@angular/router'; +import { MatIconModule } from '@angular/material/icon'; +import {ReferencesManagerModule} from '../../references-manager/references-manager.module'; +import {MatDialogModule} from '@angular/material/dialog'; +import {MatSortModule} from '@angular/material/sort'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; +import { CodeDisplayModule } from '@gsrs-core/utils/code-display.module'; +import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; + +@NgModule({ + imports: [ + CommonModule, + DynamicComponentLoaderModule.forChild(SubstanceDependenciesImageComponent), + MatTableModule, + CdkTableModule, + MatPaginatorModule, + MatInputModule, + MatFormFieldModule, + MatSelectModule, + ReactiveFormsModule, + FormsModule, + MatTreeModule, + RouterModule, + MatIconModule, + ReferencesManagerModule, + MatDialogModule, + MatSortModule, + CodeDisplayModule, + SubstanceImageModule + ], + declarations: [ + SubstanceDependenciesImageComponent + ] +}) +export class SubstanceDependenciesImageModule { } diff --git a/src/app/core/substance-details/substance-details.component.html b/src/app/core/substance-details/substance-details.component.html index 04b1e1f86..488aefcc7 100644 --- a/src/app/core/substance-details/substance-details.component.html +++ b/src/app/core/substance-details/substance-details.component.html @@ -1,7 +1,7 @@ - - + + - {{property.title}} {{property.count}} @@ -11,13 +11,19 @@ -

+

+ +

Staging Area Record

+

+ +
{{substance.approvalID}}
diff --git a/src/app/core/substance-details/substance-details.component.scss b/src/app/core/substance-details/substance-details.component.scss index 2857c2a68..3b521cbb8 100644 --- a/src/app/core/substance-details/substance-details.component.scss +++ b/src/app/core/substance-details/substance-details.component.scss @@ -24,19 +24,30 @@ margin-bottom: 10px; } +.white-background { + background-color: var(--regular-white-color); +} + .substance-title { display: inline-block; } .approvalID { font-size: 20px; - color: #c7254e; + color: var(--pink-span-color); font-weight: 500; } +.approvalIDColor { + color: var(--pink-span-color); + margin-bottom: 0px !important; + margin-top: 0px !important; + +} + .chip { display: inline-block; - background-color: #E0E0E0; + background-color: var(--pale-border-color); border-radius: 50%; padding: 0 5px; margin-left: 15px; diff --git a/src/app/core/substance-details/substance-details.component.ts b/src/app/core/substance-details/substance-details.component.ts index 38ffe1e0e..494136cfe 100644 --- a/src/app/core/substance-details/substance-details.component.ts +++ b/src/app/core/substance-details/substance-details.component.ts @@ -24,6 +24,7 @@ import { GoogleAnalyticsService } from '../google-analytics/google-analytics.ser import { environment } from '../../../environments/environment'; import {Subject, Subscription} from 'rxjs'; import {Title} from '@angular/platform-browser'; +import { AdminService } from '@gsrs-core/admin/admin.service'; @Component({ selector: 'app-substance-details', @@ -37,6 +38,7 @@ export class SubstanceDetailsComponent implements OnInit, AfterViewInit, OnDestr termSubscriber: Subscription; substance: SubstanceDetail; substanceDetailsProperties: Array = []; + source = 'substances'; @ViewChildren('dynamicComponent', { read: ViewContainerRef }) dynamicComponents: QueryList; @ViewChild('matSideNavInstance', { static: true }) matSideNav: MatSidenav; hasBackdrop = false; @@ -53,7 +55,8 @@ export class SubstanceDetailsComponent implements OnInit, AfterViewInit, OnDestr private utilsService: UtilsService, private gaService: GoogleAnalyticsService, private activeRoute: ActivatedRoute, - private titleService: Title + private titleService: Title, + private adminService: AdminService ) { } // use aspirin for initial development a05ec20c-8fe2-4e02-ba7f-df69e5e30248 @@ -61,29 +64,35 @@ export class SubstanceDetailsComponent implements OnInit, AfterViewInit, OnDestr this.gaService.sendPageView(`Substance Details`); this.id = this.activatedRoute.snapshot.params['id']; this.version = this.activatedRoute.snapshot.params['version']; + this.source = this.activatedRoute.snapshot.queryParams['source'] || 'substances'; this.loadingService.setLoading(true); - this.checkVersion().subscribe((result: number) => {this.latestVersion = result; - this.activeRoute.params.subscribe(routeParams => { - this.id = routeParams.id; - this.version = routeParams.version; - if (this.version) { - if (Number(this.latestVersion) > Number(this.version)) { - this.getSubstanceDetails(this.id, this.version.toString()); - } else { - this.getSubstanceDetails(this.id); - } - } else { - this.getSubstanceDetails(this.id); - } - - - }); - - }, error => { - this.gaService.sendException('checkVersionCall: error from API call'); - this.loadingService.setLoading(false); - this.handleSubstanceRetrivalError(); - }); + if(this.source !== 'staging'){ + this.checkVersion().subscribe((result: number) => {this.latestVersion = result; + this.activeRoute.params.subscribe(routeParams => { + this.id = routeParams.id; + this.version = routeParams.version; + if (this.version) { + if (Number(this.latestVersion) > Number(this.version)) { + this.getSubstanceDetails(this.id, this.version.toString()); + } else { + this.getSubstanceDetails(this.id); + } + } else { + this.getSubstanceDetails(this.id); + } + + + }); + + }, error => { + this.gaService.sendException('checkVersionCall: error from API call'); + this.loadingService.setLoading(false); + this.handleSubstanceRetrivalError(); + }); + } else { + this.getSubstanceDetails(this.id); + } + } @@ -92,8 +101,6 @@ export class SubstanceDetailsComponent implements OnInit, AfterViewInit, OnDestr ngAfterViewInit(): void { - - this.dynamicComponents.changes .subscribe(() => { this.dynamicComponents.forEach((cRef, index) => { @@ -103,6 +110,10 @@ export class SubstanceDetailsComponent implements OnInit, AfterViewInit, OnDestr this.dynamicComponentLoader .getComponentFactory(substanceProperty.dynamicComponentId) .subscribe(componentFactory => { + if (this.source === 'staging') { + this.substance.$$source = 'staging'; + this.substance.uuid = this.id; + } const ref = cRef.createComponent(componentFactory); ref.instance.countUpdate.subscribe(count => { substanceProperty.updateCount(count); @@ -145,28 +156,67 @@ export class SubstanceDetailsComponent implements OnInit, AfterViewInit, OnDestr checkVersion() { return this.substanceService.checkVersion(this.id); } - - getSubstanceDetails(id: string, version?: string) { - this.substanceService.getSubstanceDetails(id, version).subscribe(response => { - if (response) { - this.titleService.setTitle(response._name); - this.substance = response; - this.substanceUpdated.next(response); - this.substanceCardsService.getSubstanceDetailsPropertiesAsync(this.substance).subscribe(substanceProperty => { - if (substanceProperty != null) { - this.insertSubstanceProperty(substanceProperty); + if (this.source && this.source === 'staging') { + this.adminService.GetStagedRecord(id).subscribe(response => { + this.processDetailsResponse(id, response); + }, error => { + this.gaService.sendException('getSubstanceDetails: error from API call'); + this.loadingService.setLoading(false); + this.handleSubstanceRetrivalError(); + }); + } else { + this.substanceService.getSubstanceDetails(id, version).subscribe(response => { + this.processDetailsResponse(id, response); + }, error => { + this.gaService.sendException('getSubstanceDetails: error from API call'); + this.loadingService.setLoading(false); + this.handleSubstanceRetrivalError(); + }); + } + } + + processDetailsResponse(id, response) { + if (response) { + let name = response._name; + response.names.forEach(current => { + if (current.displayName && current.stdName) { + name = current.stdName; + } + }); + name = name.replace(/<[^>]*>?/gm, ''); + this.titleService.setTitle(name); + this.substance = response; + this.substanceUpdated.next(response); + this.substanceCardsService.getSubstanceDetailsPropertiesAsync(this.substance, this.source).subscribe(substanceProperty => { + if (substanceProperty != null) { + this.insertSubstanceProperty(substanceProperty); + } + }); + this.substanceService.getMixtureParent(id).subscribe(response2 => { + if (response2 && response2.content && response2.content.length > 0) { + this.substance.$$mixtureParents = response2.content; + this.substanceCardsService.getSubstanceDetailsPropertiesAsync(this.substance, this.source).subscribe(substanceProperty => { + if (substanceProperty != null) { + this.insertSubstanceProperty(substanceProperty); + } + }); } }); - } else { - this.handleSubstanceRetrivalError(); - } - this.loadingService.setLoading(false); - }, error => { - this.gaService.sendException('getSubstanceDetails: error from API call'); - this.loadingService.setLoading(false); + this.substanceService.getConstituentParent(id).subscribe(response3 => { + if (response3 && response3.content && response3.content.length > 0) { + this.substance.$$constituentParents = response3.content; + this.substanceCardsService.getSubstanceDetailsPropertiesAsync(this.substance, this.source).subscribe(substanceProperty => { + if (substanceProperty != null) { + this.insertSubstanceProperty(substanceProperty); + } + }); + } + }); + } else { this.handleSubstanceRetrivalError(); - }); + } + this.loadingService.setLoading(false); } private insertSubstanceProperty(property: SubstanceDetailsProperty, startVal?: number, endVal?: number): void { @@ -203,6 +253,16 @@ export class SubstanceDetailsComponent implements OnInit, AfterViewInit, OnDestr this.insertSubstanceProperty(property, m + 1, end); return; } + + + this.substanceDetailsProperties.forEach(prop => { + if (prop.title === 'identifiers') { + prop.title = 'Codes - Identifiers'; + } + if (prop.title === 'classification') { + prop.title = 'Codes - Classification'; + } + }); } private handleSubstanceRetrivalError() { diff --git a/src/app/core/substance-details/substance-disulfide-links/substance-disulfide-links.component.spec.ts b/src/app/core/substance-details/substance-disulfide-links/substance-disulfide-links.component.spec.ts index 26d9e07d3..8fe10cdac 100644 --- a/src/app/core/substance-details/substance-disulfide-links/substance-disulfide-links.component.spec.ts +++ b/src/app/core/substance-details/substance-disulfide-links/substance-disulfide-links.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { SubstanceDisulfideLinksComponent } from './substance-disulfide-links.component'; -import { MatTableModule } from '@angular/material'; +import { MatTableModule } from '@angular/material/table'; import { CdkTableModule } from '@angular/cdk/table'; describe('SubstanceDisulfideLinksComponent', () => { diff --git a/src/app/core/substance-details/substance-disulfide-links/substance-disulfide-links.module.ts b/src/app/core/substance-details/substance-disulfide-links/substance-disulfide-links.module.ts index 463ea9cb8..452707785 100644 --- a/src/app/core/substance-details/substance-disulfide-links/substance-disulfide-links.module.ts +++ b/src/app/core/substance-details/substance-disulfide-links/substance-disulfide-links.module.ts @@ -3,7 +3,9 @@ import { CommonModule } from '@angular/common'; import { SubstanceDisulfideLinksComponent } from './substance-disulfide-links.component'; import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; import {RouterModule} from '@angular/router'; -import {MatInputModule, MatPaginatorModule, MatTableModule} from '@angular/material'; +import {MatInputModule} from '@angular/material/input'; +import {MatPaginatorModule} from '@angular/material/paginator'; +import {MatTableModule} from '@angular/material/table'; import {CdkTableModule} from '@angular/cdk/table'; @NgModule({ diff --git a/src/app/core/substance-details/substance-glycosylation/substance-glycosylation.component.spec.ts b/src/app/core/substance-details/substance-glycosylation/substance-glycosylation.component.spec.ts index dee99d1e9..d0e588111 100644 --- a/src/app/core/substance-details/substance-glycosylation/substance-glycosylation.component.spec.ts +++ b/src/app/core/substance-details/substance-glycosylation/substance-glycosylation.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { SubstanceGlycosylationComponent } from './substance-glycosylation.component'; -import { MatTableModule } from '@angular/material'; +import { MatTableModule } from '@angular/material/table'; import { CdkTableModule } from '@angular/cdk/table'; describe('SubstanceGlycosylationComponent', () => { diff --git a/src/app/core/substance-details/substance-glycosylation/substance-glycosylation.module.ts b/src/app/core/substance-details/substance-glycosylation/substance-glycosylation.module.ts index f5c0c894a..242480778 100644 --- a/src/app/core/substance-details/substance-glycosylation/substance-glycosylation.module.ts +++ b/src/app/core/substance-details/substance-glycosylation/substance-glycosylation.module.ts @@ -3,7 +3,9 @@ import { CommonModule } from '@angular/common'; import { SubstanceGlycosylationComponent } from './substance-glycosylation.component'; import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; import {RouterModule} from '@angular/router'; -import {MatInputModule, MatPaginatorModule, MatTableModule} from '@angular/material'; +import {MatInputModule} from '@angular/material/input'; +import {MatPaginatorModule} from '@angular/material/paginator'; +import {MatTableModule} from '@angular/material/table'; import {CdkTableModule} from '@angular/cdk/table'; @NgModule({ diff --git a/src/app/core/substance-details/substance-hierarchy/substance-hierarchy.component.html b/src/app/core/substance-details/substance-hierarchy/substance-hierarchy.component.html new file mode 100644 index 000000000..bdcc19c12 --- /dev/null +++ b/src/app/core/substance-details/substance-hierarchy/substance-hierarchy.component.html @@ -0,0 +1,62 @@ +
+ +
+
Substance Hierarchy
+ + +
  • + +
    + +
    +
    +
    + {{node.value.approvalID? node.value.approvalID : 'PENDING'}} + {{node.relationship}} + +
    +
    +
  • + + +
  • +
    + + + +
    +
    + + + + + +
    +
    + {{node.value.approvalID || 'PENDING'}} + {{node.relationship}} +
    +
    +
    +
      + +
    +
  • +
    + +
    + +
    diff --git a/src/app/core/substance-details/substance-hierarchy/substance-hierarchy.component.scss b/src/app/core/substance-details/substance-hierarchy/substance-hierarchy.component.scss new file mode 100644 index 000000000..911b29241 --- /dev/null +++ b/src/app/core/substance-details/substance-hierarchy/substance-hierarchy.component.scss @@ -0,0 +1,127 @@ +.mat-tree-node { + min-height:30px !important; + } + .hierarchy-tree-invisible { + display: none; + } + + .hierarchy-tree ul, + .hierarchy-tree li { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; + } + + .spacer { + width: 50%; + } + .approval { + color: var(--regular-maroon-color); + flex: 0 0 100%; + } + + .end { + white-space: nowrap; + max-width: 250px; + flex: 0 0 100%; + flex-wrap: nowrap; + overflow: hidden; + font-style: italic; + text-overflow: ellipsis; + + } + + .hierarchy-link { + white-space: nowrap; + flex-wrap: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .tree-button { + height:30px !important; + line-height: 30px !important; + width: 35px; + background-color: var(--regular-transparent-color); + border: 0px; + } + .right-container { + max-width: 250px; + text-align: right; + white-space: nowrap; + flex-wrap: wrap; + display: flex; + padding-right: 10px; + margin: 4px; + vertical-align: top; + justify-content: right; + } + + .title { + align-items: center; + display: flex; + max-width: calc(100% - 300px); + } + + .tree-label { + padding-bottom: 10px; + font-weight: bold; + } + + .filler { + flex: 1 1 auto; + vertical-align: center; + height: 2px; + margin-top: auto; + margin-bottom:auto; + margin-left :50px; + padding-left: 20px; + + } + + .filler-line { + height: 2px; + width: 100%; + } + + .no-border { + border-top:0px; + } + .primary { + background-color: var(--sub-hierarchy-bg-color) !important; + } + + .hierarchy-link { + color: var(--link-primary-color); + font-style: unset; + max-lines: 2; + } + .odd { + background-color: var(--sub-hierarchy-odd-bg-color); + } + .node-container { + display:flex; + width:calc(100% - 35px); + justify-content: space-between; + } + + .hierarchy-edit { + height: 20px; + margin-left: 15px; + } + + + .mat-card { + max-width: 1228px; + margin-bottom: 20px; + } + + .mat-card, .controls-container { + width: 100%; + box-sizing: border-box; + } + + .current-record { + font-weight:bold; + } + diff --git a/src/app/core/substance-details/substance-hierarchy/substance-hierarchy.component.spec.ts b/src/app/core/substance-details/substance-hierarchy/substance-hierarchy.component.spec.ts new file mode 100644 index 000000000..ac7d0e6f5 --- /dev/null +++ b/src/app/core/substance-details/substance-hierarchy/substance-hierarchy.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SubstanceHierarchyComponent } from './substance-hierarchy.component'; + +describe('SubstanceHierarchyComponent', () => { + let component: SubstanceHierarchyComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SubstanceHierarchyComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SubstanceHierarchyComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-details/substance-hierarchy/substance-hierarchy.component.ts b/src/app/core/substance-details/substance-hierarchy/substance-hierarchy.component.ts new file mode 100644 index 000000000..32b609579 --- /dev/null +++ b/src/app/core/substance-details/substance-hierarchy/substance-hierarchy.component.ts @@ -0,0 +1,192 @@ +import { Component, OnInit } from '@angular/core'; +import { SubstanceCardBase } from '../substance-card-base'; +import { SubstanceService } from '@gsrs-core/substance/substance.service'; +import { AuthService } from '@gsrs-core/auth'; +import { NestedTreeControl } from '@angular/cdk/tree'; +import { MatTreeNestedDataSource } from '@angular/material/tree'; +import { HierarchyNode } from '@gsrs-core/substances-browse/substance-hierarchy/hierarchy.model'; + +@Component({ + selector: 'app-substance-hierarchy', + templateUrl: './substance-hierarchy.component.html', + styleUrls: ['./substance-hierarchy.component.scss'] +}) +export class SubstanceHierarchyComponent extends SubstanceCardBase implements OnInit { + + constructor( + private substanceService: SubstanceService, + private authService: AuthService + ) { + + super(); + } + + uuid: string; + name: string; + approvalID?: string; + treeControl = new NestedTreeControl(node => node.children); + dataSource = new MatTreeNestedDataSource(); + selfNode: HierarchyNode; + activeNode: any; + isAdmin: boolean; + hasChild = (_: number, node: any) => !!node.children && node.children.length > 0; + + ngOnInit() { + this.uuid = this.substance.uuid; + this.name = this.substance._nameHTML; + this.selfNode = { + 'id': 0, + 'type': 'ROOT', + 'parent': '#', + 'expandable': false, + 'value': { + 'refuuid': this.uuid, + 'name': this.name, + 'approvalID': this.approvalID || '' + }, + 'relationship': '' + }; + this.substanceService.getHierarchy(this.uuid).subscribe(resp => { + this.loadHierarchy(resp); + }, error => { + this.loadHierarchy([this.selfNode]); + }); + this.isAdmin = this.authService.hasAnyRoles('Admin', 'Updater', 'SuperUpdater'); + } + + loadHierarchy(orig: any): void { + + if (orig.length === 0) { + orig.push(this.selfNode); + } + + for (let i = 0; i < orig.length; i++) { + const row: any = orig[i]; + if (row.depth === 0) { + row.parent = '#'; + } + } + if (orig.length > 1) { + orig = this.formatHierarchy(orig); + } + const temp2 = this.list_to_tree(orig); + this.dataSource.data = temp2; + this.activeNode = this.dataSource.data[0]; + } + + formatHierarchy(data: any): HierarchyNode { + let lastID = ''; + let lastProp = ''; + const parentRemap = []; + + for (let i = (data.length - 1); i >= 0; i--) { + if (data[i].depth === 0) { + data[i].parent = '#'; + } + const subref = data[i].value; + data[i].relationship = ''; + if (subref.refuuid === this.uuid) { + data[i].self = true; + } + + if (!subref.approvalID && subref.linkingID && subref.linkingID.length === 10) { + data[i].value.approvalID = data[i].value.linkingID; + } + if (!data[i].value.approvalID) { + const matches = data[i].text.match(/\[(.*?)\]/); + if (matches) { + data[i].value.approvalID = matches[1]; + } + } + // remove children identical to parent with active moiety relationship, format text + if ((subref.refuuid === lastID) && (lastProp.includes('HAS ACTIVE MOIETY'))) { + parentRemap.push([data[i + 1].id, data[i].id]); + data.splice(i + 1, 1); + data[i].relationship += '{ACTIVE MOIETY} '; + } + if (data[i].type.includes('HAS ACTIVE MOIETY')) { + data[i].relationship += '{ACTIVE FORM} '; + } else if (data[i].type.includes('IS SALT/SOLVATE OF')) { + data[i].relationship += '{SALT/SOLVATE} '; + } else if (data[i].type.includes('IS SUBCONCEPT OF')) { + data[i].relationship += '{SUBCONCEPT} '; + } else if (data[i].type.includes('IS G1SS CONSTITUENT OF')) { + data[i].relationship += '{G1SS} '; + } else if ((data[i].type.length > 8 ) && (data[i].relationship = '')) { + data[i].relationship += ' {' + data[i].type + '} '; + } + + + data[i].refuuid = data[i].value.refuuid; + lastID = data[i].refuuid; + lastProp = data[i].type; + } + // further remove self referential relationships with both salt and moiety relationship. + data.sort(function(a, b) { + const textA = a.refuuid.toUpperCase(); + const textB = b.refuuid.toUpperCase(); + if (textA === textB) { + return (a.parent < b.parent) ? -1 : (a.parent > b.parent) ? 1 : 0; + } else { + return (textA < textB) ? -1 : (textA > textB) ? 1 : 0; + } + }); + + // delete duplicates entries with both active moiety and salt/ solvate relationships. + for (let i = (data.length - 1); i >= 0; i--) { + if (i !== data.length - 1) { + if ((data[i].value.refuuid === data[i + 1].value.refuuid)) { + if ((data[i].parent === data[i + 1].parent)) { + if (data[i].type.includes('HAS ACTIVE MOIETY') && data[i + 1].type.includes('IS SALT/SOLVATE OF')) { + parentRemap.push([data[i].id, data[i + 1].id]); + data.splice(i, 1); + } else if (data[i + 1].type.includes('HAS ACTIVE MOIETY') && data[i].type.includes('IS SALT/SOLVATE OF')) { + parentRemap.push([data[i + 1].id, data[i].id]); + data.splice(i + 1, 1); + } + } + } + } + } + data.sort(function(a, b) { + return a.id - b.id; + }); + + for (let i = data.length - 1; i >= 0; i--) { + for (let k = 0; k < parentRemap.length; k++) { + if (data[i].parent === parentRemap[k][0]) { + data[i].parent = parentRemap[k][1]; + } + } + } + return data; + } + + list_to_tree(list) { + const map = {}, roots = []; + let node, i; + for (i = 0; i < list.length; i += 1) { + map[list[i].id] = i; + list[i].children = []; + if (i === 0) { + list[i].order = 'primary'; + + } else if (i % 2 === 0) { + list[i].order = 'even'; + } else { + list[i].order = 'odd'; + } + } + for (i = 0; i < list.length; i += 1) { + node = list[i]; + if (node.parent !== '#') { + list[map[node.parent]].children.push(node); + } else { + roots.push(node); + } + } + return roots; + } +} + + diff --git a/src/app/core/substance-details/substance-hierarchy/substance-hierarchy.module.ts b/src/app/core/substance-details/substance-hierarchy/substance-hierarchy.module.ts new file mode 100644 index 000000000..a1b1f2215 --- /dev/null +++ b/src/app/core/substance-details/substance-hierarchy/substance-hierarchy.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SubstanceHierarchyComponent } from './substance-hierarchy.component'; +import {MatTreeModule} from '@angular/material/tree'; +import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; +import { MatIconModule } from '@angular/material/icon'; +import { RouterModule } from '@angular/router'; + +@NgModule({ + imports: [ + CommonModule, + RouterModule, + DynamicComponentLoaderModule.forChild(SubstanceHierarchyComponent), + MatTreeModule, + MatIconModule + ], + declarations: [SubstanceHierarchyComponent] +}) +export class SubstanceHierarchyModule { } diff --git a/src/app/core/substance-details/substance-history/substance-history.component.ts b/src/app/core/substance-details/substance-history/substance-history.component.ts index b2a8593ac..45850c030 100644 --- a/src/app/core/substance-details/substance-history/substance-history.component.ts +++ b/src/app/core/substance-details/substance-history/substance-history.component.ts @@ -8,7 +8,8 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatInputModule } from '@angular/material/input'; import { ReactiveFormsModule, FormsModule } from '@angular/forms'; import {ReferencesManagerModule} from '../../references-manager/references-manager.module'; -import {MatDialogModule, MatIconModule, MatDialog} from '@angular/material'; +import {MatDialog, MatDialogModule} from '@angular/material/dialog'; +import {MatIconModule} from '@angular/material/icon'; import {SubstanceCardBase} from '@gsrs-core/substance-details/substance-card-base'; import {SubstanceDetail, SubstanceEdit, SubstanceName} from '@gsrs-core/substance/substance.model'; import {SubstanceService} from '@gsrs-core/substance/substance.service'; diff --git a/src/app/core/substance-details/substance-history/substance-history.module.ts b/src/app/core/substance-details/substance-history/substance-history.module.ts index 891217ca5..5e250d167 100644 --- a/src/app/core/substance-details/substance-history/substance-history.module.ts +++ b/src/app/core/substance-details/substance-history/substance-history.module.ts @@ -2,15 +2,13 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import {SubstanceHistoryComponent} from '@gsrs-core/substance-details/substance-history/substance-history.component'; import {DynamicComponentLoaderModule} from '@gsrs-core/dynamic-component-loader/dynamic-component-loader.module'; -import { - MatDialogModule, - MatIconModule, - MatInputModule, - MatPaginatorModule, - MatSortModule, - MatTableModule, - MatTreeModule -} from '@angular/material'; +import {MatInputModule} from '@angular/material/input'; +import {MatPaginatorModule} from '@angular/material/paginator'; +import {MatTableModule} from '@angular/material/table'; +import {MatDialogModule} from '@angular/material/dialog'; +import {MatIconModule} from '@angular/material/icon'; +import {MatSortModule} from '@angular/material/sort'; +import {MatTreeModule} from '@angular/material/tree'; import {CdkTableModule} from '@angular/cdk/table'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {ReferencesManagerModule} from '@gsrs-core/references-manager/references-manager.module'; diff --git a/src/app/core/substance-details/substance-mixture-components/substance-mixture-components.component.html b/src/app/core/substance-details/substance-mixture-components/substance-mixture-components.component.html index 7c289f992..4ef86cdf4 100644 --- a/src/app/core/substance-details/substance-mixture-components/substance-mixture-components.component.html +++ b/src/app/core/substance-details/substance-mixture-components/substance-mixture-components.component.html @@ -4,7 +4,9 @@

    All of the following components
    {{component.substance.linkingID}}
    - + + +
    {{component.substance.name}} @@ -17,8 +19,12 @@

    All of the following components

    Any of these components may be present:

    + + + + +
    + +
    + +
    + +
    + +
    +
    +
    \ No newline at end of file diff --git a/src/app/core/substance-details/substance-mixture-components/substance-mixture-components.component.scss b/src/app/core/substance-details/substance-mixture-components/substance-mixture-components.component.scss index c4e2b949d..2688351b1 100644 --- a/src/app/core/substance-details/substance-mixture-components/substance-mixture-components.component.scss +++ b/src/app/core/substance-details/substance-mixture-components/substance-mixture-components.component.scss @@ -3,3 +3,27 @@ .wrap{ flex-wrap: wrap; } + +.mat-mini-fab { + float: right; + background-color: var(--mat-icon-button-bg-color); + color: var(--label-color); + width: 35px; + height: 35px; + + .icon { + margin-top: 5px; + } + + &:not(:first-child) { + margin-top: 3px; + } + + ::ng-deep .mat-button-wrapper { + padding: 0; + } +} + +.close-container { + width:100%; +} \ No newline at end of file diff --git a/src/app/core/substance-details/substance-mixture-components/substance-mixture-components.component.ts b/src/app/core/substance-details/substance-mixture-components/substance-mixture-components.component.ts index ed438942d..e6ed10f9f 100644 --- a/src/app/core/substance-details/substance-mixture-components/substance-mixture-components.component.ts +++ b/src/app/core/substance-details/substance-mixture-components/substance-mixture-components.component.ts @@ -2,6 +2,8 @@ import { Component, OnInit } from '@angular/core'; import {SubstanceCardBase} from '../substance-card-base'; import {MixtureComponents, SubstanceDetail} from '../../substance/substance.model'; import {Subject} from 'rxjs'; +import { StructureImageModalComponent } from '@gsrs-core/structure'; +import { MatDialog } from '@angular/material/dialog'; @Component({ selector: 'app-substance-mixture-components', @@ -14,8 +16,11 @@ export class SubstanceMixtureComponentsComponent extends SubstanceCardBase imple presentInAny: Array; presentInOne: Array; substanceUpdated = new Subject(); + active: string; - constructor() { + constructor( + private dialog: MatDialog + ) { super(); } @@ -35,4 +40,19 @@ export class SubstanceMixtureComponentsComponent extends SubstanceCardBase imple }); } + openImageModal(templateRef, related: any) { + let data = {uuid: related.substance.refuuid}; + this.active = related.substance.refuuid; + + const dialogRef = this.dialog.open(templateRef, { + width: '750px', + height: '700px', + panelClass: 'structure-image-panel' + }); +} + +close() { + this.dialog.closeAll(); +} + } diff --git a/src/app/core/substance-details/substance-mixture-components/substance-mixture-components.module.ts b/src/app/core/substance-details/substance-mixture-components/substance-mixture-components.module.ts index 7de6865ef..57076463f 100644 --- a/src/app/core/substance-details/substance-mixture-components/substance-mixture-components.module.ts +++ b/src/app/core/substance-details/substance-mixture-components/substance-mixture-components.module.ts @@ -4,11 +4,15 @@ import { RouterModule } from '@angular/router'; import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; import { SubstanceMixtureComponentsComponent } from './substance-mixture-components.component'; import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatIconModule } from '@angular/material/icon'; @NgModule({ imports: [ CommonModule, RouterModule, + MatDialogModule, + MatIconModule, DynamicComponentLoaderModule.forChild(SubstanceMixtureComponentsComponent), SubstanceImageModule ], diff --git a/src/app/core/substance-details/substance-mixture-parent/substance-mixture-parent.component.html b/src/app/core/substance-details/substance-mixture-parent/substance-mixture-parent.component.html new file mode 100644 index 000000000..1228cb86d --- /dev/null +++ b/src/app/core/substance-details/substance-mixture-parent/substance-mixture-parent.component.html @@ -0,0 +1,13 @@ +

    This substance is a component of the following mixture(s):

    +
    diff --git a/src/app/core/substance-details/substance-mixture-parent/substance-mixture-parent.component.scss b/src/app/core/substance-details/substance-mixture-parent/substance-mixture-parent.component.scss new file mode 100644 index 000000000..7d8dca698 --- /dev/null +++ b/src/app/core/substance-details/substance-mixture-parent/substance-mixture-parent.component.scss @@ -0,0 +1,22 @@ +.thumb-col{ + max-width:400px; + min-width: 25%; + } + + + .image-icon { + width: 150px; + height: auto; + } + + .substance-icon-container{ + text-align:center; + padding-left:10px; + padding-right:10px; + max-width:250px; + } + + .wrap{ + flex-wrap: wrap; + } + \ No newline at end of file diff --git a/src/app/core/substance-details/substance-mixture-parent/substance-mixture-parent.component.spec.ts b/src/app/core/substance-details/substance-mixture-parent/substance-mixture-parent.component.spec.ts new file mode 100644 index 000000000..2b5e9c433 --- /dev/null +++ b/src/app/core/substance-details/substance-mixture-parent/substance-mixture-parent.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SubstanceMixtureParentComponent } from './substance-mixture-parent.component'; + +describe('SubstanceMixtureParentComponent', () => { + let component: SubstanceMixtureParentComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SubstanceMixtureParentComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SubstanceMixtureParentComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-details/substance-mixture-parent/substance-mixture-parent.component.ts b/src/app/core/substance-details/substance-mixture-parent/substance-mixture-parent.component.ts new file mode 100644 index 000000000..facf594ca --- /dev/null +++ b/src/app/core/substance-details/substance-mixture-parent/substance-mixture-parent.component.ts @@ -0,0 +1,35 @@ +import { Component, OnInit } from '@angular/core'; +import { SubstanceDetail } from '@gsrs-core/substance/substance.model'; +import { Subject } from 'rxjs'; +import { SubstanceCardBase } from '@gsrs-core/substance-details/substance-card-base'; + +@Component({ + selector: 'app-substance-mixture-parent', + templateUrl: './substance-mixture-parent.component.html', + styleUrls: ['./substance-mixture-parent.component.scss'] +}) +export class SubstanceMixtureParentComponent extends SubstanceCardBase implements OnInit { + substanceUpdated = new Subject(); + mixtures: Array; + + constructor() { + super(); + } +ngOnInit(){ + + this.substanceUpdated.subscribe(substance => { + this.substance = substance; + this.mixtures = []; + if ((this.substance != null) && (this.substance.$$mixtureParents) && (this.substance.$$mixtureParents.length > 0)) { + this.substance.$$mixtureParents.forEach( parent => { + parent.mixture.components.forEach(comp => { + if (comp.substance.refuuid === this.substance.uuid) { + this.mixtures.push(parent); + } + }); + + }); + } + }); + } +} diff --git a/src/app/core/substance-details/substance-mixture-parent/substance-mixture-parent.module.ts b/src/app/core/substance-details/substance-mixture-parent/substance-mixture-parent.module.ts new file mode 100644 index 000000000..99d362706 --- /dev/null +++ b/src/app/core/substance-details/substance-mixture-parent/substance-mixture-parent.module.ts @@ -0,0 +1,21 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { DynamicComponentLoaderModule } from '@gsrs-core/dynamic-component-loader'; +import { SubstanceMixtureParentComponent } from '@gsrs-core/substance-details/substance-mixture-parent/substance-mixture-parent.component'; +import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; + + + +@NgModule({ + imports: [ + CommonModule, + RouterModule, + DynamicComponentLoaderModule.forChild(SubstanceMixtureParentComponent), + SubstanceImageModule + ], + declarations: [ + SubstanceMixtureParentComponent + ] +}) +export class SubstanceMixtureParentModule { } diff --git a/src/app/core/substance-details/substance-modifications/substance-modifications.component.scss b/src/app/core/substance-details/substance-modifications/substance-modifications.component.scss index 26597eabd..e720deba4 100644 --- a/src/app/core/substance-details/substance-modifications/substance-modifications.component.scss +++ b/src/app/core/substance-details/substance-modifications/substance-modifications.component.scss @@ -16,5 +16,5 @@ } .bottom-border { - border-bottom: 1px solid rgba(0, 0, 0, .05) + border-bottom: 1px solid var(--sub-hierarchy-odd-bg-color); } diff --git a/src/app/core/substance-details/substance-modifications/substance-modifications.component.spec.ts b/src/app/core/substance-details/substance-modifications/substance-modifications.component.spec.ts index 99a7d5bc0..86a824330 100644 --- a/src/app/core/substance-details/substance-modifications/substance-modifications.component.spec.ts +++ b/src/app/core/substance-details/substance-modifications/substance-modifications.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { SubstanceModificationsComponent } from './substance-modifications.component'; -import { MatTableModule } from '@angular/material'; +import { MatTableModule } from '@angular/material/table'; import { CdkTableModule } from '@angular/cdk/table'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ConfigService } from '../../config/config.service'; diff --git a/src/app/core/substance-details/substance-modifications/substance-modifications.module.ts b/src/app/core/substance-details/substance-modifications/substance-modifications.module.ts index 53750f0e0..db7007a7e 100644 --- a/src/app/core/substance-details/substance-modifications/substance-modifications.module.ts +++ b/src/app/core/substance-details/substance-modifications/substance-modifications.module.ts @@ -2,7 +2,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; import { SubstanceModificationsComponent } from './substance-modifications.component'; -import { MatTableModule } from '@angular/material'; +import { MatTableModule } from '@angular/material/table'; import { CdkTableModule } from '@angular/cdk/table'; import { RouterModule } from '@angular/router'; diff --git a/src/app/core/substance-details/substance-moieties/substance-moieties.component.html b/src/app/core/substance-details/substance-moieties/substance-moieties.component.html index c3b708991..fb269d6a1 100644 --- a/src/app/core/substance-details/substance-moieties/substance-moieties.component.html +++ b/src/app/core/substance-details/substance-moieties/substance-moieties.component.html @@ -1,7 +1,7 @@
    @@ -12,7 +12,7 @@
    Molecular Weight:
    -
    {{moiety.mwt}}
    +
    {{moiety.mwt | number: rounding}}
    Charge:
    diff --git a/src/app/core/substance-details/substance-moieties/substance-moieties.component.scss b/src/app/core/substance-details/substance-moieties/substance-moieties.component.scss index 364762c3c..80415e76a 100644 --- a/src/app/core/substance-details/substance-moieties/substance-moieties.component.scss +++ b/src/app/core/substance-details/substance-moieties/substance-moieties.component.scss @@ -10,7 +10,7 @@ align-items: center; &:not(:last-child) { - border-bottom: solid 1px rgba(0, 0, 0, 0.12); + border-bottom: solid 1px var(--box-shadow-color-3); } } @@ -85,4 +85,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/app/core/substance-details/substance-moieties/substance-moieties.component.ts b/src/app/core/substance-details/substance-moieties/substance-moieties.component.ts index 76034fad4..b516dafdd 100644 --- a/src/app/core/substance-details/substance-moieties/substance-moieties.component.ts +++ b/src/app/core/substance-details/substance-moieties/substance-moieties.component.ts @@ -6,7 +6,8 @@ import {UtilsService} from '../../utils/utils.service'; import {Subject} from 'rxjs'; import { StructureImageModalComponent, StructureService } from '@gsrs-core/structure'; import { OverlayContainer } from '@angular/cdk/overlay'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; +import { ConfigService } from '@gsrs-core/config'; @Component({ selector: 'app-substance-moieties', @@ -17,12 +18,16 @@ export class SubstanceMoietiesComponent extends SubstanceCardBase implements OnI moieties: Array = []; substanceUpdated = new Subject(); private overlayContainer: HTMLElement; + rounding = '1.0-2'; + stagingId: any = null; + constructor( private utilService: UtilsService, private overlayContainerService: OverlayContainer, private structureService: StructureService, - private dialog: MatDialog + private dialog: MatDialog, + private configService: ConfigService ) { super(); } @@ -30,8 +35,12 @@ export class SubstanceMoietiesComponent extends SubstanceCardBase implements OnI ngOnInit() { this.substanceUpdated.subscribe(substance => { this.substance = substance; + + if (this.substance.$$source && this.substance.$$source === 'staging') { + this.stagingId = this.substance.uuid; + } if (this.substance != null && this.substance.moieties != null) { - this.moieties = this.substance.moieties; + this.moieties = JSON.parse(JSON.stringify(this.substance.moieties)); this.moieties.forEach( unit => { unit.formula = this.structureService.formatFormula(unit); @@ -41,6 +50,10 @@ export class SubstanceMoietiesComponent extends SubstanceCardBase implements OnI }); this.overlayContainer = this.overlayContainerService.getContainerElement(); + if (this.configService.configData && this.configService.configData.molWeightRounding) { + this.rounding = '1.0-' + this.configService.configData.molWeightRounding; + } + } openImageModal(substance) { const dialogRef = this.dialog.open(StructureImageModalComponent, { diff --git a/src/app/core/substance-details/substance-monomers/substance-monomers.component.scss b/src/app/core/substance-details/substance-monomers/substance-monomers.component.scss index 23855fbc0..44fa39895 100644 --- a/src/app/core/substance-details/substance-monomers/substance-monomers.component.scss +++ b/src/app/core/substance-details/substance-monomers/substance-monomers.component.scss @@ -5,7 +5,7 @@ .yesDef { - color:grey; + color:var(--regular-grey-color); } .noDef { diff --git a/src/app/core/substance-details/substance-monomers/substance-monomers.component.spec.ts b/src/app/core/substance-details/substance-monomers/substance-monomers.component.spec.ts index e81bfbb22..1e9474c04 100644 --- a/src/app/core/substance-details/substance-monomers/substance-monomers.component.spec.ts +++ b/src/app/core/substance-details/substance-monomers/substance-monomers.component.spec.ts @@ -1,6 +1,8 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { SubstanceMonomersComponent } from './substance-monomers.component'; -import { MatInputModule, MatPaginatorModule, MatTableModule } from '@angular/material'; +import {MatInputModule} from '@angular/material/input'; +import {MatPaginatorModule} from '@angular/material/paginator'; +import {MatTableModule} from '@angular/material/table'; import { CdkTableModule } from '@angular/cdk/table'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { HttpClientTestingModule } from '@angular/common/http/testing'; diff --git a/src/app/core/substance-details/substance-monomers/substance-monomers.module.ts b/src/app/core/substance-details/substance-monomers/substance-monomers.module.ts index c50a38720..c76897b5c 100644 --- a/src/app/core/substance-details/substance-monomers/substance-monomers.module.ts +++ b/src/app/core/substance-details/substance-monomers/substance-monomers.module.ts @@ -2,7 +2,9 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; import { SubstanceMonomersComponent } from './substance-monomers.component'; -import { MatInputModule, MatPaginatorModule, MatTableModule } from '@angular/material'; +import {MatInputModule} from '@angular/material/input'; +import {MatPaginatorModule} from '@angular/material/paginator'; +import {MatTableModule} from '@angular/material/table'; import { CdkTableModule } from '@angular/cdk/table'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; diff --git a/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.component.html b/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.component.html index d976cd76d..1e7e1ce78 100644 --- a/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.component.html +++ b/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.component.html @@ -6,6 +6,11 @@ {{getLinkageDisplay(linkage.linkage)}} + + Structure + + + Site Range {{linkage.sitesShorthand}} @@ -17,6 +22,6 @@ - +
    diff --git a/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.component.scss b/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.component.scss index e69de29bb..a7318b32a 100644 --- a/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.component.scss +++ b/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.component.scss @@ -0,0 +1,3 @@ +.structure { + margin: -12px; +} \ No newline at end of file diff --git a/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.component.spec.ts b/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.component.spec.ts index c5376d47d..816456211 100644 --- a/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.component.spec.ts +++ b/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { SubstanceNaLinkagesComponent } from './substance-na-linkages.component'; -import { MatTableModule } from '@angular/material'; +import { MatTableModule } from '@angular/material/table'; import { CdkTableModule } from '@angular/cdk/table'; describe('SubstanceNaLinkagesComponent', () => { diff --git a/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.component.ts b/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.component.ts index bb801a8c8..f844a9b06 100644 --- a/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.component.ts +++ b/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.component.ts @@ -12,7 +12,7 @@ import {ControlledVocabularyService, VocabularyDictionary, VocabularyTerm} from }) export class SubstanceNaLinkagesComponent extends SubstanceCardBase implements OnInit, OnDestroy { linkages: Array; - displayedColumns: string[] = ['linkage' , 'Site Range' , 'Site Count' ]; + displayedColumns: string[] = ['linkage' , 'Structure', 'Site Range' , 'Site Count' ]; siteCount: number; vocabulary: any; substanceUpdated = new Subject(); @@ -36,6 +36,13 @@ export class SubstanceNaLinkagesComponent extends SubstanceCardBase implements O this.countUpdate.emit(this.linkages.length); const cvSubscription = this.cvService.getDomainVocabulary('NUCLEIC_ACID_LINKAGE').subscribe(response => { this.vocabulary = response['NUCLEIC_ACID_LINKAGE'].dictionary; + this.linkages.forEach(linkage => { + if(this.vocabulary[linkage.linkage]) { + linkage.structure = this.cvService.getStructureUrlFragment(this.vocabulary[linkage.linkage].fragmentStructure); + } else { + linkage.structure = null; + } + }) }); this.subscriptions.push(cvSubscription); this.getTotalSites(); diff --git a/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.module.ts b/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.module.ts index 57065a2ec..16b1b2f66 100644 --- a/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.module.ts +++ b/src/app/core/substance-details/substance-na-linkages/substance-na-linkages.module.ts @@ -2,7 +2,9 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import {SubstanceNaLinkagesComponent} from './substance-na-linkages.component'; import {DynamicComponentLoaderModule} from '../../dynamic-component-loader/dynamic-component-loader.module'; -import {MatInputModule, MatPaginatorModule, MatTableModule} from '@angular/material'; +import {MatInputModule} from '@angular/material/input'; +import {MatPaginatorModule} from '@angular/material/paginator'; +import {MatTableModule} from '@angular/material/table'; import {CdkTableModule} from '@angular/cdk/table'; @NgModule({ diff --git a/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.component.html b/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.component.html index 91acd4c11..dcf2e1af8 100644 --- a/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.component.html +++ b/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.component.html @@ -6,6 +6,11 @@ {{getSugarDisplay(sugar.sugar)}} + + Structure + + + Site Range {{sugar.sitesShorthand}} @@ -17,6 +22,6 @@ - +
    diff --git a/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.component.scss b/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.component.scss index e69de29bb..a7318b32a 100644 --- a/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.component.scss +++ b/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.component.scss @@ -0,0 +1,3 @@ +.structure { + margin: -12px; +} \ No newline at end of file diff --git a/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.component.spec.ts b/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.component.spec.ts index 6f0af5d04..5046602d2 100644 --- a/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.component.spec.ts +++ b/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { SubstanceNaSugarsComponent } from './substance-na-sugars.component'; -import { MatTableModule } from '@angular/material'; +import { MatTableModule } from '@angular/material/table'; import { CdkTableModule } from '@angular/cdk/table'; describe('SubstanceNaSugarsComponent', () => { diff --git a/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.component.ts b/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.component.ts index a1f997a4c..a175e7b3b 100644 --- a/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.component.ts +++ b/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.component.ts @@ -11,7 +11,7 @@ import {ControlledVocabularyService} from '@gsrs-core/controlled-vocabulary'; }) export class SubstanceNaSugarsComponent extends SubstanceCardBase implements OnInit, OnDestroy { sugars: Array; - displayedColumns: string[] = ['Sugar' , 'Site Range' , 'Site Count' ]; + displayedColumns: string[] = ['Sugar' , 'Structure', 'Site Range' , 'Site Count' ]; siteCount: number; vocabulary: any; substanceUpdated = new Subject(); @@ -34,6 +34,13 @@ export class SubstanceNaSugarsComponent extends SubstanceCardBase implements OnI this.countUpdate.emit(this.sugars.length); const cvSubscription = this.cvService.getDomainVocabulary('NUCLEIC_ACID_SUGAR').subscribe(response => { this.vocabulary = response['NUCLEIC_ACID_SUGAR'].dictionary; + this.sugars.forEach(sugar => { + if(this.vocabulary[sugar.sugar]) { + sugar.structure = this.cvService.getStructureUrlFragment(this.vocabulary[sugar.sugar].fragmentStructure); + } else { + sugar.structure = null; + } + }) }); this.subscriptions.push(cvSubscription); this.getTotalSites(); diff --git a/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.module.ts b/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.module.ts index 9230231ce..a6a435e37 100644 --- a/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.module.ts +++ b/src/app/core/substance-details/substance-na-sugars/substance-na-sugars.module.ts @@ -2,7 +2,9 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import {SubstanceNaSugarsComponent} from './substance-na-sugars.component'; import {DynamicComponentLoaderModule} from '../../dynamic-component-loader/dynamic-component-loader.module'; -import {MatInputModule, MatPaginatorModule, MatTableModule} from '@angular/material'; +import {MatInputModule} from '@angular/material/input'; +import {MatPaginatorModule} from '@angular/material/paginator'; +import {MatTableModule} from '@angular/material/table'; import {CdkTableModule} from '@angular/cdk/table'; diff --git a/src/app/core/substance-details/substance-names/substance-names.component.html b/src/app/core/substance-details/substance-names/substance-names.component.html index 0c020f4f4..529ccb16f 100644 --- a/src/app/core/substance-details/substance-names/substance-names.component.html +++ b/src/app/core/substance-details/substance-names/substance-names.component.html @@ -1,16 +1,49 @@
    - +
    + +
    +
    Name View: + + Name (UTF-8) + Std. Name (ASCII) + Both + +
    + + + {{showHideFilterText}} +
    +
    - - + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -90,13 +220,14 @@

    References

    - +
    +
    Name {{name.name}} - + Name + + + + + + + + + + + + Std. Name + {{name.stdName}} + @@ -20,21 +53,118 @@ - Type Type + {{typeVocabulary[name.type] && typeVocabulary[name.type].display}} Language Language + {{getLanguages(name)}} +
    + + + +
    +
    {{name.name}} + + + + + + + +
    + + + +
    +
    {{name.stdName}} + + + + + + + +
    + + + + {{typeFilter.value ? typeFilter.value : ''}} + + Select Type Filter + {{type.display}} + + +
    +
    {{typeVocabulary[name.type] && typeVocabulary[name.type].display}} +
    + + + + {{langFilter.value ? langFilter.value : ''}} + + Select Language Filter + {{lang.display}} + + +
    +
    {{getLanguages(name)}} + +
    + + + +
    +
    Details Details - + (!name.nameJurisdiction || name.nameJurisdiction.length == 0 )" [ngClass] = "isButtonDisabled(name)? 'disabled-btn':''"> View

    Details

    @@ -72,7 +202,7 @@

    Details

    - +
    diff --git a/src/app/core/substance-details/substance-names/substance-names.component.scss b/src/app/core/substance-details/substance-names/substance-names.component.scss index 508b768b9..bae811c79 100644 --- a/src/app/core/substance-details/substance-names/substance-names.component.scss +++ b/src/app/core/substance-details/substance-names/substance-names.component.scss @@ -14,6 +14,23 @@ padding: 10px; } +.mat-radio-button { + padding-left: 30px; +} + +.name-view { + margin-top: 16px; +} + +.name-view-label { + font-weight: 500; +} + +.right { + justify-content: right; + align-items: right; + padding-top:10px; +} td.mat-cell, td.mat-footer-cell, th.mat-header-cell { padding-top: 10px; @@ -36,4 +53,8 @@ width: 150px; .subval{ display: inline; -} \ No newline at end of file +} +.disabled-btn { + color: var(--box-shadow-color-5); + background-color: var(--box-shadow-color-3); +} diff --git a/src/app/core/substance-details/substance-names/substance-names.component.ts b/src/app/core/substance-details/substance-names/substance-names.component.ts index 85d4b788a..687e3a7b4 100644 --- a/src/app/core/substance-details/substance-names/substance-names.component.ts +++ b/src/app/core/substance-details/substance-names/substance-names.component.ts @@ -1,14 +1,17 @@ import {Component, OnInit, AfterViewInit} from '@angular/core'; import { SubstanceCardBaseFilteredList } from '../substance-card-base-filtered-list'; -import {SubstanceDetail, SubstanceName} from '../../substance/substance.model'; +import {SubstanceDetail, SubstanceName, TableFilterDDModel} from '../../substance/substance.model'; import { ControlledVocabularyService } from '../../controlled-vocabulary/controlled-vocabulary.service'; import { VocabularyTerm } from '../../controlled-vocabulary/vocabulary.model'; -import {MatDialog} from '@angular/material'; +import {MatDialog} from '@angular/material/dialog'; import { GoogleAnalyticsService } from '../../google-analytics/google-analytics.service'; import {Subject} from 'rxjs'; -import {Sort} from '@angular/material'; +import {Sort} from '@angular/material/sort'; import { OverlayContainer } from '@angular/cdk/overlay'; import {UtilsService} from '@gsrs-core/utils'; +import { FormControl } from '@angular/forms'; +import { throws } from 'assert'; +import { I } from '@angular/cdk/keycodes'; @Component({ selector: 'app-substance-names', @@ -18,12 +21,26 @@ import {UtilsService} from '@gsrs-core/utils'; export class SubstanceNamesComponent extends SubstanceCardBaseFilteredList implements OnInit { names: Array; displayedColumns: string[] = ['name', 'type', 'language', 'details', 'references']; + displayedFilterColumns: string[] = ['nameFilter', 'typeFilter', 'languageFilter', 'emptyFilter', 'resetFilter']; languageVocabulary: { [vocabularyTermValue: string]: VocabularyTerm } = {}; typeVocabulary: { [vocabularyTermValue: string]: VocabularyTerm } = {}; substanceUpdated = new Subject(); private overlayContainer: HTMLElement; hideOrgs = true; pageSize = 10; + uniqueVals: Array; + filterSelectObj = []; + filterBackup: Array; + typeFilterOn = 'false'; + nameFilter = new FormControl(); + stdNameFilter = new FormControl(); + typeFilter = new FormControl(); + langFilter = new FormControl(); + langFilterOptions: Array = []; + typeFilterOptions: Array = []; + nameType = 'name'; + hideFilters = true; + showHideFilterText = 'Show Filter'; constructor( private dialog: MatDialog, @@ -36,6 +53,15 @@ export class SubstanceNamesComponent extends SubstanceCardBaseFilteredList { this.substance = substance; if (this.substance != null && this.substance.names != null) { @@ -43,18 +69,29 @@ export class SubstanceNamesComponent extends SubstanceCardBaseFilteredList { - this.filterList(value, this.names, this.analyticsEventCategory); + if (this.typeFilterOn === 'false') { + this.filterList(value, this.names, this.analyticsEventCategory); + + } else if (this.typeFilterOn === 'true') { + const tempFilter = JSON.parse(JSON.stringify(this.filtered)); + this.filterList(value, this.filterBackup, this.analyticsEventCategory); + } }, error => { console.log(error); }); this.getVocabularies(); - // move display name to top this.filtered = this.names.slice().sort((a, b) => { let returned = -1; - if ( b.displayName === true) { + if (a.displayName) { + returned = -1; + } else if (b.displayName) { returned = 1; - } else if (b.preferred === true && a.displayName !== true) { + } else if (b.preferred && !a.preferred) { + returned = 1; + } else if (!b.preferred && a.preferred) { + returned = -1; + }else if (a.name.toUpperCase() > b.name.toUpperCase()) { returned = 1; } return returned; @@ -62,11 +99,201 @@ export class SubstanceNamesComponent extends SubstanceCardBaseFilteredList { + o.options = this.getFilterObject(this.names, o.columnProp); + }); }); this.overlayContainer = this.overlayContainerService.getContainerElement(); + this.nameFilter.valueChanges.subscribe((nameFilterValue) => { + this.filterTable(); + }); + this.stdNameFilter.valueChanges.subscribe((stdNameFilterValue) => { + this.filterTable('std'); + }); + this.typeFilter.valueChanges.subscribe((typeFilterValue) => { + this.filterTable(); + }); + this.langFilter.valueChanges.subscribe((langFilterValue) => { + this.filterTable(); + }); + } + + toggleFilter() { + this.hideFilters = !this.hideFilters; + if(this.hideFilters) { + this.showHideFilterText = 'Show Filter'; + } else { + this.showHideFilterText = 'Hide Filter'; + } + } + + updateType(event) { + this.nameType = event.value; + if(event.value === 'name') { + this.displayedColumns = ['name', 'type', 'language', 'details', 'references']; + this.displayedFilterColumns = ['nameFilter', 'typeFilter', 'languageFilter', 'emptyFilter', 'resetFilter']; + } else if (event.value === 'ascii') { + this.displayedColumns = ['stdName', 'type', 'language', 'details', 'references']; + this.displayedFilterColumns = ['stdNameFilter', 'typeFilter', 'languageFilter', 'emptyFilter', 'resetFilter']; + } else { + this.displayedColumns = ['name', 'stdName', 'type', 'language', 'details', 'references']; + this.displayedFilterColumns = ['nameFilter', 'stdNameFilter', 'typeFilter', 'languageFilter', 'resetFilter']; + } +} + + filterTable(type?:string) { + const nFilter = this.nameFilter.value === null ? '' : this.nameFilter.value; + const snFilter = this.stdNameFilter.value === null ? '' : this.stdNameFilter.value; + + const lFilter = this.langFilter.value === null ? '' : this.langFilter.value; + const tFilter = this.typeFilter.value === null ? '' : this.typeFilter.value; + const lFilterCode = this.getLangFilterValue(lFilter) === undefined ? '' : this.getLangFilterValue(lFilter).value; + const tFilterCode = this.getTypeFilterValue(tFilter) === undefined ? '' : this.getTypeFilterValue(tFilter).value; + this.filtered = []; + if(type && type === 'std') { + for(let n of this.names) { + let stdNameStr = n.stdName === undefined ? '' : n.stdName; + if((stdNameStr.toLowerCase().includes(snFilter.toLowerCase())) && + (this.isIncluded(n, tFilterCode, 'type')) && + (this.isIncluded(n, lFilterCode, 'lang'))) { + this.filtered.push(n); + } + } + } else { + for(let n of this.names) { + if((n.name.toLowerCase().includes(nFilter.toLowerCase())) && + (this.isIncluded(n, tFilterCode, 'type')) && + (this.isIncluded(n, lFilterCode, 'lang'))) { + this.filtered.push(n); + } + } + } + + this.pageChange(); } + isIncluded(name: SubstanceName, value: string, field: string) { + if(field === 'type') { + if(value.length > 0) { + if(name.type.includes(value)) { + return true; + } else { + return false; + } + } else { + return true; + } + } else if(field === 'lang') { + if(value.length > 0) { + if(name.languages.includes(value)) { + return true; + } else { + return false; + } + } else { + return true; + } + } + } + getLangFilterValue(value) { + for(let l of this.langFilterOptions) { + if(l.display === value) { + return l; + } + } + } + + getLangFilterOptions() { + for(let n of this.names) { + let nLangs = n.languages; + for(let l of nLangs) { + let oneLang = l; + let oneLangDisplay = this.languageVocabulary[oneLang] && this.languageVocabulary[oneLang].display ? this.languageVocabulary[oneLang].display : oneLang; + let value: TableFilterDDModel = { + value: oneLang, + display: oneLangDisplay + } + if (this.langFilterOptions.filter(e => e.value === oneLang).length > 0) { + } else { + this.langFilterOptions.push(value); + } + } + } + } + + getTypeFilterValue(value) { + for(let l of this.typeFilterOptions) { + if(l.display === value) { + return l; + } + } + } + + getTypeFilterOptions() { + for(let n of this.names) { + let oneType = n.type; + let oneTypeDisplay = this.typeVocabulary[oneType] && this.typeVocabulary[oneType].display ? this.typeVocabulary[oneType].display : oneType; + let value: TableFilterDDModel = { + value: oneType, + display: oneTypeDisplay + } + if (this.typeFilterOptions.filter(e => e.value === oneType).length > 0) { + } else { + this.typeFilterOptions.push(value); + } + } + } + + filterChange(filter, event) { + this.typeFilterOn = 'false'; + const tempFiltered = []; + this.filterBackup = []; + this.names.forEach(item => { + const itemString = JSON.stringify(item[filter.columnProp]).toLowerCase(); + if (itemString.indexOf(event.target.value.toLowerCase()) > -1) { + this.filterBackup.push(item); + } + }); + setTimeout(() => { + + this.names.forEach(item => { + const itemString = JSON.stringify(item[filter.columnProp]).toLowerCase(); + if (itemString.indexOf(event.target.value.toLowerCase()) > -1) { + tempFiltered.push(item); + } + }); + this.filtered = tempFiltered; + this.typeFilterOn = 'true'; + this.page = 0; + this.pageChange(); + }, 50); + } + + setDisplay(value, column) { + if (column === 'type') { + return (this.typeVocabulary[value] && this.typeVocabulary[value].display ? this.typeVocabulary[value].display : value); + } else if (column === 'language') { + return ( this.languageVocabulary[value] && this.languageVocabulary[value].display ? this.typeVocabulary[value].display : value); + } else { + return value; + } + } + + getFilterObject(fullObj, key) { + const uniqChk = []; + if (key === 'type') { + + } + fullObj.filter((obj) => { + if (!uniqChk.includes(obj[key])) { + uniqChk.push(obj[key]); + } + return obj; + }); + return uniqChk; + } sortData(sort: Sort) { const data = this.names.slice(); @@ -78,8 +305,8 @@ export class SubstanceNamesComponent extends SubstanceCardBaseFilteredList { const isAsc = sort.direction === 'asc'; switch (sort.active) { - case 'name': return this.utilsService.compare(a.name.toUpperCase(), b.name.toUpperCase(), isAsc); - case 'type': return this.utilsService.compare(a.type, b.type, isAsc); + case 'name': return this.utilsService.compare(a.name ? a.name.toUpperCase() : '', b.name ? b.name.toUpperCase() : '', isAsc); + case 'type': return this.utilsService.compare(a.type ? a.type : '', b.type ? b.type : '', isAsc); case 'language': return this.utilsService.compare(this.getLanguages(a), this.getLanguages(b), isAsc); default: return 0; } @@ -87,10 +314,27 @@ export class SubstanceNamesComponent extends SubstanceCardBaseFilteredList { + value.modelValue = undefined; + }); + this.typeFilterOn = 'false'; + this.filtered = this.names; + this.pageChange(); + this.searchControl.setValue(''); + this.nameFilter.setValue(''); + this.stdNameFilter.setValue(''); + this.langFilter.setValue(''); + this.typeFilter.setValue(''); + } + + getVocabularies(): void { this.cvService.getDomainVocabulary('LANGUAGE', 'NAME_TYPE').subscribe(response => { this.languageVocabulary = response['LANGUAGE'] && response['LANGUAGE'].dictionary; this.typeVocabulary = response['NAME_TYPE'] && response['NAME_TYPE'].dictionary; + this.getLangFilterOptions(); + this.getTypeFilterOptions(); }); } @@ -123,5 +367,18 @@ export class SubstanceNamesComponent extends SubstanceCardBaseFilteredListReferences

    - +
    diff --git a/src/app/core/substance-details/substance-notes/substance-notes.component.scss b/src/app/core/substance-details/substance-notes/substance-notes.component.scss index 55bb1e931..924d24136 100644 --- a/src/app/core/substance-details/substance-notes/substance-notes.component.scss +++ b/src/app/core/substance-details/substance-notes/substance-notes.component.scss @@ -1 +1,31 @@ -@import '../../../../styles/table'; \ No newline at end of file +@import '../../../../styles/table'; + + + +.disabled { + color: var(--box-shadow-color-5); + background-color: var(--box-shadow-color-3); + cursor: pointer; + &:hover { + cursor: pointer !important; + } + } + + .disabled:hover { + cursor: pointer !important; + + } + + .disabled-btn { + color: var(--box-shadow-color-5); + background-color: var(--box-shadow-color-3); + cursor: pointer; + &:hover { + cursor: pointer !important; + } + } + + .disabled-btn:hover { + cursor: pointer !important; + + } \ No newline at end of file diff --git a/src/app/core/substance-details/substance-notes/substance-notes.component.ts b/src/app/core/substance-details/substance-notes/substance-notes.component.ts index 1cb1537a4..7c30a6331 100644 --- a/src/app/core/substance-details/substance-notes/substance-notes.component.ts +++ b/src/app/core/substance-details/substance-notes/substance-notes.component.ts @@ -1,7 +1,7 @@ import {AfterViewInit, Component, OnInit} from '@angular/core'; import { SubstanceCardBase } from '../substance-card-base'; import {SubstanceDetail, SubstanceNote} from '../../substance/substance.model'; -import {MatDialog} from '@angular/material'; +import {MatDialog} from '@angular/material/dialog'; import { GoogleAnalyticsService } from '../../google-analytics/google-analytics.service'; import { ReadMoreComponent } from '@gsrs-core/substance-details/substance-notes/read-more/read-more.component'; import {Subject} from 'rxjs'; @@ -52,4 +52,8 @@ export class SubstanceNotesComponent extends SubstanceCardBase implements OnInit }); } + close() { + this.dialog.closeAll(); + } + } diff --git a/src/app/core/substance-details/substance-notes/substance-notes.module.ts b/src/app/core/substance-details/substance-notes/substance-notes.module.ts index bb916cdbc..091ab34a5 100644 --- a/src/app/core/substance-details/substance-notes/substance-notes.module.ts +++ b/src/app/core/substance-details/substance-notes/substance-notes.module.ts @@ -4,7 +4,8 @@ import { SubstanceNotesComponent } from './substance-notes.component'; import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; import { MatTableModule } from '@angular/material/table'; import { CdkTableModule } from '@angular/cdk/table'; -import {MatDialogModule, MatIconModule} from '@angular/material'; +import {MatDialogModule} from '@angular/material/dialog'; +import {MatIconModule} from '@angular/material/icon'; import {ReferencesManagerModule} from '../../references-manager/references-manager.module'; import {ReadMoreComponent} from '@gsrs-core/substance-details/substance-notes/read-more/read-more.component'; diff --git a/src/app/core/substance-details/substance-other-links/substance-other-links.component.spec.ts b/src/app/core/substance-details/substance-other-links/substance-other-links.component.spec.ts index a634b4d93..a749ccb62 100644 --- a/src/app/core/substance-details/substance-other-links/substance-other-links.component.spec.ts +++ b/src/app/core/substance-details/substance-other-links/substance-other-links.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { SubstanceOtherLinksComponent } from './substance-other-links.component'; -import { MatTableModule } from '@angular/material'; +import { MatTableModule } from '@angular/material/table'; import { CdkTableModule } from '@angular/cdk/table'; describe('SubstanceOtherLinksComponent', () => { diff --git a/src/app/core/substance-details/substance-other-links/substance-other-links.module.ts b/src/app/core/substance-details/substance-other-links/substance-other-links.module.ts index a93f538b3..cc9b8aa84 100644 --- a/src/app/core/substance-details/substance-other-links/substance-other-links.module.ts +++ b/src/app/core/substance-details/substance-other-links/substance-other-links.module.ts @@ -3,7 +3,9 @@ import { CommonModule } from '@angular/common'; import { SubstanceOtherLinksComponent } from './substance-other-links.component'; import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; import {RouterModule} from '@angular/router'; -import {MatInputModule, MatPaginatorModule, MatTableModule} from '@angular/material'; +import {MatInputModule} from '@angular/material/input'; +import {MatPaginatorModule} from '@angular/material/paginator'; +import {MatTableModule} from '@angular/material/table'; import {CdkTableModule} from '@angular/cdk/table'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; diff --git a/src/app/core/substance-details/substance-overview/substance-overview.component.html b/src/app/core/substance-details/substance-overview/substance-overview.component.html index 990db30f9..014ce4b15 100644 --- a/src/app/core/substance-details/substance-overview/substance-overview.component.html +++ b/src/app/core/substance-details/substance-overview/substance-overview.component.html @@ -1,28 +1,35 @@
    - - + + name="edit_record" aria-label="edit record"> - + + + + + + - + - @@ -53,8 +60,8 @@
    Substance Class
    {{substance.substanceClass | classDisplay}}
    -
    -
    Record UNII
    +
    +
    Record {{approvalCode? approvalCode : 'Approval ID'}}
    {{substance._approvalIDDisplay}}
    @@ -74,9 +81,9 @@
    -
    +
    Record Status
    -
    {{substance.status === 'approved' ? 'Validated (UNII)' : 'pending'}} +
    {{substance.status === 'approved' ? primaryCode : substance.status}} RECORD IS DEPRECATED
    @@ -200,7 +207,7 @@
    -
    Hybrid Paternal Organism
    +
    Hybrid Maternal Organism
    @@ -213,11 +220,11 @@
    -
    +
    Record Version
    {{substance.version}}
    -
    +
    Record Version
    @@ -229,7 +236,7 @@
    -
    +
    This is an older version of this substance - Ver. {{this.substance.version}} @@ -248,7 +255,7 @@
    - + {{showDef === false ? 'Show' : 'Hide'}} Definitional References @@ -257,5 +264,27 @@
    +
    +
    Definitional Access
    +
    + + Not a public definition  + + + + Public definition  + + +
    + +
    \ No newline at end of file diff --git a/src/app/core/substance-details/substance-overview/substance-overview.component.scss b/src/app/core/substance-details/substance-overview/substance-overview.component.scss index d2eb33d84..9334ba0c8 100644 --- a/src/app/core/substance-details/substance-overview/substance-overview.component.scss +++ b/src/app/core/substance-details/substance-overview/substance-overview.component.scss @@ -12,26 +12,33 @@ } .view-version { margin-left:10px; - } -::ng-deep .mat-form-field-infix{ - border: 0px; + +.version-dropdown { + ::ng-deep .mat-form-field-infix{ + border: 0px; + } } + .options-area { float:right; display: flex; flex-direction: column; } +.pre { + white-space: pre; +} + .definition-level { - color: #1565C0; + color: var(--link-color); font-size: 16px; } .deprecated { margin-left: 10px; font-size: 13px; - color: #c7254e; + color: var(--pink-span-color); } .extra-area { diff --git a/src/app/core/substance-details/substance-overview/substance-overview.component.ts b/src/app/core/substance-details/substance-overview/substance-overview.component.ts index 03f64e486..8bc69d73e 100644 --- a/src/app/core/substance-details/substance-overview/substance-overview.component.ts +++ b/src/app/core/substance-details/substance-overview/substance-overview.component.ts @@ -16,7 +16,7 @@ import { SubstanceClassPipe } from '../../utils/substance-class.pipe'; import {ConfigService} from '@gsrs-core/config'; import { catchError } from 'rxjs/operators'; import { LoadingService } from '@gsrs-core/loading'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; import { OverlayContainer } from '@angular/cdk/overlay'; import { SubstanceHistoryDialogComponent } from '@gsrs-core/substance-history-dialog/substance-history-dialog.component'; @@ -32,6 +32,7 @@ export class SubstanceOverviewComponent extends SubstanceCardBase implements OnI defIcon = 'drop_down'; latestVersion: number; canEdit: boolean; + defAccess: Array; versionControl = new FormControl('', Validators.required); versions: string[] = []; isEditable = false; @@ -42,8 +43,11 @@ export class SubstanceOverviewComponent extends SubstanceCardBase implements OnI defaultCodeSystem = 'BDNUM'; defaultCodes: string; clasicBaseHref: string; + primaryCode = "Validated (UNII)"; + approvalCode: string; private overlayContainer: HTMLElement; private subscriptions: Array = []; + showlinks = false; constructor( @@ -86,8 +90,26 @@ export class SubstanceOverviewComponent extends SubstanceCardBase implements OnI if (this.configService.configData.defaultCodeSystem != null && this.configService.configData.defaultCodeSystem !== '') { this.defaultCodeSystem = this.configService.configData.defaultCodeSystem; - } + } + if (this.configService.configData.approvalCodeName != null + && this.configService.configData.approvalCodeName !== '') { + this.approvalCode = this.configService.configData.approvalCodeName; + this.substance.codes.forEach(code => { + if (code.codeSystem == this.approvalCode ) { + this.substance._approvalIDDisplay = code.code; + } + }); + } + if (this.configService.configData.primaryCode + && this.configService.configData.primaryCode !== '') { + this.primaryCode = 'Validated (' + this.configService.configData.primaryCode + ')'; + } + + if (this.configService.configData && this.configService.configData.showOldLinks) { + this.showlinks = true; + } + this.getDefAccess(); if (this.substance.codes != null && this.substance.codes.length > 0) { const defaultCodes = []; this.substance.codes.forEach(code => { @@ -116,6 +138,25 @@ export class SubstanceOverviewComponent extends SubstanceCardBase implements OnI } + getDefAccess() { + if (this.substance.structurallyDiverse) { + this.defAccess = this.substance.structurallyDiverse.access; + } else if (this.substance.protein) { + this.defAccess = this.substance.protein.access; + } else if (this.substance.structure) { + this.defAccess = this.substance.structure.access; + } else if (this.substance.mixture) { + this.defAccess = this.substance.mixture.access; + } else if (this.substance.polymer) { + this.defAccess = this.substance.polymer.access; + } else if (this.substance.nucleicAcid) { + this.defAccess = this.substance.nucleicAcid.access; + } else if (this.substance.specifiedSubstance) { + this.defAccess = this.substance.specifiedSubstance.access; + } + + } + getSubtypeRefs(substance: SubstanceDetail): void { if (substance.mixture) { this.references = substance.mixture.references; diff --git a/src/app/core/substance-details/substance-overview/substance-overview.module.ts b/src/app/core/substance-details/substance-overview/substance-overview.module.ts index 326b62bb5..01cb53425 100644 --- a/src/app/core/substance-details/substance-overview/substance-overview.module.ts +++ b/src/app/core/substance-details/substance-overview/substance-overview.module.ts @@ -3,7 +3,9 @@ import { CommonModule } from '@angular/common'; import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; import { SubstanceOverviewComponent } from './substance-overview.component'; import {ReferencesManagerModule} from '../../references-manager/references-manager.module'; -import {MatExpansionModule, MatIconModule, MatChipsModule} from '@angular/material'; +import {MatExpansionModule} from '@angular/material/expansion'; +import {MatChipsModule} from '@angular/material/chips'; +import {MatIconModule} from '@angular/material/icon'; import {RouterModule} from '@angular/router'; import { MatFormFieldModule } from '@angular/material/form-field'; import { ReactiveFormsModule } from '@angular/forms'; diff --git a/src/app/core/substance-details/substance-polymer-structure/substance-polymer-structure.module.ts b/src/app/core/substance-details/substance-polymer-structure/substance-polymer-structure.module.ts index b5dcddb84..51afee0c2 100644 --- a/src/app/core/substance-details/substance-polymer-structure/substance-polymer-structure.module.ts +++ b/src/app/core/substance-details/substance-polymer-structure/substance-polymer-structure.module.ts @@ -4,7 +4,8 @@ import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dyn import { SubstancePolymerStructureComponent } from './substance-polymer-structure.component'; import {RouterModule} from '@angular/router'; import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; -import { MatIconModule, MatTooltipModule } from '@angular/material'; +import {MatTooltipModule} from '@angular/material/tooltip'; +import {MatIconModule} from '@angular/material/icon'; @NgModule({ imports: [ diff --git a/src/app/core/substance-details/substance-properties/substance-properties.component.html b/src/app/core/substance-details/substance-properties/substance-properties.component.html index 7ccbb1d22..73ad130fa 100644 --- a/src/app/core/substance-details/substance-properties/substance-properties.component.html +++ b/src/app/core/substance-details/substance-properties/substance-properties.component.html @@ -13,7 +13,11 @@ Amount - {{toString(property.value)}} + {{toString(property.value)}} + + + + @@ -54,7 +58,7 @@

    References

    - +
    diff --git a/src/app/core/substance-details/substance-properties/substance-properties.component.scss b/src/app/core/substance-details/substance-properties/substance-properties.component.scss index e69de29bb..bdd80476c 100644 --- a/src/app/core/substance-details/substance-properties/substance-properties.component.scss +++ b/src/app/core/substance-details/substance-properties/substance-properties.component.scss @@ -0,0 +1,27 @@ +.disabled { + color: var(--box-shadow-color-5); + background-color: var(--box-shadow-color-3); + cursor: pointer; + &:hover { + cursor: pointer !important; + } + } + + .disabled:hover { + cursor: pointer !important; + + } + + .disabled-btn { + color: var(--box-shadow-color-5); + background-color: var(--box-shadow-color-3); + cursor: pointer; + &:hover { + cursor: pointer !important; + } + } + + .disabled-btn:hover { + cursor: pointer !important; + + } \ No newline at end of file diff --git a/src/app/core/substance-details/substance-properties/substance-properties.component.ts b/src/app/core/substance-details/substance-properties/substance-properties.component.ts index b763ce401..026835129 100644 --- a/src/app/core/substance-details/substance-properties/substance-properties.component.ts +++ b/src/app/core/substance-details/substance-properties/substance-properties.component.ts @@ -4,7 +4,7 @@ import {SubstanceAmount, SubstanceDetail, SubstanceProperty} from '../../substan import {Subject} from 'rxjs'; import { UtilsService } from '@gsrs-core/utils'; import { OverlayContainer } from '@angular/cdk/overlay'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; @Component({ selector: 'app-substance-properties', @@ -39,6 +39,16 @@ export class SubstancePropertiesComponent extends SubstanceCardBase implements O return this.utilsService.displayAmount(amount); } + codeIsProtected(access: string[]) { + let itIs = false; + for(let a of access) { + if(a.toLowerCase() === 'protected') { + itIs = true; + } + } + return itIs; + } + openModal(templateRef) { const dialogRef = this.dialog.open(templateRef, { @@ -52,4 +62,8 @@ export class SubstancePropertiesComponent extends SubstanceCardBase implements O }); } + close(){ + this.dialog.closeAll(); + } + } diff --git a/src/app/core/substance-details/substance-properties/substance-properties.module.ts b/src/app/core/substance-details/substance-properties/substance-properties.module.ts index 7df194e8d..5235d0358 100644 --- a/src/app/core/substance-details/substance-properties/substance-properties.module.ts +++ b/src/app/core/substance-details/substance-properties/substance-properties.module.ts @@ -2,8 +2,13 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import {DynamicComponentLoaderModule} from '../../dynamic-component-loader/dynamic-component-loader.module'; import {SubstancePropertiesComponent} from './substance-properties.component'; -import {MatInputModule, MatPaginatorModule, MatTableModule, MatDialogModule, MatTooltipModule} from '@angular/material'; +import {MatInputModule} from '@angular/material/input'; +import {MatPaginatorModule} from '@angular/material/paginator'; +import {MatTableModule} from '@angular/material/table'; +import {MatDialogModule} from '@angular/material/dialog'; +import {MatTooltipModule} from '@angular/material/tooltip'; import {CdkTableModule} from '@angular/cdk/table'; +import { MatIconModule } from '@angular/material/icon'; import {RouterModule} from '@angular/router'; import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; import { ReferencesManagerModule } from '@gsrs-core/references-manager'; @@ -13,6 +18,7 @@ import { ReferencesManagerModule } from '@gsrs-core/references-manager'; CommonModule, DynamicComponentLoaderModule.forChild(SubstancePropertiesComponent), MatTableModule, + MatIconModule, CdkTableModule, MatPaginatorModule, MatInputModule, diff --git a/src/app/core/substance-details/substance-references/substance-references.component.html b/src/app/core/substance-details/substance-references/substance-references.component.html index b0301b230..ff1607685 100644 --- a/src/app/core/substance-details/substance-references/substance-references.component.html +++ b/src/app/core/substance-details/substance-references/substance-references.component.html @@ -5,6 +5,9 @@
    + + {{showHideFilterText}} + @@ -15,7 +18,7 @@ - + @@ -27,20 +30,83 @@ - + + + + + + + + + + + + + + + + + + + - - - - + + + + +
    Type {{reference.docType}} +
    + + + +
    +
    + {{reference.citation}} + {{reference.citation}} + +
    + + + + {{typeFilter.value ? typeFilter.value : ''}} + + Select Type Filter + {{type.display}} + + +
    +
    {{reference.docType}} +
    + + + + {{tagsFilter.value ? tagsFilter.value : ''}} + + Select Tags Filter + {{tags.display}} + + +
    +
    + {{tag}} + +
    + +
    +
    File - + Date Accessed {{reference.accessed | date : 'short'}} Access + + + + +
    diff --git a/src/app/core/substance-details/substance-references/substance-references.component.scss b/src/app/core/substance-details/substance-references/substance-references.component.scss index 0388a6b5a..24dfa9b2e 100644 --- a/src/app/core/substance-details/substance-references/substance-references.component.scss +++ b/src/app/core/substance-details/substance-references/substance-references.component.scss @@ -3,4 +3,8 @@ .search { width: 400px; max-width: 100%; +} +.show-filter-span { + display: block; + float: right; } \ No newline at end of file diff --git a/src/app/core/substance-details/substance-references/substance-references.component.ts b/src/app/core/substance-details/substance-references/substance-references.component.ts index 95288c764..5ab085d21 100644 --- a/src/app/core/substance-details/substance-references/substance-references.component.ts +++ b/src/app/core/substance-details/substance-references/substance-references.component.ts @@ -1,10 +1,11 @@ import { Component, OnInit } from '@angular/core'; -import {SubstanceDetail, SubstanceReference} from '../../substance/substance.model'; +import {SubstanceDetail, SubstanceReference, TableFilterDDModel} from '../../substance/substance.model'; import { SubstanceCardBaseFilteredList } from '../substance-card-base-filtered-list'; import { GoogleAnalyticsService } from '../../google-analytics/google-analytics.service'; import {Subject} from 'rxjs'; import {Sort} from '@angular/material/sort'; import {UtilsService} from '@gsrs-core/utils'; +import { FormControl } from '@angular/forms'; @Component({ selector: 'app-substance-references', @@ -13,9 +14,17 @@ import {UtilsService} from '@gsrs-core/utils'; }) export class SubstanceReferencesComponent extends SubstanceCardBaseFilteredList implements OnInit { references: Array = []; - displayedColumns: string[] = ['citation', 'type', 'tags', 'files', 'dateAcessed']; + displayedColumns: string[] = ['citation', 'docType', 'tags', 'files', 'access']; substanceUpdated = new Subject(); pageSize = 10; + hideFilters = true; + showHideFilterText = 'Show Filter'; + displayedFilterColumns: string[] = ['citationFilter', 'typeFilter', 'tagsFilter', 'resetFilter']; + citationFilter = new FormControl(); + typeFilter = new FormControl(); + tagsFilter = new FormControl(); + typeFilterOptions: Array = []; + tagsFilterOptions: Array = []; constructor( public gaService: GoogleAnalyticsService, @@ -39,10 +48,93 @@ export class SubstanceReferencesComponent extends SubstanceCardBaseFilteredList< }, error => { console.log(error); }); + this.getFilterOptions('tags'); + this.getFilterOptions('type'); } this.countUpdate.emit(this.references.length); }); + this.citationFilter.valueChanges.subscribe((citationFilterValue) => { + this.filterTable(); + }); + this.typeFilter.valueChanges.subscribe((typeFilterValue) => { + this.filterTable(); + }); + this.tagsFilter.valueChanges.subscribe((tagsFilterValue) => { + this.filterTable(); + }); + } + + filterTable(type?:string) { + const cFilter = this.citationFilter.value === null ? '' : this.citationFilter.value; + const tgFilter = this.tagsFilter.value === null ? '' : this.tagsFilter.value; + const tFilter = this.typeFilter.value === null ? '' : this.typeFilter.value; + this.filtered = []; + for(let n of this.references) { + if((n.citation.toLowerCase().includes(cFilter.toLowerCase())) && + (n.docType.toLowerCase().includes(tFilter.toLowerCase())) && + (this.isIncluded(n, tgFilter))) { + this.filtered.push(n); + } + } + + this.pageChange(); + } + + isIncluded(name, value) { + if(value.length > 0) { + if(name.tags.includes(value)) { + return true; + } else { + return false; + } + } else { + return true; + } + } + + getFilterOptions(string) { + for(let n of this.references) { + if(string === 'tags') { + for(let t of n.tags) { + let oneTag = t; + let val: TableFilterDDModel = { + value: oneTag, + display: oneTag + } + if (this.tagsFilterOptions.filter(e => e.value === oneTag).length > 0) { + } else { + this.tagsFilterOptions.push(val); + } + } + } else if(string === 'type') { + let oneType = n.docType; + let value: TableFilterDDModel = { + value: oneType, + display: oneType + } + if (this.typeFilterOptions.filter(e => e.value === oneType).length > 0) { + } else { + this.typeFilterOptions.push(value); + } + } + } + } + toggleFilter() { + this.hideFilters = !this.hideFilters; + if(this.hideFilters) { + this.showHideFilterText = 'Show Filter'; + } else { + this.showHideFilterText = 'Hide Filter'; + } + } + + resetFilters() { + this.pageChange(); + this.searchControl.setValue(''); + this.citationFilter.setValue(''); + this.tagsFilter.setValue(''); + this.typeFilter.setValue(''); } sortData(sort: Sort) { @@ -54,7 +146,7 @@ export class SubstanceReferencesComponent extends SubstanceCardBaseFilteredList< } this.filtered = data.sort((a, b) => { const isAsc = sort.direction === 'asc'; - return this.utilsService.compare(a[sort.active].toUpperCase, b[sort.active].toUpperCase, isAsc); + return this.utilsService.compare(a[sort.active] ? a[sort.active].toString().toUpperCase() : null, b[sort.active] ? b[sort.active].toString().toUpperCase() : null, isAsc); }); this.pageChange(); } diff --git a/src/app/core/substance-details/substance-references/substance-references.module.ts b/src/app/core/substance-details/substance-references/substance-references.module.ts index e94a96d0e..62d45548c 100644 --- a/src/app/core/substance-details/substance-references/substance-references.module.ts +++ b/src/app/core/substance-details/substance-references/substance-references.module.ts @@ -7,8 +7,12 @@ import { CdkTableModule } from '@angular/cdk/table'; import { MatPaginatorModule } from '@angular/material/paginator'; import { MatInputModule } from '@angular/material/input'; import { ReactiveFormsModule, FormsModule } from '@angular/forms'; -import {MatSort, MatSortModule} from '@angular/material/sort'; -import { MatIconModule } from '@angular/material'; +import {MatSortModule} from '@angular/material/sort'; +import {MatTooltipModule} from '@angular/material/tooltip'; +import {MatIconModule} from '@angular/material/icon'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; + @NgModule({ imports: [ @@ -18,10 +22,13 @@ import { MatIconModule } from '@angular/material'; CdkTableModule, MatPaginatorModule, MatInputModule, + MatFormFieldModule, + MatSelectModule, ReactiveFormsModule, FormsModule, MatSortModule, - MatIconModule + MatIconModule, + MatTooltipModule ], declarations: [SubstanceReferencesComponent] }) diff --git a/src/app/core/substance-details/substance-relationships/substance-relationships.component.html b/src/app/core/substance-details/substance-relationships/substance-relationships.component.html index 33d2c7bbb..7744294c1 100644 --- a/src/app/core/substance-details/substance-relationships/substance-relationships.component.html +++ b/src/app/core/substance-details/substance-relationships/substance-relationships.component.html @@ -3,6 +3,7 @@ +
    @@ -14,8 +15,8 @@
    {{relationship.relatedSubstance.linkingID}}
    @@ -31,8 +32,20 @@ @@ -84,7 +92,7 @@

    Substance Ingredient Names:

    - + + + + + + + + + + + + + + + + + + + + + +
    Details - - + + +

    Details

    @@ -50,8 +63,8 @@

    Details

    {{relationship.mediatorSubstance.linkingID}}
    @@ -96,7 +109,7 @@

    Details

    - +
    @@ -114,7 +127,7 @@

    References

    - +
    diff --git a/src/app/core/substance-details/substance-relationships/substance-relationships.component.ts b/src/app/core/substance-details/substance-relationships/substance-relationships.component.ts index 3df366f4d..8a2c2619f 100644 --- a/src/app/core/substance-details/substance-relationships/substance-relationships.component.ts +++ b/src/app/core/substance-details/substance-relationships/substance-relationships.component.ts @@ -2,12 +2,12 @@ import { Component, OnInit } from '@angular/core'; import {SubstanceDetail, SubstanceRelationship} from '../../substance/substance.model'; import { UtilsService } from '../../utils/utils.service'; import { ConfigService } from '../../config/config.service'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; import { SubstanceCardBaseFilteredList } from '../substance-card-base-filtered-list'; import { GoogleAnalyticsService } from '../../google-analytics/google-analytics.service'; import {Subject} from 'rxjs'; import { OverlayContainer } from '@angular/cdk/overlay'; -import {Sort} from '@angular/material'; +import {Sort} from '@angular/material/sort'; @Component({ selector: 'app-substance-relationships', @@ -73,9 +73,9 @@ export class SubstanceRelationshipsComponent extends SubstanceCardBaseFilteredLi this.filtered = data.sort((a, b) => { const isAsc = sort.direction === 'asc'; switch (sort.active) { - case 'relatedRecord': return this.utilService.compare(a.relatedSubstance.name.toUpperCase(), - b.relatedSubstance.name.toUpperCase(), isAsc); - case 'type': return this.utilService.compare(a.type, b.type, isAsc); + case 'relatedRecord': return this.utilService.compare(a.relatedSubstance.name ? a.relatedSubstance.name.toUpperCase() : '', + b.relatedSubstance.name ? b.relatedSubstance.name.toUpperCase() : '', isAsc); + case 'type': return this.utilService.compare(a.type ? a.type : '', b.type ? b.type : '', isAsc); default: return 0; } }); @@ -212,4 +212,8 @@ export class SubstanceRelationshipsComponent extends SubstanceCardBaseFilteredLi } return ret; } + + close(){ + this.dialog.closeAll(); + } } diff --git a/src/app/core/substance-details/substance-relationships/substance-relationships.module.ts b/src/app/core/substance-details/substance-relationships/substance-relationships.module.ts index feeb8ed01..21b22370d 100644 --- a/src/app/core/substance-details/substance-relationships/substance-relationships.module.ts +++ b/src/app/core/substance-details/substance-relationships/substance-relationships.module.ts @@ -4,13 +4,18 @@ import { SubstanceRelationshipsComponent } from './substance-relationships.compo import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; import { MatTableModule } from '@angular/material/table'; import {ReferencesManagerModule} from '../../references-manager/references-manager.module'; -import {MatIconModule, MatDialogModule, MatPaginatorModule, MatFormFieldModule, MatInputModule} from '@angular/material'; +import {MatInputModule} from '@angular/material/input'; +import {MatPaginatorModule} from '@angular/material/paginator'; +import {MatDialogModule} from '@angular/material/dialog'; +import {MatIconModule} from '@angular/material/icon'; +import {MatFormFieldModule} from '@angular/material/form-field'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {CdkTableModule} from '@angular/cdk/table'; import {RouterModule} from '@angular/router'; import {MatSortModule} from '@angular/material/sort'; import {MatTooltipModule} from '@angular/material/tooltip'; import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; +import { RelationshipsDownloadButtonComponent } from '@gsrs-core/substance-form/relationships/relationships-download-button/relationships-download-button.component'; @NgModule({ imports: [ @@ -31,6 +36,12 @@ import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.modul MatSortModule, SubstanceImageModule ], - declarations: [SubstanceRelationshipsComponent] + declarations: [ + SubstanceRelationshipsComponent, + RelationshipsDownloadButtonComponent + ], + exports: [ + RelationshipsDownloadButtonComponent + ] }) export class SubstanceRelationshipsModule { } diff --git a/src/app/core/substance-details/substance-ssg-definition/substance-ssg-definition.component.html b/src/app/core/substance-details/substance-ssg-definition/substance-ssg-definition.component.html index 5e089b0df..7eac10413 100644 --- a/src/app/core/substance-details/substance-ssg-definition/substance-ssg-definition.component.html +++ b/src/app/core/substance-details/substance-ssg-definition/substance-ssg-definition.component.html @@ -18,7 +18,7 @@

    References

    - +
    diff --git a/src/app/core/substance-details/substance-ssg-definition/substance-ssg-definition.component.ts b/src/app/core/substance-details/substance-ssg-definition/substance-ssg-definition.component.ts index 6344a9f0a..9e95634d4 100644 --- a/src/app/core/substance-details/substance-ssg-definition/substance-ssg-definition.component.ts +++ b/src/app/core/substance-details/substance-ssg-definition/substance-ssg-definition.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit} from '@angular/core'; import { SubstanceCardBase } from '../substance-card-base'; import { SubstanceDetail, Definition} from '../../substance/substance.model'; -import { MatDialog} from '@angular/material'; +import { MatDialog} from '@angular/material/dialog'; import { GoogleAnalyticsService } from '../../google-analytics/google-analytics.service'; import { Subject } from 'rxjs'; import { OverlayContainer } from '@angular/cdk/overlay'; @@ -54,4 +54,9 @@ export class SubstanceSsgDefinitionComponent extends SubstanceCardBase implement }); } + close(){ + this.dialog.closeAll(); + } + + } diff --git a/src/app/core/substance-details/substance-ssg-definition/substance-ssg-definition.module.ts b/src/app/core/substance-details/substance-ssg-definition/substance-ssg-definition.module.ts index 00863c289..593c599e3 100644 --- a/src/app/core/substance-details/substance-ssg-definition/substance-ssg-definition.module.ts +++ b/src/app/core/substance-details/substance-ssg-definition/substance-ssg-definition.module.ts @@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'; import { SubstanceSsgDefinitionComponent } from './substance-ssg-definition.component'; import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; import { ReferencesManagerModule } from '../../references-manager/references-manager.module'; -import { MatDialogModule } from '@angular/material'; +import { MatDialogModule } from '@angular/material/dialog'; @NgModule({ imports: [ diff --git a/src/app/core/substance-details/substance-ssg-grade/substance-ssg-grade.component.ts b/src/app/core/substance-details/substance-ssg-grade/substance-ssg-grade.component.ts index 6d76e086f..30680d650 100644 --- a/src/app/core/substance-details/substance-ssg-grade/substance-ssg-grade.component.ts +++ b/src/app/core/substance-details/substance-ssg-grade/substance-ssg-grade.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit} from '@angular/core'; import { SubstanceCardBase } from '../substance-card-base'; import { SubstanceDetail, Grade} from '../../substance/substance.model'; -import { MatDialog} from '@angular/material'; +import { MatDialog} from '@angular/material/dialog'; import { GoogleAnalyticsService } from '../../google-analytics/google-analytics.service'; import { Subject } from 'rxjs'; import { OverlayContainer } from '@angular/cdk/overlay'; diff --git a/src/app/core/substance-details/substance-ssg-grade/substance-ssg-grade.module.ts b/src/app/core/substance-details/substance-ssg-grade/substance-ssg-grade.module.ts index 84d1a7b88..468e0ae18 100644 --- a/src/app/core/substance-details/substance-ssg-grade/substance-ssg-grade.module.ts +++ b/src/app/core/substance-details/substance-ssg-grade/substance-ssg-grade.module.ts @@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'; import { SubstanceSsgGradeComponent } from './substance-ssg-grade.component'; import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; import { ReferencesManagerModule } from '../../references-manager/references-manager.module'; -import { MatDialogModule } from '@angular/material'; +import { MatDialogModule } from '@angular/material/dialog'; @NgModule({ imports: [ diff --git a/src/app/core/substance-details/substance-ssg-parent-substance/substance-ssg-parent-substance.component.ts b/src/app/core/substance-details/substance-ssg-parent-substance/substance-ssg-parent-substance.component.ts index 04f4abd0f..1ef6faddc 100644 --- a/src/app/core/substance-details/substance-ssg-parent-substance/substance-ssg-parent-substance.component.ts +++ b/src/app/core/substance-details/substance-ssg-parent-substance/substance-ssg-parent-substance.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit} from '@angular/core'; import { SubstanceCardBase } from '../substance-card-base'; import { SubstanceDetail, SubstanceRelated} from '../../substance/substance.model'; -import { MatDialog} from '@angular/material'; +import { MatDialog} from '@angular/material/dialog'; import { GoogleAnalyticsService } from '../../google-analytics/google-analytics.service'; import { Subject } from 'rxjs'; import { OverlayContainer } from '@angular/cdk/overlay'; diff --git a/src/app/core/substance-details/substance-ssg-parent-substance/substance-ssg-parent-substance.module.ts b/src/app/core/substance-details/substance-ssg-parent-substance/substance-ssg-parent-substance.module.ts index a3bf662cd..b07800979 100644 --- a/src/app/core/substance-details/substance-ssg-parent-substance/substance-ssg-parent-substance.module.ts +++ b/src/app/core/substance-details/substance-ssg-parent-substance/substance-ssg-parent-substance.module.ts @@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'; import { SubstanceSsgParentSubstanceComponent } from './substance-ssg-parent-substance.component'; import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; import { ReferencesManagerModule } from '../../references-manager/references-manager.module'; -import { MatDialogModule } from '@angular/material'; +import { MatDialogModule } from '@angular/material/dialog'; import { RouterModule } from '@angular/router'; import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; diff --git a/src/app/core/substance-details/substance-ssg1-parent/substance-ssg1-parent.component.html b/src/app/core/substance-details/substance-ssg1-parent/substance-ssg1-parent.component.html new file mode 100644 index 000000000..8301cc9b9 --- /dev/null +++ b/src/app/core/substance-details/substance-ssg1-parent/substance-ssg1-parent.component.html @@ -0,0 +1,13 @@ +

    This substance has the following group 1 specified substance forms:

    +
    +
    +
    {{component.approvalID}}
    +
    {{component.uuid}}
    + + +
    +
    diff --git a/src/app/core/substance-details/substance-ssg1-parent/substance-ssg1-parent.component.scss b/src/app/core/substance-details/substance-ssg1-parent/substance-ssg1-parent.component.scss new file mode 100644 index 000000000..39b6ee0a0 --- /dev/null +++ b/src/app/core/substance-details/substance-ssg1-parent/substance-ssg1-parent.component.scss @@ -0,0 +1,22 @@ +.thumb-col{ + width: 33%; + min-width: 200px; + } + + + .image-icon { + width: 150px; + height: auto; + } + + .substance-icon-container{ + text-align:center; + padding-left:10px; + padding-right:10px; + max-width:250px; + } + + .wrap{ + flex-wrap: wrap; + } + \ No newline at end of file diff --git a/src/app/core/substance-details/substance-ssg1-parent/substance-ssg1-parent.component.spec.ts b/src/app/core/substance-details/substance-ssg1-parent/substance-ssg1-parent.component.spec.ts new file mode 100644 index 000000000..115e7d6d1 --- /dev/null +++ b/src/app/core/substance-details/substance-ssg1-parent/substance-ssg1-parent.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SubstanceSsg1ParentComponent } from './substance-ssg1-parent.component'; + +describe('SubstanceSsg1ParentComponent', () => { + let component: SubstanceSsg1ParentComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SubstanceSsg1ParentComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SubstanceSsg1ParentComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-details/substance-ssg1-parent/substance-ssg1-parent.component.ts b/src/app/core/substance-details/substance-ssg1-parent/substance-ssg1-parent.component.ts new file mode 100644 index 000000000..9b814ff36 --- /dev/null +++ b/src/app/core/substance-details/substance-ssg1-parent/substance-ssg1-parent.component.ts @@ -0,0 +1,23 @@ +import { Component, OnInit } from '@angular/core'; +import { SubstanceCardBase } from '@gsrs-core/substance-details/substance-card-base'; +import { Subject } from 'rxjs'; +import { SubstanceDetail } from '@gsrs-core/substance/substance.model'; + +@Component({ + selector: 'app-substance-ssg1-parent', + templateUrl: './substance-ssg1-parent.component.html', + styleUrls: ['./substance-ssg1-parent.component.scss'] +}) +export class SubstanceSsg1ParentComponent extends SubstanceCardBase implements OnInit { + substanceUpdated = new Subject(); + constructor() { + super(); + } + + ngOnInit() { + this.substanceUpdated.subscribe(substance => { + this.substance = substance; + }); +} + +} diff --git a/src/app/core/substance-details/substance-ssg1-parent/substance-ssg1-parent.module.ts b/src/app/core/substance-details/substance-ssg1-parent/substance-ssg1-parent.module.ts new file mode 100644 index 000000000..3c58597d4 --- /dev/null +++ b/src/app/core/substance-details/substance-ssg1-parent/substance-ssg1-parent.module.ts @@ -0,0 +1,25 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; +import { RouterModule } from '@angular/router'; +import { MatDialogModule } from '@angular/material/dialog'; +import { ReferencesManagerModule } from '@gsrs-core/references-manager'; +import { DynamicComponentLoaderModule } from '@gsrs-core/dynamic-component-loader'; +import { SubstanceSsg1ParentComponent } from '@gsrs-core/substance-details/substance-ssg1-parent/substance-ssg1-parent.component'; + + + +@NgModule({ + imports: [ + CommonModule, + DynamicComponentLoaderModule.forChild(SubstanceSsg1ParentComponent), + ReferencesManagerModule, + MatDialogModule, + RouterModule, + SubstanceImageModule + ], + declarations: [ + SubstanceSsg1ParentComponent + ] +}) +export class SubstanceSsg1ParentModule { } diff --git a/src/app/core/substance-details/substance-structural-units/substance-structural-units.module.ts b/src/app/core/substance-details/substance-structural-units/substance-structural-units.module.ts index be1f87be2..6da5e0625 100644 --- a/src/app/core/substance-details/substance-structural-units/substance-structural-units.module.ts +++ b/src/app/core/substance-details/substance-structural-units/substance-structural-units.module.ts @@ -2,7 +2,9 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; -import { MatInputModule, MatPaginatorModule, MatTableModule } from '@angular/material'; +import {MatInputModule} from '@angular/material/input'; +import {MatPaginatorModule} from '@angular/material/paginator'; +import {MatTableModule} from '@angular/material/table'; import { CdkTableModule } from '@angular/cdk/table'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; diff --git a/src/app/core/substance-details/substance-subunits/substance-subunits.component.html b/src/app/core/substance-details/substance-subunits/substance-subunits.component.html index 5d9c55e76..90e7cb748 100644 --- a/src/app/core/substance-details/substance-subunits/substance-subunits.component.html +++ b/src/app/core/substance-details/substance-subunits/substance-subunits.component.html @@ -1,9 +1,9 @@
    - + - + @@ -13,8 +13,10 @@
    Subunit {{subunitSequence.subunitIndex}}
    diff --git a/src/app/core/substance-details/substance-subunits/substance-subunits.component.scss b/src/app/core/substance-details/substance-subunits/substance-subunits.component.scss index 4551cad06..ffda24f94 100644 --- a/src/app/core/substance-details/substance-subunits/substance-subunits.component.scss +++ b/src/app/core/substance-details/substance-subunits/substance-subunits.component.scss @@ -6,7 +6,7 @@ } .subunit-title { - color: #1565C0; + color: var(--link-color); text-decoration: none; } @@ -26,7 +26,7 @@ } error { - color: red; + color: var(--regular-red-color); } .subunit-row { @@ -112,7 +112,7 @@ error { padding-bottom: 20px; &:not(:last-child) { - border-bottom: solid 1px rgba(0, 0, 0, 0.12); + border-bottom: solid 1px var(--box-shadow-color-3); } } @@ -130,3 +130,7 @@ error { .last-section { flex-basis: 0!important; } +.mat-icon { + height: 15px !important; + width: 15px !important; +} diff --git a/src/app/core/substance-details/substance-variant-concepts/substance-variant-concepts.module.ts b/src/app/core/substance-details/substance-variant-concepts/substance-variant-concepts.module.ts index 08bcac6be..61b13c838 100644 --- a/src/app/core/substance-details/substance-variant-concepts/substance-variant-concepts.module.ts +++ b/src/app/core/substance-details/substance-variant-concepts/substance-variant-concepts.module.ts @@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'; import {DynamicComponentLoaderModule} from '../../dynamic-component-loader/dynamic-component-loader.module'; import {SubstanceVariantConceptsComponent} from './substance-variant-concepts.component'; import {RouterModule} from '@angular/router'; -import { MatTableModule} from '@angular/material'; +import { MatTableModule} from '@angular/material/table'; import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; @NgModule({ diff --git a/src/app/core/substance-edit-import-dialog/substance-edit-import-dialog.component.html b/src/app/core/substance-edit-import-dialog/substance-edit-import-dialog.component.html index c2c9c1dfe..2ced63ecd 100644 --- a/src/app/core/substance-edit-import-dialog/substance-edit-import-dialog.component.html +++ b/src/app/core/substance-edit-import-dialog/substance-edit-import-dialog.component.html @@ -1,22 +1,27 @@
    -

    Substance Import

    +

    {{title}}

    +
    Select a .json file to import into a new edit form.
    -
    +
    {{filename? filename: 'no file chosen'}}
    -
    +
    +
    Or paste JSON here:
    + +
    +
    {{message}}
    -
    +
    - - - + + +
    \ No newline at end of file diff --git a/src/app/core/substance-edit-import-dialog/substance-edit-import-dialog.component.scss b/src/app/core/substance-edit-import-dialog/substance-edit-import-dialog.component.scss index 35e09805d..e65d9c4fd 100644 --- a/src/app/core/substance-edit-import-dialog/substance-edit-import-dialog.component.scss +++ b/src/app/core/substance-edit-import-dialog/substance-edit-import-dialog.component.scss @@ -1,5 +1,6 @@ +/* awtodo not sure about differences */ .message { - padding:20px; + padding: 10px; } .info { @@ -18,7 +19,7 @@ .italics { font-style: italic; - color: rgba(0, 0, 0, .5); + color: var(--text-color); } .file-name { @@ -28,7 +29,7 @@ margin-top: auto; margin-bottom: auto; font-size: 15px; - color: rgba(0, 0, 0, .8); + color: var(--deprecated-color); } .full-row { @@ -37,4 +38,57 @@ flex-direction: row; justify-content: flex-start; padding: 15px; -} \ No newline at end of file +} + +.full-row-col { + width: 100%; + display: flex; + flex-direction: column; + justify-content: flex-start; + padding: 10px; +} + +.button-select-file { + color: #007CBA; + border: 1px solid #007CBA; +} + +.button-cancel { + background-color: #FFFFFF; + border: 1px solid #D60036; +} + +.button-import { + color: #FFFFFF; + background-color: #007CBA; + border: 1px solid 007CBA; +} + +.colorwhite { + color: #FFFFFF; +} + +.colorred { + color: red; +} + +.colorblue { + color: #007CBA; +} + +.bordergray { + border: 1px solid rgb(37, 34, 34); +} + +.padbottom20px { + padding-bottom: 20px; +} + +hr { + border: none; + border-top: 1px solid rgb(126, 131, 126); + color: #333; + overflow: visible; + text-align: center; + height: 5px; +} diff --git a/src/app/core/substance-edit-import-dialog/substance-edit-import-dialog.component.ts b/src/app/core/substance-edit-import-dialog/substance-edit-import-dialog.component.ts index 9fab0b23e..dd28c539b 100644 --- a/src/app/core/substance-edit-import-dialog/substance-edit-import-dialog.component.ts +++ b/src/app/core/substance-edit-import-dialog/substance-edit-import-dialog.component.ts @@ -1,5 +1,5 @@ -import { Component, OnInit } from '@angular/core'; -import { MatDialogRef } from '@angular/material'; +import { Component, OnInit, Inject } from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog'; import { Router } from '@angular/router'; @Component({ @@ -13,17 +13,26 @@ export class SubstanceEditImportDialogComponent implements OnInit { loaded = false; record: any; filename: string; + pastedJSON: string; + uploaded = false; + title = 'Substance Import'; + entity = 'Substance'; constructor( private router: Router, - public dialogRef: MatDialogRef - + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any ) { } ngOnInit() { + if (this.data) { + if (this.data.title) { + this.title = this.data.title; + this.entity = this.data.entity; + } + } } - uploadFile(event) { if (event.target.files.length !== 1) { this.message = 'No file selected'; @@ -36,7 +45,7 @@ export class SubstanceEditImportDialogComponent implements OnInit { const response = reader.result.toString(); if (this.jsonValid(response)) { const read = JSON.parse(response); - if (!read['substanceClass']) { + if (!read['substanceClass'] && this.entity === 'Substance') { this.message = 'Error: Invalid JSON format'; this.loaded = false; } else { @@ -50,13 +59,38 @@ export class SubstanceEditImportDialogComponent implements OnInit { } }; reader.readAsText(event.target.files[0]); + this.uploaded = true; } } useFile() { + if (!this.uploaded && this.pastedJSON) { + const read = JSON.parse(this.pastedJSON); + if (!read['substanceClass']) { + this.message = 'Error: Invalid JSON format'; + this.loaded = false; + } else { + this.loaded = true; + this.record = this.pastedJSON; + this.message = ''; + } + } this.dialogRef.close(this.record); } + + checkLoaded() { + this.loaded = true; + try { + JSON.parse(this.pastedJSON); + this.message = ''; + } catch (e) { + this.message = 'Error: Invalid JSON format in pasted string'; + this.loaded = false; + } +} + + openInput(): void { document.getElementById('fileInput').click(); } diff --git a/src/app/core/substance-form/access-manager/access-manager.component.html b/src/app/core/substance-form/access-manager/access-manager.component.html index 3c4811647..2bafedd56 100644 --- a/src/app/core/substance-form/access-manager/access-manager.component.html +++ b/src/app/core/substance-form/access-manager/access-manager.component.html @@ -7,7 +7,7 @@ (menuClosed)="menuClosed()"> - Access + {{label? label: "Access"}}
    diff --git a/src/app/core/substance-form/access-manager/access-manager.component.ts b/src/app/core/substance-form/access-manager/access-manager.component.ts index af7b3595a..6216f7a75 100644 --- a/src/app/core/substance-form/access-manager/access-manager.component.ts +++ b/src/app/core/substance-form/access-manager/access-manager.component.ts @@ -14,6 +14,7 @@ export class AccessManagerComponent implements OnInit, AfterViewInit { privateAccess: Array = []; @Output() accessOut = new EventEmitter>(); tooltipMessage: string; + @Input() label?: string; accessFormGroup = new FormGroup({}); constructor( diff --git a/src/app/core/substance-form/agent-modifications/agent-modification-form.component.html b/src/app/core/substance-form/agent-modifications/agent-modification-form.component.html index 760980fc2..3fbae98b4 100644 --- a/src/app/core/substance-form/agent-modifications/agent-modification-form.component.html +++ b/src/app/core/substance-form/agent-modifications/agent-modification-form.component.html @@ -41,6 +41,10 @@ +
    + + +
    diff --git a/src/app/core/substance-form/agent-modifications/agent-modification-form.component.scss b/src/app/core/substance-form/agent-modifications/agent-modification-form.component.scss index 21b823458..93bd0e48f 100644 --- a/src/app/core/substance-form/agent-modifications/agent-modification-form.component.scss +++ b/src/app/core/substance-form/agent-modifications/agent-modification-form.component.scss @@ -23,12 +23,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .form-row { @@ -59,7 +59,7 @@ font-size: 11px; padding-bottom: 3.5px; line-height: 11px; - color: rgba(0, 0, 0, 0.54); + color: var(--dark-label-color); font-weight: 400; font-family: Roboto, "Helvetica Neue", sans-serif; } @@ -122,5 +122,5 @@ .group, .access { - width: 45%; + width: 22%; } diff --git a/src/app/core/substance-form/agent-modifications/agent-modification-form.component.ts b/src/app/core/substance-form/agent-modifications/agent-modification-form.component.ts index 816dacc70..0f6810fa8 100644 --- a/src/app/core/substance-form/agent-modifications/agent-modification-form.component.ts +++ b/src/app/core/substance-form/agent-modifications/agent-modification-form.component.ts @@ -50,6 +50,10 @@ export class AgentModificationFormComponent implements OnInit { return this.privateMod; } + updateAccess(access: Array): void { + this.mod.access = access; + } + getVocabularies(): void { this.cvService.getDomainVocabulary('AGENT_MODIFICATION_TYPE', 'AGENT_MODIFICATION_PROCESS', 'ROLE').subscribe(response => { this.modTypeList = response['AGENT_MODIFICATION_TYPE'].list; @@ -106,7 +110,7 @@ export class AgentModificationFormComponent implements OnInit { this.mod.agentSubstance = relatedSubstance; this.relatedSubstanceUuid = this.mod.agentSubstance.refuuid; } else { - this.mod.agentSubstance = {}; + this.mod.agentSubstance = null; this.relatedSubstanceUuid = ''; } } diff --git a/src/app/core/substance-form/agent-modifications/substance-form-agent-modifications-card.component.html b/src/app/core/substance-form/agent-modifications/substance-form-agent-modifications-card.component.html index 69a14f240..ec6bc550b 100644 --- a/src/app/core/substance-form/agent-modifications/substance-form-agent-modifications-card.component.html +++ b/src/app/core/substance-form/agent-modifications/substance-form-agent-modifications-card.component.html @@ -9,3 +9,11 @@ + + +
    + + +
    diff --git a/src/app/core/substance-form/agent-modifications/substance-form-agent-modifications-card.component.scss b/src/app/core/substance-form/agent-modifications/substance-form-agent-modifications-card.component.scss index 4d0602d7d..338eedd60 100644 --- a/src/app/core/substance-form/agent-modifications/substance-form-agent-modifications-card.component.scss +++ b/src/app/core/substance-form/agent-modifications/substance-form-agent-modifications-card.component.scss @@ -3,9 +3,9 @@ } .mat-divider { - border-top-color: rgba(0, 0, 0, .12); + border-top-color: var(--box-shadow-color-3); } - +/* awtodo not sure about .code */ .code { &:nth-child(odd) { diff --git a/src/app/core/substance-form/agent-modifications/substance-form-agent-modifications-card.component.ts b/src/app/core/substance-form/agent-modifications/substance-form-agent-modifications-card.component.ts index 16c5a5a18..5e16cd66a 100644 --- a/src/app/core/substance-form/agent-modifications/substance-form-agent-modifications-card.component.ts +++ b/src/app/core/substance-form/agent-modifications/substance-form-agent-modifications-card.component.ts @@ -11,7 +11,7 @@ import {GoogleAnalyticsService} from '@gsrs-core/google-analytics'; templateUrl: './substance-form-agent-modifications-card.component.html', styleUrls: ['./substance-form-agent-modifications-card.component.scss'] }) -// tslint:disable-next-line:max-line-length +// eslint-disable-next-line max-len export class SubstanceFormAgentModificationsCardComponent extends SubstanceCardBaseFilteredList implements OnInit, AfterViewInit, OnDestroy, SubstanceCardBaseList { diff --git a/src/app/core/substance-form/amount-form/amount-form.component.html b/src/app/core/substance-form/amount-form/amount-form.component.html index c58a63fde..278d0f830 100644 --- a/src/app/core/substance-form/amount-form/amount-form.component.html +++ b/src/app/core/substance-form/amount-form/amount-form.component.html @@ -1,3 +1,7 @@ +
    + + +
    @@ -5,9 +9,7 @@ Type * - - - + Clear selection {{type.display}} @@ -22,28 +24,29 @@
    - + - + - +
    - + - +
    Units + Clear selection {{unit.display}} diff --git a/src/app/core/substance-form/amount-form/amount-form.component.scss b/src/app/core/substance-form/amount-form/amount-form.component.scss index da34e753e..7fa4807a1 100644 --- a/src/app/core/substance-form/amount-form/amount-form.component.scss +++ b/src/app/core/substance-form/amount-form/amount-form.component.scss @@ -36,13 +36,17 @@ margin-top: -15px; } +.deselect { + color: var(--text-color); + } + .other-container { display: flex; flex-direction:column; } .custom { - border-bottom: 1px solid rgba(0, 0, 0, .35); + border-bottom: 1px solid var(--box-shadow-color-4); } .amount-form { @@ -50,4 +54,6 @@ flex-wrap: wrap; flex-direction: row; } - +.right { + float: right; +} diff --git a/src/app/core/substance-form/amount-form/amount-form.component.ts b/src/app/core/substance-form/amount-form/amount-form.component.ts index 91b434fac..89a51fc15 100644 --- a/src/app/core/substance-form/amount-form/amount-form.component.ts +++ b/src/app/core/substance-form/amount-form/amount-form.component.ts @@ -41,22 +41,62 @@ export class AmountFormComponent implements OnInit { }); this.averageControl.setValue(this.privateSubstanceAmount.average); this.averageControl.valueChanges.subscribe(value => { + if(value === null) { + this.averageControl.setValue(''); + } else if(value.length === 1 && value.match(/[a-z]/i)) { + this.averageControl.setValue(''); + } else if(value.match(/^[-E0-9,.]*$/)) { // what we want + } else { + this.averageControl.setValue(''); + } this.privateSubstanceAmount.average = value; }); this.lowControl.setValue(this.privateSubstanceAmount.low); this.lowControl.valueChanges.subscribe(value => { + if(value === null) { + this.lowControl.setValue(''); + } else if(value.length === 1 && value.match(/[a-z]/i)) { + this.lowControl.setValue(''); + } else if(value.match(/^[-E0-9,.]*$/)) { // what we want + } else { + this.lowControl.setValue(''); + } this.privateSubstanceAmount.low = value; }); this.highControl.setValue(this.privateSubstanceAmount.high); this.highControl.valueChanges.subscribe(value => { + if(value === null) { + this.highControl.setValue(''); + } else if(value.length === 1 && value.match(/[a-z]/i)) { + this.highControl.setValue(''); + } else if(value.match(/^[-E0-9,.]*$/)) { // what we want + } else { + this.highControl.setValue(''); + } this.privateSubstanceAmount.high = value; }); this.lowLimitControl.setValue(this.privateSubstanceAmount.lowLimit); this.lowLimitControl.valueChanges.subscribe(value => { + if(value === null) { + this.lowLimitControl.setValue(''); + } else if(value.length === 1 && value.match(/[a-z]/i)) { + this.lowLimitControl.setValue(''); + } else if(value.match(/^[-E0-9,.]*$/)) { // what we want + } else { + this.lowLimitControl.setValue(''); + } this.privateSubstanceAmount.lowLimit = value; }); this.highLimitControl.setValue(this.privateSubstanceAmount.highLimit); this.highLimitControl.valueChanges.subscribe(value => { + if(value === null) { + this.highLimitControl.setValue(''); + } else if(value.length === 1 && value.match(/[a-z]/i)) { + this.highLimitControl.setValue(''); + } else if(value.match(/^[-E0-9,.]*$/)) { // what we want + } else { + this.highLimitControl.setValue(''); + } this.privateSubstanceAmount.highLimit = value; }); this.unitsControl.setValue(this.privateSubstanceAmount.units); @@ -74,6 +114,11 @@ export class AmountFormComponent implements OnInit { return this.privateSubstanceAmount; } + updateAccess(access: Array): void { + this.privateSubstanceAmount.access = access; + this.substanceAmount.access = access; + } + updateType(event: any) { setTimeout(() => { this.typeControl.setValue(event.value); diff --git a/src/app/core/substance-form/base-classes/substance-form-base.ts b/src/app/core/substance-form/base-classes/substance-form-base.ts index cef506fe2..a0ca1a3f4 100644 --- a/src/app/core/substance-form/base-classes/substance-form-base.ts +++ b/src/app/core/substance-form/base-classes/substance-form-base.ts @@ -1,5 +1,6 @@ -import { Output, EventEmitter } from '@angular/core'; +import { Output, EventEmitter, Injectable } from '@angular/core'; +@Injectable() export abstract class SubstanceFormBase { analyticsEventCategory: string; @Output() menuLabelUpdate = new EventEmitter(); diff --git a/src/app/core/substance-form/codes/code-form.component.html b/src/app/core/substance-form/codes/code-form.component.html index c2942b7d6..88fb16869 100644 --- a/src/app/core/substance-form/codes/code-form.component.html +++ b/src/app/core/substance-form/codes/code-form.component.html @@ -41,7 +41,7 @@
    - +
    diff --git a/src/app/core/substance-form/codes/code-form.component.scss b/src/app/core/substance-form/codes/code-form.component.scss index 5bde54e11..516203bf3 100644 --- a/src/app/core/substance-form/codes/code-form.component.scss +++ b/src/app/core/substance-form/codes/code-form.component.scss @@ -10,13 +10,13 @@ .resolve { padding: 0px 20px 20px 0px; - color: #4793d1; + color: var(--primary-color); } .chevron { width: 20px; line-height: 67px; - color: rgba(0,0,0,.6); + color: var(--chevron-color); } .chevron-button { width: 20px; @@ -31,12 +31,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .form-row { @@ -67,7 +67,7 @@ font-size: 11px; padding-bottom: 3.5px; line-height: 11px; - color: rgba(0, 0, 0, 0.54); + color: var(--dark-label-color); font-weight: 400; font-family: Roboto, "Helvetica Neue", sans-serif; } diff --git a/src/app/core/substance-form/codes/code-form.component.ts b/src/app/core/substance-form/codes/code-form.component.ts index 60a00ff94..0b864df51 100644 --- a/src/app/core/substance-form/codes/code-form.component.ts +++ b/src/app/core/substance-form/codes/code-form.component.ts @@ -70,6 +70,13 @@ export class CodeFormComponent implements OnInit { } } + trimWhitespace(value) { + + this.code.url = value; + this.privateCode.url = this.privateCode.url.replace(/[\u200A|\u2009|\u2006|\u2008]/g, ' ').trim(); + this.privateCode.url = value.trim().trim().trim(); + } + undoDelete(): void { clearTimeout(this.deleteTimer); delete this.privateCode.$$deletedCode; diff --git a/src/app/core/substance-form/codes/substance-form-codes-card.component.html b/src/app/core/substance-form/codes/substance-form-codes-card.component.html index 68a2506ee..a39b278dd 100644 --- a/src/app/core/substance-form/codes/substance-form-codes-card.component.html +++ b/src/app/core/substance-form/codes/substance-form-codes-card.component.html @@ -20,3 +20,10 @@ + +
    + + +
    diff --git a/src/app/core/substance-form/codes/substance-form-codes-card.component.scss b/src/app/core/substance-form/codes/substance-form-codes-card.component.scss index 91bd3a185..be13a5bc0 100644 --- a/src/app/core/substance-form/codes/substance-form-codes-card.component.scss +++ b/src/app/core/substance-form/codes/substance-form-codes-card.component.scss @@ -3,18 +3,18 @@ } .mat-divider { - border-top-color: rgba(0, 0, 0, .5); + border-top-color: var(--text-color); } .code { &:nth-child(odd) { - background-color: rgba(68, 138, 255, .07); + background-color: var(--nth-child-color-2); ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(68, 138, 255, .15); + background-color: var(--nth-child-color-3); } } } @@ -23,14 +23,14 @@ ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(128, 128, 128, .15); + background-color: var(--nth-child-color-1); } } } ::ng-deep { .mat-expansion-panel, .mat-table, textarea { - background-color: transparent; + background-color: var(--regular-transparent-color); } } } @@ -38,4 +38,4 @@ .search { width: 400px; max-width: 100%; -} \ No newline at end of file +} diff --git a/src/app/core/substance-form/codes/substance-form-codes-card.component.ts b/src/app/core/substance-form/codes/substance-form-codes-card.component.ts index aa94f49aa..5cf6f6a6f 100644 --- a/src/app/core/substance-form/codes/substance-form-codes-card.component.ts +++ b/src/app/core/substance-form/codes/substance-form-codes-card.component.ts @@ -19,6 +19,7 @@ export class SubstanceFormCodesCardComponent extends SubstanceCardBaseFilteredLi private subscriptions: Array = []; pageSize = 10; expanded = true; + validate = false; constructor( private substanceFormCodesService: SubstanceFormCodesService, @@ -41,7 +42,7 @@ export class SubstanceFormCodesCardComponent extends SubstanceCardBaseFilteredLi ngAfterViewInit() { const definitionSubscription = this.substanceFormService.definition.subscribe( level => { if (level.definitionType && level.definitionType === 'ALTERNATIVE') { - this.canAddItemUpdate.emit(false); + // this.canAddItemUpdate.emit(false); this.hiddenStateUpdate.emit(true); } else { this.canAddItemUpdate.emit(true); diff --git a/src/app/core/substance-form/codes/substance-form-codes.module.ts b/src/app/core/substance-form/codes/substance-form-codes.module.ts index a732cef00..68df25d9b 100644 --- a/src/app/core/substance-form/codes/substance-form-codes.module.ts +++ b/src/app/core/substance-form/codes/substance-form-codes.module.ts @@ -12,7 +12,7 @@ import { ReactiveFormsModule, FormsModule } from '@angular/forms'; import { MatPaginatorModule } from '@angular/material/paginator'; import { MatInputModule } from '@angular/material/input'; import { CodeFormComponent } from './code-form.component'; -import { MatTooltipModule } from '@angular/material'; +import { MatTooltipModule } from '@angular/material/tooltip'; @NgModule({ imports: [ diff --git a/src/app/core/substance-form/constituents/constituent-form.component.scss b/src/app/core/substance-form/constituents/constituent-form.component.scss index 9c3269cba..034f287a6 100644 --- a/src/app/core/substance-form/constituents/constituent-form.component.scss +++ b/src/app/core/substance-form/constituents/constituent-form.component.scss @@ -23,12 +23,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .form-row { diff --git a/src/app/core/substance-form/constituents/substance-form-constituents-card.component.html b/src/app/core/substance-form/constituents/substance-form-constituents-card.component.html index 3ae362295..4dc5e2af7 100644 --- a/src/app/core/substance-form/constituents/substance-form-constituents-card.component.html +++ b/src/app/core/substance-form/constituents/substance-form-constituents-card.component.html @@ -9,3 +9,11 @@
    + +
    + + +
    + diff --git a/src/app/core/substance-form/constituents/substance-form-constituents.service.ts b/src/app/core/substance-form/constituents/substance-form-constituents.service.ts index 8956a08c3..4c6b60ee6 100644 --- a/src/app/core/substance-form/constituents/substance-form-constituents.service.ts +++ b/src/app/core/substance-form/constituents/substance-form-constituents.service.ts @@ -15,14 +15,35 @@ export class SubstanceFormConstituentsService extends SubstanceFormServiceBase { + this.substance = substance; + if (this.substance.specifiedSubstance.constituents == null) { + this.substance.specifiedSubstance.constituents = []; + } + this.substanceFormService.resetState(); + this.propertyEmitter.next(this.substance.specifiedSubstance.constituents); + }); + this.subscriptions.push(subscription); + */ + super.initSubtanceForm(); const subscription = this.substanceFormService.substance.subscribe(substance => { this.substance = substance; - if (this.substance.specifiedSubstance.constituents == null) { - this.substance.specifiedSubstance.constituents = []; + if (this.substance.substanceClass === 'specifiedSubstanceG1') { + if (this.substance.specifiedSubstance.constituents == null) { + this.substance.specifiedSubstance.constituents = []; + } + this.substanceFormService.resetState(); + this.propertyEmitter.next(this.substance.specifiedSubstance.constituents); + } else if (this.substance.substanceClass === 'specifiedSubstanceG2') { + if (this.substance.specifiedSubstanceG2.constituents == null) { + this.substance.specifiedSubstanceG2.constituents = []; + } + this.substanceFormService.resetState(); + this.propertyEmitter.next(this.substance.specifiedSubstanceG2.constituents); } - this.substanceFormService.resetState(); - this.propertyEmitter.next(this.substance.specifiedSubstance.constituents); }); this.subscriptions.push(subscription); } @@ -32,9 +53,15 @@ export class SubstanceFormConstituentsService extends SubstanceFormServiceBaseAdd Term to CV - {{vocabulary.domain}}
    +
    true + +
    @@ -16,6 +19,10 @@

    Add Term to CV - {{vocabulary.domain}}



    {{message}}
    +
    CV not valid:
    +
    {{message.messageType}} :{{message.message}}
    + +
    diff --git a/src/app/core/substance-form/cv-dialog/cv-dialog.component.scss b/src/app/core/substance-form/cv-dialog/cv-dialog.component.scss index 2050562a5..4e279c5a9 100644 --- a/src/app/core/substance-form/cv-dialog/cv-dialog.component.scss +++ b/src/app/core/substance-form/cv-dialog/cv-dialog.component.scss @@ -17,3 +17,43 @@ .description { max-width:50%; } + + +.validation { + display:flex; + flex-direction: column; + width: 100%; + } + + + .message { + padding-top: 5px; + padding-left: 15px; + } + + + .ERROR { + background-color: var(--regular-red-color); + color: var(--error-dialog-color); + background-color: var(--error-dialog-bg-color); + padding: 4px 5px; + margin-right: 5px; + border-radius: 5px; +} + + +.WARNING { +color: var(--warning-dialog-color); +background-color: var(--warning-dialog-bg-color); +padding: 4px 5px; +margin-right: 5px; +border-radius: 5px; +} + +.INFO { +color: var(--regular-black-color); +background-color: var(--regular-lightgray-color); +padding: 4px 5px; +margin-right: 5px; +border-radius: 5px; +} diff --git a/src/app/core/substance-form/cv-dialog/cv-dialog.component.ts b/src/app/core/substance-form/cv-dialog/cv-dialog.component.ts index 162e9c436..474231523 100644 --- a/src/app/core/substance-form/cv-dialog/cv-dialog.component.ts +++ b/src/app/core/substance-form/cv-dialog/cv-dialog.component.ts @@ -14,6 +14,7 @@ export class CvDialogComponent implements OnInit { term: VocabularyTerm = {value: '', display: ''}; vocabulary: Vocabulary; message: string; + validationMessages = []; constructor( public cvService: ControlledVocabularyService, @@ -34,6 +35,8 @@ export class CvDialogComponent implements OnInit { submit(): void { let extant = false; + this.message = ''; + this.validationMessages = []; this.vocabulary.terms.forEach(term => { if (term.value === this.term.value) { extant = true; @@ -41,20 +44,64 @@ export class CvDialogComponent implements OnInit { }); if (!extant) { this.vocabulary.terms.push(this.term); - this.cvService.addVocabTerm( this.vocabulary).subscribe (response => { - if (response.terms && response.terms.length === this.vocabulary.terms.length) { - this.message = 'Term ' + this.term.value + ' Added to ' + this.vocabulary.domain + ''; - setTimeout(() => {this.dialogRef.close(this.term); }, 3000); + this.cvService.validateVocab(this.vocabulary).subscribe(response => { + if(response && response.valid) { + this.cvService.addVocabTerm( this.vocabulary).subscribe (response => { + if (response.terms && response.terms.length === this.vocabulary.terms.length) { + this.message = 'Term ' + this.term.value + ' Added to ' + this.vocabulary.domain + ''; + setTimeout(() => {this.dialogRef.close(this.term); }, 3000); + } + }, error => { + console.log(error); + this.vocabulary.terms.pop(); + let str = 'Server Error'; + if (error.error && error.error.message) { + str += ' - ' + error.error.message; + + } + else if(error.message) { + str += ' - ' + error.message; + } + this.message = str; + + }); + + } else { + if(response.validationMessages) { + response.validationMessages.forEach(message => { + this.validationMessages.push(message); + }); + } + this.vocabulary.terms.pop(); } + },error => { + console.log(error); + this.vocabulary.terms.pop(); + let str = 'Validation Error'; + if (error.error && error.error.message) { + str += ' - ' + error.error.message; + + } + else if(error.message) { + str += ' - ' + error.message; + } + this.message = str; + }); + } else { + this.message = 'Term already exists'; setTimeout(() => { this.message = ''; - }, 1000); + }, 3000); } } + updateStructure(value) { + this.term = value; + } + cancel(): void { this.dialogRef.close(); } diff --git a/src/app/core/substance-form/cv-input/cv-input.component.scss b/src/app/core/substance-form/cv-input/cv-input.component.scss index 51193656d..0da2825c5 100644 --- a/src/app/core/substance-form/cv-input/cv-input.component.scss +++ b/src/app/core/substance-form/cv-input/cv-input.component.scss @@ -6,11 +6,11 @@ .custom { padding-bottom: 10px; padding-top:10px; - border-bottom: 1px solid rgba(0, 0, 0, .45); + border-bottom: 1px solid var(--box-shadow-color-6); } .deselect { - color: rgba(0, 0, 0, .5); + color: var(--text-color); } .add-term { @@ -34,6 +34,6 @@ .add-term { - color:#229fdd; + color: var(--secondary-blue); font-style: italic; } diff --git a/src/app/core/substance-form/cv-input/cv-input.component.ts b/src/app/core/substance-form/cv-input/cv-input.component.ts index 30dc0603d..9d74cdba9 100644 --- a/src/app/core/substance-form/cv-input/cv-input.component.ts +++ b/src/app/core/substance-form/cv-input/cv-input.component.ts @@ -8,6 +8,7 @@ import {Subscription} from 'rxjs'; import {CvDialogComponent} from '@gsrs-core/substance-form/cv-dialog/cv-dialog.component'; import {DataDictionaryService} from '@gsrs-core/utils/data-dictionary.service'; import {AuthService} from '@gsrs-core/auth'; +import { FragmentWizardComponent } from '@gsrs-core/admin/fragment-wizard/fragment-wizard.component'; /* used for any input that uses cv vocabulary to handle custom values after selecting 'other' @@ -124,12 +125,29 @@ export class CvInputComponent implements OnInit, OnDestroy { } openDialog(vocab: any, term: string): void { - const dialogRef = this.dialog.open(CvDialogComponent, { - data: {'vocabulary': vocab, 'term': term}, - width: '1040px' - }); - this.overlayContainer.style.zIndex = '1002'; + let thisy = window.pageYOffset; + window.scroll({ + top: 0, + left: 0, + behavior: 'auto' +}); + + // this.overlayContainer = this.overlayContainerService.getContainerElement(); + if (vocab.domain === 'NUCLEIC_ACID_LINKAGE' || vocab.domain === 'NUCLEIC_ACID_SUGAR'){ + this.overlayContainer.style.zIndex = '1005'; + + let dialogRef = this.dialog.open(FragmentWizardComponent, { + data: {'vocabulary': vocab, 'term': term}, + width: '1040px', + height: '85%' + }); + this.overlayContainer.style.zIndex = '1005'; const dialogSubscription = dialogRef.afterClosed().subscribe(response => { + window.scroll({ + top: thisy, + left: 0, + behavior: 'auto' + }); this.overlayContainer.style.zIndex = null; if (response ) { this.privateMod = response.display; @@ -138,5 +156,26 @@ export class CvInputComponent implements OnInit, OnDestroy { } }); this.subscriptions.push(dialogSubscription); + } else { + let dialogRef = this.dialog.open(CvDialogComponent, { + data: {'vocabulary': vocab, 'term': term}, + width: '1040px' + }); + // this.overlayContainer.style.zIndex = '1002'; + const dialogSubscription = dialogRef.afterClosed().subscribe(response => { + // this.overlayContainer.style.zIndex = null; + window.scroll({ + top: thisy, + left: 0, + behavior: 'auto' +}); + if (response ) { + this.privateMod = response.display; + this.vocabulary.push(response); + this.valueChange.emit(this.privateMod); + } + }); + this.subscriptions.push(dialogSubscription); + } } } diff --git a/src/app/core/substance-form/definition-switch-dialog/definition-switch-dialog.component.scss b/src/app/core/substance-form/definition-switch-dialog/definition-switch-dialog.component.scss index 7a8d728be..60258a0db 100644 --- a/src/app/core/substance-form/definition-switch-dialog/definition-switch-dialog.component.scss +++ b/src/app/core/substance-form/definition-switch-dialog/definition-switch-dialog.component.scss @@ -38,7 +38,7 @@ } .button-content { - background-color: white; + background-color: var(--regular-white-color); padding: 5px; width: 210px; display: inline-block; @@ -55,4 +55,4 @@ .new-icon { height:18px; width: 18px; -} \ No newline at end of file +} diff --git a/src/app/core/substance-form/definition-switch-dialog/definition-switch-dialog.component.ts b/src/app/core/substance-form/definition-switch-dialog/definition-switch-dialog.component.ts index a969096aa..05eb186b5 100644 --- a/src/app/core/substance-form/definition-switch-dialog/definition-switch-dialog.component.ts +++ b/src/app/core/substance-form/definition-switch-dialog/definition-switch-dialog.component.ts @@ -5,8 +5,8 @@ import { SubstanceFormService } from '@gsrs-core/substance-form/substance-form.s import { DomSanitizer } from '@angular/platform-browser'; import * as _ from 'lodash'; import * as defiant from '../../../../../node_modules/defiant.js/dist/defiant.min.js'; -import { LoadingService } from '@gsrs-core/loading/index.js'; -import { UtilsService } from '@gsrs-core/utils/index.js'; +import { LoadingService } from '@gsrs-core/loading/index'; +import { UtilsService } from '@gsrs-core/utils/index'; @Component( { selector: 'app-definition-switch-dialog', @@ -75,13 +75,13 @@ export class DefinitionSwitchDialogComponent implements OnInit { this.structureid = this.utilsService.newUUID(); this.alt = {}; - this.currentAlts = _.chain(this.sub.relationships).filter(function (r) { + this.currentAlts = this.sub.relationships.map(r=>r).filter(function (r) { if (r.type === 'SUBSTANCE->SUB_ALTERNATE') { return r; } }).map(function (r) { return r['relatedSubstance']; - }).value(); + }); if (this.currentAlts.length > 0) { this.text = 'Select a substance to switch'; @@ -414,7 +414,7 @@ export class DefinitionSwitchDialogComponent implements OnInit { console.log('SENT SUBSTANCE'); console.log(nsub); let errorString = 'VALIDATION ERROR - \n\n'; - if (error && error.error) { + if (error && error.error && error.error.validationMessages) { error.error.validationMessages.forEach( e => { if (e.messageType === 'ERROR') { errorString += e.message + '\n\n'; @@ -422,6 +422,8 @@ export class DefinitionSwitchDialogComponent implements OnInit { }); } else if (error && error.message) { errorString += error.message; + } else { + errorString += "unknown server error"; } this.error = errorString; this.undo(step); diff --git a/src/app/core/substance-form/disulfide-links/disulfide-links-form.component.scss b/src/app/core/substance-form/disulfide-links/disulfide-links-form.component.scss index 351f9f8d5..abf786ca2 100644 --- a/src/app/core/substance-form/disulfide-links/disulfide-links-form.component.scss +++ b/src/app/core/substance-form/disulfide-links/disulfide-links-form.component.scss @@ -6,12 +6,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .link-form-container { diff --git a/src/app/core/substance-form/disulfide-links/substance-form-disulfide-links-card.component.html b/src/app/core/substance-form/disulfide-links/substance-form-disulfide-links-card.component.html index b655d24bb..360d3f457 100644 --- a/src/app/core/substance-form/disulfide-links/substance-form-disulfide-links-card.component.html +++ b/src/app/core/substance-form/disulfide-links/substance-form-disulfide-links-card.component.html @@ -28,6 +28,14 @@
    +
    + + +
    + + diff --git a/src/app/core/substance-form/disulfide-links/substance-form-disulfide-links-card.component.scss b/src/app/core/substance-form/disulfide-links/substance-form-disulfide-links-card.component.scss index 0367dbab3..8ad7063cf 100644 --- a/src/app/core/substance-form/disulfide-links/substance-form-disulfide-links-card.component.scss +++ b/src/app/core/substance-form/disulfide-links/substance-form-disulfide-links-card.component.scss @@ -12,12 +12,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .form-row2 { @@ -64,23 +64,23 @@ .alternate-backgrounds2 { &:nth-child(4n) { - background-color: rgba(68, 138, 255, .07); + background-color: var(--nth-child-color-2); ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(68, 138, 255, .15); + background-color: var(--nth-child-color-3); } } } &:nth-child(4n + 3) { - background-color: rgba(68, 138, 255, .07); + background-color: var(--nth-child-color-2); ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(68, 138, 255, .15); + background-color: var(--nth-child-color-3); } } } @@ -89,7 +89,7 @@ ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(128, 128, 128, .15); + background-color: var(--nth-child-color-1); } } } @@ -98,14 +98,14 @@ ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(128, 128, 128, .15); + background-color: var(--nth-child-color-1); } } } ::ng-deep { .mat-expansion-panel, .mat-table, textarea { - background-color: transparent; + background-color: var(--regular-transparent-color); } } } diff --git a/src/app/core/substance-form/disulfide-links/substance-form-disulfide-links.module.ts b/src/app/core/substance-form/disulfide-links/substance-form-disulfide-links.module.ts index 712a291a6..34e8ed6b6 100644 --- a/src/app/core/substance-form/disulfide-links/substance-form-disulfide-links.module.ts +++ b/src/app/core/substance-form/disulfide-links/substance-form-disulfide-links.module.ts @@ -12,10 +12,10 @@ import {MatPaginatorModule} from '@angular/material/paginator'; import {MatInputModule} from '@angular/material/input'; import {MatTooltipModule} from '@angular/material/tooltip'; import {MatButtonToggleModule} from '@angular/material/button-toggle'; -// tslint:disable-next-line:max-line-length +// eslint-disable-next-line max-len import {SubstanceFormDisulfideLinksCardComponent} from '@gsrs-core/substance-form/disulfide-links/substance-form-disulfide-links-card.component'; import { DisulfideLinksFormComponent } from './disulfide-links-form.component'; -import { MatSelectModule } from '@angular/material'; +import { MatSelectModule } from '@angular/material/select'; @NgModule({ imports: [ diff --git a/src/app/core/substance-form/form-sections.constant.ts b/src/app/core/substance-form/form-sections.constant.ts index 9e7ec76c5..2832f4dc0 100644 --- a/src/app/core/substance-form/form-sections.constant.ts +++ b/src/app/core/substance-form/form-sections.constant.ts @@ -117,6 +117,13 @@ export const formSections: { [substanceType: string]: Array } = { 'substance-form-references', 'substance-form-change-reason' ], + specifiedSubstanceG2: [ + 'substance-form-ssg2-overview', + 'substance-form-names', + 'substance-form-constituents', + 'substance-form-ssg2-manufacturing', + 'substance-form-references' + ], specifiedSubstanceG3: [ 'substance-form-definition', 'substance-form-names', @@ -126,5 +133,10 @@ export const formSections: { [substanceType: string]: Array } = { 'substance-form-codes-card', 'substance-form-notes', 'substance-form-references' + ], + specifiedSubstanceG4m: [ + 'substance-form-references', + 'substance-form-ssg-parent-substance', + 'substance-form-ssg4m-process', ] }; diff --git a/src/app/core/substance-form/glycosylation/substance-form-glycosylation.component.scss b/src/app/core/substance-form/glycosylation/substance-form-glycosylation.component.scss index a2584fd46..333dca2c4 100644 --- a/src/app/core/substance-form/glycosylation/substance-form-glycosylation.component.scss +++ b/src/app/core/substance-form/glycosylation/substance-form-glycosylation.component.scss @@ -6,12 +6,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .sites { diff --git a/src/app/core/substance-form/glycosylation/substance-form-glycosylation.component.ts b/src/app/core/substance-form/glycosylation/substance-form-glycosylation.component.ts index 1fe218702..1a8a741ab 100644 --- a/src/app/core/substance-form/glycosylation/substance-form-glycosylation.component.ts +++ b/src/app/core/substance-form/glycosylation/substance-form-glycosylation.component.ts @@ -14,7 +14,7 @@ import { SubstanceFormGlycosylationService } from './substance-form-glycosylatio templateUrl: './substance-form-glycosylation.component.html', styleUrls: ['./substance-form-glycosylation.component.scss'] }) -// tslint:disable-next-line:max-line-length +// eslint-disable-next-line max-len export class SubstanceFormGlycosylationComponent extends SubstanceCardBaseFilteredList implements OnInit, AfterViewInit, OnDestroy { glycosylation: Glycosylation; glycosylationTypes: Array; diff --git a/src/app/core/substance-form/json-dialog/json-dialog.component.ts b/src/app/core/substance-form/json-dialog/json-dialog.component.ts index b5010f007..14f0f61b2 100644 --- a/src/app/core/substance-form/json-dialog/json-dialog.component.ts +++ b/src/app/core/substance-form/json-dialog/json-dialog.component.ts @@ -23,7 +23,9 @@ export class JsonDialogComponent implements OnInit { ) { } ngOnInit() { - this.json = this.substanceFormService.getJson(); + // apply the same cleaning to remove deleted objects and return what will be sent to the server on validation / submission + this.json = this.substanceFormService.cleanSubstance(); + // this.json = this.cleanObject(substanceCopy); const uri = this.sanitizer.bypassSecurityTrustUrl('data:text/json;charset=UTF-8,' + encodeURIComponent(JSON.stringify(this.json))); this.downloadJsonHref = uri; } diff --git a/src/app/core/substance-form/links/link-form.component.html b/src/app/core/substance-form/links/link-form.component.html index b5f6008b8..919eaac15 100644 --- a/src/app/core/substance-form/links/link-form.component.html +++ b/src/app/core/substance-form/links/link-form.component.html @@ -11,8 +11,11 @@
    +
    + +
    - +
    +
    diff --git a/src/app/core/substance-form/links/substance-form-links_card.component.scss b/src/app/core/substance-form/links/substance-form-links_card.component.scss index 94d06be0c..4553cb9e7 100644 --- a/src/app/core/substance-form/links/substance-form-links_card.component.scss +++ b/src/app/core/substance-form/links/substance-form-links_card.component.scss @@ -8,5 +8,5 @@ } .too-many { - color:red; + color:var(--regular-red-color); } diff --git a/src/app/core/substance-form/merge-concept-dialog/merge-concept-dialog.component.ts b/src/app/core/substance-form/merge-concept-dialog/merge-concept-dialog.component.ts index 63b469950..73b7ec022 100644 --- a/src/app/core/substance-form/merge-concept-dialog/merge-concept-dialog.component.ts +++ b/src/app/core/substance-form/merge-concept-dialog/merge-concept-dialog.component.ts @@ -151,7 +151,7 @@ export class MergeConceptDialogComponent implements OnInit { addAll(oldSub.references, newSub.references); oldSub.changeReason = 'Merged with ' + newBdnum; // setJson(oldSub); - this.substanceFormService.loadSubstance(oldSub.substanceClass, oldSub); + this.substanceFormService.loadSubstance(oldSub.substanceClass, oldSub, null, true); this.addmergebutton = true; this.text = 'Fields merged. Click \'Confirm Deprecate old record\' to to prevent duplicate collision'; this.oldBdnum = oldBdnum; @@ -204,11 +204,11 @@ export class MergeConceptDialogComponent implements OnInit { concept.changeReason = 'Migrated data into:' + this.oldBdnum; this.loading = false; this.text = 'Deprecating...'; - this.substanceService.saveSubstance(concept).subscribe(response => { + this.substanceService.saveSubstance(concept).subscribe(response => { this.loading = false; this.text = 'Old record deprecated, please save this record to complete the merge.'; this.subconcepts = undefined; - this.addmergebutton = false; + this.addmergebutton = false; }, error => { this.loading = false; this.text = 'There was a problem deprecating the old record. Refresh the page to undo the changes to the parent record.'; diff --git a/src/app/core/substance-form/mixture-components/mixture-component-form.component.html b/src/app/core/substance-form/mixture-components/mixture-component-form.component.html index 4a855ca3e..0b31a6ef1 100644 --- a/src/app/core/substance-form/mixture-components/mixture-component-form.component.html +++ b/src/app/core/substance-form/mixture-components/mixture-component-form.component.html @@ -20,7 +20,8 @@
    - + + *required
    diff --git a/src/app/core/substance-form/mixture-components/mixture-component-form.component.scss b/src/app/core/substance-form/mixture-components/mixture-component-form.component.scss index dfbc82668..b48214638 100644 --- a/src/app/core/substance-form/mixture-components/mixture-component-form.component.scss +++ b/src/app/core/substance-form/mixture-components/mixture-component-form.component.scss @@ -29,3 +29,24 @@ max-width:125px !important; margin: auto; } + + +.invalid > * { + color: var(--regular-red-color); +} + + +.invalid ::ng-deep .mat-form-field-label { + color: var(--regular-red-color); +} + +.invalid ::ng-deep .mat-select-arrow { + color: var(--regular-red-color); +} + +.invalid-note { + margin-bottom: 18px; + font-style: italic; + font-size: 14px; + color: var(--regular-red-color); +} diff --git a/src/app/core/substance-form/mixture-components/mixture-component-form.component.ts b/src/app/core/substance-form/mixture-components/mixture-component-form.component.ts index 1b7c2667b..7817940dd 100644 --- a/src/app/core/substance-form/mixture-components/mixture-component-form.component.ts +++ b/src/app/core/substance-form/mixture-components/mixture-component-form.component.ts @@ -1,4 +1,4 @@ -import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; +import {Component, EventEmitter, Input, OnInit, Output, HostListener} from '@angular/core'; import {MixtureComponents, SubstanceRelated, SubstanceSummary} from '@gsrs-core/substance'; import {UtilsService} from '@gsrs-core/utils'; import {OverlayContainer} from '@angular/cdk/overlay'; @@ -15,6 +15,15 @@ export class MixtureComponentFormComponent implements OnInit { relatedSubstanceUuid: string; private overlayContainer: HTMLElement; siteDisplay: string; + invalid = false; + + @HostListener('focusout') onFocusout() { + if (!this.privateComp.type) { + this.invalid = true; + } else { + this.invalid = false; + } + } constructor( private utilsService: UtilsService, @@ -39,8 +48,15 @@ export class MixtureComponentFormComponent implements OnInit { updateType(event: any): void { this.privateComp.type = event; + if (!this.privateComp.type) { + this.invalid = true; + } else { + this.invalid = false; + } } + + deleteComponent(): void { this.privateComp.$$deletedCode = this.utilsService.newUUID(); diff --git a/src/app/core/substance-form/mixture-components/substance-form-mixture-components-card.component.html b/src/app/core/substance-form/mixture-components/substance-form-mixture-components-card.component.html index 9fc97c7cb..36a3ba945 100644 --- a/src/app/core/substance-form/mixture-components/substance-form-mixture-components-card.component.html +++ b/src/app/core/substance-form/mixture-components/substance-form-mixture-components-card.component.html @@ -10,3 +10,15 @@
    +
    + +
    + +
    + + +
    diff --git a/src/app/core/substance-form/mixture-details/substance-form-mixture-details.component.html b/src/app/core/substance-form/mixture-details/substance-form-mixture-details.component.html index e206690e4..e4a68893b 100644 --- a/src/app/core/substance-form/mixture-details/substance-form-mixture-details.component.html +++ b/src/app/core/substance-form/mixture-details/substance-form-mixture-details.component.html @@ -5,8 +5,8 @@ [subuuid]="relatedSubstanceUuid">
    - - +
    diff --git a/src/app/core/substance-form/mixture-details/substance-form-mixture-details.component.ts b/src/app/core/substance-form/mixture-details/substance-form-mixture-details.component.ts index 7205f6fb7..bdec21390 100644 --- a/src/app/core/substance-form/mixture-details/substance-form-mixture-details.component.ts +++ b/src/app/core/substance-form/mixture-details/substance-form-mixture-details.component.ts @@ -13,7 +13,7 @@ import { SubstanceFormBase } from '../base-classes/substance-form-base'; templateUrl: './substance-form-mixture-details.component.html', styleUrls: ['./substance-form-mixture-details.component.scss'] }) -// tslint:disable-next-line:max-line-length +// eslint-disable-next-line max-len export class SubstanceFormMixtureDetailsComponent extends SubstanceFormBase implements OnInit, AfterViewInit, OnDestroy { parent: SubstanceRelated; @@ -60,15 +60,21 @@ export class SubstanceFormMixtureDetailsComponent extends SubstanceFormBase imp } parentSubstanceUpdated(substance: SubstanceSummary): void { - const relatedSubstance: SubstanceRelated = { - refPname: substance._name, - name: substance._name, - refuuid: substance.uuid, - substanceClass: 'reference', - approvalID: substance.approvalID - }; - this.mixture.parentSubstance = relatedSubstance; - this.relatedSubstanceUuid = relatedSubstance && relatedSubstance.refuuid || ''; + + if (substance !== null) { + const relatedSubstance: SubstanceRelated = { + refPname: substance._name, + name: substance._name, + refuuid: substance.uuid, + substanceClass: 'reference', + approvalID: substance.approvalID + }; + this.mixture.parentSubstance = relatedSubstance; + this.relatedSubstanceUuid = relatedSubstance && relatedSubstance.refuuid || ''; + } else { + this.mixture.parentSubstance = null; + this.relatedSubstanceUuid = null; + } } } diff --git a/src/app/core/substance-form/moieties/substance-form-moieties.component.scss b/src/app/core/substance-form/moieties/substance-form-moieties.component.scss index 0afc185b3..7faed26df 100644 --- a/src/app/core/substance-form/moieties/substance-form-moieties.component.scss +++ b/src/app/core/substance-form/moieties/substance-form-moieties.component.scss @@ -3,18 +3,18 @@ } .mat-divider { - border-top-color: rgba(0, 0, 0, .5); + border-top-color: var(--text-color); } .moiety { &:nth-child(odd) { - background-color: rgba(68, 138, 255, .07); + background-color: var(--nth-child-color-2); ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(68, 138, 255, .15); + background-color: var(--nth-child-color-3); } } } @@ -23,14 +23,14 @@ ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(128, 128, 128, .15); + background-color: var(--nth-child-color-1); } } } ::ng-deep { .mat-expansion-panel, .mat-table, textarea { - background-color: transparent; + background-color: var(--regular-transparent-color); } } } @@ -63,4 +63,4 @@ .amount-form-container { padding: 0 7px; -} \ No newline at end of file +} diff --git a/src/app/core/substance-form/monomers/monomer-form.component.scss b/src/app/core/substance-form/monomers/monomer-form.component.scss index 8dddd24dd..3a2722429 100644 --- a/src/app/core/substance-form/monomers/monomer-form.component.scss +++ b/src/app/core/substance-form/monomers/monomer-form.component.scss @@ -6,12 +6,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .component-form-container { diff --git a/src/app/core/substance-form/monomers/monomer-form.component.ts b/src/app/core/substance-form/monomers/monomer-form.component.ts index c7ceb93c8..376b92b1d 100644 --- a/src/app/core/substance-form/monomers/monomer-form.component.ts +++ b/src/app/core/substance-form/monomers/monomer-form.component.ts @@ -74,15 +74,22 @@ export class MonomerFormComponent implements OnInit, AfterViewInit { } componentUpdated(substance: SubstanceSummary): void { - const relatedSubstance: SubstanceRelated = { - refPname: substance._name, - name: substance._name, - refuuid: substance.uuid, - substanceClass: 'reference', - approvalID: substance.approvalID - }; - this.privateMonomer.monomerSubstance = relatedSubstance; - this.relatedSubstanceUuid = this.privateMonomer.monomerSubstance.refuuid; + + if (substance !== null) { + const relatedSubstance: SubstanceRelated = { + refPname: substance._name, + name: substance._name, + refuuid: substance.uuid, + substanceClass: 'reference', + approvalID: substance.approvalID + }; + this.privateMonomer.monomerSubstance = relatedSubstance; + this.relatedSubstanceUuid = this.privateMonomer.monomerSubstance.refuuid; + } else { + this.privateMonomer.monomerSubstance = null; + this.relatedSubstanceUuid = null; + } + } diff --git a/src/app/core/substance-form/monomers/substance-form-monomers-card.component.html b/src/app/core/substance-form/monomers/substance-form-monomers-card.component.html index ed93514f4..104f77342 100644 --- a/src/app/core/substance-form/monomers/substance-form-monomers-card.component.html +++ b/src/app/core/substance-form/monomers/substance-form-monomers-card.component.html @@ -9,3 +9,10 @@
    + +
    + + +
    diff --git a/src/app/core/substance-form/monomers/substance-form-monomers-card.component.scss b/src/app/core/substance-form/monomers/substance-form-monomers-card.component.scss index 91bd3a185..be13a5bc0 100644 --- a/src/app/core/substance-form/monomers/substance-form-monomers-card.component.scss +++ b/src/app/core/substance-form/monomers/substance-form-monomers-card.component.scss @@ -3,18 +3,18 @@ } .mat-divider { - border-top-color: rgba(0, 0, 0, .5); + border-top-color: var(--text-color); } .code { &:nth-child(odd) { - background-color: rgba(68, 138, 255, .07); + background-color: var(--nth-child-color-2); ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(68, 138, 255, .15); + background-color: var(--nth-child-color-3); } } } @@ -23,14 +23,14 @@ ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(128, 128, 128, .15); + background-color: var(--nth-child-color-1); } } } ::ng-deep { .mat-expansion-panel, .mat-table, textarea { - background-color: transparent; + background-color: var(--regular-transparent-color); } } } @@ -38,4 +38,4 @@ .search { width: 400px; max-width: 100%; -} \ No newline at end of file +} diff --git a/src/app/core/substance-form/monomers/substance-form-monomers-card.component.ts b/src/app/core/substance-form/monomers/substance-form-monomers-card.component.ts index 194e9fc0a..15910b1c2 100644 --- a/src/app/core/substance-form/monomers/substance-form-monomers-card.component.ts +++ b/src/app/core/substance-form/monomers/substance-form-monomers-card.component.ts @@ -58,5 +58,4 @@ export class SubstanceFormMonomersCardComponent extends SubstanceCardBaseFiltere deleteMonomer(monomer: Monomer): void { this.substanceFormMonomersService.deleteSubstanceMonomer(monomer); } - } diff --git a/src/app/core/substance-form/monomers/substance-form-monomers.module.ts b/src/app/core/substance-form/monomers/substance-form-monomers.module.ts index 4fc8ac0a3..c1bebbec7 100644 --- a/src/app/core/substance-form/monomers/substance-form-monomers.module.ts +++ b/src/app/core/substance-form/monomers/substance-form-monomers.module.ts @@ -13,7 +13,7 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatInputModule } from '@angular/material/input'; import { MonomerFormComponent } from './monomer-form.component'; import { SubstanceSelectorModule } from '@gsrs-core/substance-selector/substance-selector.module'; -import { MatCheckboxModule } from '@angular/material'; +import { MatCheckboxModule } from '@angular/material/checkbox'; @NgModule({ imports: [ diff --git a/src/app/core/substance-form/names/name-form.component.html b/src/app/core/substance-form/names/name-form.component.html index f19461ffd..63c697559 100644 --- a/src/app/core/substance-form/names/name-form.component.html +++ b/src/app/core/substance-form/names/name-form.component.html @@ -14,13 +14,13 @@
    - PN + matTooltip="{{ 'displayNameTitle' | elementLabel : 'substance_names_name' }}"> + {{ 'displayNameShortTitle' | elementLabel : 'substance_names_name' }}
    - - AL + + {{ 'preferredShortTitle' | elementLabel : 'substance_names_name' }}
    diff --git a/src/app/core/substance-form/names/name-form.component.scss b/src/app/core/substance-form/names/name-form.component.scss index cc7f4617b..ecdb2e099 100644 --- a/src/app/core/substance-form/names/name-form.component.scss +++ b/src/app/core/substance-form/names/name-form.component.scss @@ -10,13 +10,13 @@ .resolve { padding: 0px 20px 20px 0px; - color: #4793d1; + color: var(--primary-color); } .chevron { width: 20px; line-height: 67px; - color: rgba(0,0,0,.6); + color: var(--chevron-color); } .chevron-button { width: 20px; @@ -31,12 +31,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .form-row { @@ -79,7 +79,7 @@ padding-left: 0; font-size: 11px; padding-bottom: 4px; - color: rgba(0, 0, 0, 0.54); + color: var(--dark-label-color); font-weight: 400; font-family: Roboto, "Helvetica Neue", sans-serif; } @@ -100,7 +100,7 @@ padding-left: 0; font-size: 11px; padding-bottom: 2px; - color: rgba(0, 0, 0, 0.54); + color: var(--dark-label-color); font-weight: 400; font-family: Roboto, "Helvetica Neue", sans-serif; } diff --git a/src/app/core/substance-form/names/name-form.component.ts b/src/app/core/substance-form/names/name-form.component.ts index 0c9896ec7..42e55ba9b 100644 --- a/src/app/core/substance-form/names/name-form.component.ts +++ b/src/app/core/substance-form/names/name-form.component.ts @@ -45,7 +45,7 @@ export class NameFormComponent implements OnInit, OnDestroy { this.substanceType = def.substanceClass; }); definition.unsubscribe(); - + } diff --git a/src/app/core/substance-form/names/substance-form-names-card.component.html b/src/app/core/substance-form/names/substance-form-names-card.component.html index 4b741710a..e207affa9 100644 --- a/src/app/core/substance-form/names/substance-form-names-card.component.html +++ b/src/app/core/substance-form/names/substance-form-names-card.component.html @@ -3,7 +3,7 @@ - + - +
    @@ -33,3 +31,9 @@ +
    + + +
    diff --git a/src/app/core/substance-form/names/substance-form-names-card.component.scss b/src/app/core/substance-form/names/substance-form-names-card.component.scss index 3f87e46f5..78bddaa28 100644 --- a/src/app/core/substance-form/names/substance-form-names-card.component.scss +++ b/src/app/core/substance-form/names/substance-form-names-card.component.scss @@ -3,7 +3,7 @@ } .mat-divider { - border-top-color: rgba(0, 0, 0, .5); + border-top-color: var(--text-color); } .checkbox { @@ -13,12 +13,12 @@ .name { &:nth-child(odd) { - background-color: rgba(68, 138, 255, .07); + background-color: var(--nth-child-color-2); ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(68, 138, 255, .15); + background-color: var(--nth-child-color-3); } } } @@ -27,14 +27,14 @@ ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(128, 128, 128, .15); + background-color: var(--nth-child-color-1); } } } ::ng-deep { .mat-expansion-panel, .mat-table, textarea { - background-color: transparent; + background-color: var(--regular-transparent-color); } } } @@ -42,4 +42,4 @@ .search { width: 400px; max-width: 100%; -} \ No newline at end of file +} diff --git a/src/app/core/substance-form/names/substance-form-names-card.component.ts b/src/app/core/substance-form/names/substance-form-names-card.component.ts index 99de71ba3..136f2cf09 100644 --- a/src/app/core/substance-form/names/substance-form-names-card.component.ts +++ b/src/app/core/substance-form/names/substance-form-names-card.component.ts @@ -20,9 +20,10 @@ export class SubstanceFormNamesCardComponent private subscriptions: Array = []; pageSize = 10; expanded = true; - showStd = false; + showStd = true; showMore = false; appId: string; + standardizeButton = false; constructor( private substanceFormNamesService: SubstanceFormNamesService, @@ -39,9 +40,10 @@ export class SubstanceFormNamesCardComponent ngOnInit() { this.menuLabelUpdate.emit('Names'); this.appId = this.configService.environment.appId; + this.standardizeButton = this.configService.configData.showNameStandardizeButton || false; const definitionSubscription = this.substanceFormService.definition.subscribe( level => { if (level.definitionType && level.definitionType === 'ALTERNATIVE') { - this.canAddItemUpdate.emit(false); + // this.canAddItemUpdate.emit(false); this.hiddenStateUpdate.emit(true); } else { this.canAddItemUpdate.emit(true); @@ -50,6 +52,7 @@ export class SubstanceFormNamesCardComponent }); this.subscriptions.push(definitionSubscription); const namesSubscription = this.substanceFormNamesService.substanceNames.subscribe(names => { + this.names = names; this.filtered = names; const searchSubscription = this.searchControl.valueChanges.subscribe(value => { @@ -65,6 +68,7 @@ export class SubstanceFormNamesCardComponent } ngAfterViewInit() { + } @@ -73,7 +77,8 @@ export class SubstanceFormNamesCardComponent } standardize(): void { - this.substanceFormNamesService.standardizeNames(); + // We currently only want the back-end to standardize names + // this.substanceFormNamesService.standardizeNames(); } ngOnDestroy() { diff --git a/src/app/core/substance-form/names/substance-form-names.module.ts b/src/app/core/substance-form/names/substance-form-names.module.ts index 200720770..4245dc7dc 100644 --- a/src/app/core/substance-form/names/substance-form-names.module.ts +++ b/src/app/core/substance-form/names/substance-form-names.module.ts @@ -13,8 +13,15 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatInputModule } from '@angular/material/input'; import { NameFormComponent } from './name-form.component'; import { NameOrgsComponent } from './name-orgs.component'; +import {MatRadioModule} from '@angular/material/radio'; +import {MatCheckboxModule} from '@angular/material/checkbox'; +import {MatBadgeModule} from '@angular/material/badge'; +import {MatExpansionModule} from '@angular/material/expansion'; +import {MatTableModule} from '@angular/material/table'; +import {MatSelectModule} from '@angular/material/select'; +import {MatTooltipModule} from '@angular/material/tooltip'; +import { ElementLabelDisplayModule } from '@gsrs-core/utils/element-label-display.module'; -import { MatRadioModule, MatCheckboxModule, MatBadgeModule, MatExpansionModule, MatTableModule, MatSelectModule, MatTooltipModule } from '@angular/material'; @NgModule({ imports: [ @@ -36,12 +43,14 @@ import { MatRadioModule, MatCheckboxModule, MatBadgeModule, MatExpansionModule, MatTableModule, MatBadgeModule, MatSelectModule, - MatTooltipModule + MatTooltipModule, + ElementLabelDisplayModule ], declarations: [ SubstanceFormNamesCardComponent, NameFormComponent, NameOrgsComponent + ] }) export class SubstanceFormNamesModule { } diff --git a/src/app/core/substance-form/names/substance-form-names.service.ts b/src/app/core/substance-form/names/substance-form-names.service.ts index 2b549363f..91af800d6 100644 --- a/src/app/core/substance-form/names/substance-form-names.service.ts +++ b/src/app/core/substance-form/names/substance-form-names.service.ts @@ -25,6 +25,23 @@ export class SubstanceFormNamesService extends SubstanceFormServiceBase b.name) { + return 1; + } + return 0; + } + }); this.substanceFormService.resetState(); this.propertyEmitter.next(this.substance.names); }); @@ -59,7 +76,7 @@ export class SubstanceFormNamesService extends SubstanceFormServiceBase;\xB1;+/-;\u2190;<-;\xB2;2;\xB3;3;\xB9;1;\u2070;0;\u2071;1;\u2072;2;\u2073;3;\u2074;4;\u2075;5;\u2076;6;\u2077;7;\u2078;8;\u2079;9;\u207A;+;\u207B;-;\u2080;0;\u2081;1;\u2082;2;\u2083;3;\u2084;4;\u2085;5;\u2086;6;\u2087;7;\u2088;8;\u2089;9;\u208A;+;\u208B;-'.split(';'); + const rep = '\u2032;\';\u201b;\';\u2018;\';\u2019;\';\u03B1;.ALPHA.;\u03B2;.BETA.;\u03B3;.GAMMA.;\u03B4;.DELTA.;\u03B5;.EPSILON.;\u03B6;.ZETA.;\u03B7;.ETA.;\u03B8;.THETA.;\u03B9;.IOTA.;\u03BA;.KAPPA.;\u03BB;.LAMBDA.;\u03BC;.MU.;\u03BD;.NU.;\u03BE;.XI.;\u03BF;.OMICRON.;\u03C0;.PI.;\u03C1;.RHO.;\u03C2;.SIGMA.;\u03C3;.SIGMA.;\u03C4;.TAU.;\u03C5;.UPSILON.;\u03C6;.PHI.;\u03C7;.CHI.;\u03C8;.PSI.;\u03C9;.OMEGA.;\u0391;.ALPHA.;\u0392;.BETA.;\u0393;.GAMMA.;\u0394;.DELTA.;\u0395;.EPSILON.;\u0396;.ZETA.;\u0397;.ETA.;\u0398;.THETA.;\u0399;.IOTA.;\u039A;.KAPPA.;\u039B;.LAMBDA.;\u039C;.MU.;\u039D;.NU.;\u039E;.XI.;\u039F;.OMICRON.;\u03A0;.PI.;\u03A1;.RHO.;\u03A3;.SIGMA.;\u03A4;.TAU.;\u03A5;.UPSILON.;\u03A6;.PHI.;\u03A7;.CHI.;\u03A8;.PSI.;\u03A9;.OMEGA.;\u2192;->;\xB1;+/-;\u2190;<-;\xB2;2;\xB3;3;\xB9;1;\u2070;0;\u2071;1;\u2072;2;\u2073;3;\u2074;4;\u2075;5;\u2076;6;\u2077;7;\u2078;8;\u2079;9;\u207A;+;\u207B;-;\u2080;0;\u2081;1;\u2082;2;\u2083;3;\u2084;4;\u2085;5;\u2086;6;\u2087;7;\u2088;8;\u2089;9;\u208A;+;\u208B;-;\u002d;-;\u058a;-;\u05be;-;\u1400;-;\u1806;-;\u2011;-;\u2012;-;\u2013;-;\u2014;-;\u2015;-;\u2e17;-;\u2e1a;-;\u2e3a;-;\u2e3b;-;\u2e40;-;\u301c;-;\u3030;-;\u30a0;-;\ufe31;-;\ufe32;-;\ufe58;-;\ufe63;-;\uff0d;-;\u10ead;-;\u2010;-;\u2122;-;'.split(';'); const map = {}; for (let s = 0; s < rep.length; s++) { if (s % 2 === 0) { @@ -69,9 +86,9 @@ export class SubstanceFormNamesService extends SubstanceFormServiceBase { if (n.name) { let name = n.name; - name = name.replace(/([\u0390-\u03C9||\u2192|\u00B1-\u00B9|\u2000-\u208F|\u2190|])/g, replacer).trim(); + name = name.replace(/([\u002d|\u0390-\u03C9|\u2122|\u2192|\u00B1-\u00B9|\u2000-\u208F|\u2e17-\u2e40|\u301c-\u30a0|\ufe31-\uff0d|\u2190|])/g, replacer).trim(); + name = name.replace(' -', '-'); + name = name.replace('- ', '-'); name = name.replace(bad, ''); name = name.replace(/[[]([A-Z -.]*)\]$/g, ' !!@!$1_!@!'); name = name.replace(/[ \t]+/g, ' '); diff --git a/src/app/core/substance-form/notes/note-form.component.scss b/src/app/core/substance-form/notes/note-form.component.scss index 5a2a72864..41cb19838 100644 --- a/src/app/core/substance-form/notes/note-form.component.scss +++ b/src/app/core/substance-form/notes/note-form.component.scss @@ -11,12 +11,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .form-row { @@ -37,4 +37,4 @@ .references-container { width: 100%; -} \ No newline at end of file +} diff --git a/src/app/core/substance-form/notes/substance-form-notes-card.component.html b/src/app/core/substance-form/notes/substance-form-notes-card.component.html index 1f7a62ee2..0667b7b8e 100644 --- a/src/app/core/substance-form/notes/substance-form-notes-card.component.html +++ b/src/app/core/substance-form/notes/substance-form-notes-card.component.html @@ -17,4 +17,12 @@
    - \ No newline at end of file + + + +
    + + +
    \ No newline at end of file diff --git a/src/app/core/substance-form/notes/substance-form-notes-card.component.scss b/src/app/core/substance-form/notes/substance-form-notes-card.component.scss index a66a12f40..3e67ef4e1 100644 --- a/src/app/core/substance-form/notes/substance-form-notes-card.component.scss +++ b/src/app/core/substance-form/notes/substance-form-notes-card.component.scss @@ -3,18 +3,18 @@ } .mat-divider { - border-top-color: rgba(0, 0, 0, .5); + border-top-color: var(--text-color); } .note { &:nth-child(odd) { - background-color: rgba(68, 138, 255, .07); + background-color: var(--nth-child-color-2); ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(68, 138, 255, .15); + background-color: var(--nth-child-color-3); } } } @@ -23,14 +23,14 @@ ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(128, 128, 128, .15); + background-color: var(--nth-child-color-1); } } } ::ng-deep { .mat-expansion-panel, .mat-table, textarea { - background-color: transparent; + background-color: var(--regular-transparent-color); } } } @@ -38,4 +38,4 @@ .search { width: 400px; max-width: 100%; -} \ No newline at end of file +} diff --git a/src/app/core/substance-form/nucleic-acid-details-form/nucleic-acid-details-form.component.html b/src/app/core/substance-form/nucleic-acid-details-form/nucleic-acid-details-form.component.html index 80030d85f..1eeff346d 100644 --- a/src/app/core/substance-form/nucleic-acid-details-form/nucleic-acid-details-form.component.html +++ b/src/app/core/substance-form/nucleic-acid-details-form/nucleic-acid-details-form.component.html @@ -9,8 +9,8 @@ - - +
    diff --git a/src/app/core/substance-form/nucleic-acid-details-form/nucleic-acid-details-form.component.scss b/src/app/core/substance-form/nucleic-acid-details-form/nucleic-acid-details-form.component.scss index 78a762d48..affae2449 100644 --- a/src/app/core/substance-form/nucleic-acid-details-form/nucleic-acid-details-form.component.scss +++ b/src/app/core/substance-form/nucleic-acid-details-form/nucleic-acid-details-form.component.scss @@ -1,18 +1,11 @@ - - - .subtype { width: 33%; } - - .tags { width: 100%; } - - .form-row { display: flex; justify-content: space-between; diff --git a/src/app/core/substance-form/nucleic-acid-details-form/nucleic-acid-details-form.component.ts b/src/app/core/substance-form/nucleic-acid-details-form/nucleic-acid-details-form.component.ts index 8182374e4..fcc450169 100644 --- a/src/app/core/substance-form/nucleic-acid-details-form/nucleic-acid-details-form.component.ts +++ b/src/app/core/substance-form/nucleic-acid-details-form/nucleic-acid-details-form.component.ts @@ -12,7 +12,7 @@ import {GoogleAnalyticsService} from '@gsrs-core/google-analytics'; templateUrl: './nucleic-acid-details-form.component.html', styleUrls: ['./nucleic-acid-details-form.component.scss'] }) -// tslint:disable-next-line:max-line-length +// eslint-disable-next-line max-len export class NucleicAcidDetailsFormComponent extends SubstanceCardBaseFilteredList implements OnInit, AfterViewInit, OnDestroy { nucleicAcid: NucleicAcid; diff --git a/src/app/core/substance-form/nucleic-acid-details-form/nucleic-acid-details-form.module.ts b/src/app/core/substance-form/nucleic-acid-details-form/nucleic-acid-details-form.module.ts index b85342baf..02be58263 100644 --- a/src/app/core/substance-form/nucleic-acid-details-form/nucleic-acid-details-form.module.ts +++ b/src/app/core/substance-form/nucleic-acid-details-form/nucleic-acid-details-form.module.ts @@ -7,7 +7,7 @@ import { ReactiveFormsModule, FormsModule } from '@angular/forms'; import { MatSelectModule } from '@angular/material/select'; import { SubstanceFormModule } from '../substance-form.module'; import { NameResolverModule } from '../../name-resolver/name-resolver.module'; -// tslint:disable-next-line:max-line-length +// eslint-disable-next-line max-len import {NgMultiSelectDropDownModule} from 'ng-multiselect-dropdown'; import {NucleicAcidDetailsFormComponent} from '@gsrs-core/substance-form/nucleic-acid-details-form/nucleic-acid-details-form.component'; diff --git a/src/app/core/substance-form/other-links/other-links-form.component.scss b/src/app/core/substance-form/other-links/other-links-form.component.scss index 1545a5ec3..0d816a0a4 100644 --- a/src/app/core/substance-form/other-links/other-links-form.component.scss +++ b/src/app/core/substance-form/other-links/other-links-form.component.scss @@ -11,12 +11,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .form-row { diff --git a/src/app/core/substance-form/other-links/substance-form-other-links-card.component.html b/src/app/core/substance-form/other-links/substance-form-other-links-card.component.html index 0a34e050b..eba88199a 100644 --- a/src/app/core/substance-form/other-links/substance-form-other-links-card.component.html +++ b/src/app/core/substance-form/other-links/substance-form-other-links-card.component.html @@ -9,5 +9,11 @@
    +
    + +
    + diff --git a/src/app/core/substance-form/other-links/substance-form-other-links-card.component.scss b/src/app/core/substance-form/other-links/substance-form-other-links-card.component.scss index 0aea0c91a..59198bfc5 100644 --- a/src/app/core/substance-form/other-links/substance-form-other-links-card.component.scss +++ b/src/app/core/substance-form/other-links/substance-form-other-links-card.component.scss @@ -3,18 +3,18 @@ } .mat-divider { - border-top-color: rgba(0, 0, 0, .12); + border-top-color: var(--box-shadow-color-3); } .code { &:nth-child(odd) { - background-color: rgba(68, 138, 255, .07); + background-color: var(--nth-child-color-2); ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(68, 138, 255, .15); + background-color: var(--nth-child-color-3); } } } @@ -23,14 +23,14 @@ ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(128, 128, 128, .15); + background-color: var(--nth-child-color-1); } } } ::ng-deep { .mat-expansion-panel, .mat-table, textarea { - background-color: transparent; + background-color: var(--regular-transparent-color); } } } diff --git a/src/app/core/substance-form/physical-modifications/physical-modification-form.component.html b/src/app/core/substance-form/physical-modifications/physical-modification-form.component.html index 1818f7004..39c2ff8e2 100644 --- a/src/app/core/substance-form/physical-modifications/physical-modification-form.component.html +++ b/src/app/core/substance-form/physical-modifications/physical-modification-form.component.html @@ -11,7 +11,7 @@ - +
    Parameters @@ -117,10 +117,17 @@
    +
    + + +
    +
    +
    *physical modifications require a modification role or parameter +
    diff --git a/src/app/core/substance-form/physical-modifications/physical-modification-form.component.scss b/src/app/core/substance-form/physical-modifications/physical-modification-form.component.scss index dbe4fe4c1..5a4cb4a2c 100644 --- a/src/app/core/substance-form/physical-modifications/physical-modification-form.component.scss +++ b/src/app/core/substance-form/physical-modifications/physical-modification-form.component.scss @@ -22,12 +22,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .form-row { @@ -60,7 +60,7 @@ font-size: 11px; padding-bottom: 3.5px; line-height: 11px; - color: rgba(0, 0, 0, 0.54); + color: var(--dark-label-color); font-weight: 400; font-family: Roboto, "Helvetica Neue", sans-serif; } @@ -106,7 +106,7 @@ } .group { - width: 75px; + width: 85px; } .type { @@ -131,12 +131,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .form-row { @@ -179,7 +179,7 @@ padding-left: 0; font-size: 11px; padding-bottom: 4px; - color: rgba(0, 0, 0, 0.54); + color: var(--dark-label-color); font-weight: 400; font-family: Roboto, "Helvetica Neue", sans-serif; } @@ -200,7 +200,7 @@ padding-left: 0; font-size: 11px; padding-bottom: 2px; - color: rgba(0, 0, 0, 0.54); + color: var(--dark-label-color); font-weight: 400; font-family: Roboto, "Helvetica Neue", sans-serif; } @@ -243,3 +243,9 @@ } +.invalid-note { + margin-top: 5px; + font-style: italic; + font-size: 14px; + color: var(--regular-red-color); +} diff --git a/src/app/core/substance-form/physical-modifications/physical-modification-form.component.ts b/src/app/core/substance-form/physical-modifications/physical-modification-form.component.ts index 1ddc7df7e..becd15673 100644 --- a/src/app/core/substance-form/physical-modifications/physical-modification-form.component.ts +++ b/src/app/core/substance-form/physical-modifications/physical-modification-form.component.ts @@ -1,4 +1,4 @@ -import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; +import {Component, EventEmitter, Input, OnInit, Output, HostListener} from '@angular/core'; import {PhysicalModification, SubstanceAmount, SubstanceRelated, SubstanceSummary} from '@gsrs-core/substance'; import {ControlledVocabularyService, VocabularyTerm} from '@gsrs-core/controlled-vocabulary'; import {Subscription} from 'rxjs'; @@ -24,6 +24,51 @@ export class PhysicalModificationFormComponent implements OnInit { private subscriptions: Array = []; private overlayContainer: HTMLElement; siteDisplay: string; + invalid = false; + + @HostListener('focusout') onFocusout() { + if (!this.privateMod.physicalModificationRole) { + let present = false; + if (this.privateMod.parameters){ + this.privateMod.parameters.forEach (param => { + if (param.amount.type) { + present = true; + } + }); + if (!present) { + this.invalid = true; + } else { + this.invalid = false; + } + } else { + this.invalid = true; + } + } else { + this.invalid = false; + } + } + + @HostListener('focusin') onFocusin() { + if (!this.privateMod.physicalModificationRole) { + let present = false; + if (this.privateMod.parameters) { + this.privateMod.parameters.forEach (param => { + if (param.amount.type) { + present = true; + } + }); + if (!present) { + this.invalid = true; + } else { + this.invalid = false; + } + } else { + this.invalid = true; + } + } else { + this.invalid = false; + } + } constructor( private cvService: ControlledVocabularyService, @@ -69,6 +114,32 @@ export class PhysicalModificationFormComponent implements OnInit { delete this.privateMod.$$deletedCode; } + updateRequired(): void { + if (!this.privateMod.physicalModificationRole) { + let present = false; + if (this.privateMod.parameters){ + this.privateMod.parameters.forEach (param => { + if (param.amount.type) { + present = true; + } + }); + if (!present) { + this.invalid = true; + } else { + this.invalid = false; + } + } else { + this.invalid = true; + } + } else { + this.invalid = false; + } + } + + updateAccess(access: Array): void { + this.mod.access = access; + } + openParameterDialog(): void { if (!this.mod.parameters) { this.mod.parameters = []; @@ -88,6 +159,11 @@ export class PhysicalModificationFormComponent implements OnInit { this.subscriptions.push(dialogSubscription); } + updateRole(event: any) { + this.mod.physicalModificationRole = event; + this.updateRequired(); + } + openPropertyParameter(parameter?: any): void { let isNew: boolean; @@ -117,6 +193,7 @@ export class PhysicalModificationFormComponent implements OnInit { parameter[key] = newParameter[key]; }); } + this.updateRequired(); } }); } diff --git a/src/app/core/substance-form/physical-modifications/substance-form-physical-modifications-card.component.html b/src/app/core/substance-form/physical-modifications/substance-form-physical-modifications-card.component.html index a4d0a92e8..0e32de15d 100644 --- a/src/app/core/substance-form/physical-modifications/substance-form-physical-modifications-card.component.html +++ b/src/app/core/substance-form/physical-modifications/substance-form-physical-modifications-card.component.html @@ -9,3 +9,9 @@ +
    + + +
    \ No newline at end of file diff --git a/src/app/core/substance-form/physical-modifications/substance-form-physical-modifications-card.component.scss b/src/app/core/substance-form/physical-modifications/substance-form-physical-modifications-card.component.scss index 10d24ea81..9dc74597c 100644 --- a/src/app/core/substance-form/physical-modifications/substance-form-physical-modifications-card.component.scss +++ b/src/app/core/substance-form/physical-modifications/substance-form-physical-modifications-card.component.scss @@ -8,19 +8,19 @@ } .mat-divider { - border-top-color: rgba(0, 0, 0, .12); + border-top-color: var(--box-shadow-color-3); } .code { &:nth-child(odd) { - background-color: rgba(68, 138, 255, .07); + background-color: var(--nth-child-color-2); ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(68, 138, 255, .15); + background-color: var(--nth-child-color-3); } } } @@ -29,14 +29,14 @@ ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(128, 128, 128, .15); + background-color: var(--nth-child-color-1); } } } ::ng-deep { .mat-expansion-panel, .mat-table, textarea { - background-color: transparent; + background-color: var(--regular-transparent-color); } } } diff --git a/src/app/core/substance-form/physical-modifications/substance-form-physical-modifications.module.ts b/src/app/core/substance-form/physical-modifications/substance-form-physical-modifications.module.ts index 1966ff279..f8d1adc31 100644 --- a/src/app/core/substance-form/physical-modifications/substance-form-physical-modifications.module.ts +++ b/src/app/core/substance-form/physical-modifications/substance-form-physical-modifications.module.ts @@ -12,7 +12,7 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatInputModule } from '@angular/material/input'; import {SubstanceFormPhysicalModificationsCardComponent} from '@gsrs-core/substance-form/physical-modifications/substance-form-physical-modifications-card.component'; import { PhysicalModificationFormComponent } from './physical-modification-form.component'; -import { MatListModule } from '@angular/material'; +import { MatListModule } from '@angular/material/list'; @NgModule({ imports: [ diff --git a/src/app/core/substance-form/polymer-classification/substance-form-polymer-classification.component.html b/src/app/core/substance-form/polymer-classification/substance-form-polymer-classification.component.html index 6b6fae404..e0a4afcf3 100644 --- a/src/app/core/substance-form/polymer-classification/substance-form-polymer-classification.component.html +++ b/src/app/core/substance-form/polymer-classification/substance-form-polymer-classification.component.html @@ -23,8 +23,8 @@ [subuuid]="relatedSubstanceUuid"> - - + diff --git a/src/app/core/substance-form/polymer-classification/substance-form-polymer-classification.component.scss b/src/app/core/substance-form/polymer-classification/substance-form-polymer-classification.component.scss index cb6c6477d..11c3c65db 100644 --- a/src/app/core/substance-form/polymer-classification/substance-form-polymer-classification.component.scss +++ b/src/app/core/substance-form/polymer-classification/substance-form-polymer-classification.component.scss @@ -1,11 +1,6 @@ - - - .subtype { } - - .related-substance { width: 45%; } diff --git a/src/app/core/substance-form/polymer-classification/substance-form-polymer-classification.component.ts b/src/app/core/substance-form/polymer-classification/substance-form-polymer-classification.component.ts index 55d77c1e3..e969f4f06 100644 --- a/src/app/core/substance-form/polymer-classification/substance-form-polymer-classification.component.ts +++ b/src/app/core/substance-form/polymer-classification/substance-form-polymer-classification.component.ts @@ -19,7 +19,7 @@ import { SubstanceFormPolymerClassificationService } from './substance-form-poly templateUrl: './substance-form-polymer-classification.component.html', styleUrls: ['./substance-form-polymer-classification.component.scss'] }) -// tslint:disable-next-line:max-line-length +// eslint-disable-next-line max-len export class SubstanceFormPolymerClassificationComponent extends SubstanceFormBase implements OnInit, AfterViewInit, OnDestroy { @@ -75,15 +75,21 @@ export class SubstanceFormPolymerClassificationComponent extends SubstanceFormBa } parentSubstanceUpdated(substance: SubstanceSummary): void { - const relatedSubstance: SubstanceRelated = { - refPname: substance._name, - name: substance._name, - refuuid: substance.uuid, - substanceClass: 'reference', - approvalID: substance.approvalID - }; - this.classification.parentSubstance = relatedSubstance; - this.relatedSubstanceUuid = relatedSubstance && relatedSubstance.refuuid || ''; + if (substance !== null){ + const relatedSubstance: SubstanceRelated = { + refPname: substance._name, + name: substance._name, + refuuid: substance.uuid, + substanceClass: 'reference', + approvalID: substance.approvalID + }; + this.classification.parentSubstance = relatedSubstance; + this.relatedSubstanceUuid = relatedSubstance && relatedSubstance.refuuid || ''; + } else { + this.classification.parentSubstance = null; + this.relatedSubstanceUuid = null; + } + } updateAccess(access: Array): void { diff --git a/src/app/core/substance-form/properties/property-form.component.html b/src/app/core/substance-form/properties/property-form.component.html index 9b4819994..cb85207b9 100644 --- a/src/app/core/substance-form/properties/property-form.component.html +++ b/src/app/core/substance-form/properties/property-form.component.html @@ -1,162 +1,164 @@
    -
    - Deleted  - -
    -
    -
    - +
    + Deleted  + +
    +
    +
    + +
    +
    + +
    +
    + + + + + + +
    + + Defining +
    -
    -
    - - -
    -
    -
    - - - - - - -
    - - Defining - -
    -
    -
    Site Range
    -
    {{property.value.nonNumericValue}} +
    +
    Site Range
    +
    +
    + + +
    +
    + + +
    -
    - - -
    -
    -
    -
    -

    - Parameters - -

    - -
    - - - {{parameter.name}} - - -   - {{parameter.value.type}} - - -  - -   - {{parameter.value.average}} -   - {{parameter.value.units}} - - - +
    +
    +

    + Parameters + +

    + +
    + + + {{parameter.name}} + +   - - - -   - [ - - > - - - < - - - {{parameter.value.low}} + {{parameter.value.type}} - -  to  - - - {{parameter.value.high}} - - ] - + +  -   - {{parameter.value.units}} + {{parameter.value.average}}   - (average) - - - -   - (average) - - -  - -   - [ - - > + {{parameter.value.units}} - - < + + +   + - + +   + [ + + > + + + < + + + {{parameter.value.low}} + + +  to  + + + {{parameter.value.high}} + + ] + +   + {{parameter.value.units}} +   + (average) + - - {{parameter.value.lowLimit}} + +   + (average) - -  to  + +  - +   + [ + + > + + + < + + + {{parameter.value.lowLimit}} + + +  to  + + + {{parameter.value.highLimit}} + + ] +  (limits) - - {{parameter.value.highLimit}} + +  -  + {{parameter.value.nonNumericValue}} - ] -  (limits) - -  -  - {{parameter.value.nonNumericValue}} - - - + +
    +
    -
    +
    -
    -
    -
    -
    Amount
    - -
    -
    -
    - +
    +
    +
    Amount
    + +
    +
    +
    + +
    + +
    - - -
    + \ No newline at end of file diff --git a/src/app/core/substance-form/properties/property-form.component.scss b/src/app/core/substance-form/properties/property-form.component.scss index 29c148111..a2701e7b9 100644 --- a/src/app/core/substance-form/properties/property-form.component.scss +++ b/src/app/core/substance-form/properties/property-form.component.scss @@ -18,12 +18,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .referenced-substance { diff --git a/src/app/core/substance-form/properties/property-form.component.ts b/src/app/core/substance-form/properties/property-form.component.ts index 63446baf5..8e200db71 100644 --- a/src/app/core/substance-form/properties/property-form.component.ts +++ b/src/app/core/substance-form/properties/property-form.component.ts @@ -9,6 +9,7 @@ import { PropertyParameterDialogComponent } from '../property-parameter-dialog/p import { UtilsService } from '../../utils/utils.service'; import { OverlayContainer } from '@angular/cdk/overlay'; import {SubunitSelectorDialogComponent} from '@gsrs-core/substance-form/subunit-selector-dialog/subunit-selector-dialog.component'; +import { SubstanceFormService } from '@gsrs-core/substance-form/substance-form.service'; @Component({ selector: 'app-property-form', @@ -23,12 +24,14 @@ export class PropertyFormComponent implements OnInit { propertyNameList: Array = []; propertyTypeList: Array = []; private overlayContainer: HTMLElement; + _nonNumeric: string; constructor( private cvService: ControlledVocabularyService, private dialog: MatDialog, private utilsService: UtilsService, - private overlayContainerService: OverlayContainer + private overlayContainerService: OverlayContainer, + private substanceFormService: SubstanceFormService ) { } ngOnInit() { @@ -42,6 +45,9 @@ export class PropertyFormComponent implements OnInit { if ( !this.privateProperty.value) { this.privateProperty.value = {}; } + if (this.property.value && this.property.value.nonNumericValue) { + this._nonNumeric = this.property.value.nonNumericValue; + } } get property(): SubstanceProperty { @@ -74,14 +80,19 @@ export class PropertyFormComponent implements OnInit { } referencedSubstanceUpdated(substance: SubstanceSummary): void { - const referencedSubstance: SubstanceRelated = { - refPname: substance._name, - name: substance._name, - refuuid: substance.uuid, - substanceClass: 'reference', - approvalID: substance.approvalID - }; - this.property.referencedSubstance = referencedSubstance; + if (substance !== null){ + const referencedSubstance: SubstanceRelated = { + refPname: substance._name, + name: substance._name, + refuuid: substance.uuid, + substanceClass: 'reference', + approvalID: substance.approvalID + }; + this.property.referencedSubstance = referencedSubstance; + } else { + this.property.referencedSubstance = null; + } + } openPropertyParameter(parameter?: SubstanceParameter): void { @@ -129,9 +140,20 @@ export class PropertyFormComponent implements OnInit { this.overlayContainer.style.zIndex = null; this.property.name = features.name || ''; this.property.value.nonNumericValue = features.siteRange; + this._nonNumeric = features.siteRange; }); } + validateRange() { + try { + this.substanceFormService.stringToSites(this._nonNumeric); + this.property.value.nonNumericValue = this._nonNumeric; + } catch (error) { + alert('invalid shorthand for a site. Must be of form "{subunit}_{residue}" with multiple ranges seperated by a comma. Changes will be reverted'); + this._nonNumeric = this.property.value.nonNumericValue; + } + } + addOtherOption(vocab: Array, property: string) { if (vocab.some(r => property === r.value)) { } else { diff --git a/src/app/core/substance-form/properties/substance-form-properties-card.component.html b/src/app/core/substance-form/properties/substance-form-properties-card.component.html index 99c326a92..341ee4b8a 100644 --- a/src/app/core/substance-form/properties/substance-form-properties-card.component.html +++ b/src/app/core/substance-form/properties/substance-form-properties-card.component.html @@ -17,4 +17,12 @@
    - \ No newline at end of file + + + +
    + + +
    \ No newline at end of file diff --git a/src/app/core/substance-form/properties/substance-form-properties-card.component.scss b/src/app/core/substance-form/properties/substance-form-properties-card.component.scss index eca960ad3..bce9c9450 100644 --- a/src/app/core/substance-form/properties/substance-form-properties-card.component.scss +++ b/src/app/core/substance-form/properties/substance-form-properties-card.component.scss @@ -3,18 +3,18 @@ } .mat-divider { - border-top-color: rgba(0, 0, 0, .5); + border-top-color: var(--text-color); } .property { &:nth-child(odd) { - background-color: rgba(68, 138, 255, .07); + background-color: var(--nth-child-color-2); ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(68, 138, 255, .15); + background-color: var(--nth-child-color-3); } } } @@ -23,14 +23,14 @@ ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(128, 128, 128, .15); + background-color: var(--nth-child-color-1); } } } ::ng-deep { .mat-expansion-panel, .mat-table, textarea { - background-color: transparent; + background-color: var(--regular-transparent-color); } } -} \ No newline at end of file +} diff --git a/src/app/core/substance-form/properties/substance-form-properties.module.ts b/src/app/core/substance-form/properties/substance-form-properties.module.ts index 96be0656d..2260acdba 100644 --- a/src/app/core/substance-form/properties/substance-form-properties.module.ts +++ b/src/app/core/substance-form/properties/substance-form-properties.module.ts @@ -13,7 +13,8 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatInputModule } from '@angular/material/input'; import { PropertyFormComponent } from './property-form.component'; import { SubstanceSelectorModule } from '@gsrs-core/substance-selector/substance-selector.module'; -import { MatCheckboxModule, MatListModule } from '@angular/material'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatListModule } from '@angular/material/list'; @NgModule({ imports: [ diff --git a/src/app/core/substance-form/properties/substance-form-properties.service.ts b/src/app/core/substance-form/properties/substance-form-properties.service.ts index dc4ace4ea..5db7cb6cf 100644 --- a/src/app/core/substance-form/properties/substance-form-properties.service.ts +++ b/src/app/core/substance-form/properties/substance-form-properties.service.ts @@ -63,6 +63,8 @@ export class SubstanceFormPropertiesService extends SubstanceFormServiceBase -1) { this.substance.properties.splice(subPropertyIndex, 1); this.propertyEmitter.next(this.substance.properties); + this.substanceFormService.recalculateAllSites('features'); + } } } diff --git a/src/app/core/substance-form/property-parameter-form/property-parameter-form.component.scss b/src/app/core/substance-form/property-parameter-form/property-parameter-form.component.scss index ee2e4f370..15605a765 100644 --- a/src/app/core/substance-form/property-parameter-form/property-parameter-form.component.scss +++ b/src/app/core/substance-form/property-parameter-form/property-parameter-form.component.scss @@ -11,12 +11,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .form-row { @@ -33,4 +33,4 @@ .type { flex-grow: 1; } -} \ No newline at end of file +} diff --git a/src/app/core/substance-form/protein-details/substance-form-protein-details.component.html b/src/app/core/substance-form/protein-details/substance-form-protein-details.component.html index 237c34151..a4e44514b 100644 --- a/src/app/core/substance-form/protein-details/substance-form-protein-details.component.html +++ b/src/app/core/substance-form/protein-details/substance-form-protein-details.component.html @@ -8,8 +8,8 @@
    - - +
    diff --git a/src/app/core/substance-form/protein-details/substance-form-protein-details.component.scss b/src/app/core/substance-form/protein-details/substance-form-protein-details.component.scss index 2bf1243d2..b14db2ee0 100644 --- a/src/app/core/substance-form/protein-details/substance-form-protein-details.component.scss +++ b/src/app/core/substance-form/protein-details/substance-form-protein-details.component.scss @@ -1,6 +1,3 @@ - - - .subtype { width: 30%; margin-right: 15px; @@ -14,8 +11,6 @@ width: 100%; } - - .form-row { display: flex; justify-content: space-between; diff --git a/src/app/core/substance-form/protein-details/substance-form-protein-details.component.ts b/src/app/core/substance-form/protein-details/substance-form-protein-details.component.ts index 8f9bd6683..26992fa13 100644 --- a/src/app/core/substance-form/protein-details/substance-form-protein-details.component.ts +++ b/src/app/core/substance-form/protein-details/substance-form-protein-details.component.ts @@ -13,7 +13,7 @@ import { SubstanceFormBase } from '../base-classes/substance-form-base'; templateUrl: './substance-form-protein-details.component.html', styleUrls: ['./substance-form-protein-details.component.scss'] }) -// tslint:disable-next-line:max-line-length +// eslint-disable-next-line max-len export class SubstanceFormProteinDetailsComponent extends SubstanceFormBase implements OnInit, AfterViewInit, OnDestroy { protein: Protein; diff --git a/src/app/core/substance-form/references/apply-reference/apply-reference.component.html b/src/app/core/substance-form/references/apply-reference/apply-reference.component.html index e367f008b..2df9b898e 100644 --- a/src/app/core/substance-form/references/apply-reference/apply-reference.component.html +++ b/src/app/core/substance-form/references/apply-reference/apply-reference.component.html @@ -31,15 +31,15 @@
    - {{domain[domainsWithReferences[domainKey].displayKey]}} + {{(domain[domainsWithReferences[domainKey].displayKey].length>50)? (domain[domainsWithReferences[domainKey].displayKey] | slice:0:50)+'...':(domain[domainsWithReferences[domainKey].displayKey])}} - {{domain.relatedSubstance.name}} + {{(domain.relatedSubstance.name.length>40)? (domain.relatedSubstance.name | slice:0:40)+'...':(domain.relatedSubstance.name)}} - {{domain.substance.name}} + {{(domain.substance.name.length > 40)? (domain.substance.name | slice:0:40)+'...':(domain.substance.name)}}
    diff --git a/src/app/core/substance-form/references/domain-references/domain-references.component.html b/src/app/core/substance-form/references/domain-references/domain-references.component.html index 3187e968c..59a0e79f0 100644 --- a/src/app/core/substance-form/references/domain-references/domain-references.component.html +++ b/src/app/core/substance-form/references/domain-references/domain-references.component.html @@ -62,16 +62,16 @@

    -

    + - + - + + + + + + + [routerLink]="['/substances', substance.uuid]"> @@ -212,7 +402,7 @@ - + @@ -148,21 +152,26 @@ - - + + - - + +
    Open Edit Remove Deselect Tags + {{tag}} hide + {{reference.tags.length > 0? reference.tags[0]:null}} more... + diff --git a/src/app/core/substance-form/references/domain-references/domain-references.component.scss b/src/app/core/substance-form/references/domain-references/domain-references.component.scss index afe700eea..4bd0be89e 100644 --- a/src/app/core/substance-form/references/domain-references/domain-references.component.scss +++ b/src/app/core/substance-form/references/domain-references/domain-references.component.scss @@ -1,3 +1,4 @@ +/* awtodo keep domain-reference ?? */ .domain-reference { display: block; padding: 5px 0 10px 0; @@ -37,8 +38,8 @@ } .custom-badge > .mat-badge-content { - background-color: white !important; - color: rgb(71, 147, 209) !important; + background-color: var(--regular-white-color) !important; + color: var(--secondary-rgb-blue) !important; } .title-actions { @@ -59,4 +60,4 @@ .ref-title { min-width: 200px; -} \ No newline at end of file +} diff --git a/src/app/core/substance-form/references/domain-references/domain-references.component.ts b/src/app/core/substance-form/references/domain-references/domain-references.component.ts index bc0cdf025..e0fe52d1a 100644 --- a/src/app/core/substance-form/references/domain-references/domain-references.component.ts +++ b/src/app/core/substance-form/references/domain-references/domain-references.component.ts @@ -25,9 +25,10 @@ export class DomainReferencesComponent implements OnInit, OnDestroy { canReuse = false; references: Array = []; documentTypesDictionary: { [dictionaryValue: string]: VocabularyTerm } = {}; - displayedColumns: string[] = ['type', 'citation', 'publicDomain', 'access', 'goToReference', 'remove', 'attachment', 'delete', 'apply']; + displayedColumns: string[] = ['type', 'citation', 'publicDomain', 'access', 'goToReference', 'remove', 'attachment', 'tags', 'apply']; tableData: MatTableDataSource; isExpanded = false; + showmore = false; private subscriptions: Array = []; private overlayContainer: HTMLElement; @@ -201,10 +202,11 @@ export class DomainReferencesComponent implements OnInit, OnDestroy { this.tableData.data = this.references; } - deleteReference(reference: SubstanceReference): void { - reference.$$deletedCode = this.utilsService.newUUID(); - this.substanceFormReferencesService.emitReferencesUpdate(); - } + // COMMENTING OUT IN CASE WE NEED TO ADD BACK SOMEDAY + // deleteReference(reference: SubstanceReference): void { + // reference.$$deletedCode = this.utilsService.newUUID(); + // this.substanceFormReferencesService.emitReferencesUpdate(); + // } panelOpened(): void { this.isExpanded = true; diff --git a/src/app/core/substance-form/references/previous-references/previous-references-dialog/previous-references-dialog.component.ts b/src/app/core/substance-form/references/previous-references/previous-references-dialog/previous-references-dialog.component.ts index c4c5c4b02..47b7d0ea7 100644 --- a/src/app/core/substance-form/references/previous-references/previous-references-dialog/previous-references-dialog.component.ts +++ b/src/app/core/substance-form/references/previous-references/previous-references-dialog/previous-references-dialog.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { MatDialogRef } from '@angular/material'; +import { MatDialogRef } from '@angular/material/dialog'; import { SubstanceReference } from '@gsrs-core/substance/substance.model'; @Component({ diff --git a/src/app/core/substance-form/references/previous-references/previous-references.component.html b/src/app/core/substance-form/references/previous-references/previous-references.component.html index 1f346fa70..84a15202c 100644 --- a/src/app/core/substance-form/references/previous-references/previous-references.component.html +++ b/src/app/core/substance-form/references/previous-references/previous-references.component.html @@ -7,7 +7,7 @@
    Select a recently submitted reference to populate the form - +
    diff --git a/src/app/core/substance-form/references/previous-references/previous-references.component.scss b/src/app/core/substance-form/references/previous-references/previous-references.component.scss index f58ad4352..5f6dc3308 100644 --- a/src/app/core/substance-form/references/previous-references/previous-references.component.scss +++ b/src/app/core/substance-form/references/previous-references/previous-references.component.scss @@ -1,5 +1,5 @@ .reuse-button { - color: #4793d1 !important; + color: var(--primary-color) !important; } .narrow-row { @@ -18,4 +18,4 @@ margin-left: 30px; font-style: italic; font-size: 14px; -} \ No newline at end of file +} diff --git a/src/app/core/substance-form/references/reference-form.component.html b/src/app/core/substance-form/references/reference-form.component.html index f5c02ff93..88f1f125b 100644 --- a/src/app/core/substance-form/references/reference-form.component.html +++ b/src/app/core/substance-form/references/reference-form.component.html @@ -25,10 +25,10 @@
    - + - + @@ -57,6 +57,20 @@
    +
    +
    + + +
    + + Uploading +
    +
    + Error: There was a problem uploading this document +
    diff --git a/src/app/core/substance-form/references/substance-form-references-card.component.html b/src/app/core/substance-form/references/substance-form-references-card.component.html index 5c78f70f8..4bc7ca7bd 100644 --- a/src/app/core/substance-form/references/substance-form-references-card.component.html +++ b/src/app/core/substance-form/references/substance-form-references-card.component.html @@ -16,4 +16,11 @@
    - \ No newline at end of file + + +
    + + +
    \ No newline at end of file diff --git a/src/app/core/substance-form/references/substance-form-references-card.component.scss b/src/app/core/substance-form/references/substance-form-references-card.component.scss index 9c7dcd73a..faa967e92 100644 --- a/src/app/core/substance-form/references/substance-form-references-card.component.scss +++ b/src/app/core/substance-form/references/substance-form-references-card.component.scss @@ -3,18 +3,18 @@ } .mat-divider { - border-top-color: rgba(0, 0, 0, .5); + border-top-color: var(--text-color); } .reference { &:nth-child(odd) { - background-color: rgba(68, 138, 255, .07); + background-color: var(--nth-child-color-2); ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(68, 138, 255, .15); + background-color: var(--nth-child-color-3); } } } @@ -23,7 +23,7 @@ ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(128, 128, 128, .15); + background-color: var(--nth-child-color-1); } } } @@ -32,4 +32,4 @@ .search { width: 400px; max-width: 100%; -} \ No newline at end of file +} diff --git a/src/app/core/substance-form/relationships/relationship-form.component.html b/src/app/core/substance-form/relationships/relationship-form.component.html index 3f0fabce3..1accac44d 100644 --- a/src/app/core/substance-form/relationships/relationship-form.component.html +++ b/src/app/core/substance-form/relationships/relationship-form.component.html @@ -15,7 +15,7 @@
    diff --git a/src/app/core/substance-form/relationships/relationship-form.component.scss b/src/app/core/substance-form/relationships/relationship-form.component.scss index 8297bc932..106f6f300 100644 --- a/src/app/core/substance-form/relationships/relationship-form.component.scss +++ b/src/app/core/substance-form/relationships/relationship-form.component.scss @@ -18,7 +18,7 @@ .chevron { width: 20px; line-height: 67px; - color: rgba(0,0,0,.6); + color: var(--chevron-color); } .chevron-button { width: 20px; @@ -31,12 +31,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .related-substance, .mediator-substance { @@ -120,4 +120,4 @@ flex-wrap: wrap; justify-content: space-between; } - } \ No newline at end of file + } diff --git a/src/app/core/substance-form/relationships/relationship-form.component.ts b/src/app/core/substance-form/relationships/relationship-form.component.ts index cba1bf557..667dad1bd 100644 --- a/src/app/core/substance-form/relationships/relationship-form.component.ts +++ b/src/app/core/substance-form/relationships/relationship-form.component.ts @@ -17,6 +17,7 @@ export class RelationshipFormComponent implements OnInit { @Output() relationshipDeleted = new EventEmitter(); deleteTimer: any; viewFull = true; + name?: string; constructor( private cvService: ControlledVocabularyService, @@ -45,6 +46,7 @@ export class RelationshipFormComponent implements OnInit { } this.relatedSubstanceUuid = this.privateRelationship.relatedSubstance && this.privateRelationship.relatedSubstance.refuuid || ''; this.mediatorSubstanceUuid = this.privateRelationship.mediatorSubstance && this.privateRelationship.mediatorSubstance.refuuid || ''; + this.name = this.privateRelationship.relatedSubstance.refPname? this.privateRelationship.relatedSubstance.refPname : this.privateRelationship.relatedSubstance.name; } get relationship(): SubstanceRelationship { @@ -88,6 +90,7 @@ export class RelationshipFormComponent implements OnInit { } mediatorSubstanceUpdated(substance: SubstanceSummary): void { + if ( substance !== null) { const relatedSubstance: MediatorSubstance = { refPname: substance._name, refuuid: substance.uuid, @@ -95,5 +98,8 @@ export class RelationshipFormComponent implements OnInit { approvalID: substance.approvalID }; this.relationship.mediatorSubstance = relatedSubstance; + } else { + this.relationship.mediatorSubstance = {}; } } +} diff --git a/src/app/core/substance-form/relationships/relationships-download-button/relationships-download-button.component.html b/src/app/core/substance-form/relationships/relationships-download-button/relationships-download-button.component.html new file mode 100644 index 000000000..3960b5884 --- /dev/null +++ b/src/app/core/substance-form/relationships/relationships-download-button/relationships-download-button.component.html @@ -0,0 +1,9 @@ +
    + +
    + +
    +
    \ No newline at end of file diff --git a/src/app/core/substance-form/relationships/relationships-download-button/relationships-download-button.component.scss b/src/app/core/substance-form/relationships/relationships-download-button/relationships-download-button.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/core/substance-form/relationships/relationships-download-button/relationships-download-button.component.ts b/src/app/core/substance-form/relationships/relationships-download-button/relationships-download-button.component.ts new file mode 100644 index 000000000..70c9f11a7 --- /dev/null +++ b/src/app/core/substance-form/relationships/relationships-download-button/relationships-download-button.component.ts @@ -0,0 +1,125 @@ +import { Component, OnInit, AfterViewInit, Input, OnDestroy } from '@angular/core'; +import { ActivatedRoute, Router, NavigationExtras } from '@angular/router'; +import { SubstanceBrowseHeaderDynamicContent } from '@gsrs-core/substances-browse/substance-browse-header-dynamic-content.component'; +// import { GeneralService } from '../../service/general.service'; +import { ConfigService } from '@gsrs-core/config/config.service'; +import { AuthService } from '@gsrs-core/auth/auth.service'; +import { take } from 'rxjs/operators'; +import { LoadingService } from '@gsrs-core/loading'; +import { MatDialog } from '@angular/material/dialog'; +import { SubstanceService } from '@gsrs-core/substance/substance.service'; +import { ExportDialogComponent } from '@gsrs-core/substances-browse/export-dialog/export-dialog.component'; +import { Subscription } from 'rxjs'; +import {Subject} from 'rxjs'; +import {SubstanceDetail, SubstanceRelationship} from '@gsrs-core/substance/substance.model'; + + +@Component({ + selector: 'app-relationships-download-button', + templateUrl: './relationships-download-button.component.html', + styleUrls: ['./relationships-download-button.component.scss'] +}) +export class RelationshipsDownloadButtonComponent implements OnInit, AfterViewInit, OnDestroy, SubstanceBrowseHeaderDynamicContent { + private subscriptions: Array = []; + test: any; + isAdmin = false; + privateExport = false; + etag = ''; + etagDetails: any; + paramUrl = ''; + totalSubstance = 0; + loadedComponents: any; + exportOptions: Array; + hasAdditionalDownloads = false; + additionalExportOptions = []; + + constructor( + // private generalService: GeneralService, + private configService: ConfigService, + private authService: AuthService, + private substanceService: SubstanceService, + private router: Router, + public activatedRoute: ActivatedRoute, + public loadingService: LoadingService, + private dialog: MatDialog) { } + + // @Input() substance: Subject; // parent class uses this object but vscode complains. + @Input() substance: any; + + + ngOnInit() { + + this.authService.hasAnyRolesAsync('Admin', 'Updater', 'SuperUpdater').pipe(take(1)).subscribe(response => { + this.isAdmin = response; + + if (this.isAdmin === true) { + } + }); + this.loadedComponents = (this.configService.configData && this.configService.configData.loadedComponents) || null; + } + + ngAfterViewInit() { + // put something; + } + + ngOnDestroy() { + this.subscriptions.forEach(subscription => { + if (subscription) { + subscription.unsubscribe(); + } + }); + } + + export(source: string) { + + // Do this first subscription here to avoid unecessary searches on init + const subscriptionResult = this.substanceService.searchFromString( + 'q=root_uuid:"^' + this.substance.uuid + '$"') + .subscribe(response => { + if (response) { + this.etag = response.etag; + this.totalSubstance = response.total; + } + + if (this.etag) { + const extension = 'relationships.txt'; + const url = this.getApiExportUrl(this.etag, extension, source); + if (this.isAdmin === true) { + let type = ''; + if (source != null) { + if (source === 'relationships') { + type = 'export'; + } + } + const dialogReference = this.dialog.open(ExportDialogComponent, { + // height: '215x', + width: '700px', + data: { 'extension': extension, 'type': type, 'entity': 'substances', 'hideOptionButtons': true } + }); + dialogReference.afterClosed().subscribe(response => { + const name = response.name; + const id = response.id; + if (name && name !== '') { + this.loadingService.setLoading(true); + const fullname = name + '.' + extension; + this.authService.startUserDownload(url, this.privateExport, fullname, id).subscribe(response => { + this.loadingService.setLoading(false); + const navigationExtras: NavigationExtras = { + queryParams: { + } + }; + this.router.navigate(['/user-downloads/', response.id]); + }, error => this.loadingService.setLoading(false)); + } + }); + } + } // if etag + }); + this.subscriptions.push(subscriptionResult); + } + + getApiExportUrl(etag: string, extension: string, source: string): string { + return `${this.configService.configData.apiBaseUrl}api/v1/substances/export/${etag}/${extension}`; + } + +} diff --git a/src/app/core/substance-form/relationships/substance-form-relationships-card.component.html b/src/app/core/substance-form/relationships/substance-form-relationships-card.component.html index 96feb6445..b2f46dbee 100644 --- a/src/app/core/substance-form/relationships/substance-form-relationships-card.component.html +++ b/src/app/core/substance-form/relationships/substance-form-relationships-card.component.html @@ -20,4 +20,13 @@
    - \ No newline at end of file + + + + +
    + + +
    \ No newline at end of file diff --git a/src/app/core/substance-form/relationships/substance-form-relationships-card.component.scss b/src/app/core/substance-form/relationships/substance-form-relationships-card.component.scss index 8af741b33..96a81bc37 100644 --- a/src/app/core/substance-form/relationships/substance-form-relationships-card.component.scss +++ b/src/app/core/substance-form/relationships/substance-form-relationships-card.component.scss @@ -3,18 +3,18 @@ } .mat-divider { - border-top-color: rgba(0, 0, 0, .5); + border-top-color: var(--text-color); } .relationship { &:nth-child(odd) { - background-color: rgba(68, 138, 255, .07); + background-color: var(--nth-child-color-2); ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(68, 138, 255, .15); + background-color: var(--nth-child-color-3); } } } @@ -23,14 +23,14 @@ ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(128, 128, 128, .15); + background-color: var(--nth-child-color-1); } } } ::ng-deep { .mat-expansion-panel, .mat-table, textarea { - background-color: transparent; + background-color: var(--regular-transparent-color); } } } @@ -38,4 +38,4 @@ .search { width: 400px; max-width: 100%; -} \ No newline at end of file +} diff --git a/src/app/core/substance-form/relationships/substance-form-relationships.module.ts b/src/app/core/substance-form/relationships/substance-form-relationships.module.ts index 2d9cf73ea..852087fb8 100644 --- a/src/app/core/substance-form/relationships/substance-form-relationships.module.ts +++ b/src/app/core/substance-form/relationships/substance-form-relationships.module.ts @@ -13,7 +13,7 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatInputModule } from '@angular/material/input'; import { RelationshipFormComponent } from './relationship-form.component'; import { SubstanceSelectorModule } from '@gsrs-core/substance-selector/substance-selector.module'; -import { MatTooltipModule } from '@angular/material'; +import { MatTooltipModule } from '@angular/material/tooltip'; @NgModule({ imports: [ diff --git a/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.component.html b/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.component.html index e8b2bfae2..5ffa8845c 100644 --- a/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.component.html +++ b/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.component.html @@ -1,10 +1,24 @@ -
    +
    + + + +
    \ No newline at end of file diff --git a/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.component.scss b/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.component.scss index e45a2d598..be7477403 100644 --- a/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.component.scss +++ b/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.component.scss @@ -6,6 +6,26 @@ align-items: flex-end; } +.row { + display: flex; + justify-content: space-between; + align-items: flex-end; + + .delete-container { + padding: 0 10px 8px 0; + } + + .col { + flex-grow: 1; + padding-right: 15px; + } + + .col-1-1 { + width: calc((100% - 20px * 1)); //one column + margin-right: 20px; + } +} + .related-substance { width: 350px; max-width: 350px; @@ -23,3 +43,11 @@ max-width: 220px; } } + +.marginleft50px { + margin-left: 50px; +} + +.font11px { + font-size: 11px; +} diff --git a/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.component.ts b/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.component.ts index 59378ee91..d9258ecd7 100644 --- a/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.component.ts +++ b/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.component.ts @@ -1,11 +1,13 @@ import { AfterViewInit, Component, OnDestroy, OnInit } from '@angular/core'; -import { SubstanceRelated, SubstanceSummary, SpecifiedSubstanceG3 } from '@gsrs-core/substance'; import { Subscription } from 'rxjs'; -import { SubstanceFormService } from '@gsrs-core/substance-form/substance-form.service'; -import { ScrollToService } from '@gsrs-core/scroll-to/scroll-to.service'; import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; +import { SubstanceRelated, SubstanceSummary, SpecifiedSubstanceG3, SpecifiedSubstanceG4m } from '@gsrs-core/substance'; +import { SubstanceFormService } from '@gsrs-core/substance-form/substance-form.service'; import { ControlledVocabularyService, VocabularyTerm } from '@gsrs-core/controlled-vocabulary'; +import { ConfigService } from '@gsrs-core/config/config.service'; +import { ScrollToService } from '@gsrs-core/scroll-to/scroll-to.service'; import { SubstanceFormBase } from '../base-classes/substance-form-base'; +import { SubstanceFormSsg4mStartingMaterialsModule } from '@gsrs-core/substance-ssg4m/ssg4m-starting-materials/substance-form-ssg4m-starting-materials.module'; @Component({ selector: 'app-ssg-parent-substance-form', @@ -14,22 +16,60 @@ import { SubstanceFormBase } from '../base-classes/substance-form-base'; }) export class SsgParentSubstanceFormComponent extends SubstanceFormBase implements OnInit, AfterViewInit, OnDestroy { - + substance: any; + substanceClass: string; + privateShowAdvancedSettings: boolean; + specifiedSubstanceG4m: SpecifiedSubstanceG4m; parentSubstance: SubstanceRelated; relatedSubstanceUuid: string; + configSsg4Form: any; + public configSettingsDisplay = {}; + public parentSubstanceHeader = 'Parent Substance'; private subscriptions: Array = []; + constructor( - private substanceFormService: SubstanceFormService, public gaService: GoogleAnalyticsService, - public cvService: ControlledVocabularyService + private substanceFormService: SubstanceFormService, + public cvService: ControlledVocabularyService, + public configService: ConfigService ) { super(); - this.analyticsEventCategory = 'substance form ssg 3 parent substance'; + this.analyticsEventCategory = 'substance form ssg 3 and 4 parent substance'; } ngOnInit() { - this.menuLabelUpdate.emit('Parent Substance'); + // Get Config variables for SSG4 + this.configSsg4Form = (this.configService.configData && this.configService.configData.ssg4Form) || null; + let configTitle = 'Search or Register a New Substance'; + if (this.configSsg4Form) { + if (this.configSsg4Form.titles.parentSubstance) { + configTitle = this.configSsg4Form.titles.parentSubstance; + } + if (this.configSsg4Form.parentSubstanceHeader) { + this.parentSubstanceHeader = this.configSsg4Form.parentSubstanceHeader; + } + } + + this.menuLabelUpdate.emit(configTitle); const substanceSubscription = this.substanceFormService.substance.subscribe(substance => { + this.substance = substance; + this.substanceClass = substance.substanceClass; + + // SSG4m: Load/Set Substance Name with Structure + if (this.substanceClass && this.substanceClass === 'specifiedSubstanceG4m') { + if (this.substance.specifiedSubstanceG4m) { + this.specifiedSubstanceG4m = this.substance.specifiedSubstanceG4m; + + if (this.substance.specifiedSubstanceG4m.parentSubstance) { + if (this.substance.specifiedSubstanceG4m.parentSubstance.refuuid) { + this.relatedSubstanceUuid = this.substance.specifiedSubstanceG4m.parentSubstance.refuuid; + } + } + } + } + + // Specified Substance Group 3 + /* if (substance.specifiedSubstanceG3.parentSubstance == null) { substance.specifiedSubstanceG3.parentSubstance = {}; } @@ -39,7 +79,18 @@ export class SsgParentSubstanceFormComponent extends SubstanceFormBase implement if (substance.specifiedSubstanceG3.parentSubstance != null) { this.relatedSubstanceUuid = substance.specifiedSubstanceG3.parentSubstance.refuuid; } + */ + // Specified Substance Group 4 Manufacturing + if (substance.specifiedSubstanceG4m.parentSubstance == null) { + // substance.specifiedSubstanceG4m.parentSubstance = {}; + } + this.substanceFormService.resetState(); + // this.parentSubstance = substance.specifiedSubstanceG4m.parentSubstance; + + // if (substance.specifiedSubstanceG4m.parentSubstance != null) { + // this.relatedSubstanceUuid = substance.specifiedSubstanceG4m.parentSubstance.refuuid; + // } }); this.subscriptions.push(substanceSubscription); } @@ -53,16 +104,51 @@ export class SsgParentSubstanceFormComponent extends SubstanceFormBase implement }); } + + getConfigSettings(): void { + // Get SSG4 Config Settings from config.json file to show and hide fields in the form + let configSsg4Form: any; + configSsg4Form = this.configService.configData && this.configService.configData.ssg4Form || null; + // *** IMPORTANT: get the correct value. Get 'startingMaterial' json values from config + const confSettings = configSsg4Form.settingsDisplay.startingMaterial; + Object.keys(confSettings).forEach(key => { + if (confSettings[key] != null) { + if (confSettings[key] === 'simple') { + this.configSettingsDisplay[key] = true; + } else if (confSettings[key] === 'advanced') { + if (this.privateShowAdvancedSettings === true) { + this.configSettingsDisplay[key] = true; + } else { + this.configSettingsDisplay[key] = false; + } + } else if (confSettings[key] === 'removed') { + this.configSettingsDisplay[key] = false; + } + } + }); + } + relatedSubstanceUpdated(substance: SubstanceSummary): void { - if (substance != null) { - this.parentSubstance.refPname = substance._name; - this.parentSubstance.name = substance._name; - this.parentSubstance.refuuid = substance.uuid; - this.parentSubstance.substanceClass = 'reference'; - this.parentSubstance.approvalID = substance.approvalID; - - this.relatedSubstanceUuid = this.parentSubstance.refuuid; + if (substance !== null) { + const relatedSubstance: SubstanceRelated = { + refPname: substance._name, + name: substance._name, + refuuid: substance.uuid, + substanceClass: 'reference', + approvalID: substance.approvalID + }; + if (this.substanceClass && this.substanceClass === 'specifiedSubstanceG4m') { + this.substance.specifiedSubstanceG4m.parentSubstance = relatedSubstance; + } + } else { + if (this.substanceClass && this.substanceClass === 'specifiedSubstanceG4m') { + this.substance.specifiedSubstanceG4m.parentSubstance = {}; + } } } + updateManufactureIdType(manufactureIdType: string): void { + this.specifiedSubstanceG4m.manufactureIdType = manufactureIdType; + } + } diff --git a/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.module.ts b/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.module.ts index b8054c36f..3f93ae406 100644 --- a/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.module.ts +++ b/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.module.ts @@ -1,5 +1,10 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatInputModule } from '@angular/material/input'; import { SsgParentSubstanceFormComponent } from './ssg-parent-substance-form.component'; import { SubstanceFormModule } from '../substance-form.module'; import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; @@ -8,6 +13,7 @@ import { SubstanceSelectorModule } from '@gsrs-core/substance-selector/substance @NgModule({ imports: [ CommonModule, + RouterModule, DynamicComponentLoaderModule.forChild(SsgParentSubstanceFormComponent), SubstanceFormModule, SubstanceSelectorModule diff --git a/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.service.spec.ts b/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.service.spec.ts new file mode 100644 index 000000000..35097d174 --- /dev/null +++ b/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { SubstanceFormNotesService } from './substance-form-notes.service'; + +describe('SubstanceSFormNotesService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: SubstanceFormNotesService = TestBed.get(SubstanceFormNotesService); + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.service.ts b/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.service.ts new file mode 100644 index 000000000..251f8d02d --- /dev/null +++ b/src/app/core/substance-form/ssg-parent-substance-form/ssg-parent-substance-form.service.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@angular/core'; +import { SubstanceFormServiceBase } from '../base-classes/substance-form-service-base'; +import { SubstanceFormService } from '../substance-form.service'; +import { Observable } from 'rxjs'; +import { SpecifiedSubstanceParent } from '@gsrs-core/substance/substance.model'; + +@Injectable() +export class SsgParentSubstanceFormService extends SubstanceFormServiceBase { + + constructor( + public substanceFormService: SubstanceFormService + ) { + super(substanceFormService); + } + + initSubtanceForm(): void { + super.initSubtanceForm(); + const subscription = this.substanceFormService.substance.subscribe(substance => { + this.substance = substance; + if (!this.substance.specifiedSubstanceG4m.parentSubstance) { + this.substance.specifiedSubstanceG4m.parentSubstance = {}; + } + this.substanceFormService.resetState(); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.parentSubstance); + }); + this.subscriptions.push(subscription); + } + + get parentSubstance(): Observable { + return this.propertyEmitter.asObservable(); + } +} diff --git a/src/app/core/substance-form/structural-modifications/structural-modification-form.component.html b/src/app/core/substance-form/structural-modifications/structural-modification-form.component.html index 223e7482e..03013e7a0 100644 --- a/src/app/core/substance-form/structural-modifications/structural-modification-form.component.html +++ b/src/app/core/substance-form/structural-modifications/structural-modification-form.component.html @@ -30,7 +30,7 @@
    -
    +
    diff --git a/src/app/core/substance-form/structural-modifications/structural-modification-form.component.scss b/src/app/core/substance-form/structural-modifications/structural-modification-form.component.scss index 069145f7b..953a7b139 100644 --- a/src/app/core/substance-form/structural-modifications/structural-modification-form.component.scss +++ b/src/app/core/substance-form/structural-modifications/structural-modification-form.component.scss @@ -23,12 +23,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .form-row { @@ -59,7 +59,7 @@ font-size: 11px; padding-bottom: 3.5px; line-height: 11px; - color: rgba(0, 0, 0, 0.54); + color: var(--dark-label-color); font-weight: 400; font-family: Roboto, "Helvetica Neue", sans-serif; } @@ -141,5 +141,5 @@ .custom { padding-bottom: 10px; padding-top:10px; - border-bottom: 1px solid rgba(0, 0, 0, .45); -} \ No newline at end of file + border-bottom: 1px solid var(--box-shadow-color-6); +} diff --git a/src/app/core/substance-form/structural-modifications/substance-form-structural-modifications-card.component.html b/src/app/core/substance-form/structural-modifications/substance-form-structural-modifications-card.component.html index 6f6f894c4..2af01f168 100644 --- a/src/app/core/substance-form/structural-modifications/substance-form-structural-modifications-card.component.html +++ b/src/app/core/substance-form/structural-modifications/substance-form-structural-modifications-card.component.html @@ -9,3 +9,12 @@
    + +
    + + + +
    + diff --git a/src/app/core/substance-form/structural-modifications/substance-form-structural-modifications-card.component.scss b/src/app/core/substance-form/structural-modifications/substance-form-structural-modifications-card.component.scss index 4d0602d7d..091771b59 100644 --- a/src/app/core/substance-form/structural-modifications/substance-form-structural-modifications-card.component.scss +++ b/src/app/core/substance-form/structural-modifications/substance-form-structural-modifications-card.component.scss @@ -3,18 +3,18 @@ } .mat-divider { - border-top-color: rgba(0, 0, 0, .12); + border-top-color: var(--box-shadow-color-3); } .code { &:nth-child(odd) { - background-color: rgba(68, 138, 255, .07); + background-color: var(--nth-child-color-2); ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(68, 138, 255, .15); + background-color: var(--nth-child-color-3); } } } @@ -23,14 +23,14 @@ ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(128, 128, 128, .15); + background-color: var(--nth-child-color-1); } } } ::ng-deep { .mat-expansion-panel, .mat-table, textarea { - background-color: transparent; + background-color: var(--regular-transparent-color); } } } diff --git a/src/app/core/substance-form/structural-modifications/substance-form-structural-modifications.module.ts b/src/app/core/substance-form/structural-modifications/substance-form-structural-modifications.module.ts index 73b00bd2c..b9a6f233a 100644 --- a/src/app/core/substance-form/structural-modifications/substance-form-structural-modifications.module.ts +++ b/src/app/core/substance-form/structural-modifications/substance-form-structural-modifications.module.ts @@ -13,7 +13,7 @@ import { MatInputModule } from '@angular/material/input'; import {SubstanceFormStructuralModificationsCardComponent} from '@gsrs-core/substance-form/structural-modifications/substance-form-structural-modifications-card.component'; import { StructuralModificationFormComponent } from './structural-modification-form.component'; import { SubstanceSelectorModule } from '@gsrs-core/substance-selector/substance-selector.module'; -import { MatOptionModule, MatSelectModule } from '@angular/material'; +import { MatSelectModule } from '@angular/material/select'; @NgModule({ imports: [ @@ -28,7 +28,6 @@ import { MatOptionModule, MatSelectModule } from '@angular/material'; ReactiveFormsModule, FormsModule, MatPaginatorModule, - MatOptionModule, MatSelectModule, MatInputModule, SubstanceSelectorModule diff --git a/src/app/core/substance-form/structural-units/structural-unit-form.component.html b/src/app/core/substance-form/structural-units/structural-unit-form.component.html index a1000fd4d..5cf379b35 100644 --- a/src/app/core/substance-form/structural-units/structural-unit-form.component.html +++ b/src/app/core/substance-form/structural-units/structural-unit-form.component.html @@ -6,7 +6,7 @@
    @@ -36,7 +36,7 @@
    Connectivity -
    diff --git a/src/app/core/substance-form/structural-units/structural-unit-form.component.scss b/src/app/core/substance-form/structural-units/structural-unit-form.component.scss index 078f79913..849d34612 100644 --- a/src/app/core/substance-form/structural-units/structural-unit-form.component.scss +++ b/src/app/core/substance-form/structural-units/structural-unit-form.component.scss @@ -7,6 +7,7 @@ .related-substance { max-width: 20%; width: 20%; + cursor: pointer; } ::ng-deep .related-substance img { @@ -30,12 +31,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .form-row { @@ -52,10 +53,6 @@ padding-right: 15px; } - .connectivity { - width: 50%; - } - .amount { width: 50%; padding-right: 15px; @@ -81,7 +78,6 @@ } .connectivity-container { - width:50%; display:flex; flex-direction:column; } @@ -95,7 +91,7 @@ .inner-row-bottom { display:flex; .connectivity { - width: 50%; + /* width: 50%;*/ } .amount { @@ -104,7 +100,6 @@ } } - .references-container { width: 100%; } diff --git a/src/app/core/substance-form/structural-units/structural-unit-form.component.ts b/src/app/core/substance-form/structural-units/structural-unit-form.component.ts index f1a6af895..99ed9a0c6 100644 --- a/src/app/core/substance-form/structural-units/structural-unit-form.component.ts +++ b/src/app/core/substance-form/structural-units/structural-unit-form.component.ts @@ -22,6 +22,7 @@ export class StructuralUnitFormComponent implements OnInit { siteDisplay: string; substanceType: string; errors = []; + hidePopup: boolean = false; constructor( private cvService: ControlledVocabularyService, @@ -30,13 +31,23 @@ export class StructuralUnitFormComponent implements OnInit { private substanceService: SubstanceService, private overlayContainerService: OverlayContainer, private substanceFormService: SubstanceFormService - ) { } + ) { + this.substanceService.showImagePopup.subscribe (data => { + this.hidePopup = data; + }) + } ngOnInit() { this.overlayContainer = this.overlayContainerService.getContainerElement(); } + showHidePopup(): void { + this.hidePopup = !this.hidePopup; + this.substanceService.showImagePopup.next(this.hidePopup); + this.substanceService.imagePopupUnit.next(this.unit); + } + openAmountDialog(): void { if (!this.unit.amount) { diff --git a/src/app/core/substance-form/structural-units/substance-form-structural-units-card.component.scss b/src/app/core/substance-form/structural-units/substance-form-structural-units-card.component.scss index 0aea0c91a..59198bfc5 100644 --- a/src/app/core/substance-form/structural-units/substance-form-structural-units-card.component.scss +++ b/src/app/core/substance-form/structural-units/substance-form-structural-units-card.component.scss @@ -3,18 +3,18 @@ } .mat-divider { - border-top-color: rgba(0, 0, 0, .12); + border-top-color: var(--box-shadow-color-3); } .code { &:nth-child(odd) { - background-color: rgba(68, 138, 255, .07); + background-color: var(--nth-child-color-2); ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(68, 138, 255, .15); + background-color: var(--nth-child-color-3); } } } @@ -23,14 +23,14 @@ ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(128, 128, 128, .15); + background-color: var(--nth-child-color-1); } } } ::ng-deep { .mat-expansion-panel, .mat-table, textarea { - background-color: transparent; + background-color: var(--regular-transparent-color); } } } diff --git a/src/app/core/substance-form/structural-units/substance-form-structural-units.service.ts b/src/app/core/substance-form/structural-units/substance-form-structural-units.service.ts index 69f0f4a8e..016cde838 100644 --- a/src/app/core/substance-form/structural-units/substance-form-structural-units.service.ts +++ b/src/app/core/substance-form/structural-units/substance-form-structural-units.service.ts @@ -50,7 +50,7 @@ export class SubstanceFormStructuralUnitsService extends SubstanceFormServiceBas private setSRUConnectivityDisplay(srus: any) { const rmap = this.getAttachmentMapUnits(srus); - // tslint:disable-next-line:forin + // eslint-disable-next-line guard-for-in for (const i in srus) { const disp = this.sruConnectivityToDisplay(srus[i].attachmentMap, rmap); srus[i]._displayConnectivity = disp; @@ -59,7 +59,7 @@ export class SubstanceFormStructuralUnitsService extends SubstanceFormServiceBas private getAttachmentMapUnits(srus: any) { const rmap = {}; - // tslint:disable-next-line:forin + // eslint-disable-next-line guard-for-in for (const i in srus) { let lab = srus[i].label; if (!lab) { @@ -79,7 +79,7 @@ export class SubstanceFormStructuralUnitsService extends SubstanceFormServiceBas for (const k in amap) { if (amap.hasOwnProperty(k)) { const start = rmap[k] + '_' + k; - // tslint:disable-next-line:forin + // eslint-disable-next-line guard-for-in for (const i in amap[k]) { const end = rmap[amap[k][i]] + '_' + amap[k][i]; disp += start + '-' + end + ';\n'; diff --git a/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-organism/substance-form-structurally-diverse-organism.component.html b/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-organism/substance-form-structurally-diverse-organism.component.html index 523b0b4b2..bd01fef34 100644 --- a/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-organism/substance-form-structurally-diverse-organism.component.html +++ b/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-organism/substance-form-structurally-diverse-organism.component.html @@ -74,7 +74,7 @@ - +
    diff --git a/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-organism/substance-form-structurally-diverse-organism.component.ts b/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-organism/substance-form-structurally-diverse-organism.component.ts index d9e74cc9a..71879c265 100644 --- a/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-organism/substance-form-structurally-diverse-organism.component.ts +++ b/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-organism/substance-form-structurally-diverse-organism.component.ts @@ -20,6 +20,7 @@ export class SubstanceFormStructurallyDiverseOrganismComponent extends Substance maternalUuid: string; parentUuid: string; structurallyDiverse: StructurallyDiverse; + temporaryPart: string; private subscriptions: Array = []; constructor( @@ -35,14 +36,26 @@ export class SubstanceFormStructurallyDiverseOrganismComponent extends Substance const structurallyDiverseSubscription = this.substanceFormStructurallyDiverseService .substanceStructurallyDiverse.subscribe(structurallyDiverse => { this.structurallyDiverse = structurallyDiverse; - this.part = this.structurallyDiverse.$$diverseType; + // this.part = this.structurallyDiverse.$$diverseType; + // when the form submits, $$diversetype is stripped so part must be used in it's place + if (this.structurallyDiverse.$$diverseType) { + this.part = this.structurallyDiverse.$$diverseType; + } if (this.part === 'whole') { this.menuLabelUpdate.emit('Organism Details'); this.maternalUuid = this.structurallyDiverse.hybridSpeciesMaternalOrganism && this.structurallyDiverse.hybridSpeciesMaternalOrganism.refuuid || ''; this.paternalUuid = this.structurallyDiverse.hybridSpeciesPaternalOrganism && this.structurallyDiverse.hybridSpeciesPaternalOrganism.refuuid || ''; - } else { + this.parentUuid = this.structurallyDiverse.parentSubstance + && this.structurallyDiverse.parentSubstance.refuuid || null; + } else if (this.part === 'full_fields') { + this.menuLabelUpdate.emit('Organism Details / Parts And Fractions'); + this.maternalUuid = this.structurallyDiverse.hybridSpeciesMaternalOrganism + && this.structurallyDiverse.hybridSpeciesMaternalOrganism.refuuid || ''; + this.paternalUuid = this.structurallyDiverse.hybridSpeciesPaternalOrganism + && this.structurallyDiverse.hybridSpeciesPaternalOrganism.refuuid || ''; + } else { this.menuLabelUpdate.emit('Parts And Fractions'); this.parentUuid = this.structurallyDiverse.parentSubstance && this.structurallyDiverse.parentSubstance.refuuid || ''; @@ -63,17 +76,23 @@ export class SubstanceFormStructurallyDiverseOrganismComponent extends Substance } paternalUpdated(substance: SubstanceSummary): void { - const relatedSubstance: SubstanceRelated = { - refPname: substance._name, - name: substance._name, - refuuid: substance.uuid, - substanceClass: 'reference', - approvalID: substance.approvalID - }; - this.structurallyDiverse.hybridSpeciesPaternalOrganism = relatedSubstance; + if (substance !== null) { + const relatedSubstance: SubstanceRelated = { + refPname: substance._name, + name: substance._name, + refuuid: substance.uuid, + substanceClass: 'reference', + approvalID: substance.approvalID + }; + this.structurallyDiverse.hybridSpeciesPaternalOrganism = relatedSubstance; + } else { + this.structurallyDiverse.hybridSpeciesPaternalOrganism = null; + } + } maternalUpdated(substance: SubstanceSummary): void { + if (substance !== null) { const relatedSubstance: SubstanceRelated = { refPname: substance._name, name: substance._name, @@ -82,17 +101,25 @@ export class SubstanceFormStructurallyDiverseOrganismComponent extends Substance approvalID: substance.approvalID }; this.structurallyDiverse.hybridSpeciesMaternalOrganism = relatedSubstance; + } else { + this.structurallyDiverse.hybridSpeciesMaternalOrganism = null; + } } sourceMaterialUpdated(substance: SubstanceSummary): void { - const relatedSubstance: SubstanceRelated = { - refPname: substance._name, - name: substance._name, - refuuid: substance.uuid, - substanceClass: 'reference', - approvalID: substance.approvalID - }; - this.structurallyDiverse.parentSubstance = relatedSubstance; +if (substance !== null) { + + const relatedSubstance: SubstanceRelated = { + refPname: substance._name, + name: substance._name, + refuuid: substance.uuid, + substanceClass: 'reference', + approvalID: substance.approvalID + }; + this.structurallyDiverse.parentSubstance = relatedSubstance; +} else { + this.structurallyDiverse.parentSubstance = null; +} } updateLocation(event): void { diff --git a/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-source/substance-form-structurally-diverse-source.component.html b/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-source/substance-form-structurally-diverse-source.component.html index 040f4762d..3bbb77d5d 100644 --- a/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-source/substance-form-structurally-diverse-source.component.html +++ b/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-source/substance-form-structurally-diverse-source.component.html @@ -10,14 +10,17 @@ (valueChange) = "update('state',$event)" [model] = "structurallyDiverse.sourceMaterialState">
    - Whole - Part/ Fraction - Full Fields + Whole + Part/ Fraction + Full Fields
    - +
    + +
    Changes made to the now hidden form will still be submitted
    diff --git a/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-source/substance-form-structurally-diverse-source.component.scss b/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-source/substance-form-structurally-diverse-source.component.scss index b97738260..b17cc047f 100644 --- a/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-source/substance-form-structurally-diverse-source.component.scss +++ b/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-source/substance-form-structurally-diverse-source.component.scss @@ -9,13 +9,12 @@ padding: 10px; align-items: flex-end; } + .form-row { display: flex; justify-content: space-between; align-items: flex-end; - - .checkbox-container, .radio-container { padding-bottom: 18px; } @@ -31,7 +30,6 @@ } - .column-radio { ::ng-deep .mat-radio-label { flex-direction: column-reverse; @@ -41,20 +39,17 @@ padding-left: 0; font-size: 11px; padding-bottom: 4px; - color: rgba(0, 0, 0, 0.54); + color: var(--dark-label-color); font-weight: 400; font-family: Roboto, "Helvetica Neue", sans-serif; } } - ::ng-deep .related-substance img { max-width:125px !important; margin: auto; } - - .type { ::ng-deep mat-radio-button { padding-right:15px; diff --git a/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-source/substance-form-structurally-diverse-source.component.ts b/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-source/substance-form-structurally-diverse-source.component.ts index c356b1926..7e2bf0fe8 100644 --- a/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-source/substance-form-structurally-diverse-source.component.ts +++ b/src/app/core/substance-form/structurally-diverse/substance-form-structurally-diverse-source/substance-form-structurally-diverse-source.component.ts @@ -17,6 +17,7 @@ export class SubstanceFormStructurallyDiverseSourceComponent extends SubstanceF confirm = false; structurallyDiverse: StructurallyDiverse; private subscriptions: Array = []; + part?: string; constructor( private substanceFormStructurallyDiverseService: SubstanceFormStructurallyDiverseService, @@ -36,17 +37,22 @@ export class SubstanceFormStructurallyDiverseSourceComponent extends SubstanceF if (this.structurallyDiverse.part.length === 1 && this.structurallyDiverse.part[0].toUpperCase() === ('WHOLE')) { if (this.checkParts() === false) { this.structurallyDiverse.$$diverseType = 'full_fields'; + this.part = 'full_fields'; } else { this.structurallyDiverse.$$diverseType = 'whole'; + this.part = 'whole'; } } else { if (this.checkWhole() === false) { this.structurallyDiverse.$$diverseType = 'full_fields'; + this.part = 'full_fields'; } else { this.structurallyDiverse.$$diverseType = 'fraction'; + this.part = 'fraction'; + } @@ -63,6 +69,10 @@ export class SubstanceFormStructurallyDiverseSourceComponent extends SubstanceF ngAfterViewInit() { } + updateAccess(access: Array): void { + this.structurallyDiverse.access = access; + } + update(field: string, event: any): void { if (field === 'type') { @@ -107,6 +117,9 @@ export class SubstanceFormStructurallyDiverseSourceComponent extends SubstanceF updateType(event: any): void { this.confirm = false; + if (event.value && event.value !== '' && event.value !== null) { + this.part = event.value; + } if (event.value === 'whole') { if (this.checkParts()) { this.confirm = false; diff --git a/src/app/core/substance-form/structure/structure-form.component.html b/src/app/core/substance-form/structure/structure-form.component.html index 8ded5ae56..0f303bba6 100644 --- a/src/app/core/substance-form/structure/structure-form.component.html +++ b/src/app/core/substance-form/structure/structure-form.component.html @@ -66,8 +66,8 @@
    - - +
    diff --git a/src/app/core/substance-form/structure/structure-form.component.scss b/src/app/core/substance-form/structure/structure-form.component.scss index a4972f244..45c97372b 100644 --- a/src/app/core/substance-form/structure/structure-form.component.scss +++ b/src/app/core/substance-form/structure/structure-form.component.scss @@ -51,23 +51,20 @@ } .WARNING { - color: #8a6d3b; - background-color: #fcf8e3; + color: var(--warning-dialog-color); + background-color: var(--warning-dialog-bg-color); } - .ERROR { - color: #a94442; - background-color: #f2dede; + color: var(--error-dialog-color); + background-color: var(--error-dialog-bg-color); } .dupe-check { margin-bottom: 5px; - } .internal-link { - color: #448aff; + color: var(--link-primary-color); } - diff --git a/src/app/core/substance-form/structure/structure-form.component.ts b/src/app/core/substance-form/structure/structure-form.component.ts index 0749aa1e2..7668ac683 100644 --- a/src/app/core/substance-form/structure/structure-form.component.ts +++ b/src/app/core/substance-form/structure/structure-form.component.ts @@ -17,7 +17,7 @@ export class StructureFormComponent implements OnInit, OnDestroy { opticalActivityList: Array = []; atropisomerismList: Array = []; optical: string; - @Input() hideAccess = false; + @Input() hideAccess = true; @Input() showSettings = false; @Input() type?: string; @Output() structureImported = new EventEmitter(); diff --git a/src/app/core/substance-form/structure/substance-form-structure-card.component.html b/src/app/core/substance-form/structure/substance-form-structure-card.component.html index 2180dafb0..0d2651b52 100644 --- a/src/app/core/substance-form/structure/substance-form-structure-card.component.html +++ b/src/app/core/substance-form/structure/substance-form-structure-card.component.html @@ -37,7 +37,7 @@
    - - + + + +
    - + +
    \ No newline at end of file diff --git a/src/app/core/substance-form/submit-success-dialog/submit-success-dialog.component.ts b/src/app/core/substance-form/submit-success-dialog/submit-success-dialog.component.ts index 98a1f0faa..d259704b9 100644 --- a/src/app/core/substance-form/submit-success-dialog/submit-success-dialog.component.ts +++ b/src/app/core/substance-form/submit-success-dialog/submit-success-dialog.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit, Inject } from '@angular/core'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { ConfigService } from '@gsrs-core/config'; @Component({ selector: 'app-submit-success-dialog', @@ -10,9 +11,14 @@ export class SubmitSuccessDialogComponent implements OnInit { dialogTitle: string; dialogMessage: string = "Update was performed."; fileUrl: string = null; + appId = ""; + + public isCoreSubstance = 'true'; + public staging = false; constructor( public dialogRef: MatDialogRef, + private configService: ConfigService, @Inject(MAT_DIALOG_DATA) public data: any ) { switch (data.type) { @@ -36,9 +42,19 @@ export class SubmitSuccessDialogComponent implements OnInit { } ngOnInit() { + if (this.data) { + if (this.data.isCoreSubstance) { + this.isCoreSubstance = this.data.isCoreSubstance; + } + if (this.data.type && this.data.type === 'staging') { + this.staging = true; + } + } + this.appId = (this.configService.configData && this.configService.configData.appId) || null; } - dismissDialog(action: 'continue'|'browse'|'view'|'viewInPfda'): void { + + dismissDialog(action: 'continue' | 'browse' | 'view' | 'home' | 'staging'|'viewInPfda'): void { this.dialogRef.close(action); } diff --git a/src/app/core/substance-form/substance-drafts/substance-drafts.component.html b/src/app/core/substance-form/substance-drafts/substance-drafts.component.html new file mode 100644 index 000000000..27917d9e4 --- /dev/null +++ b/src/app/core/substance-form/substance-drafts/substance-drafts.component.html @@ -0,0 +1,81 @@ +
    + +
    + +
    Saved Drafts
    + + +
    + +
    + +
    Use select
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Delete Type {{draft.type}} Name {{draft.name}} Date Saved {{draft.date | date:'medium'}} [AUTOSAVE]
    {{draft.fromNow}}
    UUID {{draft.uuid}} Use
    +
    + No drafts were found for these conditions under local storage. Substance drafts are stored in this browser's cache, so using an incognito tab or clearing cache + will clear or not allow access to drafts. You can save and load stored record drafts using the buttons below. +
    + + + + + + + +
    + +
    +
    + + Show only current record + +
    + +
    + + Show only new registrations + +
    +
    + +
    + + Save Backup + +
    + +
    {{filename? filename: 'no file chosen'}}
    + +
    + + + +
    +
    \ No newline at end of file diff --git a/src/app/core/substance-form/substance-drafts/substance-drafts.component.scss b/src/app/core/substance-form/substance-drafts/substance-drafts.component.scss new file mode 100644 index 000000000..cd265d50e --- /dev/null +++ b/src/app/core/substance-form/substance-drafts/substance-drafts.component.scss @@ -0,0 +1,28 @@ +.form-row { + display:flex; + width: 100%; + flex-direction: row; +} + +.title { + font-size: 1.17em; + font-weight: bold; + padding-top: 15px; + +} + +.head { + padding-bottom:15px; +} + +.italics { + font-style: italic; + color: var(--text-color); +} + +.file-row { + width: 40%; + height: 40px; + display: flex; + flex-direction: row; +} diff --git a/src/app/core/substance-form/substance-drafts/substance-drafts.component.spec.ts b/src/app/core/substance-form/substance-drafts/substance-drafts.component.spec.ts new file mode 100644 index 000000000..db933c2db --- /dev/null +++ b/src/app/core/substance-form/substance-drafts/substance-drafts.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SubstanceDraftsComponent } from './substance-drafts.component'; + +describe('SubstanceDraftsComponent', () => { + let component: SubstanceDraftsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ SubstanceDraftsComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(SubstanceDraftsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-form/substance-drafts/substance-drafts.component.ts b/src/app/core/substance-form/substance-drafts/substance-drafts.component.ts new file mode 100644 index 000000000..a1fa75743 --- /dev/null +++ b/src/app/core/substance-form/substance-drafts/substance-drafts.component.ts @@ -0,0 +1,229 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { SubstanceService } from '@gsrs-core/substance/substance.service'; +import { SubstanceFormService } from '@gsrs-core/substance-form/substance-form.service'; +import {MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog'; +import { UtilsService } from '@gsrs-core/utils'; +import { Sort } from '@angular/material/sort'; +import { DomSanitizer } from '@angular/platform-browser'; +import { FormGroup } from '@angular/forms'; +import { Router } from '@angular/router'; +import * as moment from 'moment'; + +@Component({ + selector: 'app-substance-drafts', + templateUrl: './substance-drafts.component.html', + styleUrls: ['./substance-drafts.component.scss'] +}) +export class SubstanceDraftsComponent implements OnInit { + draft: SubstanceDraft; + displayedColumns: string[] = ['delete', 'type', 'name', 'uuid', 'date', 'load']; + + json: any; + values: Array; + filtered: Array; + onlyRegister = false; + onlyCurrent = false; + downloadJsonHref: any; + fileName: string; + filename: string; + uploadForm: FormGroup; + view = 'edit'; + file: any; + uuid: string; + + constructor( + private substanceFormService: SubstanceFormService, + public dialogRef: MatDialogRef, + private utilsService: UtilsService, + private sanitizer: DomSanitizer, + private router: Router, + @Inject(MAT_DIALOG_DATA) public data: any + ) { + this.view = data.view || null; + this.data = data; + } + + ngOnInit(): void { + if (!this.view || this.data.view !== 'user') { + this.json = this.substanceFormService.cleanSubstance(); + this.uuid = this.json.uuid; + + } + + this.fetchDrafts(); + const time = new Date().getTime(); + this.fileName = 'gsrs-drafts-' + time; + this.download(); + + + } + + + download() { + const uri = this.sanitizer.bypassSecurityTrustUrl('data:text/json;charset=UTF-8,' + encodeURIComponent(JSON.stringify(this.values))); + this.downloadJsonHref = uri; + } + + fileChanged(e) { + this.file = e.target.files[0]; + } + + readFile() { + var reader = new FileReader(); + reader.onload = (e) => { + const file = e.target.result; + this.filtered = JSON.parse(file); + this.values = JSON.parse(file); + }; + reader.readAsText(this.file); +} + + + filterToggle(value:string) { + if (value === 'register') { + this.onlyRegister = !this.onlyRegister; + } else { + this.onlyCurrent = !this.onlyCurrent; + } + + this.filtered = this.values; + if (this.onlyCurrent) { + this.filtered = this.filtered.filter(obj => { + if(this.uuid) { + return obj.uuid == this.uuid; + } else if (this.json && this.json.uuid){ + return obj.uuid == this.json.uuid; + } else { + return false; + } + }); + } + + if (this.onlyRegister) { + this.filtered = this.filtered.filter(function( obj ) { + return obj.uuid === 'register'; + }); + } + + } + + + useDraft(index) { + + this.dialogRef.close(index); + } + + + sortData(sort: Sort) { + if (!sort.active || sort.direction === '') { + // this.filtered = data; + return; + } + const data = this.filtered.slice(); + this.filtered = data.sort((a, b) => { + const isAsc = sort.direction === 'asc'; + return this.utilsService.compare(a[sort.active] ? a[sort.active] : null, b[sort.active] ? b[sort.active] : null, isAsc); + }); + } + + deleteDraft(draft: any): void { + localStorage.removeItem(draft.key); + this.filtered = this.filtered.filter(function( obj ) { + return obj.key !== draft.key; + }); + + this.values = this.values.filter(function( obj ) { + return obj.key !== draft.key; +}); + + } + + onFileSelect(event): void { + if (event.target.files.length > 0) { + this.file = event.target.files[0]; + this.filename = this.file.name; + // this.uploadForm.get('file').setValue(this.file); + this.readFile() + } + } + + openInput(): void { + document.getElementById('fileInput').click(); + } + + fetchDrafts() { + + this.values = []; + let keys = Object.keys(localStorage); + let i = keys.length; + + while ( i-- ) { + if (keys[i].startsWith('gsrs-draft-')){ + const entry = JSON.parse(localStorage.getItem(keys[i])); + entry.key = keys[i]; + entry.fromNow = moment(entry.date).fromNow(); + this.values.push( entry ); + + } + } + this.filtered = this.values.sort((a, b) => { + return b.date - a.date; + });; + + if (this.json && this.json.uuid) { + this.filterToggle('substance'); + } else { + this.filterToggle('register'); + } + } + + + saveDraft() { + this.json = this.substanceFormService.cleanSubstance(); + const time = new Date().getTime(); + const file = 'gsrs-draft-' + time; + const uuid = this.json.uuid ? this.json.uuid : 'register'; + const type = this.json.substanceClass; + let primary = null; + this.json.names.forEach(name => { + if (name.displayName) { + primary = name.name; + } + }); + if (!primary && this.json.names.length > 0) { + primary = this.json.names[0].name; + } + + let draft = { + 'uuid': uuid, + 'date': time, + 'type': type, + 'name': primary, + 'substance': this.json + } + + localStorage.setItem(file, JSON.stringify(draft)); + + } + + + close() { + this.dialogRef.close(); + } + +} + + + + +export interface SubstanceDraft { + key: string; + uuid: any; + date: string; + type: string; + name?: string; + substance: any; + auto?: boolean; + file?: any; + fromNow?: string; +} \ No newline at end of file diff --git a/src/app/core/substance-form/substance-form-definition/substance-form-definition.component.html b/src/app/core/substance-form/substance-form-definition/substance-form-definition.component.html index 1959792dc..35caa633d 100644 --- a/src/app/core/substance-form/substance-form-definition/substance-form-definition.component.html +++ b/src/app/core/substance-form/substance-form-definition/substance-form-definition.component.html @@ -6,10 +6,10 @@ {{definition.approvalID}}
    -
    Preferred Term:
    +
    {{ 'displayNameTitle' | elementLabel : 'substance_names_name' }}:
    - + Definition Type @@ -37,10 +37,10 @@ Deprecated
    - +
    +
    +
    + +
    +
    Concepts have no defining information, but are collections of terms, codes and related information.
    They can be promoted to a defined substance at a later time, when appropriate. diff --git a/src/app/core/substance-form/substance-form-definition/substance-form-definition.component.scss b/src/app/core/substance-form/substance-form-definition/substance-form-definition.component.scss index d56e38f0d..3a088f3fc 100644 --- a/src/app/core/substance-form/substance-form-definition/substance-form-definition.component.scss +++ b/src/app/core/substance-form/substance-form-definition/substance-form-definition.component.scss @@ -4,7 +4,7 @@ } .approval { - color: #c7254e; + color: var(--pink-span-color); font-size: 24px; } @@ -13,7 +13,7 @@ } .section-header { - color: rgba(0, 0, 0, 0.54); + color: var(--dark-label-color); font-size: 11px; text-align: left; letter-spacing: .005px; @@ -44,7 +44,7 @@ .redirect-links { a{ - color: #4793d1!important; + color: var(--primary-color)!important; float:right; text-decoration:none; } @@ -54,7 +54,6 @@ } } - .icon { font-size:10px !important; } @@ -68,7 +67,7 @@ padding-left: 5px; font-size: 11px; padding-bottom: 4px; - color: rgba(0, 0, 0, 0.54); + color: var(--dark-label-color); font-weight: 400; font-family: Roboto, "Helvetica Neue", sans-serif; } @@ -82,14 +81,13 @@ width: 50%; } - .form-name { font-size: 22px; padding-bottom: 15px; } .name { - color: blue; + color: var(--regular-blue-color); padding-left:5px; } diff --git a/src/app/core/substance-form/substance-form-definition/substance-form-definition.component.ts b/src/app/core/substance-form/substance-form-definition/substance-form-definition.component.ts index d6b608b8e..536b6b523 100644 --- a/src/app/core/substance-form/substance-form-definition/substance-form-definition.component.ts +++ b/src/app/core/substance-form/substance-form-definition/substance-form-definition.component.ts @@ -7,12 +7,15 @@ import { SubstanceService } from '../../substance/substance.service'; import { SubstanceSummary, SubstanceRelationship } from '../../substance/substance.model'; import { SubstanceFormService } from '../substance-form.service'; import { SubstanceFormDefinition } from '../substance-form.model'; -import { MatChipInputEvent, MatAutocompleteSelectedEvent } from '@angular/material'; +import {MatChipInputEvent} from '@angular/material/chips'; +import {MatAutocompleteSelectedEvent} from '@angular/material/autocomplete'; import {COMMA, ENTER} from '@angular/cdk/keycodes'; import { FormControl } from '@angular/forms'; import { OverlayContainer } from '@angular/cdk/overlay'; import { Subscription } from 'rxjs'; import { take } from 'rxjs/operators'; +import { ConfigService } from '@gsrs-core/config'; +import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'app-substance-form-definition', @@ -30,6 +33,7 @@ export class SubstanceFormDefinitionComponent extends SubstanceFormBase implemen feature: string; substanceClass: string; status: string; + accessLabel?:string; readonly separatorKeysCodes: number[] = [ENTER, COMMA]; tagsCtrl = new FormControl({value: '', disabled: true}); private suggestedTags: Array; @@ -37,14 +41,19 @@ export class SubstanceFormDefinitionComponent extends SubstanceFormBase implemen private usedSuggestedTags: Array = []; private overlayContainer: HTMLElement; private subscriptions: Array = []; + defAccess: Array; @ViewChild('tagsInput', { read: ElementRef, static: false }) tagsInput: ElementRef; imported = false; + oldlink = false; constructor( private cvService: ControlledVocabularyService, public substanceService: SubstanceService, private substanceFormService: SubstanceFormService, - private overlayContainerService: OverlayContainer + private overlayContainerService: OverlayContainer, + private configService: ConfigService, + private activatedRoute: ActivatedRoute, + ) { super(); } @@ -63,10 +72,16 @@ export class SubstanceFormDefinitionComponent extends SubstanceFormBase implemen this.filteredSuggestedTags = this.suggestedTags.filter(tag => tag.toLowerCase().indexOf((value || '').toLowerCase()) > -1); }); this.subscriptions.push(tagsSubscription); + + if (this.configService.configData && this.configService.configData.showOldLinks) { + this.oldlink = true; + + } } ngAfterViewInit() { const subscription = this.substanceFormService.definition.subscribe(definition => { + this.defAccess = this.substanceFormService.getDefinitionForDefRef(); this.definition = definition || {}; this.crossCheckTags(); if (this.definition.substanceClass === 'structure') { @@ -113,7 +128,8 @@ export class SubstanceFormDefinitionComponent extends SubstanceFormBase implemen } this.uuid = this.substanceFormService.getUuid(); const imported = this.substanceFormService.getMethod(); - if (imported && imported === 'import') { + const source = this.activatedRoute.snapshot.queryParams['source'] || null; + if (imported && imported === 'import' && (!source || source !== 'draft')) { this.imported = true; } else { this.imported = false; @@ -169,6 +185,7 @@ export class SubstanceFormDefinitionComponent extends SubstanceFormBase implemen setPrimarySubstance(substance: SubstanceSummary): void { this.primarySubstance = substance; + this.primarySubUuid = substance.uuid; if (this.definition.relationships == null || Object.prototype.toString.call(this.definition.relationships) !== '[object Array]') { @@ -195,7 +212,7 @@ export class SubstanceFormDefinitionComponent extends SubstanceFormBase implemen removePrimarySubstance(): void { const indexToRemove = this.definition.relationships - .findIndex((relationship) => relationship.relatedSubstance.refuuid === this.primarySubstance.uuid); + .findIndex((relationship) => relationship.relatedSubstance.refuuid === this.primarySubUuid); this.definition.relationships.splice(indexToRemove, 1); this.primarySubstance = null; this.substanceFormService.updateDefinition(this.definition); @@ -206,6 +223,11 @@ export class SubstanceFormDefinitionComponent extends SubstanceFormBase implemen this.substanceFormService.updateDefinition(this.definition); } + updateDefAccess(access: Array): void { + this.substanceFormService.setDefinitionFromDefRef(access); + this.substanceFormService.updateDefinition(this.definition); + } + updateDefinition(): void { this.substanceFormService.updateDefinition(this.definition); } diff --git a/src/app/core/substance-form/substance-form-definition/substance-form-definition.module.ts b/src/app/core/substance-form/substance-form-definition/substance-form-definition.module.ts index 4f0bf15ef..fcbfca8cd 100644 --- a/src/app/core/substance-form/substance-form-definition/substance-form-definition.module.ts +++ b/src/app/core/substance-form/substance-form-definition/substance-form-definition.module.ts @@ -16,7 +16,9 @@ import {MatTooltipModule} from '@angular/material/tooltip'; import {RouterModule} from '@angular/router'; import {MatRadioModule} from '@angular/material/radio'; import {MatChipsModule} from '@angular/material/chips'; -import { MatAutocompleteModule, MatProgressBarModule } from '@angular/material'; +import {MatProgressBarModule} from '@angular/material/progress-bar'; +import {MatAutocompleteModule} from '@angular/material/autocomplete'; +import { ElementLabelDisplayModule } from '@gsrs-core/utils/element-label-display.module'; @NgModule({ imports: [ @@ -38,7 +40,8 @@ import { MatAutocompleteModule, MatProgressBarModule } from '@angular/material'; MatRadioModule, MatChipsModule, MatAutocompleteModule, - MatProgressBarModule + MatProgressBarModule, + ElementLabelDisplayModule ], declarations: [ SubstanceFormDefinitionComponent diff --git a/src/app/core/substance-form/substance-form-section.ts b/src/app/core/substance-form/substance-form-section.ts index aaf05759b..405cd6cc3 100644 --- a/src/app/core/substance-form/substance-form-section.ts +++ b/src/app/core/substance-form/substance-form-section.ts @@ -1,8 +1,9 @@ -import { ComponentRef, Output, EventEmitter } from '@angular/core'; +import { ComponentRef, Output, EventEmitter, Injectable } from '@angular/core'; import { SubstanceFormBase } from './base-classes/substance-form-base'; import { SubstanceCardBaseFilteredList } from '@gsrs-core/substance-details'; -import { MatExpansionPanel } from '@angular/material'; +import { MatExpansionPanel } from '@angular/material/expansion'; +@Injectable() export class SubstanceFormSection { dynamicComponentName: string; dynamicComponentRef: ComponentRef | any>; diff --git a/src/app/core/substance-form/substance-form-subunits/substance-form-subunits.component.scss b/src/app/core/substance-form/substance-form-subunits/substance-form-subunits.component.scss index f032775b6..7e951c693 100644 --- a/src/app/core/substance-form/substance-form-subunits/substance-form-subunits.component.scss +++ b/src/app/core/substance-form/substance-form-subunits/substance-form-subunits.component.scss @@ -66,7 +66,7 @@ padding-bottom: 20px; &:not(:last-child) { - border-bottom: solid 1px rgba(0, 0, 0, 0.12); + border-bottom: solid 1px var(--box-shadow-color-3); } } @@ -87,7 +87,7 @@ } .add-container-bottom { - display:flex; + display: flex; margin-top: 10px; justify-content: flex-end; -} \ No newline at end of file +} diff --git a/src/app/core/substance-form/substance-form-sugars/substance-form-sugars.component.html b/src/app/core/substance-form/substance-form-sugars/substance-form-sugars.component.html index 739231c97..54b87008c 100644 --- a/src/app/core/substance-form/substance-form-sugars/substance-form-sugars.component.html +++ b/src/app/core/substance-form/substance-form-sugars/substance-form-sugars.component.html @@ -10,3 +10,11 @@
    +
    + +
    + +
    +
    diff --git a/src/app/core/substance-form/substance-form-sugars/substance-form-sugars.component.scss b/src/app/core/substance-form/substance-form-sugars/substance-form-sugars.component.scss index b2f3effa6..8e62cbc9d 100644 --- a/src/app/core/substance-form/substance-form-sugars/substance-form-sugars.component.scss +++ b/src/app/core/substance-form/substance-form-sugars/substance-form-sugars.component.scss @@ -10,5 +10,5 @@ .too-many { - color:red; + color:var(--regular-red-color); } diff --git a/src/app/core/substance-form/substance-form.component.html b/src/app/core/substance-form/substance-form.component.html index 64acad9d8..b4783b07e 100644 --- a/src/app/core/substance-form/substance-form.component.html +++ b/src/app/core/substance-form/substance-form.component.html @@ -1,10 +1,18 @@ +
    +
    + x + +
    - + + + + @@ -14,7 +22,7 @@ Advanced Features - + Change Substance Class @@ -33,6 +41,9 @@ Un-approve record (Remove approval ID) + + Change Approval ID + Set concept status to non-approved @@ -50,10 +61,46 @@ Predict disulfide links by monoclonal antibody type + + Register a Fragment +
    + +
    Saved Drafts
    +
    +
    +
    + +
    April 1, 2022 08:05
    +
    +
    +
    +
    + +
    April 1, 2022 08:00
    +
    +
    + +
    +
    +
    register = protein
    +
    April 1, 2022 08:00
    +
    +
    + + +
    + +
    + + +
    +
    + +
    New Class @@ -69,7 +116,7 @@
    @@ -82,17 +129,21 @@
    Please correct or dismiss the following errors and submit again:
    -
    -
    +
    + +
    {{message.messageType}}
    {{message.message}}
    - {{link.text}}
    +
    + +
    @@ -100,6 +151,9 @@ + @@ -119,8 +173,8 @@

    {{section.menuLabel}}

    - @@ -132,4 +186,4 @@

    {{section.menuLabel}}

    -
    \ No newline at end of file +
    diff --git a/src/app/core/substance-form/substance-form.component.scss b/src/app/core/substance-form/substance-form.component.scss index 384fc3fc3..24a8cadae 100644 --- a/src/app/core/substance-form/substance-form.component.scss +++ b/src/app/core/substance-form/substance-form.component.scss @@ -1,13 +1,62 @@ +#overlay { + position: fixed; /* Sit on top of the page content */ + display: block; /* Hidden by default */ + width: 100%; /* Full width (cover the whole page) */ + height: 100%; /* Full height (cover the whole page) */ + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: var(--text-color); /* Black background with opacity */ + z-index: 2; /* Specify a stack order in case you're using a different order for other elements */ + cursor: pointer; /* Add a pointer on hover */ +} +.hide { + display: none!important; +} +.center-screen { + z-index: 2; + position: fixed; + background-color: var(--regular-white-color); + width: 50%; + height: 50%; + top: 50%; + left: 50%; + margin-right: -50%; + transform: translate(-50%, -50%); +} +.center-screen img { + transition-duration: 4s; + margin: 0 auto; + display: block; +} +.center-screen img:hover { + transform: scale(1.7); + -webkit-transform: scale(1.7); + -moz-transform: scale(1.7); + z-index: 0; +} +.image-popped-up { + width: 100%; + height: 100%; + padding: 10px; +} +.close-out { + padding: 10px; + float: right; + font-weight: 500; + cursor: pointer; +} .top-fixed { position: fixed; display: flex; flex-direction: column; top: 64px; width: 100%; - background-color: white; + background-color: var(--regular-white-color); align-items: center; justify-content: center; - box-shadow: 0px 3px 3px -2px rgba(0, 0, 0, 0.2), 0px 3px 4px 0px rgba(0, 0, 0, 0.14), 0px 1px 8px 0px rgba(0, 0, 0, 0.12); + box-shadow: 0px 3px 3px -2px var(--box-shadow-color), 0px 3px 4px 0px var(--box-shadow-color-2), 0px 1px 8px 0px var(--box-shadow-color-3); z-index: 1001; } @@ -23,7 +72,7 @@ mat-select-panel{ margin-left: 30px; margin-right: 5px; margin-top: -10px; - color: #4793d1; + color: var(--primary-color); ::ng-deep .mat-form-field-wrapper { @@ -56,9 +105,9 @@ mat-select-panel{ } .actions-container { - max-width: 1028px; + max-width: 1128px; width: 100%; - background-color: white; + background-color: var(--regular-white-color); padding: 10px; display: flex; } @@ -73,6 +122,17 @@ mat-select-panel{ display: none !important; } +.validation-body { + max-width:95%; + display:flex; + max-width: 960px; +word-break: break-word; +} + +.validation-dismiss { + width: 5%; +} + .submission-messages { overflow: hidden; height: auto; @@ -80,7 +140,7 @@ mat-select-panel{ transition: all 500ms ease-out; max-width: 1028px; width: 100%; - background-color: white; + background-color: var(--regular-white-color); display: flex; flex-direction: column; @@ -102,7 +162,6 @@ mat-select-panel{ .validation-message { display: flex; padding: 5px 0; - align-items: center; .message-type { text-transform: uppercase; @@ -110,6 +169,8 @@ mat-select-panel{ margin-right: 20px; padding:10px; border-radius:3px; + height: 40px; + min-width: 95px; } } @@ -118,15 +179,21 @@ mat-select-panel{ } .warning-message { - color: #8a6d3b; - background-color: #fcf8e3; + color: var(--warning-dialog-color); + background-color: var(--warning-dialog-bg-color); } .error-message { - color: #a94442; - background-color: #f2dede; + color: var(--error-dialog-color); + background-color: var(--error-dialog-bg-color); + } + + .notice-message { + color: var(--notice-dialog-color); + background-color: var(--notice-dialog-bg-color); + } } @@ -145,10 +212,26 @@ mat-select-panel{ } .internal-link { - color: #448aff; + color: var(--link-primary-color); } .import-button { margin-left: 15px; } +.json-button { + margin-left: 30px; +} + +.draft-button { + margin-left: 15px; +} + +.chip { + background-color: var(--regular-white-color); + border-radius: 50%; + padding: 3px 5px; + margin-left: 5px; + color: var(--link-color); + height: 33px; +} diff --git a/src/app/core/substance-form/substance-form.component.ts b/src/app/core/substance-form/substance-form.component.ts index 8b7d72e62..1acbb90ea 100644 --- a/src/app/core/substance-form/substance-form.component.ts +++ b/src/app/core/substance-form/substance-form.component.ts @@ -27,11 +27,19 @@ import * as defiant from '../../../../node_modules/defiant.js/dist/defiant.min.j import { Title } from '@angular/platform-browser'; import { AuthService } from '@gsrs-core/auth'; import { take, map } from 'rxjs/operators'; -import { MatExpansionPanel } from '@angular/material'; +import { MatExpansionPanel } from '@angular/material/expansion'; import { SubmitSuccessDialogComponent } from './submit-success-dialog/submit-success-dialog.component'; import {MergeConceptDialogComponent} from '@gsrs-core/substance-form/merge-concept-dialog/merge-concept-dialog.component'; import {DefinitionSwitchDialogComponent} from '@gsrs-core/substance-form/definition-switch-dialog/definition-switch-dialog.component'; import { SubstanceEditImportDialogComponent } from '@gsrs-core/substance-edit-import-dialog/substance-edit-import-dialog.component'; +import { StructuralUnit } from '@gsrs-core/substance'; +import { ConfigService } from '@gsrs-core/config'; +import { FragmentWizardComponent } from '@gsrs-core/admin/fragment-wizard/fragment-wizard.component'; +import { SubstanceDraftsComponent } from '@gsrs-core/substance-form/substance-drafts/substance-drafts.component'; +import { UtilsService } from '@gsrs-core/utils'; +import { ungzip, deflate, inflate } from 'pako'; +import { Buffer } from 'buffer'; +import { AdminService } from '@gsrs-core/admin/admin.service'; @Component({ @@ -46,7 +54,7 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy @ViewChildren('dynamicComponent', { read: ViewContainerRef }) dynamicComponents: QueryList; @ViewChildren('expansionPanel', { read: MatExpansionPanel }) matExpansionPanels: QueryList; private subClass: string; - private definitionType: string; + definitionType: string; expandedComponents = [ 'substance-form-definition', 'substance-form-structure', @@ -71,7 +79,12 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy messageField: string; uuid: string; substanceClass: string; + drafts: Array; + draftCount = 0; status: string; + hidePopup: boolean; + unit: StructuralUnit; + autoSaveWait = 60000; classes = [ 'concept', 'protein', @@ -82,11 +95,13 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy 'mixture', 'specifiedSubstanceG1', 'specifiedSubstanceG2', - 'specifiedSubstanceG3', - 'specifiedSubstanceG4']; + 'specifiedSubstanceG3']; imported = false; forceChange = false; sameSubstance = false; + UNII: string; + approvalType = 'lastEditedBy'; + previousState: number; constructor( private activatedRoute: ActivatedRoute, @@ -98,12 +113,113 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy private gaService: GoogleAnalyticsService, private substanceFormService: SubstanceFormService, private overlayContainerService: OverlayContainer, + private configService: ConfigService, private dialog: MatDialog, private authService: AuthService, - private titleService: Title + private titleService: Title, + private utilsService: UtilsService ) { + this.substanceService.showImagePopup.subscribe (data => { + this.hidePopup = data; + }) + this.substanceService.imagePopupUnit.subscribe (data => { + this.unit = data; + }) } + showHidePopup(): void { + this.hidePopup = !this.hidePopup; + this.substanceService.showImagePopup.next(this.hidePopup); + } + + + autoSave(): void { + setTimeout(() => { + if (this.substanceFormService.autoSave()) { + this.saveDraft(true); + } else { + } + this.autoSave(); + }, this.autoSaveWait); + } + + openModal(templateRef) { + + const dialogRef = this.dialog.open(templateRef, { + height: '200px', + width: '400px' + }); + this.overlayContainer.style.zIndex = '1002'; + + dialogRef.afterClosed().subscribe(result => { + this.overlayContainer.style.zIndex = null; + }); + } + + showDrafts(): void { + const dialogRef = this.dialog.open(SubstanceDraftsComponent, { + maxHeight: '85%', + width: '70%', + data: {uuid: this.id} + }); + this.overlayContainer.style.zIndex = '1002'; + + dialogRef.afterClosed().subscribe(response => { + this.overlayContainer.style.zIndex = null; + + + if (response) { + this.loadingService.setLoading(true); + // console.log(response.json); + + const read = response.substance; + if (this.id && read.uuid && this.id === read.uuid) { + this.substanceFormService.importSubstance(read, 'update'); + this.submissionMessage = null; + this.validationMessages = []; + this.showSubmissionMessages = false; + setTimeout(() => { + this.loadingService.setLoading(false); + this.isLoading = false; + this.overlayContainer.style.zIndex = null; + }, 1000); + }else if (response.uuid && response.uuid != 'register'){ + const url = '/substances/' + response.uuid + '/edit?action=import&source=draft'; + this.router.navigateByUrl(url, { state: { record: response.substance } }); + } else { + setTimeout(() => { + this.overlayContainer.style.zIndex = null; + this.router.onSameUrlNavigation = 'reload'; + this.loadingService.setLoading(false); + this.router.onSameUrlNavigation = 'reload'; + this.router.navigateByUrl('/substances/register/' + response.substance.substanceClass + '?action=import', { state: { record: response.substance } }); + + }, 1000); + } + } + + let keys = Object.keys(localStorage); + let i = keys.length; + this.draftCount =0; + this.drafts = []; + + while ( i-- ) { + if (keys[i].startsWith('gsrs-draft-')){ + const entry = JSON.parse(localStorage.getItem(keys[i])); + entry.key = keys[i]; + if (this.id && entry.uuid === this.id) { + this.draftCount++; + } else if (!this.id && entry.type === (this.activatedRoute.snapshot.params['type']) && entry.uuid === 'register') { + this.draftCount++; + } + this.drafts.push( entry ); + + } + } + }); + } + + importDialog(): void { const dialogRef = this.dialog.open(SubstanceEditImportDialogComponent, { width: '650px', @@ -114,17 +230,24 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy const dialogSubscription = dialogRef.afterClosed().pipe(take(1)).subscribe(response => { if (response) { + // this.overlayContainer.style.zIndex = null; this.loadingService.setLoading(true); - this.overlayContainer.style.zIndex = null; - const read = JSON.parse(response); + + // attempting to reload a substance without a router refresh has proven to cause issues with the relationship dropdowns + // There are probably other components affected. There is an issue with subscriptions likely due to some OnInit not firing + + const read = JSON.parse(response); if (this.id && read.uuid && this.id === read.uuid) { this.substanceFormService.importSubstance(read, 'update'); this.submissionMessage = null; this.validationMessages = []; this.showSubmissionMessages = false; - this.loadingService.setLoading(false); - this.isLoading = false; - } else { + setTimeout(() => { + this.loadingService.setLoading(false); + this.isLoading = false; + this.overlayContainer.style.zIndex = null; + }, 1000); + /* } else { if ( read.substanceClass === this.substanceClass) { this.imported = true; this.substanceFormService.importSubstance(read); @@ -132,19 +255,20 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy this.validationMessages = []; this.showSubmissionMessages = false; this.loadingService.setLoading(false); - this.isLoading = false; + this.isLoading = false;*/ } else { setTimeout(() => { + this.overlayContainer.style.zIndex = null; this.router.onSameUrlNavigation = 'reload'; this.loadingService.setLoading(false); - this.router.navigateByUrl('/substances/register?action=import', { state: { record: response } }); - + this.router.navigateByUrl('/substances/register?action=import', { state: { record: response } }); + }, 1000); } - } - } + } + // } }); - + } test() { @@ -152,8 +276,16 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy this.router.navigate([this.router.url]); } + + ngOnInit() { this.loadingService.setLoading(true); + if (this.configService.configData && this.configService.configData.approvalType) { + this.approvalType = this.configService.configData.approvalType; + } + if (this.configService.configData && this.configService.configData.autoSaveWait) { + this.autoSaveWait = this.configService.configData.autoSaveWait; + } this.isAdmin = this.authService.hasRoles('admin'); this.isUpdater = this.authService.hasAnyRoles('Updater', 'SuperUpdater'); this.overlayContainer = this.overlayContainerService.getContainerElement(); @@ -161,27 +293,67 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy const routeSubscription = this.activatedRoute .params .subscribe(params => { + + const action = this.activatedRoute.snapshot.queryParams['action'] || null; + if (params['id']) { - const id = params['id']; - if (id !== this.id) { - this.id = id; - this.gaService.sendPageView(`Substance Edit`); - const newType = this.activatedRoute.snapshot.queryParamMap.get('switch') || null; - if (newType) { - this.getSubstanceDetails(newType); - } else { - this.getSubstanceDetails(); + + if(action && action === 'import' && window.history.state) { + const record = window.history.state; + this.imported = true; + + this.getDetailsFromImport(record.record); + } else { + const id = params['id']; + if (id !== this.id) { + this.id = id; + this.gaService.sendPageView(`Substance Edit`); + const newType = this.activatedRoute.snapshot.queryParamMap.get('switch') || null; + if (newType) { + this.getSubstanceDetails(newType); + } else { + this.getSubstanceDetails(); + } } } } else { - const action = this.activatedRoute.snapshot.queryParams['action'] || null; if (action && action === 'import' && window.history.state) { const record = window.history.state; this.imported = true; this.getDetailsFromImport(record.record); this.gaService.sendPageView(`Substance Register`); - } else { + } else if (this.activatedRoute.snapshot.queryParams['stagingID']) { + this.substanceService.GetStagedRecord(this.activatedRoute.snapshot.queryParams['stagingID']).subscribe(response => { + response.uuid = null; + + + if (response._name){ + let name = response._name; + response.names.forEach(current => { + if (current.displayName && current.stdName) { + name = current.stdName; + } + }); + name = name.replace(/<[^>]*>?/gm, ''); + this.titleService.setTitle('Edit - ' + name); + } + if (response) { + this.definitionType = response.definitionType; + this.substanceClass = response.substanceClass; + this.status = response.status; + this.substanceFormService.loadSubstance(response.substanceClass, response).pipe(take(1)).subscribe(() => { + this.setFormSections(formSections[response.substanceClass]); + this.isLoading = false; + this.loadingService.setLoading(false); + }); + + } + }, error => { + this.isLoading = false; + this.loadingService.setLoading(false); + }); + } else { this.copy = this.activatedRoute.snapshot.queryParams['copy'] || null; if (this.copy) { const copyType = this.activatedRoute.snapshot.queryParams['copyType'] || null; @@ -197,11 +369,12 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy this.setFormSections(formSections[this.subClass]); this.loadingService.setLoading(false); this.isLoading = false; + }); }); } } - + } }); @@ -224,19 +397,95 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy this.user = auth.identifier; setTimeout(() => { this.canApprove = this.canBeApproved(); - + }); }); - } + + + +} + +isBase64(str) { + let base64regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/; + let result = decodeURIComponent(str); + return base64regex.test(str); +} + +setStructureFromUrl(structure: string, type: string):void { + // check if structureUR is encoded molfile or raw smiles string, then import into form + /*structure = this.gunzip(structure); + structure = decodeURIComponent(structure); + this.substanceFormService.importStructure(structure, 'molfile');*/ + this.substanceFormService.importStructure(structure, 'smiles'); + +} + + + +gunzip(t): string{ + + const gezipedData = Buffer.from(t, 'base64') +const gzipedDataArray = Uint8Array.from(gezipedData); +const ungzipedData = ungzip(gzipedDataArray); +return new TextDecoder().decode(ungzipedData); +} + +getDrafts() { + let keys = Object.keys(localStorage); + let i = keys.length; + this.drafts = []; + let temp = 0; + while ( i-- ) { + if (keys[i].startsWith('gsrs-draft-')){ + const entry = JSON.parse(localStorage.getItem(keys[i])); + entry.key = keys[i]; + if (this.id && entry.uuid === this.id) { + temp++; + // this.draftCount++; + } else if (!this.id && entry.type === (this.activatedRoute.snapshot.params['type']) && entry.uuid === 'register') { + temp++; + // this.draftCount++; + } + this.drafts.push( entry ); + + } + } + this.draftCount = temp; +} ngAfterViewInit(): void { + this.getDrafts(); + // set structure based on smiles or molfile + const structure = this.activatedRoute.snapshot.queryParams['importStructure'] || null; + if (structure) { + let decode = decodeURIComponent(structure); + console.log(decode); + setTimeout(() => { + this.setStructureFromUrl(decode, 'molfile'); + }); + } + const json = this.activatedRoute.snapshot.queryParams['jsonStructure'] || null; + // TODO add json support + // this.setStructureFromUrl(structure, 'json'); + if (json) { + let decode = decodeURI(json); + } + + + const subscription = this.dynamicComponents.changes .subscribe(() => { + + const total = this.formSections.length; + let finished = 0; if (!this.forceChange) { + this.loadingService.setLoading(true); + const startTime = new Date(); this.dynamicComponents.forEach((cRef, index) => { this.dynamicComponentLoader .getComponentFactory(this.formSections[index].dynamicComponentName) .subscribe(componentFactory => { + this.loadingService.setLoading(true); this.formSections[index].dynamicComponentRef = cRef.createComponent(componentFactory); this.formSections[index].matExpansionPanel = this.matExpansionPanels.find((item, panelIndex) => index === panelIndex); this.formSections[index].dynamicComponentRef.instance.menuLabelUpdate.pipe(take(1)).subscribe(label => { @@ -260,10 +509,31 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy } }); this.formSections[index].dynamicComponentRef.changeDetectorRef.detectChanges(); + finished++; + if (finished >= total) { + this.loadingService.setLoading(false); + } else { + const currentTime = new Date(); + if (currentTime.getTime() - startTime.getTime() > 12000) { + if (confirm('There was a network error while fetching files, would you like to refresh?')) { + window.location.reload(); + } + } + } + setTimeout(() => { + this.loadingService.setLoading(false); + this.UNII = this.substanceFormService.getUNII(); + }, 5); }); }); + // this.loadingService.setLoading(false); + } subscription.unsubscribe(); + setTimeout(() => { + + this.autoSave();},10000); + }); } @@ -313,7 +583,28 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy this.definitionSwitch(); this.feature = undefined; } + if (this.feature === 'changeApproval') { + this.substanceFormService.changeApproval(); + } + if (this.feature === 'fragment') { + this.openFragmentDialog(); + } + + + + } + + openFragmentDialog(): void { + const dialogRef = this.dialog.open(FragmentWizardComponent, { + width: '70%', + height: '70%' + }); + this.overlayContainer.style.zIndex = '50'; + const dialogSubscription = dialogRef.afterClosed().pipe(take(1)).subscribe(response => { + + }); + this.subscriptions.push(dialogSubscription); } changeClass(type: any): void { @@ -353,21 +644,45 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy if (action && action === 'import') { return false; } - if (this.definition && this.definition.lastEditedBy && this.user) { - const lastEdit = this.definition.lastEditedBy; - if (!lastEdit) { - return false; - } - if (this.definition.status === 'approved') { - return false; - } - if (lastEdit === this.user) { + const staging = this.activatedRoute.snapshot.queryParams['stagingID'] || null; + if (staging && staging.length > 0 ) { + return false + } + // if config var set and set to 'createdBy then set approval button enabled if user is not creator + if(this.approvalType === 'createdBy') { + if (this.definition && this.definition.createdBy && this.user) { + const creator = this.definition.createdBy; + if (!creator) { + return false; + } + if (this.definition.status === 'approved') { + return false; + } + if (creator === this.user) { + return false; + } + return true; + + } return false; - } - return true; + //default to 'lastEditedBy' if not set in config + } else { + if (this.definition && this.definition.lastEditedBy && this.user) { + const lastEdit = this.definition.lastEditedBy; + if (!lastEdit) { + return false; + } + if (this.definition.status === 'approved') { + return false; + } + if (lastEdit === this.user) { + return false; + } + return true; + } } - return false; + } showJSON(): void { @@ -385,7 +700,14 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy getSubstanceDetails(newType?: string): void { this.substanceService.getSubstanceDetails(this.id).pipe(take(1)).subscribe(response => { if (response._name){ - this.titleService.setTitle('Edit - ' + response._name); + let name = response._name; + response.names.forEach(current => { + if (current.displayName && current.stdName) { + name = current.stdName; + } + }); + name = name.replace(/<[^>]*>?/gm, ''); + this.titleService.setTitle('Edit - ' + name); } if (response) { this.definitionType = response.definitionType; @@ -420,9 +742,12 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy } getDetailsFromImport(state: any, same?: boolean) { + if(!this.jsonValid(state)) { + state = JSON.stringify(state); + } if (state && this.jsonValid(state)) { const response = JSON.parse(state); - + same = false; this.definitionType = response.definitionType; this.substanceClass = response.substanceClass; this.status = response.status; @@ -507,11 +832,11 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy this.formSections = []; sectionNames.forEach(sectionName => { const formSection = new SubstanceFormSection(sectionName); - if (!this.definitionType || !(this.definitionType === 'ALTERNATIVE' && + /* if (!this.definitionType || !(this.definitionType === 'ALTERNATIVE' && (formSection.dynamicComponentName === 'substance-form-names' || formSection.dynamicComponentName === 'substance-form-codes-card'))) { - this.formSections.push(formSection); - } + } */ + this.formSections.push(formSection); }); } @@ -544,7 +869,7 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy this.substanceFormService.validateSubstance().pipe(take(1)).subscribe(results => { this.submissionMessage = null; this.validationMessages = results.validationMessages.filter( - message => message.messageType.toUpperCase() === 'ERROR' || message.messageType.toUpperCase() === 'WARNING'); + message => message.messageType.toUpperCase() === 'ERROR' || message.messageType.toUpperCase() === 'WARNING'|| message.messageType.toUpperCase() === 'NOTICE'); this.validationResult = results.valid; this.showSubmissionMessages = true; this.loadingService.setLoading(false); @@ -588,10 +913,27 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy ); } + submitStaging() { + this.substanceFormService.submitStaging(this.activatedRoute.snapshot.queryParams['stagingID']).subscribe(response => { + this.loadingService.setLoading(false); + this.isLoading = false; + this.validationMessages = null; + this.showSubmissionMessages = false; + this.submissionMessage = ''; + if (!this.id) { + this.id = response.uuid; + } + this.openSuccessDialog('staging'); + }) + } + submit(): void { this.isLoading = true; this.approving = false; this.loadingService.setLoading(true); + if (this.activatedRoute.snapshot.queryParams['stagingID']) { + this.submitStaging(); + } else { this.substanceFormService.saveSubstance().pipe(take(1)).subscribe(response => { this.loadingService.setLoading(false); this.isLoading = false; @@ -621,6 +963,8 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy }, 8000); } }); + + } } dismissValidationMessage(index: number) { @@ -730,9 +1074,16 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy } } defiant.json.search(old, '//*[uuid]'); - _.remove(old.codes, { - codeSystem: 'BDNUM' - }); + let remove = ['BDNUM']; + if (this.configService.configData && this.configService.configData.filteredDuplicationCodes) { + remove = this.configService.configData.filteredDuplicationCodes; + } + remove.forEach(code => { + _.remove(old.codes, { + codeSystem: code + }); + }) + const createHolders = defiant.json.search(old, '//*[created]'); for (let i = 0; i < createHolders.length; i++) { const rec = createHolders[i]; @@ -757,6 +1108,7 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy old.codes = []; old.notes = []; old.relationships = []; + old.tags = []; } delete old['createdBy']; delete old['created']; @@ -794,11 +1146,10 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy } - return old; } - openSuccessDialog({ type, fileUrl }: { type?: 'submit'|'approve', fileUrl?: string }): void { + openSuccessDialog({ type, fileUrl }: { type?: 'submit'|'approve'|'staging', fileUrl?: string }): void { const dialogRef = this.dialog.open(SubmitSuccessDialogComponent, { data: { type: type, @@ -806,15 +1157,18 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy }, disableClose: true }); + this.overlayContainer.style.zIndex = '1002'; - const dialogSubscription = dialogRef.afterClosed().pipe(take(1)).subscribe((response?: 'continue' | 'browse' | 'view') => { + const dialogSubscription = dialogRef.afterClosed().pipe(take(1)).subscribe((response?: 'continue' | 'browse' | 'view' | 'staging') => { this.substanceFormService.bypassUpdateCheck(); if (response === 'continue') { this.router.navigate(['/substances', this.id, 'edit']); } else if (response === 'browse') { this.router.navigate(['/browse-substance']); + } else if (response === 'staging') { + this.router.navigate(['/admin/staging-area']); } else if (response === 'view') { this.router.navigate(['/substances', this.id]); } else if (response === 'viewInPfda') { @@ -827,11 +1181,15 @@ export class SubstanceFormComponent implements OnInit, AfterViewInit, OnDestroy } this.showSubmissionMessages = true; this.validationResult = false; - setTimeout(() => { - this.showSubmissionMessages = false; - this.submissionMessage = ''; - this.router.navigate(['/substances', this.id, 'edit']); - }, 3000); + if (type && type == 'staging') { + this.submissionMessage = 'Edits to staged substance were saved successfully'; + } else { + setTimeout(() => { + this.showSubmissionMessages = false; + this.submissionMessage = ''; + this.router.navigate(['/substances', this.id, 'edit']); + }, 3000); + } } }); this.subscriptions.push(dialogSubscription); @@ -855,4 +1213,106 @@ mergeConcept() { this.overlayContainer.style.zIndex = '1000'; } + fixLink(link: string) { + return this.substanceService.oldLinkFix(link); + } + + + saveDraft(auto?: boolean) { + const json = this.substanceFormService.cleanSubstance(); + const time = new Date().getTime(); + + const uuid = json.uuid ? json.uuid : 'register'; + const type = json.substanceClass; + let primary = null; + json.names.forEach(name => { + if (name.displayName) { + primary = name.name; + } + }); + if (!primary && json.names.length > 0) { + primary = json.names[0].name; + } + if(!auto) { + const file = 'gsrs-draft-' + time; + + let draft = { + 'uuid': uuid, + 'date': time, + 'type': type, + 'name': primary, + 'substance': json, + 'auto': false, + 'file': file + } + + localStorage.setItem(file, JSON.stringify(draft)); + this.draftCount++; + + } else { + this.getDrafts(); + let autos = this.drafts.filter(opt => { + return opt.auto; + }); + let auto1 = null; + let auto2 = null; + let auto3 = null; + this.drafts.forEach(draft => { + if (draft.auto) { + if (draft.file === 'gsrs-draft-auto1') { + auto1 = draft; + } + if (draft.file === 'gsrs-draft-auto2') { + auto2 = draft; + } + if (draft.file === 'gsrs-draft-auto3') { + auto3 = draft; + } + } + }); + let file = 'gsrs-draft-auto'; + + if (!auto1) { + file = 'gsrs-draft-auto1'; + this.draftCount++; + + } else if (!auto2) { + file = 'gsrs-draft-auto2'; + this.draftCount++; + + } else if (!auto3) { + file = 'gsrs-draft-auto3'; + this.draftCount++; + + } else { + if(auto1.date < auto2.date && auto1.date < auto3.date) { + file = 'gsrs-draft-auto1'; + } + else if (auto2.date < auto1.date && auto2.date < auto3.date) { + file = 'gsrs-draft-auto2'; + } + else { + file = 'gsrs-draft-auto3'; + } + } + + let draft = { + 'uuid': uuid, + 'date': time, + 'type': type, + 'name': primary, + 'substance': json, + 'auto': true, + 'file': file + } + + localStorage.setItem(file, JSON.stringify(draft)); + + + } + + + + + } } diff --git a/src/app/core/substance-form/substance-form.model.ts b/src/app/core/substance-form/substance-form.model.ts index e2b2e984d..b4e42b844 100644 --- a/src/app/core/substance-form/substance-form.model.ts +++ b/src/app/core/substance-form/substance-form.model.ts @@ -16,6 +16,7 @@ export interface SubstanceFormDefinition { approvalID?: string; _name?: string; _name_html?: string; + _nameHTML?: string; relationships?: Array; tags?: Array; } diff --git a/src/app/core/substance-form/substance-form.module.ts b/src/app/core/substance-form/substance-form.module.ts index c8d4b0c98..8fa5a7b9e 100644 --- a/src/app/core/substance-form/substance-form.module.ts +++ b/src/app/core/substance-form/substance-form.module.ts @@ -80,8 +80,12 @@ import { SubstanceFormRelationshipsService } from './relationships/substance-for import { SubstanceFormStructuralModificationsService } from './structural-modifications/substance-form-structural-modifications.service'; import { PreviousReferencesComponent } from '@gsrs-core/substance-form/references/previous-references/previous-references.component'; import { PreviousReferencesDialogComponent } from '@gsrs-core/substance-form/references/previous-references/previous-references-dialog/previous-references-dialog.component'; -import { MatProgressSpinnerModule } from '@angular/material'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { CopyDisulfideDialogComponent } from '@gsrs-core/substance-form/copy-disulfide-dialog/copy-disulfide-dialog.component'; +import { FragmentWizardComponent } from '@gsrs-core/admin/fragment-wizard/fragment-wizard.component'; +import { SubstanceDraftsComponent } from '@gsrs-core/substance-form/substance-drafts/substance-drafts.component'; +import { MatSortModule } from '@angular/material/sort'; +import { ElementLabelDisplayModule } from '@gsrs-core/utils/element-label-display.module'; @NgModule({ imports: [ @@ -115,7 +119,9 @@ import { CopyDisulfideDialogComponent } from '@gsrs-core/substance-form/copy-dis RouterModule, SubstanceImageModule, MatProgressBarModule, - MatProgressSpinnerModule + MatProgressSpinnerModule, + MatSortModule, + ElementLabelDisplayModule ], declarations: [ SubstanceFormComponent, @@ -147,7 +153,8 @@ import { CopyDisulfideDialogComponent } from '@gsrs-core/substance-form/copy-dis MergeConceptDialogComponent, DefinitionSwitchDialogComponent, PreviousReferencesDialogComponent, - CopyDisulfideDialogComponent + CopyDisulfideDialogComponent, + SubstanceDraftsComponent ], exports: [ SubstanceFormComponent, @@ -177,7 +184,8 @@ import { CopyDisulfideDialogComponent } from '@gsrs-core/substance-form/copy-dis MergeConceptDialogComponent, DefinitionSwitchDialogComponent, PreviousReferencesDialogComponent, - CopyDisulfideDialogComponent + CopyDisulfideDialogComponent, + SubstanceDraftsComponent ], entryComponents: [ RefernceFormDialogComponent, @@ -196,11 +204,12 @@ import { CopyDisulfideDialogComponent } from '@gsrs-core/substance-form/copy-dis MergeConceptDialogComponent, DefinitionSwitchDialogComponent, PreviousReferencesDialogComponent, - CopyDisulfideDialogComponent + CopyDisulfideDialogComponent, + SubstanceDraftsComponent ] }) export class SubstanceFormModule { - static forRoot(): ModuleWithProviders { + static forRoot(): ModuleWithProviders { return { ngModule: SubstanceFormModule, providers: [ diff --git a/src/app/core/substance-form/substance-form.service.ts b/src/app/core/substance-form/substance-form.service.ts index 82a3ff0a5..ee1037077 100644 --- a/src/app/core/substance-form/substance-form.service.ts +++ b/src/app/core/substance-form/substance-form.service.ts @@ -13,7 +13,7 @@ import { Sugar, Linkage, NucleicAcid, - StructurallyDiverse, DisplayStructure, Monomer, PolymerClassification + StructurallyDiverse, DisplayStructure, Monomer, PolymerClassification, SubstanceSummary } from '../substance/substance.model'; import { SequenceUnit, @@ -26,6 +26,7 @@ import { UtilsService } from '../utils/utils.service'; import { StructureService } from '@gsrs-core/structure'; import * as _ from 'lodash'; import { take } from 'rxjs/operators'; +import { AdminService } from '@gsrs-core/admin/admin.service'; @Injectable() export class SubstanceFormService implements OnDestroy { @@ -55,11 +56,14 @@ export class SubstanceFormService implements OnDestroy { resolvedMol = this.nameResolver.asObservable(); private _bypassUpdateCheck = false; private method?: string; + private previousHash?: number; + storedRelated = {}; constructor( private substanceService: SubstanceService, public utilsService: UtilsService, - private structureService: StructureService + private structureService: StructureService, + private adminService: AdminService ) { this.substanceEmitter = new ReplaySubject(); } @@ -68,19 +72,36 @@ export class SubstanceFormService implements OnDestroy { this.unloadSubstance(); } - loadSubstance(substanceClass: string = 'chemical', substance?: SubstanceDetail, method?: string): Observable { + getStoredRelated(header: string) { + return (this.storedRelated && this.storedRelated[header]) ? this.storedRelated[header] : null; + } + + setStoredRelated(substance: SubstanceSummary, header: string) { + this.storedRelated[header] = substance; + + } + + loadSubstance(substanceClass: string = 'chemical', substance?: SubstanceDetail, method?: string, mergeConcept?: boolean): Observable { if (method) { this.method = method; } else { this.method = null; } + if (mergeConcept) { + this.privateSubstance = substance; + this.substanceEmitter.next(substance); + this.namesUpdated(); + } + + this.substanceEmitter.subscribe(val => { + }); return new Observable(observer => { if (substance != null) { this.privateSubstance = substance; substanceClass = this.privateSubstance.substanceClass; } else { - //the second case happens in the forms sometimes but really shouldn't - if (substanceClass === 'chemical' || substanceClass === 'structure') { + // the second case happens in the forms sometimes but really shouldn't + if (substanceClass === 'chemical' || substanceClass === 'structure') { this.privateSubstance = { substanceClass: 'chemical', references: [], @@ -148,6 +169,17 @@ export class SubstanceFormService implements OnDestroy { relationships: [], properties: [] }; + } else if (substanceClass === 'specifiedSubstanceG2') { + this.privateSubstance = { + substanceClass: 'specifiedSubstanceG2', + references: [], + names: [], + specifiedSubstanceG2: { + constituents: [], + manufacturing: [], + references: [] + } + }; } else if (substanceClass === 'specifiedSubstanceG3') { this.privateSubstance = { substanceClass: substanceClass, @@ -155,12 +187,33 @@ export class SubstanceFormService implements OnDestroy { names: [], specifiedSubstanceG3: { parentSubstance: {}, - definition: {references: []}, - grade: {references: []} + definition: { references: [] }, + grade: { references: [] } }, codes: [], properties: [] }; + } else if (substanceClass === 'specifiedSubstanceG4m') { + this.privateSubstance = { + substanceClass: substanceClass, + // references: [], + specifiedSubstanceG4m: { + parentSubstance: {}, + process: [{"processName": "Process 1", + sites: [{ + stages: [{ + "stageNumber": "1", + startingMaterials: [], + processingMaterials: [], + resultingMaterials: [], + criticalParameters: [] + }] + }] + }] + } + // codes: [], + // properties: [] + }; } else if (substanceClass === 'polymer') { this.privateSubstance = { substanceClass: substanceClass, @@ -183,13 +236,18 @@ export class SubstanceFormService implements OnDestroy { codes: [] }; } - //default values - - //TP: default to protected for root level record. - this.privateSubstance.access=["protected"]; - this.privateSubstance.definitionLevel = "COMPLETE"; - this.privateSubstance.definitionType = "PRIMARY"; - + // default values + + // TP: default to protected for root level record. + // ***** AN: Adding this right now for SSG4m and G2 ****** + if (substanceClass !== 'specifiedSubstanceG4m') { + this.privateSubstance.access = ["protected"]; + + if (substanceClass !== 'specifiedSubstanceG2') { + this.privateSubstance.definitionLevel = "COMPLETE"; + this.privateSubstance.definitionType = "PRIMARY"; + } + } } this.subClass = this.privateSubstance.substanceClass; @@ -197,11 +255,11 @@ export class SubstanceFormService implements OnDestroy { // Only these two substance classes differ from // the name of their JSON defintional element // That's why they are used as exceptions - + if (this.subClass === 'chemical') { - this.subClass = 'structure'; //? - } else if (this.subClass === 'specifiedSubstanceG1') { - this.subClass = 'specifiedSubstance'; //? + this.subClass = 'structure'; + } else if (this.subClass === 'specifiedSubstanceG1') { + this.subClass = 'specifiedSubstance'; } if (this.privateSubstance[this.subClass] == null) { @@ -263,6 +321,59 @@ export class SubstanceFormService implements OnDestroy { }); } + setDefinitionFromDefRef(access: any) { + + if (this.privateSubstance.structurallyDiverse) { + this.privateSubstance.structurallyDiverse.access = access; + } else if (this.privateSubstance.protein) { + this.privateSubstance.protein.access = access; + } else if (this.privateSubstance.structure) { + this.privateSubstance.structure.access = access; + } else if (this.privateSubstance.mixture) { + this.privateSubstance.mixture.access = access; + } else if (this.privateSubstance.polymer) { + this.privateSubstance.polymer.access = access; + } else if (this.privateSubstance.nucleicAcid) { + this.privateSubstance.nucleicAcid.access = access; + } else if (this.privateSubstance.specifiedSubstance) { + this.privateSubstance.specifiedSubstance.access = access; + } else { + } + this.substanceEmitter.next(this.privateSubstance); + } + + getDefinitionForDefRef() { + + if (this.privateSubstance.structurallyDiverse) { + return this.privateSubstance.structurallyDiverse.access; + } else if (this.privateSubstance.protein) { + return this.privateSubstance.protein.access; + } else if (this.privateSubstance.structure) { + return this.privateSubstance.structure.access; + } else if (this.privateSubstance.mixture) { + return this.privateSubstance.mixture.access; + } else if (this.privateSubstance.polymer) { + return this.privateSubstance.polymer.access; + } else if (this.privateSubstance.nucleicAcid) { + return this.privateSubstance.nucleicAcid.access; + } else if (this.privateSubstance.specifiedSubstance) { + return this.privateSubstance.specifiedSubstance.access; + } else { + } + this.definitionEmitter.next(this.getDefinition()); + } + + changeApproval() { + const apid = prompt('Enter new ApprovalID:'); + + if (apid) { + const old = this.privateSubstance.approvalID; + this.privateSubstance.approvalID = apid; + alert('Approval ID changed from"' + old + '" to "' + apid + '". Submit changes to save'); + this.definitionEmitter.next(this.getDefinition()); + } + } + switchType(substance: SubstanceDetail, newClass: string) { const fieldGetter = { 'protein': ['protein', 'modifications', 'properties'], @@ -385,6 +496,22 @@ export class SubstanceFormService implements OnDestroy { } } + autoSave(): boolean { + const substanceString = JSON.stringify(this.privateSubstance); + if (!this.previousHash) { + this.previousHash = this.utilsService.hashCode(substanceString); + return false; + } else { + const match = this.previousHash !== this.utilsService.hashCode(substanceString); + if (match) { + this.previousHash = this.utilsService.hashCode(substanceString); + return true; + } else { + return false; + } + } + } + bypassUpdateCheck(): void { this._bypassUpdateCheck = true; } @@ -396,7 +523,7 @@ export class SubstanceFormService implements OnDestroy { this.ready().subscribe(() => { const definition = this.getDefinition(); observer.next(definition); - // tslint:disable-next-line:no-shadowed-variable + // eslint-disable-next-line @typescript-eslint/no-shadow this.definitionEmitter.subscribe(definition => { observer.next(definition); }); @@ -614,7 +741,7 @@ export class SubstanceFormService implements OnDestroy { if (this.privateSubstance.properties) { this.privateSubstance.properties.forEach(prop => { if (prop.propertyType === 'PROTEIN FEATURE' || prop.propertyType === 'NUCLEIC ACID FEATURE') { - const featArr = prop.value.nonNumericValue.split(';'); + const featArr = prop.value.nonNumericValue ? prop.value.nonNumericValue.split(';') : []; featArr.forEach(f => { const sites = f.split('-'); const subunitIndex = Number(sites[0].split('_')[0]); @@ -932,27 +1059,27 @@ export class SubstanceFormService implements OnDestroy { // disulfide links start copyDisulfideLinks(to: number, from: number): any { - const test= JSON.parse(JSON.stringify(this.privateSubstance.protein.disulfideLinks)); -const push = []; -const test3 = []; -for (let i = 0; i < test.length; i++) { - const link = JSON.parse(JSON.stringify(test[i])); - if (link['sites'][0].subunitIndex === to || (link['sites'][1].subunitIndex === to)) { - } else if (link['sites'][0].subunitIndex === from && link['sites'][1].subunitIndex === from) { - const copy = JSON.parse(JSON.stringify(test[i])); - copy.sites[0].subunitIndex = to; - copy.sites[1].subunitIndex = to; - copy.sitesShorthand = copy.sites[0].subunitIndex + '_' + copy.sites[0].residueIndex + - ';' + copy.sites[1].subunitIndex + '_' + copy.sites[1].residueIndex; - test3.push(link); - test3.push(copy); - } else { - test3.push(link); - } -} + const test = JSON.parse(JSON.stringify(this.privateSubstance.protein.disulfideLinks)); + const push = []; + const test3 = []; + for (let i = 0; i < test.length; i++) { + const link = JSON.parse(JSON.stringify(test[i])); + if (link['sites'][0].subunitIndex === to || (link['sites'][1].subunitIndex === to)) { + } else if (link['sites'][0].subunitIndex === from && link['sites'][1].subunitIndex === from) { + const copy = JSON.parse(JSON.stringify(test[i])); + copy.sites[0].subunitIndex = to; + copy.sites[1].subunitIndex = to; + copy.sitesShorthand = copy.sites[0].subunitIndex + '_' + copy.sites[0].residueIndex + + ';' + copy.sites[1].subunitIndex + '_' + copy.sites[1].residueIndex; + test3.push(link); + test3.push(copy); + } else { + test3.push(link); + } + } -this.privateSubstance.protein.disulfideLinks = test3; -this.emitDisulfideLinkUpdate(); + this.privateSubstance.protein.disulfideLinks = test3; + this.emitDisulfideLinkUpdate(); } disulfideLinksUpdated(): Observable> { @@ -1098,6 +1225,8 @@ this.emitDisulfideLinkUpdate(); this.substanceChangeReasonEmitter.next(this.privateSubstance.changeReason); } + + // end change reason validateSubstance(): Observable { @@ -1109,24 +1238,24 @@ this.emitDisulfideLinkUpdate(); for (let i = 0; i < substanceCopy.references.length; i++) { const ref = substanceCopy.references[i]; if (ref.docType !== 'SYSTEM') { - if ((!ref.citation || ref.citation === '') || (!ref.docType || ref.docType === '')) { - const invalidReferenceMessage: ValidationMessage = { - actionType: 'frontEnd', - appliedChange: false, - links: [], - message: 'All references require a non-empty source type and text/citation value', - messageType: 'WARNING', - suggestedChange: true - }; - results.validationMessages.push(invalidReferenceMessage); - break; + if ((!ref.citation || ref.citation === '') || (!ref.docType || ref.docType === '')) { + const invalidReferenceMessage: ValidationMessage = { + actionType: 'frontEnd', + appliedChange: false, + links: [], + message: 'All references require a non-empty source type and text/citation value', + messageType: 'WARNING', + suggestedChange: true + }; + results.validationMessages.push(invalidReferenceMessage); + break; + } } } - } if (substanceCopy.properties) { for (let i = 0; i < substanceCopy.properties.length; i++) { const prop = substanceCopy.properties[i]; - if (!prop.propertyType || ! prop.name) { + if (!prop.propertyType || !prop.name) { const invalidPropertyMessage: ValidationMessage = { actionType: 'frontEnd', appliedChange: false, @@ -1157,11 +1286,54 @@ this.emitDisulfideLinkUpdate(); } } } + if (substanceCopy.polymer && substanceCopy.polymer.monomers) { + for (let i = 0; i < substanceCopy.polymer.monomers.length; i++) { + const prop = substanceCopy.polymer.monomers[i]; + if (!prop.monomerSubstance || prop.monomerSubstance === {}) { + const invalidPropertyMessage: ValidationMessage = { + actionType: 'frontEnd', + appliedChange: false, + links: [], + message: 'Monomer #' + (i + 1) + ' requires a selected substance', + messageType: 'ERROR', + suggestedChange: true + }; + results.validationMessages.push(invalidPropertyMessage); + results.valid = false; + } + } + } + if (substanceCopy.modifications && substanceCopy.modifications.physicalModifications) { + for (let i = 0; i < substanceCopy.modifications.physicalModifications.length; i++) { + const prop = substanceCopy.modifications.physicalModifications[i]; + let present = false; + if (prop && prop.parameters){ + prop.parameters.forEach(param => { + if (param.parameterName) { + present = true; + } + }); + } + + if (!prop.physicalModificationRole && !present) { + const invalidPropertyMessage: ValidationMessage = { + actionType: 'frontEnd', + appliedChange: false, + links: [], + message: 'Physical Modification #' + (i + 1) + ' requires a modification role or valid parameter', + messageType: 'ERROR', + suggestedChange: true + }; + results.validationMessages.push(invalidPropertyMessage); + results.valid = false; + } + } + } } observer.next(results); observer.complete(); }, error => { - observer.error(); + observer.error(error); observer.complete(); }); }); @@ -1175,6 +1347,14 @@ this.emitDisulfideLinkUpdate(); if (this.privateSubstance.structurallyDiverse.$$storedPart) { delete this.privateSubstance.structurallyDiverse.$$storedPart; } + + const toclean = ['organismFamily', 'organismGenus', 'organismSpecies', 'organismAuthor', 'infraSpecificName', 'infraSpecificType', 'fractionMaterialType', 'fractionName', 'developmentalStage']; + toclean.forEach(field => { + if (this.privateSubstance.structurallyDiverse[field] && this.privateSubstance.structurallyDiverse[field] !== null && + this.privateSubstance.structurallyDiverse[field] !== '') { + this.privateSubstance.structurallyDiverse[field] = this.privateSubstance.structurallyDiverse[field].trim(); + } + }); } /* if (this.privateSubstance.nucleicAcid) { @@ -1199,54 +1379,54 @@ this.emitDisulfideLinkUpdate(); and can cause errors upon submission. the view change was to allow the stdName property to be visible to the forms*/ if (this.privateSubstance.structure) { - if ( this.privateSubstance.structure.properties) { + if (this.privateSubstance.structure.properties) { delete this.privateSubstance.structure.properties; } - if ( this.privateSubstance.structure.links) { + if (this.privateSubstance.structure.links) { delete this.privateSubstance.structure.links; } } if (this.privateSubstance.polymer && this.privateSubstance.polymer.displayStructure) { - if ( this.privateSubstance.polymer.displayStructure.properties) { + if (this.privateSubstance.polymer.displayStructure.properties) { delete this.privateSubstance.polymer.displayStructure.properties; } - if ( this.privateSubstance.polymer.displayStructure.links) { + if (this.privateSubstance.polymer.displayStructure.links) { delete this.privateSubstance.polymer.displayStructure.links; } } if (this.privateSubstance.polymer && this.privateSubstance.polymer.idealizedStructure) { - if ( this.privateSubstance.polymer.idealizedStructure.properties) { + if (this.privateSubstance.polymer.idealizedStructure.properties) { delete this.privateSubstance.polymer.idealizedStructure.properties; } - if ( this.privateSubstance.polymer.idealizedStructure.links) { + if (this.privateSubstance.polymer.idealizedStructure.links) { delete this.privateSubstance.polymer.idealizedStructure.links; } } if (this.privateSubstance.moieties) { this.privateSubstance.moieties.forEach(moiety => { - if (moiety.properties) { - delete moiety.properties; - } - if (moiety.links) { - delete moiety.links; - } + if (moiety.properties) { + delete moiety.properties; + } + if (moiety.links) { + delete moiety.links; + } }); } if (this.privateSubstance.protein && this.privateSubstance.protein.disulfideLinks - && this.privateSubstance.protein.disulfideLinks.length > 0) { - for ( let i = this.privateSubstance.protein.disulfideLinks.length; i >= 0; i--) { - if (this.privateSubstance.protein.disulfideLinks[i] && this.privateSubstance.protein.disulfideLinks[i].sites && - this.privateSubstance.protein.disulfideLinks[i].sites[0] && this.privateSubstance.protein.disulfideLinks[i].sites[1] && - Object.keys(this.privateSubstance.protein.disulfideLinks[i].sites[0]).length === 0 && - Object.keys(this.privateSubstance.protein.disulfideLinks[i].sites[1]).length === 0 ) { - this.privateSubstance.protein.disulfideLinks.splice(i, 1); - } + && this.privateSubstance.protein.disulfideLinks.length > 0) { + for (let i = this.privateSubstance.protein.disulfideLinks.length; i >= 0; i--) { + if (this.privateSubstance.protein.disulfideLinks[i] && this.privateSubstance.protein.disulfideLinks[i].sites && + this.privateSubstance.protein.disulfideLinks[i].sites[0] && this.privateSubstance.protein.disulfideLinks[i].sites[1] && + Object.keys(this.privateSubstance.protein.disulfideLinks[i].sites[0]).length === 0 && + Object.keys(this.privateSubstance.protein.disulfideLinks[i].sites[1]).length === 0) { + this.privateSubstance.protein.disulfideLinks.splice(i, 1); } - } + } + } // end view=internal changes let substanceString = JSON.stringify(this.privateSubstance); @@ -1261,7 +1441,7 @@ this.emitDisulfideLinkUpdate(); deletedUuids.forEach(uuid => { substanceString = substanceString.replace(new RegExp(`"${uuid}"`, 'g'), ''); }); - substanceString = substanceString.replace(/,,/g, ','); + substanceString = substanceString.replace(/,[,]+/g, ','); substanceString = substanceString.replace(/\[,/g, '['); substanceString = substanceString.replace(/,\]/g, ']'); substanceCopy = JSON.parse(substanceString); @@ -1319,6 +1499,32 @@ this.emitDisulfideLinkUpdate(); }); } + getUNII() { + return this.privateSubstance._approvalIDDisplay; + } + + importStructure(structure, type) { + // import a structure from mol or smiles + if(this.privateSubstance) { + if (type === 'molfile') { + this.privateSubstance.structure.molfile = structure; + this.substanceEmitter.next(this.privateSubstance); + } else { + this.privateSubstance.structure.smiles = structure; + + this.structureService.interpretStructure(structure).pipe(take(1)).subscribe(response => { + if (response && response.structure && response.structure.molfile) { + this.privateSubstance.structure.molfile = response.structure.molfile; + this.substanceEmitter.next(this.privateSubstance); + } + }); + } + } else { + // service substance loaded improperly + } + + } + approveSubstance(): Observable { return new Observable(observer => { const results: SubstanceFormResults = { @@ -1354,6 +1560,34 @@ this.emitDisulfideLinkUpdate(); }); } + submitStaging(id: any): Observable { + if (this.privateSubstance.structure != null && !this.privateSubstance.structure.uuid) { + this.privateSubstance.structure.id = this.utilsService.newUUID(); + this.privateSubstance.structure.uuid = this.privateSubstance.structure.id; + } + if (this.privateSubstance.moieties != null && this.privateSubstance.moieties.length) { + this.privateSubstance.moieties.forEach(moiety => { + if (!moiety.uuid) { + moiety.id = this.utilsService.newUUID(); + moiety.uuid = moiety.id; + } + }); + } + const substanceCopy = this.cleanSubstance(); + return new Observable(observer => { + + this.adminService.updateStagingArea(id, substanceCopy).subscribe(response => { + console.log(response); + observer.next(response); + observer.complete(); + }, error => { + console.log(error); + observer.error(error); + observer.complete(); + }); + }); + } + saveSubstance(): Observable { return new Observable(observer => { const results: SubstanceFormResults = { @@ -1435,18 +1669,18 @@ this.emitDisulfideLinkUpdate(); } else { this.method = null; } - this.definitionEmitter.next(this.getDefinition()); - if (this.privateSubstance.substanceClass === 'protein') { - this.substanceSubunitsEmitter.next(this.privateSubstance.protein.subunits); - } else if (this.privateSubstance.substanceClass === 'nucleicAcid') { - this.substanceSugarsEmitter.next(this.privateSubstance.nucleicAcid.sugars); - this.substanceSubunitsEmitter.next(this.privateSubstance.nucleicAcid.subunits); - } else if (this.privateSubstance.substanceClass === 'mixture') { - this.substanceSubunitsEmitter.next(this.privateSubstance.mixture.components); - } - this.substanceChangeReasonEmitter.next(this.privateSubstance.changeReason); - this.resetState(); - this.substanceEmitter.next(this.privateSubstance); + this.definitionEmitter.next(this.getDefinition()); + if (this.privateSubstance.substanceClass === 'protein') { + this.substanceSubunitsEmitter.next(this.privateSubstance.protein.subunits); + } else if (this.privateSubstance.substanceClass === 'nucleicAcid') { + this.substanceSugarsEmitter.next(this.privateSubstance.nucleicAcid.sugars); + this.substanceSubunitsEmitter.next(this.privateSubstance.nucleicAcid.subunits); + } else if (this.privateSubstance.substanceClass === 'mixture') { + this.substanceSubunitsEmitter.next(this.privateSubstance.mixture.components); + } + this.substanceChangeReasonEmitter.next(this.privateSubstance.changeReason); + this.resetState(); + this.substanceEmitter.next(this.privateSubstance); } stringToSites(slist: string): Array { @@ -1456,7 +1690,7 @@ this.emitDisulfideLinkUpdate(); } const toks = slist.split(';'); const sites = []; - // tslint:disable-next-line:forin + // eslint-disable-next-line guard-for-in for (const i in toks) { const l = toks[i]; if (l === '') { diff --git a/src/app/core/substance-form/subunit-form/subunit-form.component.html b/src/app/core/substance-form/subunit-form/subunit-form.component.html index c0a368f0b..dd3941ca2 100644 --- a/src/app/core/substance-form/subunit-form/subunit-form.component.html +++ b/src/app/core/substance-form/subunit-form/subunit-form.component.html @@ -28,7 +28,7 @@
    - +
    diff --git a/src/app/core/substance-form/subunit-form/subunit-form.component.scss b/src/app/core/substance-form/subunit-form/subunit-form.component.scss index bd02e510c..b3fdb0d9f 100644 --- a/src/app/core/substance-form/subunit-form/subunit-form.component.scss +++ b/src/app/core/substance-form/subunit-form/subunit-form.component.scss @@ -8,6 +8,10 @@ } } +.sequence-textarea { + font-size: 14px; + letter-spacing: 2px; +} .show { display:block; @@ -77,7 +81,7 @@ padding-bottom: 20px; &:not(:last-child) { - border-bottom: solid 1px rgba(0, 0, 0, 0.12); + border-bottom: solid 1px var(--box-shadow-color-3); } } @@ -104,12 +108,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .form-row { @@ -143,8 +147,8 @@ } .glycosylation{ - color:white; - background-color:darkgreen; + color:var(--regular-white-color); + background-color:var(--regular-darkgreen-color); } .disulfide{ @@ -153,8 +157,8 @@ } .other{ - color:white; - background-color:darkslateblue; + color:var(--regular-white-color); + background-color:var(--regular-darkslateblue-color); } .glycosylation{ @@ -179,24 +183,19 @@ } .feature{ - border-top:1px solid magenta; - border-bottom: 1px solid magenta; + border-top:1px solid var(--regular-magenta-color); + border-bottom: 1px solid var(--regular-magenta-color); } .modification{ - color:white; - background-color:DARKOLIVEGREEN; + color:var(--regular-white-color); + background-color:var(--regular-darkolivegreen-color); } .selectedSite{ display:block; text-align: center; } -/* -.chosen { - color:black !important; - background-color:turquoise !important; -}*/ .last-section { flex-basis: 0!important; @@ -209,7 +208,7 @@ } .generated { - background-color: rgba(0, 0, 0, 0.15) + background-color: var(--box-shadow-color-2); } .subunit-container { @@ -217,7 +216,7 @@ } .button-link { - color: #4793d1; + color: var(--primary-color); text-decoration: none; vertical-align: middle; font-weight: 500; @@ -232,5 +231,5 @@ } .error { - color: red; + color: var(--regular-red-color); } diff --git a/src/app/core/substance-form/subunit-form/subunit-form.component.ts b/src/app/core/substance-form/subunit-form/subunit-form.component.ts index aad7309e5..acaacaf4b 100644 --- a/src/app/core/substance-form/subunit-form/subunit-form.component.ts +++ b/src/app/core/substance-form/subunit-form/subunit-form.component.ts @@ -17,7 +17,7 @@ import { ScrollToService } from '@gsrs-core/scroll-to/scroll-to.service'; import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; import * as deepEqual from 'deep-equal'; import { SubstanceFormLinksService } from '../links/substance-form-links.service'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; import { OverlayContainer } from '@angular/cdk/overlay'; import { CopyDisulfideDialogComponent } from '@gsrs-core/substance-form/copy-disulfide-dialog/copy-disulfide-dialog.component'; import { Router } from '@angular/router'; @@ -150,7 +150,7 @@ export class SubunitFormComponent implements OnInit, OnDestroy, OnChanges, After this.cvService.getDomainVocabulary('AMINO_ACID_RESIDUE').subscribe(response => { this.vocabulary = response['AMINO_ACID_RESIDUE'].dictionary; this.vocabulary.X = nonStandard; - // tslint:disable-next-line:forin + // eslint-disable-next-line guard-for-in for (const key in this.vocabulary) { this.validArray.push(this.vocabulary[key].value); } @@ -160,7 +160,7 @@ export class SubunitFormComponent implements OnInit, OnDestroy, OnChanges, After this.cvService.getDomainVocabulary('NUCLEIC_ACID_BASE').subscribe(response => { this.vocabulary = response['NUCLEIC_ACID_BASE'].dictionary; this.vocabulary.X = nonStandard; - // tslint:disable-next-line:forin + // eslint-disable-next-line guard-for-in for (const key in this.vocabulary) { this.validArray.push(this.vocabulary[key].value); } @@ -194,7 +194,7 @@ export class SubunitFormComponent implements OnInit, OnDestroy, OnChanges, After }*/ getTooltipMessage(subunitIndex: number, unitIndex: number, unitValue: string, type: string): any { - const vocab = (this.vocabulary[unitValue] === undefined ? 'UNDEFINED' : this.vocabulary[unitValue].display); + const vocab = (this.vocabulary[unitValue.toUpperCase()] === undefined ? 'UNDEFINED' : this.vocabulary[unitValue.toUpperCase()].display); const arr = []; const formatted = { 'modification': 'Structural Modification', @@ -276,7 +276,7 @@ window.open( url, '_blank'); let ret = ''; if (seq) { for (let i = 0; i < seq.length; i += 10) { - if (i % 60 === 0) { + if (i % 50 === 0) { ret += '\n'; } ret += seq.substr(i, 10) + ' '; @@ -289,9 +289,13 @@ window.open( url, '_blank'); if (!this.toggle[this.subunit.subunitIndex]) { const toArray = this.subunit.sequence.split(''); - const cleanedSequence = toArray.filter(char => this.validArray.indexOf(char.toUpperCase()) >= 0).toString().replace(/,/g, '').trim(); + let cleanedSequence = toArray.filter(char => this.validArray.indexOf(char.toUpperCase()) >= 0).toString().replace(/,/g, '').trim(); if (this.toggle[this.subunit.subunitIndex] === false) { + } + if (this.substanceType !== 'protein') { + //non-proteins should have sequences be upper-case always + cleanedSequence = cleanedSequence.toUpperCase(); } if (cleanedSequence !== this.subunit.sequence) { this.subunit.sequence = cleanedSequence; @@ -300,7 +304,11 @@ window.open( url, '_blank'); } } else { const toArray = this.editSequence.replace(/\s/g, '').split(''); - const cleanedSequence = toArray.filter(char => this.validArray.indexOf(char.toUpperCase()) >= 0).toString().replace(/,/g, '').trim(); + let cleanedSequence = toArray.filter(char => this.validArray.indexOf(char.toUpperCase()) >= 0).toString().replace(/,/g, '').trim(); + if (this.substanceType !== 'protein') { + //non-proteins should have sequences be upper-case always + cleanedSequence = cleanedSequence.toUpperCase(); + } this.editSequence = this.preformatSeq(cleanedSequence); } } diff --git a/src/app/core/substance-form/subunit-selector/subunit-selector.component.scss b/src/app/core/substance-form/subunit-selector/subunit-selector.component.scss index a13b6d249..c4e518c5c 100644 --- a/src/app/core/substance-form/subunit-selector/subunit-selector.component.scss +++ b/src/app/core/substance-form/subunit-selector/subunit-selector.component.scss @@ -89,7 +89,7 @@ padding-bottom: 20px; &:not(:last-child) { - border-bottom: solid 1px rgba(0, 0, 0, 0.12); + border-bottom: solid 1px var(--box-shadow-color-3); } } @@ -116,12 +116,12 @@ left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .form-row { @@ -168,7 +168,6 @@ width:100%; } - .unit-tooltip { white-space: pre-line !important; } @@ -184,54 +183,53 @@ width:100%; } .disulfide{ - color:white; - background-color: #cca300; + color:var(--regular-white-color); + background-color: var(--disulfide-color); } .other{ - color:white; - background-color:darkslateblue; + color:var(--regular-white-color); + background-color:var(--regular-darkslateblue-color); } .glycosylation{ - color:white; - background-color:#608000; + color: var(--regular-white-color); + background-color:var(--glycosylation-color); } .N-Glycosylation{ - color:white; - background-color:#0066cc; + color:var(--regular-white-color); + background-color:var(--n-glycosylation-color); } .C-Glycosylation{ - color:white; - background-color:GRAY; + color:var(--regular-white-color); + background-color:var(--regular-grey-color); } .O-Glycosylation{ - color:white; - //background-color:darkgoldenrod; - background-color:#ff6666; + color:var(--regular-white-color); + background-color:var(--o-glycosylation-color); } .feature{ - border-top:1px solid magenta; - border-bottom: 1px solid magenta; + border-top:1px solid var(--regular-magenta-color); + border-bottom: 1px solid var(--regular-magenta-color); } .modification{ - color:white; - background-color:darkgreen; + color:var(--regular-white-color); + background-color:var(--regular-darkgreen-color); } .chosen { - color:black !important; - background-color:turquoise !important; + color:var(--regular-black-color) !important; + background-color:var(--regular-turquoise-color) !important; } .new-disulfide { - color:black !important; - background-color:lightskyblue !important; + color:var(--regular-black-color) !important; + background-color:var(--regular-lightskyblue-color) !important; } .selectedSite{ @@ -264,7 +262,7 @@ width:100%; font-size: 11px; padding-bottom: 3.5px; line-height: 11px; - color: rgba(0, 0, 0, 0.54); + color: var(--dark-label-color); font-weight: 400; font-family: Roboto, "Helvetica Neue", sans-serif; width: 55px; @@ -286,7 +284,7 @@ width:100%; .featlink { padding-right:7px; - color: #4793d1; + color: var(--primary-color); } .featlink a:hover { font-weight:bold; @@ -329,7 +327,6 @@ width:100%; } } - .clear-button { width:40px; } @@ -343,16 +340,14 @@ width:100%; } .cys { - color: black; + color: var(--regular-black-color); font-weight: 500; } .unavailable { - color:lightgrey!important; + color:var(--regular-lightgray-color) !important; } - - .invalid_blink { animation: blinker .5s linear 1; } @@ -362,27 +357,22 @@ width:100%; } } - .invalid { - border: solid red; - color: red; + border: solid var(--regular-red-color); + color: var(--regular-red-color); } .valid { - color:black; + color:var(--regular-black-color); } .invalid-cysteine { - color:red; + color:var(--regular-red-color); } .last-section { flex-basis: 0!important; } -.last-units { - -} - .note { padding-top:10px; padding-left:15px; diff --git a/src/app/core/substance-form/sugar-form/sugar-form.component.html b/src/app/core/substance-form/sugar-form/sugar-form.component.html index 3caeab6bc..5d03d7d2b 100644 --- a/src/app/core/substance-form/sugar-form/sugar-form.component.html +++ b/src/app/core/substance-form/sugar-form/sugar-form.component.html @@ -11,8 +11,11 @@
    +
    + +
    - +
    + + + + + +
    +
    + + +
    + + + +
    +
    + + + Search Type + Substructure + Similarity + Exact + Flex + + +
    +
    + +
    +
    + +
    +
    +
    +
    Similarity cutoff (tanimoto)
    + +
    {{similarityCutoff}}
    +
    +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    + + + + +
    +

    + Get Structure From Name +

    + +
    + +
    +
    + + + + + +
    + +
    + + + + + + + + +
    +
    +
    +
    + + {{substance.structure.stereochemistry}} + +
    +
    +
    +
    + + {{substance.substanceClass}} + +
    +
    +
    +
    + + + +
    +
    + +
    +
    +
    + similarity: {{substance._matchContext.similarity.toFixed(3)}} +
    +
    +   +
    +
    + + +
    + + +
    + +
    +
    +
    + + + +
    +
    +
    + + +
    + + +
    + +
    + + + + + Sort By + + + {{option.display}} + + + + + + +
    +
    + No results found for '{{searchValue}}' +
    + +
    + + + + + + + +
    +
    +
    +
    + + {{substance.structure.stereochemistry}} + +
    +
    +
    +
    + + {{substance.substanceClass}} + +
    +
    +
    +
    + + + +
    +
    + +
    +
    +
    + + +
    + +
    + +
    +
    +
    + +
    +
    +
    \ No newline at end of file diff --git a/src/app/core/substance-selector/advanced-selector-dialog/advanced-selector-dialog.component.scss b/src/app/core/substance-selector/advanced-selector-dialog/advanced-selector-dialog.component.scss new file mode 100644 index 000000000..1ba9b009e --- /dev/null +++ b/src/app/core/substance-selector/advanced-selector-dialog/advanced-selector-dialog.component.scss @@ -0,0 +1,694 @@ +.form-row2 { + display:flex; + flex-direction:row; + width:100%; + flex-flow: row wrap; +max-width: 1265px; +} + + +.search-radio { + padding: 5px; +} + +.radio-label { + font-size: 19px; + margin-bottom: 10px; +} + +.form-row { + display:flex; + flex-direction:row; + width:100%; + flex-flow: row wrap; +} + +.type { + display: flex; + flex-direction:column; +} + +.search-actions { + display: flex; + flex-direction: column; + align-items: flex-start; + padding-left: 15px; + margin-left: 10px; + border-left: 1px solid #b3b3b3; + min-width: 235px; + + .mat-slider-horizontal { + width: 100%; + } +} + + +.action-button-container { + margin-top: 20px; + align-self: flex-end; + width: 100%; + + button { + width: 100%; + } +} + +.search-content-container { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 65px 5px 0 5px; +} + + + + +.sort { + margin-right:25px; +} + + + + +.mat-card-title { + white-space: normal; + + .substance-name { + color: #448aff; + padding-right: 10px; + } + + .approval-id { + font-size: 16px; + color: red; + } +} +.table-view-name { + color: #448aff; +} + +.tile-title { + height:19px; +} + +::ng-deep .mat-paginator-range-label { + font-weight: 550; +} + +.main-title { + font-size: 26px; + padding-left: 10px; + padding-right: 25px; + font-weight: 550; + padding-top: 10px; + color: #4793d1; + width: 285px; +} + +.mat-card-content { + + .mat-chip-list-container { + margin-left: -20px; + margin-bottom: 10px; + } + .mat-chip-list-container-2 { + margin-bottom: 10px; + } +} + +.substance-content { + display: flex; + flex-direction: row; +} + +.upper-padding { + margin-top: 15px; +} + +.tile { + height:305px; + margin:10px; + padding:10px; + width: calc(25% - 20px); + min-width:200px; + max-width:230px; + align-content:center; + overflow:hidden; + text-overflow: ellipsis; +} + +.tile .image-thumbnail { + margin:auto; + height:175px; + width:175px; +} + +.tile .structure-container { + width:100%; + padding-right:0px; +} + +.tile-name { + white-space:nowrap; + text-overflow: ellipsis; + overflow: hidden; + width:100%; + height:25px; + text-align: center; +} + +.tile-name ::ng-deep { + svg { + margin-bottom: -10px; + } +} + +.expand-button { + align-content: flex-end; + color: #448aff; +} +.mat-expansion-panel { + box-shadow: none !important; +} + +.substance-tiles{ + flex-flow: row wrap; +} + +.image-other { + width:175px; + height:175px; +} + +.substance-data { + display: flex; + flex-direction: row; + + &:not(:last-child) { + margin-bottom: 15px; + } + + .label { + font-weight: bold; + min-width: 100px; + } +} + +.structure-container { + padding-right: 10px; +} + +.no-results { + text-align: center; + margin-top: 30px; + font-size: 1.2em; +} + +.mat-paginator { + background: transparent; +} + +.image-thumbnail{ + height:150px; + width:150px; +} + +.space-bottom { + margin-bottom: 5px; +} + +.exact-matches-container { + margin-top: 80px; +} + +.exact-match-control { + display: flex; + flex-direction: column; + align-content: center; + justify-content: center; + align-items: center; + + button { + margin-top: 15px; + } +} + +.add-height { + height: 20px; +} + +.facet-advanced-options-link { + margin-top: 7px; + color: #448aff; +} + +.facet-search-container { + display: flex; + + .mat-form-field { + flex-grow: 1; + } +} + +.facet-search-loading.mat-progress-bar { + margin-top: -18px; + margin-bottom: 1.09em; +} + +.break { + flex-basis: 0%; + height: 0; + width:0px; + } + + .export { + margin: auto; +} + +.export-button { + color: white; + border-radius: 4px; +} + +.similarity { + padding-left: 10px; + font-family: Menlo,Monaco,Consolas,"Courier New",monospace; + color: #c7254e; +} + +.similarity-label { + font-style: italic; +} + +.menu-checkbox:hover { + background-color: white; +} +@media(max-width: 1750px) { + + .full-paginator { + width: 100%; + align-content: center; + } + .break { + flex-basis: 100%; + height: 0; + width: auto; + } + + .controls-container { + flex-wrap: wrap; + } + + .export { + margin: auto; + } +} + +@media(max-width: 1100px) { + .controls-container, .search-parameters { + padding-left: 30px; + } + + .controls-container { + flex-wrap: wrap; + } + + .side-nav-content { + padding-top: 10px; + } + + .export { + margin-right:30px; + } +} + +@media(max-width: 730px) { + .mat-card-title { + flex-direction: column; + align-items: flex-start; + + .substance-name { + margin-bottom: 10px; + } + } + + .page-selector { + display:none !important; + } + + .full-paginator { + min-width: 500px !important; + } + .substance-data { + flex-direction: column; + + .label { + margin-bottom: 10px; + } + + .value, .code-system { + padding-left: 20px; + } + } + + .search-parameters { + font-size: .9em; + + .mat-icon-button { + width: 35px; + height: 35px; + line-height: 35px; + } + } + + .side-nav-content { + padding: 5px; + } +} + +.lock-icon { + color: #f0ad4e; +} + +.narrow-search-suggestions-container { + display: flex; + align-content: center; + align-items: center; + margin-bottom: 10px; + justify-content: center; + flex-direction: column; +} + +.flex-row { + width: 100%; + margin: auto; +} + + +.mat-tab-label-content { + font-size: 18px; +} + +.paginator-row { + justify-content: flex-end; + margin-top: 15px; +} + +.mat-tab-labels { + margin-bottom: 15px; +} + +.narrow-search-suggestions { + padding: 12px 20px; + width: auto; + display: flex; + align-content: center; + align-items: center; + + .mat-flat-button { + margin-left: 7px; + } +} + +@media(max-width: 600px) { + + .substance-content { + flex-direction: column; + + .image-thumbnail { + margin: 0 auto; + } + } + + .controls-container { + + ::ng-deep { + + .mat-paginator-range-l.references-link{ + display: inline-flex; + vertical-align: middle; +}abel { + margin: 0 3px 0 0; + } + + .mat-paginator-page-size-select { + margin-left: 0; + } + + .mat-paginator-container { + padding-right: 0; + padding-left: 0; + } + + .mat-paginator-page-size { + margin-right: 4px; + } + + .mat-paginator-range-actions { + + .mat-icon-button { + width: 35px; + height: 35px; + line-height: 35px; + } + } + } + } +} + +@media(max-width: 505px) { + .full-paginator { + min-width: 0px !important; + } + .controls-container { + + ::ng-deep { + + .mat-paginator-page-size-label { + display: none; + } + } + } +} + +.zoom:hover{ + cursor:zoom-in; +} + +.icon-align{ + margin-top: -10px; + vertical-align: bottom; +} + +.ext-link{ + color: #448aff; + text-decoration-style: unset; +} + +.sort{ + margin:auto; + margin-bottom: -7px; +} + +.advanced { + padding-left:20px; + color:#448aff; + +} + +.facet-options { + width: 100%; + display:flex; +} + +.tile-button-container { + display: flex; + flex-direction: row; + justify-content: space-evenly +} + +.mat-elevation-z2 { + background-color: white; +} + +.more-content { + width:45%; +} + +.advanced-container { + width:55%; + align-content:right; +} + +.selected-container { + font-size:14px; +} + +.selected-label { + padding-right:5px; +} + +.reset-facets-button { + margin-left: 5px; + margin-bottom: 5px; +} + +.selected-parameter { + height:auto; + padding-top: 3px; + padding-bottom: 3px; +} + +.selected-container { + max-width:700px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.selected-label { + max-width:200px; + overflow: hidden; + text-overflow: ellipsis; +} + +.not-icon { + height:18px !important; + width: 18px !important; + vertical-align: bottom; +} +.page-select { + width:100px; +} + +::ng-deep .auto-width{ + ::ng-deep .mat-form-field { + width: auto !important; + } + ::ng-deep .mat-select-value { + max-width: 100%; + width: auto; + } +} + +.page-label { + color:rgba(0, 0, 0, 0.54); + display:block; + font-family:Roboto, "Helvetica Neue", sans-serif; + font-size:14px; + padding-right: 12px; +} + +.page-selector { + display: flex; + flex-direction: row; + align-items: center; +margin-left: 20px; + } + + + :host ::ng-deep .mat-paginator-range-label{ + margin: 0 5px 0 5px !important; + + } + + mat-paginator { + font-size:16px; + } + + .page-input { + width: 50px; + font-size:14px; + } + +.bad-page { + color: red; +} + +.full-paginator { + display: flex; + min-width: 660px; +} + +.disable-export { + pointer-events:none; + opacity:.5; +} + +.button-link-img { + line-height: 40px; + color: black; +} +.red-text { + color: red; + font-style: italic; +} +.orange-text { + color: orange; + font-style: italic; +} +.sub-search-div { + display: none; // remove to see UI added +} +.sub-search-ref-btn { + padding: 10px; + margin: 0 5px; + background-color: white; + box-shadow: 2px 2px lightgrey; + border: 0.4px solid white; + border-radius: 5px; +} +.sub-search-cancel-btn { + padding: 10px; + color: white; + background-color: red; + box-shadow: 2px 2px lightgrey; + border: 0.4px solid red; + border-radius: 5px; +} +.sub-search-text { + text-align: center; + // margin: 7px; +} +.match-txt { + display: inline; + padding: 0 5px; +} +.sub-search-text-div { + text-align: center; +} +.search-spinner { + width: 15px; + height: 15px; + display: inline-table; + margin: auto; +} +// .mat-progress-spinner circle, .mat-spinner circle { +// stroke: grey; +// } +.search-spinner ::ng-deep .mat-progress-spinner circle, .mat-spinner circle { + stroke: grey; + width: 10px !important; + height: 10px !important; + display: inline; +} +.cdk-overlay-backdrop, .cdk-global-overlay-wrapper { + display: none !important; +} + + +.mat-mini-fab { + background-color: var(--mat-icon-button-bg-color); + color: var(--label-color); + width: 35px; + height: 35px; + + &:not(:first-child) { + margin-top: 3px; + } + + ::ng-deep .mat-button-wrapper { + padding: 0; + } + } \ No newline at end of file diff --git a/src/app/core/substance-selector/advanced-selector-dialog/advanced-selector-dialog.component.spec.ts b/src/app/core/substance-selector/advanced-selector-dialog/advanced-selector-dialog.component.spec.ts new file mode 100644 index 000000000..ffa31f692 --- /dev/null +++ b/src/app/core/substance-selector/advanced-selector-dialog/advanced-selector-dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AdvancedSelectorDialogComponent } from './advanced-selector-dialog.component'; + +describe('AdvancedSelectorDialogComponent', () => { + let component: AdvancedSelectorDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AdvancedSelectorDialogComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AdvancedSelectorDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-selector/advanced-selector-dialog/advanced-selector-dialog.component.ts b/src/app/core/substance-selector/advanced-selector-dialog/advanced-selector-dialog.component.ts new file mode 100644 index 000000000..6e6ad11f1 --- /dev/null +++ b/src/app/core/substance-selector/advanced-selector-dialog/advanced-selector-dialog.component.ts @@ -0,0 +1,519 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { Editor } from '@gsrs-core/structure-editor'; +import { ControlledVocabularyService } from '@gsrs-core/controlled-vocabulary'; +import { LoadingService } from '@gsrs-core/loading'; +import { MAT_DIALOG_DATA, MatDialogRef, MatDialog } from '@angular/material/dialog'; +import { StructureService, InterpretStructureResponse, StructureImageModalComponent, StructureImportComponent } from '@gsrs-core/structure'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { NavigationExtras, Router } from '@angular/router'; +import { SubstanceService } from '@gsrs-core/substance/substance.service'; +import { FacetParam } from '@gsrs-core/facets-manager'; +import { PageEvent } from '@angular/material/paginator'; +import { SubstanceDetail } from '@gsrs-core/substance/substance.model'; +import { ConfigService } from '@gsrs-core/config'; +import { StructureExportComponent } from '@gsrs-core/structure/structure-export/structure-export.component'; +import { searchSortValues } from '@gsrs-core/utils'; +@Component({ + selector: 'app-advanced-selector-dialog', + templateUrl: './advanced-selector-dialog.component.html', + styleUrls: ['./advanced-selector-dialog.component.scss'] +}) +export class AdvancedSelectorDialogComponent implements OnInit { + + private editor: Editor; + connectivity: Array; + dat: any; + domains: any; + forms: Array = []; + term2: any = {value: '', display: ''}; + privateTerm: any = {value: '', display: ''}; + asDialog = false; + vocabulary: any; + message: string; + validationMessages =[]; + adminPanel?: boolean; + molfile: string; + private privateFacetParams: FacetParam; + showResults = false; + totalSubstances = 0; + pageIndex = 0; + pageSize = 10; + nameTotalSubstances = 0; + namePageIndex = 0; + namePageSize = 10; + lastPage: number; + searchValue: string; + nameSearched = false; + structureSearched = false; + + loading = false; + order = "default"; + public sortValues = searchSortValues; + + activeTab: number; + current: string; + lastTab: number; + +smiles?: any; +searchType = 'substructure'; +_searchtype: string; +similarityCutoff?: number; +showSimilarityCutoff = false; +substances?: Array; +nameSubstances?: Array; +nameResponse: any; +response: any; + + +panelOpenState = true; +private overlayContainer: HTMLElement; + +private privateSearchTerm = ''; +private privateStructureSearchTerm?: string; +private privateSequenceSearchTerm?: string; +private privateSearchType?: string; +private privateSearchCutoff?: number; +private privateSearchSeqType?: string; +private privateSequenceSearchKey?: string; + + + constructor( + private CVService: ControlledVocabularyService, + private loadingService: LoadingService, + private structureService: StructureService, + private overlayContainerService: OverlayContainer, + private substanceService: SubstanceService, + private configService: ConfigService, + private dialog: MatDialog, + public dialogRef: MatDialogRef, + private router: Router, + + @Inject(MAT_DIALOG_DATA) public data: any + ) { + this.dat = data; + + } + + + + get standardized(): boolean { + return this.privateTerm; + } + + seedSmiles(smiles: string) { + const navigationExtras: NavigationExtras = { + queryParams: { + importStructure: encodeURIComponent(smiles) + } + }; + let url = ''; + if (this.configService.configData && this.configService.configData.gsrsHomeBaseUrl) { + url = this.configService.configData.gsrsHomeBaseUrl + '/substances/register/chemical' + '?importStructure=' + encodeURIComponent(smiles); + } else { + url = this.router.serializeUrl( + this.router.createUrlTree(['/substances/register/chemical'], { + queryParams: navigationExtras.queryParams}) + ); + } + + window.open(url, '_blank'); + } + + close() { + this.dialogRef.close(); + } + + standardize(standard: string): void { + const mol = this.editor.getMolfile(); + this.structureService.interpretStructure(mol, '', standard).subscribe((response: InterpretStructureResponse) => { + if (response && response.structure && response.structure.molfile) { + this.editor.setMolecule(response.structure.molfile); + } + }, () => {}); + } + + onTabChanged(event: any): void { + this.activeTab = event.index; + } + + searchCutoffChanged(event): void { + this.similarityCutoff = event.value; + } + + nameSearch(event: any): void { + this.searchValue = event; + this.privateSearchTerm = event; + this.privateStructureSearchTerm = null; + this.nameSearched = true; + this.namePageIndex = 0; + this.searchSubstances(null, null, 'name'); + } + + reSort() { + this.searchSubstances(null, null, 'name'); + } + + ngOnInit(): void { + this.overlayContainer = this.overlayContainerService.getContainerElement(); + + if (this.privateTerm.simplifiedStructure) { + this.privateTerm.simpleSrc = this.CVService.getStructureUrl(this.privateTerm.simplifiedStructure); + } + if (this.privateTerm.fragmentStructure) { + this.privateTerm.fragmentSrc = this.CVService.getStructureUrl(this.privateTerm.fragmentStructure); + } + this.overlayContainer = this.overlayContainerService.getContainerElement(); + + if(this.dat && this.dat.uuid && this.data.tab !== 1) { + this.structureService.getMolfile(this.dat.uuid); + } + + if(this.dat && this.dat.name) { + this.searchValue = this.dat.name; + } + this.activeTab = this.data.tab; + setTimeout(() => { + this.activeTab = this.data.tab; + }, 10); + + } + + molvecUpdate(mol: any) { + this.editor.setMolecule(mol); + } + + editorOnLoad(editor: Editor): void { + this.overlayContainer.style.zIndex = '1003'; + + this.overlayContainer.style.zIndex = '10003'; + this.loadingService.setLoading(false); + this.editor = editor; + if(this.dat && this.dat.uuid) { + this.structureService.getMolfile(this.data.uuid).subscribe( response => { + this.editor.setMolecule(response); + this.overlayContainer.style.zIndex = '1003'; + + this.overlayContainer.style.zIndex = '10003'; + + }); + } else if ( + this.dat && this.dat.molfile + ) { + this.editor.setMolecule(this.dat.molfile); + this.overlayContainer.style.zIndex = '10003'; + } + + setTimeout(() => { + // re-adjust z-index after editor messes it up (to adjust relative index for periodic table, right click) + this.overlayContainer.style.zIndex = '1003'; + + this.overlayContainer.style.zIndex = '10003'; + }, 100); + } + + search(): void { + const mol = this.editor.getMolfile(); + this.structureService.interpretStructure(mol).subscribe((response: InterpretStructureResponse) => { + this.smiles = response.structure.smiles; + this.response = response.structure.id; + this.searchSubstances(response.structure.id, response.structure.smiles); + }, () => {}); + } + + + nameResolved(molfile: string): void { + this.editor.setMolecule(molfile); + } + + updateType(event: any) { + this.searchType = event.value; + + this.privateSearchType = event.value; + + if (this.searchType === 'similarity') { + this.showSimilarityCutoff = true; + this.similarityCutoff = 0.8; + } else { + this.showSimilarityCutoff = false; + } + } + + navigateToBrowseSubstance(type: string, searchTerm?: string, smiles?: string): void { + + let navString = ''; + + + const navigationExtras: NavigationExtras = { + queryParams: {} + }; + const navigationExtras2: NavigationExtras = { + queryParams: {} + }; + + if (type === 'structure') { + navigationExtras.queryParams['structure_search'] = this.privateStructureSearchTerm || null; + navigationExtras.queryParams['type'] = this.searchType || null; + + navigationExtras2.queryParams['structure'] = this.privateStructureSearchTerm || null; + navigationExtras2.queryParams['type'] = this.searchType || null; + + navString += '?structure_search=' + navigationExtras.queryParams['structure_search'] + + '&type=' + navigationExtras.queryParams['type']; + + if (this.searchType === 'similarity') { + navigationExtras.queryParams['cutoff'] = this.similarityCutoff || 0; + navString += '&cutoff=' + navigationExtras.queryParams['cutoff'] ; + } + + if (smiles != null) { + navigationExtras.queryParams['smiles'] = smiles; + } + + + + + } else { + navigationExtras.queryParams['search'] = this.searchValue; + navString += '?search=' + navigationExtras.queryParams['search']; + if (this.order) { + navigationExtras.queryParams['order'] = this.order; + + navString += '&order=' + this.order; + } + } + + let url = ''; + if (this.configService.configData && this.configService.configData.gsrsHomeBaseUrl) { + url = this.configService.configData.gsrsHomeBaseUrl + '/browse-substance' + navString; + } else { + url = this.router.serializeUrl( + this.router.createUrlTree(['/browse-substance'], { + queryParams: navigationExtras.queryParams}) + ); + } + + window.open(url, '_blank'); + + } + + + searchSubstances(structureSearchTerm?: string, smiles?: string, type?: string) { + let size = this.pageSize; + let index = this.pageIndex * this.pageSize; + if (structureSearchTerm){ + this.privateStructureSearchTerm = structureSearchTerm || null; + this.privateSearchType = this.searchType || 'substructure'; + + if (this.searchType === 'similarity') { + this.privateSearchCutoff = this.similarityCutoff || 0; + } + + + } else { + this.privateStructureSearchTerm = null; + size = this.namePageSize; + index = this.namePageIndex * this.namePageSize; + } + let sort = null; + if(type && type === 'name') { + sort = this.order; + + } + this.loadingService.setLoading(true); + this.loading = true; + const subscription = this.substanceService.getSubstancesSummaries({ + searchTerm: this.privateSearchTerm, + structureSearchTerm: this.privateStructureSearchTerm, + sequenceSearchTerm: this.privateSequenceSearchTerm, + cutoff: this.privateSearchCutoff, + type: this.privateSearchType, + seqType: this.privateSearchSeqType, + order: sort, + pageSize: size, + facets: this.privateFacetParams, + skip: index, + sequenceSearchKey: this.privateSequenceSearchKey, + deprecated: false + }) + .subscribe(pagingResponse => { + + if(type && type === 'name') { + this.nameSubstances = (pagingResponse && pagingResponse.content) ? pagingResponse.content : []; + this.nameTotalSubstances = pagingResponse.total; + } else { + this.substances = (pagingResponse && pagingResponse.content) ? pagingResponse.content : []; + this.totalSubstances = pagingResponse.total; + if (this.totalSubstances > 0) { + this.panelOpenState = false; + } + } + + if (pagingResponse.total % this.pageSize === 0) { + this.lastPage = (pagingResponse.total / this.pageSize); + } else { + this.lastPage = Math.floor(pagingResponse.total / this.pageSize + 1); + } + + + this.overlayContainer.style.zIndex = '1003'; + + this.overlayContainer.style.zIndex = '10003'; + this.loadingService.setLoading(false); + + this.overlayContainer.style.zIndex = '10003'; + setTimeout(() => { + // re-adjust z-index after editor messes it up + this.overlayContainer.style.zIndex = '1003'; + + this.overlayContainer.style.zIndex = '10003'; + + this.loading = false; + }); + + }); + + } + + openStructureImportDialog(): void { + const dialogRef = this.dialog.open(StructureImportComponent, { + height: 'auto', + width: '650px', + data: {} + }); + // this.overlayContainer.style.zIndex = '1002'; + + dialogRef.afterClosed().subscribe((structurePostResponse?: InterpretStructureResponse) => { + setTimeout(() => { + this.overlayContainer.style.zIndex = '1003'; + this.overlayContainer.style.zIndex = '10003'; + }); + if (structurePostResponse && structurePostResponse.structure && structurePostResponse.structure.molfile) { + setTimeout(()=>{ + this.editor.setMolecule(structurePostResponse.structure.molfile); + }) + } + }, () => { + setTimeout(() => { + this.overlayContainer.style.zIndex = '1003'; + this.overlayContainer.style.zIndex = '10003'; + }); + }); + } + + openStructureExportDialog(): void { + const dialogRef = this.dialog.open(StructureExportComponent, { + height: 'auto', + width: '650px', + data: { + molfile: this.editor.getMolfile(), + smiles: this.editor.getSmiles() + } + }); + // this.overlayContainer.style.zIndex = '1002'; + + dialogRef.afterClosed().subscribe(() => { + setTimeout(() => { + this.overlayContainer.style.zIndex = '1003'; + this.overlayContainer.style.zIndex = '10003'; + }); + }, () => { + setTimeout(() => { + this.overlayContainer.style.zIndex = '1003'; + this.overlayContainer.style.zIndex = '10003'; + }); + }); + } + + + checkImg(term: any) { + term.fragmentSrc = this.CVService.getStructureUrlFragment(term.fragmentStructure); + term.simpleSrc = this.CVService.getStructureUrlFragment(term.simplifiedStructure); + + } + openImageModal(substance: SubstanceDetail): void { + + let data: any; + let molfile: string; + + if (substance.substanceClass === 'chemical') { + data = { + structure: substance.uuid, + smiles: substance.structure.smiles, + uuid: substance.uuid, + names: substance.names, + component: 'selector' + }; + molfile = substance.structure.molfile; + } else { + data = { + structure: substance.uuid, + names: substance.names, + component: 'selector' + }; + if (substance.polymer) { + molfile = substance.polymer.idealizedStructure.molfile; + } else { + molfile = null; + } + } + + + + const dialogRef = this.dialog.open(StructureImageModalComponent, { + width: '650px', + panelClass: 'structure-image-panel', + data: data + }); + + this.overlayContainer.style.zIndex = '1002'; + + const subscription = dialogRef.afterClosed().subscribe(response => { + if (response && response === 'molfile') { + this.panelOpenState = true; + if (this.activeTab === 1) { + this.activeTab = 0; + this.dat.molfile = molfile; + } else { + setTimeout(()=> { + if (this.editor) { + this.editor.setMolecule(molfile); + } + }, 150); + } + + } + if (response && response === 'select') { + this.selectSubstance(substance); + + } + this.overlayContainer.style.zIndex = '1003'; + subscription.unsubscribe(); + }, () => { + this.overlayContainer.style.zIndex = '1003'; + subscription.unsubscribe(); + }); + + } + + selectSubstance(substance: SubstanceDetail) { + this.dialogRef.close(substance); + } + + + changePage(pageEvent: PageEvent, type?: string) { + if (type && type === 'name') { + this.namePageSize = pageEvent.pageSize; + this.namePageIndex = pageEvent.pageIndex; + this.searchSubstances(null, null, 'name'); + + } else { + this.pageSize = pageEvent.pageSize; + this.pageIndex = pageEvent.pageIndex; + this.searchSubstances(this.response); + + } + + } +} + + diff --git a/src/app/core/substance-selector/substance-selector.component.html b/src/app/core/substance-selector/substance-selector.component.html index 18963fb54..3ff863bf1 100644 --- a/src/app/core/substance-selector/substance-selector.component.html +++ b/src/app/core/substance-selector/substance-selector.component.html @@ -1,29 +1,84 @@
    - - - -
    - + + +
    + +
    + +
    +
    + + + + + + +
    + +
    {{header}}
    +
    + + {{selectedSubstance.approvalID}} + +
    + + +
    + + + + + + +
    NOT IN DATABASE
    +
    +
    + Change Selection + + +
    + + +
    + +
    + + + +
    NO SUBSTANCES FOUND
    + + + +
    \ No newline at end of file diff --git a/src/app/core/substance-selector/substance-selector.component.scss b/src/app/core/substance-selector/substance-selector.component.scss index 8e2119d63..24cc6abe7 100644 --- a/src/app/core/substance-selector/substance-selector.component.scss +++ b/src/app/core/substance-selector/substance-selector.component.scss @@ -1,8 +1,59 @@ + .selected-substance-container { max-width: 100%; width: 100%; } +.zoom { + cursor: zoom-in !important; +} + +::ng-deep .zoom:hover { + cursor: zoom-in !important; + +} + +.zoom:hover { + cursor: zoom-in !important; + +} + +.approval-id { + font-size: 16px; + color: var(--primary-color); +} + +.name-div { + margin-top: -3px; + margin-bottom: 7px; +} +.change-link { + margin-right: 25px; + margin-left: -40px; +} + +.under-link { + color: var(--link-color); + font-weight:550; +} + +.name-link { + color: var(--link-color); + text-decoration: underline; +} +.new-tab { + height: 16px !important; + width: 16px !important; +} + +.centered { + text-align: center; + margin-left: -42px; + margin-top: 5px; +} + + + .selected-substance { display: flex; flex-direction: column; @@ -14,6 +65,10 @@ display: block; max-width: 200px; } + img :hover { + cursor: zoom-in !important; + + } } .undo { @@ -21,6 +76,8 @@ margin-top: -5px; } + + .selected-substance-options { position: absolute; right: 5px; @@ -29,8 +86,8 @@ flex-direction: column; .mat-mini-fab { - background-color: rgba(242, 242, 242, .85); - color: #404040; + background-color: var(--mat-icon-button-bg-color); + color: var(--label-color); width: 35px; height: 35px; @@ -47,3 +104,12 @@ .hidden { display: none !important; } + +.divflex { + display: flex; + font-size: 12px; +} + +.marginright10px { + margin-right: 10px; +} diff --git a/src/app/core/substance-selector/substance-selector.component.ts b/src/app/core/substance-selector/substance-selector.component.ts index edc7e20b6..f3e4970f9 100644 --- a/src/app/core/substance-selector/substance-selector.component.ts +++ b/src/app/core/substance-selector/substance-selector.component.ts @@ -2,6 +2,13 @@ import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; import { SubstanceService } from '../substance/substance.service'; import { SubstanceSummary } from '../substance/substance.model'; import { ConfigService } from '@gsrs-core/config'; +import { MatDialog } from '@angular/material/dialog'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { AdvancedSelectorDialogComponent } from '@gsrs-core/substance-selector/advanced-selector-dialog/advanced-selector-dialog.component'; +import { StructureImageModalComponent } from '@gsrs-core/structure'; +import { SubstanceFormService } from '@gsrs-core/substance-form/substance-form.service'; +import { Router } from '@angular/router'; +import { ScrollToService } from '@gsrs-core/scroll-to/scroll-to.service'; @Component({ selector: 'app-substance-selector', @@ -17,29 +24,59 @@ export class SubstanceSelectorComponent implements OnInit { @Input() header = 'Substance'; @Input() name?: string; @Input() hideImage?: boolean; + @Input() showMorelinks? = false; errorMessage: string; showOptions: boolean; previousSubstance: SubstanceSummary; displayName: string; - private substanceSelectorProperties: Array = [ + overlayContainer: any; + savedRecord: SubstanceSummary; + searchVal: string; + + // Change to configuration approach. + private substanceSelectorProperties: Array = null; + /* 'root_names_name', + 'root_names_stdName', 'root_approvalID', 'CAS', 'ECHA\ \(EC\/EINECS\)' ]; - + */ constructor( public substanceService: SubstanceService, - public configService: ConfigService + private substanceFormService: SubstanceFormService, + public configService: ConfigService, + private overlayContainerService: OverlayContainer, + public scrollToService: ScrollToService, + private dialog: MatDialog, + private router: Router ) { } + StoreSelection() { + // sessionStorage.setItem('GSRS-default-selected-substance', JSON.stringify(this.selectedSubstance)); + this.substanceFormService.setStoredRelated(this.selectedSubstance, this.header); + alert('Default ' + this.header + ' is set to ' + this.selectedSubstance._name); + } + ngOnInit() { - if (!this.hideImage) { - this.hideImage = false; + + // const data = sessionStorage.getItem('GSRS-default-selected-substance'); + const data = this.substanceFormService.getStoredRelated(this.header); + if(data) { + this.selectedSubstance = data; + this.selectionUpdated.emit(this.selectedSubstance); } + + + if (this.configService.configData.substanceSelectorProperties != null) { this.substanceSelectorProperties = this.configService.configData.substanceSelectorProperties; + } else { + console.log("The config value for substanceSelectorProperties is null."); } + this.overlayContainer = this.overlayContainerService.getContainerElement(); + } @Input() @@ -47,6 +84,7 @@ export class SubstanceSelectorComponent implements OnInit { if (uuid) { this.substanceService.getSubstanceSummary(uuid).subscribe(response => { this.selectedSubstance = response; + this.errorMessage = ''; }, error => { if (this.name && this.name !== '') { this.selectedSubstance = {_name: this.name}; @@ -60,13 +98,8 @@ export class SubstanceSelectorComponent implements OnInit { processSubstanceSearch(searchValue: string = ''): void { const q = searchValue.replace('\"', ''); - const searchStr = this.substanceSelectorProperties.map(property => `${property}:\"^${q}$\"`).join(' OR '); - // const searchStr = `root_names_name:\"^${q}$\" OR ` + - // `root_approvalID:\"^${q}$\" OR ` + - // `root_codes_BDNUM:\"^${q}$\"`; - this.substanceService.getQuickSubstancesSummaries(searchStr, true).subscribe(response => { if (response.content && response.content.length) { this.selectedSubstance = response.content[0]; @@ -78,9 +111,97 @@ export class SubstanceSelectorComponent implements OnInit { }); } + + + advanced(type: string): void { + + let thisy = window.pageYOffset; + window.scroll({ + top: 0, + left: 0, + behavior: 'auto' }); + + let active = 0; + if (type === 'name') { + active = 1; + } + const dialogRef = this.dialog.open(AdvancedSelectorDialogComponent, { + minWidth: '80%', + maxWidth: '90%', + height: '92%', + data: {uuid: this.selectedSubstance ? this.selectedSubstance.uuid : null, + name: this.selectedSubstance ? this.selectedSubstance._name : null, + tab: active} + }); + this.overlayContainer.style.zIndex = '1002'; + + dialogRef.afterClosed().subscribe(result => { + window.scroll({ + top: thisy, + left: 0, + behavior: 'auto' }); + if (result) { + this.selectedSubstance = result; + this.selectionUpdated.emit(result); + this.errorMessage = ''; + } + + this.overlayContainer.style.zIndex = null; + }); + } + + + openImageModal() { + let data: any; + let molfile: string; + let substance = this.selectedSubstance; + + if (substance.substanceClass === 'chemical') { + data = { + structure: substance.uuid, + smiles: substance.structure.smiles, + uuid: substance.uuid, + names: substance.names, + component: 'substanceSelector' + }; + molfile = substance.structure.molfile; + } else { + data = { + structure: substance.uuid, + names: substance.names, + component: 'substanceSelector', + uuid: substance.uuid, + }; + if (substance.polymer) { + molfile = substance.polymer.idealizedStructure.molfile; + } else { + molfile = null; + } + } + + + + const dialogRef = this.dialog.open(StructureImageModalComponent, { + width: '650px', + panelClass: 'structure-image-panel', + data: data + }); + + this.overlayContainer.style.zIndex = '1002'; + + const subscription = dialogRef.afterClosed().subscribe(response => { + this.overlayContainer.style.zIndex = null; + subscription.unsubscribe(); + }, () => { + this.overlayContainer.style.zIndex = null; + subscription.unsubscribe(); + }); + } + editSelectedSubstance(): void { this.previousSubstance = JSON.parse(JSON.stringify(this.selectedSubstance)); this.selectedSubstance = null; + this.searchVal = null; this.selectionUpdated.emit(this.selectedSubstance); } @@ -89,4 +210,35 @@ export class SubstanceSelectorComponent implements OnInit { this.selectionUpdated.emit(this.selectedSubstance); } + delete(): void { + this.selectedSubstance = null; + this.selectionUpdated.emit(null); + } + + openInNewTab(uuid: string): void { + let url = ''; + if (this.configService.configData && this.configService.configData.gsrsHomeBaseUrl) { + url = this.configService.configData.gsrsHomeBaseUrl + '/substances/' + uuid; + + } else { + url = this.router.serializeUrl( + this.router.createUrlTree(['/substances/' + uuid]) + ); + } + window.open(url, '_blank'); + } + + registerNew() { + let url = ''; + if (this.configService.configData && this.configService.configData.gsrsHomeBaseUrl) { + url = this.configService.configData.gsrsHomeBaseUrl + '/substances/register'; + + } else { + url = this.router.serializeUrl( + this.router.createUrlTree(['/substances/register']) + ); + } + window.open(url, '_blank'); + } + } diff --git a/src/app/core/substance-selector/substance-selector.module.ts b/src/app/core/substance-selector/substance-selector.module.ts index 053697cec..7393b7e79 100644 --- a/src/app/core/substance-selector/substance-selector.module.ts +++ b/src/app/core/substance-selector/substance-selector.module.ts @@ -6,7 +6,20 @@ import { MatIconModule } from '@angular/material/icon'; import { MatButtonModule } from '@angular/material/button'; import { RouterModule } from '@angular/router'; import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; -import { MatTooltipModule } from '@angular/material'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { AdvancedSelectorDialogComponent } from '@gsrs-core/substance-selector/advanced-selector-dialog/advanced-selector-dialog.component'; +import { StructureEditorComponent, StructureEditorModule } from '@gsrs-core/structure-editor'; +import { NameResolverModule } from '@gsrs-core/name-resolver/name-resolver.module'; +import { MatOptionModule } from '@angular/material/core'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; +import { MatCardModule } from '@angular/material/card'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatSliderModule } from '@angular/material/slider'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { FormsModule } from '@angular/forms'; @NgModule({ imports: [ @@ -16,13 +29,28 @@ import { MatTooltipModule } from '@angular/material'; MatButtonModule, MatTooltipModule, RouterModule, - SubstanceImageModule + SubstanceImageModule, + StructureEditorModule, + NameResolverModule, + MatOptionModule, + MatFormFieldModule, + MatSelectModule, + MatCardModule, + MatRadioModule, + MatPaginatorModule, + MatTabsModule, + MatSliderModule, + MatExpansionModule, + FormsModule ], declarations: [ - SubstanceSelectorComponent + SubstanceSelectorComponent, + AdvancedSelectorDialogComponent + ], exports: [ - SubstanceSelectorComponent + SubstanceSelectorComponent, + AdvancedSelectorDialogComponent ] }) export class SubstanceSelectorModule { } diff --git a/src/app/core/substance-ssg2/ssg2-manufacturing/ssg2-manufacturing.component.html b/src/app/core/substance-ssg2/ssg2-manufacturing/ssg2-manufacturing.component.html new file mode 100644 index 000000000..fbde86f28 --- /dev/null +++ b/src/app/core/substance-ssg2/ssg2-manufacturing/ssg2-manufacturing.component.html @@ -0,0 +1,147 @@ +
    + +
    + +

    +
    + +
    + +
    + +
    + + + + + + + + + + + + + +
    + +
    + + + + + +
    + +
    +
    + Organization:     +
    + + + + + +
    + +
    + OR Enter a new Organization + +
    + + + + + + + + + + + + + + + +
    + + + +
    +
    + Sites:     + + + + + +
    + + +
    + +
    + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + +
    + +
    +
    \ No newline at end of file diff --git a/src/app/core/substance-ssg2/ssg2-manufacturing/ssg2-manufacturing.component.scss b/src/app/core/substance-ssg2/ssg2-manufacturing/ssg2-manufacturing.component.scss new file mode 100644 index 000000000..1c064dc4d --- /dev/null +++ b/src/app/core/substance-ssg2/ssg2-manufacturing/ssg2-manufacturing.component.scss @@ -0,0 +1,182 @@ +.form-container { + padding: 5px 10px 12px 10px; + position: relative; +} + +.notification-backdrop { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: flex; + z-index: 10; + background-color: rgba(255, 255, 255, .8); + justify-content: center; + align-items: center; + font-size: 30px; + font-weight: bold; + color: #666; +} + +.form-row { + display: flex; + width: 100%; + + .col-4-1 { + width: calc((100% - 20px * 3) / 4); //four column + margin-right: 20px; + } + + .col-3-1 { + width: calc((100% - 20px * 2) / 3); //three column + margin-right: 20px; + } + + .col-1-1 { + width: calc((100% - 20px * 1)); //one column + margin-right: 10px; + } + + .delete-container { + padding: 0 10px 8px 0; + } +} +/* +.form-row { + display: flex; + justify-content: space-between; + align-items: flex-end; + + .delete-container { + padding: 0 10px 8px 0; + } + + .row { + flex-grow: 1; + padding-right: 15px; + } +} +*/ + +.references-container { + width: 100%; +} + +.materialborder { + padding-top: 10px; + margin-top: 15px; + border: 2px solid rgb(182, 175, 175); +} + +.materialtitle { + font-size: 18px; + padding-left: 10px; +} + +.divflex { + display: flex; +} + +.divflexright { + display: flex; + justify-content: flex-end; +} + +.bordergray { + border: 1px solid gray; +} + +.borderorange { + border: 1px solid orangered; +} + +.fontsize18px { + font-size: 18px; +} + +.colorgreen { + color: green; +} + +.margintop10px { + margin-top: 10px; +} + +.marginleft40px { + margin-left: 40px; +} + +.marginbottom20px { + margin-top: 20px; +} + +.button-add { + z-index: 1; + position: relative; + top: 25px; + margin-right: 20px; + background-color: #FFFFFF; + border: 1px solid #007CBA; +} + +.button-delete-stage { + z-index: 1; + position: absolute; + top: -60px; + margin-right: 20px; + color: red; + background-color: #FFFFFF; + border: 1px solid red; +} + +.button-delete { + display: flex; + justify-content: flex-end; + margin-top: -15px; +} + +.button-insert-before { + z-index: 1; + position: absolute; + top: -60px; + margin-right: 360px; + color: #007CBA; + background-color: #FFFFFF; + border: 1px solid #007CBA; +} + +.button-insert-after { + z-index: 1; + position: absolute; + top: -60px; + margin-right: 180px; + color: #007CBA; + background-color: #FFFFFF; + border: 1px solid #007CBA; +} + +fieldset.border { + border: solid 1px #A0A0A3 !important; + padding: 0 10px 10px 10px; + border-bottom: none; + border-radius: 3px; + margin-bottom: 30px; + min-width: 0; /* override the default value of min-content */ + /* + -webkit-box-shadow: 2px 3px 3px 1px rgba(110,104,110,0.64); + -moz-box-shadow: 2px 3px 5px 1px rgba(110,104,110,0.64); + box-shadow: 2px 2px 3px 1px rgba(110,104,110,0.64); */ +} + +legend.border { + width: auto !important; + border: none; + border-bottom: none; + font-size: 15px; + /* FDA COLOR BLUE CODE Hex: #007CBA */ + color: #007CBA; + font-family: Verdana; + font-weight: bold; + margin-bottom: 10px; +} diff --git a/src/app/core/substance-ssg2/ssg2-manufacturing/ssg2-manufacturing.component.spec.ts b/src/app/core/substance-ssg2/ssg2-manufacturing/ssg2-manufacturing.component.spec.ts new file mode 100644 index 000000000..01b5b7854 --- /dev/null +++ b/src/app/core/substance-ssg2/ssg2-manufacturing/ssg2-manufacturing.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Ssg2ManufacturingComponent } from './ssg2-manufacturing.component'; + +describe('Ssg2ManufacturingComponent', () => { + let component: Ssg2ManufacturingComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ Ssg2ManufacturingComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(Ssg2ManufacturingComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-ssg2/ssg2-manufacturing/ssg2-manufacturing.component.ts b/src/app/core/substance-ssg2/ssg2-manufacturing/ssg2-manufacturing.component.ts new file mode 100644 index 000000000..1e40aa70f --- /dev/null +++ b/src/app/core/substance-ssg2/ssg2-manufacturing/ssg2-manufacturing.component.ts @@ -0,0 +1,96 @@ +import { AfterViewInit, Component, OnDestroy, OnInit } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { SubstanceFormService } from '@gsrs-core/substance-form/substance-form.service'; +import { ScrollToService } from '@gsrs-core/scroll-to/scroll-to.service'; +import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; +import { ControlledVocabularyService, VocabularyTerm } from '@gsrs-core/controlled-vocabulary'; +import { SubstanceFormBase } from '../../substance-form/base-classes/substance-form-base'; +import { SubstanceDetail, SpecifiedSubstanceG2Manufacturing } from '@gsrs-core/substance/substance.model'; +import { SubstanceFormSsg2ManufacturingService } from './substance-form-ssg2-manufacturing.service'; + +@Component({ + selector: 'app-ssg2-manufacturing', + templateUrl: './ssg2-manufacturing.component.html', + styleUrls: ['./ssg2-manufacturing.component.scss'] +}) +export class Ssg2ManufacturingComponent extends SubstanceFormBase implements OnInit, AfterViewInit, OnDestroy { + + private substance: SubstanceDetail; + manufacturing: Array; + private subscriptions: Array = []; + constructor( + private substanceFormService: SubstanceFormService, + private substanceFormSsg2ManufacturingService: SubstanceFormSsg2ManufacturingService, + public gaService: GoogleAnalyticsService, + public cvService: ControlledVocabularyService + ) { + super(); + this.analyticsEventCategory = 'substance form ssg 2 Manufacturing'; + } + + ngOnInit() { + this.canAddItemUpdate.emit(true); + this.menuLabelUpdate.emit('Manufacturing'); + const substanceSubscription = this.substanceFormService.substance.subscribe(substance => { + this.substance = substance; + if (!substance.specifiedSubstanceG2.manufacturing) { + substance.specifiedSubstanceG2.manufacturing = []; + } + this.substanceFormService.resetState(); + this.manufacturing = substance.specifiedSubstanceG2.manufacturing; + }); + this.subscriptions.push(substanceSubscription); + } + + ngAfterViewInit() { + } + + ngOnDestroy() { + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + + /* + addItem(): void { + this.substanceFormSsg4mProcessService.addProcess(); + setTimeout(() => { + this.scrollToService.scrollToElement(`substance-process-0`, 'center'); + }); + } + */ + + addItem(): void { + this.substanceFormSsg2ManufacturingService.addManufacturing(); + /* + const newManufacturing: SpecifiedSubstanceG2Manufacturing = {}; + this.substance.specifiedSubstanceG2.manufacturing.unshift(newManufacturing); + // const processIndex = this.substance.specifiedSubstanceG4m.process.push(newProcess); + this.propertyEmitter.next(this.substance.specifiedSubstanceG2.manufacturing); + // Add Site + // this.substanceFormSsg4mSitesService.addSite(processIndex-1); + */ + } + + confirmDeleteManufacturing(manufactureIndex: number): void { + this.substanceFormSsg2ManufacturingService.deleteManufacturing(manufactureIndex); + } + + searchOrganization(): void { + + } + + /* + updateGradeName(name: string): void { + this.grade.name = name; + } + + updateGradeType(type: string): void { + this.grade.type = type; + } + + updateAccess(access: Array): void { + this.grade.access = access; + } + */ +} \ No newline at end of file diff --git a/src/app/core/substance-ssg2/ssg2-manufacturing/ssg2-manufacturing.module.ts b/src/app/core/substance-ssg2/ssg2-manufacturing/ssg2-manufacturing.module.ts new file mode 100644 index 000000000..c4faab479 --- /dev/null +++ b/src/app/core/substance-ssg2/ssg2-manufacturing/ssg2-manufacturing.module.ts @@ -0,0 +1,108 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { CommonModule, formatDate } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; +import { MatInputModule } from '@angular/material/input'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatButtonModule } from '@angular/material/button'; +import { MatButtonToggleModule } from '@angular/material/button-toggle'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatTableModule } from '@angular/material/table'; +import { MatListModule } from '@angular/material/list'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatBadgeModule } from '@angular/material/badge'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatProgressBarModule} from '@angular/material/progress-bar'; +import { NgxJsonViewerModule} from 'ngx-json-viewer'; +// GSRS Imports +import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; +import { FileSelectModule } from 'file-select'; +import { ScrollToModule } from '@gsrs-core/scroll-to/scroll-to.module'; +import { ExpandDetailsModule } from '@gsrs-core/expand-details/expand-details.module'; +import { SubstanceFormModule } from '../../substance-form/substance-form.module'; +import { SubstanceSelectorModule } from '@gsrs-core/substance-selector/substance-selector.module'; +import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; +import { Ssg2ManufacturingComponent } from './ssg2-manufacturing.component'; + + +import { CvInputComponent} from '@gsrs-core/substance-form/cv-input/cv-input.component'; +import { CvDialogComponent} from '@gsrs-core/substance-form/cv-dialog/cv-dialog.component'; +import { JsonDialogComponent} from '@gsrs-core/substance-form/json-dialog/json-dialog.component'; +import { AuditInfoComponent} from '@gsrs-core/substance-form/audit-info/audit-info.component'; + +// import { SubmitSuccessDialogComponent } from './submit-success-dialog/submit-success-dialog.component'; +// import { MergeConceptDialogComponent} from '@gsrs-core/substance-form/merge-concept-dialog/merge-concept-dialog.component'; +// import { SubstanceFormComponent } from './substance-form.component'; +// import { CanActivateSubstanceForm } from './can-activate-substance-form'; +// import { CanRegisterSubstanceForm } from './can-register-substance-form'; +// import { SubstanceFormService } from '../substance-form.service'; +// import { AccessManagerComponent } from './access-manager/access-manager.component'; +// import { SubstanceSsg4mService } from './substance-ssg4m-form.service'; +// import { SubstanceFormComponent } from '../substance-form/substance-form.component'; +// import { SubstanceFormSsg4mSitesService } from './ssg4m-sites/substance-form-ssg4m-sites.service.'; + +@NgModule({ + imports: [ + DynamicComponentLoaderModule.forChild(Ssg2ManufacturingComponent), + CommonModule, + ReactiveFormsModule, + FormsModule, + MatFormFieldModule, + MatMenuModule, + MatCheckboxModule, + MatButtonModule, + MatIconModule, + MatTooltipModule, + MatTabsModule, + MatDividerModule, + MatSelectModule, + MatInputModule, + MatChipsModule, + MatAutocompleteModule, + ScrollToModule, + MatDialogModule, + MatTableModule, + MatExpansionModule, + MatBadgeModule, + MatRadioModule, + ExpandDetailsModule, + SubstanceSelectorModule, + MatListModule, + FileSelectModule, + MatButtonToggleModule, + NgxJsonViewerModule, + RouterModule, + SubstanceImageModule, + MatProgressBarModule, + MatProgressSpinnerModule, + SubstanceFormModule + ], + declarations: [ + Ssg2ManufacturingComponent + ], + exports: [ + Ssg2ManufacturingComponent + ], + entryComponents: [ + ] +}) + +export class Ssg2ManufacturingModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: Ssg2ManufacturingModule, + providers: [ + ] + }; + } +} diff --git a/src/app/core/substance-ssg2/ssg2-manufacturing/substance-form-ssg2-manufacturing.service.spec.ts b/src/app/core/substance-ssg2/ssg2-manufacturing/substance-form-ssg2-manufacturing.service.spec.ts new file mode 100644 index 000000000..c84680b1f --- /dev/null +++ b/src/app/core/substance-ssg2/ssg2-manufacturing/substance-form-ssg2-manufacturing.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { SubstanceFormSsg2ManufacturingService } from './substance-form-ssg2-manufacturing.service'; + +describe('SubstanceFormSsg2ManufacturingService', () => { + let service: SubstanceFormSsg2ManufacturingService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(SubstanceFormSsg2ManufacturingService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-ssg2/ssg2-manufacturing/substance-form-ssg2-manufacturing.service.ts b/src/app/core/substance-ssg2/ssg2-manufacturing/substance-form-ssg2-manufacturing.service.ts new file mode 100644 index 000000000..a9356f80b --- /dev/null +++ b/src/app/core/substance-ssg2/ssg2-manufacturing/substance-form-ssg2-manufacturing.service.ts @@ -0,0 +1,55 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { SubstanceFormServiceBase } from '../../substance-form/base-classes/substance-form-service-base'; +import { SubstanceFormService } from '../../substance-form/substance-form.service'; +import { SpecifiedSubstanceG2Manufacturing } from '@gsrs-core/substance/substance.model'; + +@Injectable({ + providedIn: 'root' +}) + +@Injectable() +export class SubstanceFormSsg2ManufacturingService extends SubstanceFormServiceBase> { + + constructor( + public substanceFormService: SubstanceFormService + //private substanceFormSsg4mSitesService: SubstanceFormSsg4mSitesService, + ) { + super(substanceFormService); + } + + initSubtanceForm(): void { + super.initSubtanceForm(); + const subscription = this.substanceFormService.substance.subscribe(substance => { + this.substance = substance; + if (!this.substance.specifiedSubstanceG2) { + this.substance.specifiedSubstanceG2 = {}; + } + if (!this.substance.specifiedSubstanceG2.manufacturing) { + this.substance.specifiedSubstanceG2.manufacturing = []; + } + this.substanceFormService.resetState(); + this.propertyEmitter.next(this.substance.specifiedSubstanceG2.manufacturing); + }); + this.subscriptions.push(subscription); + } + + get specifiedSubstanceG2Manufacturing(): Observable> { + return this.propertyEmitter.asObservable(); + } + + addManufacturing(): void { + const newManufacturing: SpecifiedSubstanceG2Manufacturing = {}; + this.substance.specifiedSubstanceG2.manufacturing.unshift(newManufacturing); + this.propertyEmitter.next(this.substance.specifiedSubstanceG2.manufacturing); + } + + deleteManufacturing(manufactureIndex: number): void { + // const processIndex = this.substance.specifiedSubstanceG4m.process.findIndex(pro => pro.$$deletedCode === pro.$$deletedCode); + // if (processIndex > -1) { + this.substance.specifiedSubstanceG2.manufacturing.splice(manufactureIndex, 1); + this.propertyEmitter.next(this.substance.specifiedSubstanceG2.manufacturing); + // } + } + +} diff --git a/src/app/core/substance-ssg2/ssg2-overview-form/ssg2-overview-form.component.html b/src/app/core/substance-ssg2/ssg2-overview-form/ssg2-overview-form.component.html new file mode 100644 index 000000000..2a2ca923a --- /dev/null +++ b/src/app/core/substance-ssg2/ssg2-overview-form/ssg2-overview-form.component.html @@ -0,0 +1,16 @@ +
    + + + + + + + + + + + +
    \ No newline at end of file diff --git a/src/app/core/substance-ssg2/ssg2-overview-form/ssg2-overview-form.component.scss b/src/app/core/substance-ssg2/ssg2-overview-form/ssg2-overview-form.component.scss new file mode 100644 index 000000000..f40aab64e --- /dev/null +++ b/src/app/core/substance-ssg2/ssg2-overview-form/ssg2-overview-form.component.scss @@ -0,0 +1,182 @@ +.form-container { + padding: 5px 10px 12px 10px; + position: relative; +} + +.notification-backdrop { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: flex; + z-index: 10; + background-color: rgba(255, 255, 255, .8); + justify-content: center; + align-items: center; + font-size: 30px; + font-weight: bold; + color: #666; +} + +.form-row { + display: flex; + width: 100%; + + .col-4-1 { + width: calc((100% - 20px * 3) / 4); //four column + margin-right: 20px; + } + + .col-3-1 { + width: calc((100% - 20px * 2) / 3); //three column + margin-right: 20px; + } + + .col-1-1 { + width: calc((100% - 20px * 1)); //one column + margin-right: 10px; + } + + .delete-container { + padding: 0 10px 8px 0; + } +} +/* +.form-row { + display: flex; + justify-content: space-between; + align-items: flex-end; + + .delete-container { + padding: 0 10px 8px 0; + } + + .row { + flex-grow: 1; + padding-right: 15px; + } +} +*/ + +.references-container { + width: 100%; +} + +.materialborder { + padding-top: 10px; + margin-top: 15px; + border: 2px solid rgb(182, 175, 175); +} + +.materialtitle { + font-size: 18px; + padding-left: 10px; +} + +.divflex { + display: flex; +} + +.divflexright { + display: flex; + justify-content: flex-end; +} + +.bordergray { + border: 1px solid gray; +} + +.borderorange { + border: 1px solid orangered; +} + +.fontsize18px { + font-size: 18px; +} + +.colorgreen { + color: green; +} + +.margintop10px { + margin-top: 10px; +} + +.marginleft40px { + margin-left: 40px; +} + +.marginbottom20px { + margin-top: 20px; +} + +.button-add { + z-index: 1; + position: relative; + top: 25px; + margin-right: 20px; + background-color: #FFFFFF; + border: 1px solid #007CBA; +} + +.button-delete-stage { + z-index: 1; + position: absolute; + top: -60px; + margin-right: 20px; + color: red; + background-color: #FFFFFF; + border: 1px solid red; +} + +.button-delete { + display: flex; + justify-content: flex-end; + margin-top: -15px; +} + +.button-insert-before { + z-index: 1; + position: absolute; + top: -60px; + margin-right: 360px; + color: #007CBA; + background-color: #FFFFFF; + border: 1px solid #007CBA; +} + +.button-insert-after { + z-index: 1; + position: absolute; + top: -60px; + margin-right: 180px; + color: #007CBA; + background-color: #FFFFFF; + border: 1px solid #007CBA; +} + +fieldset.border { + border: solid 2px #A0A0A3 !important; + padding: 0 10px 10px 10px; + border-bottom: none; + border-radius: 3px; + margin-bottom: 30px; + min-width: 0; /* override the default value of min-content */ + /* + -webkit-box-shadow: 2px 3px 3px 1px rgba(110,104,110,0.64); + -moz-box-shadow: 2px 3px 5px 1px rgba(110,104,110,0.64); + box-shadow: 2px 2px 3px 1px rgba(110,104,110,0.64); */ +} + +legend.border { + width: auto !important; + border: none; + border-bottom: none; + font-size: 15px; + /* FDA COLOR BLUE CODE Hex: #007CBA */ + color: #007CBA; + font-family: Verdana; + font-weight: bold; + margin-bottom: 10px; +} diff --git a/src/app/core/substance-ssg2/ssg2-overview-form/ssg2-overview-form.component.spec.ts b/src/app/core/substance-ssg2/ssg2-overview-form/ssg2-overview-form.component.spec.ts new file mode 100644 index 000000000..617f4f10f --- /dev/null +++ b/src/app/core/substance-ssg2/ssg2-overview-form/ssg2-overview-form.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Ssg2OverviewFormComponent } from './ssg2-overview-form.component'; + +describe('Ssg2OverviewFormComponent', () => { + let component: Ssg2OverviewFormComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ Ssg2OverviewFormComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(Ssg2OverviewFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-ssg2/ssg2-overview-form/ssg2-overview-form.component.ts b/src/app/core/substance-ssg2/ssg2-overview-form/ssg2-overview-form.component.ts new file mode 100644 index 000000000..e40f4c2cb --- /dev/null +++ b/src/app/core/substance-ssg2/ssg2-overview-form/ssg2-overview-form.component.ts @@ -0,0 +1,52 @@ +import { AfterViewInit, Component, OnDestroy, OnInit } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { SubstanceFormService } from '@gsrs-core/substance-form/substance-form.service'; +import { ScrollToService } from '@gsrs-core/scroll-to/scroll-to.service'; +import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; +import { ControlledVocabularyService, VocabularyTerm } from '@gsrs-core/controlled-vocabulary'; +import { SubstanceFormBase } from '../../substance-form/base-classes/substance-form-base'; +import { SubstanceDetail, SpecifiedSubstanceG2 } from '@gsrs-core/substance/substance.model'; + +@Component({ + selector: 'app-ssg2-overview-form', + templateUrl: './ssg2-overview-form.component.html', + styleUrls: ['./ssg2-overview-form.component.scss'] +}) +export class Ssg2OverviewFormComponent extends SubstanceFormBase implements OnInit, AfterViewInit, OnDestroy { + + substance: SubstanceDetail; + specifiedSubstanceG2: SpecifiedSubstanceG2; + private subscriptions: Array = []; + constructor( + private substanceFormService: SubstanceFormService, + public gaService: GoogleAnalyticsService, + public cvService: ControlledVocabularyService + ) { + super(); + this.analyticsEventCategory = 'substance form ssg 2 Manufacturing'; + } + + ngOnInit() { + this.menuLabelUpdate.emit('Overview'); + const substanceSubscription = this.substanceFormService.substance.subscribe(substance => { + this.substance = substance; + + if (!substance.specifiedSubstanceG2) { + substance.specifiedSubstanceG2 = {}; + } + this.substanceFormService.resetState(); + this.specifiedSubstanceG2 = substance.specifiedSubstanceG2; + }); + this.subscriptions.push(substanceSubscription); + } + + ngAfterViewInit() { + } + + ngOnDestroy() { + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + +} \ No newline at end of file diff --git a/src/app/core/substance-ssg2/ssg2-overview-form/ssg2-overview-form.module.ts b/src/app/core/substance-ssg2/ssg2-overview-form/ssg2-overview-form.module.ts new file mode 100644 index 000000000..aaa493831 --- /dev/null +++ b/src/app/core/substance-ssg2/ssg2-overview-form/ssg2-overview-form.module.ts @@ -0,0 +1,107 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { CommonModule, formatDate } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; +import { MatInputModule } from '@angular/material/input'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatButtonModule } from '@angular/material/button'; +import { MatButtonToggleModule } from '@angular/material/button-toggle'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatTableModule } from '@angular/material/table'; +import { MatListModule } from '@angular/material/list'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatBadgeModule } from '@angular/material/badge'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatProgressBarModule} from '@angular/material/progress-bar'; +import { NgxJsonViewerModule} from 'ngx-json-viewer'; +// GSRS Imports +import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; +import { FileSelectModule } from 'file-select'; +import { ScrollToModule } from '@gsrs-core/scroll-to/scroll-to.module'; +import { ExpandDetailsModule } from '@gsrs-core/expand-details/expand-details.module'; +import { SubstanceFormModule } from '../../substance-form/substance-form.module'; +import { SubstanceSelectorModule } from '@gsrs-core/substance-selector/substance-selector.module'; +import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; +import { Ssg2OverviewFormComponent } from './ssg2-overview-form.component'; + +import { CvInputComponent} from '@gsrs-core/substance-form/cv-input/cv-input.component'; +import { CvDialogComponent} from '@gsrs-core/substance-form/cv-dialog/cv-dialog.component'; +import { JsonDialogComponent} from '@gsrs-core/substance-form/json-dialog/json-dialog.component'; +import { AuditInfoComponent} from '@gsrs-core/substance-form/audit-info/audit-info.component'; + +// import { SubmitSuccessDialogComponent } from './submit-success-dialog/submit-success-dialog.component'; +// import { MergeConceptDialogComponent} from '@gsrs-core/substance-form/merge-concept-dialog/merge-concept-dialog.component'; +// import { SubstanceFormComponent } from './substance-form.component'; +// import { CanActivateSubstanceForm } from './can-activate-substance-form'; +// import { CanRegisterSubstanceForm } from './can-register-substance-form'; +// import { SubstanceFormService } from '../substance-form.service'; +// import { AccessManagerComponent } from './access-manager/access-manager.component'; +// import { SubstanceSsg4mService } from './substance-ssg4m-form.service'; +// import { SubstanceFormComponent } from '../substance-form/substance-form.component'; +// import { SubstanceFormSsg4mSitesService } from './ssg4m-sites/substance-form-ssg4m-sites.service.'; + +@NgModule({ + imports: [ + DynamicComponentLoaderModule.forChild(Ssg2OverviewFormComponent), + CommonModule, + ReactiveFormsModule, + FormsModule, + MatFormFieldModule, + MatMenuModule, + MatCheckboxModule, + MatButtonModule, + MatIconModule, + MatTooltipModule, + MatTabsModule, + MatDividerModule, + MatSelectModule, + MatInputModule, + MatChipsModule, + MatAutocompleteModule, + ScrollToModule, + MatDialogModule, + MatTableModule, + MatExpansionModule, + MatBadgeModule, + MatRadioModule, + ExpandDetailsModule, + SubstanceSelectorModule, + MatListModule, + FileSelectModule, + MatButtonToggleModule, + NgxJsonViewerModule, + RouterModule, + SubstanceImageModule, + MatProgressBarModule, + MatProgressSpinnerModule, + SubstanceFormModule + ], + declarations: [ + Ssg2OverviewFormComponent + ], + exports: [ + Ssg2OverviewFormComponent + ], + entryComponents: [ + ] +}) + +export class Ssg2OverviewFormModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: Ssg2OverviewFormModule, + providers: [ + ] + }; + } +} diff --git a/src/app/core/substance-ssg2/substance-ssg2-form.component.html b/src/app/core/substance-ssg2/substance-ssg2-form.component.html new file mode 100644 index 000000000..b638a24c4 --- /dev/null +++ b/src/app/core/substance-ssg2/substance-ssg2-form.component.html @@ -0,0 +1,200 @@ + +
    +
    + + + + + + + + + + +
    +
    +
    + {{submissionMessage}} +
    +
    + +
    + Please correct or dismiss the following errors and submit again: +
    +
    + +
    + {{message.messageType}}
    +
    {{message.message}}
    + {{link.text}}
    +
    +
    + + + +
    +
    +
    + + + + + +
    +
    +
    +
    +
    +
    + {{(uuid && !imported) ? 'Editing ' : 'Registering New '}} - + Specified Substance Group 2 + +
    + +
    +
    + + + + +

    {{section.menuLabel}}

    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/src/app/core/substance-ssg2/substance-ssg2-form.component.scss b/src/app/core/substance-ssg2/substance-ssg2-form.component.scss new file mode 100644 index 000000000..902fc4561 --- /dev/null +++ b/src/app/core/substance-ssg2/substance-ssg2-form.component.scss @@ -0,0 +1,277 @@ +#overlay { + position: fixed; /* Sit on top of the page content */ + display: block; /* Hidden by default */ + width: 100%; /* Full width (cover the whole page) */ + height: 100%; /* Full height (cover the whole page) */ + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0,0,0,0.5); /* Black background with opacity */ + z-index: 2; /* Specify a stack order in case you're using a different order for other elements */ + cursor: pointer; /* Add a pointer on hover */ +} +.hide { + display: none!important; +} +.center-screen { + z-index: 2; + position: fixed; + background-color: white; + width: 50%; + height: 50%; + top: 50%; + left: 50%; + margin-right: -50%; + transform: translate(-50%, -50%); +} +.center-screen img { + transition-duration: 4s; + margin: 0 auto; + display: block; +} +.center-screen img:hover { + transform: scale(1.7); + -webkit-transform: scale(1.7); + -moz-transform: scale(1.7); + z-index: 0; +} +.image-popped-up { + width: 100%; + height: 100%; + padding: 10px; +} +.close-out { + padding: 10px; + float: right; + font-weight: 500; + cursor: pointer; +} +.top-fixed { + position: fixed; + display: flex; + flex-direction: column; + top: 64px; + width: 100%; + background-color: white; + align-items: center; + justify-content: center; + box-shadow: 0px 3px 3px -2px rgba(0, 0, 0, 0.2), 0px 3px 4px 0px rgba(0, 0, 0, 0.14), 0px 1px 8px 0px rgba(0, 0, 0, 0.12); + z-index: 1001; +} + +.cdk-overlay-pane { + z-index: 1005; +} +mat-select-panel{ + z-index: 1005; +} + +.admin-functions, .changeClass { + margin-bottom: -15px; + margin-left: 30px; + margin-right: 5px; + margin-top: -10px; + color: #4793d1; + + + ::ng-deep .mat-form-field-wrapper { + padding-bottom: 20px !important; + } +} + +::ng-deep .mat-form-field-wrapper { + padding-bottom: 5px; +} +.advanced-features { + z-index: 2000 !important; + + display:flex; + flex-direction: row; + padding-left: 10px; +} +::ng-deep .cdk-overlay-pane { + margin-top: 20px !important; +} + +.form-content-container { + // position: fixed; + // top: 121px; + // right: 0; + // bottom: 0; + // left: 0; + overflow: hidden; + padding-top: 121px; +} + +.actions-container { + max-width: 1128px; + width: 100%; + background-color: white; + padding: 10px; + display: flex; +} + +.mat-accordion { + width: 100%; + max-width: 1020px; + box-sizing: border-box; +} + +.hidden { + display: none !important; +} + +.validation-body { + max-width:95%; + display:flex; + max-width: 960px; +word-break: break-word; +} + +.validation-dismiss { + width: 5%; +} + +.submission-messages { + overflow: hidden; + height: auto; + -webkit-transition: all 500ms ease-out; + transition: all 500ms ease-out; + max-width: 1028px; + width: 100%; + background-color: white; + display: flex; + flex-direction: column; + + &.collapsed { + max-height: 0; + } + + &.expanded { + max-height: 500px; + overflow-y: auto; + padding: 10px; + } + + .submission-message { + font-weight: 500; + text-align: center; + } + + .validation-message { + display: flex; + padding: 5px 0; + + + .message-type { + text-transform: uppercase; + font-weight: 500; + margin-right: 20px; + padding:10px; + border-radius:3px; + height: 40px; + min-width: 95px; + } + } + + .dismiss-container { + display: flex; + } + + .warning-message { + color: #8a6d3b; + background-color: #fcf8e3; + + } + + + .error-message { + color: #a94442; + background-color: #f2dede; + } + +} + +.validate-button{ + margin-left:15px; +} + +.mat-expansion-panel-header-title { + align-items: center; +} + +.hide-show-messages { + margin-left: 10px; + border: 1px solid; +} + +.internal-link { + color: #448aff; +} + +.import-button { + margin-left: 15px; +} + +.json-button { + margin-left: 30px; +} + +.draft-button { + margin-left: 15px; +} + +.chip { + background-color: white; + border-radius: 50%; + padding: 3px 5px; + margin-left: 5px; + color: #1565c0; + height: 33px; +} + +.form-header { + padding-top: 20px; + padding-left: 210px; + font-weight: 500; + font-size: 28px; +} + +.fontsize20px { +font-size: 20px; +} + +.padtop90px { +padding-top: 90px; +} + +.padleft25px { +padding-left: 25px; +} + +.bordergray { +border: 1px solid green; +} + +.borderblue { +border: 1px solid #007CBA; +} + +.fontbold { +font-weight: bold; +} + +/* FDA COLOR BLUE CODE Hex: #007CBA */ +.colorbluefda { +color: #007CBA; +} + +::ng-deep .custom-menu{ +margin-top: -12px; +/*z-index: 4000;*/ +} + +.mat-menu-item { +color: #007CBA !important; +} diff --git a/src/app/core/substance-ssg2/substance-ssg2-form.component.ts b/src/app/core/substance-ssg2/substance-ssg2-form.component.ts new file mode 100644 index 000000000..a53f17e7b --- /dev/null +++ b/src/app/core/substance-ssg2/substance-ssg2-form.component.ts @@ -0,0 +1,1195 @@ +import { + Component, + OnInit, + AfterViewInit, + ViewChildren, + ViewContainerRef, + QueryList, + OnDestroy, HostListener +} from '@angular/core'; +import { formSections } from '../substance-form/form-sections.constant'; +import { ActivatedRoute, Router, RouterEvent, NavigationStart, NavigationEnd } from '@angular/router'; +import { SubstanceService } from '../substance/substance.service'; +import { LoadingService } from '../loading/loading.service'; +import { MainNotificationService } from '../main-notification/main-notification.service'; +import { AppNotification, NotificationType } from '../main-notification/notification.model'; +import { DynamicComponentLoader } from '../dynamic-component-loader/dynamic-component-loader.service'; +import { GoogleAnalyticsService } from '../google-analytics/google-analytics.service'; +import { SubstanceFormSection } from '../substance-form/substance-form-section'; +import { SubstanceFormService } from '../substance-form/substance-form.service'; +import { ValidationMessage, SubstanceFormResults, SubstanceFormDefinition } from '../substance-form/substance-form.model'; +import { Subscription, Observable } from 'rxjs'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { MatDialog } from '@angular/material/dialog'; +import { JsonDialogComponent } from '@gsrs-core/substance-form/json-dialog/json-dialog.component'; +import * as _ from 'lodash'; +import * as defiant from '../../../../node_modules/defiant.js/dist/defiant.min.js'; +import { Title } from '@angular/platform-browser'; +import { AuthService } from '@gsrs-core/auth'; +import { take, map } from 'rxjs/operators'; +import { MatExpansionPanel } from '@angular/material/expansion'; +import { SubmitSuccessDialogComponent } from '../substance-form/submit-success-dialog/submit-success-dialog.component'; +import { MergeConceptDialogComponent } from '@gsrs-core/substance-form/merge-concept-dialog/merge-concept-dialog.component'; +import { DefinitionSwitchDialogComponent } from '@gsrs-core/substance-form/definition-switch-dialog/definition-switch-dialog.component'; +import { SubstanceEditImportDialogComponent } from '@gsrs-core/substance-edit-import-dialog/substance-edit-import-dialog.component'; +import { StructuralUnit } from '@gsrs-core/substance'; +import { ConfigService } from '@gsrs-core/config'; +import { FragmentWizardComponent } from '@gsrs-core/admin/fragment-wizard/fragment-wizard.component'; +import { SubstanceDraftsComponent } from '@gsrs-core/substance-form/substance-drafts/substance-drafts.component'; +import { UtilsService } from '@gsrs-core/utils'; +import { SubstanceSsg2FormService } from './substance-ssg2-form.service'; + +@Component({ + selector: 'app-substance-ssg2-form', + templateUrl: './substance-ssg2-form.component.html', + styleUrls: ['./substance-ssg2-form.component.scss'] +}) +export class SubstanceSsg2FormComponent implements OnInit, AfterViewInit, OnDestroy { + isLoading = true; + id?: string; + formSections: Array = []; + @ViewChildren('dynamicComponent', { read: ViewContainerRef }) dynamicComponents: QueryList; + @ViewChildren('expansionPanel', { read: MatExpansionPanel }) matExpansionPanels: QueryList; + private subClass: string; + definitionType: string; + expandedComponents = [ + 'substance-form-definition', + 'substance-form-structure', + 'substance-form-moieties', + 'substance-form-references' + ]; + showSubmissionMessages = false; + submissionMessage: string; + validationMessages: Array; + validationResult = false; + private subscriptions: Array = []; + copy: string; + private overlayContainer: HTMLElement; + serverError: boolean; + canApprove: boolean; + approving: boolean; + definition: SubstanceFormDefinition; + user: string; + feature: string; + isAdmin: boolean; + isUpdater: boolean; + messageField: string; + uuid: string; + substanceClass: string; + drafts: Array; + draftCount = 0; + status: string; + hidePopup: boolean; + unit: StructuralUnit; + autoSaveWait = 60000; + classes = [ + 'concept', + 'protein', + 'chemical', + 'structurallyDiverse', + 'polymer', + 'nucleicAcid', + 'mixture', + 'specifiedSubstanceG1', + 'specifiedSubstanceG2', + 'specifiedSubstanceG3']; + imported = false; + forceChange = false; + sameSubstance = false; + UNII: string; + approvalType = 'lastEditedBy'; + previousState: number; + + constructor( + private activatedRoute: ActivatedRoute, + private substanceService: SubstanceService, + private loadingService: LoadingService, + private mainNotificationService: MainNotificationService, + private router: Router, + private dynamicComponentLoader: DynamicComponentLoader, + private gaService: GoogleAnalyticsService, + private substanceFormService: SubstanceFormService, + private overlayContainerService: OverlayContainer, + private configService: ConfigService, + private dialog: MatDialog, + private authService: AuthService, + private titleService: Title, + private utilsService: UtilsService, + private substanceSsg2FormService: SubstanceSsg2FormService + ) { + this.substanceService.showImagePopup.subscribe(data => { + this.hidePopup = data; + }) + this.substanceService.imagePopupUnit.subscribe(data => { + this.unit = data; + }) + } + + showHidePopup(): void { + this.hidePopup = !this.hidePopup; + this.substanceService.showImagePopup.next(this.hidePopup); + } + + + autoSave(): void { + setTimeout(() => { + if (this.substanceFormService.autoSave()) { + this.saveDraft(true); + } else { + } + this.autoSave(); + }, this.autoSaveWait); + } + + openModal(templateRef) { + + const dialogRef = this.dialog.open(templateRef, { + height: '200px', + width: '400px' + }); + this.overlayContainer.style.zIndex = '1002'; + + dialogRef.afterClosed().subscribe(result => { + this.overlayContainer.style.zIndex = null; + }); + } + + showDrafts(): void { + const dialogRef = this.dialog.open(SubstanceDraftsComponent, { + maxHeight: '85%', + width: '70%', + data: { uuid: this.id } + }); + this.overlayContainer.style.zIndex = '1002'; + + dialogRef.afterClosed().subscribe(response => { + this.overlayContainer.style.zIndex = null; + + + if (response) { + this.loadingService.setLoading(true); + // console.log(response.json); + + const read = response.substance; + if (this.id && read.uuid && this.id === read.uuid) { + this.substanceFormService.importSubstance(read, 'update'); + this.submissionMessage = null; + this.validationMessages = []; + this.showSubmissionMessages = false; + setTimeout(() => { + this.loadingService.setLoading(false); + this.isLoading = false; + this.overlayContainer.style.zIndex = null; + }, 1000); + } else if (response.uuid && response.uuid != 'register') { + const url = '/substances/' + response.uuid + '/edit?action=import&source=draft'; + this.router.navigateByUrl(url, { state: { record: response.substance } }); + } else { + setTimeout(() => { + this.overlayContainer.style.zIndex = null; + this.router.onSameUrlNavigation = 'reload'; + this.loadingService.setLoading(false); + this.router.onSameUrlNavigation = 'reload'; + this.router.navigateByUrl('/substances/register/' + response.substance.substanceClass + '?action=import', { state: { record: response.substance } }); + + }, 1000); + } + } + + let keys = Object.keys(localStorage); + let i = keys.length; + this.draftCount = 0; + this.drafts = []; + + while (i--) { + if (keys[i].startsWith('gsrs-draft-')) { + const entry = JSON.parse(localStorage.getItem(keys[i])); + entry.key = keys[i]; + if (this.id && entry.uuid === this.id) { + this.draftCount++; + } else if (!this.id && entry.type === (this.activatedRoute.snapshot.params['type']) && entry.uuid === 'register') { + this.draftCount++; + } + this.drafts.push(entry); + + } + } + }); + } + + + importDialog(): void { + const dialogRef = this.dialog.open(SubstanceEditImportDialogComponent, { + width: '650px', + autoFocus: false + + }); + this.overlayContainer.style.zIndex = '1002'; + + const dialogSubscription = dialogRef.afterClosed().pipe(take(1)).subscribe(response => { + if (response) { + // this.overlayContainer.style.zIndex = null; + this.loadingService.setLoading(true); + + // attempting to reload a substance without a router refresh has proven to cause issues with the relationship dropdowns + // There are probably other components affected. There is an issue with subscriptions likely due to some OnInit not firing + + const read = JSON.parse(response); + if (this.id && read.uuid && this.id === read.uuid) { + this.substanceFormService.importSubstance(read, 'update'); + this.submissionMessage = null; + this.validationMessages = []; + this.showSubmissionMessages = false; + setTimeout(() => { + this.loadingService.setLoading(false); + this.isLoading = false; + this.overlayContainer.style.zIndex = null; + }, 1000); + /* } else { + if ( read.substanceClass === this.substanceClass) { + this.imported = true; + this.substanceFormService.importSubstance(read); + this.submissionMessage = null; + this.validationMessages = []; + this.showSubmissionMessages = false; + this.loadingService.setLoading(false); + this.isLoading = false;*/ + } else { + setTimeout(() => { + this.overlayContainer.style.zIndex = null; + this.router.onSameUrlNavigation = 'reload'; + this.loadingService.setLoading(false); + this.router.navigateByUrl('/substances/register?action=import', { state: { record: response } }); + + }, 1000); + } + } + // } + }); + + } + + test() { + this.router.navigated = false; + this.router.navigate([this.router.url]); + } + + ngOnInit() { + this.loadingService.setLoading(true); + if (this.configService.configData && this.configService.configData.approvalType) { + this.approvalType = this.configService.configData.approvalType; + } + if (this.configService.configData && this.configService.configData.autoSaveWait) { + this.autoSaveWait = this.configService.configData.autoSaveWait; + } + this.isAdmin = this.authService.hasRoles('admin'); + this.isUpdater = this.authService.hasAnyRoles('Updater', 'SuperUpdater'); + this.overlayContainer = this.overlayContainerService.getContainerElement(); + this.imported = false; + const routeSubscription = this.activatedRoute + .params + .subscribe(params => { + + const action = this.activatedRoute.snapshot.queryParams['action'] || null; + + if (params['id']) { + + if (action && action === 'import' && window.history.state) { + const record = window.history.state; + this.imported = true; + + this.getDetailsFromImport(record.record); + } else { + const id = params['id']; + if (id !== this.id) { + this.id = id; + this.gaService.sendPageView(`Substance Edit`); + const newType = this.activatedRoute.snapshot.queryParamMap.get('switch') || null; + if (newType) { + this.getSubstanceDetails(newType); + } else { + this.getSubstanceDetails(); + } + } + } + } else { + if (action && action === 'import' && window.history.state) { + const record = window.history.state; + this.imported = true; + this.getDetailsFromImport(record.record); + this.gaService.sendPageView(`Substance Register`); + + } else { + this.copy = this.activatedRoute.snapshot.queryParams['copy'] || null; + if (this.copy) { + const copyType = this.activatedRoute.snapshot.queryParams['copyType'] || null; + this.getPartialSubstanceDetails(this.copy, copyType); + this.gaService.sendPageView(`Substance Register`); + } else { // new record / register + setTimeout(() => { + this.gaService.sendPageView(`Substance Register`); + this.subClass = this.activatedRoute.snapshot.params['type'] || 'specifiedSubstanceG2'; + this.substanceClass = this.subClass; + this.titleService.setTitle('Register - ' + this.subClass); + this.substanceFormService.loadSubstance(this.subClass).pipe(take(1)).subscribe(() => { + this.setFormSections(formSections[this.subClass]); + this.loadingService.setLoading(false); + this.isLoading = false; + + }); + }); + } + } + + + } + }); + this.subscriptions.push(routeSubscription); + const routerSubscription = this.router.events.subscribe((event: RouterEvent) => { + if (event instanceof NavigationStart) { + this.substanceFormService.unloadSubstance(); + } + }); + this.subscriptions.push(routerSubscription); + this.approving = false; + const definitionSubscription = this.substanceFormService.definition.subscribe(response => { + this.definition = response; + setTimeout(() => { + this.canApprove = this.canBeApproved(); + }); + }); + this.subscriptions.push(definitionSubscription); + this.authService.getAuth().pipe(take(1)).subscribe(auth => { + this.user = auth.identifier; + setTimeout(() => { + this.canApprove = this.canBeApproved(); + + }); + }); + + } + + getDrafts() { + let keys = Object.keys(localStorage); + let i = keys.length; + this.drafts = []; + let temp = 0; + while (i--) { + if (keys[i].startsWith('gsrs-draft-')) { + const entry = JSON.parse(localStorage.getItem(keys[i])); + entry.key = keys[i]; + if (this.id && entry.uuid === this.id) { + temp++; + // this.draftCount++; + } else if (!this.id && entry.type === (this.activatedRoute.snapshot.params['type']) && entry.uuid === 'register') { + temp++; + // this.draftCount++; + } + this.drafts.push(entry); + + } + } + this.draftCount = temp; + } + + ngAfterViewInit(): void { + this.getDrafts(); + + const subscription = this.dynamicComponents.changes + .subscribe(() => { + + const total = this.formSections.length; + let finished = 0; + if (!this.forceChange) { + this.loadingService.setLoading(true); + const startTime = new Date(); + this.dynamicComponents.forEach((cRef, index) => { + this.dynamicComponentLoader + .getComponentFactory(this.formSections[index].dynamicComponentName) + .subscribe(componentFactory => { + this.loadingService.setLoading(true); + this.formSections[index].dynamicComponentRef = cRef.createComponent(componentFactory); + this.formSections[index].matExpansionPanel = this.matExpansionPanels.find((item, panelIndex) => index === panelIndex); + this.formSections[index].dynamicComponentRef.instance.menuLabelUpdate.pipe(take(1)).subscribe(label => { + this.formSections[index].menuLabel = label; + }); + const hiddenStateSubscription = + this.formSections[index].dynamicComponentRef.instance.hiddenStateUpdate.subscribe(isHidden => { + this.formSections[index].isHidden = isHidden; + }); + this.subscriptions.push(hiddenStateSubscription); + this.formSections[index].dynamicComponentRef.instance.canAddItemUpdate.pipe(take(1)).subscribe(isList => { + this.formSections[index].canAddItem = isList; + if (isList) { + const aieSubscription = this.formSections[index].addItemEmitter.subscribe(() => { + this.formSections[index].matExpansionPanel.open(); + this.formSections[index].dynamicComponentRef.instance.addItem(); + }); + this.formSections[index].dynamicComponentRef.instance.componentDestroyed.pipe(take(1)).subscribe(() => { + aieSubscription.unsubscribe(); + }); + } + }); + this.formSections[index].dynamicComponentRef.changeDetectorRef.detectChanges(); + finished++; + if (finished >= total) { + this.loadingService.setLoading(false); + } else { + const currentTime = new Date(); + if (currentTime.getTime() - startTime.getTime() > 12000) { + if (confirm('There was a network error while fetching files, would you like to refresh?')) { + window.location.reload(); + } + } + } + setTimeout(() => { + this.loadingService.setLoading(false); + this.UNII = this.substanceFormService.getUNII(); + }, 5); + }); + }); + // this.loadingService.setLoading(false); + + } + subscription.unsubscribe(); + setTimeout(() => { + + this.autoSave(); + }, 10000); + + }); + } + + openedChange(event: any) { + if (event) { + this.overlayContainer.style.zIndex = '1002'; + } else { + this.overlayContainer.style.zIndex = '1000'; + } + } + + useFeature(feature: any): void { + this.feature = feature.value; + if (this.feature === 'glyco') { + this.glyco(); + } else if (this.feature === 'disulfide') { + this.disulfide(); + } if (this.feature === 'concept') { + this.concept(); + } if (this.feature === 'unapprove') { + if (confirm('Are you sure you\'d like to remove the approvalID?')) { + this.substanceFormService.unapproveRecord(); + } + this.feature = undefined; + } + if (this.feature === 'setPrivate') { + this.substanceFormService.setDefinitionPrivate(); + this.feature = undefined; + } + if (this.feature === 'setPublic') { + this.substanceFormService.setDefinitionPublic(); + this.feature = undefined; + } + if (this.feature === 'approved') { + this.substanceFormService.changeStatus('approved'); + this.feature = undefined; + } + if (this.feature === 'pending') { + this.substanceFormService.changeStatus('pending'); + this.feature = undefined; + } + if (this.feature === 'merge') { + this.mergeConcept(); + this.feature = undefined; + } + if (this.feature === 'switch') { + this.definitionSwitch(); + this.feature = undefined; + } + if (this.feature === 'changeApproval') { + this.substanceFormService.changeApproval(); + } + if (this.feature === 'fragment') { + this.openFragmentDialog(); + } + + + + } + + openFragmentDialog(): void { + const dialogRef = this.dialog.open(FragmentWizardComponent, { + width: '70%', + height: '70%' + }); + this.overlayContainer.style.zIndex = '50'; + + const dialogSubscription = dialogRef.afterClosed().pipe(take(1)).subscribe(response => { + + }); + this.subscriptions.push(dialogSubscription); + } + + changeClass(type: any): void { + this.router.navigate(['/substances', this.id, 'edit'], { queryParams: { switch: type.value } }); + this.feature = undefined; + } + + changeStatus(status: any): void { + this.substanceFormService.changeStatus(status); + this.feature = undefined; + } + + concept(): void { + this.substanceFormService.conceptNonApproved(); + this.feature = undefined; + } + + glyco(): void { + this.substanceFormService.predictSites(); + this.feature = undefined; + } + + disulfide(): void { + this.substanceFormService.disulfideLinks(); + this.feature = undefined; + } + + ngOnDestroy(): void { + // this.substanceFormService.unloadSubstance(); + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + + canBeApproved(): boolean { + const action = this.activatedRoute.snapshot.queryParams['action'] || null; + if (action && action === 'import') { + return false; + } + // if config var set and set to 'createdBy then set approval button enabled if user is not creator + if (this.approvalType === 'createdBy') { + if (this.definition && this.definition.createdBy && this.user) { + const creator = this.definition.createdBy; + if (!creator) { + return false; + } + if (this.definition.status === 'approved') { + return false; + } + if (creator === this.user) { + return false; + } + return true; + + } + return false; + //default to 'lastEditedBy' if not set in config + } else { + if (this.definition && this.definition.lastEditedBy && this.user) { + const lastEdit = this.definition.lastEditedBy; + if (!lastEdit) { + return false; + } + if (this.definition.status === 'approved') { + return false; + + } + if (lastEdit === this.user) { + return false; + } + return true; + } + } + + } + + showJSON(): void { + const dialogRef = this.dialog.open(JsonDialogComponent, { + width: '90%' + }); + this.overlayContainer.style.zIndex = '1002'; + + const dialogSubscription = dialogRef.afterClosed().pipe(take(1)).subscribe(response => { + + }); + this.subscriptions.push(dialogSubscription); + } + + getSubstanceDetails(newType?: string): void { + this.substanceService.getSubstanceDetails(this.id).pipe(take(1)).subscribe(response => { + if (response._name) { + this.titleService.setTitle('Edit - ' + response._name); + } + if (response) { + this.definitionType = response.definitionType; + if (newType) { + response = this.substanceFormService.switchType(response, newType); + } + this.substanceClass = response.substanceClass; + this.status = response.status; + this.substanceFormService.loadSubstance(response.substanceClass, response).pipe(take(1)).subscribe(() => { + this.setFormSections(formSections[response.substanceClass]); + }); + } else { + this.handleSubstanceRetrivalError(); + } + this.loadingService.setLoading(false); + this.isLoading = false; + }, error => { + this.gaService.sendException('getSubstanceDetails: error from API call'); + this.loadingService.setLoading(false); + this.isLoading = false; + this.handleSubstanceRetrivalError(); + }); + } + + jsonValid(file: any): boolean { + try { + JSON.parse(file); + } catch (e) { + return false; + } + return true; + } + + getDetailsFromImport(state: any, same?: boolean) { + if (!this.jsonValid(state)) { + state = JSON.stringify(state); + } + if (state && this.jsonValid(state)) { + const response = JSON.parse(state); + same = false; + this.definitionType = response.definitionType; + this.substanceClass = response.substanceClass; + this.status = response.status; + this.substanceFormService.loadSubstance(response.substanceClass, response, 'import').pipe(take(1)).subscribe(() => { + this.setFormSections(formSections[response.substanceClass]); + if (!same) { + setTimeout(() => { + this.forceChange = true; + this.dynamicComponents.forEach((cRef, index) => { + this.dynamicComponentLoader + .getComponentFactory(this.formSections[index].dynamicComponentName) + .subscribe(componentFactory => { + this.formSections[index].dynamicComponentRef = cRef.createComponent(componentFactory); + this.formSections[index].matExpansionPanel = this.matExpansionPanels.find((item, panelIndex) => index === panelIndex); + this.formSections[index].dynamicComponentRef.instance.menuLabelUpdate.pipe(take(1)).subscribe(label => { + this.formSections[index].menuLabel = label; + }); + const hiddenStateSubscription = + this.formSections[index].dynamicComponentRef.instance.hiddenStateUpdate.subscribe(isHidden => { + this.formSections[index].isHidden = isHidden; + }); + this.subscriptions.push(hiddenStateSubscription); + this.formSections[index].dynamicComponentRef.instance.canAddItemUpdate.pipe(take(1)).subscribe(isList => { + this.formSections[index].canAddItem = isList; + if (isList) { + const aieSubscription = this.formSections[index].addItemEmitter.subscribe(() => { + this.formSections[index].matExpansionPanel.open(); + this.formSections[index].dynamicComponentRef.instance.addItem(); + }); + this.formSections[index].dynamicComponentRef.instance.componentDestroyed.pipe(take(1)).subscribe(() => { + aieSubscription.unsubscribe(); + }); + } + }); + this.formSections[index].dynamicComponentRef.changeDetectorRef.detectChanges(); + }); + }); + + this.canApprove = false; + }); + } + }, error => { + this.loadingService.setLoading(false); + }); + } else { + this.handleSubstanceRetrivalError(); + this.loadingService.setLoading(false); + + } + this.loadingService.setLoading(false); + this.isLoading = false; + } + + getPartialSubstanceDetails(uuid: string, type: string): void { + this.substanceService.getSubstanceDetails(uuid).pipe(take(1)).subscribe(response => { + if (response) { + this.substanceClass = response.substanceClass; + this.status = response.status; + delete response.uuid; + if (response._name) { + delete response._name; + } + this.scrub(response, type); + this.substanceFormService.loadSubstance(response.substanceClass, response).pipe(take(1)).subscribe(() => { + this.setFormSections(formSections[response.substanceClass]); + this.loadingService.setLoading(false); + this.isLoading = false; + }); + } else { + this.handleSubstanceRetrivalError(); + } + }, error => { + this.gaService.sendException('getSubstanceDetails: error from API call'); + this.loadingService.setLoading(false); + this.isLoading = false; + this.handleSubstanceRetrivalError(); + }); + } + + + private setFormSections(sectionNames: Array = []): void { + this.formSections = []; + sectionNames.forEach(sectionName => { + const formSection = new SubstanceFormSection(sectionName); + /* if (!this.definitionType || !(this.definitionType === 'ALTERNATIVE' && + (formSection.dynamicComponentName === 'substance-form-names' + || formSection.dynamicComponentName === 'substance-form-codes-card'))) { + } */ + this.formSections.push(formSection); + }); + } + + private handleSubstanceRetrivalError() { + const notification: AppNotification = { + message: 'The substance you\'re trying to edit doesn\'t exist.', + type: NotificationType.error, + milisecondsToShow: 4000 + }; + this.mainNotificationService.setNotification(notification); + setTimeout(() => { + this.router.navigate(['/substances/register']); + this.substanceFormService.loadSubstance(this.subClass).pipe(take(1)).subscribe(() => { + this.setFormSections(formSections.chemical); + this.loadingService.setLoading(false); + this.isLoading = false; + }); + }, 5000); + } + + validate(validationType?: string): void { + if (validationType && validationType === 'approval') { + this.approving = true; + } else { + this.approving = false; + } + this.isLoading = true; + this.serverError = false; + this.loadingService.setLoading(true); + this.substanceFormService.validateSubstance().pipe(take(1)).subscribe(results => { + this.submissionMessage = null; + this.validationMessages = results.validationMessages.filter( + message => message.messageType.toUpperCase() === 'ERROR' || message.messageType.toUpperCase() === 'WARNING'); + this.validationResult = results.valid; + this.showSubmissionMessages = true; + this.loadingService.setLoading(false); + this.isLoading = false; + if (this.validationMessages.length === 0 && results.valid === true) { + this.submissionMessage = 'Substance is Valid. Would you like to submit?'; + } + if (validationType && validationType === 'approval') { + this.submissionMessage = 'Are you sure you\'d like to approve this substance?'; + } + }, error => { + this.addServerError(error); + this.loadingService.setLoading(false); + this.isLoading = false; + }); + } + + approve(): void { + this.isLoading = true; + this.loadingService.setLoading(true); + this.substanceFormService.approveSubstance().pipe(take(1)).subscribe(response => { + this.loadingService.setLoading(false); + this.isLoading = false; + this.validationMessages = null; + this.openSuccessDialog('approve'); + this.submissionMessage = 'Substance was approved successfully'; + this.showSubmissionMessages = true; + this.validationResult = false; + }, + (error: SubstanceFormResults) => { + this.showSubmissionMessages = true; + this.loadingService.setLoading(false); + this.isLoading = false; + this.submissionMessage = 'Substance Could not be approved'; + this.addServerError(error.serverError); + setTimeout(() => { + this.showSubmissionMessages = false; + this.submissionMessage = null; + }, 10000); + } + ); + } + + submit(): void { + this.isLoading = true; + this.approving = false; + this.loadingService.setLoading(true); + this.substanceFormService.saveSubstance().pipe(take(1)).subscribe(response => { + this.loadingService.setLoading(false); + this.isLoading = false; + this.validationMessages = null; + this.showSubmissionMessages = false; + this.submissionMessage = ''; + if (!this.id) { + this.id = response.uuid; + } + this.openSuccessDialog(); + }, (error: SubstanceFormResults) => { + this.showSubmissionMessages = true; + this.loadingService.setLoading(false); + this.isLoading = false; + this.submissionMessage = null; + if (error.validationMessages && error.validationMessages.length) { + this.validationResult = error.isSuccessfull; + this.validationMessages = error.validationMessages + .filter(message => message.messageType.toUpperCase() === 'ERROR' || message.messageType.toUpperCase() === 'WARNING'); + this.showSubmissionMessages = true; + } else { + this.submissionMessage = 'There was a problem with your submission'; + this.addServerError(error.serverError); + setTimeout(() => { + this.showSubmissionMessages = false; + this.submissionMessage = null; + }, 8000); + } + }); + } + + dismissValidationMessage(index: number) { + this.validationMessages.splice(index, 1); + + if (this.validationMessages.length === 0) { + this.submissionMessage = 'Substance is Valid. Would you like to submit?'; + } + } + + addServerError(error: any): void { + this.serverError = true; + this.validationResult = false; + this.validationMessages = null; + + const message: ValidationMessage = { + actionType: 'server failure', + links: [], + appliedChange: false, + suggestedChange: false, + messageType: 'ERROR', + message: 'Unknown Server Error' + }; + if (error && error.error && error.error.message) { + message.message = 'Server Error ' + (error.status + ': ' || ': ') + error.error.message; + } else if (error && error.error && (typeof error.error) === 'string') { + message.message = 'Server Error ' + (error.status + ': ' || '') + error.error; + } else if (error && error.message) { + message.message = 'Server Error ' + (error.status + ': ' || '') + error.message; + } + this.validationMessages = [message]; + this.showSubmissionMessages = true; + } + + toggleValidation(): void { + this.showSubmissionMessages = !this.showSubmissionMessages; + } + + dismissAllValidationMessages(): void { + for (let i = this.validationMessages.length - 1; i >= 0; i--) { + if (this.validationMessages[i].messageType !== 'ERROR') { + this.validationMessages.splice(i, 1); + } + } + if (this.validationMessages.length === 0) { + this.submissionMessage = 'Substance is Valid. Would you like to submit?'; + } + } + + @HostListener('window:beforeunload', ['$event']) + unloadNotification($event: any) { + if (this.substanceFormService.isSubstanceUpdated) { + $event.returnValue = true; + } + } + + scrub(oldraw: any, importType: string): any { + function guid() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + + s4() + '-' + s4() + s4() + s4(); + } + const old = oldraw; + + const idHolders = defiant.json.search(old, '//*[id]'); + const idMap = {}; + for (let i = 0; i < idHolders.length; i++) { + const oid = idHolders[i].id; + if (idMap[oid]) { + idHolders[i].id = idMap[oid]; + } else { + const nid = guid(); + idHolders[i].id = nid; + idMap[oid] = nid; + } + } + + const uuidHolders = defiant.json.search(old, '//*[uuid]'); + const _map = {}; + for (let i = 0; i < uuidHolders.length; i++) { + const ouuid = uuidHolders[i].uuid; + if (_map[ouuid]) { + uuidHolders[i].uuid = _map[ouuid]; + if (uuidHolders[i].id) { + uuidHolders[i].id = _map[ouuid]; + } + } else { + const nid = guid(); + uuidHolders[i].uuid = nid; + _map[ouuid] = nid; + if (uuidHolders[i].id) { + uuidHolders[i].id = nid; + } + } + } + const refHolders = defiant.json.search(old, '//*[references]'); + for (let i = 0; i < refHolders.length; i++) { + const refs = refHolders[i].references; + for (let j = 0; j < refs.length; j++) { + const or = refs[j]; + if (typeof or === 'object') { continue; } + refs[j] = _map[or]; + } + } + defiant.json.search(old, '//*[uuid]'); + let remove = ['BDNUM']; + if (this.configService.configData && this.configService.configData.filteredDuplicationCodes) { + remove = this.configService.configData.filteredDuplicationCodes; + } + remove.forEach(code => { + _.remove(old.codes, { + codeSystem: code + }); + }) + + const createHolders = defiant.json.search(old, '//*[created]'); + for (let i = 0; i < createHolders.length; i++) { + const rec = createHolders[i]; + delete rec['created']; + delete rec['createdBy']; + delete rec['lastEdited']; + delete rec['lastEditedBy']; + } + + const originHolders = defiant.json.search(old, '//*[originatorUuid]'); + for (let i = 0; i < originHolders.length; i++) { + const rec = originHolders[i]; + delete rec['originatorUuid']; + } + + delete old.approvalID; + delete old.approved; + delete old.approvedBy; + old.status = 'pending'; + if ((importType) && (importType === 'definition')) { + old.names = []; + old.codes = []; + old.notes = []; + old.relationships = []; + old.tags = []; + } + delete old['createdBy']; + delete old['created']; + delete old['lastEdited']; + delete old['lastEditedBy']; + delete old['version']; + delete old['$$update']; + delete old['changeReason']; + + + if (true) { + const refSet = {}; + + const refHolders2 = defiant.json.search(old, '//*[references]'); + for (let i = 0; i < refHolders2.length; i++) { + const refs = refHolders2[i].references; + for (let j = 0; j < refs.length; j++) { + const or = refs[j]; + if (typeof or === 'object') { continue; } + refSet[or] = true; + } + } + + const nrefs = _.chain(old.references) + .filter(function (ref) { + if (refSet[ref.uuid]) { + return true; + } else { + return false; + } + }) + .value(); + + old.references = nrefs; + + } + + return old; + } + + openSuccessDialog(type?: string): void { + const dialogRef = this.dialog.open(SubmitSuccessDialogComponent, {}); + this.overlayContainer.style.zIndex = '1002'; + + const dialogSubscription = dialogRef.afterClosed().pipe(take(1)).subscribe((response?: 'continue' | 'browse' | 'view') => { + + this.substanceFormService.bypassUpdateCheck(); + if (response === 'continue') { + this.router.navigate(['/substances', this.id, 'edit']); + } else if (response === 'browse') { + this.router.navigate(['/browse-substance']); + } else if (response === 'view') { + this.router.navigate(['/substances', this.id]); + } else { + this.submissionMessage = 'Substance was saved successfully!'; + if (type && type === 'approve') { + this.submissionMessage = 'Substance was approved successfully'; + } + this.showSubmissionMessages = true; + this.validationResult = false; + setTimeout(() => { + this.showSubmissionMessages = false; + this.submissionMessage = ''; + this.router.navigate(['/substances', this.id, 'edit']); + }, 3000); + } + }); + this.subscriptions.push(dialogSubscription); + + } + + + mergeConcept() { + this.feature = undefined; + const dialogRef = this.dialog.open(MergeConceptDialogComponent, { + width: '900px', data: { uuid: this.id } + }); + this.overlayContainer.style.zIndex = '1002'; + } + + definitionSwitch() { + this.feature = undefined; + const dialogRef = this.dialog.open(DefinitionSwitchDialogComponent, { + width: '900px', data: { uuid: this.id }, autoFocus: false + }); + this.overlayContainer.style.zIndex = '1000'; + } + + fixLink(link: string) { + return this.substanceService.oldLinkFix(link); + } + + + saveDraft(auto?: boolean) { + const json = this.substanceFormService.cleanSubstance(); + const time = new Date().getTime(); + + const uuid = json.uuid ? json.uuid : 'register'; + const type = json.substanceClass; + let primary = null; + json.names.forEach(name => { + if (name.displayName) { + primary = name.name; + } + }); + if (!primary && json.names.length > 0) { + primary = json.names[0].name; + } + if (!auto) { + const file = 'gsrs-draft-' + time; + + let draft = { + 'uuid': uuid, + 'date': time, + 'type': type, + 'name': primary, + 'substance': json, + 'auto': false, + 'file': file + } + + localStorage.setItem(file, JSON.stringify(draft)); + this.draftCount++; + + } else { + this.getDrafts(); + let autos = this.drafts.filter(opt => { + return opt.auto; + }); + let auto1 = null; + let auto2 = null; + let auto3 = null; + this.drafts.forEach(draft => { + if (draft.auto) { + if (draft.file === 'gsrs-draft-auto1') { + auto1 = draft; + } + if (draft.file === 'gsrs-draft-auto2') { + auto2 = draft; + } + if (draft.file === 'gsrs-draft-auto3') { + auto3 = draft; + } + } + }); + let file = 'gsrs-draft-auto'; + + if (!auto1) { + file = 'gsrs-draft-auto1'; + this.draftCount++; + + } else if (!auto2) { + file = 'gsrs-draft-auto2'; + this.draftCount++; + + } else if (!auto3) { + file = 'gsrs-draft-auto3'; + this.draftCount++; + + } else { + if (auto1.date < auto2.date && auto1.date < auto3.date) { + file = 'gsrs-draft-auto1'; + } + else if (auto2.date < auto1.date && auto2.date < auto3.date) { + file = 'gsrs-draft-auto2'; + } + else { + file = 'gsrs-draft-auto3'; + } + } + + let draft = { + 'uuid': uuid, + 'date': time, + 'type': type, + 'name': primary, + 'substance': json, + 'auto': true, + 'file': file + } + + localStorage.setItem(file, JSON.stringify(draft)); + + + } + + + + + } +} diff --git a/src/app/core/substance-ssg2/substance-ssg2-form.service.ts b/src/app/core/substance-ssg2/substance-ssg2-form.service.ts new file mode 100644 index 000000000..bdb1d9935 --- /dev/null +++ b/src/app/core/substance-ssg2/substance-ssg2-form.service.ts @@ -0,0 +1,1959 @@ +import { Injectable, OnDestroy } from '@angular/core'; +import { + SubstanceDetail, + SubstanceName, + SubstanceStructure, + SubstanceMoiety, + Subunit, + Link, + DisulfideLink, + Glycosylation, + Site, + StructuralModification, + Sugar, + Linkage, + NucleicAcid, + StructurallyDiverse, DisplayStructure, Monomer, PolymerClassification +} from '../substance/substance.model'; +import { + SequenceUnit, + SubstanceFormDefinition, + SubstanceFormResults, SubunitSequence, ValidationResults, ValidationMessage +} from '../substance-form/substance-form.model'; +import { Observable, Subject, ReplaySubject, Subscription } from 'rxjs'; +import { SubstanceService } from '../substance/substance.service'; +import { UtilsService } from '../utils/utils.service'; +import { StructureService } from '@gsrs-core/structure'; +import * as _ from 'lodash'; +import { take } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) + +@Injectable() +export class SubstanceSsg2FormService implements OnDestroy { + private privateSubstance: SubstanceDetail; + private substanceStateHash?: number; + private substanceEmitter: ReplaySubject; + private substanceDisulfideLinksEmitter = new ReplaySubject>(); + private substanceGlycosylationEmitter = new ReplaySubject(); + private substanceLinksEmitter = new ReplaySubject>(); + private substanceNamesEmitter = new ReplaySubject>(); + private substanceOtherLinksEmitter = new ReplaySubject>(); + private substanceStructuralModificationsEmitter = new ReplaySubject>(); + private substanceCysteineEmitter = new ReplaySubject>(); + private substanceFormActionEmitter = new ReplaySubject<'load' | 'unload'>(); + + private definitionEmitter = new Subject(); + private subClass: string; + private substanceSubunitsEmitter = new Subject>(); + private substanceSugarsEmitter = new Subject>(); + private substanceNucleicAcidEmitter = new Subject(); + private allSitesArr: Array; + private allSitesEmitter = new Subject>(); + private displaySequences: Array; + private displaySequencesEmitter = new Subject>(); + private substanceChangeReasonEmitter = new Subject(); + private nameResolver = new Subject(); + resolvedMol = this.nameResolver.asObservable(); + private _bypassUpdateCheck = false; + private method?: string; + private previousHash?: number; + + constructor( + private substanceService: SubstanceService, + public utilsService: UtilsService, + private structureService: StructureService + ) { + this.substanceEmitter = new ReplaySubject(); + } + + ngOnDestroy() { + this.unloadSubstance(); + } + + loadSubstance(substanceClass: string = 'chemical', substance?: SubstanceDetail, method?: string, mergeConcept?: boolean): Observable { + if (method) { + this.method = method; + } else { + this.method = null; + } + if (mergeConcept) { + this.privateSubstance = substance; + this.substanceEmitter.next(substance); + this.namesUpdated(); + } + + this.substanceEmitter.subscribe(val => { + }); + return new Observable(observer => { + if (substance != null) { + this.privateSubstance = substance; + substanceClass = this.privateSubstance.substanceClass; + } else { + // the second case happens in the forms sometimes but really shouldn't + if (substanceClass === 'chemical' || substanceClass === 'structure') { + this.privateSubstance = { + substanceClass: 'chemical', + references: [], + names: [], + structure: { + molfile: '\n\n\n 0 0 0 0 0 0 999 V2000\nM END' + }, + codes: [], + relationships: [], + properties: [] + }; + } else if (substanceClass === 'protein') { + this.privateSubstance = { + substanceClass: 'protein', + references: [], + names: [], + protein: { proteinType: '' }, + codes: [], + relationships: [], + properties: [] + }; + } else if (substanceClass === 'nucleicAcid') { + this.privateSubstance = { + substanceClass: 'nucleicAcid', + references: [], + names: [], + nucleicAcid: {}, + codes: [], + relationships: [], + properties: [] + }; + } else if (substanceClass === 'mixture') { + this.privateSubstance = { + substanceClass: 'mixture', + references: [], + names: [], + mixture: {}, + codes: [], + relationships: [], + properties: [] + }; + } else if (substanceClass === 'structurallyDiverse') { + this.privateSubstance = { + substanceClass: 'structurallyDiverse', + references: [], + names: [], + structurallyDiverse: { + part: ['whole'], + $$diverseType: 'whole' + }, + codes: [], + relationships: [], + properties: [] + }; + } else if (substanceClass === 'specifiedSubstance' || (substanceClass === 'specifiedSubstanceG1')) { + this.privateSubstance = { + substanceClass: 'specifiedSubstanceG1', + references: [], + names: [], + specifiedSubstance: { + constituents: [], + references: [] + }, + codes: [], + relationships: [], + properties: [] + }; + } else if (substanceClass === 'specifiedSubstanceG2') { + this.privateSubstance = { + substanceClass: 'specifiedSubstanceG2', + references: [], + names: [], + specifiedSubstanceG2: { + constituents: [], + references: [] + } + }; + } else if (substanceClass === 'specifiedSubstanceG3') { + this.privateSubstance = { + substanceClass: substanceClass, + references: [], + names: [], + specifiedSubstanceG3: { + parentSubstance: {}, + definition: { references: [] }, + grade: { references: [] } + }, + codes: [], + properties: [] + }; + } else if (substanceClass === 'specifiedSubstanceG4m') { + this.privateSubstance = { + substanceClass: substanceClass, + // references: [], + specifiedSubstanceG4m: { + parentSubstance: {}, + process: [{ + sites: [{ + stages: [{ + "stageNumber": "Stage 1", + startingMaterials: [], + processingMaterials: [], + resultingMaterials: [], + criticalParameters: [] + }] + }] + }] + } + // codes: [], + // properties: [] + }; + } else if (substanceClass === 'polymer') { + this.privateSubstance = { + substanceClass: substanceClass, + references: [], + names: [], + polymer: { + idealizedStructure: {}, + monomers: [], + }, + codes: [], + moieties: [], + relationships: [], + properties: [] + }; + } else { + this.privateSubstance = { + substanceClass: substanceClass, + references: [], + names: [], + codes: [] + }; + } + // default values + + // TP: default to protected for root level record. + this.privateSubstance.access = ["protected"]; + + // ***** AN: Adding this right now for SSG4m and G2 ****** + if ((substanceClass !== 'specifiedSubstanceG4m') && (substanceClass !== 'specifiedSubstanceG2')) { + this.privateSubstance.definitionLevel = "COMPLETE"; + this.privateSubstance.definitionType = "PRIMARY"; + } + } + + this.subClass = this.privateSubstance.substanceClass; + + // Only these two substance classes differ from + // the name of their JSON defintional element + // That's why they are used as exceptions + + if (this.subClass === 'chemical') { + this.subClass = 'structure'; + } else if (this.subClass === 'specifiedSubstanceG1') { + this.subClass = 'specifiedSubstance'; + } + + if (this.privateSubstance[this.subClass] == null) { + this.privateSubstance[this.subClass] = {}; + } + this.initForm(); + this.substanceEmitter.next(this.privateSubstance); + observer.next(); + observer.complete(); + }); + } + + get substanceFormAction(): Observable<'load' | 'unload'> { + return this.substanceFormActionEmitter.asObservable(); + } + + initForm(): void { + this.substanceFormActionEmitter.next('load'); + } + + get substance(): Observable { + return this.substanceEmitter.asObservable(); + } + + resetState(): void { + const substanceString = JSON.stringify(this.privateSubstance); + this.substanceStateHash = this.utilsService.hashCode(substanceString); + } + + unloadSubstance(): void { + // this.privateSubstance = null; + this.allSitesArr = null; + this.displaySequences = null; + this.substanceEmitter.complete(); + this.substanceDisulfideLinksEmitter.complete(); + this.substanceGlycosylationEmitter.complete(); + this.substanceLinksEmitter.complete(); + this.substanceNamesEmitter.complete(); + this.substanceOtherLinksEmitter.complete(); + this.substanceStructuralModificationsEmitter.complete(); + this.substanceCysteineEmitter.complete(); + this.substanceEmitter = new ReplaySubject(); + this.substanceDisulfideLinksEmitter = new ReplaySubject>(); + this.substanceGlycosylationEmitter = new ReplaySubject(); + this.substanceLinksEmitter = new ReplaySubject>(); + this.substanceLinksEmitter = new ReplaySubject>(); + this.substanceOtherLinksEmitter = new ReplaySubject>(); + this.substanceStructuralModificationsEmitter = new ReplaySubject>(); + this.substanceCysteineEmitter = new ReplaySubject>(); + this.substanceFormActionEmitter.next('unload'); + } + + ready(): Observable { + return new Observable(observer => { + this.substanceEmitter.pipe(take(1)).subscribe(substance => { + observer.next(); + observer.complete(); + }); + }); + } + + setDefinitionFromDefRef(access: any) { + + if (this.privateSubstance.structurallyDiverse) { + this.privateSubstance.structurallyDiverse.access = access; + } else if (this.privateSubstance.protein) { + this.privateSubstance.protein.access = access; + } else if (this.privateSubstance.structure) { + this.privateSubstance.structure.access = access; + } else if (this.privateSubstance.mixture) { + this.privateSubstance.mixture.access = access; + } else if (this.privateSubstance.polymer) { + this.privateSubstance.polymer.access = access; + } else if (this.privateSubstance.nucleicAcid) { + this.privateSubstance.nucleicAcid.access = access; + } else if (this.privateSubstance.specifiedSubstance) { + this.privateSubstance.specifiedSubstance.access = access; + } else { + } + this.substanceEmitter.next(this.privateSubstance); + } + + getDefinitionForDefRef() { + + if (this.privateSubstance.structurallyDiverse) { + return this.privateSubstance.structurallyDiverse.access; + } else if (this.privateSubstance.protein) { + return this.privateSubstance.protein.access; + } else if (this.privateSubstance.structure) { + return this.privateSubstance.structure.access; + } else if (this.privateSubstance.mixture) { + return this.privateSubstance.mixture.access; + } else if (this.privateSubstance.polymer) { + return this.privateSubstance.polymer.access; + } else if (this.privateSubstance.nucleicAcid) { + return this.privateSubstance.nucleicAcid.access; + } else if (this.privateSubstance.specifiedSubstance) { + return this.privateSubstance.specifiedSubstance.access; + } else { + } + this.definitionEmitter.next(this.getDefinition()); + } + + changeApproval() { + const apid = prompt('Enter new ApprovalID:'); + + if (apid) { + const old = this.privateSubstance.approvalID; + this.privateSubstance.approvalID = apid; + alert('Approval ID changed from"' + old + '" to "' + apid + '". Submit changes to save'); + this.definitionEmitter.next(this.getDefinition()); + } + } + + switchType(substance: SubstanceDetail, newClass: string) { + const fieldGetter = { + 'protein': ['protein', 'modifications', 'properties'], + 'chemical': ['structure', 'moieties', 'modifications', 'properties'], + 'structurallyDiverse': ['structurallyDiverse', 'modifications', 'properties'], + 'polymer': ['polymer', 'modifications', 'properties'], + 'nucleicAcid': ['nucleicAcid', 'modifications', 'properties'], + 'mixture': ['mixture', 'modifications', 'properties'], + 'specifiedSubstanceG1': [] + }; + if (fieldGetter[newClass]) { + fieldGetter[newClass].forEach(function (x) { + if (substance[x]) { + delete substance[x]; + } + }); + } + substance.substanceClass = newClass; + if (newClass === 'chemical') { + substance.structure = {}; + } else if (newClass === 'protein') { + substance.protein = { proteinType: '' }; + + } else if (newClass === 'nucleicAcid') { + substance.nucleicAcid = {}; + } else if (newClass === 'mixture') { + substance.mixture = {}; + } else if (newClass === 'structurallyDiverse') { + substance.structurallyDiverse = { + part: ['whole'], + $$diverseType: 'whole' + }; + } else if (newClass === 'specifiedSubstanceG1') { + substance.specifiedSubstance = { + }; + } else if (newClass === 'polymer') { + substance.polymer = { + idealizedStructure: {}, + monomers: [], + }; + } + alert('Substance type switched. Submit changes to save'); + return substance; + } + + setDefinitionPrivate() { + if (this.privateSubstance.structurallyDiverse) { + this.setPrivate(this.privateSubstance.structurallyDiverse); + } else if (this.privateSubstance.protein) { + this.setPrivate(this.privateSubstance.protein); + } else if (this.privateSubstance.structure) { + this.setPrivate(this.privateSubstance.structure); + } else if (this.privateSubstance.mixture) { + this.setPrivate(this.privateSubstance.mixture); + } else if (this.privateSubstance.polymer) { + this.setPrivate(this.privateSubstance.polymer); + } else if (this.privateSubstance.nucleicAcid) { + this.setPrivate(this.privateSubstance.nucleicAcid); + } else if (this.privateSubstance.specifiedSubstance) { + this.setPrivate(this.privateSubstance.specifiedSubstance); + } else { + } + } + setPrivate(e) { + e.access = ['protected']; + alert('Substance definition now set to protected, please submit to save change'); + } + + + setDefinitionPublic() { + + if (this.privateSubstance.structurallyDiverse) { + this.setPublic(this.privateSubstance.structurallyDiverse); + } else if (this.privateSubstance.protein) { + this.setPublic(this.privateSubstance.protein); + } else if (this.privateSubstance.structure) { + this.setPublic(this.privateSubstance.structure); + } else if (this.privateSubstance.mixture) { + this.setPublic(this.privateSubstance.mixture); + } else if (this.privateSubstance.polymer) { + this.setPublic(this.privateSubstance.polymer); + } else if (this.privateSubstance.nucleicAcid) { + this.setPublic(this.privateSubstance.nucleicAcid); + } else if (this.privateSubstance.specifiedSubstance) { + this.setPublic(this.privateSubstance.specifiedSubstance); + } else { + } + } + + conceptNonApproved() { + if (this.privateSubstance.substanceClass === 'concept') { + this.privateSubstance.status = 'non-approved'; + alert('Concept status set to "non approved", please submit to save changes'); + } else { + alert('Can only change status of concept records'); + } + } + + unapproveRecord() { + const old = this.privateSubstance.approvalID; + this.privateSubstance.approvalID = null; + this.privateSubstance.status = null; + this.privateSubstance.approved = null; + this.privateSubstance.approvedBy = null; + alert('Removed approvalID \'' + old + '\'. Submit record to save.'); + } + + setPublic(e) { + e.access = []; + alert('Substance definition set to be PUBLIC, please submit to save change'); + } + + get isSubstanceUpdated(): boolean { + const substanceString = JSON.stringify(this.privateSubstance); + if (this._bypassUpdateCheck) { + this._bypassUpdateCheck = false; + return false; + } else { + return this.substanceStateHash !== this.utilsService.hashCode(substanceString); + } + } + + autoSave(): boolean { + const substanceString = JSON.stringify(this.privateSubstance); + if (!this.previousHash) { + this.previousHash = this.utilsService.hashCode(substanceString); + return false; + } else { + const match = this.previousHash !== this.utilsService.hashCode(substanceString); + if (match) { + this.previousHash = this.utilsService.hashCode(substanceString); + return true; + } else { + return false; + } + } + } + + bypassUpdateCheck(): void { + this._bypassUpdateCheck = true; + } + + // Definition Start + + get definition(): Observable { + return new Observable(observer => { + this.ready().subscribe(() => { + const definition = this.getDefinition(); + observer.next(definition); + // eslint-disable-next-line @typescript-eslint/no-shadow + this.definitionEmitter.subscribe(definition => { + observer.next(definition); + }); + }); + }); + } + + updateDefinition(definition: SubstanceFormDefinition): void { + this.privateSubstance.definitionLevel = definition.definitionLevel; + this.privateSubstance.deprecated = definition.deprecated; + this.privateSubstance.access = definition.access; + this.privateSubstance.created = definition.created; + this.privateSubstance.createdBy = definition.createdBy; + this.privateSubstance.lastEdited = definition.lastEdited; + this.privateSubstance.lastEditedBy = definition.lastEditedBy; + if (definition.status) { + this.privateSubstance.status = definition.status; + } + if (definition.approvalID) { + this.privateSubstance.approvalID = definition.approvalID; + } + if (this.privateSubstance[definition.substanceClass]) { + this.privateSubstance[definition.substanceClass].references = definition.references; + } else { + this.privateSubstance[definition.substanceClass] = { + references: definition.references + }; + } + + if (this.privateSubstance.definitionType !== definition.definitionType) { + if (definition.definitionType === 'ALTERNATIVE') { + this.privateSubstance.names = []; + this.privateSubstance.codes = []; + this.substanceNamesEmitter.next(this.privateSubstance.names); + } + } + this.privateSubstance.definitionType = definition.definitionType; + this.definitionEmitter.next(this.getDefinition()); + } + + getJson() { + return this.privateSubstance; + } + + getUuid(): string { + return this.privateSubstance.uuid; + } + + getClass(): string { + return this.privateSubstance.substanceClass; + } + + changeStatus(status: string): void { + this.privateSubstance.status = status; + alert('Status changed to ' + status); + } + + private getDefinition(): SubstanceFormDefinition { + + if (!this.privateSubstance[this.subClass]) { + this.privateSubstance[this.subClass] = { + references: [] + }; + const substanceString = JSON.stringify(this.privateSubstance); + this.substanceStateHash = this.utilsService.hashCode(substanceString); + } + + if (!this.privateSubstance[this.subClass].references) { + this.privateSubstance[this.subClass].references = []; + const substanceString = JSON.stringify(this.privateSubstance); + this.substanceStateHash = this.utilsService.hashCode(substanceString); + } + + if (!this.privateSubstance.tags) { + this.privateSubstance.tags = []; + const substanceString = JSON.stringify(this.privateSubstance); + this.substanceStateHash = this.utilsService.hashCode(substanceString); + } + + + const definition: SubstanceFormDefinition = { + uuid: this.privateSubstance[this.subClass].uuid || this.privateSubstance[this.subClass].id, + substanceClass: this.subClass, + definitionType: this.privateSubstance.definitionType, + definitionLevel: this.privateSubstance.definitionLevel, + deprecated: this.privateSubstance.deprecated, + references: this.privateSubstance[this.subClass].references, + access: this.privateSubstance.access, + relationships: this.privateSubstance.relationships, + created: this.privateSubstance.created, + createdBy: this.privateSubstance.createdBy, + lastEdited: this.privateSubstance.lastEdited, + lastEditedBy: this.privateSubstance.lastEditedBy, + _name: this.privateSubstance._name, + tags: this.privateSubstance.tags + }; + if (this.privateSubstance.status) { + definition.status = this.privateSubstance.status; + } + if (this.privateSubstance.approvalID) { + definition.approvalID = this.privateSubstance.approvalID; + } + + return definition; + } + + // Definition end + + + get allSites(): Observable> { + return new Observable(observer => { + if (!this.allSitesArr) { + this.allSitesArr = this.getAllSites(); + } + observer.next(this.allSitesArr); + this.allSitesEmitter.subscribe(sites => { + observer.next(this.allSitesArr); + }); + }); + } + + emitAllsitesUpdate(): void { + this.allSitesEmitter.next(this.getAllSites()); + } + + + getAllSites(): Array { + + const allSitesArr = []; + + if (this.privateSubstance.substanceClass === 'protein') { + if (this.privateSubstance.protein.disulfideLinks) { + this.privateSubstance.protein.disulfideLinks.forEach(link => { + if (link.sites) { + link.sites.forEach(site => { + if (site.subunitIndex && site.residueIndex) { + const newLink: DisplaySite = { + residue: site.residueIndex, + subunit: site.subunitIndex, + type: 'disulfide' + }; + allSitesArr.push(newLink); + } + }); + } + }); + } + if (this.privateSubstance.protein.otherLinks) { + this.privateSubstance.protein.otherLinks.forEach(link => { + if (link.sites) { + link.sites.forEach(site => { + if (site.subunitIndex && site.residueIndex) { + const newLink: DisplaySite = { residue: site.residueIndex, subunit: site.subunitIndex, type: 'other' }; + allSitesArr.push(newLink); + } + }); + } + }); + } + if (this.privateSubstance.protein.glycosylation) { + const glycosylation = this.privateSubstance.protein.glycosylation; + if (glycosylation.CGlycosylationSites) { + glycosylation.CGlycosylationSites.forEach(site => { + const newLink: DisplaySite = { + residue: site.residueIndex, + subunit: site.subunitIndex, + type: 'C-Glycosylation' + }; + allSitesArr.push(newLink); + }); + } + if (glycosylation.NGlycosylationSites) { + glycosylation.NGlycosylationSites.forEach(site => { + const newLink: DisplaySite = { + residue: site.residueIndex, + subunit: site.subunitIndex, + type: 'N-Glycosylation' + }; + allSitesArr.push(newLink); + }); + } + + if (glycosylation.OGlycosylationSites) { + glycosylation.OGlycosylationSites.forEach(site => { + if (site.subunitIndex && site.residueIndex) { + const newLink: DisplaySite = { + residue: site.residueIndex, + subunit: site.subunitIndex, + type: 'O-Glycosylation' + }; + allSitesArr.push(newLink); + } + }); + } + } + } + if (this.privateSubstance.modifications.structuralModifications) { + this.privateSubstance.modifications.structuralModifications.forEach(mod => { + if (mod.sites) { + mod.sites.forEach(site => { + if (site.subunitIndex && site.residueIndex) { + const newLink: DisplaySite = { + residue: site.residueIndex, + subunit: site.subunitIndex, + type: 'modification' + }; + allSitesArr.push(newLink); + } + }); + } + }); + } + if (this.privateSubstance.properties) { + this.privateSubstance.properties.forEach(prop => { + if (prop.propertyType === 'PROTEIN FEATURE' || prop.propertyType === 'NUCLEIC ACID FEATURE') { + const featArr = prop.value.nonNumericValue.split(';'); + featArr.forEach(f => { + const sites = f.split('-'); + const subunitIndex = Number(sites[0].split('_')[0]); + for (let i = Number(sites[0].split('_')[1]); i <= Number(sites[1].split('_')[1]); i++) { + const newLink: DisplaySite = { residue: Number(i), subunit: subunitIndex, type: 'feature' }; + allSitesArr.push(newLink); + } + }); + } + }); + } + return allSitesArr; + } + + // ### possibly use type to only partially calculate allsites? + recalculateAllSites(type?: string): void { + const newSites = this.getAllSites(); + if (newSites !== this.allSitesArr) { + this.allSitesArr = newSites; + this.allSitesEmitter.next(this.allSitesArr); + } + } + + resolvedName(mol: string): void { + this.nameResolver.next(mol); + } + + updateNucleicAcidDetails(acid: NucleicAcid): void { + this.privateSubstance.nucleicAcid.nucleicAcidType = acid.nucleicAcidType; + this.privateSubstance.nucleicAcid.nucleicAcidSubType = acid.nucleicAcidSubType; + this.privateSubstance.nucleicAcid.sequenceOrigin = acid.sequenceOrigin; + this.privateSubstance.nucleicAcid.sequenceType = acid.sequenceType; + } + + get substanceNucleicAcid(): Observable { + return new Observable(observer => { + this.ready().subscribe(substance => { + if (this.privateSubstance.nucleicAcid == null) { + this.privateSubstance.nucleicAcid = { nucleicAcidType: '' }; + + } + observer.next(this.privateSubstance.nucleicAcid); + this.substanceNucleicAcidEmitter.subscribe(protein => { + observer.next(this.privateSubstance.nucleicAcid); + }); + }); + }); + } + + // Names start + + namesUpdated(): Observable> { + return this.substanceNamesEmitter.asObservable(); + } + + // Names end + + // Subunits start + + get substanceSubunits(): Observable> { + return new Observable(observer => { + this.ready().subscribe(() => { + if (this.privateSubstance.substanceClass === 'protein') { + if (!this.privateSubstance.protein.subunits) { + this.privateSubstance.protein.subunits = []; + const substanceString = JSON.stringify(this.privateSubstance); + this.substanceStateHash = this.utilsService.hashCode(substanceString); + } + observer.next(this.privateSubstance.protein.subunits); + this.substanceSubunitsEmitter.subscribe(subunits => { + observer.next(this.privateSubstance.protein.subunits); + }); + } else { + if (!this.privateSubstance.nucleicAcid.subunits) { + this.privateSubstance.nucleicAcid.subunits = []; + const substanceString = JSON.stringify(this.privateSubstance); + this.substanceStateHash = this.utilsService.hashCode(substanceString); + } + observer.next(this.privateSubstance.nucleicAcid.subunits); + this.substanceSubunitsEmitter.subscribe(subunits => { + observer.next(this.privateSubstance.nucleicAcid.subunits); + }); + } + }); + }); + } + + get subunitDisplaySequences(): Observable> { + return new Observable(observer => { + this.ready().subscribe(() => { + if (!this.displaySequences) { + this.displaySequences = this.createSubunitDisplay(); + } + observer.next(this.displaySequences); + this.displaySequencesEmitter.subscribe(newDisplay => { + this.displaySequences = newDisplay; + observer.next(this.displaySequences); + }); + }); + }); + } + + + addSubstanceSubunit(): void { + if (this.privateSubstance.substanceClass === 'protein') { + const index: number = this.privateSubstance.protein.subunits.length + 1; + const newSubunit: Subunit = { + references: [], + access: [], + sequence: '', + subunitIndex: index + }; + this.privateSubstance.protein.subunits.push(newSubunit); + this.displaySequencesEmitter.next(this.createSubunitDisplay()); + this.substanceSubunitsEmitter.next(this.privateSubstance.protein.subunits); + } else { + let index = this.privateSubstance.nucleicAcid.subunits.length || 0; + index = index + 1; + const newSubunit: Subunit = { + references: [], + access: [], + sequence: '', + subunitIndex: index + }; + this.privateSubstance.nucleicAcid.subunits.push(newSubunit); + this.displaySequencesEmitter.next(this.createSubunitDisplay()); + this.substanceSubunitsEmitter.next(this.privateSubstance.nucleicAcid.subunits); + this.emitSugarUpdate(); + this.emitLinkUpdate(); + } + + } + + deleteSubstanceSubunit(subunit: Subunit): void { + if (this.privateSubstance.substanceClass === 'protein') { + const subUnitIndex = this.privateSubstance.protein.subunits.findIndex(subUnit => subunit.subunitIndex === subUnit.subunitIndex); + if (subUnitIndex > -1) { + this.rearrangeSubunitIndexes('protein', subunit.subunitIndex); + this.displaySequencesEmitter.next(this.createSubunitDisplay()); + this.substanceSubunitsEmitter.next(this.privateSubstance.protein.subunits); + } + } else { + const subUnitIndex = this.privateSubstance.nucleicAcid.subunits.findIndex(subUnit => subunit.subunitIndex === subUnit.subunitIndex); + if (subUnitIndex > -1) { + this.rearrangeNAIndexes('nucleicAdid', subunit.subunitIndex); + this.displaySequencesEmitter.next(this.createSubunitDisplay()); + this.substanceSubunitsEmitter.next(this.privateSubstance.nucleicAcid.subunits); + this.emitSugarUpdate(); + this.emitLinkUpdate(); + } + } + + } + + rearrangeNAIndexes(type: string, index: number) { + const arrIndex = this.privateSubstance.nucleicAcid.subunits.findIndex(subUnit => index === subUnit.subunitIndex); + this.privateSubstance.nucleicAcid.subunits.splice(arrIndex, 1); + if (this.privateSubstance.nucleicAcid.subunits.length > (arrIndex - 1)) { + this.privateSubstance.nucleicAcid.subunits.forEach(subunit => { + if (subunit.subunitIndex > index) { + + const newIndex = subunit.subunitIndex - 1; + subunit.subunitIndex = newIndex; + } + }); + if (this.privateSubstance.nucleicAcid.sugars) { + this.privateSubstance.nucleicAcid.sugars.forEach(link => { + if (link.sites) { + link.sites = link.sites.filter(site => (site.subunitIndex !== index)); + link.sites.forEach(site => { + if (site.subunitIndex && (site.subunitIndex > index)) { + site.subunitIndex = site.subunitIndex - 1; + } + }); + } + }); + this.emitSugarUpdate(); + } + if (this.privateSubstance.nucleicAcid.linkages) { + this.privateSubstance.nucleicAcid.linkages.forEach(link => { + if (link.sites) { + link.sites = link.sites.filter(site => (site.subunitIndex !== index)); + link.sites.forEach(site => { + if (site.subunitIndex && (site.subunitIndex > index)) { + site.subunitIndex = site.subunitIndex - 1; + } + }); + } + }); + this.emitSugarUpdate(); + } + } + } + + rearrangeSubunitIndexes(type: string, index: number) { + const arrIndex = this.privateSubstance.protein.subunits.findIndex(subUnit => index === subUnit.subunitIndex); + this.privateSubstance.protein.subunits.splice(arrIndex, 1); + if (this.privateSubstance.protein.subunits.length > (arrIndex - 1)) { + this.privateSubstance.protein.subunits.forEach(subunit => { + if (subunit.subunitIndex > index) { + + const newIndex = subunit.subunitIndex - 1; + subunit.subunitIndex = newIndex; + } + }); + if (this.privateSubstance.protein.disulfideLinks) { + this.privateSubstance.protein.disulfideLinks.forEach(link => { + if (link.sites) { + link.sites.forEach(site => { + if (site.subunitIndex === index) { + site = {}; + } + if (site.subunitIndex && (site.subunitIndex > index)) { + site.subunitIndex = site.subunitIndex - 1; + } + }); + } + }); + + this.emitDisulfideLinkUpdate(); + } + if (this.privateSubstance.protein.otherLinks) { + this.privateSubstance.protein.otherLinks.forEach(link => { + if (link.sites) { + link.sites = link.sites.filter(site => (site.subunitIndex !== index)); + link.sites.forEach(site => { + if (site.subunitIndex && (site.subunitIndex > index)) { + site.subunitIndex = site.subunitIndex - 1; + } + }); + } + }); + this.emitOtherLinkUpdate(); + } + if (this.privateSubstance.protein.glycosylation) { + const glycosylation = this.privateSubstance.protein.glycosylation; + if (glycosylation.CGlycosylationSites) { + glycosylation.CGlycosylationSites = glycosylation.CGlycosylationSites.filter(site => (site.subunitIndex !== index)); + glycosylation.CGlycosylationSites.forEach(site => { + if (site.subunitIndex && (site.subunitIndex > index)) { + site.subunitIndex = site.subunitIndex - 1; + } + }); + } + if (glycosylation.NGlycosylationSites) { + glycosylation.NGlycosylationSites = glycosylation.NGlycosylationSites.filter(site => (site.subunitIndex !== index)); + glycosylation.NGlycosylationSites.forEach(site => { + if (site.subunitIndex && (site.subunitIndex > index)) { + site.subunitIndex = site.subunitIndex - 1; + } + }); + } + + if (glycosylation.OGlycosylationSites) { + glycosylation.OGlycosylationSites = glycosylation.OGlycosylationSites.filter(site => (site.subunitIndex !== index)); + glycosylation.OGlycosylationSites.forEach(site => { + if (site.subunitIndex && (site.subunitIndex > index)) { + site.subunitIndex = site.subunitIndex - 1; + } + }); + } + this.emitGlycosylationUpdate(); + } + if (this.privateSubstance.modifications.structuralModifications) { + this.privateSubstance.modifications.structuralModifications.forEach(link => { + if (link.sites) { + link.sites = link.sites.filter(site => (site.subunitIndex !== index)); + link.sites.forEach(site => { + if (site.subunitIndex && (site.subunitIndex > index)) { + site.subunitIndex = site.subunitIndex - 1; + } + }); + } + }); + this.emitStructuralModificationsUpdate(); + } + if (this.privateSubstance.properties) { + this.privateSubstance.properties.forEach(prop => { + if (prop.propertyType === 'PROTEIN FEATURE' || prop.propertyType === 'NUCLEIC ACID FEATURE') { + const featArr = prop.value.nonNumericValue.split(';'); + featArr.forEach(f => { + }); + } + }); + } + } + } + + emitSubunitUpdate(): void { + if (this.privateSubstance.substanceClass === 'protein') { + this.substanceSubunitsEmitter.next(this.privateSubstance.protein.subunits); + this.displaySequencesEmitter.next(this.createSubunitDisplay()); + } else { + this.substanceSubunitsEmitter.next(this.privateSubstance.nucleicAcid.subunits); + this.displaySequencesEmitter.next(this.createSubunitDisplay()); + this.emitSugarUpdate(); + this.emitLinkUpdate(); + } + } + + // subunits end + + // other links start + + otherLinksUpdated(): Observable> { + return this.substanceOtherLinksEmitter.asObservable(); + } + + emitOtherLinkUpdate(): void { + this.recalculateAllSites('other'); + this.substanceOtherLinksEmitter.next(this.privateSubstance.protein.otherLinks); + } + + // other links end + + // disulfide links start + copyDisulfideLinks(to: number, from: number): any { + const test = JSON.parse(JSON.stringify(this.privateSubstance.protein.disulfideLinks)); + const push = []; + const test3 = []; + for (let i = 0; i < test.length; i++) { + const link = JSON.parse(JSON.stringify(test[i])); + if (link['sites'][0].subunitIndex === to || (link['sites'][1].subunitIndex === to)) { + } else if (link['sites'][0].subunitIndex === from && link['sites'][1].subunitIndex === from) { + const copy = JSON.parse(JSON.stringify(test[i])); + copy.sites[0].subunitIndex = to; + copy.sites[1].subunitIndex = to; + copy.sitesShorthand = copy.sites[0].subunitIndex + '_' + copy.sites[0].residueIndex + + ';' + copy.sites[1].subunitIndex + '_' + copy.sites[1].residueIndex; + test3.push(link); + test3.push(copy); + } else { + test3.push(link); + } + } + + this.privateSubstance.protein.disulfideLinks = test3; + this.emitDisulfideLinkUpdate(); + } + + disulfideLinksUpdated(): Observable> { + return this.substanceDisulfideLinksEmitter.asObservable(); + } + + private emitDisulfideLinkUpdate(): void { + this.recalculateAllSites('disulfide'); + this.substanceDisulfideLinksEmitter.next(this.privateSubstance.protein.disulfideLinks); + this.recalculateCysteine(); + } + + // disulfide links end + + // Glycosylation start + + glycosylationUpdated(): Observable { + return this.substanceGlycosylationEmitter.asObservable(); + } + + emitGlycosylationUpdate(): void { + this.recalculateAllSites('glycosylation'); + this.substanceGlycosylationEmitter.next(this.privateSubstance.protein.glycosylation); + } + + // Glycosylation end + + // modifications start + + structuralModificationsUpdated(): Observable> { + return this.substanceStructuralModificationsEmitter.asObservable(); + } + + emitStructuralModificationsUpdate(): void { + if (this.privateSubstance.substanceClass === 'protein' || 'nucleic acid') { + this.recalculateAllSites('glycosylation'); + } + this.substanceStructuralModificationsEmitter.next(this.privateSubstance.modifications.structuralModifications); + } + + // modifications end + + cysteineUpdated(): Observable> { + return this.substanceCysteineEmitter.asObservable(); + } + + recalculateCysteine(): void { + let available = []; + if (this.privateSubstance.substanceClass === 'protein') { + for (let i = 0; i < this.privateSubstance.protein.subunits.length; i++) { + const sequence = this.privateSubstance.protein.subunits[i].sequence; + if (sequence != null && sequence.length > 0) { + for (let j = 0; j < sequence.length; j++) { + const site = sequence[j]; + if (site.toUpperCase() === 'C') { + available.push({ 'residueIndex': (j + 1), 'subunitIndex': (i + 1) }); + } + } + } + } + + this.privateSubstance.protein.disulfideLinks.forEach(link => { + if (link.sites) { + link.sites.forEach(site => { + available = available.filter(r => (r.residueIndex !== site.residueIndex) || (r.subunitIndex !== site.subunitIndex)); + }); + } + }); + } + this.substanceCysteineEmitter.next(available); + } + + // ################################ Start Nucleic acid (break into new file, one for each class or on with only class specific? + + // begin link + + linksUpdated(): Observable> { + return this.substanceLinksEmitter.asObservable(); + } + + emitLinkUpdate(): void { + this.substanceLinksEmitter.next(this.privateSubstance.nucleicAcid.linkages); + } + + + // begin sugars + + get substanceSugars(): Observable> { + return new Observable(observer => { + this.ready().subscribe(() => { + if (this.privateSubstance.nucleicAcid.sugars == null) { + this.privateSubstance.nucleicAcid.sugars = []; + const substanceString = JSON.stringify(this.privateSubstance); + + this.substanceStateHash = this.utilsService.hashCode(substanceString); + } + observer.next(this.privateSubstance.nucleicAcid.sugars); + this.substanceSugarsEmitter.subscribe(sugars => { + observer.next(this.privateSubstance.nucleicAcid.sugars); + }); + }); + }); + } + + addSubstanceSugar(): void { + const newSugars: Sugar = { + sites: [], + sugar: '' + }; + this.privateSubstance.nucleicAcid.sugars.unshift(newSugars); + this.substanceSugarsEmitter.next(this.privateSubstance.nucleicAcid.sugars); + } + + deleteSubstanceSugar(sugar: Sugar): void { + const subSugarIndex = this.privateSubstance.nucleicAcid.sugars.findIndex(subCode => sugar.$$deletedCode === subCode.$$deletedCode); + if (subSugarIndex > -1) { + this.privateSubstance.nucleicAcid.sugars.splice(subSugarIndex, 1); + this.substanceSugarsEmitter.next(this.privateSubstance.nucleicAcid.sugars); + } + } + + emitSugarUpdate(): void { + this.substanceSugarsEmitter.next(this.privateSubstance.nucleicAcid.sugars); + } + + // end sugars + + // start change reason + + get changeReason(): Observable { + return new Observable(observer => { + this.ready().subscribe(() => { + observer.next(this.privateSubstance.changeReason); + this.substanceChangeReasonEmitter.subscribe(changeReason => { + observer.next(this.privateSubstance.changeReason); + }); + }); + }); + } + + updateChangeReason(changeReason: string): void { + this.privateSubstance.changeReason = changeReason; + this.substanceChangeReasonEmitter.next(this.privateSubstance.changeReason); + } + + + + // end change reason + + validateSubstance(): Observable { + return new Observable(observer => { + const substanceCopy = this.cleanSubstance(); + this.substanceService.validateSubstance(substanceCopy).subscribe(results => { + // check for missing required reference fields and append a validationMessage + if (results.validationMessages) { + for (let i = 0; i < substanceCopy.references.length; i++) { + const ref = substanceCopy.references[i]; + if (ref.docType !== 'SYSTEM') { + if ((!ref.citation || ref.citation === '') || (!ref.docType || ref.docType === '')) { + const invalidReferenceMessage: ValidationMessage = { + actionType: 'frontEnd', + appliedChange: false, + links: [], + message: 'All references require a non-empty source type and text/citation value', + messageType: 'WARNING', + suggestedChange: true + }; + results.validationMessages.push(invalidReferenceMessage); + break; + } + } + } + if (substanceCopy.properties) { + for (let i = 0; i < substanceCopy.properties.length; i++) { + const prop = substanceCopy.properties[i]; + if (!prop.propertyType || !prop.name) { + const invalidPropertyMessage: ValidationMessage = { + actionType: 'frontEnd', + appliedChange: false, + links: [], + message: 'Property #' + (i + 1) + ' requires a non-empty name and type', + messageType: 'ERROR', + suggestedChange: true + }; + results.validationMessages.push(invalidPropertyMessage); + results.valid = false; + } + } + } + if (substanceCopy.relationships) { + for (let i = 0; i < substanceCopy.relationships.length; i++) { + const relationship = substanceCopy.relationships[i]; + if (!relationship.relatedSubstance || !relationship.type || relationship.type === '') { + const invalidRelationshipMessage: ValidationMessage = { + actionType: 'frontEnd', + appliedChange: false, + links: [], + message: 'Relationship #' + (i + 1) + ' requires a non-empty related substance and type', + messageType: 'ERROR', + suggestedChange: true + }; + results.validationMessages.push(invalidRelationshipMessage); + results.valid = false; + } + } + } + if (substanceCopy.polymer && substanceCopy.polymer.monomers) { + for (let i = 0; i < substanceCopy.polymer.monomers.length; i++) { + const prop = substanceCopy.polymer.monomers[i]; + if (!prop.monomerSubstance || prop.monomerSubstance === {}) { + const invalidPropertyMessage: ValidationMessage = { + actionType: 'frontEnd', + appliedChange: false, + links: [], + message: 'Monomer #' + (i + 1) + ' requires a selected substance', + messageType: 'ERROR', + suggestedChange: true + }; + results.validationMessages.push(invalidPropertyMessage); + results.valid = false; + } + } + } + if (substanceCopy.modifications && substanceCopy.modifications.physicalModifications) { + for (let i = 0; i < substanceCopy.modifications.physicalModifications.length; i++) { + const prop = substanceCopy.modifications.physicalModifications[i]; + let present = false; + prop.parameters.forEach(param => { + if (param.parameterName) { + present = true; + } + }); + + if (!prop.physicalModificationRole && !present) { + const invalidPropertyMessage: ValidationMessage = { + actionType: 'frontEnd', + appliedChange: false, + links: [], + message: 'Physical Modification #' + (i + 1) + ' requires a modification role or valid parameter', + messageType: 'ERROR', + suggestedChange: true + }; + results.validationMessages.push(invalidPropertyMessage); + results.valid = false; + } + } + } + } + observer.next(results); + observer.complete(); + }, error => { + observer.error(error); + observer.complete(); + }); + }); + } + + cleanSubstance(): SubstanceDetail { + if (this.privateSubstance.structurallyDiverse) { + if (this.privateSubstance.structurallyDiverse.$$diverseType) { + delete this.privateSubstance.structurallyDiverse.$$diverseType; + } + if (this.privateSubstance.structurallyDiverse.$$storedPart) { + delete this.privateSubstance.structurallyDiverse.$$storedPart; + } + + const toclean = ['organismFamily', 'organismGenus', 'organismSpecies', 'organismAuthor', 'infraSpecificName', 'infraSpecificType', 'fractionMaterialType', 'fractionName', 'developmentalStage']; + toclean.forEach(field => { + if (this.privateSubstance.structurallyDiverse[field] && this.privateSubstance.structurallyDiverse[field] !== null && + this.privateSubstance.structurallyDiverse[field] !== '') { + this.privateSubstance.structurallyDiverse[field] = this.privateSubstance.structurallyDiverse[field].trim(); + } + }); + } + /* + if (this.privateSubstance.nucleicAcid) { + if (this.privateSubstance.nucleicAcid.sugars) { + this.privateSubstance.nucleicAcid.sugars.forEach((sugar, index) => { + if (sugar.sites.length === 0) { + this.privateSubstance.nucleicAcid.sugars.splice(index, 1); + } + }); + } + if (this.privateSubstance.nucleicAcid.linkages) { + this.privateSubstance.nucleicAcid.linkages.forEach((linkage, index) => { + if (linkage.sites.length === 0) { + this.privateSubstance.nucleicAcid.linkages.splice(index, 1); + } + }); + } + } + */ + + /*the substance API call for view=internal vs the usual 'view=full' adds some properties that should not be submitted + and can cause errors upon submission. the view change was to allow the stdName property to be visible to the forms*/ + if (this.privateSubstance.structure) { + + if (this.privateSubstance.structure.properties) { + delete this.privateSubstance.structure.properties; + } + if (this.privateSubstance.structure.links) { + delete this.privateSubstance.structure.links; + } + } + if (this.privateSubstance.polymer && this.privateSubstance.polymer.displayStructure) { + + if (this.privateSubstance.polymer.displayStructure.properties) { + delete this.privateSubstance.polymer.displayStructure.properties; + } + if (this.privateSubstance.polymer.displayStructure.links) { + delete this.privateSubstance.polymer.displayStructure.links; + } + } + if (this.privateSubstance.polymer && this.privateSubstance.polymer.idealizedStructure) { + + if (this.privateSubstance.polymer.idealizedStructure.properties) { + delete this.privateSubstance.polymer.idealizedStructure.properties; + } + if (this.privateSubstance.polymer.idealizedStructure.links) { + delete this.privateSubstance.polymer.idealizedStructure.links; + } + } + + if (this.privateSubstance.moieties) { + this.privateSubstance.moieties.forEach(moiety => { + if (moiety.properties) { + delete moiety.properties; + } + if (moiety.links) { + delete moiety.links; + } + }); + } + + if (this.privateSubstance.protein && this.privateSubstance.protein.disulfideLinks + && this.privateSubstance.protein.disulfideLinks.length > 0) { + for (let i = this.privateSubstance.protein.disulfideLinks.length; i >= 0; i--) { + if (this.privateSubstance.protein.disulfideLinks[i] && this.privateSubstance.protein.disulfideLinks[i].sites && + this.privateSubstance.protein.disulfideLinks[i].sites[0] && this.privateSubstance.protein.disulfideLinks[i].sites[1] && + Object.keys(this.privateSubstance.protein.disulfideLinks[i].sites[0]).length === 0 && + Object.keys(this.privateSubstance.protein.disulfideLinks[i].sites[1]).length === 0) { + this.privateSubstance.protein.disulfideLinks.splice(i, 1); + } + } + } + // end view=internal changes + + let substanceString = JSON.stringify(this.privateSubstance); + let substanceCopy: SubstanceDetail = JSON.parse(substanceString); + + const response = this.cleanObject(substanceCopy); + const deletedUuids = response.deletedUuids; + + if (deletedUuids.length > 0) { + substanceString = JSON.stringify(substanceCopy); + + deletedUuids.forEach(uuid => { + substanceString = substanceString.replace(new RegExp(`"${uuid}"`, 'g'), ''); + }); + substanceString = substanceString.replace(/,[,]+/g, ','); + substanceString = substanceString.replace(/\[,/g, '['); + substanceString = substanceString.replace(/,\]/g, ']'); + substanceCopy = JSON.parse(substanceString); + } + + return substanceCopy; + } + + private cleanObject(substanceProperty: any, deletedUuids: Array = []): { deletedUuids: Array, isDeleted: boolean } { + if (Object.prototype.toString.call(substanceProperty) === '[object Object]') { + + const hasDeleletedCode = substanceProperty.$$deletedCode != null; + if (!hasDeleletedCode) { + delete substanceProperty.$$deletedCode; + Object.keys(substanceProperty).forEach(key => { + if (Object.prototype.toString.call(substanceProperty[key]) === '[object Array]') { + substanceProperty[key] = substanceProperty[key].filter(childProperty => { + const response = this.cleanObject(childProperty, deletedUuids); + return !response.isDeleted; + }); + } else if (Object.prototype.toString.call(substanceProperty[key]) === '[object Object]') { + this.cleanObject(substanceProperty[key], deletedUuids); + } + }); + } else if (substanceProperty.uuid != null) { + deletedUuids.push(substanceProperty.uuid); + } + + return { + deletedUuids: deletedUuids, + isDeleted: hasDeleletedCode + }; + } else if (Object.prototype.toString.call(substanceProperty) === '[object Array]') { + substanceProperty.forEach(childProperty => { + this.cleanObject(childProperty, deletedUuids); + }); + } else { + return { + deletedUuids: deletedUuids, + isDeleted: false + }; + } + } + + getMethod(): string { + return this.method; + } + + structureDuplicateCheck(): any { + return new Observable(observer => { + this.structureService.duplicateCheck(this.privateSubstance).subscribe(response => { + observer.next(response); + observer.complete(); + }); + }); + } + + getUNII() { + return this.privateSubstance._approvalIDDisplay; + } + + approveSubstance(): Observable { + return new Observable(observer => { + const results: SubstanceFormResults = { + isSuccessfull: true + }; + this.substanceService.approveSubstance(this.privateSubstance.uuid).subscribe(substance => { + this.privateSubstance = substance; + results.uuid = substance.uuid; + this.definitionEmitter.next(this.getDefinition()); + if (this.privateSubstance.substanceClass === 'protein') { + this.substanceSubunitsEmitter.next(this.privateSubstance.protein.subunits); + } else if (this.privateSubstance.substanceClass === 'nucleicAcid') { + this.substanceSugarsEmitter.next(this.privateSubstance.nucleicAcid.sugars); + this.substanceSubunitsEmitter.next(this.privateSubstance.nucleicAcid.subunits); + } else if (this.privateSubstance.substanceClass === 'mixture') { + this.substanceSubunitsEmitter.next(this.privateSubstance.mixture.components); + } + this.substanceChangeReasonEmitter.next(this.privateSubstance.changeReason); + this.resetState(); + this.substanceEmitter.next(this.privateSubstance); + observer.next(results); + observer.complete(); + }, error => { + results.isSuccessfull = false; + if (error && error.error && error.error.validationMessages) { + results.validationMessages = error.error.validationMessages; + } else { + results.serverError = error; + } + observer.error(results); + observer.complete(); + }); + }); + } + + saveSubstance(): Observable { + return new Observable(observer => { + const results: SubstanceFormResults = { + isSuccessfull: true + }; + if (this.privateSubstance.structure != null && !this.privateSubstance.structure.uuid) { + this.privateSubstance.structure.id = this.utilsService.newUUID(); + this.privateSubstance.structure.uuid = this.privateSubstance.structure.id; + } + if (this.privateSubstance.moieties != null && this.privateSubstance.moieties.length) { + this.privateSubstance.moieties.forEach(moiety => { + if (!moiety.uuid) { + moiety.id = this.utilsService.newUUID(); + moiety.uuid = moiety.id; + } + }); + } + + const substanceCopy = this.cleanSubstance(); + this.substanceService.saveSubstance(substanceCopy, this.method).subscribe(substance => { + this.privateSubstance = substance; + results.uuid = substance.uuid; + this.definitionEmitter.next(this.getDefinition()); + if (this.privateSubstance.substanceClass === 'protein') { + this.substanceSubunitsEmitter.next(this.privateSubstance.protein.subunits); + } else if (this.privateSubstance.substanceClass === 'nucleicAcid') { + this.substanceSugarsEmitter.next(this.privateSubstance.nucleicAcid.sugars); + this.substanceSubunitsEmitter.next(this.privateSubstance.nucleicAcid.subunits); + } else if (this.privateSubstance.substanceClass === 'mixture') { + this.substanceSubunitsEmitter.next(this.privateSubstance.mixture.components); + } + this.substanceChangeReasonEmitter.next(this.privateSubstance.changeReason); + this.substanceService.getSubstanceDetails(results.uuid).subscribe(resp => { + this.privateSubstance = resp; + this.resetState(); + this.substanceEmitter.next(this.privateSubstance); + observer.next(results); + observer.complete(); + }, error => { + observer.next(results); + observer.complete(); + }); + }, error => { + results.isSuccessfull = false; + if (error && error.error && error.error.validationMessages) { + results.validationMessages = error.error.validationMessages; + } else { + results.serverError = error; + } + observer.error(results); + observer.complete(); + }); + }); + } + + siteDisplayToSite(site) { + const subres = site.split('_'); + + if (site.match(/^[0-9][0-9]*_[0-9][0-9]*$/g) === null) { + throw new Error('"' + site + '" is not a valid shorthand for a site. Must be of form "{subunit}_{residue}"'); + } + + return { + subunitIndex: subres[0] - 0, + residueIndex: subres[1] - 0 + }; + } + + importSubstance(substance, method?: string) { + this.privateSubstance = substance; + if (!method || method !== 'update') { + this.method = 'import'; + } else { + this.method = null; + } + this.definitionEmitter.next(this.getDefinition()); + if (this.privateSubstance.substanceClass === 'protein') { + this.substanceSubunitsEmitter.next(this.privateSubstance.protein.subunits); + } else if (this.privateSubstance.substanceClass === 'nucleicAcid') { + this.substanceSugarsEmitter.next(this.privateSubstance.nucleicAcid.sugars); + this.substanceSubunitsEmitter.next(this.privateSubstance.nucleicAcid.subunits); + } else if (this.privateSubstance.substanceClass === 'mixture') { + this.substanceSubunitsEmitter.next(this.privateSubstance.mixture.components); + } + this.substanceChangeReasonEmitter.next(this.privateSubstance.changeReason); + this.resetState(); + this.substanceEmitter.next(this.privateSubstance); + } + + stringToSites(slist: string): Array { + slist = slist.replace(/ /g, ''); + if (!slist) { + return []; + } + const toks = slist.split(';'); + const sites = []; + // eslint-disable-next-line guard-for-in + for (const i in toks) { + const l = toks[i]; + if (l === '') { + continue; + } + const rng = l.split('-'); + if (rng.length > 1) { + const site1 = this.siteDisplayToSite(rng[0]); + const site2 = this.siteDisplayToSite(rng[1]); + if (site1.subunitIndex !== site2.subunitIndex) { + throw new Error('"' + rng + '" is not a valid shorthand for a site range. Must be between the same subunits.'); + } + if (site2.residueIndex <= site1.residueIndex) { + throw new Error('"' + rng + '" is not a valid shorthand for a site range. Second residue index must be greater than first.'); + } + sites.push(site1); + for (let j = site1.residueIndex + 1; j < site2.residueIndex; j++) { + sites.push({ + subunitIndex: site1.subunitIndex, + residueIndex: j + }); + } + sites.push(site2); + } else { + sites.push(this.siteDisplayToSite(rng[0])); + } + } + return sites; + } + + addAnySiteType(data: any) { + if (data.siteType === 'CGlycosylation') { + this.privateSubstance.protein.glycosylation.CGlycosylationSites = + this.privateSubstance.protein.glycosylation.CGlycosylationSites.concat(data.links); + this.emitGlycosylationUpdate(); + } else if (data.siteType === 'NGlycosylation') { + this.privateSubstance.protein.glycosylation.NGlycosylationSites = + this.privateSubstance.protein.glycosylation.NGlycosylationSites.concat(data.links); + this.emitGlycosylationUpdate(); + } else if (data.siteType === 'OGlycosylation') { + this.privateSubstance.protein.glycosylation.OGlycosylationSites = + this.privateSubstance.protein.glycosylation.OGlycosylationSites.concat(data.links); + this.emitGlycosylationUpdate(); + } else if (data.siteType === 'disulfide') { + const newLink: Link = { sites: data.links }; + this.privateSubstance.protein.disulfideLinks.unshift(newLink); + this.emitDisulfideLinkUpdate(); + + } + } + + siteString(sites: Array): string { + + if (!sites || sites.length === 0) { + return ''; + } + if (sites.length === 1) { + return sites[0].subunitIndex + '_' + sites[0].residueIndex; + } + { + sites.sort(function (site1, site2) { + let d = site1.subunitIndex - site2.subunitIndex; + if (d === 0) { + d = site1.residueIndex - site2.residueIndex; + } + return d; + + }); + let csub = 0; + let cres = 0; + let rres = 0; + let finish = false; + let disp = ''; + for (let i = 0; i < sites.length; i++) { + + const site = sites[i]; + if (site.subunitIndex === csub && site.residueIndex === cres) { + continue; + } + finish = false; + if (site.subunitIndex === csub) { + if (site.residueIndex === cres + 1) { + if (rres === 0) { + rres = cres; + } + } else { + finish = true; + } + } else { + finish = true; + } + if (finish && csub !== 0) { + if (rres !== 0) { + disp += csub + '_' + rres + '-' + csub + '_' + cres + '; '; + } else { + disp += csub + '_' + cres + '; '; + } + rres = 0; + } + csub = site.subunitIndex; + cres = site.residueIndex; + } + if (sites.length > 0) { + if (rres !== 0) { + disp += csub + '_' + rres + '-' + csub + '_' + cres; + } else { + disp += csub + '_' + cres; + } + } + return disp; + } + } + + createSubunitDisplay(): Array { + let subunits = []; + if (this.privateSubstance.substanceClass === 'protein') { + subunits = this.privateSubstance.protein.subunits; + } else { + subunits = this.privateSubstance.nucleicAcid.subunits; + } + const t0 = performance.now(); + const subunitSequences = []; + let subunitIndex = 1; + subunits.forEach(subunit => { + const subsections = []; + let currentSections = []; + if (subunit.sequence != null && subunit.sequence.length > 0) { + for (let count = 0; count < subunit.sequence.length; count = count + 10) { + if ((count + 10) >= subunit.sequence.length) { + currentSections.push([count, subunit.sequence.length]); + if ((count + 10) % 50 !== 0) { + subsections.push(currentSections); + } + } else { + currentSections.push([count, count + 10]); + } + if ((count + 10) % 50 === 0) { + subsections.push(currentSections); + currentSections = []; + } + } + } + const thisTest: TestSequence = { + subunitIndex: subunitIndex, + subunits: [], + subsections: subsections, + subgroups: currentSections + }; + let index = 0; + const indexEnd = subunit.sequence && subunit.sequence.length || 0; + while (index < indexEnd) { + if (subunit.sequence[index]) { + const sequenceUnit: SequenceUnit = { + unitIndex: index + 1, + unitValue: subunit.sequence[index], + class: '' + }; + thisTest.subunits.push(sequenceUnit); + } + index++; + } + subunitSequences.push(thisTest); + subunitIndex++; + }); + // this.addStyle(); + const t1 = performance.now(); + const totaltime = t1 - t0; + return subunitSequences; + } + + disulfideLinks() { + + const KNOWN_DISULFIDE_PATTERNS = {}; + ('IGG4 0-1,11-12,13-31,14-15,18-19,2-26,20-21,22-23,24-25,27-28,29-30,3-4,5-16,6-17,7-8,9-10\n' + + 'IGG2 0-1,11-12,13-14,15-35,16-17,2-30,22-23,24-25,26-27,28-29,3-4,31-32,33-34,5-18,6-19,7-20,8-21,9-10\n' + + 'IGG1 0-1,11-12,13-14,15-31,18-19,2-3,20-21,22-23,24-25,27-28,29-30,4-26,5-16,6-17,7-8,9-10').split('\n').map(function (s) { + const tup = s.split('\t'); + + const list = _.chain(tup[1].split(',')).map(function (t) { + return _.map(t.split('-'), function (temp) { + return +temp - 0; + }); + }).value(); + + KNOWN_DISULFIDE_PATTERNS[tup[0]] = list; + }); + const proteinSubstance = this.privateSubstance; + const prot = proteinSubstance.protein; + const pattern = KNOWN_DISULFIDE_PATTERNS[prot.proteinSubType]; + + if (!pattern) { + alert('Unknown disulfide pattern for protein subtype:"' + prot.proteinSubType + '"'); + return; + } else { + if (!confirm('Would you like to set the disulfide pattern for:"' + prot.proteinSubType + '"')) { + return; + } + } + let ng = ''; + let og = ''; + let cg = ''; + const realList = []; + const cst = []; + + let cs = _.chain(prot.subunits).map(function (s) { + const sid = s.subunitIndex; + let i1 = 1; + + const v = _.chain(s.sequence).map(function (r) { + return { + 'i': i1++, + 'r': r + }; + }).filter(function (r) { + return r.r === 'C'; + }).map(function (r) { + return { + 'su': sid, + 'r': r.r, + 'ri': r.i + }; + }).value(); + + for (let i = 0; i < v.length; i++) { + cst.push(v[i]); + } + + return v; + }).value(); + cs = cst; + for (let i = 0; i < cs.length; i++) { + const c1 = cs[i]; + const real: any = {}; + real.subunitIndex = c1['su']; + real.residueIndex = c1['ri']; + real.display = c1['su'] + '_' + c1['ri']; + real.value = real.display; + realList.push(real); + } + const newDS = _.chain(pattern).map(function (sl) { + return [realList[sl[0]], realList[sl[1]]]; + }).map(function (s) { + return { + 'sites': s, + 'sitesShorthand': s[0].display + ';' + s[1].display + }; + }).value(); + + if (prot.glycosylation) { + if (prot.glycosylation.NGlycosylationSites) { + const s = prot.glycosylation.NGlycosylationSites; + ng = _.chain(s).map(function (s1) { + return s1.subunitIndex + '_' + s1.residueIndex; + }).value().join(';'); + } + + if (prot.glycosylation.CGlycosylationSites) { + const s = prot.glycosylation.CGlycosylationSites; + cg = _.chain(s).map(function (s1) { + return s1.subunitIndex + '_' + s1.residueIndex; + }).value().join(';'); + } + + if (prot.glycosylation.OGlycosylationSites) { + const s = prot.glycosylation.OGlycosylationSites; + og = _.chain(s).map(function (s1) { + return s1.subunitIndex + '_' + s1.residueIndex; + }).value().join(';'); + } + } + + this.privateSubstance.protein.disulfideLinks = newDS; + this.emitDisulfideLinkUpdate(); + alert('Found and added ' + newDS.length + 'sites'); + } + + + predictSites() { + function setJson(json) { + + } + + function gfinder(sn, seq) { + const re = new RegExp('N[^P][ST]', 'g'); + let xArray; + const sites = []; + + while (xArray = re.exec(seq)) { + const ri = xArray.index + 1; + sites.push({ + subunitIndex: sn, + residueIndex: ri + }); + } + + return sites; + } + + function proteinGlycFinder(proteinSubstance) { + return _.chain(proteinSubstance.protein.subunits).flatMap(function (su) { + return gfinder(su.subunitIndex, su.sequence); + }).value(); + } + + const sub = this.privateSubstance; + const gsites = proteinGlycFinder(sub); + + if (gsites.length === 0) { + alert('No potential N-Glycosylation sites found'); + + } else { + alert('Found: ' + gsites.length + ' glycosylation sites. Submit record to save changes'); + sub.protein.glycosylation.NGlycosylationSites = gsites; + setJson(sub); + } + + // gsites.$$displayString = angular.element(document.body).injector().get('siteList').siteString(gsites); + + } + +} + + + +interface DisplaySite { + type: string; + subunit: number; + residue: number; +} + +interface TestSequence { + subunitIndex?: number; + subsections?: Array; + subgroups?: Array; + subunits?: Array; +} + diff --git a/src/app/core/substance-ssg2/substance-ssg2.module.ts b/src/app/core/substance-ssg2/substance-ssg2.module.ts new file mode 100644 index 000000000..722789281 --- /dev/null +++ b/src/app/core/substance-ssg2/substance-ssg2.module.ts @@ -0,0 +1,132 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { CommonModule, formatDate } from '@angular/common'; +import { Router, Routes, RouterModule } from '@angular/router'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatSelectModule } from '@angular/material/select'; +import { MatInputModule } from '@angular/material/input'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { ScrollToModule } from '../scroll-to/scroll-to.module'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatTableModule } from '@angular/material/table'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatBadgeModule } from '@angular/material/badge'; +import { MatRadioModule } from '@angular/material/radio'; +import { ExpandDetailsModule } from '../expand-details/expand-details.module'; +// import { AccessManagerComponent } from './access-manager/access-manager.component'; +import { SubstanceSelectorModule } from '../substance-selector/substance-selector.module'; +import { MatListModule } from '@angular/material/list'; +import { FileSelectModule } from 'file-select'; +import { CvInputComponent } from '@gsrs-core/substance-form/cv-input/cv-input.component'; +import { CvDialogComponent } from '@gsrs-core/substance-form/cv-dialog/cv-dialog.component'; +import { MatButtonToggleModule } from '@angular/material/button-toggle'; +import { JsonDialogComponent } from '@gsrs-core/substance-form/json-dialog/json-dialog.component'; +import { NgxJsonViewerModule } from 'ngx-json-viewer'; +import { AuditInfoComponent } from '@gsrs-core/substance-form/audit-info/audit-info.component'; +import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; +// import { SubmitSuccessDialogComponent } from './submit-success-dialog/submit-success-dialog.component'; +import { MergeConceptDialogComponent } from '@gsrs-core/substance-form/merge-concept-dialog/merge-concept-dialog.component'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +// import { SubstanceFormComponent } from './substance-form.component'; +// import { CanActivateSubstanceForm } from './can-activate-substance-form'; +// import { CanRegisterSubstanceForm } from './can-register-substance-form'; +// import { SubstanceFormService } from '../substance-form.service'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +// import { SubstanceSsg4mService } from './substance-ssg4m-form.service'; +// import { SubstanceFormSsg4mProcessService } from './ssg4m-process/substance-form-ssg4m-process.service'; +import { SubstanceFormComponent } from '../substance-form/substance-form.component'; +import { SubstanceSsg2FormService } from './substance-ssg2-form.service'; +import { SubstanceSsg2FormComponent } from './substance-ssg2-form.component'; +import { Ssg2ManufacturingModule } from './ssg2-manufacturing/ssg2-manufacturing.module'; + +const ssg2Routes: Routes = [ + { + path: 'substances-ssg2/register', + component: SubstanceSsg2FormComponent + // canActivate: [CanRegisterSubstanceForm], + // canDeactivate: [CanDeactivateSubstanceFormGuard] + }, + { + path: 'substances-ssg2/:id/edit', + component: SubstanceSsg2FormComponent, + // canActivate: [CanRegisterSubstanceForm], + // canDeactivate: [CanDeactivateSubstanceFormGuard] + } + // , + // { + // path: 'substances-ssg4m/:id', + // component: SubstanceSsg4ManufactureFormComponent + // } +]; + +@NgModule({ + imports: [ + RouterModule.forChild(ssg2Routes), + RouterModule, + CommonModule, + ReactiveFormsModule, + FormsModule, + MatFormFieldModule, + MatMenuModule, + MatCheckboxModule, + MatButtonModule, + MatIconModule, + MatTooltipModule, + MatTabsModule, + MatDividerModule, + MatSelectModule, + MatInputModule, + MatChipsModule, + MatAutocompleteModule, + MatDialogModule, + MatTableModule, + MatExpansionModule, + MatBadgeModule, + MatRadioModule, + MatProgressBarModule, + MatProgressSpinnerModule, + MatListModule, + MatButtonToggleModule, + ExpandDetailsModule, + SubstanceSelectorModule, + ScrollToModule, + FileSelectModule, + NgxJsonViewerModule, + SubstanceImageModule, + Ssg2ManufacturingModule + ], + declarations: [ + SubstanceSsg2FormComponent, + ], + exports: [ + ], + entryComponents: [ + ] +}) + +export class SubstanceSsg2Module { + constructor(router: Router) { + ssg2Routes.forEach(route => { + router.config[0].children.push(route); + }); + } + + static forRoot(): ModuleWithProviders { + return { + ngModule: SubstanceSsg2Module, + providers: [ + // SubstanceSsg2FormService + ] + }; + } +} + + diff --git a/src/app/core/substance-ssg4m/model/substance-ssg4m.model.ts b/src/app/core/substance-ssg4m/model/substance-ssg4m.model.ts new file mode 100644 index 000000000..5555d42fa --- /dev/null +++ b/src/app/core/substance-ssg4m/model/substance-ssg4m.model.ts @@ -0,0 +1,33 @@ + +export interface Ssg4mSyntheticPathway { + createdBy?: string; + modifiedBy?: string; + creationDate?: number; + lastModifiedDate?: number; + synthPathwaySkey?: number; + synthPathwayId?: string; + versionNumber?: number; + appCenterCd?: string; + appType?: string; + appNumber?: number; + versionType?: string; + versionStatusCd?: string; + sbmsnDataText?: string; + printSbstncUuid?: string; + printSbstncPrfrdNm?: string; + sbmsnImage?: string; + ssg4mSyntheticPathwayDetailsList?: Array; +} + +export interface Ssg4mSyntheticPathwayDetail { + createdBy?: string; + modifiedBy?: string; + creationDate?: number; + lastModifiedDate?: number; + synthPathwayDetailSkey?: number; + sbstncUuid?: string; + sbstncPfrdNm?: string; + sbstncReactnSectNm?: string; + sbstncRoleNm?: string; +} + diff --git a/src/app/core/substance-ssg4m/ssg4m-critical-parameter/ssg4m-critical-parameter-form.component.html b/src/app/core/substance-ssg4m/ssg4m-critical-parameter/ssg4m-critical-parameter-form.component.html new file mode 100644 index 000000000..76197f9a5 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-critical-parameter/ssg4m-critical-parameter-form.component.html @@ -0,0 +1,282 @@ +
    +
    +
    + +
    +
    + Deleted  + +
    + +
    +
    + +
    +
    + +
    + + + + + + + + + + + + + + + + + +
    + + Units + + Clear selection + + {{unit.display}} + + {{privateSubstanceAmount.units}} (not in CV) + Other (New Value) + + + + + +
    +
    + +
    + + + +
    + + +
    + + + Amount Type + + Clear selection + + {{type.display}} + + {{privateSubstanceAmount.type}} (not in CV) + Other (New Value) + + +
    + +
    +
    + +
    + + + + + + + + + + + +
    + + +
    +
    +

    + Parameters + +

    + + +
    + + + {{parameter.name}} + + +   + {{parameter.value.type}} + + +  - +   + {{parameter.value.average}} +   + {{parameter.value.units}} + + + +   + - + +   + [ + + > + + + < + + {{parameter.value.low}} + + +  to  + + + {{parameter.value.high}} + + ] + +   + {{parameter.value.units}} +   + (average) + + + +   + (average) + + +  - +   + [ + + > + + + < + + {{parameter.value.lowLimit}} + + +  to  + + + {{parameter.value.highLimit}} + + ] +  (limits) + + +  -  + {{parameter.value.nonNumericValue}} + + + +
    +
    + +
    +
    + + + + + + + + + + +
    \ No newline at end of file diff --git a/src/app/core/substance-ssg4m/ssg4m-critical-parameter/ssg4m-critical-parameter-form.component.scss b/src/app/core/substance-ssg4m/ssg4m-critical-parameter/ssg4m-critical-parameter-form.component.scss new file mode 100644 index 000000000..0a44f8cb4 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-critical-parameter/ssg4m-critical-parameter-form.component.scss @@ -0,0 +1,169 @@ +.form-container { + padding: 5px 10px 12px 10px; + position: relative; + /*border: 1px solid var(--pale-border-color-rgb);*/ +} + +.notification-backdrop { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: flex; + z-index: 10; + background-color: var(--notif-backdrop-bg-color); + justify-content: center; + align-items: center; + font-size: 30px; + font-weight: bold; + color: var(--notif-backdrop-color); +} + +.form-row { + display: flex; + width: 100%; + + .delete-container { + padding: 0 10px 8px 0; + } + + .col-5-1 { + width: calc((100% - 20px * 4) / 5); // 5 column + margin-right: 20px; + } + + .col-4-1 { + width: calc((100% - 20px * 3) / 4); // 4 column + margin-right: 20px; + } + + .col-3-1 { + width: calc((100% - 20px * 2) / 3); // 3 column + margin-right: 20px; + } + + .col-2-1 { + width: calc((100% - 20px * 1) / 2); // 2 column + margin-right: 20px; + } +} + +.width130px { + width: 130px; +} + +.width180px { + width: 180px; +} + +.references-container { + width: 100%; +} + +.related-substance { + width: 300px; + max-width: 300px; +} + +::ng-deep .selected-substance { + display: flex; + flex-direction: column; + text-align: left !important; + position: relative; + img { + width: 100%; + height: auto; + display: block; + max-width: 220px; + } +} + +.divflex { + display: flex; +} + +.bordergray { + border: 1px solid var(--regular-grey-color);; +} + +.margintop10px { + margin-top: 10px; +} + +.marginleft50px { + margin-left: 50px; +} + +.padleftneg10 { + padding-left: -10px; +} + +.padleft15px { + padding-left: 15px; +} + +.padleft50px { + padding-left: 50px; +} + +.padright20px { + padding-right: 20px; +} + +.padtop10px { + padding-top: 10px; +} + +.padbottom10px { + padding-bottom: 10px; +} + +.height40px { + height: 40px; +} + +.font11px { + font-size: 11px; +} + +fieldset.border { + border: solid 1px rgb(33, 136, 20) !important; + padding: 0 10px 10px 10px; + border-bottom: none; + border-radius: 8px; + min-width: 0; /* override the default value of min-content */ + -webkit-box-shadow: 2px 3px 3px 1px rgba(110,104,110,0.64); + -moz-box-shadow: 2px 3px 5px 1px rgba(110,104,110,0.64); + box-shadow: 2px 2px 3px 1px rgba(110,104,110,0.64); +} + +legend.border { + width: auto !important; + border: none; + border-bottom: none; + font-size: 15px; + color: var(--legend-green-border-color); + font-family: Verdana; + font-weight: bold; + margin-bottom: 10px; +} + +.button-delete { + display: flex; + justify-content: flex-end; + margin-top: -15px; +} + +.colorred { + color: red; +} + +hr { + border: none; + border-top: 1px solid rgba(75, 80, 75, 0.781); + color: #333; + overflow: visible; + text-align: center; + height: 5px; +} diff --git a/src/app/core/substance-ssg4m/ssg4m-critical-parameter/ssg4m-critical-parameter-form.component.spec.ts b/src/app/core/substance-ssg4m/ssg4m-critical-parameter/ssg4m-critical-parameter-form.component.spec.ts new file mode 100644 index 000000000..52b13d967 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-critical-parameter/ssg4m-critical-parameter-form.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Ssg4mCriticalParameterFormComponent } from './ssg4m-critical-parameter-form.component'; + +describe('Ssg4mCriticalParameterComponent', () => { + let component: Ssg4mCriticalParameterFormComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ Ssg4mCriticalParameterFormComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(Ssg4mCriticalParameterFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-ssg4m/ssg4m-critical-parameter/ssg4m-critical-parameter-form.component.ts b/src/app/core/substance-ssg4m/ssg4m-critical-parameter/ssg4m-critical-parameter-form.component.ts new file mode 100644 index 000000000..1e94eb040 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-critical-parameter/ssg4m-critical-parameter-form.component.ts @@ -0,0 +1,323 @@ +import { Component, OnInit, OnDestroy, Input } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { Subscription } from 'rxjs'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { FormControl, Validators } from '@angular/forms'; +import { ConfigService } from '@gsrs-core/config/config.service'; +import { SubstanceFormService } from '@gsrs-core/substance-form/substance-form.service'; +import { ControlledVocabularyService } from '../../controlled-vocabulary/controlled-vocabulary.service'; +import { SubstanceFormPropertiesService } from '@gsrs-core/substance-form/properties/substance-form-properties.service'; +import { SubstanceDetail } from '@gsrs-core/substance/substance.model'; +import { VocabularyTerm } from '../../controlled-vocabulary/vocabulary.model'; +import { SubstanceRelated, SubstanceSummary, SubstanceProperty, SubstanceParameter } from '@gsrs-core/substance'; +import { PropertyParameterDialogComponent } from '@gsrs-core/substance-form/property-parameter-dialog/property-parameter-dialog.component'; +import { SpecifiedSubstanceG4mCriticalParameter, SubstanceAmount } from '@gsrs-core/substance/substance.model'; +import { ConfirmDialogComponent } from '../../../fda/confirm-dialog/confirm-dialog.component'; + +@Component({ + selector: 'app-ssg4m-critical-parameter-form', + templateUrl: './ssg4m-critical-parameter-form.component.html', + styleUrls: ['./ssg4m-critical-parameter-form.component.scss'] +}) +export class Ssg4mCriticalParameterFormComponent implements OnInit, OnDestroy { + + @Input() criticalParameterIndex: number; + privateProcessIndex: number; + privateSiteIndex: number; + privateStageIndex: number; + public configSettingsDisplay = {}; + privateShowAdvancedSettings: boolean; + privateCriticalParameter: SpecifiedSubstanceG4mCriticalParameter; + properties: Array; + relatedSubstanceUuid: string; + substance: SubstanceDetail; + _nonNumeric: string; + private overlayContainer: HTMLElement; + private subscriptions: Array = []; + privateSubstanceAmount: SubstanceAmount; + amountTypeList: Array = []; + amountUnitList: Array = []; + typeControl = new FormControl(''); + averageControl = new FormControl(''); + lowControl = new FormControl(''); + highControl = new FormControl(''); + lowLimitControl = new FormControl(''); + highLimitControl = new FormControl(''); + unitsControl = new FormControl(''); + nonNumericValueControl = new FormControl(''); + + constructor( + private substanceFormService: SubstanceFormService, + private substanceFormPropertiesService: SubstanceFormPropertiesService, + private cvService: ControlledVocabularyService, + public configService: ConfigService, + private overlayContainerService: OverlayContainer, + private dialog: MatDialog + ) { } + + @Input() + set criticalParameter(criticalParameter: SpecifiedSubstanceG4mCriticalParameter) { + this.privateCriticalParameter = criticalParameter; + } + + get criticalParameter(): SpecifiedSubstanceG4mCriticalParameter { + return this.privateCriticalParameter; + } + + @Input() + set processIndex(processIndex: number) { + this.privateProcessIndex = processIndex; + } + + get processIndex(): number { + return this.privateProcessIndex; + } + + @Input() + set siteIndex(siteIndex: number) { + this.privateSiteIndex = siteIndex; + } + + get siteIndex(): number { + return this.privateSiteIndex; + } + + @Input() + set stageIndex(stageIndex: number) { + this.privateStageIndex = stageIndex; + } + + get stageIndex(): number { + return this.privateStageIndex; + } + + @Input() + set showAdvancedSettings(showAdvancedSettings: boolean) { + this.privateShowAdvancedSettings = showAdvancedSettings; + // Get Config Settins from config file + this.getConfigSettings(); + } + + get showAdvancedSettings(): boolean { + return this.privateShowAdvancedSettings; + } + + ngOnInit(): void { + const subscription = this.substanceFormService.substance.subscribe(substance => { + this.substance = substance; + }); + this.subscriptions.push(subscription); + + this.overlayContainer = this.overlayContainerService.getContainerElement(); + if (!this.criticalParameter.value) { + this.criticalParameter.value = {}; + } + this.privateSubstanceAmount = this.criticalParameter.value; + this.setSubstanceAmount(); + this.getVocabularies(); + + // Load Referenced Substance Name + if (this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex].criticalParameters[this.criticalParameterIndex].referencedSubstance) { + let referencedSubstance = this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex].criticalParameters[this.criticalParameterIndex].referencedSubstance; + this.relatedSubstanceUuid = referencedSubstance.refuuid; + } + } + + ngOnDestroy(): void { + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + + getConfigSettings(): void { + // Get SSG4 Config Settings from config.json file to show and hide fields in the form + let configSsg4Form: any; + configSsg4Form = this.configService.configData && this.configService.configData.ssg4Form || null; + // Get 'criteriaParameter' json values from config + const confSettings = configSsg4Form.settingsDisplay.criteriaParameter; + Object.keys(confSettings).forEach(key => { + if (confSettings[key] != null) { + if (confSettings[key] === 'simple') { + this.configSettingsDisplay[key] = true; + } else if (confSettings[key] === 'advanced') { + if (this.privateShowAdvancedSettings === true) { + this.configSettingsDisplay[key] = true; + } else { + this.configSettingsDisplay[key] = false; + } + } else if (confSettings[key] === 'removed') { + this.configSettingsDisplay[key] = false; + } + } + }); + } + + confirmDeleteCriticalParameter() { + const dialogRef = this.dialog.open(ConfirmDialogComponent, { + data: { message: 'Are you sure you want to delele Critical Parameter ' + (this.criticalParameterIndex + 1) + ' for Step ' + (this.stageIndex + 1) + ' for Site ' + (this.siteIndex + 1) + ' for Process ' + (this.processIndex + 1) + '?' } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result && result === true) { + this.deleteCriticalParameter(); + } + }); + } + + deleteCriticalParameter(): void { + this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex].criticalParameters.splice(this.criticalParameterIndex, 1); + } + + relatedSubstanceUpdated(substance: SubstanceSummary): void { + if (substance != null) { + const relatedSubstance: SubstanceRelated = { + refPname: substance._name, + name: substance._name, + refuuid: substance.uuid, + substanceClass: 'reference', + approvalID: substance.approvalID + }; + this.privateCriticalParameter.referencedSubstance = relatedSubstance; + } + } + + openPropertyParameter(parameter?: SubstanceParameter): void { + let isNew: boolean; + if (parameter == null) { + isNew = true; + parameter = { value: {} }; + } + const parameterCopyString = JSON.stringify(parameter); + + const dialogRef = this.dialog.open(PropertyParameterDialogComponent, { + data: JSON.parse(parameterCopyString), + width: '1200px' + }); + this.overlayContainer.style.zIndex = '1002'; + + dialogRef.afterClosed().subscribe(newParameter => { + this.overlayContainer.style.zIndex = null; + if (newParameter != null) { + if (this.criticalParameter.parameters == null) { + this.criticalParameter.parameters = []; + } + if (isNew) { + this.criticalParameter.parameters.unshift(newParameter); + } else { + Object.keys(newParameter).forEach(key => { + parameter[key] = newParameter[key]; + }); + } + } + }); + } + + deleteParameter(id: number): void { + this.criticalParameter.parameters.splice(id, 1); + } + + setSubstanceAmount() { + this.typeControl.setValue(this.privateSubstanceAmount.type); + this.typeControl.valueChanges.subscribe(value => { + this.privateSubstanceAmount.type = value; + }); + this.averageControl.setValue(this.privateSubstanceAmount.average); + this.averageControl.valueChanges.subscribe(value => { + if (value === null) { + this.averageControl.setValue(''); + } else if (value.length === 1 && value.match(/[a-z]/i)) { + this.averageControl.setValue(''); + } + this.privateSubstanceAmount.average = value; + }); + this.lowControl.setValue(this.privateSubstanceAmount.low); + this.lowControl.valueChanges.subscribe(value => { + if (value === null) { + this.lowControl.setValue(''); + } else if (value.length === 1 && value.match(/[a-z]/i)) { + this.lowControl.setValue(''); + } + this.privateSubstanceAmount.low = value; + }); + this.highControl.setValue(this.privateSubstanceAmount.high); + this.highControl.valueChanges.subscribe(value => { + if (value === null) { + this.highControl.setValue(''); + } else if (value.length === 1 && value.match(/[a-z]/i)) { + this.highControl.setValue(''); + } + this.privateSubstanceAmount.high = value; + }); + this.lowLimitControl.setValue(this.privateSubstanceAmount.lowLimit); + this.lowLimitControl.valueChanges.subscribe(value => { + if (value === null) { + this.lowLimitControl.setValue(''); + } else if (value.length === 1 && value.match(/[a-z]/i)) { + this.lowLimitControl.setValue(''); + } + this.privateSubstanceAmount.lowLimit = value; + }); + this.highLimitControl.setValue(this.privateSubstanceAmount.highLimit); + this.highLimitControl.valueChanges.subscribe(value => { + if (value === null) { + this.highLimitControl.setValue(''); + } else if (value.length === 1 && value.match(/[a-z]/i)) { + this.highLimitControl.setValue(''); + } + this.privateSubstanceAmount.highLimit = value; + }); + this.unitsControl.setValue(this.privateSubstanceAmount.units); + this.unitsControl.valueChanges.subscribe(value => { + this.privateSubstanceAmount.units = value; + }); + this.nonNumericValueControl.setValue(this.privateSubstanceAmount.nonNumericValue); + this.nonNumericValueControl.valueChanges.subscribe(value => { + this.privateSubstanceAmount.nonNumericValue = value; + }); + } + + get substanceAmount(): SubstanceAmount { + return this.privateSubstanceAmount; + } + + updateAccess(access: Array): void { + this.privateSubstanceAmount.access = access; + this.substanceAmount.access = access; + } + + updateType(event: any) { + setTimeout(() => { + this.typeControl.setValue(event.value); + }); + this.privateSubstanceAmount.type = event.value; + } + + updateUnits(event: any) { + setTimeout(() => { + this.unitsControl.setValue(event.value); + }); + this.privateSubstanceAmount.units = event.value; + } + + updatePropertyName(event: any): void { + this.criticalParameter.name = event; + + if (event && event === 'Temperature') { + // if Temperature is selected, set the Unit value to C + setTimeout(() => { + this.unitsControl.setValue('°C'); + }); + } + } + + getVocabularies(): void { + this.cvService.getDomainVocabulary('AMOUNT_TYPE', 'AMOUNT_UNIT').subscribe(response => { + this.amountTypeList = response['AMOUNT_TYPE'].list; + this.amountUnitList = response['AMOUNT_UNIT'].list; + }); + } + + inCV(vocab: Array, property: string) { + return vocab.some(r => property === r.value); + } +} diff --git a/src/app/core/substance-ssg4m/ssg4m-critical-parameter/ssg4m-critical-parameter.module.ts b/src/app/core/substance-ssg4m/ssg4m-critical-parameter/ssg4m-critical-parameter.module.ts new file mode 100644 index 000000000..878dc4367 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-critical-parameter/ssg4m-critical-parameter.module.ts @@ -0,0 +1,35 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatIconModule } from '@angular/material/icon'; +import { MatButtonModule } from '@angular/material/button'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatSelectModule } from '@angular/material/select'; +import { SubstanceFormModule } from '../../substance-form/substance-form.module'; +import { SubstanceSelectorModule } from '@gsrs-core/substance-selector/substance-selector.module'; +import { Ssg4mCriticalParameterFormComponent } from '../ssg4m-critical-parameter/ssg4m-critical-parameter-form.component'; + +@NgModule({ + imports: [ + ReactiveFormsModule, + MatFormFieldModule, + FormsModule, + MatInputModule, + MatButtonModule, + MatIconModule, + MatTooltipModule, + SubstanceFormModule, + MatSelectModule, + SubstanceSelectorModule, + CommonModule + ], + declarations: [ + Ssg4mCriticalParameterFormComponent + ], + exports: [ + Ssg4mCriticalParameterFormComponent + ] +}) +export class Ssg4mCriticalParameterModule { } diff --git a/src/app/core/substance-ssg4m/ssg4m-process/ssg4m-process-form.component.html b/src/app/core/substance-ssg4m/ssg4m-process/ssg4m-process-form.component.html new file mode 100644 index 000000000..33936edfc --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-process/ssg4m-process-form.component.html @@ -0,0 +1,142 @@ +
    +
    +
    + Deleted  + +
    + + +
    + + + + + +
    + +
    + + + + + + + + + + + + +
    + +
    +
    + + + + + + + +
    +
    + + + + +
    + +
    +
    + Site  {{siteIndex + 1}} of + {{process.sites.length}} + + + + + + +
    + + +
    +
    +
    + + +
    Click on Show All Parameters checkbox to add a Site
    +
    + + +
    + + + + +
    +
    +
    \ No newline at end of file diff --git a/src/app/core/substance-ssg4m/ssg4m-process/ssg4m-process-form.component.scss b/src/app/core/substance-ssg4m/ssg4m-process/ssg4m-process-form.component.scss new file mode 100644 index 000000000..7fcd99a11 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-process/ssg4m-process-form.component.scss @@ -0,0 +1,198 @@ +/* awtodo check this later */ +.process-form-container { + padding: 5px 10px 0px 10px; + position: relative; +} + +.notification-backdrop { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: flex; + z-index: 10; + background-color: var(--notif-backdrop-bg-color); + justify-content: center; + align-items: center; + font-size: 30px; + font-weight: bold; + color: var(--notif-backdrop-color); +} + +.form-row { + display: flex; + width: 100%; + + .delete-container { + padding: 0 10px 8px 0; + } + + .col-4-1 { + width: calc((100% - 20px * 3) / 4); // 4 column + margin-right: 20px; + } + + .col-3-1 { + width: calc((100% - 20px * 2) / 3); // 3 column + margin-right: 20px; + } + + .col-2-1 { + width: calc((100% - 20px * 1) / 2); // 2 column + margin-right: 20px; + } +} + +/* +.form-row { + display: flex; + justify-content: space-between; + align-items: flex-end; + width: 100%; + + .delete-container { + padding: 0 10px 8px 0; + } + + .process { + flex-grow: 1; + padding-right: 15px; + border:1px solid green; + } +} +*/ + +.references-container { + width: 100%; +} + +hr { + border: none; + border-top: 3px dotted var(--regular-green-color); + color: var(--hr-color); + overflow: visible; + text-align: center; + height: 5px; +} + +.divflex { + display: flex; +} + +.divflexright { + display: flex; + justify-content: flex-end; +} + +.bordergray { + border: 1px solid var(--regular-grey-color); +} + +.borderorange { + border: 1px solid var(--regular-orangered-color); +} + +.fontsize17px { + font-size: 17px; +} + +.fontsize18px { + font-size: 18px; +} + +.margintop10px { + margin-top: 10px; +} + +.margintop10px { + margin-top: 10px; +} + +.marginleft50px { + margin-left: 50px; +} + +.marginbottom25px { + margin-bottom: 25px; +} + +.colorgreen { + color: var(--regular-green-color); +} + +.divflexright { + display: flex; + justify-content: flex-end; +} + +.bordernone { + /*border: none !important;*/ + border: solid 2px rgb(99, 99, 91) !important; +} + +.button-add { + z-index: 1; + position: relative; + top: 18px; + margin-right: 25px; + background-color: #FFFFFF; + border: 1px solid #007CBA; +} + +.button-delete { + z-index: 1; + position: absolute; + top: -40px; + margin-right: 20px; + color: red; + background-color: #FFFFFF; + border: 1px solid red; +} + +.button-insert-before { + z-index: 1; + position: absolute; + top: -40px; + margin-right: 400px; + color: #007CBA; + background-color: #FFFFFF; + border: 1px solid #007CBA; +} + +.button-insert-after { + z-index: 1; + position: absolute; + top: -40px; + margin-right: 200px; + color: #007CBA; + background-color: #FFFFFF; + border: 1px solid #007CBA; +} + +fieldset.border { + /*border: solid 2px rgb(99, 99, 91) !important;*/ + border: none; + padding: 0 10px 0px 10px; + border-bottom: none; + margin-bottom: 30px; + /*border-radius: 8px;*/ + /*margin-bottom: 10px;*/ + min-width: 0; /* override the default value of min-content */ + /*-webkit-box-shadow: 2px 3px 3px 1px rgba(110,104,110,0.64); + -moz-box-shadow: 2px 3px 5px 1px rgba(110,104,110,0.64); + box-shadow: 2px 2px 3px 1px rgba(110,104,110,0.64);*/ +} + +legend.border { + width: auto !important; + border: none; + border-bottom: none; + font-size: 15px; + /* FDA COLOR BLUE CODE Hex: #007CBA */ + color: #007CBA; + font-family: Verdana; + font-weight: bold; + margin-bottom: 10px; + margin-left: 30px; +} diff --git a/src/app/core/substance-ssg4m/ssg4m-process/ssg4m-process-form.component.ts b/src/app/core/substance-ssg4m/ssg4m-process/ssg4m-process-form.component.ts new file mode 100644 index 000000000..cfc69792e --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-process/ssg4m-process-form.component.ts @@ -0,0 +1,183 @@ +import { Component, OnInit, AfterViewInit, Input, OnDestroy, Output, EventEmitter } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { MatSelectChange } from '@angular/material/select'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { Subscription } from 'rxjs'; +import { take } from 'rxjs/operators'; +import { ScrollToService } from '../../scroll-to/scroll-to.service'; +import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; +import { ControlledVocabularyService } from '../../controlled-vocabulary/controlled-vocabulary.service'; +import { VocabularyTerm } from '../../controlled-vocabulary/vocabulary.model'; +import { ConfigService } from '@gsrs-core/config'; +import { SubstanceService } from '../../substance/substance.service'; +import { SubstanceFormBase } from '../../substance-form/base-classes/substance-form-base'; +import { SubstanceFormService } from '@gsrs-core/substance-form/substance-form.service'; +import { SubstanceDetail } from '@gsrs-core/substance/substance.model'; +import { SpecifiedSubstanceG4mProcess, SubstanceRelated } from '../../substance/substance.model'; +import { SubstanceFormSsg4mProcessService } from './substance-form-ssg4m-process.service'; +import { SubstanceFormSsg4mSitesService } from '../ssg4m-sites/substance-form-ssg4m-sites.service'; +import { ConfirmDialogComponent } from '../../../fda/confirm-dialog/confirm-dialog.component'; + +@Component({ + selector: 'app-ssg4m-process-form', + templateUrl: './ssg4m-process-form.component.html', + styleUrls: ['./ssg4m-process-form.component.scss'] +}) +export class Ssg4mProcessFormComponent implements OnInit, OnDestroy { + @Output() tabSelectedIndexOut = new EventEmitter(); + + tabSelectedIndex: number; + private privateShowAdvancedSettings: boolean; + public configSettingsDisplay = {}; + private privateProcessIndex: number; + private privateTabSelectedView: string; + private privateProcess: SpecifiedSubstanceG4mProcess; + parent: SubstanceRelated; + private substance: SubstanceDetail; + private overlayContainer: HTMLElement; + private subscriptions: Array = []; + + constructor( + private substanceFormSsg4mProcessService: SubstanceFormSsg4mProcessService, + private substanceFormSsg4mSitesService: SubstanceFormSsg4mSitesService, + private substanceFormService: SubstanceFormService, + private configService: ConfigService, + public gaService: GoogleAnalyticsService, + public cvService: ControlledVocabularyService, + private overlayContainerService: OverlayContainer, + private scrollToService: ScrollToService, + private dialog: MatDialog + ) { + } + + ngAfterViewInit(): void { + } + + @Input() + set process(process: SpecifiedSubstanceG4mProcess) { + this.privateProcess = process; + } + + get process(): SpecifiedSubstanceG4mProcess { + return this.privateProcess; + } + + @Input() + set processIndex(processIndex: number) { + this.privateProcessIndex = processIndex; + // Set the Process Name + const prevProcessName = 'Process ' + (processIndex + 1).toString(); + const nextProcessName = 'Process ' + (processIndex).toString(); + + if (this.privateProcess.processName && (this.privateProcess.processName === prevProcessName || this.privateProcess.processName === nextProcessName)) { + this.privateProcess.processName = 'Process ' + (this.processIndex + 1); + } + } + + get processIndex(): number { + return this.privateProcessIndex; + } + + @Input() + set showAdvancedSettings(showAdvancedSettings: boolean) { + this.privateShowAdvancedSettings = showAdvancedSettings; + // Get Config Settins from config file + this.getConfigSettings(); + } + + get showAdvancedSettings(): boolean { + return this.privateShowAdvancedSettings; + } + + @Input() + set tabSelectedView(tabSelectedView: string) { + this.privateTabSelectedView = tabSelectedView; + } + + get tabSelectedView(): string { + return this.privateTabSelectedView; + } + + ngOnInit() { + this.overlayContainer = this.overlayContainerService.getContainerElement(); + const subscription = this.substanceFormService.substance.subscribe(substance => { + this.substance = substance; + }); + this.subscriptions.push(subscription); + } + + ngOnDestroy(): void { + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + + getConfigSettings(): void { + // Get SSG4 Config Settings from config.json file to show and hide fields in the form + let configSsg4Form: any; + configSsg4Form = this.configService.configData && this.configService.configData.ssg4Form || null; + // Get 'process' json values from config + const confSettings = configSsg4Form.settingsDisplay.process; + Object.keys(confSettings).forEach(key => { + if (confSettings[key] != null) { + if (confSettings[key] === 'simple') { + this.configSettingsDisplay[key] = true; + } else if (confSettings[key] === 'advanced') { + if (this.privateShowAdvancedSettings === true) { + this.configSettingsDisplay[key] = true; + } else { + this.configSettingsDisplay[key] = false; + } + } else if (confSettings[key] === 'removed') { + this.configSettingsDisplay[key] = false; + } + } + }); + } + + updateAccess() { + + } + + insertProcess(processIndex: number, insertDirection?: string): void { + this.substanceFormSsg4mProcessService.insertProcess(processIndex, insertDirection); + setTimeout(() => { + this.scrollToService.scrollToElement(`substance-process-0`, 'center'); + }); + } + + addProcess(): void { + this.substanceFormSsg4mProcessService.addProcess(); + setTimeout(() => { + this.scrollToService.scrollToElement(`substance-process-0`, 'center'); + }); + } + + addSite() { + this.substanceFormSsg4mSitesService.addSite(this.processIndex); + setTimeout(() => { + this.scrollToService.scrollToElement(`substance-process-site-0`, 'center'); + }); + } + + confirmDeleteProcess() { + const dialogRef = this.dialog.open(ConfirmDialogComponent, { + data: { message: 'Are you sure you want to delele Process ' + (this.processIndex + 1) + '?' } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result && result === true) { + this.deleteProcess(); + } + }); + } + + deleteProcess() { + this.substanceFormSsg4mProcessService.deleteProcess(this.privateProcess, this.processIndex); + } + + tabSelectedIndexOutChange(index: number) { + this.tabSelectedIndexOut.emit(index); + } +} + diff --git a/src/app/core/substance-ssg4m/ssg4m-process/substance-form-ssg4m-process-card.component.html b/src/app/core/substance-ssg4m/ssg4m-process/substance-form-ssg4m-process-card.component.html new file mode 100644 index 000000000..22a5b4ef7 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-process/substance-form-ssg4m-process-card.component.html @@ -0,0 +1,89 @@ + + + + +
    + + +
    + + Show All Parameters + +
    + + +
    + + + + +
    +
    + Process  {{processIndex + 1}} of + {{paged.length}} + + + + + +
    + + +
    +
    +
    + + +
    + +
    +

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

    +     + +
    + +
    + +
    \ No newline at end of file diff --git a/src/app/core/substance-ssg4m/ssg4m-process/substance-form-ssg4m-process-card.component.scss b/src/app/core/substance-ssg4m/ssg4m-process/substance-form-ssg4m-process-card.component.scss new file mode 100644 index 000000000..1f8a3f9ff --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-process/substance-form-ssg4m-process-card.component.scss @@ -0,0 +1,168 @@ +/* awtodo come back */ +.bordergray { + border: 1px border var(--regular-grey-color); +} + +.mat-divider.mat-divider-inset { + margin-left: 0; +} + +.mat-divider { + border-top-color: var(--text-color); +} + +.process { + margin-bottom: 60px; + + &:nth-child(odd) { + background-color: var(--nth-child-color-2); + + ::ng-deep { + + .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { + background-color: var(--nth-child-color-3); + } + } + } + + &:nth-child(even) { + ::ng-deep { + + .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { + background-color: var(--nth-child-color-1); + } + } + } + + ::ng-deep { + .mat-expansion-panel, .mat-table, textarea { + background-color: var(--regular-transparent-color); + } + } +} + +.search { + width: 400px; + max-width: 100%; +} + +.paddingleft800px { + padding-left: 800px; +} + +.divflex { + display: flex; +} + +.divflexright { + display: flex; + justify-content: flex-end; + padding-top: 10px; +} + +.top25px { + top: 25px; +} + +.button-add { + z-index: 1; + position: relative; + /*top: 25px;*/ + margin-right: 25px; + background-color: #FFFFFF; + border: 1px solid #007CBA; +} + +fieldset.border { + border: solid 2px var(--fieldset-blue-border-color) !important; + padding: 0 10px 10px 10px; + border-bottom: none; + /*border-radius: 8px;*/ + margin-top: 20px; + min-width: 0; /* override the default value of min-content */ +/*-webkit-box-shadow: 2px 3px 3px 1px var(--fieldset-box-shadow-color); + -moz-box-shadow: 2px 3px 5px 1px var(--fieldset-box-shadow-color); + box-shadow: 2px 2px 3px 1px var(--fieldset-box-shadow-color);*/ +} + +legend.border { + width: auto !important; + border: none; + border-bottom: none; + font-size: 15px; + color: var(--legend-blue-border-color-2); + font-family: Verdana; + font-weight: bold; + margin-bottom: 10px; + margin-left: 30px; +} + +:host ::ng-deep .mat-tab-list .mat-tab-label { + /* + border-width: 1px; + border-style: solid; + border-color: black;*/ + /*color: black;*/ + /*background-color: rgb(247, 241, 241);*/ + /*border-right: 1px solid rgb(182, 177, 177);*/ + min-width: 120px; + max-width: 120px; + text-align: top; + margin-bottom: 10px; + /* FDA COLOR BLUE CODE Hex: #007CBA */ + color: #007CBA; + opacity: 1 !important; + /* + margin-left: 5px; + border-radius: 10px 10px 0px 0px; + flex-wrap: wrap!important; + min-width: 120px; + max-width: 120px; + height: 35px;*/ +} + +:host ::ng-deep .mat-tab-label-content { + margin-top: -5px; +} +/* +::ng-deep .mat-tab-list .mat-tab-labels .mat-tab-label-active { + color: red; + background-color: green; +} +*/ + +:host ::ng-deep .mat-tab-header { + margin-top: 10px; + border-bottom: 2px solid rgb(148, 149, 163); +} + +:host ::ng-deep .mat-tab-list .mat-tab-labels .mat-tab-label-active { + background: url("../../assets/images/home/tab_active.png"); + color: #FFFFFF; + padding-top: -10px; + /*font-weight: bold;*/ + /*background-color: #FFFFFF;*/ + /*background-image: url("http://localhost:8081/ginas/app/beta/assets/images/home/tab_active.png");*/ + /*background-color: rgb(20, 76, 189);*/ + /* + color: rgb(248, 250, 248); + background-color: rgb(44, 70, 141); + + opacity: 1; + */ +} + +:host ::ng-deep .mat-ink-bar { + /*background-color: var(--primary-color, --royal-blue-color) !important; + height: 3px;*/ + display: none !important; +} + +/* Tyler trying to make the sites disappear by default */ +fieldset.border.minimize { + border: none !important; + padding: 0px !important; + box-shadow: none !important; + margin-top: 0px !important; +} + diff --git a/src/app/core/substance-ssg4m/ssg4m-process/substance-form-ssg4m-process-card.component.spec.ts b/src/app/core/substance-ssg4m/ssg4m-process/substance-form-ssg4m-process-card.component.spec.ts new file mode 100644 index 000000000..37941923b --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-process/substance-form-ssg4m-process-card.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SubstanceFormSsg4mProcessCardComponent } from './substance-form-ssg4m-process-card.component'; + +describe('SubstanceFormSsg4mProcessCardComponent', () => { + let component: SubstanceFormSsg4mProcessCardComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ SubstanceFormSsg4mProcessCardComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(SubstanceFormSsg4mProcessCardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-ssg4m/ssg4m-process/substance-form-ssg4m-process-card.component.ts b/src/app/core/substance-ssg4m/ssg4m-process/substance-form-ssg4m-process-card.component.ts new file mode 100644 index 000000000..edd3d1d05 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-process/substance-form-ssg4m-process-card.component.ts @@ -0,0 +1,260 @@ +import { Component, OnInit, AfterViewInit, OnDestroy, Input } from '@angular/core'; +import { ActivatedRoute, Router, RouterEvent, NavigationStart, NavigationEnd } from '@angular/router'; +import { ScrollToService } from '../../scroll-to/scroll-to.service'; +import { Subscription } from 'rxjs'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { MatDialog } from '@angular/material/dialog'; +import { HttpClient, HttpParams } from '@angular/common/http'; +import { GoogleAnalyticsService } from '../../google-analytics/google-analytics.service'; +import { SubstanceCardBaseFilteredList, SubstanceCardBaseList } from '../../substance-form/base-classes/substance-form-base-filtered-list'; +import { SubstanceFormService } from '../../substance-form/substance-form.service'; +import { SubstanceFormSsg4mProcessService } from './substance-form-ssg4m-process.service'; +import { SubstanceFormSsg4mSitesService } from '../ssg4m-sites/substance-form-ssg4m-sites.service'; +import { SpecifiedSubstanceG4mProcess } from '@gsrs-core/substance/substance.model'; +import { ConfigService } from '@gsrs-core/config/config.service'; +import { StructureImageModalComponent, StructureService } from '@gsrs-core/structure'; +import { Ssg4mStepViewDialogComponent } from '../ssg4m-step-view-dialog/ssg4m-step-view-dialog.component'; + +@Component({ + selector: 'app-substance-form-ssg4m-process-card', + templateUrl: './substance-form-ssg4m-process-card.component.html', + styleUrls: ['./substance-form-ssg4m-process-card.component.scss'] +}) + +export class SubstanceFormSsg4mProcessCardComponent extends SubstanceCardBaseFilteredList + implements OnInit, AfterViewInit, OnDestroy, SubstanceCardBaseList { + + process: Array; + private subscriptions: Array = []; + showAdvancedSettings = false; + tabSelectedView = 'Form View'; + showView = 'form'; + tabSelectedIndex = 0; + tabTitle = ['form', 'step', 'scheme']; + private overlayContainer: HTMLElement; + + constructor( + private substanceFormSsg4mProcessService: SubstanceFormSsg4mProcessService, + private substanceFormSsg4mSitesService: SubstanceFormSsg4mSitesService, + private substanceFormService: SubstanceFormService, + public configService: ConfigService, + private overlayContainerService: OverlayContainer, + private dialog: MatDialog, + private scrollToService: ScrollToService, + public gaService: GoogleAnalyticsService, + private http: HttpClient, + private activatedRoute: ActivatedRoute + ) { + super(gaService); + // this.analyticsEventCategory = 'substance form ssg4m process'; + } + + ngOnInit() { + this.canAddItemUpdate.emit(true); + this.menuLabelUpdate.emit('Processes'); + this.overlayContainer = this.overlayContainerService.getContainerElement(); + let loaded = false; + + setInterval(() => { + if (window['schemeUtil'] && !loaded) { + loaded = true; + //setup viz stuff + //TODO: make more configurable and standardized + console.log("About to configure the scheme view"); + window['schemeUtil'].debug = false; + const url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/'}api/v1/`; + const httpp = this.http; + window['schemeUtil'].apiBaseURL = url; + + //allow resolution of svgs + window['schemeUtil'].urlResolver = (u, cb) => { + httpp.get(u, { responseType: 'text' }).subscribe(svg => { + cb(svg); + }, error => { + cb("ERROR"); + }); + }; + //TODO: + window['schemeUtil'].onClickReaction = (d) => { + //Can we add a popup dialog that would show the specific step here? + let pindex = d.processIndex; + let sindex = d.stepIndex; + let siteIndex = d.siteIndex; + if (typeof siteIndex === "undefined") { + siteIndex = 0; + } + this.showStepViewDialog(pindex, siteIndex, sindex); + + //I just want to show a dialog that shows the step/stage component rendered in a popup for now. + //maybe in the future it should instead be a side window, I don't know. + }; + + //TODO: + window['schemeUtil'].onClickMaterial = (d) => { + this.openImageModal(d.refuuid, d.name, d.bottomText); + }; + + if (window['schemeUtil'].executeWhenLoaded) { + window['schemeUtil'].executeWhenLoaded(); + } + } + }, 100); + + // Get the parameter from URL and set the tab to either form view, step view, or scheme view. + this.showView = this.activatedRoute.snapshot.queryParams['view'] || 'form'; + let urlTabIndex = this.tabTitle.indexOf(this.showView); + if (urlTabIndex != -1) { + this.onSelectedIndexChange(urlTabIndex); + } + + } + + ngAfterViewInit() { + const processSubscription = this.substanceFormSsg4mProcessService.specifiedSubstanceG4mProcess.subscribe(process => { + this.process = process; + this.filtered = process; + /* + const searchSubscription = this.searchControl.valueChanges.subscribe(value => { + this.filterList(value, this.notes, this.analyticsEventCategory); + }, error => { + console.log(error); + }); + */ + // this.subscriptions.push(searchSubscription); + this.page = 0; + this.pageChange(); + }); + + this.subscriptions.push(processSubscription); + } + + ngOnDestroy() { + this.componentDestroyed.emit(); + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + + addItem(): void { + this.addProcess(); + } + + openImageModal(subUuid: string, approvalID: string, displayName: string): void { + + let data: any; + data = { + structure: subUuid, + uuid: subUuid, + approvalID: approvalID, + displayName: displayName + }; + + const dialogRef = this.dialog.open(StructureImageModalComponent, { + height: '96%', + width: '650px', + panelClass: 'structure-image-panel', + data: data + }); + + this.overlayContainer.style.zIndex = '1002'; + + const subscription = dialogRef.afterClosed().subscribe(() => { + this.overlayContainer.style.zIndex = null; + subscription.unsubscribe(); + }, () => { + this.overlayContainer.style.zIndex = null; + subscription.unsubscribe(); + }); + } + + addProcess(): void { + this.substanceFormSsg4mProcessService.addProcess(); + setTimeout(() => { + this.scrollToService.scrollToElement(`substance-process-0`, 'center'); + }); + } + + deleteProcess(process: SpecifiedSubstanceG4mProcess): void { + // this.substanceFormSsg4mProcessService.deleteProcess(process); + } + + updateProcess($event) { + + } + + updateAdvancedSettings(event): void { + this.showAdvancedSettings = event.checked; + } + + tabSelected($event) { + if ($event) { + const evt: any = $event.tab; + const textLabel: string = evt.textLabel; + if (textLabel != null) { + this.tabSelectedView = textLabel; + } + // const ssgjs = JSON.stringify(this.substanceFormService.cleanSubstance()); + // window['schemeUtil'].renderScheme(window['schemeUtil'].makeDisplayGraph(JSON.parse(ssgjs)), "#scheme-viz-view"); + } + + } + + onSelectedIndexChange(tabIndex: number) { + this.tabSelectedIndex = tabIndex; + if (this.tabSelectedIndex === 2) { + document.querySelector("#scheme-viz-view").className = ""; + //This is a hacky placeholder way to force viz + //TODO finish this + const ssgjs = JSON.stringify(this.substanceFormService.cleanSubstance()); + + console.log("About to load the scheme view"); + if (window['schemeUtil']) { + if (window['schemeUtil'].debug) { + window['schemeUtil'].executeWhenLoaded = (() => { + console.log("About to render the scheme view"); + window['schemeUtil'].renderScheme(window['schemeUtil'].makeDisplayGraph(JSON.parse(ssgjs)), "#scheme-viz-view"); + window['schemeUtil'].executeWhenLoaded = null; + }); + } else { + console.log("About to render the scheme view"); + window['schemeUtil'].renderScheme(window['schemeUtil'].makeDisplayGraph(JSON.parse(ssgjs)), "#scheme-viz-view"); + } + } + } else { + document.querySelector("#scheme-viz-view").className = "hidden"; + } + } + + tabSelectedIndexOutChange(tabIndex: number) { + this.tabSelectedIndex = tabIndex; + } + + showStepViewDialog(processIndex: number, siteIndex: number, stageIndex: number) { + const data = { + processIndex: processIndex, + siteIndex: siteIndex, + stageIndex: stageIndex + }; + + const dialogRef = this.dialog.open(Ssg4mStepViewDialogComponent, { + width: '90%', + height: '80%', + panelClass: 'structure-image-panel', + data: data + }); + + this.overlayContainer.style.zIndex = '1002'; + + let localTabSelectedIndex = -1; + const subscription = dialogRef.afterClosed().subscribe(response => { + this.overlayContainer.style.zIndex = null; + localTabSelectedIndex = response; + subscription.unsubscribe(); + if (localTabSelectedIndex > -1) { + this.tabSelectedIndex = localTabSelectedIndex; + } + this.overlayContainer.style.zIndex = null; + subscription.unsubscribe(); + }); + } +} diff --git a/src/app/core/substance-ssg4m/ssg4m-process/substance-form-ssg4m-process.module.ts b/src/app/core/substance-ssg4m/ssg4m-process/substance-form-ssg4m-process.module.ts new file mode 100644 index 000000000..d21df6038 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-process/substance-form-ssg4m-process.module.ts @@ -0,0 +1,55 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +// import { SubstanceFormNotesCardComponent } from './substance-form-notes-card.component'; +import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; +import { SubstanceFormModule } from '../../substance-form/substance-form.module'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatIconModule } from '@angular/material/icon'; +import { MatButtonModule } from '@angular/material/button'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatBadgeModule } from '@angular/material/badge'; +import { ScrollToModule } from '../../scroll-to/scroll-to.module'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatInputModule } from '@angular/material/input'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatTabsModule } from '@angular/material/tabs'; +// import { SubstanceFormSsg4mSitesCardComponent} from '../ssg4m-sites/ssg4m-sites.module' +import { Ssg4mSitesModule } from '../ssg4m-sites/ssg4m-sites.module'; +import { Ssg4mSchemeViewModule } from '../ssg4m-scheme-view/ssg4m-scheme-view.module'; +import { SubstanceFormSsg4mProcessCardComponent } from './substance-form-ssg4m-process-card.component'; +import { Ssg4mProcessFormComponent } from './ssg4m-process-form.component'; +import { Ssg4mSitesComponent } from '../ssg4m-sites/ssg4m-sites.component'; + +@NgModule({ + imports: [ + CommonModule, + DynamicComponentLoaderModule.forChild(SubstanceFormSsg4mProcessCardComponent), + SubstanceFormModule, + MatDividerModule, + MatIconModule, + MatTooltipModule, + MatButtonModule, + MatBadgeModule, + ScrollToModule, + MatPaginatorModule, + MatInputModule, + MatCheckboxModule, + MatFormFieldModule, + ReactiveFormsModule, + FormsModule, + MatTabsModule, + Ssg4mSitesModule, + Ssg4mSchemeViewModule + ], + exports: [ + Ssg4mProcessFormComponent + ], + declarations: [ + SubstanceFormSsg4mProcessCardComponent, + Ssg4mProcessFormComponent + ] +}) +export class SubstanceSsg4mProcessModule { } + diff --git a/src/app/core/substance-ssg4m/ssg4m-process/substance-form-ssg4m-process.service.ts b/src/app/core/substance-ssg4m/ssg4m-process/substance-form-ssg4m-process.service.ts new file mode 100644 index 000000000..a61741992 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-process/substance-form-ssg4m-process.service.ts @@ -0,0 +1,107 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { SubstanceFormServiceBase } from '../../substance-form/base-classes/substance-form-service-base'; +import { SubstanceFormService } from '../../substance-form/substance-form.service'; +import { SubstanceFormSsg4mSitesService } from '../ssg4m-sites/substance-form-ssg4m-sites.service'; +import { SpecifiedSubstanceG4mProcess, SpecifiedSubstanceG4mSite } from '@gsrs-core/substance/substance.model'; + +@Injectable({ + providedIn: 'root' +}) + +@Injectable() +export class SubstanceFormSsg4mProcessService extends SubstanceFormServiceBase> { + + constructor( + public substanceFormService: SubstanceFormService, + private substanceFormSsg4mSitesService: SubstanceFormSsg4mSitesService, + ) { + super(substanceFormService); + } + + initSubtanceForm(): void { + super.initSubtanceForm(); + const subscription = this.substanceFormService.substance.subscribe(substance => { + this.substance = substance; + if (!this.substance.specifiedSubstanceG4m) { + this.substance.specifiedSubstanceG4m = {}; + } + if (!this.substance.specifiedSubstanceG4m.process) { + this.substance.specifiedSubstanceG4m.process = []; + } + this.substanceFormService.resetState(); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process); + }); + this.subscriptions.push(subscription); + } + + get specifiedSubstanceG4mProcess(): Observable> { + return this.propertyEmitter.asObservable(); + } + + insertProcess(processIndexCurrent: number, insertDirection?: string): void { + + const newProcess: SpecifiedSubstanceG4mProcess = { + processName: '', + sites:[{stages:[{"stageNumber": "Stage 1", + startingMaterials: [], + processingMaterials: [], + resultingMaterials: [], + criticalParameters: []}]}] + }; + + let processIndex = 0; + if (insertDirection && insertDirection === 'before') { + newProcess.processName = 'Process ' + (processIndexCurrent+1).toString(); + this.substance.specifiedSubstanceG4m.process.splice(processIndexCurrent, 0, newProcess); + processIndex = processIndexCurrent; + } else { // after + newProcess.processName = 'Process ' + (processIndexCurrent+2).toString(); + this.substance.specifiedSubstanceG4m.process.splice(processIndexCurrent+1, 0, newProcess); + processIndex = processIndexCurrent + 1; + } + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process); + // Add Site + //this.substanceFormSsg4mSitesService.addSite(processIndex); + } + + addProcess(): void { + const newProcessIndex = this.substance.specifiedSubstanceG4m.process.length + 1; + const newProcess: SpecifiedSubstanceG4mProcess = { + processName: 'Process ' + newProcessIndex, + sites:[{stages:[{"stageNumber": "Stage 1", + startingMaterials: [], + processingMaterials: [], + resultingMaterials: [], + criticalParameters: []}]}] + }; + const processIndex = this.substance.specifiedSubstanceG4m.process.push(newProcess); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process); + // Add Site + // this.substanceFormSsg4mSitesService.addSite(processIndex-1); + } + + deleteProcess(process: SpecifiedSubstanceG4mProcess, processIndex: number): void { + // const processIndex = this.substance.specifiedSubstanceG4m.process.findIndex(pro => pro.$$deletedCode === pro.$$deletedCode); + if (processIndex > -1) { + this.substance.specifiedSubstanceG4m.process.splice(processIndex, 1); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process); + } + } + + addSite(processIndex: number): void { + + } + + /* + addSite(processIndex: number): void { + const newSite: SpecifiedSubstanceG4mSite = { + // references: [], + // access: [], + stages: [] + }; + this.substance.specifiedSubstanceG4m.process[processIndex].sites.unshift(newSite); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process); + } + */ +} diff --git a/src/app/core/substance-ssg4m/ssg4m-processing-materials/ssg4m-processing-materials-form.component.html b/src/app/core/substance-ssg4m/ssg4m-processing-materials/ssg4m-processing-materials-form.component.html new file mode 100644 index 000000000..2723ccc7b --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-processing-materials/ssg4m-processing-materials-form.component.html @@ -0,0 +1,218 @@ +
    +
    +
    + +
    +
    + Deleted  + +
    + + +
    + +
    + +
    + +
    + +
    + + +
    +
    + + + + + +
    + +
    + + + + + + +
    + + +
    +
    +
    +
    + Amount + +
    +
    +
    + {{displayAmount(processingMaterial.amount)}} +
    +
    +
    +
    +
    + + + + +
    +
    + +
    + +
    + + + Download + + + +
    + +
    +
    + + +
    + Uploading +
    + +
    + Error: There was a problem uploading this document +
    +
    + + + +
    +
    + Acceptance Criterias:   + + + +
    +
    +
    + + + + + + + + +
    + +
    +
    +
    +
    +
    + + + +
    +
    +
    + Manufacturer Details:   + + + + +
    +
    + +
    + + + + + + + + + + + + + + + +
    + +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    \ No newline at end of file diff --git a/src/app/core/substance-ssg4m/ssg4m-processing-materials/ssg4m-processing-materials-form.component.scss b/src/app/core/substance-ssg4m/ssg4m-processing-materials/ssg4m-processing-materials-form.component.scss new file mode 100644 index 000000000..fc7b9763f --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-processing-materials/ssg4m-processing-materials-form.component.scss @@ -0,0 +1,149 @@ +.form-container { + padding: 5px 10px 12px 10px; + position: relative; + /*border: 1px solid var(--pale-border-color-rgb);*/ +} + +.notification-backdrop { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: flex; + z-index: 10; + background-color: var(--notif-backdrop-bg-color); + justify-content: center; + align-items: center; + font-size: 30px; + font-weight: bold; + color: var(--notif-backdrop-color); +} + +.form-row { + display: flex; + justify-content: space-between; + align-items: flex-end; + + .delete-container { + padding: 0 10px 8px 0; + } + + .col { + flex-grow: 1; + padding-right: 15px; + } + + .col-1-1 { + width: calc((100% - 20px * 1)); //one column + margin-right: 20px; + } +} + +.references-container { + width: 100%; +} + +.related-substance { + min-width: 310px; + max-width: 310px; +} + +/* +.related-substance { + min-width: 387px; + max-width: 387px; +} +*/ + +::ng-deep .selected-substance { + display: flex; + flex-direction: column; + text-align: left !important; + position: relative; + img { + width: 100%; + height: auto; + display: block; + max-width: 220px; + } +} + +.divflex { + display: flex; +} + +.bordergray { + border: 1px solid var(--regular-grey-color); +} + +.margintopneg30px { + margin-top: -30px; +} + +.margintop10px { + margin-top: 10px; +} + +.margintop20px { + margin-top: 20px; +} + +.marginleftneg15px { + margin-left: -15px; +} + +.marginleft50px { + margin-left: 50px; +} + +.marginleft45px { + margin-left: 45px; +} + +.padleftneg10 { + padding-left: -10px; +} + +.padleft15px { + padding-left: 15px; +} + +.padtop10px { + padding-top: 10px; +} + +.height40px { + height: 40px; +} + +.font11px { + font-size: 11px; +} + +.width80percent { + width: 80%; +} + +.button-delete { + display: flex; + justify-content: flex-end; + margin-top: -15px; +} + +.colorred { + color: red; +} + +hr.style { + border: 1px solid rgb(111, 111, 111); +} + +.amount { + width: 100%; + display: flex; +} + +.amount-display { + padding-top:11px; +} diff --git a/src/app/core/substance-ssg4m/ssg4m-processing-materials/ssg4m-processing-materials-form.component.spec.ts b/src/app/core/substance-ssg4m/ssg4m-processing-materials/ssg4m-processing-materials-form.component.spec.ts new file mode 100644 index 000000000..456c4048f --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-processing-materials/ssg4m-processing-materials-form.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Ssg4mProcessingMaterialsFormComponent } from './ssg4m-processing-materials-form.component'; + +describe('Ssg4mProcessingMaterialsFormComponent', () => { + let component: Ssg4mProcessingMaterialsFormComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ Ssg4mProcessingMaterialsFormComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(Ssg4mProcessingMaterialsFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-ssg4m/ssg4m-processing-materials/ssg4m-processing-materials-form.component.ts b/src/app/core/substance-ssg4m/ssg4m-processing-materials/ssg4m-processing-materials-form.component.ts new file mode 100644 index 000000000..cd005c945 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-processing-materials/ssg4m-processing-materials-form.component.ts @@ -0,0 +1,271 @@ +import { Component, OnInit, OnDestroy, Input } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { Subscription } from 'rxjs'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { ConfigService } from '@gsrs-core/config/config.service'; +import { UtilsService } from '@gsrs-core/utils'; +import { SubstanceFormService } from '@gsrs-core/substance-form/substance-form.service'; +import { SubstanceDetail } from '@gsrs-core/substance/substance.model'; +import { SubstanceRelated, SubstanceSummary } from '@gsrs-core/substance'; +import { SpecifiedSubstanceG4mProcessingMaterial, SubstanceAmount } from '@gsrs-core/substance/substance.model'; +import { AmountFormDialogComponent} from '@gsrs-core/substance-form/amount-form-dialog/amount-form-dialog.component'; +import { ConfirmDialogComponent } from '../../../fda/confirm-dialog/confirm-dialog.component'; +import { SubstanceFormSsg4mStagesService } from '../ssg4m-stages/substance-form-ssg4m-stages.service'; + +@Component({ + selector: 'app-ssg4m-processing-materials-form', + templateUrl: './ssg4m-processing-materials-form.component.html', + styleUrls: ['./ssg4m-processing-materials-form.component.scss'] +}) +export class Ssg4mProcessingMaterialsFormComponent implements OnInit, OnDestroy { + + @Input() processingMaterialIndex: number; + privateProcessIndex: number; + privateSiteIndex: number; + privateStageIndex: number; + public configSettingsDisplay = {}; + privateShowAdvancedSettings: boolean; + privateProcessingMaterial: SpecifiedSubstanceG4mProcessingMaterial; + relatedSubstanceUuid: string; + substance: SubstanceDetail; + private overlayContainer: HTMLElement; + loading = false; + error = false; + private subscriptions: Array = []; + + constructor( + private substanceFormService: SubstanceFormService, + private substanceFormSsg4mStagesService: SubstanceFormSsg4mStagesService, + private overlayContainerService: OverlayContainer, + private utilsService: UtilsService, + public configService: ConfigService, + private dialog: MatDialog + ) { } + + @Input() + set processingMaterial(startingMaterial: SpecifiedSubstanceG4mProcessingMaterial) { + this.privateProcessingMaterial = startingMaterial; + } + + get processingMaterial(): SpecifiedSubstanceG4mProcessingMaterial { + return this.privateProcessingMaterial; + } + + @Input() + set processIndex(processIndex: number) { + this.privateProcessIndex = processIndex; + } + + get processIndex(): number { + return this.privateProcessIndex; + } + + @Input() + set siteIndex(siteIndex: number) { + this.privateSiteIndex = siteIndex; + } + + get siteIndex(): number { + return this.privateSiteIndex; + } + + @Input() + set stageIndex(stageIndex: number) { + this.privateStageIndex = stageIndex; + } + + get stageIndex(): number { + return this.privateStageIndex; + } + + @Input() + set showAdvancedSettings(showAdvancedSettings: boolean) { + this.privateShowAdvancedSettings = showAdvancedSettings; + // Get Config Settins from config file + this.getConfigSettings(); + } + + get showAdvancedSettings(): boolean { + return this.privateShowAdvancedSettings; + } + + ngOnInit(): void { + const subscription = this.substanceFormService.substance.subscribe(substance => { + this.substance = substance; + }); + this.subscriptions.push(subscription); + + // Load Substance Name + if (this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex].processingMaterials[this.processingMaterialIndex].substanceName) { + let substanceRelated = this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex].processingMaterials[this.processingMaterialIndex].substanceName; + this.relatedSubstanceUuid = substanceRelated.refuuid; + } + + if (this.configSettingsDisplay["references"] === true) { + if (this.privateProcessingMaterial.references == null) { + this.privateProcessingMaterial.references = []; + } + } + } + + ngOnDestroy(): void { + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + + getConfigSettings(): void { + // Get SSG4 Config Settings from config.json file to show and hide fields in the form + let configSsg4Form: any; + configSsg4Form = this.configService.configData && this.configService.configData.ssg4Form || null; + // *** IMPORTANT: get the correct value. Get 'processingMaterial' json values from config + const confSettings = configSsg4Form.settingsDisplay.processingMaterial; + Object.keys(confSettings).forEach(key => { + if (confSettings[key] != null) { + if (confSettings[key] === 'simple') { + this.configSettingsDisplay[key] = true; + } else if (confSettings[key] === 'advanced') { + if (this.privateShowAdvancedSettings === true) { + this.configSettingsDisplay[key] = true; + } else { + this.configSettingsDisplay[key] = false; + } + } else if (confSettings[key] === 'removed') { + this.configSettingsDisplay[key] = false; + } + } + }); + } + + updateSubstanceRole(role: string): void { + this.privateProcessingMaterial.substanceRole = role; + } + + updateSubstanceGrade(grade: string): void { + this.privateProcessingMaterial.substanceGrade = grade; + } + + updateSpecificationType(specificationType: string): void { + this.privateProcessingMaterial.specificationType = specificationType; + } + + updateAcceptanceCriteriaType(acceptanceCriteriaType: string, acceptanceIndex): void { + this.privateProcessingMaterial.acceptanceCriterias[acceptanceIndex].acceptanceCriteriaType = acceptanceCriteriaType; + } + + relatedSubstanceUpdated(substance: SubstanceSummary): void { + if (substance != null) { + const relatedSubstance: SubstanceRelated = { + refPname: substance._name, + name: substance._name, + refuuid: substance.uuid, + substanceClass: 'reference', + approvalID: substance.approvalID + }; + this.privateProcessingMaterial.substanceName = relatedSubstance; + } + else { + this.privateProcessingMaterial.substanceName = {}; + } + } + + addManufacturer(processIndex: number, siteIndex: number, stageIndex: number) { + this.substanceFormSsg4mStagesService.addResultingManufacturerDetails(processIndex, siteIndex, stageIndex, this.processingMaterialIndex); + } + + addAcceptanceCriteria(processIndex: number, siteIndex: number, stageIndex: number) { + this.substanceFormSsg4mStagesService.addProcessingAcceptanceCriteria(processIndex, siteIndex, stageIndex, this.processingMaterialIndex); + } + + confirmDeleteProcessingMaterial() { + const dialogRef = this.dialog.open(ConfirmDialogComponent, { + data: { message: 'Are you sure you want to delele Processing Material ' + (this.processingMaterialIndex + 1) + ' for Step ' + (this.stageIndex + 1) + ' for Site ' + (this.siteIndex + 1) + ' for Process ' + (this.processIndex + 1) + '?' } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result && result === true) { + this.deleteProcessingMaterial(); + } + }); + } + + deleteProcessingMaterial(): void { + this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex].processingMaterials.splice(this.processingMaterialIndex, 1); + } + + confirmDeleteManufacturer(manufacturerIndex: number) { + const dialogRef = this.dialog.open(ConfirmDialogComponent, { + data: { message: 'Are you sure you want to delele Manufacturer ' + (manufacturerIndex + 1) + ' for Processing Material ' + (this.processingMaterialIndex + 1) + ' for Step ' + (this.stageIndex + 1) + ' for Site ' + (this.siteIndex + 1) + ' for Process ' + (this.processIndex + 1) + '?' } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result && result === true) { + this.deleteManufacturer(manufacturerIndex); + } + }); + } + + deleteManufacturer(manufacturerIndex: number): void { + this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex].processingMaterials[this.processingMaterialIndex].manufacturerDetails.splice(manufacturerIndex, 1); + } + + confirmDeleteAcceptanceCriteria(acceptanceIndex: number) { + const dialogRef = this.dialog.open(ConfirmDialogComponent, { + data: { message: 'Are you sure you want to delele Acceptance Criteria ' + (acceptanceIndex + 1) + ' for Processing Material ' + (this.processingMaterialIndex + 1) + ' for Step ' + (this.stageIndex + 1) + ' for Site ' + (this.siteIndex + 1) + ' for Process ' + (this.processIndex + 1) + '?' } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result && result === true) { + this.deleteAcceptanceCriteria(acceptanceIndex); + } + }); + } + + deleteAcceptanceCriteria(acceptanceIndex: number): void { + this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex].processingMaterials[this.processingMaterialIndex].acceptanceCriterias.splice(acceptanceIndex, 1); + } + + openAmountDialog(): void { + if (!this.privateProcessingMaterial.amount) { + this.privateProcessingMaterial.amount = {}; + } + const dialogRef = this.dialog.open(AmountFormDialogComponent, { + data: {'subsAmount': this.privateProcessingMaterial.amount}, + width: '990px' + }); + this.overlayContainer.style.zIndex = '1002'; + + const dialogSubscription = dialogRef.afterClosed().subscribe(newAmount => { + this.overlayContainer.style.zIndex = null; + this.privateProcessingMaterial.amount = newAmount; + }); + this.subscriptions.push(dialogSubscription); + } + + displayAmount(amt: SubstanceAmount): string { + return this.utilsService.displayAmount(amt); + } + + + fileSelected(file: File): void { + this.error = false; + if (file != null) { + this.loading = true; + this.utilsService.uploadFile(file).subscribe(response => { + this.privateProcessingMaterial.specificationReference = response; + this.loading = false; + + }, error => { + this.loading = false; + this.error = true; + + }); + } + } + + downloadDocument(url: string): void { + this.substanceFormService.bypassUpdateCheck(); + window.open(url); + } + +} diff --git a/src/app/core/substance-ssg4m/ssg4m-processing-materials/substance-form-ssg4m-processing-materials.module.ts b/src/app/core/substance-ssg4m/ssg4m-processing-materials/substance-form-ssg4m-processing-materials.module.ts new file mode 100644 index 000000000..f9746c62f --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-processing-materials/substance-form-ssg4m-processing-materials.module.ts @@ -0,0 +1,79 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { CommonModule, formatDate } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; +import { MatInputModule } from '@angular/material/input'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatButtonModule } from '@angular/material/button'; +import { MatButtonToggleModule } from '@angular/material/button-toggle'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatTableModule } from '@angular/material/table'; +import { MatListModule } from '@angular/material/list'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatBadgeModule } from '@angular/material/badge'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatProgressBarModule} from '@angular/material/progress-bar'; +import { NgxJsonViewerModule} from 'ngx-json-viewer'; +// GSRS Imports +import { FileSelectModule } from 'file-select'; +import { ScrollToModule } from '@gsrs-core/scroll-to/scroll-to.module'; +import { ExpandDetailsModule } from '@gsrs-core/expand-details/expand-details.module'; +import { SubstanceSelectorModule } from '@gsrs-core/substance-selector/substance-selector.module'; +import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; +import { SubstanceFormModule } from '@gsrs-core/substance-form/substance-form.module'; +import { Ssg4mProcessingMaterialsFormComponent } from './ssg4m-processing-materials-form.component'; + +@NgModule({ + imports: [ + CommonModule, + ReactiveFormsModule, + FormsModule, + MatFormFieldModule, + MatMenuModule, + MatCheckboxModule, + MatButtonModule, + MatIconModule, + MatTooltipModule, + MatTabsModule, + MatDividerModule, + MatSelectModule, + MatInputModule, + MatChipsModule, + MatAutocompleteModule, + ScrollToModule, + MatDialogModule, + MatTableModule, + MatExpansionModule, + MatBadgeModule, + MatRadioModule, + ExpandDetailsModule, + SubstanceSelectorModule, + MatListModule, + FileSelectModule, + MatButtonToggleModule, + NgxJsonViewerModule, + RouterModule, + MatProgressBarModule, + MatProgressSpinnerModule, + SubstanceImageModule, + SubstanceSelectorModule, + SubstanceFormModule + ], + declarations: [ + Ssg4mProcessingMaterialsFormComponent + ], + exports: [ + Ssg4mProcessingMaterialsFormComponent + ] +}) +export class SubstanceFormSsg4mProcessingMaterialsModule { } diff --git a/src/app/core/substance-ssg4m/ssg4m-processing-materials/substance-form-ssg4m-processing-materials.service.spec.ts b/src/app/core/substance-ssg4m/ssg4m-processing-materials/substance-form-ssg4m-processing-materials.service.spec.ts new file mode 100644 index 000000000..a201bd24c --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-processing-materials/substance-form-ssg4m-processing-materials.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { SubstanceFormSsg4mProcessingMaterialsService } from './substance-form-ssg4m-processing-materials.service'; + +describe('SubstanceFormSsg4mProcessingMaterialsService', () => { + let service: SubstanceFormSsg4mProcessingMaterialsService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(SubstanceFormSsg4mProcessingMaterialsService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-ssg4m/ssg4m-processing-materials/substance-form-ssg4m-processing-materials.service.ts b/src/app/core/substance-ssg4m/ssg4m-processing-materials/substance-form-ssg4m-processing-materials.service.ts new file mode 100644 index 000000000..072c0ab61 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-processing-materials/substance-form-ssg4m-processing-materials.service.ts @@ -0,0 +1,9 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class SubstanceFormSsg4mProcessingMaterialsService { + + constructor() { } +} diff --git a/src/app/core/substance-ssg4m/ssg4m-resulting-materials/ssg4m-resulting-materials-form.component.html b/src/app/core/substance-ssg4m/ssg4m-resulting-materials/ssg4m-resulting-materials-form.component.html new file mode 100644 index 000000000..54a44a98a --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-resulting-materials/ssg4m-resulting-materials-form.component.html @@ -0,0 +1,129 @@ +
    +
    +
    + +
    +
    + Deleted  + +
    + +
    + + + + + +
    + +
    + +
    + +
    + + +
    +
    + + + + + +
    + +
    + + + + + + +
    + + +
    +
    +
    +
    + Amount + +
    +
    +
    + {{displayAmount(resultingMaterial.amount)}} +
    +
    +
    +
    +
    + + + +
    +
    + Acceptance Criterias:   + + + +
    +
    +
    + + + + + + + +
    + +
    +
    +
    +
    +
    + + +
    +
    + +
    \ No newline at end of file diff --git a/src/app/core/substance-ssg4m/ssg4m-resulting-materials/ssg4m-resulting-materials-form.component.scss b/src/app/core/substance-ssg4m/ssg4m-resulting-materials/ssg4m-resulting-materials-form.component.scss new file mode 100644 index 000000000..1d310ce12 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-resulting-materials/ssg4m-resulting-materials-form.component.scss @@ -0,0 +1,137 @@ +.form-container { + padding: 5px 10px 12px 10px; + position: relative; +/* border: 1px solid var(--pale-border-color-rgb);*/ +} + +.notification-backdrop { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: flex; + z-index: 10; + background-color: var(--notif-backdrop-bg-color); + justify-content: center; + align-items: center; + font-size: 30px; + font-weight: bold; + color: var(--notif-backdrop-color); +} + +.form-row { + display: flex; + justify-content: space-between; + align-items: flex-end; + + .delete-container { + padding: 0 0px 8px 0; + } + + .col { + flex-grow: 1; + padding-right: 15px; + } + + .col-1-1 { + width: calc((100% - 20px * 1)); //one column + margin-right: 20px; + } +} + +.references-container { + width: 100%; +} + +.related-substance { + min-width: 310px; + max-width: 310px; +} + +/* +.related-substance { + min-width: 387px; + max-width: 387px; +} +*/ + +::ng-deep .selected-substance { + display: flex; + flex-direction: column; + text-align: left !important; + position: relative; + img { + width: 100%; + height: auto; + display: block; + max-width: 220px; + } +} + +.divflex { + display: flex; +} + +.bordergray { + border: 1px solid var(--regular-grey-color); +} + +.margintop10px { + margin-top: 10px; +} + +.margintopneg50px { + margin-top: -50px; +} + +.marginleft45px { + margin-left: 45px; +} + +.marginleft30px { + margin-left: 30px; +} + +.padtop10px { + padding-top: 10px; +} + +.padleft15px { + padding-left: 15px; +} + +.height40px { + height: 40px; +} + +.font11px { + font-size: 11px; +} + +.width80percent { + width: 80%; +} + +.button-delete { + display: flex; + justify-content: flex-end; + margin-top: -15px; +} + +.colorred { + color: red; +} + +/* FDA COLOR BLUE CODE Hex: #007CBA */ +.colorbluefda { + color: #007CBA; +} + +hr.style { + border: 1px solid rgb(111, 111, 111); +} + +.amount-display { + padding-top:11px; +} diff --git a/src/app/core/substance-ssg4m/ssg4m-resulting-materials/ssg4m-resulting-materials-form.component.spec.ts b/src/app/core/substance-ssg4m/ssg4m-resulting-materials/ssg4m-resulting-materials-form.component.spec.ts new file mode 100644 index 000000000..9461b2d80 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-resulting-materials/ssg4m-resulting-materials-form.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Ssg4mResultingMaterialsFormComponent } from './ssg4m-resulting-materials-form.component'; + +describe('Ssg4mResultingMaterialsFormComponent', () => { + let component: Ssg4mResultingMaterialsFormComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ Ssg4mResultingMaterialsFormComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(Ssg4mResultingMaterialsFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-ssg4m/ssg4m-resulting-materials/ssg4m-resulting-materials-form.component.ts b/src/app/core/substance-ssg4m/ssg4m-resulting-materials/ssg4m-resulting-materials-form.component.ts new file mode 100644 index 000000000..590cc6450 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-resulting-materials/ssg4m-resulting-materials-form.component.ts @@ -0,0 +1,329 @@ +import { Component, OnInit, OnDestroy, Input } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { Subscription } from 'rxjs'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { ConfigService } from '@gsrs-core/config/config.service'; +import { UtilsService } from '@gsrs-core/utils'; +import { SubstanceFormService } from '@gsrs-core/substance-form/substance-form.service'; +import { SubstanceDetail } from '@gsrs-core/substance/substance.model'; +import { SubstanceRelated, SubstanceSummary } from '@gsrs-core/substance'; +import { SpecifiedSubstanceG4mResultingMaterial, SubstanceAmount } from '@gsrs-core/substance/substance.model'; +import { SubstanceFormSsg4mStagesService } from '../ssg4m-stages/substance-form-ssg4m-stages.service'; +import { AmountFormDialogComponent} from '@gsrs-core/substance-form/amount-form-dialog/amount-form-dialog.component'; +import { ConfirmDialogComponent } from '../../../fda/confirm-dialog/confirm-dialog.component'; + +@Component({ + selector: 'app-ssg4m-resulting-materials-form', + templateUrl: './ssg4m-resulting-materials-form.component.html', + styleUrls: ['./ssg4m-resulting-materials-form.component.scss'] +}) +export class Ssg4mResultingMaterialsFormComponent implements OnInit, OnDestroy { + + @Input() resultingMaterialIndex: number; + privateProcessIndex: number; + privateSiteIndex: number; + privateStageIndex: number; + public configSettingsDisplay = {}; + privateShowAdvancedSettings: boolean; + privateResultingMaterial: SpecifiedSubstanceG4mResultingMaterial; + relatedSubstanceUuid: string; + substance: SubstanceDetail; + private overlayContainer: HTMLElement; + loading = false; + error = false; + subscriptions: Array = []; + + constructor( + private substanceFormService: SubstanceFormService, + private substanceFormSsg4mStagesService: SubstanceFormSsg4mStagesService, + private overlayContainerService: OverlayContainer, + private utilsService: UtilsService, + public configService: ConfigService, + private dialog: MatDialog + ) { } + + @Input() + set resultingMaterial(resultingMaterial: SpecifiedSubstanceG4mResultingMaterial) { + this.privateResultingMaterial = resultingMaterial; + } + + get resultingMaterial(): SpecifiedSubstanceG4mResultingMaterial { + return this.privateResultingMaterial; + } + + @Input() + set processIndex(processIndex: number) { + this.privateProcessIndex = processIndex; + } + + get processIndex(): number { + return this.privateProcessIndex; + } + + @Input() + set siteIndex(siteIndex: number) { + this.privateSiteIndex = siteIndex; + } + + get siteIndex(): number { + return this.privateSiteIndex; + } + + @Input() + set stageIndex(stageIndex: number) { + this.privateStageIndex = stageIndex; + } + + get stageIndex(): number { + return this.privateStageIndex; + } + + @Input() + set showAdvancedSettings(showAdvancedSettings: boolean) { + this.privateShowAdvancedSettings = showAdvancedSettings; + // Get Config Settins from config file + this.getConfigSettings(); + } + + get showAdvancedSettings(): boolean { + return this.privateShowAdvancedSettings; + } + + ngOnInit(): void { + const subscription = this.substanceFormService.substance.subscribe(substance => { + this.substance = substance; + }); + this.subscriptions.push(subscription); + + // Load Substance Name + if (this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex].resultingMaterials[this.resultingMaterialIndex].substanceName) { + let substanceRelated = this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex].resultingMaterials[this.resultingMaterialIndex].substanceName; + this.relatedSubstanceUuid = substanceRelated.refuuid; + } + } + + ngOnDestroy(): void { + // this.substanceFormService.unloadSubstance(); + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + + getConfigSettings(): void { + // Get SSG4 Config Settings from config.json file to show and hide fields in the form + let configSsg4Form: any; + configSsg4Form = this.configService.configData && this.configService.configData.ssg4Form || null; + // *** IMPORTANT: get the correct value. Get 'resultingMaterial' json values from config + const confSettings = configSsg4Form.settingsDisplay.resultingMaterial; + Object.keys(confSettings).forEach(key => { + if (confSettings[key] != null) { + if (confSettings[key] === 'simple') { + this.configSettingsDisplay[key] = true; + } else if (confSettings[key] === 'advanced') { + if (this.privateShowAdvancedSettings === true) { + this.configSettingsDisplay[key] = true; + } else { + this.configSettingsDisplay[key] = false; + } + } else if (confSettings[key] === 'removed') { + this.configSettingsDisplay[key] = false; + } + } + }); + } + + updateSubstanceRole(role: string): void { + this.privateResultingMaterial.substanceRole = role; + } + + updateSubstanceGrade(grade: string): void { + this.privateResultingMaterial.substanceGrade = grade; + } + + updateSpecificationType(specificationType: string): void { + this.privateResultingMaterial.specificationType = specificationType; + } + + updateAcceptanceCriteriaType(acceptanceCriteriaType: string, acceptanceIndex: number): void { + this.privateResultingMaterial.acceptanceCriterias[acceptanceIndex].acceptanceCriteriaType = acceptanceCriteriaType; + } + + relatedSubstanceUpdated(substance: SubstanceSummary): void { + if (substance != null) { + const relatedSubstance: SubstanceRelated = { + refPname: substance._name, + name: substance._name, + refuuid: substance.uuid, + substanceClass: 'reference', + approvalID: substance.approvalID + }; + this.privateResultingMaterial.substanceName = relatedSubstance; + } + else { + this.privateResultingMaterial.substanceName = {}; + } + } + + addAcceptanceCriteria(processIndex: number, siteIndex: number, stageIndex: number) { + this.substanceFormSsg4mStagesService.addResultingAcceptanceCriteria(processIndex, siteIndex, stageIndex, this.resultingMaterialIndex); + } + + confirmDeleteResultingMaterial() { + const dialogRef = this.dialog.open(ConfirmDialogComponent, { + data: { message: 'Are you sure you want to delele Resulting Material ' + (this.resultingMaterialIndex + 1) + ' for Step ' + (this.stageIndex + 1) + ' for Site ' + (this.siteIndex + 1) + ' for Process ' + (this.processIndex + 1) + '?' } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result && result === true) { + this.deleteResultingMaterial(); + } + }); + } + + deleteResultingMaterial(): void { + this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex].resultingMaterials.splice(this.resultingMaterialIndex, 1); + } + + confirmDeleteAcceptanceCriteria(acceptanceIndex: number) { + const dialogRef = this.dialog.open(ConfirmDialogComponent, { + data: { message: 'Are you sure you want to delele Acceptance Criteria ' + (acceptanceIndex + 1) + ' for Processing Material ' + (this.resultingMaterialIndex + 1) + ' for Step ' + (this.stageIndex + 1) + ' for Site ' + (this.siteIndex + 1) + ' for Process ' + (this.processIndex + 1) + '?' } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result && result === true) { + this.deleteAcceptanceCriteria(acceptanceIndex); + } + }); + } + + deleteAcceptanceCriteria(acceptanceIndex: number): void { + this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex].resultingMaterials[this.resultingMaterialIndex].acceptanceCriterias.splice(acceptanceIndex, 1); + } + + copyResultingToStarting() { + this.substanceFormSsg4mStagesService.setSourceStageToCopy(this.processIndex, this.siteIndex, this.stageIndex); + this.substanceFormSsg4mStagesService.setSourceResultingToCopy(this.resultingMaterialIndex); + this.substanceFormSsg4mStagesService.copyResultingToStarting(this.processIndex, this.siteIndex, this.stageIndex + 1); + } + + openAmountDialog(): void { + if (!this.privateResultingMaterial.amount) { + this.privateResultingMaterial.amount = {}; + } + const dialogRef = this.dialog.open(AmountFormDialogComponent, { + data: {'subsAmount': this.privateResultingMaterial.amount}, + width: '990px' + }); + this.overlayContainer.style.zIndex = '1002'; + + const dialogSubscription = dialogRef.afterClosed().subscribe(newAmount => { + this.overlayContainer.style.zIndex = null; + this.privateResultingMaterial.amount = newAmount; + }); + this.subscriptions.push(dialogSubscription); + } + + displayAmount(amt: SubstanceAmount): string { + return this.utilsService.displayAmount(amt); + } + + fileSelected(file: File): void { + this.error = false; + if (file != null) { + this.loading = true; + this.utilsService.uploadFile(file).subscribe(response => { + this.privateResultingMaterial.specificationReference = response; + this.loading = false; + + }, error => { + this.loading = false; + this.error = true; + + }); + } + } + + downloadDocument(url: string): void { + this.substanceFormService.bypassUpdateCheck(); + window.open(url); + } + + /* + copyResultingToStarting() { + let found = false; + let resultMatRefUuid = ''; + let startMatRefUuid = ''; + const thisResultMatObj = this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex].resultingMaterials[this.resultingMaterialIndex]; + const thisResultMatSubName = thisResultMatObj.substanceName; + // Get Next Stage + const nextStageObj = this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex + 1]; + // Get this Resulting Material refUuid from Substance Name + if (thisResultMatSubName) { + resultMatRefUuid = thisResultMatSubName.refuuid; + } + // If next Stage exists + if (nextStageObj !== null && nextStageObj !== undefined) { + if (nextStageObj.startingMaterials.length == 0) { + // if there is no Starting Material, add a new one and copy + this.copyToStartingFields(); + } else { + // if Starting Material exists, loop through to find same refuuid + nextStageObj.startingMaterials.forEach(element => { + if (element) { + // Get this Starting Material refUuid from Substance Name + const startMatSubName = element.substanceName; + if (startMatSubName) { + startMatRefUuid = startMatSubName.refuuid; + // If the refuuid for the Resulting Material is same as FIRST Starting Material in the next Stage + if (resultMatRefUuid === startMatRefUuid) { + found = true; + alert('This Substance ' + startMatSubName.name + ' already exists in the Starting Material in the next Stage'); + } + } + } + }); // for each Starting Material in next Stage + if (found === false) { + this.copyToStartingFields(); + } + } + } + // Add a next Stage if it does not exists + if (!nextStageObj) { + this.addStage(); + this.copyToStartingFields(); + } + } + + copyToStartingFields() { + const thisResultMatObj = this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex].resultingMaterials[this.resultingMaterialIndex]; + const thisResultMatSubName = thisResultMatObj.substanceName; + // Add New Starting Material + this.addStartingMaterial(); + // Next Stage and Next New Starting Material + // Get Next Stage + const nextStageObj = this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex + 1]; + // Get the last index or push/add/copy to the Starting Material at the Last + const nextStartIndex = nextStageObj.startingMaterials.length-1; + const nextStartMat = nextStageObj.startingMaterials[nextStartIndex]; + nextStartMat.substanceName = thisResultMatSubName; + nextStartMat.verbatimName = thisResultMatObj.verbatimName; + nextStartMat.substanceRole = thisResultMatObj.substanceRole; + nextStartMat.comments = thisResultMatObj.comments; + } + + addStage() { + this.substanceFormSsg4mStagesService.addStage(this.processIndex, this.siteIndex); + setTimeout(() => { + // this.scrollToService.scrollToElement(`substance-process-site-0`, 'center'); + }); + } + + addStartingMaterial() { + // Add Starting Material in the next stage + this.substanceFormSsg4mStagesService.addStartingMaterials(this.processIndex, this.siteIndex, this.stageIndex + 1); + setTimeout(() => { + // this.scrollToService.scrollToElement(`substance-process-site-stage-startMat-0`, 'center'); + }); + } + */ +} diff --git a/src/app/core/substance-ssg4m/ssg4m-resulting-materials/substance-form-ssg4m-resulting-materials.module.ts b/src/app/core/substance-ssg4m/ssg4m-resulting-materials/substance-form-ssg4m-resulting-materials.module.ts new file mode 100644 index 000000000..adbc8d5a9 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-resulting-materials/substance-form-ssg4m-resulting-materials.module.ts @@ -0,0 +1,81 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { CommonModule, formatDate } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; +import { MatInputModule } from '@angular/material/input'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatButtonModule } from '@angular/material/button'; +import { MatButtonToggleModule } from '@angular/material/button-toggle'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatTableModule } from '@angular/material/table'; +import { MatListModule } from '@angular/material/list'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatBadgeModule } from '@angular/material/badge'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatProgressBarModule} from '@angular/material/progress-bar'; +import { NgxJsonViewerModule} from 'ngx-json-viewer'; +// GSRS Imports +import { FileSelectModule } from 'file-select'; +import { ScrollToModule } from '@gsrs-core/scroll-to/scroll-to.module'; +import { ExpandDetailsModule } from '@gsrs-core/expand-details/expand-details.module'; +import { SubstanceSelectorModule } from '@gsrs-core/substance-selector/substance-selector.module'; +import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; +import { SubstanceFormModule } from '@gsrs-core/substance-form/substance-form.module'; +import { Ssg4mStagesModule } from '../ssg4m-stages/substance-form-ssg4m-stages.module'; +import { Ssg4mResultingMaterialsFormComponent } from './ssg4m-resulting-materials-form.component'; + +@NgModule({ + imports: [ + CommonModule, + ReactiveFormsModule, + FormsModule, + MatFormFieldModule, + MatMenuModule, + MatCheckboxModule, + MatButtonModule, + MatIconModule, + MatTooltipModule, + MatTabsModule, + MatDividerModule, + MatSelectModule, + MatInputModule, + MatChipsModule, + MatAutocompleteModule, + ScrollToModule, + MatDialogModule, + MatTableModule, + MatExpansionModule, + MatBadgeModule, + MatRadioModule, + ExpandDetailsModule, + SubstanceSelectorModule, + MatListModule, + FileSelectModule, + MatButtonToggleModule, + NgxJsonViewerModule, + RouterModule, + MatProgressBarModule, + MatProgressSpinnerModule, + SubstanceImageModule, + SubstanceSelectorModule, + SubstanceFormModule + // Ssg4mStagesModule + ], + declarations: [ + Ssg4mResultingMaterialsFormComponent + ], + exports: [ + Ssg4mResultingMaterialsFormComponent + ] +}) +export class SubstanceFormSsg4mResultingMaterialsModule { } diff --git a/src/app/core/substance-ssg4m/ssg4m-resulting-materials/substance-form-ssg4m-resulting-materials.service.spec.ts b/src/app/core/substance-ssg4m/ssg4m-resulting-materials/substance-form-ssg4m-resulting-materials.service.spec.ts new file mode 100644 index 000000000..e40a13232 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-resulting-materials/substance-form-ssg4m-resulting-materials.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { SubstanceFormSsg4mResultingMaterialsService } from './substance-form-ssg4m-resulting-materials.service'; + +describe('SubstanceFormSsg4mResultingMaterialsService', () => { + let service: SubstanceFormSsg4mResultingMaterialsService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(SubstanceFormSsg4mResultingMaterialsService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-ssg4m/ssg4m-resulting-materials/substance-form-ssg4m-resulting-materials.service.ts b/src/app/core/substance-ssg4m/ssg4m-resulting-materials/substance-form-ssg4m-resulting-materials.service.ts new file mode 100644 index 000000000..170705773 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-resulting-materials/substance-form-ssg4m-resulting-materials.service.ts @@ -0,0 +1,9 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class SubstanceFormSsg4mResultingMaterialsService { + + constructor() { } +} diff --git a/src/app/core/substance-ssg4m/ssg4m-scheme-view/ssg4m-scheme-view.component.html b/src/app/core/substance-ssg4m/ssg4m-scheme-view/ssg4m-scheme-view.component.html new file mode 100644 index 000000000..09d09d84a --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-scheme-view/ssg4m-scheme-view.component.html @@ -0,0 +1,240 @@ +
    +
    + + Show Substance Role + +
    +     +
    + + Show Critical Parameter + +
    +
    + +
    +
    +
    + Process  {{processIndex + 1}} of + {{processList.length}} + + +
    +
    +
    + +
    +
    + Step  {{stageIndex + 1}} + + +
    + +
    + + + + + + + + + + + + + +
    +
    +
    +
    + + +
    + + + + +
    + + + {{startingMaterial.substanceName.refPname}} + + + + + + +
    + Material Display Name: {{startingMaterial.verbatimName}} +
    + +
    + ({{startingMaterial.substanceRole}}) +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + +
    +
    + + + {{processingMaterial.substanceName.refPname}} + + + + +  ({{processingMaterial.substanceRole}}) + +
    +
    +
    +
    +
    +
    + + +
    + +
    + + +
    +
    +
    +
    +
    +
    + + +
    +
    + + + + {{processingMaterial.substanceName.refPname}} + + + +  ({{processingMaterial.substanceRole}}) + +
    +
    +
    +
    +
    +
    + + +
    + +
    + {{displayAmount(criticalParameter.value, criticalParameter.name)}} +
    +
    +
    +
    +
    +
    +
    + + +
    + + + +
    + + + + {{resultingMaterial.substanceName.refPname}} + + + +
    + Material Display Name:{{resultingMaterial.verbatimName}} +
    + +
    + ({{resultingMaterial.substanceRole}}) +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + \ No newline at end of file diff --git a/src/app/core/substance-ssg4m/ssg4m-scheme-view/ssg4m-scheme-view.component.scss b/src/app/core/substance-ssg4m/ssg4m-scheme-view/ssg4m-scheme-view.component.scss new file mode 100644 index 000000000..6423e5f9f --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-scheme-view/ssg4m-scheme-view.component.scss @@ -0,0 +1,194 @@ +.divflexspace { + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.divflexcenter { + display: flex; + align-items: center; + justify-content: center; +} + +.divflexright { + display: flex; + justify-content: flex-end; +} + +.divflex { + display: flex; +} + +.bordergray { + border: 1px solid var(--regular-grey-color); +} + +.bordergreen { + border: 5px solid #0BA711; +} + +.margintopneg5px { + margin-top: -5px; +} + +.margintopneg25px { + margin-top: -25px; +} + +.margintopneg10px { + margin-top: -10px; +} + +.margintop10px { + margin-top: 10px; +} + +.marginbottom10px { + margin-bottom: 10px; +} + +.marginbottom20px { + margin-top: 20px; +} + +.paddingleft10px { + padding-left: 10px; +} + +.paddingleft20px { + padding-left: 20px; +} + +.paddingleft25px { + padding-left: 25px; +} + +.paddingleft350px { + padding-left: 350px; +} + +.paddingtop10px { + padding-top: 10px; +} + +.paddingtop15px { + padding-top: 15px; +} + +.paddingbottom10px { + padding-bottom: 10px; +} + +.padrightneg5px { + padding-right: -10px; +} + +.colorblue { + color: #007CBA; +} + +.fontsize14px { + font-size: 14px; +} + +.font16px { + font-size: 16px; +} + +.font30px { + font-size: 30px; +} + +.width100percent { + width: 100%; +} + +.width33percent { + width: 33%; +} + +.small-icon { + width: 15px; + height: 15px; + padding-left: 5px; +} + +.textaligncenter { + text-align: center; +} + +.button-add { + z-index: 1; + position: relative; + top: -36px; + margin-right: 20px; + background-color: #FFFFFF; + border: 1px solid #007CBA; +} + +fieldset.border { + border: solid 2px var(--fieldset-blue-border-color) !important; + padding: 0 10px 0px 10px; + border-bottom: none; + margin-bottom: 10px; + /*border-radius: 8px;*/ + min-width: 0; /* override the default value of min-content */ +/* -webkit-box-shadow: 2px 3px 3px 1px var(--fieldset-box-shadow-color); + -moz-box-shadow: 2px 3px 5px 1px var(--fieldset-box-shadow-color); + box-shadow: 2px 2px 3px 1px var(--fieldset-box-shadow-color);*/ + +} + +legend.border { + width: auto !important; + border: none; + border-bottom: none; + font-size: 15px; + color: var(--legend-blue-border-color); + font-family: Verdana; + font-weight: bold; + margin-bottom: 10px; + margin-left: 30px; +} + +fieldset.border-step { + border: solid 2px #0BA711 !important; + padding: 0 10px 10px 10px; + border-bottom: none; + margin-bottom: 30px; + /*border-radius: 8px;*/ + min-width: 0; /* override the default value of min-content */ + /*-webkit-box-shadow: 2px 3px 3px 1px rgba(110,104,110,0.64); + -moz-box-shadow: 2px 3px 5px 1px rgba(110,104,110,0.64); + box-shadow: 2px 2px 3px 1px rgba(110,104,110,0.64);*/ +} + +legend.border-step { + width: auto !important; + border: none; + border-bottom: none; + font-size: 15px; + color: #007CBA; + font-family: Verdana; + font-weight: bold; + margin-bottom: 10px; + margin-left: 30px; +} + +.structure-img-big { + width: 100%; + height: auto; +} + +.zoom:hover{ + cursor:zoom-in; +} + +a { + color: #0000EE;;; +} + +/* +.hyperlink-color a:link { + color: red; +}*/ diff --git a/src/app/core/substance-ssg4m/ssg4m-scheme-view/ssg4m-scheme-view.component.spec.ts b/src/app/core/substance-ssg4m/ssg4m-scheme-view/ssg4m-scheme-view.component.spec.ts new file mode 100644 index 000000000..757c1b2f6 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-scheme-view/ssg4m-scheme-view.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Ssg4mSchemeViewComponent } from './ssg4m-scheme-view.component'; + +describe('Ssg4mSchemeViewComponent', () => { + let component: Ssg4mSchemeViewComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ Ssg4mSchemeViewComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(Ssg4mSchemeViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-ssg4m/ssg4m-scheme-view/ssg4m-scheme-view.component.ts b/src/app/core/substance-ssg4m/ssg4m-scheme-view/ssg4m-scheme-view.component.ts new file mode 100644 index 000000000..aa498c7b2 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-scheme-view/ssg4m-scheme-view.component.ts @@ -0,0 +1,207 @@ +import { Component, OnInit, OnDestroy, Output, EventEmitter, Input } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { MatDialog } from '@angular/material/dialog'; +import { Environment } from 'src/environments/environment.model'; +import { ConfigService, LoadedComponents } from '@gsrs-core/config'; +import { StructureImageModalComponent, StructureService } from '@gsrs-core/structure'; +import { SubstanceFormService } from '../../substance-form/substance-form.service'; +import { SubstanceFormSsg4mProcessService } from '../ssg4m-process/substance-form-ssg4m-process.service'; +import { SubstanceDetail, SpecifiedSubstanceG4mProcess } from '@gsrs-core/substance/substance.model'; +import { SubstanceSsg4mService } from '../substance-ssg4m-form.service'; + +@Component({ + selector: 'app-ssg4m-scheme-view', + templateUrl: './ssg4m-scheme-view.component.html', + styleUrls: ['./ssg4m-scheme-view.component.scss'] +}) +export class Ssg4mSchemeViewComponent implements OnInit, OnDestroy { + @Output() tabSelectedIndexOut = new EventEmitter(); + @Input() showProcessIndex = -1; // -1 Show all records + @Input() showSiteIndex = -1; // -1 Show all records + @Input() showStageIndex = -1; // -1 Show all records + showSubstanceRole = true; + showCriticalParameter = false; + imageLoc: any; + environment: Environment; + substance: SubstanceDetail; + gsrsHomeBaseUrl: string; + processList: Array; + subscriptions: Array = []; + private overlayContainer: HTMLElement; + + constructor( + private configService: ConfigService, + private substanceFormSsg4mProcessService: SubstanceFormSsg4mProcessService, + private substanceSsg4mService: SubstanceSsg4mService, + private overlayContainerService: OverlayContainer, + private dialog: MatDialog, + ) { } + + ngOnInit(): void { + const processSubscription = this.substanceFormSsg4mProcessService.specifiedSubstanceG4mProcess.subscribe(process => { + this.processList = process; + }); + // const subscription = this.substanceFormService.substance.subscribe(substance => { + // this.substance = JSON.stringify(substance); + // this.process = JSON.stringify(this.substance); + // alert("JSON: " + JSON.stringify(this.substance)); + // }); + this.subscriptions.push(processSubscription); + this.environment = this.configService.environment; + this.imageLoc = `${this.environment.baseHref || ''}assets/images/home/arrow.png`; + this.overlayContainer = this.overlayContainerService.getContainerElement(); + + // Get GSRS Frontend URL fron config + this.getHomepageUrl(); + //this.gsrsHomeBaseUrl = this.configService.configData && this.configService.configData.gsrsHomeBaseUrl || ''; + } + + ngOnDestroy(): void { + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + + openImageModal(subUuid: string, approvalID: string, displayName: string): void { + // const eventLabel = environment.isAnalyticsPrivate ? 'substance' : substance._name; + + // this.gaService.sendEvent('substancesContent', 'link:structure-zoom', eventLabel); + + let data: any; + // if (substance.substanceClass === 'chemical') { + data = { + structure: subUuid, + uuid: subUuid, + approvalID: approvalID, + displayName: displayName + }; + // } + + const dialogRef = this.dialog.open(StructureImageModalComponent, { + height: '96%', + width: '650px', + panelClass: 'structure-image-panel', + data: data + }); + + this.overlayContainer.style.zIndex = '1001'; + + const subscription = dialogRef.afterClosed().subscribe(() => { + this.overlayContainer.style.zIndex = '1001'; + subscription.unsubscribe(); + }, () => { + this.overlayContainer.style.zIndex = '1001'; + subscription.unsubscribe(); + }); + } + + displayAmount(amt, propertyName: string): string { + return this.displayAmountCompose(amt, propertyName); + } + + editInForm() { + this.tabSelectedIndexOut.emit(0); + } + + updateShowSubstanceRole(event) { + this.showSubstanceRole = event.checked; + } + + updateShowCriticalParameter(event) { + this.showCriticalParameter = event.checked; + } + + getHomepageUrl() { + // Get GSRS Frontend URL fron config + this.gsrsHomeBaseUrl = this.configService.configData && this.configService.configData.gsrsHomeBaseUrl || ''; + } + + displayAmountCompose(amt, propertyType: string): string { + function formatValue(v) { + if (v) { + if (typeof v === 'object') { + if (v.display) { + return v.display; + } else if (v.value) { + return v.value; + } else { + return null; + } + } else { + return v; + } + } + return null; + } + + let ret = ''; + if (amt) { + if (typeof amt === 'object') { + if (amt) { + let addedunits = false; + let unittext = formatValue(amt.units); + if (!unittext) { + unittext = ''; + } + /* const atype = formatValue(amt.type); */ + const atype = formatValue(propertyType); + if (atype) { + ret += atype + ':' + '\n'; + } + if (amt.average || amt.high || amt.low) { + if (amt.average) { + ret += amt.average; + if (amt.units) { + ret += ' ' + unittext; + addedunits = true; + } + } + if (amt.high || amt.low) { + ret += ' ['; + if (amt.high && !amt.low) { + ret += '<' + amt.high; + } else if (!amt.high && amt.low) { + ret += '>' + amt.low; + } else if (amt.high && amt.low) { + ret += amt.low + ' to ' + amt.high; + } + ret += '] '; + if (!addedunits) { + if (amt.units) { + ret += ' ' + unittext; + addedunits = true; + } + } + } + ret += ' (average) '; + } + if (amt.highLimit || amt.lowLimit) { + ret += '\n['; + } + if (amt.highLimit && !amt.lowLimit) { + ret += '<' + amt.highLimit; + } else if (!amt.highLimit && amt.lowLimit) { + ret += '>' + amt.lowLimit; + } else if (amt.highLimit && amt.lowLimit) { + ret += amt.lowLimit + ' to ' + amt.highLimit; + } + if (amt.highLimit || amt.lowLimit) { + ret += '] '; + if (!addedunits) { + if (amt.units) { + ret += ' ' + unittext; + addedunits = true; + } + } + ret += ' (limits)'; + } + } + if (amt.nonNumericValue) { + ret += ' ' + amt.nonNumericValue; + } + } + } + return ret; + } +} diff --git a/src/app/core/substance-ssg4m/ssg4m-scheme-view/ssg4m-scheme-view.module.ts b/src/app/core/substance-ssg4m/ssg4m-scheme-view/ssg4m-scheme-view.module.ts new file mode 100644 index 000000000..d822b1836 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-scheme-view/ssg4m-scheme-view.module.ts @@ -0,0 +1,35 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Router, Routes, RouterModule } from '@angular/router'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatListModule } from '@angular/material/list'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { ScrollToModule } from '@gsrs-core/scroll-to/scroll-to.module'; +import { SubstanceFormService } from '../../substance-form/substance-form.service'; +import { SubstanceFormModule } from '../../substance-form/substance-form.module'; +import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; +import { Ssg4mSchemeViewComponent } from './ssg4m-scheme-view.component'; + +@NgModule({ + imports: [ + CommonModule, + RouterModule, + MatCheckboxModule, + MatButtonModule, + MatTooltipModule, + MatIconModule, + MatListModule, + ScrollToModule, + SubstanceFormModule, + SubstanceImageModule + ], + declarations: [ + Ssg4mSchemeViewComponent + ], + exports: [ + Ssg4mSchemeViewComponent + ] +}) +export class Ssg4mSchemeViewModule { } diff --git a/src/app/core/substance-ssg4m/ssg4m-sites/ssg4m-sites.component.html b/src/app/core/substance-ssg4m/ssg4m-sites/ssg4m-sites.component.html new file mode 100644 index 000000000..64b544de8 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-sites/ssg4m-sites.component.html @@ -0,0 +1,185 @@ + +
    + + +
    + +
    + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + {{configTitleStage}}  + + {{(stageIndex+1)}} of {{site.stages.length}} +    + + + + + + + + +
    + + +
    +
    +
    + + +
    + + +
    + + + + + +
    +
    \ No newline at end of file diff --git a/src/app/core/substance-ssg4m/ssg4m-sites/ssg4m-sites.component.scss b/src/app/core/substance-ssg4m/ssg4m-sites/ssg4m-sites.component.scss new file mode 100644 index 000000000..7f3169571 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-sites/ssg4m-sites.component.scss @@ -0,0 +1,172 @@ +.form-container { + padding: 0px 10px 0px 10px; + margin-bottom: 5px; + position: relative; +} + +.notification-backdrop { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: flex; + z-index: 10; + background-color: rgba(255, 255, 255, .8); + justify-content: center; + align-items: center; + font-size: 30px; + font-weight: bold; + color: #666; +} + +.form-row { + display: flex; + width: 100%; + + .delete-container { + padding: 0 10px 8px 0; + } + + .col-1-1 { + width: calc((100% - 20px * 1)); // 1 column + margin-right: 20px; + } + + .col-2-1 { + width: calc((100% - 20px * 1) / 2); // 2 column + margin-right: 20px; + } + + .col-3-1 { + width: calc((100% - 20px * 2) / 3); // 3 column + margin-right: 20px; + } + + .col-4-1 { + width: calc((100% - 20px * 3) / 4); // 4 column + margin-right: 20px; + } +} + +/* +.form-row { + display: flex; + justify-content: space-between; + align-items: flex-end; + + .delete-container { + padding: 0 10px 8px 0; + } + + .row { + flex-grow: 1; + padding-right: 15px; + } +} +*/ + +.references-container { + width: 100%; +} + +.divflex { + display: flex; +} + +.divflexright { + display: flex; + justify-content: flex-end; + padding-top: 10px; +} + +hr { + border: none; + border-top: 3px dotted var(--regular-orangered-color); + color: var(--hr-color); + overflow: visible; + text-align: center; + height: 5px; +} + +.bordergray { + border: 1px solid var(--regular-grey-color); +} + +.buttonstyle { + border: 2px solid var(--deep-purple-border-color-rgb); + background-color: var(--pale-border-color-rgb-3); +} + +.bordergreen { + border: 1px solid var(--regular-green-color); +} + +.margintop10px { + margin-top: 10px; +} + +.marginleft50px { + margin-left: 50px; +} + +.marginbottom30px { + margin-bottom: 30px; +} + +.fontsize18px { + font-size: 18px; +} + +.button-add-top { + z-index: 1; + position: relative; + top: -10px; + margin-right: 25px; + background-color: #FFFFFF; + border: 1px solid #007CBA; +} + +.button-add { + z-index: 1; + position: relative; + top: 30px; + margin-right: 25px; + background-color: #FFFFFF; + border: 1px solid #007CBA; +} + +.button-delete { + z-index: 1; + position: absolute; + top: -40px; + margin-right: 20px; + color: red; + background-color: #FFFFFF; + border: 1px solid red; +} + +fieldset.border { + border: solid 2px var(--fieldset-green-border-color) !important; + padding: 0 10px 10px 10px; + border-bottom: none; + /*border-radius: 8px;*/ + min-width: 0; /* override the default value of min-content */ +/*-webkit-box-shadow: 2px 3px 3px 1px var(--fieldset-box-shadow-color); + -moz-box-shadow: 2px 3px 5px 1px var(--fieldset-box-shadow-color); + box-shadow: 2px 2px 3px 1px var(--fieldset-box-shadow-color);*/ +} + +legend.border { + width: auto !important; + border: none; + border-bottom: none; + font-size: 15px; + color: var(--legend-blue-border-color-2); + font-family: Verdana; + font-weight: bold; + margin-bottom: 20px; + margin-left: 30px; + /*margin-left: 40%;*/ +} + diff --git a/src/app/core/substance-ssg4m/ssg4m-sites/ssg4m-sites.component.spec.ts b/src/app/core/substance-ssg4m/ssg4m-sites/ssg4m-sites.component.spec.ts new file mode 100644 index 000000000..b61baa852 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-sites/ssg4m-sites.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Ssg4mSitesComponent } from './ssg4m-sites.component'; + +describe('Ssg4mSitesComponent', () => { + let component: Ssg4mSitesComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ Ssg4mSitesComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(Ssg4mSitesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-ssg4m/ssg4m-sites/ssg4m-sites.component.ts b/src/app/core/substance-ssg4m/ssg4m-sites/ssg4m-sites.component.ts new file mode 100644 index 000000000..dba204d1b --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-sites/ssg4m-sites.component.ts @@ -0,0 +1,179 @@ +import { Component, OnInit, AfterViewInit, Input, OnDestroy, Output, EventEmitter } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { ScrollToService } from '../../scroll-to/scroll-to.service'; +import { Subscription } from 'rxjs'; +import { GoogleAnalyticsService } from '../../google-analytics/google-analytics.service'; +import { ConfigService } from '@gsrs-core/config/config.service'; +import { SubstanceCardBaseFilteredList, SubstanceCardBaseList } from '../../substance-form/base-classes/substance-form-base-filtered-list'; +import { SubstanceFormService } from '../../substance-form/substance-form.service'; +/* +import { take } from 'rxjs/operators'; +import { ConfigService } from '@gsrs-core/config'; +import { SubstanceFormBase } from '../../substance-form/base-classes/substance-form-base'; +import { ControlledVocabularyService } from '../../controlled-vocabulary/controlled-vocabulary.service'; +import { VocabularyTerm } from '../../controlled-vocabulary/vocabulary.model'; +*/ +import { SubstanceService } from '../../substance/substance.service'; +import { SubstanceSummary, SubstanceRelationship } from '../../substance/substance.model'; +import { SpecifiedSubstanceG4mProcess, SubstanceRelated } from '../../substance/substance.model'; +import { SubstanceDetail } from '@gsrs-core/substance/substance.model'; +import { SubstanceFormSsg4mSitesService } from './substance-form-ssg4m-sites.service'; +import { SubstanceFormSsg4mStagesService } from '../ssg4m-stages/substance-form-ssg4m-stages.service'; +import { SpecifiedSubstanceG4mSite } from '@gsrs-core/substance/substance.model'; +import { ConfirmDialogComponent } from '../../../fda/confirm-dialog/confirm-dialog.component'; + +@Component({ + selector: 'app-ssg4m-sites', + templateUrl: './ssg4m-sites.component.html', + styleUrls: ['./ssg4m-sites.component.scss'] +}) +export class Ssg4mSitesComponent implements OnInit { + @Output() tabSelectedIndexOut = new EventEmitter(); + + privateShowAdvancedSettings: boolean; + public configSettingsDisplay = {}; + configTitleStage: string; + privateTabSelectedView: string; + privateSite: SpecifiedSubstanceG4mSite; + privateProcessIndex: number; + privateSiteIndex: number; + substance: SubstanceDetail; + subscriptions: Array = []; + + constructor( + public substanceFormSsg4mSitesService: SubstanceFormSsg4mSitesService, + private substanceFormSsg4mStagesService: SubstanceFormSsg4mStagesService, + private substanceFormService: SubstanceFormService, + public gaService: GoogleAnalyticsService, + private overlayContainerService: OverlayContainer, + private scrollToService: ScrollToService, + public configService: ConfigService, + private dialog: MatDialog + ) { } + + @Input() + set site(site: SpecifiedSubstanceG4mSite) { + this.privateSite = site; + } + + get site(): SpecifiedSubstanceG4mSite { + return this.privateSite; + } + + @Input() + set processIndex(processIndex: number) { + this.privateProcessIndex = processIndex; + } + + get processIndex(): number { + return this.privateProcessIndex; + } + + @Input() + set siteIndex(siteIndex: number) { + this.privateSiteIndex = siteIndex; + // Set the Site Name + this.privateSite.siteName = 'Site ' + (this.privateSiteIndex + 1); + } + + get siteIndex(): number { + return this.privateSiteIndex; + } + + @Input() + set showAdvancedSettings(showAdvancedSettings: boolean) { + this.privateShowAdvancedSettings = showAdvancedSettings; + // Get Config Settins from config file + this.getConfigSettings(); + } + + get showAdvancedSettings(): boolean { + return this.privateShowAdvancedSettings; + } + + @Input() + set tabSelectedView(tabSelectedView: string) { + this.privateTabSelectedView = tabSelectedView; + } + + get tabSelectedView(): string { + return this.privateTabSelectedView; + } + + ngOnInit(): void { + // this.substance = this.substanceFormSsg4mSitesService.substance; + const subscription = this.substanceFormService.substance.subscribe(substance => { + this.substance = substance; + }); + this.subscriptions.push(subscription); + // Get Config variables for SSG4 + let configSsg4Form: any; + configSsg4Form = (this.configService.configData && this.configService.configData.ssg4Form) || null; + this.configTitleStage = 'Stage'; + if (configSsg4Form) { + this.configTitleStage = configSsg4Form.titles.stage || null; + if (!this.configTitleStage) { + this.configTitleStage = 'Stage'; + } + } + } + + ngOnDestroy(): void { + // this.substanceFormService.unloadSubstance(); + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + + getConfigSettings(): void { + // Get SSG4 Config Settings from config.json file to show and hide fields in the form + let configSsg4Form: any; + configSsg4Form = this.configService.configData && this.configService.configData.ssg4Form || null; + // *** IMPORTANT: get the correct value. Get 'site' json values from config + const confSettings = configSsg4Form.settingsDisplay.site; + Object.keys(confSettings).forEach(key => { + if (confSettings[key] != null) { + if (confSettings[key] === 'simple') { + this.configSettingsDisplay[key] = true; + } else if (confSettings[key] === 'advanced') { + if (this.privateShowAdvancedSettings === true) { + this.configSettingsDisplay[key] = true; + } else { + this.configSettingsDisplay[key] = false; + } + } else if (confSettings[key] === 'removed') { + this.configSettingsDisplay[key] = false; + } + } + }); + } + + addStage(processIndex: number, siteIndex: number) { + this.substanceFormSsg4mStagesService.addStage(processIndex, siteIndex); + setTimeout(() => { + this.scrollToService.scrollToElement(`stage-0`, 'center'); + }); + } + + confirmDeleteSite() { + const dialogRef = this.dialog.open(ConfirmDialogComponent, { + data: { message: 'Are you sure you want to delele Site ' + (this.siteIndex + 1) + ' for Process ' + (this.processIndex + 1) + '?' } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result && result === true) { + this.deleteSite(); + } + }); + } + + deleteSite(): void { + this.substance.specifiedSubstanceG4m.process[this.processIndex].sites.splice(this.siteIndex, 1); + } + + showSchemePreview() { + this.tabSelectedIndexOut.emit(1); + } + +} diff --git a/src/app/core/substance-ssg4m/ssg4m-sites/ssg4m-sites.module.ts b/src/app/core/substance-ssg4m/ssg4m-sites/ssg4m-sites.module.ts new file mode 100644 index 000000000..64b6f0ef4 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-sites/ssg4m-sites.module.ts @@ -0,0 +1,45 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatInputModule } from '@angular/material/input'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatButtonModule } from '@angular/material/button'; +import { MatBadgeModule } from '@angular/material/badge'; +import { ScrollToModule } from '../../scroll-to/scroll-to.module'; +// import { SubstanceFormNotesCardComponent } from './substance-form-notes-card.component'; +import { DynamicComponentLoaderModule } from '../../dynamic-component-loader/dynamic-component-loader.module'; +import { SubstanceFormModule } from '../../substance-form/substance-form.module'; +// import { SubstanceFormSsg4mSitesCardComponent} from '../ssg4m-sites/ssg4m-sites.module' +import { Ssg4mStagesModule } from '../ssg4m-stages/substance-form-ssg4m-stages.module'; +import { Ssg4mSitesComponent } from './ssg4m-sites.component'; + +@NgModule({ + imports: [ + CommonModule, + SubstanceFormModule, + MatDividerModule, + MatIconModule, + MatTooltipModule, + MatButtonModule, + ScrollToModule, + MatFormFieldModule, + ReactiveFormsModule, + FormsModule, + MatPaginatorModule, + MatInputModule, + MatBadgeModule, + Ssg4mStagesModule + ], + declarations: [ + Ssg4mSitesComponent + ], + exports: [ + Ssg4mSitesComponent + ] +}) + +export class Ssg4mSitesModule { } diff --git a/src/app/core/substance-ssg4m/ssg4m-sites/substance-form-ssg4m-sites.service.spec.ts b/src/app/core/substance-ssg4m/ssg4m-sites/substance-form-ssg4m-sites.service.spec.ts new file mode 100644 index 000000000..e026418a7 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-sites/substance-form-ssg4m-sites.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { SubstanceFormSsg4mSitesService } from './substance-form-ssg4m-sites.service'; + +describe('SubstanceFormSsg4mSitesService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [SubstanceFormSsg4mSitesService] + }); + }); + + it('should be created', inject([SubstanceFormSsg4mSitesService], (service: SubstanceFormSsg4mSitesService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/app/core/substance-ssg4m/ssg4m-sites/substance-form-ssg4m-sites.service.ts b/src/app/core/substance-ssg4m/ssg4m-sites/substance-form-ssg4m-sites.service.ts new file mode 100644 index 000000000..29201595a --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-sites/substance-form-ssg4m-sites.service.ts @@ -0,0 +1,64 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { SubstanceFormServiceBase } from '../../substance-form/base-classes/substance-form-service-base'; +import { SubstanceFormService } from '../../substance-form/substance-form.service'; +import { SpecifiedSubstanceG4mSite, SpecifiedSubstanceG4mStage } from '@gsrs-core/substance/substance.model'; + +@Injectable({ + providedIn: 'root' +}) + +@Injectable() +export class SubstanceFormSsg4mSitesService extends SubstanceFormServiceBase> { + + constructor( + public substanceFormService: SubstanceFormService + ) { + super(substanceFormService); + } + + initSubtanceForm(): void { + super.initSubtanceForm(); + const subscription = this.substanceFormService.substance.subscribe(substance => { + this.substance = substance; + if (!this.substance.specifiedSubstanceG4m) { + this.substance.specifiedSubstanceG4m = {}; + } + if (!this.substance.specifiedSubstanceG4m.process) { + this.substance.specifiedSubstanceG4m.process = [{sites:[]}]; + } + if (!this.substance.specifiedSubstanceG4m.process) { + this.substance.specifiedSubstanceG4m.process = [{sites:[]}]; + } + + this.substanceFormService.resetState(); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process); + }); + this.subscriptions.push(subscription); + } + + get specifiedSubstanceG4mSite(): Observable> { + return this.propertyEmitter.asObservable(); + } + + addSite(processIndex): void { + const newSite: SpecifiedSubstanceG4mSite = { + // references: [], + // access: [], + siteName: '', + stages: [] + }; + this.substance.specifiedSubstanceG4m.process[processIndex].sites.push(newSite); + this.propertyEmitter.next( this.substance.specifiedSubstanceG4m.process[processIndex].sites); + } + + deleteSite(site: SpecifiedSubstanceG4mSite): void { + /* + const siteIndex = this.substance.specifiedSubstanceG4m.process.findIndex(pro => pro.$$deletedCode === pro.$$deletedCode); + if (processIndex > -1) { + this.substance.specifiedSubstanceG4m.process.splice(processIndex, 1); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process); + }*/ + } + +} diff --git a/src/app/core/substance-ssg4m/ssg4m-stages/ssg4m-stages-form.component.html b/src/app/core/substance-ssg4m/ssg4m-stages/ssg4m-stages-form.component.html new file mode 100644 index 000000000..b6b891b16 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-stages/ssg4m-stages-form.component.html @@ -0,0 +1,199 @@ +
    +
    +
    + Deleted  + +
    + +
    + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + +
    + + + +
    + + + +
    +
    + + + Input Materials + + + +
    + + +
    + + +
    + + +
    +
    +
    + + + + +
    +
    + + + {{configTitleProcessingMaterials}} + + + +
    + + +
    + + +
    + + +
    +
    +
    + + + + +
    +
    + + + Resulting Materials + + +
    + + +
    + + +
    + + +
    +
    +
    + + + + +
    +
    + + Process Controls  + + +
    + + +
    + +
    + + +
    +
    +
    + + +
    +
    \ No newline at end of file diff --git a/src/app/core/substance-ssg4m/ssg4m-stages/ssg4m-stages-form.component.scss b/src/app/core/substance-ssg4m/ssg4m-stages/ssg4m-stages-form.component.scss new file mode 100644 index 000000000..4bb579fec --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-stages/ssg4m-stages-form.component.scss @@ -0,0 +1,196 @@ +.form-container { + padding: 5px 10px 12px 10px; + position: relative; +} + +.notification-backdrop { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: flex; + z-index: 10; + background-color: var(--notif-backdrop-bg-color); + justify-content: center; + align-items: center; + font-size: 30px; + font-weight: bold; + color: var(--notif-backdrop-color); +} + +.form-row { + display: flex; + width: 100%; + + + .col-1-1 { + width: calc((100% - 20px * 1)); // 1 column + margin-right: 20px; + } + + .col-2-1 { + width: calc((100% - 20px * 1) / 2); // 2 column + margin-right: 20px; + } + + .col-3-1 { + width: calc((100% - 20px * 2) / 3); // 3 column + margin-right: 20px; + } + + .col-4-1 { + width: calc((100% - 20px * 3) / 4); // 4 column + margin-right: 20px; + } +} +/* +.form-row { + display: flex; + justify-content: space-between; + align-items: flex-end; + + .delete-container { + padding: 0 10px 8px 0; + } + + .row { + flex-grow: 1; + padding-right: 15px; + } +} +*/ + +.references-container { + width: 100%; +} + +.materialborder { + padding-top: 10px; + margin-top: 15px; + border: 2px solid var(--pale-border-color-rgb-4); +} + +.materialtitle { + font-size: 18px; + padding-left: 10px; +} + +.divflex { + display: flex; +} + +.divflexright { + display: flex; + justify-content: flex-end; +} + +.bordergray { + border: 1px solid var(--regular-grey-color); +} + +.borderorange { + border: 1px solid var(--regular-orangered-color); +} + +.fontsize18px { + font-size: 18px; +} + +.colorgreen { + color: var(--regular-green-color); +} + +.margintop10px { + margin-top: 10px; +} + +.margintop20px { + margin-top: 20px; +} + +.margintop40px { + margin-top: 40px; +} + +.marginleft40px { + margin-left: 40px; +} + +.marginbottom20px { + margin-top: 20px; +} + +.button-add { + z-index: 1; + position: relative; + top: 25px; + margin-right: 20px; + background-color: #FFFFFF; + border: 1px solid #007CBA; +} + +.button-delete-stage { + z-index: 1; + position: absolute; + top: -60px; + margin-right: 20px; + color: red; + background-color: #FFFFFF; + border: 1px solid red; +} + +.button-delete { + display: flex; + justify-content: flex-end; + margin-top: -15px; +} + +.button-insert-before { + z-index: 1; + position: absolute; + top: -60px; + margin-right: 180px; + color: #007CBA; + background-color: #FFFFFF; + border: 1px solid #007CBA; +} + +.button-insert-after { + z-index: 1; + position: absolute; + top: -60px; + margin-right: 180px; + color: #007CBA; + background-color: #FFFFFF; + border: 1px solid #007CBA; +} + +fieldset.border { + border: solid 2px #A0A0A3 !important; + padding: 0 10px 10px 10px; + border-bottom: none; + border-radius: 3px; + margin-bottom: 30px; + min-width: 0; /* override the default value of min-content */ + /* + -webkit-box-shadow: 2px 3px 3px 1px rgba(110,104,110,0.64); + -moz-box-shadow: 2px 3px 5px 1px rgba(110,104,110,0.64); + box-shadow: 2px 2px 3px 1px rgba(110,104,110,0.64); */ +} + +legend.border { + width: auto !important; + border: none; + border-bottom: none; + font-size: 15px; + /* FDA COLOR BLUE CODE Hex: #007CBA */ + color: #007CBA; + font-family: Verdana; + font-weight: bold; + margin-bottom: 10px; +} + +.mat-menu-item { + color: #007CBA !important; +} diff --git a/src/app/core/substance-ssg4m/ssg4m-stages/ssg4m-stages-form.component.spec.ts b/src/app/core/substance-ssg4m/ssg4m-stages/ssg4m-stages-form.component.spec.ts new file mode 100644 index 000000000..a490cb8d1 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-stages/ssg4m-stages-form.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Ssg4mStagesFormComponent } from './ssg4m-stages-form.component'; + +describe('Ssg4mStagesComponent', () => { + let component: Ssg4mStagesFormComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ Ssg4mStagesFormComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(Ssg4mStagesFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-ssg4m/ssg4m-stages/ssg4m-stages-form.component.ts b/src/app/core/substance-ssg4m/ssg4m-stages/ssg4m-stages-form.component.ts new file mode 100644 index 000000000..1ac2de87c --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-stages/ssg4m-stages-form.component.ts @@ -0,0 +1,233 @@ +import { Component, OnInit, OnDestroy, AfterViewInit, Input } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { ScrollToService } from '../../scroll-to/scroll-to.service'; +import { Subscription } from 'rxjs'; +import { GoogleAnalyticsService } from '../../google-analytics/google-analytics.service'; +import { ConfigService } from '@gsrs-core/config/config.service'; +import { SubstanceCardBaseFilteredList, SubstanceCardBaseList } from '../../substance-form/base-classes/substance-form-base-filtered-list'; +import { SubstanceFormService } from '../../substance-form/substance-form.service'; +/* +import { take } from 'rxjs/operators'; +import { ConfigService } from '@gsrs-core/config'; +import { SubstanceFormBase } from '../../substance-form/base-classes/substance-form-base'; +import { ControlledVocabularyService } from '../../controlled-vocabulary/controlled-vocabulary.service'; +import { VocabularyTerm } from '../../controlled-vocabulary/vocabulary.model'; +*/ +import { SubstanceService } from '../../substance/substance.service'; +import { SubstanceSummary, SubstanceRelationship } from '../../substance/substance.model'; +import { SpecifiedSubstanceG4mProcess, SubstanceRelated } from '../../substance/substance.model'; +import { SubstanceDetail } from '@gsrs-core/substance/substance.model'; +import { SubstanceFormSsg4mStagesService } from './substance-form-ssg4m-stages.service'; +import { SpecifiedSubstanceG4mStage } from '@gsrs-core/substance/substance.model'; +import { ConfirmDialogComponent } from '../../../fda/confirm-dialog/confirm-dialog.component'; + +@Component({ + selector: 'app-ssg4m-stages-form', + templateUrl: './ssg4m-stages-form.component.html', + styleUrls: ['./ssg4m-stages-form.component.scss'] +}) +export class Ssg4mStagesFormComponent implements OnInit, OnDestroy { + public configSettingsDisplay = {}; + configSsg4Form: any; + configTitleStage: string; + configTitleProcessingMaterials: string; + privateStage: SpecifiedSubstanceG4mStage; + privateProcessIndex: number; + privateSiteIndex: number; + privateStageIndex: number; + privateShowAdvancedSettings: boolean; + privateTabSelectedView: string; + substance: SubstanceDetail; + subscriptions: Array = []; + + constructor( + public substanceFormSsg4mStagesService: SubstanceFormSsg4mStagesService, + private substanceFormService: SubstanceFormService, + public gaService: GoogleAnalyticsService, + private overlayContainerService: OverlayContainer, + private scrollToService: ScrollToService, + public configService: ConfigService, + private dialog: MatDialog + ) { } + + @Input() + set stage(stage: SpecifiedSubstanceG4mStage) { + this.privateStage = stage; + } + + get stage(): SpecifiedSubstanceG4mStage { + return this.privateStage; + } + + @Input() + set processIndex(processIndex: number) { + this.privateProcessIndex = processIndex; + } + + get processIndex(): number { + return this.privateProcessIndex; + } + + @Input() + set siteIndex(siteIndex: number) { + this.privateSiteIndex = siteIndex; + } + + get siteIndex(): number { + return this.privateSiteIndex; + } + + @Input() + set stageIndex(stageIndex: number) { + this.privateStageIndex = stageIndex; + // Set the Stage Name + // alert("STAGE INDEX: " + stageIndex); + this.privateStage.stageNumber = String(this.privateStageIndex + 1); + } + + get stageIndex(): number { + return this.privateStageIndex; + } + + @Input() + set showAdvancedSettings(showAdvancedSettings: boolean) { + this.privateShowAdvancedSettings = showAdvancedSettings; + // Get Config Settins from config file + this.getConfigSettings(); + } + + get showAdvancedSettings(): boolean { + return this.privateShowAdvancedSettings; + } + + @Input() + set tabSelectedView(tabSelectedView: string) { + this.privateTabSelectedView = tabSelectedView; + } + + get tabSelectedView(): string { + return this.privateTabSelectedView; + } + + ngOnInit(): void { + // this.substance = this.substanceFormSsg4mStagesService.substance; + const subscription = this.substanceFormService.substance.subscribe(substance => { + this.substance = substance; + }); + this.subscriptions.push(subscription); + + // Get Config variables for SSG4m + this.configSsg4Form = (this.configService.configData && this.configService.configData.ssg4Form) || null; + this.configTitleStage = 'Stage'; + this.configTitleProcessingMaterials = "Processing Materials"; + if (this.configSsg4Form) { + this.configTitleStage = this.configSsg4Form.titles.stage || null; + if (!this.configTitleStage) { + this.configTitleStage = 'Stage'; + } + this.configTitleProcessingMaterials = this.configSsg4Form.titles.processingMaterials || null; + if (!this.configTitleProcessingMaterials) { + this.configTitleProcessingMaterials = 'Processing Materials'; + } + } + } + + ngOnDestroy(): void { + // this.substanceFormService.unloadSubstance(); + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + + getConfigSettings(): void { + // Get SSG4 Config Settings from config.json file to show and hide fields in the form + let configSsg4Form: any; + configSsg4Form = this.configService.configData && this.configService.configData.ssg4Form || null; + // Get 'stage' json values from config + const confSettings = configSsg4Form.settingsDisplay.stage; + Object.keys(confSettings).forEach(key => { + if (confSettings[key] != null) { + if (confSettings[key] === 'simple') { + this.configSettingsDisplay[key] = true; + } else if (confSettings[key] === 'advanced') { + if (this.privateShowAdvancedSettings === true) { + this.configSettingsDisplay[key] = true; + } else { + this.configSettingsDisplay[key] = false; + } + } else if (confSettings[key] === 'removed') { + this.configSettingsDisplay[key] = false; + } + } + }); + } + + insertStage(processIndex: number, siteIndex: number, stageIndex: number, insertDirection?: string): void { + this.substanceFormSsg4mStagesService.insertStage(processIndex, siteIndex, stageIndex, insertDirection); + setTimeout(() => { + this.scrollToService.scrollToElement(`substance-process-0`, 'center'); + }); + } + + duplicateStage(processIndex: number, siteIndex: number, stageIndex: number, insertDirection?: string): void { + this.substanceFormSsg4mStagesService.duplicateStage(processIndex, siteIndex, stageIndex, insertDirection); + setTimeout(() => { + this.scrollToService.scrollToElement(`substance-stage-duplicate-0`, 'center'); + }); + } + + addCriticalParameter(processIndex: number, siteIndex: number, stageIndex: number) { + this.substanceFormSsg4mStagesService.addCriticalParameter(processIndex, siteIndex, stageIndex); + setTimeout(() => { + this.scrollToService.scrollToElement(`substance-process-site-stage-criticalParam-0`, 'center'); + }); + } + + addStartingMaterial(processIndex: number, siteIndex: number, stageIndex: number) { + this.substanceFormSsg4mStagesService.addStartingMaterials(processIndex, siteIndex, stageIndex); + setTimeout(() => { + this.scrollToService.scrollToElement(`substance-process-site-stage-startMat-0`, 'center'); + }); + } + + addProcessingMaterial(processIndex: number, siteIndex: number, stageIndex: number) { + this.substanceFormSsg4mStagesService.addProcessingMaterials(processIndex, siteIndex, stageIndex); + setTimeout(() => { + this.scrollToService.scrollToElement(`substance-process-site-stage-processMat-0`, 'center'); + }); + } + + addResultingMaterial(processIndex: number, siteIndex: number, stageIndex: number) { + this.substanceFormSsg4mStagesService.addResultingMaterials(processIndex, siteIndex, stageIndex); + setTimeout(() => { + this.scrollToService.scrollToElement(`substance-process-site-stage-resultMat-0`, 'center'); + }); + } + + confirmDeleteStage() { + const dialogRef = this.dialog.open(ConfirmDialogComponent, { + data: { message: 'Are you sure you want to delele ' + this.configTitleStage + ' ' + (this.stageIndex + 1) + ' for Site ' + (this.siteIndex + 1) + ' for Process ' + (this.processIndex + 1) + '?' } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result && result === true) { + this.deleteStage(); + } + }); + } + + deleteStage(): void { + this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages.splice(this.stageIndex, 1); + } + + /* + addStage(processIndex: number, siteIndex: number) { + this.substanceFormSsg4mStagesService.addStage(processIndex, siteIndex); + setTimeout(() => { + this.scrollToService.scrollToElement(`substance-process-site-stage-0`, 'center'); + }); + } + */ +} + diff --git a/src/app/core/substance-ssg4m/ssg4m-stages/substance-form-ssg4m-stages.module.ts b/src/app/core/substance-ssg4m/ssg4m-stages/substance-form-ssg4m-stages.module.ts new file mode 100644 index 000000000..761f96513 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-stages/substance-form-ssg4m-stages.module.ts @@ -0,0 +1,129 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { CommonModule, formatDate } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; +import { MatInputModule } from '@angular/material/input'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatButtonModule } from '@angular/material/button'; +import { MatButtonToggleModule } from '@angular/material/button-toggle'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatTableModule } from '@angular/material/table'; +import { MatListModule } from '@angular/material/list'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatBadgeModule } from '@angular/material/badge'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatProgressBarModule} from '@angular/material/progress-bar'; +import { NgxJsonViewerModule} from 'ngx-json-viewer'; +// GSRS Imports +import { FileSelectModule } from 'file-select'; +import { ScrollToModule } from '@gsrs-core/scroll-to/scroll-to.module'; +import { ExpandDetailsModule } from '@gsrs-core/expand-details/expand-details.module'; +import { SubstanceFormModule } from '../../substance-form/substance-form.module'; +import { SubstanceSelectorModule } from '@gsrs-core/substance-selector/substance-selector.module'; +import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; + +import { CvInputComponent} from '@gsrs-core/substance-form/cv-input/cv-input.component'; +import { CvDialogComponent} from '@gsrs-core/substance-form/cv-dialog/cv-dialog.component'; +import { JsonDialogComponent} from '@gsrs-core/substance-form/json-dialog/json-dialog.component'; +import { AuditInfoComponent} from '@gsrs-core/substance-form/audit-info/audit-info.component'; + +// import { SubmitSuccessDialogComponent } from './submit-success-dialog/submit-success-dialog.component'; +// import { MergeConceptDialogComponent} from '@gsrs-core/substance-form/merge-concept-dialog/merge-concept-dialog.component'; +// import { SubstanceFormComponent } from './substance-form.component'; +// import { CanActivateSubstanceForm } from './can-activate-substance-form'; +// import { CanRegisterSubstanceForm } from './can-register-substance-form'; +// import { SubstanceFormService } from '../substance-form.service'; +// import { AccessManagerComponent } from './access-manager/access-manager.component'; +// import { SubstanceSsg4mService } from './substance-ssg4m-form.service'; +// import { SubstanceFormComponent } from '../substance-form/substance-form.component'; +// import { SubstanceFormSsg4mSitesService } from './ssg4m-sites/substance-form-ssg4m-sites.service.'; +import { Ssg4mStagesFormComponent } from './ssg4m-stages-form.component'; +import { Ssg4mCriticalParameterModule } from '../ssg4m-critical-parameter/ssg4m-critical-parameter.module'; +import { SubstanceFormSsg4mStartingMaterialsModule } from '../ssg4m-starting-materials/substance-form-ssg4m-starting-materials.module'; +import { SubstanceFormSsg4mProcessingMaterialsModule } from '../ssg4m-processing-materials/substance-form-ssg4m-processing-materials.module'; +import { SubstanceFormSsg4mResultingMaterialsModule } from '../ssg4m-resulting-materials/substance-form-ssg4m-resulting-materials.module'; +import { Ssg4mSchemeViewModule } from '../ssg4m-scheme-view/ssg4m-scheme-view.module'; +import { SubstanceFormSsg4mStagesService } from './substance-form-ssg4m-stages.service'; + +@NgModule({ + imports: [ + CommonModule, + ReactiveFormsModule, + FormsModule, + MatFormFieldModule, + MatMenuModule, + MatCheckboxModule, + MatButtonModule, + MatIconModule, + MatTooltipModule, + MatTabsModule, + MatDividerModule, + MatSelectModule, + MatInputModule, + MatChipsModule, + MatAutocompleteModule, + ScrollToModule, + MatDialogModule, + MatTableModule, + MatExpansionModule, + MatBadgeModule, + MatRadioModule, + ExpandDetailsModule, + SubstanceSelectorModule, + MatListModule, + FileSelectModule, + MatButtonToggleModule, + NgxJsonViewerModule, + RouterModule, + SubstanceImageModule, + MatProgressBarModule, + MatProgressSpinnerModule, + SubstanceFormModule, + Ssg4mCriticalParameterModule, + SubstanceFormSsg4mStartingMaterialsModule, + SubstanceFormSsg4mProcessingMaterialsModule, + SubstanceFormSsg4mResultingMaterialsModule, + Ssg4mSchemeViewModule + ], + declarations: [ + // SubstanceFormComponent, + Ssg4mStagesFormComponent + // CvInputComponent, + // CvDialogComponent, + // JsonDialogComponent + ], + exports: [ + Ssg4mStagesFormComponent + // SubstanceFormComponent, + // SubstanceSsg4ManufactureFormComponent, + // CvInputComponent, + // CvDialogComponent, + // JsonDialogComponent, + ], + entryComponents: [ + // CvDialogComponent, + // JsonDialogComponent, + // AuditInfoComponent, + // SubmitSuccessDialogComponent, + ] +}) + +export class Ssg4mStagesModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: Ssg4mStagesModule, + providers: [ + ] + }; + } +} diff --git a/src/app/core/substance-ssg4m/ssg4m-stages/substance-form-ssg4m-stages.service.spec.ts b/src/app/core/substance-ssg4m/ssg4m-stages/substance-form-ssg4m-stages.service.spec.ts new file mode 100644 index 000000000..2873f682a --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-stages/substance-form-ssg4m-stages.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { SubstanceFormSsg4mStagesService } from './substance-form-ssg4m-stages.service'; + +describe('SubstanceFormSsg4mStagesService', () => { + let service: SubstanceFormSsg4mStagesService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(SubstanceFormSsg4mStagesService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-ssg4m/ssg4m-stages/substance-form-ssg4m-stages.service.ts b/src/app/core/substance-ssg4m/ssg4m-stages/substance-form-ssg4m-stages.service.ts new file mode 100644 index 000000000..ca0b4a3e1 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-stages/substance-form-ssg4m-stages.service.ts @@ -0,0 +1,549 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { SubstanceFormServiceBase } from '../../substance-form/base-classes/substance-form-service-base'; +import { SubstanceFormService } from '../../substance-form/substance-form.service'; +import { SpecifiedSubstanceG4mCriticalParameter, SpecifiedSubstanceG4mStage, SpecifiedSubstanceG4mStartingMaterial } from '@gsrs-core/substance/substance.model'; +import { SpecifiedSubstanceG4mProcessingMaterial, SpecifiedSubstanceG4mResultingMaterial, SpecifiedSubstanceG4mManufacturerDetails, SpecifiedSubstanceG4mAcceptanceCriteria } from '@gsrs-core/substance/substance.model'; +import { SubstanceFormSsg4mResultingMaterialsModule } from '../ssg4m-resulting-materials/substance-form-ssg4m-resulting-materials.module'; + +@Injectable({ + providedIn: 'root' +}) +export class SubstanceFormSsg4mStagesService extends SubstanceFormServiceBase> { + sourceStageToCopy: SpecifiedSubstanceG4mStage; + sourceStartingMatObj: SpecifiedSubstanceG4mStartingMaterial; + sourceStartingMatRefUuid: string; + sourceResultingMatObj: SpecifiedSubstanceG4mResultingMaterial; + sourceResultingMatRefUuid: string; + sourceResultingMatList: Array; + + constructor( + public substanceFormService: SubstanceFormService + ) { + super(substanceFormService); + } + + initSubtanceForm(): void { + super.initSubtanceForm(); + const subscription = this.substanceFormService.substance.subscribe(substance => { + this.substance = substance; + if (!this.substance.specifiedSubstanceG4m) { + this.substance.specifiedSubstanceG4m = {}; + } + if (!this.substance.specifiedSubstanceG4m.process) { + this.substance.specifiedSubstanceG4m.process = [{ sites: [] }]; + } + + this.substanceFormService.resetState(); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process); + }); + this.subscriptions.push(subscription); + } + + get specifiedSubstanceG4mStage(): Observable> { + return this.propertyEmitter.asObservable(); + } + + addCriticalParameter(processIndex: number, siteIndex: number, stageIndex: number): void { + const newCriticalParam: SpecifiedSubstanceG4mCriticalParameter = { value: {} }; + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].criticalParameters.push(newCriticalParam); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].criticalParameters); + } + + addStartingMaterials(processIndex: number, siteIndex: number, stageIndex: number): void { + const newStartMat: SpecifiedSubstanceG4mStartingMaterial = { substanceRole: 'Starting Material'}; + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].startingMaterials.push(newStartMat); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].startingMaterials); + } + + addProcessingMaterials(processIndex: number, siteIndex: number, stageIndex: number): void { + const newProcessMat: SpecifiedSubstanceG4mProcessingMaterial = { substanceRole: 'Solvent'}; + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].processingMaterials.push(newProcessMat); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].processingMaterials); + } + + addResultingMaterials(processIndex: number, siteIndex: number, stageIndex: number): void { + const newResultingMat: SpecifiedSubstanceG4mResultingMaterial = { substanceRole: 'Intermediate'}; + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].resultingMaterials.push(newResultingMat); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].resultingMaterials); + } + + addStartingManufacturerDetails(processIndex: number, siteIndex: number, stageIndex: number, startingMaterialIndex: number): void { + const newStartingManuDetail: SpecifiedSubstanceG4mManufacturerDetails = {}; + + if (this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].startingMaterials[startingMaterialIndex].manufacturerDetails == null) { + // this.substance.specifiedSubstanceG4m.process = [{ sites: [] }]; + + // alert(JSON.stringify(this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].startingMaterials[startingMaterialIndex])); + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].startingMaterials[startingMaterialIndex].manufacturerDetails = []; + } + // newStartObj.manufactureDetails = []; + // newStartObj.manufactureDetails.push(newStartingManuDetail); + + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].startingMaterials[startingMaterialIndex].manufacturerDetails.push(newStartingManuDetail); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].startingMaterials[startingMaterialIndex].manufacturerDetails); + } + + addResultingManufacturerDetails(processIndex: number, siteIndex: number, stageIndex: number, processingMaterialIndex: number): void { + const newProcessingManuDetail: SpecifiedSubstanceG4mManufacturerDetails = {}; + + if (this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].processingMaterials[processingMaterialIndex].manufacturerDetails == null) { + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].processingMaterials[processingMaterialIndex].manufacturerDetails = []; + } + + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].processingMaterials[processingMaterialIndex].manufacturerDetails.push(newProcessingManuDetail); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].processingMaterials[processingMaterialIndex].manufacturerDetails); + } + + addStartingAcceptanceCriteria(processIndex: number, siteIndex: number, stageIndex: number, startingMaterialIndex: number): void { + const newAcceptanceCriteria: SpecifiedSubstanceG4mAcceptanceCriteria = {}; + + if (this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].startingMaterials[startingMaterialIndex].acceptanceCriterias == null) { + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].startingMaterials[startingMaterialIndex].acceptanceCriterias = []; + } + + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].startingMaterials[startingMaterialIndex].acceptanceCriterias.push(newAcceptanceCriteria); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].startingMaterials[startingMaterialIndex].acceptanceCriterias); + } + + addProcessingAcceptanceCriteria(processIndex: number, siteIndex: number, stageIndex: number, processingMaterialIndex: number): void { + const newAcceptanceCriteria: SpecifiedSubstanceG4mAcceptanceCriteria = {}; + + if (this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].processingMaterials[processingMaterialIndex].acceptanceCriterias == null) { + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].processingMaterials[processingMaterialIndex].acceptanceCriterias = []; + } + + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].processingMaterials[processingMaterialIndex].acceptanceCriterias.push(newAcceptanceCriteria); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].processingMaterials[processingMaterialIndex].acceptanceCriterias); + } + + addResultingAcceptanceCriteria(processIndex: number, siteIndex: number, stageIndex: number, resultingMaterialIndex: number): void { + const newAcceptanceCriteria: SpecifiedSubstanceG4mAcceptanceCriteria = {}; + + if (this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].resultingMaterials[resultingMaterialIndex].acceptanceCriterias == null) { + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].resultingMaterials[resultingMaterialIndex].acceptanceCriterias = []; + } + + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].resultingMaterials[resultingMaterialIndex].acceptanceCriterias.push(newAcceptanceCriteria); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].resultingMaterials[resultingMaterialIndex].acceptanceCriterias); + } + + addStage(processIndex: number, siteIndex: number): void { + const stageIndex = this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages.length - 1; + const newStageIndex = (stageIndex + 2).toString(); + + const newStage: SpecifiedSubstanceG4mStage = { + stageNumber: newStageIndex, + criticalParameters: [], + startingMaterials: [], + processingMaterials: [], + resultingMaterials: [] + }; + + // Get Current/Source Stage to Copy (Resulting Material -> Starting Material) to New Stage + // Set the previous Stage to copy to new Stage, if there is at least one stages + // When adding a new Stage, and if there is Substance Name in the previous Resulting Material, copy/add to + // Starting Material in the new Stage. + // Get the last index or push/add/copy to the Starting Material at the Last + // Get New/Next Stage + + if (stageIndex >= 0) { + this.setSourceStageToCopy(processIndex, siteIndex, stageIndex); + + // If Substance Name exists in Resulting Materials in Current/Source Stage, then copy to Starting Material in next/new Stage + // If new Stage is first one in the list, do not copy Resulting Material + this.setSourceResultingToCopy(); + } + + // Add new Stage at the end of the list + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages.push(newStage); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages); + + //if (this.sourceResultingMatObj && this.sourceResultingMatRefUuid) { + if (this.sourceResultingMatList.length > 0) { + this.copyResultingToStarting(processIndex, siteIndex, stageIndex + 1); + } + } + + insertStage(processIndex: number, siteIndex: number, stageIndex: number, insertDirection?: string): void { + const newStage: SpecifiedSubstanceG4mStage = { + stageNumber: '', + criticalParameters: [], + startingMaterials: [], + processingMaterials: [], + resultingMaterials: [] + }; + + // Get Current/Source Stage to Copy (Resulting Material -> Starting Material) to New Stage + this.setSourceStageToCopy(processIndex, siteIndex, stageIndex); + + // Insert/Add New Stage, before or after the current/source Stage + let newStageIndex = 0; + if (insertDirection && insertDirection === 'before') { + this.setSourceStartingToCopy(); + newStageIndex = stageIndex; + // this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages.splice(newStageIndex, 0, newStage); + // stageIndexNew = stageIndex; + } else { // after + this.setSourceResultingToCopy(); + newStageIndex = stageIndex + 1; + // this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages.splice(newStageIndex, 0, newStage); + // stageIndexNew = stageIndex + 1; + } + + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages.splice(newStageIndex, 0, newStage); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages); + + if ((this.sourceStartingMatObj && this.sourceStartingMatRefUuid && newStageIndex >= 0) && (insertDirection && insertDirection === 'before')) { + this.copyStartingToResulting(processIndex, siteIndex, newStageIndex); + + // if Inserting before, and the current Stage is not the first Stage, copy the Resulting Material to Starting Material in new stage. + if ((stageIndex >= 1) && (insertDirection && insertDirection === 'before')) { + // Get Current/Source Stage to Copy (Resulting Material -> Starting Material) to New Stage + this.setSourceStageToCopy(processIndex, siteIndex, newStageIndex - 1); + this.setSourceResultingToCopy(); + this.copyResultingToStarting(processIndex, siteIndex, newStageIndex); + } + } + + // If Substance Name exists in Resulting Materials in Current/Source Stage, then copy to Starting Material in next/new Stage + // If new Stage is first one in the list, do not copy Resulting Material + // if ((this.sourceResultingMatObj && this.sourceResultingMatRefUuid && newStageIndex > 0) && (insertDirection && insertDirection === 'after')) { + if ((this.sourceResultingMatList.length > 0 && newStageIndex > 0) && (insertDirection && insertDirection === 'after')) { + this.copyResultingToStarting(processIndex, siteIndex, newStageIndex); + + // if Inserting before, and the current Stage is not the first Stage, copy the Resulting Material to Starting Material in new stage. + if ((stageIndex >= 0) && (insertDirection && insertDirection === 'after')) { + // Get Current/Source Stage to Copy (Resulting Material -> Starting Material) to New Stage + this.setSourceStageToCopy(processIndex, siteIndex, newStageIndex + 1); + this.setSourceStartingToCopy(); + this.copyStartingToResulting(processIndex, siteIndex, newStageIndex); + } + } + + /* + // Copy Resulting Material to Starting Material + // When adding a new Stage, and if there is Substance Name in the previous Resulting Material, copy/add to + // Starting Material in the new Stage. + // Get the last index or push/add/copy to the Starting Material at the Last + // Get New/Next Stage + const lastStageIndex = this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages.length - 2; + // Get Last Stage to copy Resulting Materials + alert('lastStageIndex: ' + lastStageIndex); + if (lastStageIndex >= 0) { + const lastStageObj = this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[lastStageIndex]; + const lastResultIndex = lastStageObj.resultingMaterials.length - 1; + + this.copyResultingToStarting(processIndex, siteIndex, lastStageIndex, lastResultIndex); + } + */ + } + + duplicateStage(processIndex: number, siteIndex: number, stageIndex: number, insertDirection?: string): void { + // Get this/current stage record + // const thisStageObj = this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex]; + + // Get Current/Source Stage to Copy (Resulting Material -> Starting Material) to New Stage + this.setSourceStageToCopy(processIndex, siteIndex, stageIndex); + + // Copy current stage to new stage + let newStage: SpecifiedSubstanceG4mStage = {}; + // newStage.startingMaterials = this.sourceStageToCopy.startingMaterials; + // newStage.processingMaterials = this.sourceStageToCopy.resultingMaterials; + let newStage1 = JSON.stringify(this.sourceStageToCopy); + newStage = JSON.parse(newStage1); + let stageIndexNew = 0; + if (insertDirection && insertDirection === 'before') { + stageIndexNew = stageIndex + 1; + // newStage.stageNumber = '343434'; //stageIndexNew.toString(); + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages.splice(stageIndex, 0, newStage); + } else { // after + stageIndexNew = stageIndex + 2; + // newStage.stageNumber = stageIndexNew.toString(); + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages.splice(stageIndex + 1, 0, newStage); + } + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages); + } + + setSourceStageToCopy(processIndex: number, siteIndex: number, stageIndex: number): void { + // Get Stage that will be copied to new Stage + this.sourceStageToCopy = this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex]; + } + + setSourceStartingToCopy() { + this.sourceStartingMatObj = null; + if (this.sourceStageToCopy !== null && this.sourceStageToCopy !== undefined) { + const startingLength = this.sourceStageToCopy.startingMaterials.length; + // If Starting Materials exists in the source Stage, get the Substance Uuid + if (startingLength > 0) { + // Get this/current Starting Material Object + this.sourceStartingMatObj = this.sourceStageToCopy.startingMaterials[startingLength - 1]; + + const thisStartingMatSubName = this.sourceStartingMatObj.substanceName; + // Get this Resulting Material refUuid from Substance Name + if (thisStartingMatSubName) { + this.sourceStartingMatRefUuid = thisStartingMatSubName.refuuid; + } else { + this.sourceStartingMatRefUuid = null; + } + } else { + this.sourceStartingMatObj = null; + } + } + } + + setSourceResultingToCopy(selectedResultingIndex?: number) { + this.sourceResultingMatObj = null; + // If source Stage exists, then get Source Resulting Material + if (this.sourceStageToCopy !== null && this.sourceStageToCopy !== undefined) { + const sourceResultingLength = this.sourceStageToCopy.resultingMaterials.length; + + // If Source Resulting Materials exists in the source Stage, then copy + if (sourceResultingLength > 0) { + // If selectedResultingIndex exists, Get the selected Resulting Material Object + // Only copy the selected resulting material item. + if (selectedResultingIndex >= 0) { + this.sourceResultingMatObj = this.sourceStageToCopy.resultingMaterials[selectedResultingIndex]; + + const thisResultMatSubName = this.sourceResultingMatObj.substanceName; + // Get this Resulting Material refUuid from Substance Name + if (thisResultMatSubName) { + this.sourceResultingMatRefUuid = thisResultMatSubName.refuuid; + } else { + this.sourceResultingMatRefUuid = null; + } + } else { + // Get the Source Resulting Material List. Copy all the Resulting Material items in the list + this.sourceResultingMatList = this.sourceStageToCopy.resultingMaterials; + } + } else { + this.sourceResultingMatObj = null; + } + } + } + + copyStartingToResulting(processIndex: number, siteIndex: number, stageIndex: number) { + let found = false; + // let resultMatRefUuid = ''; + let startMatRefUuid = ''; + + // Get New Stage Object + const newStageObj = this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex]; + + // If New Stage exists + if (newStageObj !== null && newStageObj !== undefined) { + // check if Resulting Materials exists in the New Stage + if (newStageObj.resultingMaterials.length == 0) { + // if there is no Resulting Material, add a new one and copy + this.copyToResultingFields(processIndex, siteIndex, stageIndex); + } + } + } + + copyResultingToStarting(processIndex: number, siteIndex: number, stageIndex: number) { + let found = false; + // let resultMatRefUuid = ''; + let startMatRefUuid = ''; + + // Get New Stage Object + const newStageObj = this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex]; + + // If New Stage exists + if (newStageObj !== null && newStageObj !== undefined) { + // check if Starting Materials exists in the New Stage + if (newStageObj.startingMaterials.length == 0) { + // if there is no Starting Material, add a new one and copy + this.copyToStartingObject(processIndex, siteIndex, stageIndex); + } else { // Starting Material exists + // if Starting Material exists, loop through to find same refuuid + // if same refuuid found, display message and do not copy + newStageObj.startingMaterials.forEach(element => { + if (element) { + // Get this Starting Material refUuid from Substance Name + const startMatSubName = element.substanceName; + if (startMatSubName) { + startMatRefUuid = startMatSubName.refuuid; + // If the refuuid for the Resulting Material is same as FIRST Starting Material in the next Stage + if (this.sourceResultingMatRefUuid === startMatSubName.refuuid) { + found = true; + alert('This Substance ' + startMatSubName.name + ' already exists in the Starting Material in the next Stage'); + } + } + } + }); // for each Starting Material in next Stage + if (found === false) { + this.copyToStartingObject(processIndex, siteIndex, stageIndex); + } + } + } // New Stage Exists + + // Add a next Stage if it does not exists + // This part is useful when clicking the copy icon on 'Resulting Material' + // to copy to 'Starting Material' of next Stage. If there is no Stage exists, create one. + // This is same as 'Add Step' button. + if (!newStageObj) { + // this.addStage(processIndex, siteIndex); + const newStage: SpecifiedSubstanceG4mStage = { + stageNumber: '', + criticalParameters: [], + startingMaterials: [], + processingMaterials: [], + resultingMaterials: [] + }; + + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages.push(newStage); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages); + + this.copyToStartingObject(processIndex, siteIndex, stageIndex); + } + + } + + /* + copyResultingToStarting(processIndex: number, siteIndex: number, stageIndex: number, resultingMaterialIndex: number) { + let found = false; + let resultMatRefUuid = ''; + let startMatRefUuid = ''; + + if (this.sourceStageToCopy !== null && this.sourceStageToCopy !== undefined) { + const resultingLength = this.sourceStageToCopy.resultingMaterials.length; + if (resultingLength > 0) { + // Get this/current Resulting Material Object + const thisResultMatObj = this.sourceStageToCopy.resultingMaterials[resultingLength - 1]; + const thisResultMatSubName = thisResultMatObj.substanceName; + // Get this Resulting Material refUuid from Substance Name + if (thisResultMatSubName) { + resultMatRefUuid = thisResultMatSubName.refuuid; + } + } + } + + if (this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].resultingMaterials.length > 0) { + // Get this/current Resulting Material Object + const thisResultMatObj = this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].resultingMaterials[resultingMaterialIndex]; + const thisResultMatSubName = thisResultMatObj.substanceName; + // Get this Resulting Material refUuid from Substance Name + if (thisResultMatSubName) { + resultMatRefUuid = thisResultMatSubName.refuuid; + } + // Get Next Stage + const nextStageObj = this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex + 1]; + + // If next Stage exists + if (nextStageObj !== null && nextStageObj !== undefined) { + if (nextStageObj.startingMaterials.length == 0) { + // if there is no Starting Material, add a new one and copy + this.copyToStartingFields(processIndex, siteIndex, stageIndex, resultingMaterialIndex); + } else { + // if Starting Material exists, loop through to find same refuuid + nextStageObj.startingMaterials.forEach(element => { + if (element) { + // Get this Starting Material refUuid from Substance Name + const startMatSubName = element.substanceName; + if (startMatSubName) { + startMatRefUuid = startMatSubName.refuuid; + // If the refuuid for the Resulting Material is same as FIRST Starting Material in the next Stage + if (resultMatRefUuid === startMatRefUuid) { + found = true; + alert('This Substance ' + startMatSubName.name + ' already exists in the Starting Material in the next Stage'); + } + } + } + }); // for each Starting Material in next Stage + if (found === false) { + this.copyToStartingFields(processIndex, siteIndex, stageIndex, resultingMaterialIndex); + } + } + } + // Add a next Stage if it does not exists + // This part is useful when clicking the copy icon on 'Resulting Material' + // to copy to 'Starting Material' of next Stage. If there is no Stage exists, create one. + // This is same as 'Add Step' button. + if (!nextStageObj) { + // this.addStage(processIndex, siteIndex); + const newStage: SpecifiedSubstanceG4mStage = { + stageNumber: '', + criticalParameters: [], + startingMaterials: [], + processingMaterials: [], + resultingMaterials: [] + }; + this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages.push(newStage); + this.propertyEmitter.next(this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages); + + this.copyToStartingFields(processIndex, siteIndex, stageIndex, resultingMaterialIndex); + } + } // if this/current Resulting Material > 0 + } + */ + + copyToStartingObject(processIndex: number, siteIndex: number, stageIndex: number) { + // Add New Starting Material in the NEXT stage, if the Resulting material exists + // if (this.sourceResultingMatObj) { + + this.sourceResultingMatList.forEach(sourceResultingMatObj => { + if (sourceResultingMatObj) { + // Get this Starting Material refUuid from Substance Name + if (sourceResultingMatObj.substanceName) { + + // Add new Starting Materials in new Stage + this.addStartingMaterials(processIndex, siteIndex, stageIndex); + + this.copyToStartingFields(processIndex, siteIndex, stageIndex, sourceResultingMatObj); + } + } + }); + } + + copyToStartingFields(processIndex: number, siteIndex: number, stageIndex: number, sourceResultingMatObj: SpecifiedSubstanceG4mResultingMaterial) { + // Get New Stage + const newStageObj = this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex]; + + // copy to the Starting Material in the new Stage + const newStartIndex = newStageObj.startingMaterials.length - 1; + const newStartMat = newStageObj.startingMaterials[newStartIndex]; + + // Copy to new Starting Material + newStartMat.substanceName = sourceResultingMatObj.substanceName; + newStartMat.verbatimName = sourceResultingMatObj.verbatimName; + newStartMat.substanceRole = 'Intermediate'; + newStartMat.comments = sourceResultingMatObj.comments; + } + + copyToResultingFields(processIndex: number, siteIndex: number, stageIndex: number) { + if (this.sourceStartingMatObj) { + this.addResultingMaterials(processIndex, siteIndex, stageIndex); + // New Stage and New Resulting Material + // Get New Stage + const newStageObj = this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex]; + + // Copy to the Resulting Material in the new Stage + const newResultIndex = newStageObj.resultingMaterials.length - 1; + const newResulttMat = newStageObj.resultingMaterials[newResultIndex]; + newResulttMat.substanceName = this.sourceStartingMatObj.substanceName; + newResulttMat.verbatimName = this.sourceStartingMatObj.verbatimName; + newResulttMat.substanceRole = 'Intermediate'; + newResulttMat.comments = this.sourceStartingMatObj.comments; + } + } + + /* + copyToStartingFields(processIndex: number, siteIndex: number, stageIndex: number, resultingMaterialIndex: number) { + // Get this Resulting Material Object + const thisResultMatObj = this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex].resultingMaterials[resultingMaterialIndex]; + const thisResultMatSubName = thisResultMatObj.substanceName; + // Add New Starting Material in the NEXT stage + this.addStartingMaterials(processIndex, siteIndex, stageIndex + 1); + // Next Stage and Next New Starting Material + // Get Next Stage + const nextStageObj = this.substance.specifiedSubstanceG4m.process[processIndex].sites[siteIndex].stages[stageIndex + 1]; + // Get the last index or push/add/copy to the Starting Material at the Last + const nextStartIndex = nextStageObj.startingMaterials.length - 1; + const nextStartMat = nextStageObj.startingMaterials[nextStartIndex]; + nextStartMat.substanceName = thisResultMatSubName; + nextStartMat.verbatimName = thisResultMatObj.verbatimName; + nextStartMat.substanceRole = 'Intermediate'; // thisResultMatObj.substanceRole; + nextStartMat.comments = thisResultMatObj.comments; + } + */ +} diff --git a/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.html b/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.html new file mode 100644 index 000000000..a0c727477 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.html @@ -0,0 +1,278 @@ +
    +
    +
    + +
    + + +
    + +
    + +
    + +
    + +
    + + +
    +
    + + + + + + +
    + +
    + + + + + + +
    + + +
    +
    +
    +
    + Amount + +
    +
    +
    + {{displayAmount(startingMaterial.amount)}} +
    +
    +
    +
    +
    + + + + +
    +
    + +
    + +
    + + + Download + + + +
    + +
    +
    + + +
    + Uploading +
    + +
    + Error: There was a problem uploading this document +
    +
    + + + +
    +
    + Acceptance Criterias:   + + + +
    +
    + +
    + + + + + + + +
    + +
    +
    + +
    +
    +
    + + + +
    +
    +
    + Manufacturer Details:   + + + + +
    +
    +
    + + + + + + + + + + + + + + + +
    + +
    +
    +
    +
    +
    + +
    +
    + + +
    +
    +
    + +
    +
    + + + + + + + + + + + + + +
    \ No newline at end of file diff --git a/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.scss b/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.scss new file mode 100644 index 000000000..4f1d02ba4 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.scss @@ -0,0 +1,178 @@ +.form-container { + padding: 5px 10px 12px 10px; + position: relative; + /*border: 1px solid var(--pale-border-color-rgb);*/ +} + +.notification-backdrop { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: flex; + z-index: 10; + background-color: var(--notif-backdrop-bg-color); + justify-content: center; + align-items: center; + font-size: 30px; + font-weight: bold; + color: var(--notif-backdrop-color); +} + +.form-row { + display: flex; + justify-content: space-between; + align-items: flex-end; + + .delete-container { + padding: 0 10px 8px 0; + } + + .col { + flex-grow: 1; + padding-right: 25px; + } + + .col-1-1 { + width: calc((100% - 20px * 1)); // 1 column + margin-right: 20px; + } +} + +.verticaltop { + vertical-align: top; +} + +.references-container { + width: 100%; +} + +::ng-deep .selected-substance { + display: flex; + flex-direction: column; + text-align: left !important; + position: relative; + img { + width: 100%; + height: auto; + display: block; + max-width: 220px; + } +} + +.divflex { + display: flex; +} + +.bordergray { + border: 1px solid var(--regular-grey-color); +} + +.fontsize18px { + font-size: 18px; +} + +.margintopneg30px { + margin-top: -30px; +} + +.margintop10px { + margin-top: 10px; +} + +.margintop20px { + margin-top: 20px; +} + +.marginleftneg10px { + margin-left: -10px; +} + +.marginleftneg15px { + margin-left: -15px; +} + +.marginleft45px { + margin-left: 45px; +} + +.marginleft50px { + margin-left: 50px; +} + +.padleftneg10 { + padding-left: -10px; +} + +.padleft15px { + padding-left: 15px; +} + +.padtop10px { + padding-top: 10px; +} + +.height40px { + height: 40px; +} + +.font11px { + font-size: 11px; +} + +.width300px { + width: 300px; +} + +.width80percent { + width: 80%; +} + +.button-delete { + display: flex; + justify-content: flex-end; + margin-top: -15px; +} + +.colorred { + color: red; +} + +hr.style { + border: 1px solid rgb(111, 111, 111); +} + +.flex-column { + display: flex; + flex-direction: column; +} + +.related-substance { + min-width: 310px; + max-width: 310px; + /* + width: 300px; + max-width: 300px; + */ +} + +/* +.related-substance { + min-width: 387px; + max-width: 387px; +} +*/ + +.amount { + width: 100%; + display: flex; +} + +.amount-display { + padding-top:11px; +} + +.middle-fill { + flex: 1 1 auto; +} diff --git a/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.spec.ts b/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.spec.ts new file mode 100644 index 000000000..6cd8b662a --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Ssg4mStartingMaterialsFormComponent } from './ssg4m-starting-materials-form.component'; + +describe('Ssg4mStartingMaterialsFormComponent', () => { + let component: Ssg4mStartingMaterialsFormComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ Ssg4mStartingMaterialsFormComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(Ssg4mStartingMaterialsFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.ts b/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.ts new file mode 100644 index 000000000..2401cd71a --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.ts @@ -0,0 +1,289 @@ +import { Component, OnInit, OnDestroy, Input } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { Subscription } from 'rxjs'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { ConfigService } from '@gsrs-core/config/config.service'; +import { UtilsService } from '@gsrs-core/utils'; +import { SubstanceFormService } from '@gsrs-core/substance-form/substance-form.service'; +import { SubstanceDetail } from '@gsrs-core/substance/substance.model'; +import { SubstanceRelated, SubstanceSummary } from '@gsrs-core/substance'; +import { SpecifiedSubstanceG4mStartingMaterial, SubstanceAmount } from '@gsrs-core/substance/substance.model'; +import { AmountFormDialogComponent } from '@gsrs-core/substance-form/amount-form-dialog/amount-form-dialog.component'; +import { ConfirmDialogComponent } from '../../../fda/confirm-dialog/confirm-dialog.component'; +import { SubstanceFormSsg4mStagesService } from '../ssg4m-stages/substance-form-ssg4m-stages.service'; + +@Component({ + selector: 'app-ssg4m-starting-materials-form', + templateUrl: './ssg4m-starting-materials-form.component.html', + styleUrls: ['./ssg4m-starting-materials-form.component.scss'] +}) +export class Ssg4mStartingMaterialsFormComponent implements OnInit, OnDestroy { + + @Input() startingMaterialIndex: number; + privateProcessIndex: number; + privateSiteIndex: number; + privateStageIndex: number; + public configSettingsDisplay = {}; + privateShowAdvancedSettings: boolean; + privateStartingMaterial: SpecifiedSubstanceG4mStartingMaterial; + relatedSubstanceUuid: string; + substance: SubstanceDetail; + private overlayContainer: HTMLElement; + loading = false; + error = false; + subscriptions: Array = []; + + constructor( + private substanceFormService: SubstanceFormService, + private substanceFormSsg4mStagesService: SubstanceFormSsg4mStagesService, + private overlayContainerService: OverlayContainer, + private utilsService: UtilsService, + public configService: ConfigService, + private dialog: MatDialog + ) { } + + @Input() + set startingMaterial(startingMaterial: SpecifiedSubstanceG4mStartingMaterial) { + this.privateStartingMaterial = startingMaterial; + } + + get startingMaterial(): SpecifiedSubstanceG4mStartingMaterial { + return this.privateStartingMaterial; + } + + @Input() + set processIndex(processIndex: number) { + this.privateProcessIndex = processIndex; + } + + get processIndex(): number { + return this.privateProcessIndex; + } + + @Input() + set siteIndex(siteIndex: number) { + this.privateSiteIndex = siteIndex; + } + + get siteIndex(): number { + return this.privateSiteIndex; + } + + @Input() + set stageIndex(stageIndex: number) { + this.privateStageIndex = stageIndex; + } + + get stageIndex(): number { + return this.privateStageIndex; + } + + @Input() + set showAdvancedSettings(showAdvancedSettings: boolean) { + this.privateShowAdvancedSettings = showAdvancedSettings; + // Get Config Settins from config file + this.getConfigSettings(); + } + + get showAdvancedSettings(): boolean { + return this.privateShowAdvancedSettings; + } + + ngOnInit(): void { + this.overlayContainer = this.overlayContainerService.getContainerElement(); + const subscription = this.substanceFormService.substance.subscribe(substance => { + this.substance = substance; + }); + this.subscriptions.push(subscription); + + // Load Substance Name + if (this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex].startingMaterials[this.startingMaterialIndex].substanceName) { + let substanceRelated = this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex].startingMaterials[this.startingMaterialIndex].substanceName; + this.relatedSubstanceUuid = substanceRelated.refuuid; + } + + if (this.configSettingsDisplay["references"] === true) { + if (this.privateStartingMaterial.references == null) { + this.privateStartingMaterial.references = []; + } + } + } + + ngOnDestroy(): void { + // this.substanceFormService.unloadSubstance(); + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + + getConfigSettings(): void { + // Get SSG4 Config Settings from config.json file to show and hide fields in the form + let configSsg4Form: any; + configSsg4Form = this.configService.configData && this.configService.configData.ssg4Form || null; + // *** IMPORTANT: get the correct value. Get 'startingMaterial' json values from config + const confSettings = configSsg4Form.settingsDisplay.startingMaterial; + Object.keys(confSettings).forEach(key => { + if (confSettings[key] != null) { + if (confSettings[key] === 'simple') { + this.configSettingsDisplay[key] = true; + } else if (confSettings[key] === 'advanced') { + if (this.privateShowAdvancedSettings === true) { + this.configSettingsDisplay[key] = true; + } else { + this.configSettingsDisplay[key] = false; + } + } else if (confSettings[key] === 'removed') { + this.configSettingsDisplay[key] = false; + } + } + }); + } + + updateSubstanceRole(role: string): void { + this.privateStartingMaterial.substanceRole = role; + } + + updateSubstanceGrade(grade: string): void { + this.privateStartingMaterial.substanceGrade = grade; + } + + updateSpecificationType(specificationType: string): void { + this.privateStartingMaterial.specificationType = specificationType; + } + + updateManufacturerIdType(manufacturerIndex: number, manufacturerIdType: string): void { + this.privateStartingMaterial.manufacturerDetails[manufacturerIndex].manufacturerIdType = manufacturerIdType; + } + + updateAcceptanceCriteriaType(acceptanceCriteriaType: string, acceptanceIndex: string): void { + this.privateStartingMaterial.acceptanceCriterias[acceptanceIndex].acceptanceCriteriaType = acceptanceCriteriaType; + } + + addManufacturer(processIndex: number, siteIndex: number, stageIndex: number) { + this.substanceFormSsg4mStagesService.addStartingManufacturerDetails(processIndex, siteIndex, stageIndex, this.startingMaterialIndex); + } + + addAcceptanceCriteria(processIndex: number, siteIndex: number, stageIndex: number) { + this.substanceFormSsg4mStagesService.addStartingAcceptanceCriteria(processIndex, siteIndex, stageIndex, this.startingMaterialIndex); + } + + relatedSubstanceUpdated(substance: SubstanceSummary): void { + if (substance !== null) { + const relatedSubstance: SubstanceRelated = { + refPname: substance._name, + name: substance._name, + refuuid: substance.uuid, + substanceClass: 'reference', + approvalID: substance.approvalID + }; + this.privateStartingMaterial.substanceName = relatedSubstance; + } else { + this.privateStartingMaterial.substanceName = {}; + } + } + + confirmDeleteStartingMaterial() { + const dialogRef = this.dialog.open(ConfirmDialogComponent, { + data: { message: 'Are you sure you want to delele Input Material ' + (this.startingMaterialIndex + 1) + ' for Step ' + (this.stageIndex + 1) + ' for Site ' + (this.siteIndex + 1) + ' for Process ' + (this.processIndex + 1) + '?' } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result && result === true) { + this.deleteStartingMaterial(); + } + }); + } + + deleteStartingMaterial(): void { + this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex].startingMaterials.splice(this.startingMaterialIndex, 1); + } + + confirmDeleteManufacturer(manufacturerIndex: number) { + const dialogRef = this.dialog.open(ConfirmDialogComponent, { + data: { message: 'Are you sure you want to delele Manufacturer ' + (manufacturerIndex + 1) + ' for Starting Material ' + (this.startingMaterialIndex + 1) + ' for Step ' + (this.stageIndex + 1) + ' for Site ' + (this.siteIndex + 1) + ' for Process ' + (this.processIndex + 1) + '?' } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result && result === true) { + this.deleteManufacturer(manufacturerIndex); + } + }); + } + + deleteManufacturer(manufacturerIndex: number): void { + this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex].startingMaterials[this.startingMaterialIndex].manufacturerDetails.splice(manufacturerIndex, 1); + } + + confirmDeleteAcceptanceCriteria(acceptanceIndex: number) { + const dialogRef = this.dialog.open(ConfirmDialogComponent, { + data: { message: 'Are you sure you want to delele Acceptance Criteria ' + (acceptanceIndex + 1) + ' for Starting Material ' + (this.startingMaterialIndex + 1) + ' for Step ' + (this.stageIndex + 1) + ' for Site ' + (this.siteIndex + 1) + ' for Process ' + (this.processIndex + 1) + '?' } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result && result === true) { + this.deleteAcceptanceCriteria(acceptanceIndex); + } + }); + } + + deleteAcceptanceCriteria(acceptanceIndex: number): void { + this.substance.specifiedSubstanceG4m.process[this.processIndex].sites[this.siteIndex].stages[this.stageIndex].startingMaterials[this.startingMaterialIndex].acceptanceCriterias.splice(acceptanceIndex, 1); + } + + openAmountDialog(): void { + if (!this.privateStartingMaterial.amount) { + this.privateStartingMaterial.amount = {}; + } + const dialogRef = this.dialog.open(AmountFormDialogComponent, { + data: { 'subsAmount': this.privateStartingMaterial.amount }, + width: '990px' + }); + this.overlayContainer.style.zIndex = '1002'; + + const dialogSubscription = dialogRef.afterClosed().subscribe(newAmount => { + this.overlayContainer.style.zIndex = null; + this.privateStartingMaterial.amount = newAmount; + }); + this.subscriptions.push(dialogSubscription); + } + + displayAmount(amt: SubstanceAmount): string { + return this.utilsService.displayAmount(amt); + } + + + fileSelected(file: File): void { + this.error = false; + if (file != null) { + this.loading = true; + this.utilsService.uploadFile(file).subscribe(response => { + this.privateStartingMaterial.specificationReference = response; + this.loading = false; + + }, error => { + this.loading = false; + this.error = true; + + }); + } + } + + downloadDocument(url: string): void { + this.substanceFormService.bypassUpdateCheck(); + window.open(url); + } + + copyResultingToStarting(processIndex: number, siteIndex: number, stageIndex: number) { + + } + + /* + deleteProductLot(prodComponentIndex: number, prodLotIndex: number) { + this.productService.deleteProductLot(prodComponentIndex, prodLotIndex); + } + + deleteProductLot(prodComponentIndex: number, prodLotIndex: number): void { + this.product.productComponentList[prodComponentIndex].productLotList.splice(prodLotIndex, 1); + } + */ +} diff --git a/src/app/core/substance-ssg4m/ssg4m-starting-materials/substance-form-ssg4m-starting-materials.module.ts b/src/app/core/substance-ssg4m/ssg4m-starting-materials/substance-form-ssg4m-starting-materials.module.ts new file mode 100644 index 000000000..03bab64ba --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-starting-materials/substance-form-ssg4m-starting-materials.module.ts @@ -0,0 +1,79 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { CommonModule, formatDate } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; +import { MatInputModule } from '@angular/material/input'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatButtonModule } from '@angular/material/button'; +import { MatButtonToggleModule } from '@angular/material/button-toggle'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatTableModule } from '@angular/material/table'; +import { MatListModule } from '@angular/material/list'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatBadgeModule } from '@angular/material/badge'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatProgressBarModule} from '@angular/material/progress-bar'; +import { NgxJsonViewerModule} from 'ngx-json-viewer'; +// GSRS Imports +import { FileSelectModule } from 'file-select'; +import { ScrollToModule } from '@gsrs-core/scroll-to/scroll-to.module'; +import { ExpandDetailsModule } from '@gsrs-core/expand-details/expand-details.module'; +import { SubstanceSelectorModule } from '@gsrs-core/substance-selector/substance-selector.module'; +import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; +import { SubstanceFormModule } from '@gsrs-core/substance-form/substance-form.module'; +import { Ssg4mStartingMaterialsFormComponent } from './ssg4m-starting-materials-form.component'; + +@NgModule({ + imports: [ + CommonModule, + ReactiveFormsModule, + FormsModule, + MatFormFieldModule, + MatMenuModule, + MatCheckboxModule, + MatButtonModule, + MatIconModule, + MatTooltipModule, + MatTabsModule, + MatDividerModule, + MatSelectModule, + MatInputModule, + MatChipsModule, + MatAutocompleteModule, + ScrollToModule, + MatDialogModule, + MatTableModule, + MatExpansionModule, + MatBadgeModule, + MatRadioModule, + ExpandDetailsModule, + SubstanceSelectorModule, + MatListModule, + FileSelectModule, + MatButtonToggleModule, + NgxJsonViewerModule, + RouterModule, + MatProgressBarModule, + MatProgressSpinnerModule, + SubstanceImageModule, + SubstanceSelectorModule, + SubstanceFormModule + ], + declarations: [ + Ssg4mStartingMaterialsFormComponent + ], + exports: [ + Ssg4mStartingMaterialsFormComponent + ] +}) +export class SubstanceFormSsg4mStartingMaterialsModule { } diff --git a/src/app/core/substance-ssg4m/ssg4m-starting-materials/substance-form-ssg4m-starting-materials.service.spec.ts b/src/app/core/substance-ssg4m/ssg4m-starting-materials/substance-form-ssg4m-starting-materials.service.spec.ts new file mode 100644 index 000000000..466c4c453 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-starting-materials/substance-form-ssg4m-starting-materials.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { SubstanceFormSsg4mStartingMaterialsService } from './substance-form-ssg4m-starting-materials.service'; + +describe('SubstanceFormSsg4mStartingMaterialsService', () => { + let service: SubstanceFormSsg4mStartingMaterialsService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(SubstanceFormSsg4mStartingMaterialsService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-ssg4m/ssg4m-starting-materials/substance-form-ssg4m-starting-materials.service.ts b/src/app/core/substance-ssg4m/ssg4m-starting-materials/substance-form-ssg4m-starting-materials.service.ts new file mode 100644 index 000000000..aa5bcaea9 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-starting-materials/substance-form-ssg4m-starting-materials.service.ts @@ -0,0 +1,9 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class SubstanceFormSsg4mStartingMaterialsService { + + constructor() { } +} diff --git a/src/app/core/substance-ssg4m/ssg4m-step-view-dialog/ssg4m-step-view-dialog.component.html b/src/app/core/substance-ssg4m/ssg4m-step-view-dialog/ssg4m-step-view-dialog.component.html new file mode 100644 index 000000000..7dc9dca0c --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-step-view-dialog/ssg4m-step-view-dialog.component.html @@ -0,0 +1,11 @@ +
    +
    + +


    + + +
    +
    \ No newline at end of file diff --git a/src/app/core/substance-ssg4m/ssg4m-step-view-dialog/ssg4m-step-view-dialog.component.scss b/src/app/core/substance-ssg4m/ssg4m-step-view-dialog/ssg4m-step-view-dialog.component.scss new file mode 100644 index 000000000..cfef9ae25 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-step-view-dialog/ssg4m-step-view-dialog.component.scss @@ -0,0 +1,173 @@ +.divflexspace { + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.divflex { + display: flex; +} + +.bordergray { + border: 1px solid var(--regular-grey-color); +} + +.margintopneg10px { + margin-top: -10px; +} + +.margintop10px { + margin-top: 10px; +} + +.marginbottom10px { + margin-bottom: 10px; +} + +.marginbottom20px { + margin-top: 20px; +} + +.paddingleft10px { + padding-left: 10px; +} + +.paddingleft20px { + padding-left: 20px; +} + +.paddingleft25px { + padding-left: 25px; +} + +.paddingleft350px { + padding-left: 350px; +} + +.paddingtop10px { + padding-top: 10px; +} + +.paddingtop15px { + padding-top: 15px; +} + +.paddingbottom10px { + padding-bottom: 10px; +} + +.font16px { + font-size: 16px; +} + +.font30px { + font-size: 30px; +} + +.width100percent { + width: 100%; +} + +.width33percent { + width: 33%; +} + +.textaligncenter { + text-align: center; +} + +fieldset.border { + border: solid 2px var(--fieldset-blue-border-color) !important; + padding: 0 10px 10px 10px; + border-bottom: none; + border-radius: 8px; + min-width: 0; /* override the default value of min-content */ + -webkit-box-shadow: 2px 3px 3px 1px var(--fieldset-box-shadow-color); + -moz-box-shadow: 2px 3px 5px 1px var(--fieldset-box-shadow-color); + box-shadow: 2px 2px 3px 1px var(--fieldset-box-shadow-color); +} + +legend.border { + width: auto !important; + border: none; + border-bottom: none; + font-size: 15px; + color: var(--legend-blue-border-color); + font-family: Verdana; + font-weight: bold; + margin-bottom: 10px; +} + +.structure-img-big { + width: 100%; + height: auto; +} + +.zoom:hover{ + cursor:zoom-in; +} + +/* Dialog Class */ +.mat-dialog-container { + padding: 5px; + position: relative; +} + +.close-button { + position: relative; + top: 0; + right: 0; + padding: 5px; + line-height: 14px; + min-width: auto; +} + +.mat-dialog-content { + margin: 0; + padding: 0; + max-height: 100%; +} + +.mat-tab-body-content { + padding-left: 10px; + padding-right: 10px; +} + +.mat-mini-fab { + position: absolute; + right: 17px; + top: 8px; + background-color: var(--mat-icon-button-bg-color); + color: var(--label-color); + width: 35px; + height: 35px; + + &:not(:first-child) { + margin-top: 3px; + } + + ::ng-deep .mat-button-wrapper { + padding: 0; + } +} + +::ng-deep { + .structure-image-panel { + + .mat-dialog-container { + padding: 5px; + position: relative; + } + + .mat-dialog-content { + margin: 0; + padding: 0; + max-height: 100%; + } + + .mat-tab-body-content { + padding-left: 10px; + padding-right: 10px; + } + } +} diff --git a/src/app/core/substance-ssg4m/ssg4m-step-view-dialog/ssg4m-step-view-dialog.component.spec.ts b/src/app/core/substance-ssg4m/ssg4m-step-view-dialog/ssg4m-step-view-dialog.component.spec.ts new file mode 100644 index 000000000..5179eada0 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-step-view-dialog/ssg4m-step-view-dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Ssg4mStepViewDialogComponent } from './ssg4m-step-view-dialog.component'; + +describe('Ssg4mStepViewDialogComponent', () => { + let component: Ssg4mStepViewDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ Ssg4mStepViewDialogComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(Ssg4mStepViewDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/substance-ssg4m/ssg4m-step-view-dialog/ssg4m-step-view-dialog.component.ts b/src/app/core/substance-ssg4m/ssg4m-step-view-dialog/ssg4m-step-view-dialog.component.ts new file mode 100644 index 000000000..6505f1300 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-step-view-dialog/ssg4m-step-view-dialog.component.ts @@ -0,0 +1,48 @@ +import { Component, Inject, OnInit, Input } from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { DomSanitizer } from '@angular/platform-browser'; + +@Component({ + selector: 'app-ssg4m-step-view-dialog', + templateUrl: './ssg4m-step-view-dialog.component.html', + styleUrls: ['./ssg4m-step-view-dialog.component.scss'] +}) + +export class Ssg4mStepViewDialogComponent implements OnInit { + stage: any; + showProcessIndex: number; + showSiteIndex: number; + showStageIndex: number; + tabSelectedIndex: number; + + constructor( + private sanitizer: DomSanitizer, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any + + ) { } + + ngOnInit() { + if (this.data.processIndex >= -1 && this.data.siteIndex >= -1 && this.data.stageIndex >= -1) { + this.showProcessIndex = this.data.processIndex; + this.showSiteIndex = this.data.siteIndex; + this.showStageIndex = this.data.stageIndex; + } + } + + close() { + this.dialogRef.close(); + } + + dismissDialog(): void { + this.dialogRef.close(); + } + + tabSelectedIndexOutChange(tabIndex: number) { + this.tabSelectedIndex = tabIndex; + + // Close the Dialog and go to Form View + this.dialogRef.close(tabIndex); + } + +} diff --git a/src/app/core/substance-ssg4m/ssg4m-step-view-dialog/ssg4m-step-view-dialog.module.ts b/src/app/core/substance-ssg4m/ssg4m-step-view-dialog/ssg4m-step-view-dialog.module.ts new file mode 100644 index 000000000..92a29bdf4 --- /dev/null +++ b/src/app/core/substance-ssg4m/ssg4m-step-view-dialog/ssg4m-step-view-dialog.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { Ssg4mStepViewDialogComponent } from './ssg4m-step-view-dialog.component'; +import { Ssg4mSchemeViewModule } from '../ssg4m-scheme-view/ssg4m-scheme-view.module'; + +@NgModule({ + imports: [ + CommonModule, + MatButtonModule, + MatIconModule, + Ssg4mSchemeViewModule + ], + declarations: [ + Ssg4mStepViewDialogComponent + ] +}) +export class Ssg4mStepViewDialogModule { } diff --git a/src/app/core/substance-ssg4m/substance-ssg4m-form.component.html b/src/app/core/substance-ssg4m/substance-ssg4m-form.component.html new file mode 100644 index 000000000..0560b73ad --- /dev/null +++ b/src/app/core/substance-ssg4m/substance-ssg4m-form.component.html @@ -0,0 +1,151 @@ +
    +
    +
    + + +
    +
    +
    + + + +
    + + + + + + Save Local Copy + + + + + + + + + + + + +
    + + +
    +
    + {{submissionMessage}} +
    +
    + +
    + Please correct or dismiss the following errors and submit again: +
    +
    +
    + {{message.messageType}}
    +
    {{message.message}}
    + {{link.text}}
    +
    + +
    +
    +
    + + + + +
    +
    +
    +
    + +
    + +
    + +
    + {{(id && !imported) ? 'Editing ' : 'Registering New '}} - + Specified Substance Group 4 Manufacturing + +
    + + +
    +
    +
    + +
    +
    + + + + + {{section.menuLabel}} +
    + +
    + +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/src/app/core/substance-ssg4m/substance-ssg4m-form.component.scss b/src/app/core/substance-ssg4m/substance-ssg4m-form.component.scss new file mode 100644 index 000000000..b369adaad --- /dev/null +++ b/src/app/core/substance-ssg4m/substance-ssg4m-form.component.scss @@ -0,0 +1,301 @@ +.top-fixed { + position: fixed; + display: flex; + flex-direction: column; + /*top: 64px; */ + width: 100%; + background-color: var(--regular-white-color); + align-items: center; + /*justify-content: center;*/ + box-shadow: 0px 3px 3px -2px var(--box-shadow-color), 0px 3px 4px 0px var(--box-shadow-color-2), 0px 1px 8px 0px var(--box-shadow-color-3); + z-index: 1001; + justify-content: flex-end; +} + +.top-fixed-top { + top: 64px; +} + +.zindex2000 { + z-index: 2000; +} + +.actions-container { + max-width: 100%; + width: 100%; + background-color: var(--regular-white-color); + padding: 10px 45px; + display: flex; + justify-content: flex-end; +} + +.form-content-container { + // position: fixed; + // top: 121px; + // right: 0; + // bottom: 0; + // left: 0; + overflow: hidden; + margin-top: -100; + padding-top: 0; +} + +.cards-container { + /*margin-top: 15px;*/ +} + +.form-header { + // padding-bottom:20px; + font-weight: 500; + font-size: 28px; +} + +.form-header-title-top { + padding-top: 150px; +} + +.form-header-no-title-top { + padding-top: 100px; +} + +.form-header-title { + max-width: 1028px; + width: 100%; + padding: 10px; + display: flex; + /*padding-top: 50px;*/ + /*overflow: hidden;*/ + /*padding-top: 150px;*/ + margin: 0 auto; +} + +.cdk-overlay-pane { + z-index: 1005; +} + +mat-select-panel { + z-index: 1005; +} + +.admin-functions, +.changeClass { + margin-bottom: -15px; + margin-left: 30px; + margin-right: 5px; + margin-top: -10px; + color: var(--primary-color); + + + ::ng-deep .mat-form-field-wrapper { + padding-bottom: 20px !important; + } +} + +::ng-deep .mat-form-field-wrapper { + padding-bottom: 5px; +} + +.advanced-features { + z-index: 2000 !important; + + display: flex; + flex-direction: row; + padding-left: 10px; +} + +::ng-deep .cdk-overlay-pane { + margin-top: 20px !important; +} + +.mat-accordion { + width: 100%; + max-width: 1020px; + box-sizing: border-box; +} + +.hidden { + display: none !important; +} + +.submission-messages { + overflow: hidden; + height: auto; + -webkit-transition: all 500ms ease-out; + transition: all 500ms ease-out; + max-width: 1028px; + width: 100%; + background-color: var(--regular-white-color); + display: flex; + flex-direction: column; + + &.collapsed { + max-height: 0; + } + + &.expanded { + max-height: 500px; + overflow-y: auto; + padding: 10px; + } + + .submission-message { + font-weight: 500; + text-align: center; + } + + .validation-message { + display: flex; + padding: 5px 0; + align-items: center; + + .message-type { + text-transform: uppercase; + font-weight: 500; + margin-right: 20px; + padding: 10px; + border-radius: 3px; + } + } + + .dismiss-container { + display: flex; + } + + .warning-message { + color: var(--warning-dialog-color); + background-color: var(--warning-dialog-bg-color); + + } + + .error-message { + color: var(--error-dialog-color); + background-color: var(--error-dialog-bg-color); + } + +} + +.validate-button { + margin-left: 15px; +} + +.mat-expansion-panel-header-title { + align-items: center; +} + +.hide-show-messages { + margin-left: 10px; + border: 1px solid; +} + +.internal-link { + color: var(--link-primary-color); +} + +.import-button { + margin-left: 15px; +} + +.substance-form-row { + display: flex; + justify-content: space-between; +} + +.sub-header { + font-weight: 400; + padding-right: 5px; +} + +.divflex { + display: flex; +} + +.fontsize16px { + font-size: 16px; +} + +.fontsize20px { + font-size: 20px; +} + +.padall15px { + padding: 15px; +} + +.padtop25px { + padding-top: 25px; +} + +.padtop70px { + padding-top: 50px; +} + +.padleft25px { + padding-left: 25px; +} + +.margintop10px { + margin-top: 10px; +} + +.marginright75px { + margin-right: 75px; +} + +.bordergray { + border: 1px solid green; +} + +.borderblue { + border: 1px solid #007CBA; +} + +.fontbold { + font-weight: bold; +} + +/* FDA COLOR BLUE CODE Hex: #007CBA */ +.colorbluefda { + color: #007CBA; +} + +.colorred { + color: red; +} + +.bkcoloryellow { + background-color: rgb(247, 247, 216); +} + +::ng-deep .custom-menu { + margin-top: -12px; + /*z-index: 4000;*/ +} + +.mat-menu-item { + color: #007CBA !important; +} + +/* +button.mat-menu-item{ + background-color: white; +} + +button.mat-menu-item:hover { + background-color: #007CBA; + color: #fff !important; +} +*/ + +/* tyler added as quick fix */ +app-substance-form-ssg4m-process-card button[color=primary] { + float: right; +} + +.ontopmessage { + position: relative; + z-index: 2000; +} + +.main-container { + position: relative; +} \ No newline at end of file diff --git a/src/app/core/substance-ssg4m/substance-ssg4m-form.component.ts b/src/app/core/substance-ssg4m/substance-ssg4m-form.component.ts new file mode 100644 index 000000000..5e113f7d0 --- /dev/null +++ b/src/app/core/substance-ssg4m/substance-ssg4m-form.component.ts @@ -0,0 +1,1346 @@ +import { + Component, + OnInit, + AfterViewInit, + ViewChildren, + ViewContainerRef, + QueryList, + OnDestroy, HostListener +} from '@angular/core'; +import { ActivatedRoute, Router, RouterEvent, NavigationStart, NavigationEnd } from '@angular/router'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { MatExpansionPanel } from '@angular/material/expansion'; +import { MatDialog } from '@angular/material/dialog'; +import { take, map } from 'rxjs/operators'; +import { Subscription, Observable } from 'rxjs'; +import * as _ from 'lodash'; +import * as moment from 'moment'; +import * as defiant from '../../../../node_modules/defiant.js/dist/defiant.min.js'; +import { Title } from '@angular/platform-browser'; +import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +// GSRS Import +import { ConfigService } from '@gsrs-core/config/config.service'; +import { GoogleAnalyticsService } from '../google-analytics/google-analytics.service'; +import { DynamicComponentLoader } from '../dynamic-component-loader/dynamic-component-loader.service'; +import { formSections } from '../substance-form/form-sections.constant'; +import { SubstanceFormSection } from '../substance-form/substance-form-section'; +import { MainNotificationService } from '../main-notification/main-notification.service'; +import { AuthService } from '@gsrs-core/auth'; +import { LoadingService } from '../loading/loading.service'; +import { SubstanceService } from '../substance/substance.service'; +import { SubstanceFormService } from '../substance-form/substance-form.service'; +import { AppNotification, NotificationType } from '../main-notification/notification.model'; +import { ValidationResults } from '../substance-form/substance-form.model'; +import { SubstanceDetail } from '../substance/substance.model'; +import { ValidationMessage, SubstanceFormResults, SubstanceFormDefinition } from '../substance-form/substance-form.model'; +import { SubmitSuccessDialogComponent } from '../substance-form/submit-success-dialog/submit-success-dialog.component'; +import { MergeConceptDialogComponent } from '@gsrs-core/substance-form/merge-concept-dialog/merge-concept-dialog.component'; +import { DefinitionSwitchDialogComponent } from '@gsrs-core/substance-form/definition-switch-dialog/definition-switch-dialog.component'; +import { SubstanceEditImportDialogComponent } from '@gsrs-core/substance-edit-import-dialog/substance-edit-import-dialog.component'; +import { JsonDialogComponent } from '@gsrs-core/substance-form/json-dialog/json-dialog.component'; +import { SubstanceSsg4mService } from './substance-ssg4m-form.service'; +import { environment } from '@gsrs-core/../../environments/environment'; +import { Ssg4mSyntheticPathway } from './model/substance-ssg4m.model'; + +@Component({ + selector: 'app-substance-ssg4m-form', + templateUrl: './substance-ssg4m-form.component.html', + styleUrls: ['./substance-ssg4m-form.component.scss'] +}) +export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewInit, OnDestroy { + isLoading = true; + id?: string; + formSections: Array = []; + @ViewChildren('dynamicComponent', { read: ViewContainerRef }) dynamicComponents: QueryList; + @ViewChildren('expansionPanel', { read: MatExpansionPanel }) matExpansionPanels: QueryList; + private subClass: string; + definitionType: string; + expandedComponents = [ + 'substance-form-ssg4m-process' + ]; + showSubmissionMessages = false; + submissionMessage: string; + validationMessages: Array; + validationResult = false; + private subscriptions: Array = []; + copy: string; + private overlayContainer: HTMLElement; + serverError: boolean; + canApprove: boolean; + approving: boolean; + definition: SubstanceFormDefinition; + user: string; + feature: string; + isAdmin: boolean; + isUpdater: boolean; + messageField: string; + errorMessage: string; + microserviceStatusUp = false; + uuid: string; + substanceClass: string; + status: string; + classes = [ + 'concept', + 'protein', + 'chemical', + 'structurallyDiverse', + 'polymer', + 'nucleicAcid', + 'mixture', + 'specifiedSubstanceG1', + 'specifiedSubstanceG2', + 'specifiedSubstanceG3', + 'specifiedSubstanceG4m']; + imported = false; + forceChange = false; + sameSubstance = false; + UNII: string; + json: SubstanceDetail; + downloadJsonHref: any; + jsonFileName: string; + showHeaderBar = 'true'; + showFormReadOnly = 'false'; + showRegisterEditTitle = 'true'; + ssg4mSyntheticPathway: Ssg4mSyntheticPathway; + saveDelayedMessage = ''; + isCancelBtnClicked = false; + isSavedSuccessful = false; + public configSettingsDisplay = {}; + configSsg4Form: any; + configSettingReferences = false; + private submitSubscription: any = null; + + private jsLibScriptUrls = [ + `${environment.baseHref || ''}assets/pathway/cola.min.js`, + `${environment.baseHref || ''}assets/pathway/d3v4.js`, + `${environment.baseHref || ''}assets/pathway/pathwayviz.js` + ]; + + constructor( + private activatedRoute: ActivatedRoute, + private substanceService: SubstanceService, + private loadingService: LoadingService, + private mainNotificationService: MainNotificationService, + private router: Router, + private dynamicComponentLoader: DynamicComponentLoader, + private gaService: GoogleAnalyticsService, + private substanceSsg4mService: SubstanceSsg4mService, + private substanceFormService: SubstanceFormService, + private overlayContainerService: OverlayContainer, + private dialog: MatDialog, + private authService: AuthService, + private titleService: Title, + private configService: ConfigService, + private sanitizer: DomSanitizer + ) { + } + + ngOnInit() { + this.showHeaderBar = this.activatedRoute.snapshot.queryParams['header'] || 'true'; + this.showFormReadOnly = this.activatedRoute.snapshot.queryParams['readonly'] || 'false'; + this.loadingService.setLoading(true); + this.isAdmin = this.authService.hasRoles('admin'); + this.isUpdater = this.authService.hasAnyRoles('Updater', 'SuperUpdater'); + this.overlayContainer = this.overlayContainerService.getContainerElement(); + this.imported = false; + + this.getConfigSettings(); + if (this.configSsg4Form) { + // Get 'showRegisterEditTitle' value from config + this.showRegisterEditTitle = this.configSsg4Form.showRegisterEditTitle; + } + + this.substanceClass = 'specifiedSubstanceG4m'; + + const routeSubscription = this.activatedRoute + .params + .subscribe(params => { + if (params['id']) { + const id = params['id']; + if (id !== this.id) { + this.id = id; + this.gaService.sendPageView(`Substance Edit`); + this.titleService.setTitle('Edit - Specified Substance Group 4 Manufacturing'); + const newType = this.activatedRoute.snapshot.queryParamMap.get('switch') || null; + if (newType) { + this.getSsg4mDetails(newType); + } else { + this.getSsg4mDetails(); + } + } // if Id + } else if (this.activatedRoute.snapshot.queryParams['action']) { // import in new register form + const actionParam = this.activatedRoute.snapshot.queryParams['action'] || null; + if (actionParam && actionParam === 'import' && window.history.state) { + const record = window.history.state.record; + if ((record) && this.jsonValid(record)) { + const responseImport = JSON.parse(record); + if (responseImport) { + this.substanceFormService.loadSubstance(this.substanceClass, responseImport).pipe(take(1)).subscribe(() => { + this.setFormSections(formSections[this.substanceClass]); + + setTimeout(() => { + this.forceChange = true; + this.dynamicComponents.forEach((cRef, index) => { + this.dynamicComponentLoader + .getComponentFactory(this.formSections[index].dynamicComponentName) + .subscribe(componentFactory => { + this.formSections[index].dynamicComponentRef = cRef.createComponent(componentFactory); + this.formSections[index].matExpansionPanel = this.matExpansionPanels.find((item, panelIndex) => index === panelIndex); + this.formSections[index].dynamicComponentRef.instance.menuLabelUpdate.pipe(take(1)).subscribe(label => { + this.formSections[index].menuLabel = label; + }); + const hiddenStateSubscription = + this.formSections[index].dynamicComponentRef.instance.hiddenStateUpdate.subscribe(isHidden => { + this.formSections[index].isHidden = isHidden; + }); + this.subscriptions.push(hiddenStateSubscription); + this.formSections[index].dynamicComponentRef.instance.canAddItemUpdate.pipe(take(1)).subscribe(isList => { + this.formSections[index].canAddItem = isList; + if (isList) { + const aieSubscription = this.formSections[index].addItemEmitter.subscribe(() => { + this.formSections[index].matExpansionPanel.open(); + this.formSections[index].dynamicComponentRef.instance.addItem(); + }); + this.formSections[index].dynamicComponentRef.instance.componentDestroyed.pipe(take(1)).subscribe(() => { + aieSubscription.unsubscribe(); + }); + } + }); + this.formSections[index].dynamicComponentRef.changeDetectorRef.detectChanges(); + }); + }); + }); + }); // load Substance in Import on new Register page + } + } + } + + this.loadingService.setLoading(false); + this.isLoading = false; + + // this.imported = true; + // this.getDetailsFromImport(record.record); + /* } else { + this.copy = this.activatedRoute.snapshot.queryParams['copy'] || null; + if (this.copy) { + const copyType = this.activatedRoute.snapshot.queryParams['copyType'] || null; + this.getPartialSubstanceDetails(this.copy, copyType); + this.gaService.sendPageView(`Substance Register`); + } */ + } else { // new record + setTimeout(() => { + this.gaService.sendPageView(`Substance Register`); + this.subClass = this.activatedRoute.snapshot.params['type'] || 'specifiedSubstanceG4m'; + this.substanceClass = this.subClass; + this.titleService.setTitle('Register - Specified Substance Group 4 Manufacturing'); + this.substanceFormService.loadSubstance(this.substanceClass).pipe(take(1)).subscribe(() => { + this.setFormSections(formSections[this.substanceClass]); + this.loadingService.setLoading(false); + this.isLoading = false; + }); + }); + } //else + }); + this.subscriptions.push(routeSubscription); + const routerSubscription = this.router.events.subscribe((event: RouterEvent) => { + if (event instanceof NavigationStart) { + this.substanceSsg4mService.unloadSubstance(); + } + }); + this.subscriptions.push(routerSubscription); + this.approving = false; + /* // Commenting this out + const definitionSubscription = this.substanceSsg4mService.definition.subscribe(response => { + this.definition = response; + setTimeout(() => { + this.canApprove = this.canBeApproved(); + }); + }); + this.subscriptions.push(definitionSubscription); + */ + /* + this.authService.getAuth().pipe(take(1)).subscribe(auth => { + this.user = auth.identifier; + setTimeout(() => { + this.canApprove = this.canBeApproved(); + }); + }); + */ + // Scheme View loading + if (!window['schemeUtil']) { + for (let i = 0; i < this.jsLibScriptUrls.length; i++) { + const node = document.createElement('script'); + node.src = this.jsLibScriptUrls[i]; + node.type = 'text/javascript'; + node.async = false; + document.getElementsByTagName('head')[0].appendChild(node); + } + } + } + + ngAfterViewInit(): void { + const subscription = this.dynamicComponents.changes + .subscribe(() => { + + const total = this.formSections.length; + let finished = 0; + if (!this.forceChange) { + this.loadingService.setLoading(true); + const startTime = new Date(); + this.dynamicComponents.forEach((cRef, index) => { + this.dynamicComponentLoader + .getComponentFactory(this.formSections[index].dynamicComponentName) + .subscribe(componentFactory => { + this.loadingService.setLoading(true); + this.formSections[index].dynamicComponentRef = cRef.createComponent(componentFactory); + this.formSections[index].matExpansionPanel = this.matExpansionPanels.find((item, panelIndex) => index === panelIndex); + this.formSections[index].dynamicComponentRef.instance.menuLabelUpdate.pipe(take(1)).subscribe(label => { + this.formSections[index].menuLabel = label; + }); + const hiddenStateSubscription = + this.formSections[index].dynamicComponentRef.instance.hiddenStateUpdate.subscribe(isHidden => { + this.formSections[index].isHidden = isHidden; + }); + this.subscriptions.push(hiddenStateSubscription); + this.formSections[index].dynamicComponentRef.instance.canAddItemUpdate.pipe(take(1)).subscribe(isList => { + this.formSections[index].canAddItem = isList; + if (isList) { + const aieSubscription = this.formSections[index].addItemEmitter.subscribe(() => { + this.formSections[index].matExpansionPanel.open(); + this.formSections[index].dynamicComponentRef.instance.addItem(); + }); + this.formSections[index].dynamicComponentRef.instance.componentDestroyed.pipe(take(1)).subscribe(() => { + aieSubscription.unsubscribe(); + }); + } + }); + this.formSections[index].dynamicComponentRef.changeDetectorRef.detectChanges(); + finished++; + if (finished >= total) { + this.loadingService.setLoading(false); + } else { + const currentTime = new Date(); + if (currentTime.getTime() - startTime.getTime() > 12000) { + if (confirm('There was a network error while fetching files, would you like to refresh?')) { + window.location.reload(); + } + } + } + setTimeout(() => { + this.loadingService.setLoading(false); + // this.UNII = this.substanceSsg4mService.getUNII(); + }, 5); + }); + }); + // this.loadingService.setLoading(false); + + } + subscription.unsubscribe(); + }); + } + + private setFormSections(sectionNames: Array = []): void { + this.formSections = []; + sectionNames.forEach(sectionName => { + let canAdd = true; + if (this.configSettingReferences === false) { + if (sectionName === 'substance-form-references') { + canAdd = false; + } + } + if (canAdd === true) { + const formSection = new SubstanceFormSection(sectionName); + this.formSections.push(formSection); + } + }); + } + + getConfigSettings(): void { + // Get SSG4 Config Settings from config.json file to show and hide fields in the form + // let configSsg4Form: any; + this.configSsg4Form = this.configService.configData && this.configService.configData.ssg4Form || null; + // *** IMPORTANT: get the correct value. Get 'startingMaterial' json values from config + let configReferences = this.configSsg4Form.settingsDisplay.references; + this.configSettingReferences = false; + if (configReferences === 'simple') { + this.configSettingReferences = true; + } else if (configReferences === 'advanced') { + this.configSettingReferences = false; + } else if (configReferences === 'removed') { + this.configSettingReferences = false; + } + } + + openedChange(event: any) { + if (event) { + this.overlayContainer.style.zIndex = '1002'; + } else { + this.overlayContainer.style.zIndex = '1000'; + } + } + + /* + useFeature(feature: any): void { + this.feature = feature.value; + if (this.feature === 'glyco') { + this.glyco(); + } else if (this.feature === 'disulfide') { + this.disulfide(); + } if (this.feature === 'concept') { + this.concept(); + } if (this.feature === 'unapprove') { + if (confirm('Are you sure you\'d like to remove the approvalID?')) { + this.substanceFormService.unapproveRecord(); + } + this.feature = undefined; + } + if (this.feature === 'setPrivate') { + this.substanceFormService.setDefinitionPrivate(); + this.feature = undefined; + } + if (this.feature === 'setPublic') { + this.substanceFormService.setDefinitionPublic(); + this.feature = undefined; + } + if (this.feature === 'approved') { + this.substanceFormService.changeStatus('approved'); + this.feature = undefined; + } + if (this.feature === 'pending') { + this.substanceFormService.changeStatus('pending'); + this.feature = undefined; + } + if (this.feature === 'merge') { + this.mergeConcept(); + this.feature = undefined; + } + if (this.feature === 'switch') { + this.definitionSwitch(); + this.feature = undefined; + } + if (this.feature === 'changeApproval') { + this.substanceFormService.changeApproval(); + } + + } + + changeClass(type: any): void { + this.router.navigate(['/substances', this.id, 'edit'], { queryParams: { switch: type.value } }); + this.feature = undefined; + } + + changeStatus(status: any): void { + this.substanceFormService.changeStatus(status); + this.feature = undefined; + } + + concept(): void { + this.substanceFormService.conceptNonApproved(); + this.feature = undefined; + } + + glyco(): void { + this.substanceFormService.predictSites(); + this.feature = undefined; + } + + disulfide(): void { + this.substanceFormService.disulfideLinks(); + this.feature = undefined; + } + */ + + ngOnDestroy(): void { + // this.substanceFormService.unloadSubstance(); + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + + + importDialog(): void { + let data: any; + data = { + title: 'Manufacturing Scheme Import' + }; + const dialogRef = this.dialog.open(SubstanceEditImportDialogComponent, { + width: '650px', + autoFocus: false, + data: data + }); + this.overlayContainer.style.zIndex = '1002'; + + const dialogSubscription = dialogRef.afterClosed().pipe(take(1)).subscribe(response => { + if (response) { + this.loadingService.setLoading(true); + this.overlayContainer.style.zIndex = null; + + // attempting to reload a substance without a router refresh has proven to cause issues with the relationship dropdowns + // There are probably other components affected. There is an issue with subscriptions likely due to some OnInit not firing + + /* const read = JSON.parse(response); + if (this.id && read.uuid && this.id === read.uuid) { + this.substanceFormService.importSubstance(read, 'update'); + this.submissionMessage = null; + this.validationMessages = []; + this.showSubmissionMessages = false; + this.loadingService.setLoading(false); + this.isLoading = false; + } else { + if ( read.substanceClass === this.substanceClass) { + this.imported = true; + this.substanceFormService.importSubstance(read); + this.submissionMessage = null; + this.validationMessages = []; + this.showSubmissionMessages = false; + this.loadingService.setLoading(false); + this.isLoading = false; + } else {*/ + setTimeout(() => { + this.router.onSameUrlNavigation = 'reload'; + this.loadingService.setLoading(false); + if (this.id) { + this.router.navigateByUrl('/substances-ssg4m/' + this.id + '/edit?action=import&header=' + this.showHeaderBar, { state: { record: response } }); + } else { // new record + this.router.navigateByUrl('/substances-ssg4m/register?action=import&header=' + this.showHeaderBar, { state: { record: response } }); + } + }, 1000); + } + // } + // } + }); + + } + + test() { + this.router.navigated = false; + this.router.navigate([this.router.url]); + } + + canBeApproved(): boolean { + const action = this.activatedRoute.snapshot.queryParams['action'] || null; + if (action && action === 'import') { + return false; + } + if (this.definition && this.definition.lastEditedBy && this.user) { + const lastEdit = this.definition.lastEditedBy; + if (!lastEdit) { + return false; + } + if (this.definition.status === 'approved') { + return false; + } + if (lastEdit === this.user) { + return false; + } + return true; + + } + return false; + } + + showJSON(): void { + const dialogRef = this.dialog.open(JsonDialogComponent, { + width: '90%' + }); + this.overlayContainer.style.zIndex = '1002'; + + const dialogSubscription = dialogRef.afterClosed().pipe(take(1)).subscribe(response => { + + }); + this.subscriptions.push(dialogSubscription); + } + + saveJSON(): void { + // apply the same cleaning to remove deleted objects and return what will be sent to the server on validation / submission + this.json = this.substanceFormService.cleanSubstance(); + // this.json = this.cleanObject(substanceCopy); + const uri = this.sanitizer.bypassSecurityTrustUrl('data:text/json;charset=UTF-8,' + encodeURIComponent(JSON.stringify(this.json))); + this.downloadJsonHref = uri; + + const date = new Date(); + this.jsonFileName = 'SSG4m_' + moment(date).format('MMM-DD-YYYY_H-mm-ss'); + } + + checkSsg4mServerStatus(): void { + // Check Microservice Server Status + this.substanceSsg4mService.checkSsg4mServerStatus().pipe(take(1)).subscribe(responseCheck => { + if (responseCheck) { + if (responseCheck["status"]) { + if (responseCheck["status"] === 'OK') { + this.microserviceStatusUp = true; + } + } + } else { + this.microserviceStatusUp = false; + } + }, error => { + // if it is authentication issue or status is 0, reload the current page + + // These lines did not work. Commenting out right now. Caused forever refresh loop. + // if (error.status === 0) { + // window.location.reload(); + // } + + if (error.status === 0) { + console.log("Error Status is 0"); + let totalNumberRefresh = 2; + let preventRefresh = parseInt((new URLSearchParams(window.location.search)).get("refreshcount")); + // if parameter 'refreshcount' is NOT found in the URL, add refreshcount in the URL. + // refresh n/totalNumberRefresh times + if (!preventRefresh || (preventRefresh && Number(preventRefresh) < totalNumberRefresh)) { + let url = window.location.href; + // if question mark '?' found in the URL, add/append '&', otherwise add/append '?' in the URL + if (!preventRefresh) { // not found preventRefresh in URL + preventRefresh = 1; + if (url.indexOf('?') > -1) { + url += '&refreshcount=' + preventRefresh; + } else { + url += '?refreshcount=' + preventRefresh; + } + } else { // found refreshcount in the URL + let currentCountUrl = 'refreshcount=' + preventRefresh; + // add 1 to current count + preventRefresh = preventRefresh + 1; + let newCounturl = 'refreshcount=' + preventRefresh; + // replace 'refreshcount=1' to 'refreshcount=2' + url = url.replace(currentCountUrl, newCounturl); + } + // Display error message to users that the page will refresh n number of times. + this.errorMessage = "There was a connection problem loading the page. Automatically refreshing " + preventRefresh + " of " + totalNumberRefresh + " times ...

    "; + + setTimeout(() => { + window.location.href = url; + }, 2000); + } + } + + this.microserviceStatusUp = false; + if (!this.errorMessage) { + this.errorMessage = ''; + } + this.errorMessage = this.errorMessage + "Unable to load the data for Record ID " + this.id + "

    "; + if (error && error.error && error.error.message) { + this.errorMessage = this.errorMessage + 'Server Error ' + (error.status + ': ' || ': ') + error.error.message; + } else if (error && error.error && (typeof error.error) === 'string') { + this.errorMessage = this.errorMessage + '
    Server Error ' + (error.status + ': ' || '') + error.error; + } else if (error && error.message) { + this.errorMessage = this.errorMessage + '
    Server Error ' + (error.status + ': ' || '') + error.message; + this.errorMessage = this.errorMessage + "
    It looks like the SSG4m microservice is not running.
    "; + this.errorMessage = this.errorMessage + "Please ask your system administrator to verify that the SSG4m microservice is running without error, and also to examine the website logs to:
    - check if there are any database connection issues, and
    - make sure the system is using valid authentication credentials into the database"; + } + else { + this.errorMessage = this.errorMessage + 'There could be an authentication issue.
    -Make sure that you are logged into the GSRS website.
    -Clear your browser cache.
    -Reload your SSG4 page or Appian'; + } + }); + } + + getSsg4mDetails(newType?: string): void { + let generateErrorMessage = "Unable to load the data for Record ID " + this.id + "

    "; + + // Check if SSG4m microservice server is UP or not + this.checkSsg4mServerStatus(); + + this.substanceSsg4mService.getSsg4mDetails(this.id).pipe(take(1)).subscribe(response => { + /*if (response._name) { + this.titleService.setTitle('Edit - ' + response._name); + }*/ + if (response) { + // Check for import in URL + const action = this.activatedRoute.snapshot.queryParams['action'] || null; + let substanceSsg4mFromDb: SubstanceDetail; + + this.ssg4mSyntheticPathway = response; + + if (action && action === 'import' && window.history.state) { + // this.gaService.sendPageView(`Substance Import`); + const record = window.history.state.record; + // this.imported = true; + // this.getDetailsFromImport(record.record); + if ((record) && this.jsonValid(record)) { + const responseImport = JSON.parse(record); + if (responseImport) { + this.substanceFormService.loadSubstance(this.substanceClass, responseImport).pipe(take(1)).subscribe(() => { + // this.substanceSsg4mService.loadSubstance(this.subClass).pipe(take(1)).subscribe(() => { + this.setFormSections(formSections[this.substanceClass]); + }); + } + } + } + else if (response.sbmsnDataText) { // If JSON form data found into the database, load into the form + substanceSsg4mFromDb = JSON.parse(response.sbmsnDataText); + + this.substanceFormService.loadSubstance(substanceSsg4mFromDb.substanceClass, substanceSsg4mFromDb).pipe(take(1)).subscribe(() => { + this.setFormSections(formSections[this.substanceClass]); + }); + } else if (!response.sbmsnDataText) { // AS NEW FORM, If JSON form data NOT found into the database, Load the Form as a new Form + this.substanceFormService.loadSubstance(this.substanceClass).pipe(take(1)).subscribe(() => { + // this.substanceSsg4mService.loadSubstance(this.subClass).pipe(take(1)).subscribe(() => { + this.setFormSections(formSections[this.substanceClass]); + }); + } + } else if (response === null) { + this.errorMessage = "There is no data found in the database for ID: " + this.id; + } + this.loadingService.setLoading(false); + this.isLoading = false; + }, error => { // Getting Error while getting Record + if (this.microserviceStatusUp === false) { + } + this.gaService.sendException('getSsg4mDetails: error from API call'); + this.loadingService.setLoading(false); + this.isLoading = false; + // this.handleSubstanceRetrivalError(); + }); + } + + jsonValid(file: any): boolean { + try { + JSON.parse(file); + } catch (e) { + return false; + } + return true; + } + + getDetailsFromImport(state: any, same?: boolean) { + if (state && this.jsonValid(state)) { + const response = JSON.parse(state); + same = false; + this.definitionType = response.definitionType; + this.substanceClass = response.substanceClass; + this.status = response.status; + this.substanceFormService.loadSubstance(response.substanceClass, response, 'import').pipe(take(1)).subscribe(() => { + // this.substanceSsg4mService.loadSubstance(response.substanceClass, response, 'import').pipe(take(1)).subscribe(() => { + this.setFormSections(formSections[response.substanceClass]); + if (!same) { + setTimeout(() => { + this.forceChange = true; + this.dynamicComponents.forEach((cRef, index) => { + this.dynamicComponentLoader + .getComponentFactory(this.formSections[index].dynamicComponentName) + .subscribe(componentFactory => { + this.formSections[index].dynamicComponentRef = cRef.createComponent(componentFactory); + this.formSections[index].matExpansionPanel = this.matExpansionPanels.find((item, panelIndex) => index === panelIndex); + this.formSections[index].dynamicComponentRef.instance.menuLabelUpdate.pipe(take(1)).subscribe(label => { + this.formSections[index].menuLabel = label; + }); + const hiddenStateSubscription = + this.formSections[index].dynamicComponentRef.instance.hiddenStateUpdate.subscribe(isHidden => { + this.formSections[index].isHidden = isHidden; + }); + this.subscriptions.push(hiddenStateSubscription); + this.formSections[index].dynamicComponentRef.instance.canAddItemUpdate.pipe(take(1)).subscribe(isList => { + this.formSections[index].canAddItem = isList; + if (isList) { + const aieSubscription = this.formSections[index].addItemEmitter.subscribe(() => { + this.formSections[index].matExpansionPanel.open(); + this.formSections[index].dynamicComponentRef.instance.addItem(); + }); + this.formSections[index].dynamicComponentRef.instance.componentDestroyed.pipe(take(1)).subscribe(() => { + aieSubscription.unsubscribe(); + }); + } + }); + this.formSections[index].dynamicComponentRef.changeDetectorRef.detectChanges(); + }); + }); + + this.canApprove = false; + }); + } + }, error => { + this.loadingService.setLoading(false); + }); + } else { + this.handleSubstanceRetrivalError(); + this.loadingService.setLoading(false); + + } + this.loadingService.setLoading(false); + this.isLoading = false; + } + + getPartialSubstanceDetails(uuid: string, type: string): void { + this.substanceService.getSubstanceDetails(uuid).pipe(take(1)).subscribe(response => { + if (response) { + this.substanceClass = response.substanceClass; + this.status = response.status; + delete response.uuid; + if (response._name) { + delete response._name; + } + this.scrub(response, type); + this.substanceSsg4mService.loadSubstance(response.substanceClass, response).pipe(take(1)).subscribe(() => { + this.setFormSections(formSections[response.substanceClass]); + this.loadingService.setLoading(false); + this.isLoading = false; + }); + } else { + this.handleSubstanceRetrivalError(); + } + }, error => { + this.gaService.sendException('getSubstanceDetails: error from API call'); + this.loadingService.setLoading(false); + this.isLoading = false; + this.handleSubstanceRetrivalError(); + }); + } + + private handleSubstanceRetrivalError() { + const notification: AppNotification = { + message: 'The substance you\'re trying to edit doesn\'t exist.', + type: NotificationType.error, + milisecondsToShow: 4000 + }; + this.mainNotificationService.setNotification(notification); + this.errorMessage = "Unable to load the data."; + setTimeout(() => { + this.router.navigate(['/home']); + this.substanceSsg4mService.loadSubstance(this.subClass).pipe(take(1)).subscribe(() => { + this.setFormSections(formSections.chemical); + this.loadingService.setLoading(false); + this.isLoading = false; + }); + }, 5000); + } + + validate(validationType?: string): void { + if (validationType && validationType === 'approval') { + this.approving = true; + } else { + this.approving = false; + } + this.isLoading = true; + this.serverError = false; + this.loadingService.setLoading(true); + // this.substanceSsg4mService.validateSsg4m().pipe(take(1)).subscribe(results => { + this.submissionMessage = null; + // this.validationMessages = results.validationMessages.filter( + // message => message.messageType.toUpperCase() === 'ERROR' || message.messageType.toUpperCase() === 'WARNING'); + this.validationMessages = []; + this.validationResult = true; + this.showSubmissionMessages = true; + this.loadingService.setLoading(false); + this.isLoading = false; + // If there is no validation error, submit/save the records without displaying the warning/validation message. + if (this.validationMessages.length === 0 && true === true) { + this.submit(); + } + /* + if (this.validationMessages.length === 0 && true === true) { + this.submissionMessage = 'Record is valid. Would you like to submit?'; + } + */ + /* + if (validationType && validationType === 'approval') { + this.submissionMessage = 'Are you sure you\'d like to approve this substance?'; + }*/ + // }, error => { + // this.addServerError(error); + this.loadingService.setLoading(false); + this.isLoading = false; + // }); + } + + /* + approve(): void { + this.isLoading = true; + this.loadingService.setLoading(true); + this.substanceSsg4mService.approveSubstance().pipe(take(1)).subscribe(response => { + this.loadingService.setLoading(false); + this.isLoading = false; + this.validationMessages = null; + this.openSuccessDialog('approve'); + this.submissionMessage = 'Substance was approved successfully'; + this.showSubmissionMessages = true; + this.validationResult = false; + }, + (error: SubstanceFormResults) => { + this.showSubmissionMessages = true; + this.loadingService.setLoading(false); + this.isLoading = false; + this.submissionMessage = 'Substance Could not be approved'; + this.addServerError(error.serverError); + setTimeout(() => { + this.showSubmissionMessages = false; + this.submissionMessage = null; + }, 10000); + } + ); + } + */ + + validateSubstance(): Observable { + return new Observable(observer => { + // const substanceCopy = this.cleanSubstance(); + // CHANGING THIS NOW CHANGING THIS NOW + const substanceCopy = null; + this.substanceService.validateSubstance(substanceCopy).subscribe(results => { + // check for missing required reference fields and append a validationMessage + if (results.validationMessages) { + for (let i = 0; i < substanceCopy.references.length; i++) { + const ref = substanceCopy.references[i]; + if (ref.docType !== 'SYSTEM') { + if ((!ref.citation || ref.citation === '') || (!ref.docType || ref.docType === '')) { + const invalidReferenceMessage: ValidationMessage = { + actionType: 'frontEnd', + appliedChange: false, + links: [], + message: 'All references require a non-empty source type and text/citation value', + messageType: 'WARNING', + suggestedChange: true + }; + results.validationMessages.push(invalidReferenceMessage); + break; + } + } + } + if (substanceCopy.properties) { + for (let i = 0; i < substanceCopy.properties.length; i++) { + const prop = substanceCopy.properties[i]; + if (!prop.propertyType || !prop.name) { + const invalidPropertyMessage: ValidationMessage = { + actionType: 'frontEnd', + appliedChange: false, + links: [], + message: 'Property #' + (i + 1) + ' requires a non-empty name and type', + messageType: 'ERROR', + suggestedChange: true + }; + results.validationMessages.push(invalidPropertyMessage); + results.valid = false; + } + } + } + if (substanceCopy.relationships) { + for (let i = 0; i < substanceCopy.relationships.length; i++) { + const relationship = substanceCopy.relationships[i]; + if (!relationship.relatedSubstance || !relationship.type || relationship.type === '') { + const invalidRelationshipMessage: ValidationMessage = { + actionType: 'frontEnd', + appliedChange: false, + links: [], + message: 'Relationship #' + (i + 1) + ' requires a non-empty related substance and type', + messageType: 'ERROR', + suggestedChange: true + }; + results.validationMessages.push(invalidRelationshipMessage); + results.valid = false; + } + } + } + if (substanceCopy.polymer && substanceCopy.polymer.monomers) { + for (let i = 0; i < substanceCopy.polymer.monomers.length; i++) { + const prop = substanceCopy.polymer.monomers[i]; + if (!prop.monomerSubstance || prop.monomerSubstance === {}) { + const invalidPropertyMessage: ValidationMessage = { + actionType: 'frontEnd', + appliedChange: false, + links: [], + message: 'Monomer #' + (i + 1) + ' requires a selected substance', + messageType: 'ERROR', + suggestedChange: true + }; + results.validationMessages.push(invalidPropertyMessage); + results.valid = false; + } + } + } + if (substanceCopy.modifications && substanceCopy.modifications.physicalModifications) { + for (let i = 0; i < substanceCopy.modifications.physicalModifications.length; i++) { + const prop = substanceCopy.modifications.physicalModifications[i]; + let present = false; + prop.parameters.forEach(param => { + if (param.parameterName) { + present = true; + } + }); + + if (!prop.physicalModificationRole && !present) { + const invalidPropertyMessage: ValidationMessage = { + actionType: 'frontEnd', + appliedChange: false, + links: [], + message: 'Physical Modification #' + (i + 1) + ' requires a modification role or valid parameter', + messageType: 'ERROR', + suggestedChange: true + }; + results.validationMessages.push(invalidPropertyMessage); + results.valid = false; + } + } + } + } + observer.next(results); + observer.complete(); + }, error => { + observer.error(); + observer.complete(); + }); + }); + } + + submit(): void { + this.isLoading = true; + this.loadingService.setLoading(true); + this.approving = false; + + this.json = this.substanceFormService.cleanSubstance(); + let jsonValue = JSON.stringify(this.json); + + // Save SVG/Image as Blob + //This is a hacky placeholder way to force viz + //TODO finish this + const ssgjs = JSON.stringify(this.substanceFormService.cleanSubstance()); + window["schemeUtil"].onFinishedLayout = (svg) => { + window["schemeUtil"].onFinishedLayout = (svg) => { }; + + // if New Record, initialize object + if (this.ssg4mSyntheticPathway == null) { + this.ssg4mSyntheticPathway = {}; + + // Generate GUID SynthPathwayId + // this.ssg4mSyntheticPathway.synthPathwayId = '2ead5343-6471-88ae-b0b0-88370d44786e'; + // this.ssg4mSyntheticPathway = { "appType": "IND", "synthPathwayId": "2ead5343-6471-88ae-b0b0-88370d44574e", "sbmsnDataText": jsonValue }; + } + // Existing Record + // get the JSON from the SSG4m Form and store as a Clob into the database + this.ssg4mSyntheticPathway.sbmsnDataText = jsonValue; + + // Save SVG as blob + this.ssg4mSyntheticPathway.sbmsnImage = document.querySelector("#scheme-viz-view").innerHTML; + + // After submitting Save button, the UI waits for 5 seconds to see if it gets a response. + // after 5 seconds it displays a warning on the top of the UI form. + setTimeout(() => { + if (this.isSavedSuccessful === false) { + this.saveDelayedMessage = "Hmm ... this seems to be taking longer than normal, there may be network issues.
    Click here to cancel and continue working on the form. We suggest you save a local copy of the JSON."; + } + }, 5000); + + this.submitSubscription = this.substanceSsg4mService.saveSsg4m(this.ssg4mSyntheticPathway).pipe(take(1)).subscribe(response => { + // Stop the spinner + this.loadingService.setLoading(false); + this.isLoading = false; + + // Set validation messages to null + this.validationMessages = null; + this.showSubmissionMessages = false; + this.validationResult = false; + + // if Saved Successfully + if (response && response.synthPathwaySkey) { + if (response.synthPathwaySkey) { + this.id = response.synthPathwaySkey.toString(); + } + + // if the API communication does resolve, AND the initial save went through, it will replace + // the warning message. Only show this message when user clicked on the 'Cancel' button. + // After user clicks 'Refresh' button, refresh the page manually. + this.isSavedSuccessful = true; + if (this.isCancelBtnClicked === true && this.isSavedSuccessful === true) { + this.saveDelayedMessage = " Network communication restored, click here to refresh with saved version."; + } + else { + this.saveDelayedMessage = ""; + this.isCancelBtnClicked = false; + // Only show successful dialog and refresh page, if user does not click on the cancel button. + this.openSuccessDialog(); + } + // Refresh the current page, this will not cause record locking issue + /* this.router.routeReuseStrategy.shouldReuseRoute = () => false; + this.router.onSameUrlNavigation = 'reload'; + this.router.navigate(['/substances-ssg4m', this.id, 'edit']); + */ + } + }, (error: SubstanceFormResults) => { + this.loadingService.setLoading(false); + this.isLoading = false; + // If submit was not successful, display Message + this.saveDelayedMessage = " Network communication restored, RECORD HAS NOT BEEN SAVED. Please resave the record."; + + this.showSubmissionMessages = true; + this.submissionMessage = null; + if (error.validationMessages && error.validationMessages.length) { + this.validationResult = error.isSuccessfull; + this.validationMessages = error.validationMessages + .filter(message => message.messageType.toUpperCase() === 'ERROR' || message.messageType.toUpperCase() === 'WARNING'); + this.showSubmissionMessages = true; + } else { + this.submissionMessage = 'There was a problem with your submission'; + this.addServerError(error.serverError); + setTimeout(() => { + this.showSubmissionMessages = false; + this.submissionMessage = null; + }, 8000); + } + }); + this.subscriptions.push(this.submitSubscription); + + }; //window + + window['schemeUtil'].renderScheme(window['schemeUtil'].makeDisplayGraph(JSON.parse(ssgjs)), "#scheme-viz-view"); + } + + cancelSubmit() { + // Stop the loading if user clicks on cancel button + this.loadingService.setLoading(false); + this.isLoading = false; + + this.isCancelBtnClicked = true; + this.saveDelayedMessage = "Warning: Network traffic is delayed, attempting to reconnect to the server ... please save a local copy of the record."; + } + + refreshPage() { + // Refresh the current page, this will not cause record locking issue + this.router.routeReuseStrategy.shouldReuseRoute = () => false; + this.router.onSameUrlNavigation = 'reload'; + if (this.showHeaderBar && this.showHeaderBar === 'false') { + const route = '/substances-ssg4m/' + this.id + '/edit?header=' + this.showHeaderBar; + this.router.navigateByUrl(route); + } else { + this.router.navigate(['/substances-ssg4m', this.id, 'edit']); + } + } + + dismissValidationMessage(index: number) { + this.validationMessages.splice(index, 1); + + if (this.validationMessages.length === 0) { + this.submissionMessage = 'Substance is Valid. Would you like to submit?'; + } + } + + addServerError(error: any): void { + this.serverError = true; + this.validationResult = false; + this.validationMessages = null; + + const message: ValidationMessage = { + actionType: 'server failure', + links: [], + appliedChange: false, + suggestedChange: false, + messageType: 'ERROR', + message: 'Unknown Server Error' + }; + if (error && error.error && error.error.message) { + message.message = 'Server Error ' + (error.status + ': ' || ': ') + error.error.message; + } else if (error && error.error && (typeof error.error) === 'string') { + message.message = 'Server Error ' + (error.status + ': ' || '') + error.error; + } else if (error && error.message) { + message.message = 'Server Error ' + (error.status + ': ' || '') + error.message; + } + this.validationMessages = [message]; + this.showSubmissionMessages = true; + } + + toggleValidation(): void { + this.showSubmissionMessages = !this.showSubmissionMessages; + } + + dismissAllValidationMessages(): void { + for (let i = this.validationMessages.length - 1; i >= 0; i--) { + if (this.validationMessages[i].messageType !== 'ERROR') { + this.validationMessages.splice(i, 1); + } + } + if (this.validationMessages.length === 0) { + this.submissionMessage = 'Substance is Valid. Would you like to submit?'; + } + } + + @HostListener('window:beforeunload', ['$event']) + unloadNotification($event: any) { + if (this.substanceSsg4mService.isSubstanceUpdated) { + $event.returnValue = true; + } + } + + scrub(oldraw: any, importType: string): any { + function guid() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + + s4() + '-' + s4() + s4() + s4(); + } + const old = oldraw; + + const idHolders = defiant.json.search(old, '//*[id]'); + const idMap = {}; + for (let i = 0; i < idHolders.length; i++) { + const oid = idHolders[i].id; + if (idMap[oid]) { + idHolders[i].id = idMap[oid]; + } else { + const nid = guid(); + idHolders[i].id = nid; + idMap[oid] = nid; + } + } + + const uuidHolders = defiant.json.search(old, '//*[uuid]'); + const _map = {}; + for (let i = 0; i < uuidHolders.length; i++) { + const ouuid = uuidHolders[i].uuid; + if (_map[ouuid]) { + uuidHolders[i].uuid = _map[ouuid]; + if (uuidHolders[i].id) { + uuidHolders[i].id = _map[ouuid]; + } + } else { + const nid = guid(); + uuidHolders[i].uuid = nid; + _map[ouuid] = nid; + if (uuidHolders[i].id) { + uuidHolders[i].id = nid; + } + } + } + const refHolders = defiant.json.search(old, '//*[references]'); + for (let i = 0; i < refHolders.length; i++) { + const refs = refHolders[i].references; + for (let j = 0; j < refs.length; j++) { + const or = refs[j]; + if (typeof or === 'object') { continue; } + refs[j] = _map[or]; + } + } + defiant.json.search(old, '//*[uuid]'); + _.remove(old.codes, { + codeSystem: 'BDNUM' + }); + const createHolders = defiant.json.search(old, '//*[created]'); + for (let i = 0; i < createHolders.length; i++) { + const rec = createHolders[i]; + delete rec['created']; + delete rec['createdBy']; + delete rec['lastEdited']; + delete rec['lastEditedBy']; + } + + const originHolders = defiant.json.search(old, '//*[originatorUuid]'); + for (let i = 0; i < originHolders.length; i++) { + const rec = originHolders[i]; + delete rec['originatorUuid']; + } + + delete old.approvalID; + delete old.approved; + delete old.approvedBy; + old.status = 'pending'; + if ((importType) && (importType === 'definition')) { + old.names = []; + old.codes = []; + old.notes = []; + old.relationships = []; + old.tags = []; + } + delete old['createdBy']; + delete old['created']; + delete old['lastEdited']; + delete old['lastEditedBy']; + delete old['version']; + delete old['$$update']; + delete old['changeReason']; + + + if (true) { + const refSet = {}; + + const refHolders2 = defiant.json.search(old, '//*[references]'); + for (let i = 0; i < refHolders2.length; i++) { + const refs = refHolders2[i].references; + for (let j = 0; j < refs.length; j++) { + const or = refs[j]; + if (typeof or === 'object') { continue; } + refSet[or] = true; + } + } + + const nrefs = _.chain(old.references) + .filter(function (ref) { + if (refSet[ref.uuid]) { + return true; + } else { + return false; + } + }) + .value(); + + old.references = nrefs; + + } + + return old; + } + + openSuccessDialog(type?: string): void { + let data = { + isCoreSubstance: 'false' + }; + const dialogRef = this.dialog.open(SubmitSuccessDialogComponent, { + data: data + }); + this.overlayContainer.style.zIndex = '1002'; + + const dialogSubscription = dialogRef.afterClosed().pipe(take(1)).subscribe((response?: 'continue') => { + + this.substanceSsg4mService.bypassUpdateCheck(); + if (response === 'continue') { + + // Refresh the current page, this will not cause record locking issue + this.router.routeReuseStrategy.shouldReuseRoute = () => false; + this.router.onSameUrlNavigation = 'reload'; + if (this.showHeaderBar && this.showHeaderBar === 'false') { + const route = '/substances-ssg4m/' + this.id + '/edit?header=' + this.showHeaderBar; + this.router.navigateByUrl(route); + } else { + this.router.navigate(['/substances-ssg4m', this.id, 'edit']); + } + + // } else if (response === 'browse') { + // this.router.navigate(['/browse-substance']); + // } else if (response === 'home') { + // this.router.navigate(['/home']); + // } else { + this.showSubmissionMessages = true; + this.validationResult = false; + this.submissionMessage = ''; + /* + setTimeout(() => { + this.showSubmissionMessages = false; + this.submissionMessage = ''; + this.router.navigate(['/substances-ssg4m', this.id, 'edit']); + }, 3000); + */ + } + }); + this.subscriptions.push(dialogSubscription); + + } + + mergeConcept() { + this.feature = undefined; + const dialogRef = this.dialog.open(MergeConceptDialogComponent, { + width: '900px', data: { uuid: this.id } + }); + this.overlayContainer.style.zIndex = '1002'; + } + + definitionSwitch() { + this.feature = undefined; + const dialogRef = this.dialog.open(DefinitionSwitchDialogComponent, { + width: '900px', data: { uuid: this.id }, autoFocus: false + }); + this.overlayContainer.style.zIndex = '1000'; + } + + fixLink(link: string) { + return this.substanceService.oldLinkFix(link); + } +} diff --git a/src/app/core/substance-ssg4m/substance-ssg4m-form.service.ts b/src/app/core/substance-ssg4m/substance-ssg4m-form.service.ts new file mode 100644 index 000000000..d0d4df469 --- /dev/null +++ b/src/app/core/substance-ssg4m/substance-ssg4m-form.service.ts @@ -0,0 +1,459 @@ +import { Injectable, OnDestroy } from '@angular/core'; +import { HttpClient, HttpParams, HttpClientJsonpModule, HttpParameterCodec } from '@angular/common/http'; +import * as _ from 'lodash'; +import { take } from 'rxjs/operators'; +import { ConfigService } from '@gsrs-core/config/config.service'; +import { Ssg4mSyntheticPathway, Ssg4mSyntheticPathwayDetail } from './model/substance-ssg4m.model'; +import { SubstanceDetail } from '../substance/substance.model'; +import { SubstanceName } from '../substance/substance.model'; +import { SubstanceFormDefinition, SubunitSequence, ValidationResults, ValidationMessage } from '../substance-form/substance-form.model'; +import { Observable, Subject, ReplaySubject, Subscription } from 'rxjs'; +import { SubstanceService } from '@gsrs-core/substance/substance.service'; +import { UtilsService } from '@gsrs-core/utils/utils.service'; +import { StructureService } from '@gsrs-core/structure'; + +@Injectable({ + providedIn: 'root' +}) + +@Injectable() +export class SubstanceSsg4mService implements OnDestroy { + private privateSubstance: SubstanceDetail; + private substanceStateHash?: number; + private substanceEmitter: ReplaySubject; + /* + private substanceDisulfideLinksEmitter = new ReplaySubject>(); + private substanceGlycosylationEmitter = new ReplaySubject(); + private substanceLinksEmitter = new ReplaySubject>(); + private substanceNamesEmitter = new ReplaySubject>(); + private substanceOtherLinksEmitter = new ReplaySubject>(); + private substanceStructuralModificationsEmitter = new ReplaySubject>(); + private substanceCysteineEmitter = new ReplaySubject>(); + */ + private substanceFormActionEmitter = new ReplaySubject<'load' | 'unload'>(); + + /* private definitionEmitter = new Subject(); */ + private subClass: string; + /* + private substanceSubunitsEmitter = new Subject>(); + private substanceSugarsEmitter = new Subject>(); + private substanceNucleicAcidEmitter = new Subject(); + private displaySequences: Array; + private displaySequencesEmitter = new Subject>(); + */ + + private substanceChangeReasonEmitter = new Subject(); + private nameResolver = new Subject(); + resolvedMol = this.nameResolver.asObservable(); + private _bypassUpdateCheck = false; + private method?: string; + + apiBaseUrlSsg4mEntityUrl = this.configService.configData.apiSSG4mBaseUrl + 'api/v1/ssg4m' + '/'; + + constructor( + private substanceService: SubstanceService, + public utilsService: UtilsService, + private structureService: StructureService, + public http: HttpClient, + public configService: ConfigService + ) { + this.substanceEmitter = new ReplaySubject(); + } + + ngOnDestroy() { + this.unloadSubstance(); + } + + loadSubstance(substanceClass: string = 'chemical', substance?: SubstanceDetail, method?: string, mergeConcept?: boolean): Observable { + if (method) { + this.method = method; + } else { + this.method = null; + } + if (mergeConcept) { + this.privateSubstance = substance; + this.substanceEmitter.next(substance); + // this.namesUpdated(); + } + return new Observable(observer => { + if (substance != null) { + this.privateSubstance = substance; + substanceClass = this.privateSubstance.substanceClass; + } else { + // the second case happens in the forms sometimes but really shouldn't + if (substanceClass === 'chemical' || substanceClass === 'structure') { + this.privateSubstance = { + substanceClass: 'chemical', + references: [], + names: [], + structure: { + molfile: '\n\n\n 0 0 0 0 0 0 999 V2000\nM END' + }, + codes: [], + relationships: [], + properties: [] + }; + } else if (substanceClass === 'protein') { + this.privateSubstance = { + substanceClass: 'protein', + references: [], + names: [], + protein: { proteinType: '' }, + codes: [], + relationships: [], + properties: [] + }; + } else if (substanceClass === 'nucleicAcid') { + this.privateSubstance = { + substanceClass: 'nucleicAcid', + references: [], + names: [], + nucleicAcid: {}, + codes: [], + relationships: [], + properties: [] + }; + } else if (substanceClass === 'mixture') { + this.privateSubstance = { + substanceClass: 'mixture', + references: [], + names: [], + mixture: {}, + codes: [], + relationships: [], + properties: [] + }; + } else if (substanceClass === 'structurallyDiverse') { + this.privateSubstance = { + substanceClass: 'structurallyDiverse', + references: [], + names: [], + structurallyDiverse: { + part: ['whole'], + $$diverseType: 'whole' + }, + codes: [], + relationships: [], + properties: [] + }; + } else if (substanceClass === 'specifiedSubstance' || (substanceClass === 'specifiedSubstanceG1')) { + this.privateSubstance = { + substanceClass: 'specifiedSubstanceG1', + references: [], + names: [], + specifiedSubstance: { + constituents: [], + references: [] + }, + codes: [], + relationships: [], + properties: [] + }; + } else if (substanceClass === 'specifiedSubstanceG3') { + this.privateSubstance = { + substanceClass: substanceClass, + references: [], + names: [], + specifiedSubstanceG3: { + parentSubstance: {}, + definition: { references: [] }, + grade: { references: [] } + }, + codes: [], + properties: [] + }; + } else if (substanceClass === 'polymer') { + this.privateSubstance = { + substanceClass: substanceClass, + references: [], + names: [], + polymer: { + idealizedStructure: {}, + monomers: [], + }, + codes: [], + moieties: [], + relationships: [], + properties: [] + }; + } else if (substanceClass === 'specifiedSubstanceG4m') { + this.privateSubstance = { + substanceClass: substanceClass, + // references: [], + specifiedSubstanceG4m: { + parentSubstance: {}, + process: [] + }, + // codes: [], + // properties: [] + }; + } else { + this.privateSubstance = { + substanceClass: substanceClass, + references: [], + names: [], + codes: [] + }; + } + // default values + + // TP: default to protected for root level record. + // this.privateSubstance.access = ["protected"]; + // this.privateSubstance.definitionLevel = "COMPLETE"; + // this.privateSubstance.definitionType = "PRIMARY"; + } + + this.subClass = this.privateSubstance.substanceClass; + + // Only these two substance classes differ from + // the name of their JSON defintional element + // That's why they are used as exceptions + + if (this.subClass === 'chemical') { + this.subClass = 'structure'; + } else if (this.subClass === 'specifiedSubstanceG1') { + this.subClass = 'specifiedSubstance'; + } + + if (this.privateSubstance[this.subClass] == null) { + this.privateSubstance[this.subClass] = {}; + } + this.initForm(); + this.substanceEmitter.next(this.privateSubstance); + observer.next(); + observer.complete(); + }); + } + + get substanceFormAction(): Observable<'load' | 'unload'> { + return this.substanceFormActionEmitter.asObservable(); + } + + initForm(): void { + this.substanceFormActionEmitter.next('load'); + } + + get substance(): Observable { + return this.substanceEmitter.asObservable(); + } + + resetState(): void { + const substanceString = JSON.stringify(this.privateSubstance); + this.substanceStateHash = this.utilsService.hashCode(substanceString); + } + + unloadSubstance(): void { + // this.privateSubstance = null; + /* + this.displaySequences = null; + this.substanceEmitter.complete(); + this.substanceDisulfideLinksEmitter.complete(); + this.substanceGlycosylationEmitter.complete(); + this.substanceLinksEmitter.complete(); + this.substanceNamesEmitter.complete(); + this.substanceOtherLinksEmitter.complete(); + this.substanceStructuralModificationsEmitter.complete(); + this.substanceCysteineEmitter.complete(); + */ + this.substanceEmitter = new ReplaySubject(); + /* + this.substanceDisulfideLinksEmitter = new ReplaySubject>(); + this.substanceGlycosylationEmitter = new ReplaySubject(); + this.substanceLinksEmitter = new ReplaySubject>(); + this.substanceLinksEmitter = new ReplaySubject>(); + this.substanceOtherLinksEmitter = new ReplaySubject>(); + this.substanceStructuralModificationsEmitter = new ReplaySubject>(); + this.substanceCysteineEmitter = new ReplaySubject>(); + */ + this.substanceFormActionEmitter.next('unload'); + } + + ready(): Observable { + return new Observable(observer => { + this.substanceEmitter.pipe(take(1)).subscribe(substance => { + observer.next(); + observer.complete(); + }); + }); + } + + get isSubstanceUpdated(): boolean { + const substanceString = JSON.stringify(this.privateSubstance); + if (this._bypassUpdateCheck) { + this._bypassUpdateCheck = false; + return false; + } else { + return this.substanceStateHash !== this.utilsService.hashCode(substanceString); + } + } + + bypassUpdateCheck(): void { + this._bypassUpdateCheck = true; + } + + checkSsg4mServerStatus(): Observable { + const url = this.apiBaseUrlSsg4mEntityUrl; + return this.http.get(url); + } + + getSsg4mDetails(id: string, version?: string): Observable { + // const url = `${this.configService.configData.apiBaseUrl}api/v1/ssg4m/${id}`; + const url = this.apiBaseUrlSsg4mEntityUrl + id; + + return this.http.get(url); + } + + saveSsg4m(ssg4m: Ssg4mSyntheticPathway, type?: string): Observable { + // const url = `${this.configService.configData.apiBaseUrl}api/v1/ssg4m`; + const url = this.apiBaseUrlSsg4mEntityUrl; + let method = ssg4m.synthPathwaySkey ? 'PUT' : 'POST'; + const options = { + body: ssg4m + }; + return this.http.request(method, url, options); + } + + validateSsg4m(ssg4m: Ssg4mSyntheticPathway): Observable { + // const url = `${this.configService.configData.apiBaseUrl}api/v1/ssg4m/@validate`; + const url = this.apiBaseUrlSsg4mEntityUrl + '@validate'; + return this.http.post(url, ssg4m); + } + + cleanSubstance(): SubstanceDetail { + if (this.privateSubstance.structurallyDiverse) { + if (this.privateSubstance.structurallyDiverse.$$diverseType) { + delete this.privateSubstance.structurallyDiverse.$$diverseType; + } + if (this.privateSubstance.structurallyDiverse.$$storedPart) { + delete this.privateSubstance.structurallyDiverse.$$storedPart; + } + + const toclean = ['organismFamily', 'organismGenus', 'organismSpecies', 'organismAuthor', 'infraSpecificName', 'infraSpecificType', 'fractionMaterialType', 'fractionName', 'developmentalStage']; + toclean.forEach(field => { + if (this.privateSubstance.structurallyDiverse[field] && this.privateSubstance.structurallyDiverse[field] !== null && + this.privateSubstance.structurallyDiverse[field] !== '') { + this.privateSubstance.structurallyDiverse[field] = this.privateSubstance.structurallyDiverse[field].trim(); + } + }); + } + + /*the substance API call for view=internal vs the usual 'view=full' adds some properties that should not be submitted + and can cause errors upon submission. the view change was to allow the stdName property to be visible to the forms*/ + if (this.privateSubstance.structure) { + + if (this.privateSubstance.structure.properties) { + delete this.privateSubstance.structure.properties; + } + if (this.privateSubstance.structure.links) { + delete this.privateSubstance.structure.links; + } + } + if (this.privateSubstance.polymer && this.privateSubstance.polymer.displayStructure) { + + if (this.privateSubstance.polymer.displayStructure.properties) { + delete this.privateSubstance.polymer.displayStructure.properties; + } + if (this.privateSubstance.polymer.displayStructure.links) { + delete this.privateSubstance.polymer.displayStructure.links; + } + } + if (this.privateSubstance.polymer && this.privateSubstance.polymer.idealizedStructure) { + + if (this.privateSubstance.polymer.idealizedStructure.properties) { + delete this.privateSubstance.polymer.idealizedStructure.properties; + } + if (this.privateSubstance.polymer.idealizedStructure.links) { + delete this.privateSubstance.polymer.idealizedStructure.links; + } + } + + if (this.privateSubstance.moieties) { + this.privateSubstance.moieties.forEach(moiety => { + if (moiety.properties) { + delete moiety.properties; + } + if (moiety.links) { + delete moiety.links; + } + }); + } + + if (this.privateSubstance.protein && this.privateSubstance.protein.disulfideLinks + && this.privateSubstance.protein.disulfideLinks.length > 0) { + for (let i = this.privateSubstance.protein.disulfideLinks.length; i >= 0; i--) { + if (this.privateSubstance.protein.disulfideLinks[i] && this.privateSubstance.protein.disulfideLinks[i].sites && + this.privateSubstance.protein.disulfideLinks[i].sites[0] && this.privateSubstance.protein.disulfideLinks[i].sites[1] && + Object.keys(this.privateSubstance.protein.disulfideLinks[i].sites[0]).length === 0 && + Object.keys(this.privateSubstance.protein.disulfideLinks[i].sites[1]).length === 0) { + this.privateSubstance.protein.disulfideLinks.splice(i, 1); + } + } + } + // end view=internal changes + + let substanceString = JSON.stringify(this.privateSubstance); + let substanceCopy: SubstanceDetail = JSON.parse(substanceString); + + const response = this.cleanObject(substanceCopy); + const deletedUuids = response.deletedUuids; + + if (deletedUuids.length > 0) { + substanceString = JSON.stringify(substanceCopy); + + deletedUuids.forEach(uuid => { + substanceString = substanceString.replace(new RegExp(`"${uuid}"`, 'g'), ''); + }); + substanceString = substanceString.replace(/,[,]+/g, ','); + substanceString = substanceString.replace(/\[,/g, '['); + substanceString = substanceString.replace(/,\]/g, ']'); + substanceCopy = JSON.parse(substanceString); + } + + return substanceCopy; + } + + private cleanObject(substanceProperty: any, deletedUuids: Array = []): { deletedUuids: Array, isDeleted: boolean } { + if (Object.prototype.toString.call(substanceProperty) === '[object Object]') { + + const hasDeleletedCode = substanceProperty.$$deletedCode != null; + if (!hasDeleletedCode) { + delete substanceProperty.$$deletedCode; + Object.keys(substanceProperty).forEach(key => { + if (Object.prototype.toString.call(substanceProperty[key]) === '[object Array]') { + substanceProperty[key] = substanceProperty[key].filter(childProperty => { + const response = this.cleanObject(childProperty, deletedUuids); + return !response.isDeleted; + }); + } else if (Object.prototype.toString.call(substanceProperty[key]) === '[object Object]') { + this.cleanObject(substanceProperty[key], deletedUuids); + } + }); + } else if (substanceProperty.uuid != null) { + deletedUuids.push(substanceProperty.uuid); + } + + return { + deletedUuids: deletedUuids, + isDeleted: hasDeleletedCode + }; + } else if (Object.prototype.toString.call(substanceProperty) === '[object Array]') { + substanceProperty.forEach(childProperty => { + this.cleanObject(childProperty, deletedUuids); + }); + } else { + return { + deletedUuids: deletedUuids, + isDeleted: false + }; + } + } + + getSyntheticPathwayIndexBySubUuid(subUuid: string) { + // const url = `${this.configService.configData.apiBaseUrl}api/v1/ssg4m/${id}`; + const url = this.apiBaseUrlSsg4mEntityUrl + '/indexbysubuuid/' + subUuid; + + return this.http.get(url); + // return this.http.get(url); + } + +} diff --git a/src/app/core/substance-ssg4m/substance-ssg4m.module.ts b/src/app/core/substance-ssg4m/substance-ssg4m.module.ts new file mode 100644 index 000000000..2b340ef6b --- /dev/null +++ b/src/app/core/substance-ssg4m/substance-ssg4m.module.ts @@ -0,0 +1,150 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { CommonModule, formatDate } from '@angular/common'; +import { Router, Routes, RouterModule } from '@angular/router'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatSelectModule } from '@angular/material/select'; +import { MatInputModule } from '@angular/material/input'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { ScrollToModule } from '../scroll-to/scroll-to.module'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatTableModule } from '@angular/material/table'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatBadgeModule } from '@angular/material/badge'; +import { MatRadioModule } from '@angular/material/radio'; +import { ExpandDetailsModule } from '../expand-details/expand-details.module'; +// import { AccessManagerComponent } from './access-manager/access-manager.component'; +import { SubstanceSelectorModule } from '../substance-selector/substance-selector.module'; +import { MatListModule } from '@angular/material/list'; +import { FileSelectModule } from 'file-select'; +import { CvInputComponent} from '@gsrs-core/substance-form/cv-input/cv-input.component'; +import { CvDialogComponent} from '@gsrs-core/substance-form/cv-dialog/cv-dialog.component'; +import { MatButtonToggleModule} from '@angular/material/button-toggle'; +import { JsonDialogComponent} from '@gsrs-core/substance-form/json-dialog/json-dialog.component'; +import { NgxJsonViewerModule} from 'ngx-json-viewer'; +import { AuditInfoComponent} from '@gsrs-core/substance-form/audit-info/audit-info.component'; +import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; +// import { SubmitSuccessDialogComponent } from './submit-success-dialog/submit-success-dialog.component'; +import { MergeConceptDialogComponent} from '@gsrs-core/substance-form/merge-concept-dialog/merge-concept-dialog.component'; +import { MatProgressBarModule} from '@angular/material/progress-bar'; +// import { SubstanceFormComponent } from './substance-form.component'; +// import { CanActivateSubstanceForm } from './can-activate-substance-form'; +// import { CanRegisterSubstanceForm } from './can-register-substance-form'; +// import { SubstanceFormService } from '../substance-form.service'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { SubstanceSsg4mService } from './substance-ssg4m-form.service'; +import { SubstanceFormSsg4mProcessService } from './ssg4m-process/substance-form-ssg4m-process.service'; +import { SubstanceSsg4ManufactureFormComponent } from './substance-ssg4m-form.component'; +import { SubstanceFormComponent } from '../substance-form/substance-form.component'; +// import { SubstanceFormSsg4mSitesService } from './ssg4m-sites/substance-form-ssg4m-sites.service.'; +import { SubstanceSsg4mProcessModule } from './ssg4m-process/substance-form-ssg4m-process.module'; +import { Ssg4mSitesModule } from './ssg4m-sites/ssg4m-sites.module'; +import { Ssg4mStepViewDialogModule } from './ssg4m-step-view-dialog/ssg4m-step-view-dialog.module'; +import { SsgParentSubstanceFormModule } from '../substance-form/ssg-parent-substance-form/ssg-parent-substance-form.module'; + +const ssg4mRoutes: Routes = [ + { + path: 'substances-ssg4m/register', + component: SubstanceSsg4ManufactureFormComponent + // canActivate: [CanRegisterSubstanceForm], + // canDeactivate: [CanDeactivateSubstanceFormGuard] + }, + { + path: 'substances-ssg4m/:id/edit', + component: SubstanceSsg4ManufactureFormComponent, + // canActivate: [CanRegisterSubstanceForm], + // canDeactivate: [CanDeactivateSubstanceFormGuard] + } + // , + // { + // path: 'substances-ssg4m/:id', + // component: SubstanceSsg4ManufactureFormComponent + // } +]; + +@NgModule({ + imports: [ + RouterModule.forChild(ssg4mRoutes), + CommonModule, + ReactiveFormsModule, + FormsModule, + MatFormFieldModule, + MatMenuModule, + MatCheckboxModule, + MatButtonModule, + MatIconModule, + MatTooltipModule, + MatTabsModule, + MatDividerModule, + MatSelectModule, + MatInputModule, + MatChipsModule, + MatAutocompleteModule, + ScrollToModule, + MatDialogModule, + MatTableModule, + MatExpansionModule, + MatBadgeModule, + MatRadioModule, + ExpandDetailsModule, + SubstanceSelectorModule, + MatListModule, + FileSelectModule, + MatButtonToggleModule, + NgxJsonViewerModule, + RouterModule, + SubstanceImageModule, + MatProgressBarModule, + MatProgressSpinnerModule, + Ssg4mStepViewDialogModule + // SubstanceSsg4mProcessModule + // Ssg4mSitesModule + ], + declarations: [ + // SubstanceFormComponent, + SubstanceSsg4ManufactureFormComponent + // CvInputComponent, + // CvDialogComponent, + // JsonDialogComponent + ], + exports: [ + // SubstanceFormComponent, + // SubstanceSsg4ManufactureFormComponent, + // CvInputComponent, + // CvDialogComponent, + // JsonDialogComponent, + ], + entryComponents: [ + // CvDialogComponent, + // JsonDialogComponent, + // AuditInfoComponent, + // SubmitSuccessDialogComponent, + ] +}) + +export class SubstanceSsg4mModule { + constructor(router: Router) { + ssg4mRoutes.forEach(route => { + router.config[0].children.push(route); + }); + } + + static forRoot(): ModuleWithProviders { + return { + ngModule: SubstanceSsg4mModule, + providers: [ + // SubstanceSsg4mService, + // SubstanceFormSsg4mProcessService, + // SubstanceFormSsg4mSitesService + ] + }; + } +} diff --git a/src/app/core/substance-text-search/substance-text-search.component.html b/src/app/core/substance-text-search/substance-text-search.component.html index 263be5c17..fa1f0e8a7 100644 --- a/src/app/core/substance-text-search/substance-text-search.component.html +++ b/src/app/core/substance-text-search/substance-text-search.component.html @@ -13,14 +13,15 @@ (closed)="autoCompleteClosed()">
    {{field['display']}}
    - +
    {{suggestion.key}}
    - + +   + +
    +
    + +
    + + + + + + + + + + + + +
    + + + + + + + +
    + + + +
    +
    +
    + Please enter a file name: +
    +
    +
    + + + +
    +
    .{{extension}}
    +
    +
    +
    + +
    +
    {{message}}
    +
    - -
    + + + + + + + \ No newline at end of file diff --git a/src/app/core/substances-browse/export-dialog/export-dialog.component.scss b/src/app/core/substances-browse/export-dialog/export-dialog.component.scss index a4518717e..8456a97d1 100644 --- a/src/app/core/substances-browse/export-dialog/export-dialog.component.scss +++ b/src/app/core/substances-browse/export-dialog/export-dialog.component.scss @@ -5,10 +5,27 @@ align-items: center; } +.config-label .mat-focused { + margin-top: -2px; +} + .name-form { width: 400px; } +.preset { + width: 250px; +} + +.top-button { + margin-left:5px; +} + +.top-button-container { + margin-top: 10px; + margin-left: 50px; +} + .format { margin-left: 10px; } @@ -16,4 +33,62 @@ .padding { margin-bottom: 15px; padding-bottom:5px; + padding-top: 12px; +} + +.mat-warn { + color: var(--regular-red-color); + background-color: white; + border: 1px solid var(--regular-red-color); +} + + +::ng-deep sf-string-widget > div { + margin-left:50px; +} + + +::ng-deep .has-success > ::ng-deep sf-string-widget { + margin-left:20px; + +} + + +::ng-deep sf-string-widget > ::ng-deep .control-label { + min-width: 250px; +display: inline-block; +} + + +.top-label { + display: flex; + flex-direction:row; + width: 100%; +} + +.message { + font-weight: 500; + margin:10px; + font-size:18px; +} + +.message-container { + width:100%; + display:flex; + flex-direction:row; + justify-content: center; +} + +.form-row { + width:100%; + display:flex; + flex-direction:row; +} + +.top-margin { + margin-top:15px; +} + +.right-margin { + margin-right: 10px; } \ No newline at end of file diff --git a/src/app/core/substances-browse/export-dialog/export-dialog.component.ts b/src/app/core/substances-browse/export-dialog/export-dialog.component.ts index a3e0a2cbd..238306e9b 100644 --- a/src/app/core/substances-browse/export-dialog/export-dialog.component.ts +++ b/src/app/core/substances-browse/export-dialog/export-dialog.component.ts @@ -1,6 +1,8 @@ import { Component, OnInit, Inject } from '@angular/core'; import * as moment from 'moment'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import * as _ from 'lodash'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { SubstanceService } from '@gsrs-core/substance/substance.service'; @Component({ selector: 'app-export-dialog', @@ -10,13 +12,88 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; export class ExportDialogComponent implements OnInit { name: string; extension: string; + showOptions = false; + scrubberModel = {}; + expanderModel = {}; + expanderSchema: any; + exporterModel = {}; + exporterSchema: any; + options = []; + loadedConfig: any; + configName: string; + message: string; + privateExpanderModel: any; + privateScrubberModel: any; + privateExporterModel: any; + unsaved = false; + entity = 'substances'; + + private privateOptions: any; + temp: any; + + + scrubberSchema = {}; constructor( public dialogRef: MatDialogRef, + public substanceService: SubstanceService, @Inject(MAT_DIALOG_DATA) public data: any - ) { } + ) { + if(data.entity) { + this.entity = data.entity; + } + } ngOnInit() { + this.sortConfigs(); + this.scrubberModel = { }; + this.expanderModel = {}; + this.substanceService.getSchema('scrubber', this.entity).subscribe(response => { + + Object.keys(response.properties).forEach(val => { + if (response.properties[val] && response.properties[val]['visibleIf']) { + Object.keys(response.properties[val]['visibleIf']).forEach(vis => { + if (response.properties[vis]) { + response.properties[vis]['children'] = 1; + } + }); + + + } + }) + this.scrubberSchema = response; + + }); + this.substanceService.getSchema('expander', this.entity).subscribe(response => { + Object.keys(response.properties).forEach(val => { + if (response.properties[val] && response.properties[val]['visibleIf']) { + Object.keys(response.properties[val]['visibleIf']).forEach(vis => { + if (response.properties[vis]) { + response.properties[vis]['children'] = 1; + } + }); + + + } + }); + this.expanderSchema = response; + + }); + this.substanceService.getExportOptions(this.data.extension, this.entity).subscribe(response => { + Object.keys(response.properties).forEach(val => { + if (response.properties[val] && response.properties[val]['visibleIf']) { + Object.keys(response.properties[val]['visibleIf']).forEach(vis => { + if (response.properties[vis]) { + response.properties[vis]['children'] = 1; + } + }); + + + } + }); + this.exporterSchema = response; + +}); const date = new Date(); if (this.data.type && this.data.type !== null && this.data.type !== '') { this.name = this.data.type + '-' + moment(date).format('DD-MM-YYYY_H-mm-ss'); @@ -26,13 +103,240 @@ export class ExportDialogComponent implements OnInit { this.extension = this.data.extension; } + + setValue(event: any, model?: string ): void { + if (model && model === 'expander') { + this.privateExpanderModel = event; + this.unsaved = this.unsavedChangeCheck(this.loadedConfig.expanderSettings, this.privateExpanderModel) + } else if( model === 'scrubber') { + this.privateScrubberModel = event; + this.unsaved = this.unsavedChangeCheck(this.loadedConfig.scrubberSettings, this.privateScrubberModel); + } else { + this.privateExporterModel = event; + this.unsaved = this.unsavedChangeCheck(this.loadedConfig.exporterSettings, this.privateExporterModel); + } + } + save(): void { - this.dialogRef.close(this.name); + let response = { + 'name': this.name, + 'id': this.loadedConfig? this.loadedConfig.configurationId : null + }; + + if (this.unsavedChanges()) { + // if options are hidden and there are changes, they should have already seen the confirm + if(this.showOptions) { + if (confirm('Warning: Unsaved changes to the configuration will not be applied. Continue?')) { + this.dialogRef.close(response); + } + } else { + this.dialogRef.close(response); + } + + } else { + this.dialogRef.close(response); + } + } cancel(): void { this.dialogRef.close(); } + toggleShowOptions(): void { + if (this.showOptions && this.unsavedChanges()) { + + if (confirm('Warning: Unsaved changes to the configuration will not be applied. Continue?')) { + this.showOptions = !this.showOptions; + } + } else { + this.showOptions = !this.showOptions; + } + + } + + + + sortConfigs() { + this.substanceService.getConfigs(this.entity).subscribe(response => { + this.options = response; + this.privateOptions = response.sort((a, b) => { + + let test = -1; + if (a.exporterKey === 'PUBLIC_DATA_ONLY') { + test = -1; + + } + else if (b.exporterKey === 'PUBLIC_DATA_ONLY') { + test = 1; + }else { + test = 1; + } + return test; + }); + let found = false; + this.privateOptions.forEach(conf => { + if (conf.exporterKey === 'PUBLIC_DATA_ONLY') { + found = true; + this.switchConfig(conf, true); + this.loadedConfig = conf; + } + }); + if (!found) { + this.privateOptions.forEach(conf => { + if (conf.exporterKey === 'ALL_DATA') { + found = true; + this.switchConfig(conf, true); + this.loadedConfig = conf; + } + }) + } + }); + } + + saveConfig() { + this.message = null; + let found = false; + const test = {"exporterKey":this.configName, + "scrubberSettings": this.privateScrubberModel, + "expanderSettings": this.privateExpanderModel, + "exporterSettings": this.privateExporterModel}; + this.privateOptions.forEach(conf => { + if (conf.exporterKey === this.configName) { + found = true; + } + }); + if (!found){ + this.substanceService.storeNewConfig(test, this.entity).subscribe(response => { + if (response.Result) { + this.message = response.Result; + } + this.options.push(test); + this.loadedConfig = test; + if (response['Newly created configuration']) { + this.message = 'Newly created configuration: ' + response['Newly created configuration']; + this.loadedConfig.configurationId = response['Newly created configuration']; + this.unsaved = false; + } + }); + } else { + alert('Cannot Save: config name "' + this.configName + "' already exists"); + } + } + + updateConfig() { + this.message = null; + + this.loadedConfig.scrubberSettings = this.privateScrubberModel; + this.loadedConfig.expanderSettings = this.privateExpanderModel; + this.loadedConfig.exporterSettings = this.privateExporterModel; + this.substanceService.updateConfig(this.loadedConfig.configurationId, this.loadedConfig, this.entity).subscribe(response => { + if (response.Result) { + this.message = response.Result; + this.unsaved = false; + } + }) + } + + deleteConfig(id?: string) { + this.message = null; + + if(!id) { + id = this.loadedConfig.configurationId; + } + if (confirm("Are you sure you want to delete this configuration?")) { + + this.substanceService.deleteConfig(id, this.entity).subscribe(response => { + this.substanceService.getConfigs(this.entity).subscribe(response2 => { + this.options = response2; + }); + this.loadedConfig = null; + if (response.Result) { + this.message = response.Result; + } + }); + } + } + + unsavedChangeCheck(config: any, model: any): boolean { + // check a given saved config to the current model used by a form, return true if unsaved changes detected + // we need to treat properties that are undefined, null, false, or empty arrays as the same value for the sake of detecting differences + + let result = false; + if (!_.isEqual(config, model)) { + if ((!config || Object.keys(config).length === 0) && + (!model || Object.keys(model).length === 0)) { + } else { + let check1 = (config)?JSON.parse(JSON.stringify(config)):{}; + let check2 = (model)?JSON.parse(JSON.stringify(model)):{}; + + Object.keys(check1).forEach(key => { + if (!check1[key] || check1[key].length === 0) { + delete check1[key]; + } + }); + Object.keys(check2).forEach(key => { + if (!check2[key] || check2[key].length === 0) { + delete check2[key]; + } + }); + if (!_.isEqual(check1, check2)) { + result = true; + } + } + } + return result; + } + + unsavedChanges(): boolean { + if ( + this.unsavedChangeCheck(this.loadedConfig.scrubberSettings, this.privateScrubberModel) || + this.unsavedChangeCheck(this.loadedConfig.exporterSettings, this.privateExporterModel) || + this.unsavedChangeCheck(this.loadedConfig.expanderSettings, this.privateExpanderModel) + ) { + return true; + } else { + return false; + } + + } + + undo(): void { + this.message = null; + this.switchConfig(this.loadedConfig); + this.message = "Reloaded saved settings for configuration '" +this.loadedConfig.exporterKey + "'"; + } + + switchConfig(event: any, preload?: boolean) { + this.message = ""; + let testing = {}; + this.unsaved = false; + this.privateOptions.forEach(opt =>{ + if (opt.configurationId === event.configurationId) { + + // added check if the settings are truthy. Sometimes they may be null or undefined. If ther are, set to empty object + // to avoid null/undefined reference calls + this.expanderModel = (opt.expanderSettings)?JSON.parse(JSON.stringify(opt.expanderSettings)):{}; + this.scrubberModel = (opt.scrubberSettings)?JSON.parse(JSON.stringify(opt.scrubberSettings)):{}; + this.exporterModel = (opt.exporterSettings)?JSON.parse(JSON.stringify(opt.exporterSettings)):{}; + this.privateExpanderModel = (opt.expanderSettings)?JSON.parse(JSON.stringify(opt.expanderSettings)):{}; + this.privateScrubberModel = (opt.scrubberSettings)?JSON.parse(JSON.stringify(opt.scrubberSettings)):{}; + this.privateExporterModel = (opt.exporterSettings)?JSON.parse(JSON.stringify(opt.exporterSettings)):{}; + + testing = opt.scrubberSettings; + this.temp = opt.scrubberSettings; + /* Object.keys(opt.scrubberSettings).forEach(val => { + this.scrubberModel[val] = opt.scrubberSettings[val]; + });*/ + } + }) + this.configName = event.exporterKey; + setTimeout(()=> { + this.scrubberModel = testing; + }, 100); + if (!preload) { + this.message = "Export Configuration " + this.configName + " Loaded"; + } + } } diff --git a/src/app/core/substances-browse/list-create-dialog/list-create-dialog.component.html b/src/app/core/substances-browse/list-create-dialog/list-create-dialog.component.html new file mode 100644 index 000000000..47095d00f --- /dev/null +++ b/src/app/core/substances-browse/list-create-dialog/list-create-dialog.component.html @@ -0,0 +1,15 @@ +
    +

    Create List from {{record._name}}

    +
    + +
    +
    +     + +
    +{{message}} +
    +
    + + +
    \ No newline at end of file diff --git a/src/app/core/substances-browse/list-create-dialog/list-create-dialog.component.scss b/src/app/core/substances-browse/list-create-dialog/list-create-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/core/substances-browse/list-create-dialog/list-create-dialog.component.spec.ts b/src/app/core/substances-browse/list-create-dialog/list-create-dialog.component.spec.ts new file mode 100644 index 000000000..f99c15e5d --- /dev/null +++ b/src/app/core/substances-browse/list-create-dialog/list-create-dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ListCreateDialogComponent } from './list-create-dialog.component'; + +describe('ListCreateDialogComponent', () => { + let component: ListCreateDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ListCreateDialogComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ListCreateDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/substances-browse/list-create-dialog/list-create-dialog.component.ts b/src/app/core/substances-browse/list-create-dialog/list-create-dialog.component.ts new file mode 100644 index 000000000..410cb7d64 --- /dev/null +++ b/src/app/core/substances-browse/list-create-dialog/list-create-dialog.component.ts @@ -0,0 +1,40 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { BulkSearchService } from '@gsrs-core/bulk-search/service/bulk-search.service'; + +@Component({ + selector: 'app-list-create-dialog', + templateUrl: './list-create-dialog.component.html', + styleUrls: ['./list-create-dialog.component.scss'] +}) +export class ListCreateDialogComponent implements OnInit { + message: string; + newList = false; + listName: string; + record: any; + constructor( @Inject(MAT_DIALOG_DATA) public data: any, + private bulkSearchService: BulkSearchService +) { + this.message = data.message; + if (data.newList) { + this.newList = true; + this.record = data.record; + } +} + + ngOnInit(): void { + } + + addList(): void { + this.bulkSearchService.saveBulkSearch(this.record.uuid, this.listName).subscribe(response => { + this.newList = false; + this.message = "List succesfully created from selected record"; + this.bulkSearchService.getBulkSearchLists().subscribe(response2 => { + this.bulkSearchService.listEmitter.next(response2.lists); + }); + }, error => { + console.log(error); + }); + } + +} diff --git a/src/app/core/substances-browse/sequence-alignment/sequence-alignment.component.scss b/src/app/core/substances-browse/sequence-alignment/sequence-alignment.component.scss index e19930641..5db1e86a3 100644 --- a/src/app/core/substances-browse/sequence-alignment/sequence-alignment.component.scss +++ b/src/app/core/substances-browse/sequence-alignment/sequence-alignment.component.scss @@ -2,7 +2,7 @@ overflow-x: auto; overflow-y: auto; border-radius: 4px; - border: 1px solid rgba(0,0,0,0.09); + border: 1px solid var(--sub-hierarchy-odd-bg-color); margin-top: 8px; padding: 5px; padding-bottom: 10px; @@ -18,7 +18,7 @@ padding-left: 10px; font-family: Menlo,Monaco,Consolas,"Courier New",monospace; - color: #c7254e; + color: var(--pink-span-color); } diff --git a/src/app/core/substances-browse/sub-browse-emitter.service.ts b/src/app/core/substances-browse/sub-browse-emitter.service.ts new file mode 100644 index 000000000..4ba21b202 --- /dev/null +++ b/src/app/core/substances-browse/sub-browse-emitter.service.ts @@ -0,0 +1,21 @@ +import { EventEmitter, Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class SubBrowseEmitterService { + + public refresh: EventEmitter = new EventEmitter(); + public cancel: EventEmitter = new EventEmitter(); + + public setRefresh(value) { + console.log('setting refresh as ', value); + this.refresh.emit(value) + } + + public setCancel(value) { + console.log('setting cancel as ', value); + this.cancel.emit(value) + } + +} \ No newline at end of file diff --git a/src/app/core/substances-browse/substance-hierarchy/substance-hierarchy.component.html b/src/app/core/substances-browse/substance-hierarchy/substance-hierarchy.component.html index 2c77a91ea..bcfe54e31 100644 --- a/src/app/core/substances-browse/substance-hierarchy/substance-hierarchy.component.html +++ b/src/app/core/substances-browse/substance-hierarchy/substance-hierarchy.component.html @@ -1,17 +1,25 @@
    -
    Substance Hierarchy
    - +
    Substance Hierarchy
    +
    + +
    +
    Loading
    +
    +
  • - {{node.value.refPname}} - + [routerLink]="['/substances', node.value.refuuid || '']" attr.aria-label = "view substance for {{node.value.refPname}}" [innerHTML] = "node.value.refPname"> + + target = "_blank" matTooltip = "edit substance in new tab" attr.aria-label="edit substance for {{node.value.refPname}} in new tab">
    @@ -38,10 +46,9 @@
    diff --git a/src/app/core/substances-browse/substance-hierarchy/substance-hierarchy.component.scss b/src/app/core/substances-browse/substance-hierarchy/substance-hierarchy.component.scss index 28cd8ae47..d565a5f8e 100644 --- a/src/app/core/substances-browse/substance-hierarchy/substance-hierarchy.component.scss +++ b/src/app/core/substances-browse/substance-hierarchy/substance-hierarchy.component.scss @@ -16,7 +16,7 @@ width: 50%; } .approval { - color: maroon; + color: var(--regular-maroon-color); flex: 0 0 100%; } @@ -31,6 +31,22 @@ } +.spinner { + height: 24px; + width: 24px; + margin-left: 10px; + margin-right: 10px; + margin-top: -5px; +margin-bottom: 5px; +} + +.loading-container { + display: flex; + width: 300px; + flex-direction: row; + align-content: flex-start; +} + .hierarchy-link { white-space: nowrap; flex-wrap: nowrap; @@ -85,16 +101,16 @@ border-top:0px; } .primary { - background-color: #cee8fb !important; + background-color: var(--sub-hierarchy-bg-color) !important; } .hierarchy-link { - color: #448aff; + color: var(--link-primary-color); font-style: unset; max-lines: 2; } .odd { - background-color : rgba(0, 0, 0, .05); + background-color: var(--sub-hierarchy-odd-bg-color); } .node-container { display:flex; diff --git a/src/app/core/substances-browse/substance-hierarchy/substance-hierarchy.component.ts b/src/app/core/substances-browse/substance-hierarchy/substance-hierarchy.component.ts index 19c3f7010..ce0a1b08a 100644 --- a/src/app/core/substances-browse/substance-hierarchy/substance-hierarchy.component.ts +++ b/src/app/core/substances-browse/substance-hierarchy/substance-hierarchy.component.ts @@ -20,6 +20,7 @@ export class SubstanceHierarchyComponent implements OnInit { selfNode: HierarchyNode; activeNode: any; isAdmin: boolean; + loading = true; hasChild = (_: number, node: any) => !!node.children && node.children.length > 0; constructor( private substanceService: SubstanceService, @@ -44,11 +45,12 @@ export class SubstanceHierarchyComponent implements OnInit { }, error => { this.loadHierarchy([this.selfNode]); }); + this.isAdmin = this.authService.hasAnyRoles('Admin', 'Updater', 'SuperUpdater'); } loadHierarchy(orig: any): void { - + this.loading = false; if (orig.length === 0) { orig.push(this.selfNode); } diff --git a/src/app/core/substances-browse/substance-summary-card/show-molfile-dialog/show-molfile-dialog.component.html b/src/app/core/substances-browse/substance-summary-card/show-molfile-dialog/show-molfile-dialog.component.html new file mode 100644 index 000000000..3f5c5d859 --- /dev/null +++ b/src/app/core/substances-browse/substance-summary-card/show-molfile-dialog/show-molfile-dialog.component.html @@ -0,0 +1,12 @@ +

    View Molfile

    + +
    + + + + +
    +
    + + +
    \ No newline at end of file diff --git a/src/app/core/substances-browse/substance-summary-card/show-molfile-dialog/show-molfile-dialog.component.scss b/src/app/core/substances-browse/substance-summary-card/show-molfile-dialog/show-molfile-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/core/substances-browse/substance-summary-card/show-molfile-dialog/show-molfile-dialog.component.spec.ts b/src/app/core/substances-browse/substance-summary-card/show-molfile-dialog/show-molfile-dialog.component.spec.ts new file mode 100644 index 000000000..b0ca5678b --- /dev/null +++ b/src/app/core/substances-browse/substance-summary-card/show-molfile-dialog/show-molfile-dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ShowMolfileDialogComponent } from './show-molfile-dialog.component'; + +describe('ShowMolfileDialogComponent', () => { + let component: ShowMolfileDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ShowMolfileDialogComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ShowMolfileDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/substances-browse/substance-summary-card/show-molfile-dialog/show-molfile-dialog.component.ts b/src/app/core/substances-browse/substance-summary-card/show-molfile-dialog/show-molfile-dialog.component.ts new file mode 100644 index 000000000..92bdc5857 --- /dev/null +++ b/src/app/core/substances-browse/substance-summary-card/show-molfile-dialog/show-molfile-dialog.component.ts @@ -0,0 +1,30 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { SubstanceHistoryDialogComponent } from '@gsrs-core/substance-history-dialog/substance-history-dialog.component'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { StructureService } from '@gsrs-core/structure'; + +@Component({ + selector: 'app-show-molfile-dialog', + templateUrl: './show-molfile-dialog.component.html', + styleUrls: ['./show-molfile-dialog.component.scss'] +}) +export class ShowMolfileDialogComponent implements OnInit { +molfile: string; + constructor( + public dialogRef: MatDialogRef, + private structureService: StructureService, + @Inject(MAT_DIALOG_DATA) public data: any + + ) { + } + + ngOnInit() { + + this.structureService.getMolfile(this.data.uuid).subscribe( response => { + this.molfile = response; + + + }); +} + +} diff --git a/src/app/core/substances-browse/substance-summary-card/substance-summary-card.component.html b/src/app/core/substances-browse/substance-summary-card/substance-summary-card.component.html index 0d692af2e..d828d6cac 100644 --- a/src/app/core/substances-browse/substance-summary-card/substance-summary-card.component.html +++ b/src/app/core/substances-browse/substance-summary-card/substance-summary-card.component.html @@ -1,14 +1,40 @@ - - + + + -
    - {{substance.approvalID}} -
    +
    + +
    + + Add to List + + + {{list}} + + + + + + + + + + + + +
    +
    + {{substance.approvalID}} +
    +
    + + +
    @@ -24,59 +50,94 @@
    - {{substance.substanceClass | facetDisplay: 'types' | uppercase}} + {{substance.substanceClass | facetDisplay: 'types' | uppercase}} +
    -
    - similarity: {{substance._matchContext.similarity.toFixed(3)}} +
    + similarity: {{substance._matchContext.similarity.toFixed(3)}}
    -
    -
    +
    +
    Names:
    +
    +
    Loading
    +
    +
    Names:
    -
    -
    - {{nameObject.name}} - +
    +
    + +
    +
    See {{names.length - 4}} More
    +
    +
    +
    + + + + + + + +
    +
    Less
    Codes:
    -
    -
    +
    +
    +
    + {{(codeSystemVocab && codeSystemVocab[codeSystemName]) ? codeSystemVocab[codeSystemName].display : codeSystemName}} + + :  + + + + + {{codeObject.code.trim()}} + + [{{codeObject.type}}] + + + + {{codeObject.code.trim()}} + [{{codeObject.type}}] + + + , + + + + {{showAll[codeSystemName] ? 'Show Less': 'Show More'}} + +
    +
    +
    See {{codeSystemNames.length - 5}} More
    +
    +
    +
    - {{codeSystemName | codeSystemDisplay | async}}:  - + {{codeSystemName | codeSystemDisplay | async}} + + :  + + {{codeObject.code.trim()}} + [{{codeObject.type}}] + - {{codeObject.code.trim()}} + {{codeObject.code.trim()}} + [{{codeObject.type}}] + + , + + + + {{showAll[codeSystemName] ? 'Show Less': 'Show More'}}
    +
    Less
    -
    -
    Subunits:
    +
    +
    Components:
    - {{substance.protein.subunits.length}} + {{substance.mixture.components.length}}
    -
    -
    Subunits:
    -
    - {{substance.nucleicAcid.subunits.length}} -
    +
    +
    Constituents:
    +
    + {{substance.specifiedSubstance.constituents.length}} +
    +
    +
    +
    Subunits:
    +
    + {{substance.nucleicAcid.subunits.length}}
    +
    Mol. Weight:
    - {{substance.structure.mwt}} + {{substance.structure.mwt | number: rounding}}
    Formula:
    - +
    @@ -189,14 +338,15 @@
    - +
    - -
    + +
    Created:
    @@ -217,11 +367,11 @@
    Status:
    - {{substance.status === 'approved' ? 'Validated (UNII)' : 'pending'}} + {{substance.status | statusDisplay}}
    -
    +
    Validated By:
    @@ -229,7 +379,7 @@
    -
    +
    Validated Date:
    @@ -264,11 +414,18 @@
    -
    - +
    + + +
    +
    + + (matched queries: {{_.join(substance._matchContext.queries, ", ")}}) +
    - +
    diff --git a/src/app/core/substances-browse/substance-summary-card/substance-summary-card.component.scss b/src/app/core/substances-browse/substance-summary-card/substance-summary-card.component.scss index f68cd5295..0541ec1f1 100644 --- a/src/app/core/substances-browse/substance-summary-card/substance-summary-card.component.scss +++ b/src/app/core/substances-browse/substance-summary-card/substance-summary-card.component.scss @@ -8,6 +8,36 @@ box-sizing: border-box; } +.button-wrapper { + max-width: 170px; +} + +.loading-label { + font-weight: bold; +min-width: 100px; +} + +.spinner { + height: 24px; + width: 24px; + margin-left: 10px; + margin-right: 10px; + margin-top: -5px; +margin-bottom: 5px; +} + + + +.name-loading { + display: flex; + width: 300px; +} +.show-more { + text-decoration: underline; + color: var(--link-primary-color); + cursor: pointer; +} + .mat-card-title { display: flex; box-sizing: border-box; @@ -17,17 +47,24 @@ white-space: nowrap; } +.moreLink { + color: var(--link-primary-color); + font-weight: 600; + padding-left: 10px; + font-size: 15px; +} + .mat-card-title { white-space: normal; .substance-name { - color: #448aff; + color: var(--link-primary-color); padding-right: 10px; } .approval { font-size: 16px; - color: #c7254e; + color: var(--pink-span-color); } } @@ -90,7 +127,7 @@ } .ext-link{ - color: #448aff; + color: var(--link-primary-color); text-decoration-style: unset; } @@ -118,7 +155,7 @@ } .lock-icon { - color: #f0ad4e; + color: var(--lock-icon-color); } @media(max-width: 600px) { @@ -139,7 +176,7 @@ .similarity { padding-left: 10px; font-family: Menlo,Monaco,Consolas,"Courier New",monospace; - color: #c7254e; + color: var(--pink-span-color); } .similarity-label { @@ -156,4 +193,12 @@ width: 20px; padding-bottom: 5px; vertical-align: middle; -} \ No newline at end of file +} + +.match-context { + display: block; + font-size: small; + color: var(--regular-green-color); + margin-top: 8px; + margin-bottom: 8px; +} diff --git a/src/app/core/substances-browse/substance-summary-card/substance-summary-card.component.ts b/src/app/core/substances-browse/substance-summary-card/substance-summary-card.component.ts index eb3f7701f..10946e141 100644 --- a/src/app/core/substances-browse/substance-summary-card/substance-summary-card.component.ts +++ b/src/app/core/substances-browse/substance-summary-card/substance-summary-card.component.ts @@ -19,6 +19,14 @@ import { SubstanceSummaryDynamicContent } from './substance-summary-dynamic-cont import {Router} from '@angular/router'; import {Alignment} from '@gsrs-core/utils'; import { take } from 'rxjs/operators'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { MatDialog } from '@angular/material/dialog'; +import { ShowMolfileDialogComponent } from '@gsrs-core/substances-browse/substance-summary-card/show-molfile-dialog/show-molfile-dialog.component'; +import { ConfigService } from '@gsrs-core/config'; +import { Vocabulary } from '@gsrs-core/controlled-vocabulary'; +import * as lodash from 'lodash'; +import { BulkSearchService } from '@gsrs-core/bulk-search/service/bulk-search.service'; +import { ListCreateDialogComponent } from '@gsrs-core/substances-browse/list-create-dialog/list-create-dialog.component'; @Component({ selector: 'app-substance-summary-card', @@ -29,15 +37,32 @@ export class SubstanceSummaryCardComponent implements OnInit { private privateSubstance: SubstanceSummary; @Output() openImage = new EventEmitter(); @Input() showAudit: boolean; - isAdmin = false; + isAdmin = false; //this shouldn't be called "isAdmin", it's typically used to mean "canUpdate". Should fix for future devs. + canCreate = false; //meant to allow creating new records subunits?: Array; @ViewChild(CardDynamicSectionDirective, {static: true}) dynamicContentContainer: CardDynamicSectionDirective; - @Input() names?: Array; @Input() codeSystemNames?: Array; - @Input() codeSystems?: { [codeSystem: string]: Array }; + @Input() codeSystemVocab?: Vocabulary; + @Input() searchStrategy?: string = ''; + @Input() userLists?: Array; + +// @Input() codeSystems?: { [codeSystem: string]: Array }; alignments?: Array; inxightLink = false; inxightUrl: string; + overlayContainer: any; + rounding = '1.0-2'; + showAll = []; + privateCodeSystems?: { [codeSystem: string]: Array }; + privateCodeSystemNames?: Array; + allPrimary = []; + showLessNames = true; + showLessCodes = true; + privateNames?: Array; + nameLoading = true; + selectedList: string; + _ = lodash; + constructor( public utilsService: UtilsService, public gaService: GoogleAnalyticsService, @@ -46,15 +71,27 @@ export class SubstanceSummaryCardComponent implements OnInit { private structureService: StructureService, private componentFactoryResolver: ComponentFactoryResolver, private router: Router, + private overlayContainerService: OverlayContainer, + private dialog: MatDialog, + private configService: ConfigService, + private bulkSearchService: BulkSearchService, @Inject(DYNAMIC_COMPONENT_MANIFESTS) private dynamicContentItems: DynamicComponentManifest[] ) { } ngOnInit() { - this.authService.hasAnyRolesAsync('Updater', 'SuperUpdater').pipe(take(1)).subscribe(response => { + this.overlayContainer = this.overlayContainerService.getContainerElement(); + console.log(this.userLists); + + this.authService.hasAnyRolesAsync('Updater', 'SuperUpdater', 'Approver', 'admin').pipe(take(1)).subscribe(response => { if (response) { this.isAdmin = response; } }); + this.authService.hasAnyRolesAsync('DataEntry', 'SuperDataEntry', 'admin').pipe(take(1)).subscribe(response => { + if (response) { + this.canCreate = response; + } + }); if (this.substance.protein) { this.subunits = this.substance.protein.subunits; this.getAlignments(); @@ -77,8 +114,40 @@ export class SubstanceSummaryCardComponent implements OnInit { } else { this.getApprovalID(); } + + if (this.configService.configData && this.configService.configData.molWeightRounding) { + this.rounding = '1.0-' + this.configService.configData.molWeightRounding; + } } + @Input() + + set names(name: any) { + if (typeof(name) != "undefined") { + this.privateNames = name; + this.nameLoading = false; + } + + } + + get names(): any { + return this.privateNames; + } + + +openMessageDialog(message: string) { + const dialogRef = this.dialog.open(ListCreateDialogComponent, { + minWidth: '25%', + maxWidth: '50%', + data: {"message": message} + }); + this.overlayContainer.style.zIndex = '1002'; + + dialogRef.afterClosed().subscribe(result => { + this.overlayContainer.style.zIndex = null; + }); +} + getApprovalID() { if (!this.substance.approvalID) { if (this.substance._approvalIDDisplay && @@ -102,7 +171,37 @@ export class SubstanceSummaryCardComponent implements OnInit { return this.privateSubstance; } + @Input() + set codeSystems(codeSystems: any) { + if (codeSystems && this.codeSystemNames) { + this.privateCodeSystems = codeSystems; + this.formatCodeSystems(); + } + } + + get codeSystems(): any { + return this.privateCodeSystems; + } + + formatCodeSystems() { + // sort() function in substance-browse isn't working... pushing this as alternative to get all primary codes first + this.codeSystemNames.forEach(sysName => { + const testing = []; + this.allPrimary[sysName] = 'true'; + this.codeSystems[sysName].forEach(code => { + if (code.type === 'PRIMARY') { + testing.unshift(code); + } else { + this.allPrimary[sysName] = 'false'; + testing.push(code); + } + }); + this.codeSystems[sysName] = testing; + }); + } + openImageModal(): void { + this.substance.names = this.privateNames; this.openImage.emit(this.substance); } @@ -135,12 +234,20 @@ export class SubstanceSummaryCardComponent implements OnInit { loadDynamicContent(): void { const viewContainerRef = this.dynamicContentContainer.viewContainerRef; viewContainerRef.clear(); - const dynamicContentItemsFlat = this.dynamicContentItems.reduce((acc, val) => acc.concat(val), []) - .filter(item => item.componentType === 'summary'); - dynamicContentItemsFlat.forEach(dynamicContentItem => { - const componentFactory = this.componentFactoryResolver.resolveComponentFactory(dynamicContentItem.component); - const componentRef = viewContainerRef.createComponent(componentFactory); - (componentRef.instance).substance = this.privateSubstance; + if (this.configService.configData && this.configService.configData.loadedComponents){ + const dynamicContentItemsFlat = this.dynamicContentItems.reduce((acc, val) => acc.concat(val), []) + .filter(item => item.componentType === 'summary'); + dynamicContentItemsFlat.forEach(dynamicContentItem => { + const componentFactory = this.componentFactoryResolver.resolveComponentFactory(dynamicContentItem.component); + const componentRef = viewContainerRef.createComponent(componentFactory); + (componentRef.instance).substance = this.privateSubstance; + }); + } + } + + downloadJson() { + this.substanceService.getSubstanceDetails(this.substance.uuid).pipe(take(1)).subscribe(response => { + this.downloadFile(JSON.stringify(response), this.substance.uuid + '.json'); }); } @@ -158,4 +265,87 @@ export class SubstanceSummaryCardComponent implements OnInit { } } } + + openMolModal() { + + const dialogRef = this.dialog.open(ShowMolfileDialogComponent, { + minWidth: '40%', + maxWidth: '90%', + height: '90%', + data: {uuid: this.substance.uuid, approval: this.substance.approvalID} + }); + this.overlayContainer.style.zIndex = '1002'; + + dialogRef.afterClosed().subscribe(result => { + this.overlayContainer.style.zIndex = null; + }); + } + + moreThanNumberCount(names, number) { + if(names.length < number) { + return false; + } else { + return true; + } + } + + showMoreLessNames() { + this.showLessNames = !this.showLessNames; + } + + showMoreLessCodes() { + this.showLessCodes = !this.showLessCodes; + } + + createList() { + let send = { + 'record': this.privateSubstance, + 'newList': true + } + + const dialogRef = this.dialog.open(ListCreateDialogComponent, { + minWidth: '25%', + maxWidth: '50%', + data: send + }); + this.overlayContainer.style.zIndex = '1002'; + + dialogRef.afterClosed().subscribe(result => { + this.overlayContainer.style.zIndex = null; + }); + } + + addToList() { + let exists = false; + let toPut = ""; + this.bulkSearchService.getSingleBulkSearchList(this.selectedList).subscribe(record => { + console.log(record); + record.lists.forEach(entry =>{ + if (entry.key === this.privateSubstance.uuid) { + exists = true; + this.openMessageDialog('Cannot add: Already in list'); + } else { + // toPut += entry.key + ", "; + } + }); + if (!exists) { + toPut += this.privateSubstance.uuid; + this.addCall(toPut); + } + }) + + } + + addCall(list: string) { + this.bulkSearchService.editKeysBulkSearchLists(this.selectedList, list, 'add').subscribe(response => { + this.openMessageDialog('Record successfully added'); + + }, error => { + console.log(error); + this.openMessageDialog('Failed to Add to List: error in console'); + + }) + } + + } diff --git a/src/app/core/substances-browse/substances-browse.component.html b/src/app/core/substances-browse/substances-browse.component.html index 941fbdc7f..fc91aa6dd 100644 --- a/src/app/core/substances-browse/substances-browse.component.html +++ b/src/app/core/substances-browse/substances-browse.component.html @@ -1,36 +1,165 @@ - + + Facet View: + + + {{option}} + + + + [rawFacets]="rawFacets" [facetViewCategorySelected]="facetViewCategorySelected" [facetDisplayType]="facetDisplayType" + configName="substances" includeFacetSearch="true"> - -
    +
    +
    + Your search did not return any results. Please try modifying it or + click here to clear all your search criteria. +
    +
    + Your search did not return any results within the default fields. However, this query has some results if you restrict your search to specific fields below. +
    +
    +
    +
    + Search Query:  + {{this.searchTerm}} +
    +
    + + +
    +
    +
    +
    +
    + {{structureSearchTerm && searchType}} Query: +   + + + + + +  ≥ {{searchCutoff}} + + + + + + + +
    + + +
    +
    + +
    + +
    +
    + Sequence Query: +   + + {{getSequenceDisplay(sequenceSearchTerm)}} + +
    +
    + + +
    +
    + +
    + + +
    + +
    + + + +
    Bulk Search Summary (Full Results)
    +
    +
    +
    +
    +
    + +
    + Clear + + | Edit + + Bulk Search +
    + +
    +
    +
    + +
    +
    -
    +
    +
    - Would you like to restrict the search for this to field? + Would you like to restrict this search to a field?
      - - -
    -
    - {{matchType == 'WORD' ? 'Contains Match' : 'Exact Match'}} +
    +
    +
    Other Match
    +
    Exact Match
    +
    Contains Match
    +
    Contains Match
    +
    Contains Match
    +
    No Match
    +
    Unknown Match
    +
    +
    +
    + For more options use the Advanced Search +
    +
    @@ -52,15 +187,18 @@ Search Query:  {{this.searchTerm}}
    + +
    - - +
    +
    @@ -81,25 +219,39 @@ -
    -
    +
    +
    {{structureSearchTerm && searchType}} Query:   - {{this.smiles}} + +  ≥ {{searchCutoff}} -
    -
    - + + + +
    +
    @@ -118,20 +270,44 @@
    + + +
    + + + +
    + +
    +
    +

    Results below are an incomplete preview

    +
    +
    + +
    +

    searching... {{totalSubstances}} matches

    + + +
    +

    Page will auto-reload when search is complete

    +
    +
    - +
    + Browse Substances +
    - + - + - - + + @@ -139,7 +315,7 @@ Sort By - + {{option.display}} @@ -149,48 +325,62 @@ {{option.displayname}} - - Include Private Data -
    - +
    -
    - - - -
    - Page: - - - - of {{lastPage}} -
    +
    +
    +
    + + + + + +
    + + + + +
    + Page: + + + + of {{lastPage}}
    -
    - - +
    + + +
    @@ -199,7 +389,7 @@
  • Name
    - {{nameObject.name}} + ;
    @@ -247,7 +437,6 @@
    Actions @@ -267,6 +456,10 @@ + + Download JSON + + Download Molfile @@ -279,6 +472,7 @@ (click)="getFasta(substance.uuid, (substance.approvalID? substance.approvalID.toString() : substance.uuid.toString())+'.fas')"> Download Fasta + @@ -351,39 +545,47 @@ [atomMaps]="substance._matchContext && substance._matchContext.atomMaps || null"> +
    + similarity: {{substance._matchContext.similarity.toFixed(3)}} +
    - + + - + - + +
    - + + + + Download JSON + + Download Molfile @@ -397,7 +599,7 @@ Download Fasta - + @@ -408,7 +610,7 @@ Search Structure - + -
    +
    +
    -
    -
    - Your search did not return any results. Please try modifying it or - click here to clear all your search criteria. -
    -
    -
    - Search Query:  - {{this.searchTerm}} -
    -
    - - -
    -
    -
    -
    - {{structureSearchTerm && searchType}} Query: -   - - {{this.smiles}} - -  ≥ {{searchCutoff}} - - -
    -
    - - -
    -
    -
    -
    - Sequence Query: -   - - {{getSequenceDisplay(sequenceSearchTerm)}} - -
    -
    - - -
    +
    + Page: + + + + of {{lastPage}}
    +
    +
    - There is one exact (name or code) match for {{this.searchTerm}} + There is one exact (name, standardized name or code) match for {{this.searchTerm}}
    -
    \ No newline at end of file +
    diff --git a/src/app/core/substances-browse/substances-browse.component.scss b/src/app/core/substances-browse/substances-browse.component.scss index 68fc588c7..09703e814 100644 --- a/src/app/core/substances-browse/substances-browse.component.scss +++ b/src/app/core/substances-browse/substances-browse.component.scss @@ -7,6 +7,14 @@ min-height:5px; } +::ng-deep .facetView > * { + z-index: 1002 !important; +} + +.smiles-input { + max-width:350px; +} + .mat-expansion-panel-header { padding: 0 12px; } @@ -19,6 +27,23 @@ } } +.wildcard-div { + margin: 10px 30px; + width: 20%; + display: inline-flex; +} + +.wildcard { + width: 100%; + height: 50%; + background: var(--regular-transparent-color); + border-top-style: hidden; + border-right-style: hidden; + border-left-style: hidden; + border-bottom-style: groove; + border-color: var(--pale-border-color-rgb-3); +} + .search-parameters { display: flex; width: 100%; @@ -45,6 +70,7 @@ } .selected-parameter { padding-right:5px; + background-color: var(--regular-white-color); font-size:14px; } } @@ -53,6 +79,12 @@ display: flex; } +.structure-link { + margin-bottom: 5px; +width: 100%; +display: flex; +} + .mat-card { max-width: 928px; margin-bottom: 20px; @@ -128,23 +160,37 @@ white-space: normal; .substance-name { - color: #448aff; + color: var(--link-primary-color); padding-right: 10px; } .approval-id { font-size: 16px; - color: red; + color: var(--regular-red-color); } } .table-view-name { - color: #448aff; + color: var(--link-primary-color); } .tile-title { height:19px; } +::ng-deep .mat-paginator-range-label { + font-weight: 550; +} + +.main-title { + font-size: 26px; + padding-left: 10px; + padding-right: 25px; + font-weight: 550; + padding-top: 10px; + color: var(--primary-color); + width: 285px; +} + .mat-card-content { .mat-chip-list-container { @@ -162,7 +208,7 @@ } .tile { - height:300px; + height:350px; margin:10px; padding:10px; width: calc(25% - 20px); @@ -193,6 +239,12 @@ height:25px; } +.tile-name ::ng-deep { + svg { + margin-bottom: -10px; + } +} + .substance-tiles{ flex-flow: row wrap; } @@ -227,7 +279,7 @@ } .mat-paginator { - background: transparent; + background: var(--regular-transparent-color); } .image-thumbnail{ @@ -285,14 +337,24 @@ } .export-button { - color: black; + color: var(--regular-black-color); border-radius: 4px; } +.similarity { + padding-left: 10px; + font-family: Menlo,Monaco,Consolas,"Courier New",monospace; + color: var(--pink-span-color); +} + +.similarity-label { + font-style: italic; +} + .menu-checkbox:hover { - background-color: white; + background-color: var(--regular-white-color); } -@media(max-width: 1390px) { +@media(max-width: 1750px) { .full-paginator { width: 100%; @@ -376,7 +438,7 @@ } .lock-icon { - color: #f0ad4e; + color: var(--lock-icon-color); } .narrow-search-suggestions-container { @@ -385,8 +447,16 @@ align-items: center; margin-bottom: 10px; justify-content: center; + flex-direction: column; } +.flex-row { + width: 100%; + margin: auto; +} + + + .narrow-search-suggestions { padding: 12px 20px; width: auto; @@ -470,12 +540,13 @@ } .ext-link{ - color: #448aff; + color: var(--link-primary-color); text-decoration-style: unset; } .sort{ margin:auto; + margin-bottom: -7px; } .advanced { @@ -495,6 +566,10 @@ justify-content: space-evenly } +.mat-elevation-z2 { + background-color: var(--regular-white-color); +} + .more-content { width:45%; } @@ -555,7 +630,7 @@ } .page-label { - color:rgba(0, 0, 0, 0.54); + color:var(--dark-label-color); display:block; font-family:Roboto, "Helvetica Neue", sans-serif; font-size:14px; @@ -576,7 +651,7 @@ margin-left: 20px; } mat-paginator { - font-size:14px; + font-size:16px; } .page-input { @@ -585,7 +660,7 @@ margin-left: 20px; } .bad-page { - color: red; + color: var(--regular-red-color); } .full-paginator { @@ -600,5 +675,68 @@ margin-left: 20px; .button-link-img { line-height: 40px; - color: black; + color: var(--regular-black-color); +} +.red-text { + color: var(--regular-red-color); + font-style: italic; +} +.orange-text { + color: var(--regular-orange-color); + font-style: italic; +} +.sub-search-div { + display: none; // remove to see UI added +} +.sub-search-ref-btn { + padding: 10px; + margin: 0 5px; + background-color: var(--regular-white-color); + box-shadow: 2px 2px var(--regular-lightgray-color); + border: 0.4px solid var(--regular-white-color); + border-radius: 5px; +} +.sub-search-cancel-btn { + padding: 10px; + color: var(--regular-white-color); + background-color: var(--regular-red-color); + box-shadow: 2px 2px var(--regular-lightgray-color); + border: 0.4px solid var(--regular-red-color); + border-radius: 5px; +} +.sub-search-text { + text-align: center; + // margin: 7px; +} +.match-txt { + display: inline; + padding: 0 5px; +} +.sub-search-text-div { + text-align: center; +} +.search-spinner { + width: 15px; + height: 15px; + display: inline-table; + margin: auto; +} +// .mat-progress-spinner circle, .mat-spinner circle { +// stroke: grey; +// } +.search-spinner ::ng-deep .mat-progress-spinner circle, .mat-spinner circle { + stroke: var(--regular-grey-color); + width: 10px !important; + height: 10px !important; + display: inline; +} +.cdk-overlay-backdrop, .cdk-global-overlay-wrapper { + display: none !important; +} + + +.smiles-structure-result { + display:flex; + flex-direction:column; + max-width: 650px; } \ No newline at end of file diff --git a/src/app/core/substances-browse/substances-browse.component.ts b/src/app/core/substances-browse/substances-browse.component.ts index 22c4842f3..2a220fbad 100644 --- a/src/app/core/substances-browse/substances-browse.component.ts +++ b/src/app/core/substances-browse/substances-browse.component.ts @@ -11,15 +11,16 @@ import { ViewChildren, QueryList } from '@angular/core'; -import { ActivatedRoute, Router, NavigationExtras } from '@angular/router'; +import { ActivatedRoute, Router, NavigationExtras, Params } from '@angular/router'; import { SubstanceService } from '../substance/substance.service'; -import { SubstanceDetail, SubstanceName, SubstanceCode } from '../substance/substance.model'; +import { SubstanceDetail, SubstanceName, SubstanceCode, SubstanceSummary } from '../substance/substance.model'; import { ConfigService } from '../config/config.service'; import * as _ from 'lodash'; import { LoadingService } from '../loading/loading.service'; import { MainNotificationService } from '../main-notification/main-notification.service'; import { AppNotification, NotificationType } from '../main-notification/notification.model'; -import { MatDialog, PageEvent } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; +import { PageEvent } from '@angular/material/paginator'; import { UtilsService } from '../utils/utils.service'; import { MatSidenav } from '@angular/material/sidenav'; import { StructureImageModalComponent } from '../structure/structure-image-modal/structure-image-modal.component'; @@ -32,20 +33,25 @@ import { Location } from '@angular/common'; import { StructureService } from '@gsrs-core/structure'; import { Subscription } from 'rxjs'; import { take } from 'rxjs/operators'; -import { NarrowSearchSuggestion } from '@gsrs-core/utils'; - +import { NarrowSearchSuggestion, PagingResponse } from '@gsrs-core/utils'; import { FacetParam } from '@gsrs-core/facets-manager'; import { Facet, FacetUpdateEvent } from '../facets-manager/facet.model'; import { FacetsManagerService } from '@gsrs-core/facets-manager'; import { DisplayFacet } from '@gsrs-core/facets-manager/display-facet'; import { SubstanceTextSearchService } from '@gsrs-core/substance-text-search/substance-text-search.service'; import { ExportDialogComponent } from '@gsrs-core/substances-browse/export-dialog/export-dialog.component'; -// tslint:disable-next-line:max-line-length +// eslint-disable-next-line max-len import { BrowseHeaderDynamicSectionDirective } from '@gsrs-core/substances-browse/browse-header-dynamic-section/browse-header-dynamic-section.directive'; import { DYNAMIC_COMPONENT_MANIFESTS, DynamicComponentManifest } from '@gsrs-core/dynamic-component-loader'; import { SubstanceBrowseHeaderDynamicContent } from '@gsrs-core/substances-browse/substance-browse-header-dynamic-content.component'; import { Title } from '@angular/platform-browser'; import { ControlledVocabularyService } from '@gsrs-core/controlled-vocabulary'; +import { FormControl } from '@angular/forms'; +import { SubBrowseEmitterService } from './sub-browse-emitter.service'; +import { WildcardService } from '@gsrs-core/utils/wildcard.service'; +import { I } from '@angular/cdk/keycodes'; +import { BulkSearchService } from '@gsrs-core/bulk-search/service/bulk-search.service'; +import { UserQueryListDialogComponent } from '@gsrs-core/bulk-search/user-query-list-dialog/user-query-list-dialog.component'; @Component({ selector: 'app-substances-browse', @@ -56,12 +62,20 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr private privateSearchTerm?: string; private privateStructureSearchTerm?: string; private privateSequenceSearchTerm?: string; + private privateBulkSearchQueryId?: number; + private privateBulkSearchStatusKey?: string; + private privateBulkSearchSummary?: any; private privateSearchType?: string; + private privateSearchStrategy?: string; private privateSearchCutoff?: number; private privateSearchSeqType?: string; private privateSequenceSearchKey?: string; + public substances: Array; public exactMatchSubstances: Array; + + searchOnIdentifiers: boolean; + searchEntity: string; pageIndex: number; pageSize: number; test: any; @@ -74,6 +88,7 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr privateExport = false; disableExport = false; isError = false; + isRefresher = false; @ViewChildren(BrowseHeaderDynamicSectionDirective) dynamicContentContainer: QueryList; @ViewChild('matSideNavInstance', { static: true }) matSideNav: MatSidenav; hasBackdrop = false; @@ -101,7 +116,9 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr narrowSearchSuggestionsCount = 0; private isComponentInit = false; sequenceID?: string; - + searchHashFromAdvanced: string; + bulkSearchFacet: Facet; + userLists: Array; // needed for facets private privateFacetParams: FacetParam; @@ -115,12 +132,20 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr showDeprecated = false; codeSystem: any; previousState: Array = []; + facetViewCategorySelected = 'Default'; + facetDisplayType = 'facetView'; + facetViewCategory: Array = []; + facetViewControl = new FormControl(); + private wildCardText: string; + bulkSearchPanelOpen = false; + constructor( private activatedRoute: ActivatedRoute, private substanceService: SubstanceService, public configService: ConfigService, + public emitService: SubBrowseEmitterService, private loadingService: LoadingService, private notificationService: MainNotificationService, public utilsService: UtilsService, @@ -136,55 +161,89 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr private substanceTextSearchService: SubstanceTextSearchService, private title: Title, private cvService: ControlledVocabularyService, + private wildCardService: WildcardService, + private bulkSearchService: BulkSearchService, @Inject(DYNAMIC_COMPONENT_MANIFESTS) private dynamicContentItems: DynamicComponentManifest[], - ) { } + ) { + } @HostListener('window:popstate', ['$event']) onPopState(event) { - setTimeout(() => { - if (this.router.url === this.previousState[0]) { - this.ngOnInit(); - } + setTimeout(() => { + if (this.router.url === this.previousState[0]) { + this.ngOnInit(); + } }, 50); } + saveWildCardText() { + this.wildCardService.getWildCardText(this.wildCardText); + } + + wildCardSearch() { + this.wildCardService.getWildCardText(this.wildCardText); + this.setUpPrivateSearchTerm(); + this.searchSubstances(); + } + + + + + fetchBulkLists() { + this.bulkSearchService.getBulkSearchLists().subscribe(result => { + console.log(result); + }, error => { + console.log(error); + }); + + } + ngOnInit() { - this.facetManagerService.registerGetFacetsHandler(this.substanceService.getSubstanceFacets); this.gaService.sendPageView('Browse Substances'); this.cvService.getDomainVocabulary('CODE_SYSTEM').pipe(take(1)).subscribe(response => { this.codeSystem = response['CODE_SYSTEM'].dictionary; + this.bulkSearchService.listEmitter.subscribe(result => { + this.userLists = result; }); + + }); + this.title.setTitle('Browse Substances'); this.pageSize = 10; this.pageIndex = 0; - this.privateSearchTerm = this.activatedRoute.snapshot.queryParams['search'] || ''; - - if (this.privateSearchTerm) { - this.searchTermHash = this.utilsService.hashCode(this.privateSearchTerm); - this.isSearchEditable = localStorage.getItem(this.searchTermHash.toString()) != null; - } + this.setUpPrivateSearchTerm(); this.privateStructureSearchTerm = this.activatedRoute.snapshot.queryParams['structure_search'] || ''; this.privateSequenceSearchTerm = this.activatedRoute.snapshot.queryParams['sequence_search'] || ''; this.privateSequenceSearchKey = this.activatedRoute.snapshot.queryParams['sequence_key'] || ''; + this.privateBulkSearchQueryId = this.activatedRoute.snapshot.queryParams['bulkQID'] || ''; + this.searchOnIdentifiers = (this.activatedRoute.snapshot.queryParams['searchOnIdentifiers']==="true") || false; + this.searchEntity = this.activatedRoute.snapshot.queryParams['searchEntity'] || ''; this.privateSearchType = this.activatedRoute.snapshot.queryParams['type'] || ''; - if ( this.activatedRoute.snapshot.queryParams['sequence_key'] && this.activatedRoute.snapshot.queryParams['sequence_key'].length > 9) { + + this.setUpPrivateSearchStrategy(); + + + if (this.activatedRoute.snapshot.queryParams['sequence_key'] && this.activatedRoute.snapshot.queryParams['sequence_key'].length > 9) { this.sequenceID = this.activatedRoute.snapshot.queryParams['source_id']; this.privateSequenceSearchTerm = JSON.parse(sessionStorage.getItem('gsrs_search_sequence_' + this.sequenceID)); } this.privateSearchCutoff = Number(this.activatedRoute.snapshot.queryParams['cutoff']) || 0; this.privateSearchSeqType = this.activatedRoute.snapshot.queryParams['seq_type'] || ''; this.smiles = this.activatedRoute.snapshot.queryParams['smiles'] || ''; - this.order = this.activatedRoute.snapshot.queryParams['order'] || '$root_lastEdited'; + // the sort order should be set to default (similarity) for structure searches, last edited for all others + this.order = this.activatedRoute.snapshot.queryParams['order'] || + (this.privateStructureSearchTerm && this.privateStructureSearchTerm !== '' ? 'default':'$root_lastEdited'); this.view = this.activatedRoute.snapshot.queryParams['view'] || 'cards'; this.pageSize = parseInt(this.activatedRoute.snapshot.queryParams['pageSize'], null) || 10; const deprecated = this.activatedRoute.snapshot.queryParams['showDeprecated']; + this.searchHashFromAdvanced = this.activatedRoute.snapshot.queryParams['g-search-hash']; if (this.pageSize > 500) { this.pageSize = 500; } @@ -193,6 +252,14 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr const authSubscription = this.authService.getAuth().subscribe(auth => { if (auth) { this.isLoggedIn = true; + this.bulkSearchService.getBulkSearchLists().subscribe( result => { + console.log(result); + this.userLists = result.lists; + + }, error => { + console.log(error); + this.userLists = null; + }) } else { this.showDeprecated = false; } @@ -203,10 +270,45 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr if (deprecated && deprecated === 'true' && this.showAudit) { this.showDeprecated = true; } + this.facetManagerService.registerGetFacetsHandler(this.substanceService.getSubstanceFacets ); this.subscriptions.push(authSubscription); this.isComponentInit = true; this.loadComponent(); + + this.loadFacetViewFromConfig(); + + } + + setUpPrivateSearchTerm() { + this.privateSearchTerm = this.activatedRoute.snapshot.queryParams['search'] || ''; + if(this.wildCardText && this.wildCardText.length > 0) { + if(this.privateSearchTerm.length > 0) { + this.privateSearchTerm += ' AND "' + this.wildCardText + '"'; + } else { + this.privateSearchTerm += '"' + this.wildCardText + '"'; + } + } + if (this.privateSearchTerm) { + this.searchTermHash = this.utilsService.hashCode(this.privateSearchTerm); + this.isSearchEditable = localStorage.getItem(this.searchTermHash.toString()) != null; + } + } + + setUpPrivateSearchStrategy() { + // Setting privateSearchStrategy so we know what + // search strategy is used, for example so we can + // pass a value for use in cards. + // I think privateSearchType is used differently. + // I see searchType being used for 'similarity' + this.privateSearchStrategy = null; + if(this.privateStructureSearchTerm) { + this.privateSearchStrategy = 'structure'; + } else if(this.privateSequenceSearchTerm) { + this.privateSearchStrategy = 'sequence'; + } else if(this.privateBulkSearchQueryId) { + this.privateSearchStrategy = 'bulk'; + } } ngAfterViewInit() { @@ -218,7 +320,7 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr this.utilsService.handleMatSidenavClose(); }); this.subscriptions.push(closeSubscription); - const dynamicSubscription = this.dynamicContentContainer.changes.pipe(take(1)).subscribe((comps: QueryList) => { + const dynamicSubscription = this.dynamicContentContainer.changes.subscribe((comps: QueryList) => { const container = this.dynamicContentContainer.toArray(); const dynamicContentItemsFlat = this.dynamicContentItems.reduce((acc, val) => acc.concat(val), []) .filter(item => item.componentType === 'browseHeader'); @@ -235,7 +337,6 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr }); this.subscriptions.push(dynamicSubscription); - } ngOnDestroy() { @@ -252,12 +353,26 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr private loadComponent(): void { - if (this.isFacetsParamsInit && this.isComponentInit) { + if (this.isFacetsParamsInit && this.isComponentInit || this.isRefresher) { this.searchSubstances(); } else { + + // There should be a better way to do this. + this.bulkSearchPanelOpen = + (this.privateSearchTerm ===undefined || this.privateSearchTerm ==='') + && (this.displayFacets && this.displayFacets.length===0); } } + clipboard(value: string) { + document.addEventListener('copy', (e: ClipboardEvent) => { + e.clipboardData.setData('text/plain', (value)); + e.preventDefault(); + document.removeEventListener('copy', null); + }); + document.execCommand('copy'); + } + changePage(pageEvent: PageEvent) { let eventAction; @@ -319,6 +434,36 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr } } + facetViewChange(event): void { + this.facetViewCategorySelected = event.value; + } + + openedSortSubstances(event: any) { + if (event) { + this.overlayContainer.style.zIndex = '1002'; + } else { + this.overlayContainer.style.zIndex = '1000'; + } + } + + openedFacetViewChange(event: any) { + if (event) { + this.overlayContainer.style.zIndex = '1002'; + } else { + this.overlayContainer.style.zIndex = '1000'; + } + } + + loadFacetViewFromConfig() { + this.facetViewControl.setValue(this.facetViewCategorySelected); + const facetConf = this.configService.configData.facets && this.configService.configData.facets['substances'] || {}; + facetConf['facetView'].forEach(categoryRow => { + const category = categoryRow['category']; + this.facetViewCategory.push(category); + }); + this.facetViewCategory.push('All'); + } + // for facets facetsLoaded(numFacetsLoaded: number) { if (numFacetsLoaded > 0) { @@ -329,11 +474,17 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr } searchSubstances() { + // There should be a better way to do this. + this.bulkSearchPanelOpen = + (this.privateSearchTerm ===undefined || this.privateSearchTerm ==='') + && (this.displayFacets && this.displayFacets.length===0); + this.disableExport = false; const newArgsHash = this.utilsService.hashCode( this.privateSearchTerm, this.privateStructureSearchTerm, this.privateSequenceSearchTerm, + this.privateBulkSearchQueryId, this.privateSearchCutoff, this.privateSearchType, this.privateSearchSeqType, @@ -352,6 +503,9 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr searchTerm: this.privateSearchTerm, structureSearchTerm: this.privateStructureSearchTerm, sequenceSearchTerm: this.privateSequenceSearchTerm, + bulkQID: this.bulkSearchQueryId, + searchOnIdentifiers: this.searchOnIdentifiers, + searchEntity: this.searchEntity, cutoff: this.privateSearchCutoff, type: this.privateSearchType, seqType: this.privateSearchSeqType, @@ -363,6 +517,7 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr deprecated: this.showDeprecated }) .subscribe(pagingResponse => { + this.privateBulkSearchStatusKey = pagingResponse.statusKey; this.isError = false; this.totalSubstances = pagingResponse.total; if (pagingResponse.total % this.pageSize === 0) { @@ -379,19 +534,31 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr this.showExactMatches = true; } + if (pagingResponse.summary && pagingResponse.summary.length > 0 + && pagingResponse.skip === 0 + && (!pagingResponse.sideway || pagingResponse.sideway.length < 2) + ) { + this.privateBulkSearchSummary = pagingResponse.summary; + } + this.substances = pagingResponse.content; this.totalSubstances = pagingResponse.total; + this.etag = pagingResponse.etag; if (pagingResponse.facets && pagingResponse.facets.length > 0) { this.rawFacets = pagingResponse.facets; + } this.narrowSearchSuggestions = {}; this.matchTypes = []; this.narrowSearchSuggestionsCount = 0; + if (pagingResponse.narrowSearchSuggestions && pagingResponse.narrowSearchSuggestions.length) { + pagingResponse.narrowSearchSuggestions.forEach(suggestion => { if (this.codeSystem && this.codeSystem[suggestion.displayField]) { suggestion.displayField = this.codeSystem[suggestion.displayField].display; } + if (this.narrowSearchSuggestions[suggestion.matchType] == null) { this.narrowSearchSuggestions[suggestion.matchType] = []; if (suggestion.matchType === 'WORD') { @@ -403,13 +570,60 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr this.narrowSearchSuggestions[suggestion.matchType].push(suggestion); this.narrowSearchSuggestionsCount++; }); + + if(this.privateSearchTerm && !this.utilsService.looksLikeComplexSearchTerm(this.privateSearchTerm)) { + + const lq: string = this.utilsService.makeBeginsWithSearchTerm('root_names_name', this.privateSearchTerm.toString()); + + // The match type usually originates from the backend. + // But below, it is specified here to make additonal match options(s) in the backend. + // Can't figure out why the sort of matchTypes does not work. + // I am not sure it worked before this change. + // I would like Additional matches to appear first. + + let suggestion: NarrowSearchSuggestion = { + matchType: 'ADDITIONAL', + count: 0, + displayField: 'Any Name Begins With', + luceneField: 'root_names_name', + luceneQuery: lq + }; + this.substanceService.searchSubstances(lq).subscribe(response => { + if(response?.total && response.total>0) { + suggestion.count = response.total; + if (this.narrowSearchSuggestions[suggestion.matchType] == null) { + this.narrowSearchSuggestions[suggestion.matchType] = []; + if (suggestion.matchType === 'WORD') { + this.matchTypes.unshift(suggestion.matchType); + } else { + this.matchTypes.push(suggestion.matchType); + } + } + this.narrowSearchSuggestions[suggestion.matchType].push(suggestion); + this.narrowSearchSuggestionsCount++; + } + }); + } + + // use method sortMatchTypes in template instead + // this.matchTypes.sort(); + } this.substanceService.getExportOptions(pagingResponse.etag).subscribe(response => { - this.exportOptions = response; + this.exportOptions = response.filter(exp => { + if (exp.extension) { + //TODO Make this generic somehow, so addditional-type exports are isolated + if ((exp.extension === 'appxlsx') || (exp.extension === 'prodxlsx') || + (exp.extension === 'ctusxlsx')|| (exp.extension === 'cteuxlsx')) { + return false; + } + } + return true; + }); }); this.substanceService.setResult(pagingResponse.etag, pagingResponse.content, pagingResponse.total); }, error => { - this.gaService.sendException('getSubstancesDetails: error from API cal'); + this.gaService.sendException('getSubstancesDetails: error from API call'); const notification: AppNotification = { message: 'There was an error trying to retrieve substances. Please refresh and try again.', type: NotificationType.error, @@ -439,6 +653,16 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr } +sortMatchTypes(a:Array) { + return _.sortBy(a); +} + +searchTermOkforBeginsWithSearch(): boolean { + return (this.privateSearchTerm && !this.utilsService.looksLikeComplexSearchTerm(this.privateSearchTerm)); +} + + + restricSearh(searchTerm: string): void { this.privateSearchTerm = searchTerm; this.searchTermHash = this.utilsService.hashCode(this.privateSearchTerm); @@ -451,19 +675,24 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr export(url: string, extension: string) { if (this.authService.getUser() !== '') { const dialogReference = this.dialog.open(ExportDialogComponent, { - height: '215x', - width: '550px', + maxHeight: '85%', + + width: '60%', data: { 'extension': extension } }); this.overlayContainer.style.zIndex = '1002'; - const exportSub = dialogReference.afterClosed().subscribe(name => { + const exportSub = dialogReference.afterClosed().subscribe(response => { + const name = response.name; + const id = response.id; this.overlayContainer.style.zIndex = null; if (name && name !== '') { this.loadingService.setLoading(true); const fullname = name + '.' + extension; - this.authService.startUserDownload(url, this.privateExport, fullname).subscribe(response => { + this.authService.startUserDownload(url, this.privateExport, fullname, id).subscribe(response => { + // this.substanceService.getConfigByID(id).subscribe(resp =>{ + // }); this.loadingService.setLoading(false); this.loadingService.setLoading(false); const navigationExtras: NavigationExtras = { @@ -483,12 +712,10 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr } setSubstanceNames(substanceId: string): void { - this.loadingService.setLoading(true); this.substanceService.getSubstanceNames(substanceId).pipe(take(1)).subscribe(names => { this.names[substanceId] = names; - this.loadingService.setLoading(false); }, error => { - this.loadingService.setLoading(false); + this.names[substanceId] = []; }); } @@ -509,6 +736,20 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr } }); this.codes[substanceId].codeSystemNames = this.sortCodeSystems(this.codes[substanceId].codeSystemNames); + this.codes[substanceId].codeSystemNames.forEach(sysName => { + this.codes[substanceId].codeSystems[sysName] = this.codes[substanceId].codeSystems[sysName].sort((a, b) => { + let test = 0; + if (a.type === 'PRIMARY' && b.type !== 'PRIMARY') { + test = 1; + } else if (a.type !== 'PRIMARY' && b.type === 'PRIMARY') { + test = -1; + } else { + test = 0; + } + return test; + }); + }); + } this.loadingService.setLoading(false); }, error => { @@ -516,6 +757,7 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr }); } + populateUrlQueryParameters(): void { const navigationExtras: NavigationExtras = { queryParams: {} @@ -524,6 +766,9 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr navigationExtras.queryParams['search'] = this.privateSearchTerm; navigationExtras.queryParams['structure_search'] = this.privateStructureSearchTerm; navigationExtras.queryParams['sequence_search'] = this.privateSequenceSearchTerm; + navigationExtras.queryParams['searchOnIdentifiers'] = this.searchOnIdentifiers; + navigationExtras.queryParams['bulkQID'] = this.privateBulkSearchQueryId; + navigationExtras.queryParams['searchEntity'] = this.searchEntity; navigationExtras.queryParams['cutoff'] = this.privateSearchCutoff; navigationExtras.queryParams['type'] = this.privateSearchType; navigationExtras.queryParams['seq_type'] = this.privateSearchSeqType; @@ -543,10 +788,49 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr this.location.go(urlTree.toString()); } - editGuidedSearch(): void { - const eventLabel = environment.isAnalyticsPrivate ? 'guided search term' : + editAdvancedSearch(): void { + const eventLabel = environment.isAnalyticsPrivate ? 'advanced search term' : `${this.privateSearchTerm}`; - this.gaService.sendEvent('substancesFiltering', 'icon-button:edit-guided-search', eventLabel); + this.gaService.sendEvent('substancesFiltering', 'icon-button:edit-advanced-search', eventLabel); + // Structure Search + // const eventLabel = environment.isAnalyticsPrivate ? 'structure search term' : + // `${this.privateStructureSearchTerm}-${this.privateSearchType}-${this.privateSearchCutoff}`; + // this.gaService.sendEvent('substancesFiltering', 'icon-button:edit-structure-search', eventLabel); + + // ** BEGIN: Store in Local Storage for Advanced Search + // storage searchterm in local storage when going from Browse Substance to Advanced Search (NOT COMING FROM ADVANCED SEARCH) + if (!this.searchHashFromAdvanced) { + const advSearchTerm: Array = []; + advSearchTerm[0] = this.privateSearchTerm; + const queryStatementHashes = []; + const queryStatement = { + condition: '', + queryableProperty: 'Manual Query Entry', + command: 'Manual Query Entry', + commandInputValues: advSearchTerm, + query: this.privateSearchTerm + }; + + // Store in cookies, Category tab (Substance, Application, etc) + const categoryHash = this.utilsService.hashCode('Substance'); + localStorage.setItem(categoryHash.toString(), 'Substance'); + queryStatementHashes.push(categoryHash); + + const queryStatementString = JSON.stringify(queryStatement); + const hash = this.utilsService.hashCode(queryStatementString); + + // Store in cookies, Each Query Statement is stored in separate hash + localStorage.setItem(hash.toString(), queryStatementString); + + // Push Query Statements Hashes in Array + queryStatementHashes.push(hash); + + // Store in cookies, store in Query Hash - Query Statement Hashes Array + const queryStatementHashesString = JSON.stringify(queryStatementHashes); + + localStorage.setItem(this.searchTermHash.toString(), queryStatementHashesString); + } + // ** END: Store in Local Storage for Advanced Search const navigationExtras: NavigationExtras = { queryParams: { @@ -554,7 +838,13 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr } }; - this.router.navigate(['/guided-search'], navigationExtras); + navigationExtras.queryParams['structure'] = this.privateStructureSearchTerm || null; + navigationExtras.queryParams['type'] = this.privateSearchType || null; + + if(this.privateSearchType === 'similarity') { + navigationExtras.queryParams['cutoff'] = this.privateSearchCutoff || 0; + } + this.router.navigate(['/advanced-search'], navigationExtras); } editStructureSearch(): void { @@ -592,6 +882,15 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr this.searchSubstances(); } + seedSmiles(smiles: string) { + const navigationExtras: NavigationExtras = { + queryParams: { + importStructure: encodeURIComponent(smiles) + } + }; + this.router.navigate(['/substances/register/chemical'], navigationExtras); + } + editSequenceSearh(): void { const eventLabel = environment.isAnalyticsPrivate ? 'sequence search term' : `${this.privateSequenceSearchTerm}-${this.privateSearchType}-${this.privateSearchCutoff}-${this.privateSearchSeqType}`; @@ -628,12 +927,47 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr this.searchSubstances(); } + editBulkSearch(): void { + const eventLabel = environment.isAnalyticsPrivate ? 'bulk search term' : + `${this.searchEntity}-bulk-search-${this.privateBulkSearchQueryId}`; + this.gaService.sendEvent('substancesFiltering', 'icon-button:edit-bulk-search', eventLabel); + + const navigationExtras: NavigationExtras = { + queryParams: { + bulkQID: this.privateBulkSearchQueryId, + searchOnIdentifiers: this.searchOnIdentifiers, + searchEntity: this.searchEntity + } + }; + this.router.navigate(['/bulk-search'], navigationExtras); + } + + clearBulkSearch(): void { + + const eventLabel = environment.isAnalyticsPrivate ? 'bulk search term' : + `${this.searchEntity}-bulk-search-${this.privateBulkSearchQueryId}`; + this.gaService.sendEvent('substancesFiltering', 'icon-button:clear-bulk-search', eventLabel); + + this.privateBulkSearchQueryId = null; + this.privateBulkSearchSummary = null; + this.searchEntity = ''; + this.searchOnIdentifiers = null; + this.privateSearchType = ''; + this.privateSearchCutoff = 0; + this.smiles = ''; + this.pageIndex = 0; + + this.populateUrlQueryParameters(); + this.searchSubstances(); + } + clearSearch(): void { const eventLabel = environment.isAnalyticsPrivate ? 'search term' : this.privateSearchTerm; this.gaService.sendEvent('substancesFiltering', 'icon-button:clear-search', eventLabel); this.privateSearchTerm = ''; + this.wildCardText = ''; this.searchTermHash = null; this.pageIndex = 0; @@ -644,6 +978,8 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr clearFilters(): void { // for facets + // Does this facet remove work completely? When I (aw) click RESET button the facet + // is cleared but this.displayFacets still has the value. this.displayFacets.forEach(displayFacet => { displayFacet.removeFacet(displayFacet.type, displayFacet.bool, displayFacet.val); }); @@ -652,12 +988,24 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr } else if ((this.privateSequenceSearchTerm != null && this.privateSequenceSearchTerm !== '') || (this.privateSequenceSearchKey != null && this.privateSequenceSearchKey !== '')) { this.clearSequenceSearch(); + } else if (this.privateBulkSearchQueryId != null && this.privateBulkSearchQueryId !== undefined) { + this.clearBulkSearch(); } else { this.clearSearch(); } this.facetManagerService.clearSelections(); } + clickToRefreshPreview() { + this.emitService.setRefresh(true); + this.isRefresher = true; + this.loadComponent(); + } + + clickToCancel() { + this.emitService.setCancel(true); + } + get searchTerm(): string { return this.privateSearchTerm; } @@ -670,10 +1018,25 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr return this.privateSequenceSearchTerm; } + get bulkSearchSummary(): string { + return this.privateBulkSearchSummary; + } + get bulkSearchQueryId(): number { + return this.privateBulkSearchQueryId; + } + get bulkSearchStatusKey(): string { + return this.privateBulkSearchStatusKey; + } + get searchType(): string { return this.privateSearchType; } + get searchStrategy(): string { + return this.privateSearchStrategy; + } + + get searchCutoff(): number { return this.privateSearchCutoff; } @@ -722,27 +1085,25 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr openImageModal(substance: SubstanceDetail): void { const eventLabel = environment.isAnalyticsPrivate ? 'substance' : substance._name; - - this.gaService.sendEvent('substancesContent', 'link:structure-zoom', eventLabel); + this.gaService.sendEvent('substancesContent', 'link:structure-zoom', eventLabel); let data: any; if (substance.substanceClass === 'chemical') { data = { - structure: substance.structure.id, + structure: substance.uuid, smiles: substance.structure.smiles, uuid: substance.uuid, - names: substance.names + names: this.names[substance.uuid] }; } else { data = { - structure: substance.polymer.displayStructure.id, - names: substance.names + structure: substance.uuid, + names: this.names[substance.uuid] }; } const dialogRef = this.dialog.open(StructureImageModalComponent, { - height: '90%', width: '650px', panelClass: 'structure-image-panel', data: data @@ -818,4 +1179,46 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr this.overlayContainer.style.zIndex = null; } + + downloadJson(id: string) { + this.substanceService.getSubstanceDetails(id).pipe(take(1)).subscribe(response => { + this.downloadFile(JSON.stringify(response), id + '.json'); + }); + + } + + copySmiles(val: string) { + const selBox = document.createElement('textarea'); + selBox.style.position = 'fixed'; + selBox.style.left = '0'; + selBox.style.top = '0'; + selBox.style.opacity = '0'; + selBox.value = val; + document.body.appendChild(selBox); + selBox.focus(); + selBox.select(); + document.execCommand('copy'); + document.body.removeChild(selBox); + } + + addToList(): void { + let data = {view: 'add', etag: this.etag, lists: this.userLists}; + + + const dialogRef = this.dialog.open(UserQueryListDialogComponent, { + width: '800px', + autoFocus: false, + data: data + + }); + this.overlayContainer.style.zIndex = '1002'; + + const dialogSubscription = dialogRef.afterClosed().pipe(take(1)).subscribe(response => { + if (response) { + this.overlayContainer.style.zIndex = null; + } + }); + } + + } diff --git a/src/app/core/unauthorized/unauthorized.component.html b/src/app/core/unauthorized/unauthorized.component.html new file mode 100644 index 000000000..8c348559a --- /dev/null +++ b/src/app/core/unauthorized/unauthorized.component.html @@ -0,0 +1,10 @@ +
    + +
    + You are not authorized to see this resource. Please contact an administrator + or send an email to {{email}} + + to be granted access. +
    +
    +
    diff --git a/src/app/core/unauthorized/unauthorized.component.scss b/src/app/core/unauthorized/unauthorized.component.scss new file mode 100644 index 000000000..1474b925d --- /dev/null +++ b/src/app/core/unauthorized/unauthorized.component.scss @@ -0,0 +1,17 @@ +.main-container { +margin-top: 100px; +} + +.form-row { + width:100%; + flex-direction: row; + + .equal-width { + flex: 1 0 0; + } + + padding: 25px; + font-size: 26px; + line-height: 46px; + text-align: center; +} \ No newline at end of file diff --git a/src/app/core/unauthorized/unauthorized.component.spec.ts b/src/app/core/unauthorized/unauthorized.component.spec.ts new file mode 100644 index 000000000..28383a05f --- /dev/null +++ b/src/app/core/unauthorized/unauthorized.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UnauthorizedComponent } from './unauthorized.component'; + +describe('UnauthorizedComponent', () => { + let component: UnauthorizedComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ UnauthorizedComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(UnauthorizedComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/unauthorized/unauthorized.component.ts b/src/app/core/unauthorized/unauthorized.component.ts new file mode 100644 index 000000000..08574541e --- /dev/null +++ b/src/app/core/unauthorized/unauthorized.component.ts @@ -0,0 +1,32 @@ +import { Component, OnInit } from '@angular/core'; +import { ConfigService } from '@gsrs-core/config'; +import { AuthService } from '@gsrs-core/auth'; +import { take } from 'rxjs/operators'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-unauthorized', + templateUrl: './unauthorized.component.html', + styleUrls: ['./unauthorized.component.scss'] +}) +export class UnauthorizedComponent implements OnInit { + email?: string; + constructor( + private configService: ConfigService, + private authService: AuthService, + private router: Router + ) { } + + ngOnInit() { + if (this.configService.configData && this.configService.configData.contactEmail) { + this.email = this.configService.configData.contactEmail; + } + + this.authService.getAuth().pipe(take(1)).subscribe(response => { + if (response && response.active) { + this.router.navigate(['/home']); + } + }); + } + +} diff --git a/src/app/core/utils/code-display.module.ts b/src/app/core/utils/code-display.module.ts index b2d1293e7..397f64ea4 100644 --- a/src/app/core/utils/code-display.module.ts +++ b/src/app/core/utils/code-display.module.ts @@ -1,15 +1,18 @@ import { CodeSystemDisplayPipe } from '@gsrs-core/utils/code-system-display.pipe'; import { NgModule } from '@angular/core'; +import { RelationshipDisplayPipe } from '@gsrs-core/utils/relationship-display.pipe.'; @NgModule({ imports: [ // dep modules ], declarations: [ - CodeSystemDisplayPipe + CodeSystemDisplayPipe, + RelationshipDisplayPipe ], exports: [ - CodeSystemDisplayPipe + CodeSystemDisplayPipe, + RelationshipDisplayPipe ] }) export class CodeDisplayModule {} \ No newline at end of file diff --git a/src/app/core/utils/element-label-display.module.ts b/src/app/core/utils/element-label-display.module.ts new file mode 100644 index 000000000..c1c9b4b8a --- /dev/null +++ b/src/app/core/utils/element-label-display.module.ts @@ -0,0 +1,15 @@ +import { ElementLabelDisplayPipe } from '@gsrs-core/utils/element-label-display.pipe'; +import { NgModule } from '@angular/core'; + +@NgModule({ + imports: [ + // dep modules + ], + declarations: [ + ElementLabelDisplayPipe + ], + exports: [ + ElementLabelDisplayPipe + ] + }) + export class ElementLabelDisplayModule {} \ No newline at end of file diff --git a/src/app/core/utils/element-label-display.pipe.ts b/src/app/core/utils/element-label-display.pipe.ts new file mode 100644 index 000000000..4cbc1d801 --- /dev/null +++ b/src/app/core/utils/element-label-display.pipe.ts @@ -0,0 +1,49 @@ +import {Pipe, PipeTransform} from '@angular/core'; +import { ConfigService } from '@gsrs-core/config'; + +@Pipe({ + name: 'elementLabel' +}) +export class ElementLabelDisplayPipe implements PipeTransform { + + private labels: any; + private defaultLabels: any; + private conf: any; + // labels.group.key.value + private defaultConf = + {"elementLabelDisplay": { + "labels": { + "substance_names_name": { + "displayNameTitle": "Display Name", + "displayNameShortTitle":"DN", + "preferredTitle": "Additional Listing Name", + "preferredShortTitle": "AL" + } + } + } + }; + + private confOK: boolean = false; + constructor( + configService: ConfigService + ) { + this.conf = configService.configData.elementLabelDisplay; + this.labels = configService.configData.elementLabelDisplay?.labels; + this.defaultLabels = this.defaultConf.elementLabelDisplay.labels; + if(this.conf && this.labels) { + this.confOK = true; + } + } + transform(key?: string, group?: string): string { + if(this.confOK && this.labels[group] && this.labels[group][key]) { + return this.labels[group][key]; + } else if(this.defaultLabels && this.defaultLabels[group] && this.defaultLabels[group][key]) { + return this.defaultLabels[group][key]; + } else if(key) { + return key; + } else { + return "UNKOWN ELEMENT LABEL KEY/VALUE"; + } + } +} + diff --git a/src/app/core/utils/names-display-order.pipe.ts b/src/app/core/utils/names-display-order.pipe.ts index 96e6d953d..046fbd68b 100644 --- a/src/app/core/utils/names-display-order.pipe.ts +++ b/src/app/core/utils/names-display-order.pipe.ts @@ -5,13 +5,17 @@ import { Pipe, PipeTransform } from '@angular/core'; export class NamesDisplayPipe implements PipeTransform { transform(names: Array): Array { names = names.slice().sort((a, b) => { - let returned = -1; - if ( b.displayName === true) { - returned = 1; - } else if (b.preferred === true && a.displayName !== true) { - returned = 1; - } - return returned; + let returned = -1; + if (a.displayName) { + returned = -1; + } else if ( b.displayName === true) { + returned = 1; + } else if (b.preferred === true && a.displayName !== true) { + returned = 1; + } else if (!b.displayName && !a.displayName && a.name > b.name) { + returned = 1; + } + return returned; }); return names.slice(0, 4); } diff --git a/src/app/core/utils/paging-response.model.ts b/src/app/core/utils/paging-response.model.ts index 92b1fd101..bd179b8cb 100644 --- a/src/app/core/utils/paging-response.model.ts +++ b/src/app/core/utils/paging-response.model.ts @@ -9,6 +9,7 @@ export interface PagingResponse { created: number; etag: string; path: string; + statusKey: string; uri: string; nextPageUri: string; method: string; @@ -24,6 +25,7 @@ export interface PagingResponse { exactMatches?: Array; facets?: Array; filter?: string; + summary?: any; // added for bulk search } export interface NarrowSearchSuggestion { diff --git a/src/app/core/utils/relationship-display.pipe..ts b/src/app/core/utils/relationship-display.pipe..ts new file mode 100644 index 000000000..8694c321d --- /dev/null +++ b/src/app/core/utils/relationship-display.pipe..ts @@ -0,0 +1,24 @@ +import {Pipe, PipeTransform} from '@angular/core'; +import { ControlledVocabularyService } from '@gsrs-core/controlled-vocabulary'; +import { map } from 'rxjs/operators'; +import { Observable } from 'rxjs'; + +@Pipe({ + name: 'relationshipDisplay' +}) +export class RelationshipDisplayPipe implements PipeTransform { + constructor(public cvService: ControlledVocabularyService) { + + } + transform(name: string, item2?: string): any { + return this.cvService.getDomainVocabulary('RELATIONSHIP_TYPE').pipe(map(response => { + let resp; + resp = response['RELATIONSHIP_TYPE'].dictionary; + if (resp[name]) { + return resp[name].display; + } else { + return name; + } + })); +} +} diff --git a/src/app/core/utils/substance-status.pipe.ts b/src/app/core/utils/substance-status.pipe.ts new file mode 100644 index 000000000..3cd194f89 --- /dev/null +++ b/src/app/core/utils/substance-status.pipe.ts @@ -0,0 +1,24 @@ +import {Pipe, PipeTransform} from '@angular/core'; +import { ConfigService } from '@gsrs-core/config'; + +@Pipe({ + name: 'statusDisplay' +}) +export class SubstanceStatusPipe implements PipeTransform { + constructor(public configService: ConfigService) { + + } + transform(name: string, item2?: string): string { + + if(name === 'approved') { + if(this.configService.configData && this.configService.configData.approvalCodeName) { + return 'Validated (' + this.configService.configData.approvalCodeName + ')'; + } else { + return 'Validated (UNII)'; + } + } else { + return name; + } + + } +} \ No newline at end of file diff --git a/src/app/core/utils/text-input-form/text-input-form.component.html b/src/app/core/utils/text-input-form/text-input-form.component.html new file mode 100644 index 000000000..1d2ca426b --- /dev/null +++ b/src/app/core/utils/text-input-form/text-input-form.component.html @@ -0,0 +1,18 @@ +
    +

    {{instruction}}

    +
    +
    + +
    +
    +
    + Select file: +
    +
    + + +
    +
    \ No newline at end of file diff --git a/src/app/core/utils/text-input-form/text-input-form.component.scss b/src/app/core/utils/text-input-form/text-input-form.component.scss new file mode 100644 index 000000000..83baebb5c --- /dev/null +++ b/src/app/core/utils/text-input-form/text-input-form.component.scss @@ -0,0 +1,27 @@ + +.text-input-form mat-form-field { + width:100% !important; +} + + +.text-input-form mat-form-field { + width:100% !important; +} + +.text-input-form textarea { + width: 100%; + border: 2px solid var(--regular-lightgray-color); + height: 300px; + overflow-y: auto; +} + + +.file-reader { + margin-top: 10px; + margin-bottom: 10px; +} + + +.error { + padding: 5px; +} diff --git a/src/app/core/utils/text-input-form/text-input-form.component.ts b/src/app/core/utils/text-input-form/text-input-form.component.ts new file mode 100644 index 000000000..33dded120 --- /dev/null +++ b/src/app/core/utils/text-input-form/text-input-form.component.ts @@ -0,0 +1,54 @@ +import { Component, OnInit, Inject, Input } from '@angular/core'; +import { FormControl } from '@angular/forms'; + +@Component({ + selector: 'app-text-input-form', + templateUrl: './text-input-form.component.html', + styleUrls: ['./text-input-form.component.scss'] +}) + +export class TextInputFormComponent implements OnInit { + @Input() placeholder = 'placeholder'; + @Input() instruction = 'Enter text'; + @Input() maxCount = 10000; + @Input() showSubmitButton = false; + @Input() showCancelButton = false; + @Input() showLoadTextFileIntoTextarea = true; + + query: string; + lineCount: number; + textControl = new FormControl(); + loadTextFileIntoTextareaError = ''; + + constructor( + ) { } + + ngOnInit() { + } + + submit(): void { + } + + cancel(): void { + } + + loadTextFileIntoTextareaChangeListener($event): void { + this.readTextFile($event.target); + } + + readTextFile(inputValue: any): void { + this.loadTextFileIntoTextareaError = ''; + const file: File = inputValue.files[0]; + if(file.type === 'text/plain') { + const textFileReader: FileReader = new FileReader(); + const fc = this.textControl; + + textFileReader.onloadend = (e) => { + fc.setValue(textFileReader.result); + }; + textFileReader.readAsText(file); + } else { + this.loadTextFileIntoTextareaError = 'File of type \'text/plain\' is required.'; + } + } +} diff --git a/src/app/core/utils/utils.service.ts b/src/app/core/utils/utils.service.ts index 6734f4753..47c19acc3 100644 --- a/src/app/core/utils/utils.service.ts +++ b/src/app/core/utils/utils.service.ts @@ -7,6 +7,7 @@ import { SubstanceSuggestionsGroup } from './substance-suggestions-group.model'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; import { map } from 'rxjs/operators'; import { BuildInfo } from './build-info.model'; +import { S } from '@angular/cdk/keycodes'; @Injectable({ providedIn: 'root' @@ -23,6 +24,7 @@ export class UtilsService { getStructureSearchSuggestions(searchTerm: string): Observable { const url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/`; + return this.http.get(url + 'suggest?q=' + searchTerm); } @@ -34,17 +36,21 @@ export class UtilsService { return this.sanitizer.bypassSecurityTrustUrl(imgUrl); } - getStructureImgUrl(structureId: string, size: number = 150, stereo?: boolean, atomMaps?: Array, version?: number): string { + getStructureImgUrl(structureId: string, size: number = 150, stereo?: boolean, atomMaps?: Array, version?: number, altId?: string): string { if (!stereo) { stereo = false; } const apiBaseUrl = this.configService.configData.apiBaseUrl; const randomKey = Math.random().toString(36).replace('0.', ''); - let url = `${apiBaseUrl}img/${structureId}.svg?size=${size.toString()}&stereo=${stereo}&cache-control=${randomKey}`; - if (atomMaps != null) { + // let url = `${apiBaseUrl}img/${structureId}.svg?size=${size.toString()}&stereo=${stereo}&cache-control=${randomKey}`; + let url = `${apiBaseUrl}api/v1/substances/render(${structureId})?format=svg&size=${size.toString()}&stereo=${stereo}&cache-control=${randomKey}`; + if (atomMaps != null) { url = `${url}&context=${atomMaps.toString()}`; } - if (version != null) { + if (altId && altId != null) { + url = `${url}&recordId=${altId}`; + } + if (version && version != null) { url = `${url}&version=${version}`; } return url; @@ -84,7 +90,7 @@ export class UtilsService { return stringArray.join(' '); } - /* tslint:disable:no-bitwise */ + /* eslint-disable no-bitwise */ hashCode(...args): number { const stringToHash = JSON.stringify([...args]); let hash = 0, i, chr; @@ -273,4 +279,37 @@ export class UtilsService { const url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/buildInfo`; return this.http.get(url); } + + looksLikeComplexSearchTerm(searchTerm:string): boolean { + // If we have an underscore followed by a colon, we think it's a complex search. + // e.g. root_names_name:Aspirin + const regexp : RegExp = /_.*:/g; + // The AND/OR checks were in a previous version but may be unneeded/confounding + // unless we're considering draft searchTerms that may have forgotten the complex search syntax. + if (regexp.test(searchTerm) || (searchTerm.indexOf(' AND ') > -1) || (searchTerm.indexOf(' OR ') > -1)){ + return true; + } + return false; + } + + looksLikeComplexSearchTermOrContainsStrings(searchTerm:string, strings:Array): boolean { + // often you'll want to check if the string has a double quote or * + if (this.looksLikeComplexSearchTerm){ + return true; + } + strings.forEach(function(value) { + if (searchTerm.indexOf(value.valueOf())>-1) { return true; }; + }); + return false; + } + + makeBeginsWithSearchTerm(targetField: string, searchTerm:string): string { + // Make and clean a begins-with search term from a non-complex search term that may + // have a wildcard or quotes. + // Remove extra carets ^ and double quotes " before adding them here. + var s: string = searchTerm.replace(/(^"|"$)/g, ''); + s = s.replace(/(^^)/g, ''); + const lq =targetField + ':"^' + s +'"'; + return lq; + } } diff --git a/src/app/core/utils/wildcard.service.spec.ts b/src/app/core/utils/wildcard.service.spec.ts new file mode 100644 index 000000000..4a7a3b936 --- /dev/null +++ b/src/app/core/utils/wildcard.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { WildcardService } from './wildcard.service'; + +describe('WildcardService', () => { + let service: WildcardService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(WildcardService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/core/utils/wildcard.service.ts b/src/app/core/utils/wildcard.service.ts new file mode 100644 index 000000000..e0ad0b306 --- /dev/null +++ b/src/app/core/utils/wildcard.service.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@angular/core'; +import { Observable, Subject } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class WildcardService { + + wildCardObservable: Observable; + topSearchObservable: Observable; + private wildcardText = new Subject(); + private topSearchBox = new Subject(); + + constructor() { + this.wildCardObservable = this.wildcardText.asObservable(); + this.topSearchObservable = this.topSearchBox.asObservable(); + } + + getWildCardText(data) { + this.wildcardText.next(data); + } + + getTopSearchBoxText(data) { + this.topSearchBox.next(data); + } + +} diff --git a/src/app/fda/advanced-search/advanced-query-statement/advanced-query-statement.component.html b/src/app/fda/advanced-search/advanced-query-statement/advanced-query-statement.component.html new file mode 100644 index 000000000..20ab9ec9c --- /dev/null +++ b/src/app/fda/advanced-search/advanced-query-statement/advanced-query-statement.component.html @@ -0,0 +1,280 @@ + + +
    + +
    + + Condition + + + {{option}} + + + +
    + +
    + + Search in Fields + + + {{option}} + + + + + Show all fields + + +
    + + + + + + + Show common fields + + + + + {{option}} + + +
    +
    + + + + + +
    +
    + + + +
    + + + + + + +
    {{suggestion.key}}
    +
    + +
    +
    +
    + + +
    + + + Required + + {{this.commandOptionsExample}} + + +
    +
    + + + Required + + + + +
    +
    + + + + {{option.display}} + + + Required + +
    +
    +
    + +
    + diff --git a/src/app/fda/advanced-search/advanced-query-statement/advanced-query-statement.component.scss b/src/app/fda/advanced-search/advanced-query-statement/advanced-query-statement.component.scss new file mode 100644 index 000000000..848d646fa --- /dev/null +++ b/src/app/fda/advanced-search/advanced-query-statement/advanced-query-statement.component.scss @@ -0,0 +1,125 @@ +.query-statement-container { + display: flex; + width: 100%; +} + +.condition-column { + width: 100px; + max-width: 100px; + min-width: 100px; + padding-right: 20px; + padding-top: 1px; +} + +.mat-form-field { + width: 100%; + max-width: 100%; + + ::ng-deep .mat-form-field-infix { + width: 100%; + } +} + +.queryable-property-column { + width: 350px; + min-width: 350px; + max-width: 350px; + padding-right: 20px; +} + +.command-column { + display: flex; + padding-right: 10px; + padding-top: 1px; + width: 250px; + max-width: 250px; + min-width: 250px; +} + +.query-column { + flex-grow: 1; + display: flex; +} + +.query-input { + flex-grow: 1; + padding: 0 10px; +} + +.mat-hint.action-hint { + width: 100%; + display: flex; + justify-content: flex-end; + font-size: 12px; + + a { + color: var(--link-primary-color); + } + + div { + color: var(--regular-grey-color); + font-size: 12px; + } + + span { + color: var(--regular-black-color); + } +} + +.mat-error { + color: var(--regular-red-color); +} + +.marginright20px { + margin-right: 20px; +} + +.marginright25px { + margin-right: 25px; +} + +.width5percent { + width: 5%; + min-width: 5%; + max-width: 5%; +} + +.width10percent { + width: 10%; + min-width: 10%; + max-width: 10%; +} + +.width15percent { + width: 15%; + min-width: 15%; + max-width: 15%; +} + +.width25percent { + width: 25%; + min-width: 25%; + max-width: 25%; +} + +.width27percent { + width: 27%; + min-width: 27%; + max-width: 27%; +} + +.width30percent { + width: 30%; + min-width: 30%; + max-width: 30%; +} + +.width33percent { + width: 33%; + min-width: 33%; + max-width: 33%; +} + +.bordergray { + border: 1px solid var(--regular-grey-color); +} diff --git a/src/app/fda/advanced-search/advanced-query-statement/advanced-query-statement.component.spec.ts b/src/app/fda/advanced-search/advanced-query-statement/advanced-query-statement.component.spec.ts new file mode 100644 index 000000000..de44b11c5 --- /dev/null +++ b/src/app/fda/advanced-search/advanced-query-statement/advanced-query-statement.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AdvancedQueryStatementComponent } from './advanced-query-statement.component'; + +describe('AdvancedQueryStatementComponent', () => { + let component: AdvancedQueryStatementComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ AdvancedQueryStatementComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AdvancedQueryStatementComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/fda/advanced-search/advanced-query-statement/advanced-query-statement.component.ts b/src/app/fda/advanced-search/advanced-query-statement/advanced-query-statement.component.ts new file mode 100644 index 000000000..5a2be2a43 --- /dev/null +++ b/src/app/fda/advanced-search/advanced-query-statement/advanced-query-statement.component.ts @@ -0,0 +1,555 @@ +import { Component, OnInit, Input, OnDestroy, Output, EventEmitter, AfterViewInit } from '@angular/core'; +import { QueryableSubstanceDictionary, CommandInput, Command } from '@gsrs-core/guided-search/queryable-substance-dictionary.model'; +import { FormControl } from '@angular/forms'; +import { debounceTime, distinctUntilChanged, switchMap, take } from 'rxjs/operators'; +import { Subscription } from 'rxjs'; +import { typeCommandOptions, inputTypes } from '@gsrs-core/guided-search/query-statement/type-command-options.constant'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { ControlledVocabularyService, VocabularyTerm } from '@gsrs-core/controlled-vocabulary'; +import { AdvancedQueryStatement } from './advanced-query-statement.model'; +import { HttpClient } from '@angular/common/http'; +import { ConfigService } from '@gsrs-core/config'; +import { NavigationExtras, Router, ActivatedRoute } from '@angular/router'; +import { FacetsManagerService } from '@gsrs-core/facets-manager'; +import { UtilsService } from '@gsrs-core/utils'; +import { SubstanceService } from '@gsrs-core/substance/substance.service'; +import { SubstanceRelated, SubstanceSummary } from '@gsrs-core/substance'; +import { ApplicationService } from '../../application/service/application.service'; +import { ProductService } from '../../product/service/product.service'; +import { ClinicalTrialService } from '../../clinical-trials/clinical-trial/clinical-trial.service'; +import { SubstanceSuggestionsGroup } from '@gsrs-core/utils'; +import { AdvancedSearchService } from '../service/advanced-search.service'; +import { MatAutocompleteSelectedEvent, MatAutocomplete } from '@angular/material/autocomplete'; + +@Component({ + selector: 'app-advanced-query-statement', + templateUrl: './advanced-query-statement.component.html', + styleUrls: ['./advanced-query-statement.component.scss'] +}) + +export class AdvancedQueryStatementComponent implements OnInit, OnDestroy { + + queryableSubstanceDict: QueryableSubstanceDictionary; + displayProperties: Array; + displayPropertiesCommon: Array; + queryStatements: Array = []; + query = ''; + @Input() queryStatementHash?: number; + @Input() queryableOptionsCommon: Array; + // @Input() category: string; + private _index = 0; + private _queryableDictionary: QueryableSubstanceDictionary; + @Output() queryUpdated = new EventEmitter(); + @Output() processSearchOut = new EventEmitter(); + + // @Output() tabSelectUpdated = new EventEmitter(); + private allOptions: Array; + queryableOptionsAll: Array; + queryablePropertiesControl = new FormControl(); + queryablePropertiesAutocompleteControl = new FormControl(); + private subscriptions: Array = []; + conditionOptions = [ + 'AND', + 'OR', + 'NOT' + ]; + searchFields: Array; + conditionControl = new FormControl(); + categoryControl = new FormControl(); + commandControl = new FormControl(); + searchFieldControl = new FormControl(); + + selectedCondition = ''; + selectedQueryableProperty: string; + selectedQueryablePropertyType: string; + selectedLucenePath: string; + commandOptions: Array; + commandOptionsExample: string; + isShowAllCommandOptions = false; + selectedCommandOption: string; + commandInputs: Array; + queryParts: Array = []; + private typeCommandOptions = typeCommandOptions; + commandInputValueDict: { [queryablePropertyType: string]: Array } = {}; + private overlayContainer: HTMLElement; + cvOptions: Array; + isShowCommonFields = true; + searchText: string; + categoryinput: string; + advancedQueryStatements: Array = []; + dictionaryFileName: string; + searchValue: string; + + searchControl = new FormControl(); + substanceSuggestionsGroup: SubstanceSuggestionsGroup; + suggestionsFields: Array; + suggestions: Array = []; + private testElem: HTMLElement; + matOpen = true; + categorySearch: string; + typeAheadFieldName: string; + selectedQueryableSuggest: string; + @Output() searchPerformed = new EventEmitter(); + @Output() opened = new EventEmitter(); + @Output() closed = new EventEmitter(); + @Output() typeAheadUpdatedOut: EventEmitter = new EventEmitter(); + + constructor( + private overlayContainerService: OverlayContainer, + public cvService: ControlledVocabularyService, + private http: HttpClient, + private router: Router, + private configService: ConfigService, + private utilitiesService: UtilsService, + private substanceService: SubstanceService, + public applicationService: ApplicationService, + public productService: ProductService, + private clinicalTrialService: ClinicalTrialService, + private facetManagerService: FacetsManagerService, + private activatedRoute: ActivatedRoute, + private advancedSearchService: AdvancedSearchService + ) { } + + @Input() + set category(cat) { + this.categoryinput = cat; + /* + // Set the Dropdown 'Search In Fields' to default 'All', when user clicks on the tab, clear the previous criteria + this.queryablePropertiesControl.setValue('All'); + // Set the Dropdown 'For' to default 'Contains'. + this.commandControl.setValue('Contains'); + // Set the Search Text Box to default, empty/clear the textbox. + this.searchControl.setValue(''); + let inputType: string; + let queryablePropertyType = 'string'; + // this.commandInputValueDict[queryablePropertyType] = 'TEst'; + this.loadAdvancedSearchInitial(); + */ + } + + @Input() + set queryableDictionary(queryableSubstanceDictionary: QueryableSubstanceDictionary) { + if (queryableSubstanceDictionary != null) { + this._queryableDictionary = queryableSubstanceDictionary; + } + } + + @Input() + set queryableOptions(options: Array) { + this.allOptions = options; + this.queryableOptionsAll = options; + } + + @Input() + set index(index: number) { + if (index != null) { + this._index = index; + if (this._index === 0 && this.commandInputs) { + this.selectedCondition = ''; + this.refreshQuery(); + } + } + } + + get index(): number { + return this._index; + } + + ngOnInit() { + this.overlayContainer = this.overlayContainerService.getContainerElement(); + + inputTypes.forEach(key => { + this.commandInputValueDict[key] = []; + }); + + const subscription = this.queryablePropertiesControl.valueChanges.subscribe(value => { + this.queryablePropertySelected(value); + }); + this.subscriptions.push(subscription); + + const allQueriablePropertiesSubscription = this.queryablePropertiesAutocompleteControl.valueChanges.subscribe(value => { + this.queryableOptionsAll = this.allOptions.filter(option => { + return option.toLowerCase().indexOf(value.toLowerCase()) > -1; + }); + }); + this.subscriptions.push(allQueriablePropertiesSubscription); + + const commandSubscription = this.commandControl.valueChanges.subscribe((command: string) => { + this.setCommand(command); + }); + this.subscriptions.push(commandSubscription); + + if (this._index > 0) { + this.conditionControl.setValue('AND'); + this.selectedCondition = 'AND '; + const conditionSubscription = this.conditionControl.valueChanges.subscribe((condition: string) => { + this.selectedCondition = `${condition} `; + this.refreshQuery(); + }); + this.subscriptions.push(conditionSubscription); + } + + let queryStatement: AdvancedQueryStatement; + + // load/get Search text from hashcode from localStorage. + // For example: 1838576297 + if (this.queryStatementHash) { + const queryStatementString = localStorage.getItem(this.queryStatementHash.toString()); + if (queryStatementString) { + queryStatement = JSON.parse(queryStatementString); + } + } + + // if queryStatement exists from hashcode + // queryStatement returns something like this: + // {"queryHash":1638073503,"condition":"OR ","queryableProperty":"CAS RN","command":"Contains","commandInputValues":["15475 56 6"],"query":"OR root_codes_CAS:\"*15475 56 6*\""} + if (queryStatement != null) { + // Get type (text, Date, Number) for dropdown field 'Search In Field' from dictionary file + let queryablePropertyType = 'string'; + // if (queryStatement.queryableProperty && queryStatement.queryableProperty !== 'Manual Query Entry') { + if (this._queryableDictionary[queryStatement.queryableProperty]) { + queryablePropertyType = this._queryableDictionary[queryStatement.queryableProperty].type; + } else { + queryablePropertyType = 'string'; + } + let inputType: string; + const commandObject = typeCommandOptions[queryablePropertyType][queryStatement.command] as Command; + // commandObject returns something like this: {"commandInputs":[{"type":"text","example":"Example: sodium"}]} + if (commandObject.commandInputs) { + inputType = commandObject.commandInputs[0].type; + // search textbox value + // {"text":["15475 56 6"],"datetime":[],"number":[],"select":[]} + // **** LOAD PREVIOUS SEARCH TEXT BOX VALUE HERE + this.commandInputValueDict[inputType] = queryStatement.commandInputValues; + } + this.queryParts = queryStatement.queryParts; + this.conditionControl.setValue(queryStatement.condition.trim(), { emitEvent: false }); + this.selectedCondition = queryStatement.condition; + + // Get value for 'suggest' key from dictionary file, if there is a value, will use to display + // typeahead text box. + if (queryStatement.queryableProperty) { + // if (queryStatement.queryableProperty && queryStatement.queryableProperty !== 'Manual Query Entry') { + if (this._queryableDictionary[queryStatement.queryableProperty]) { + this.selectedQueryableSuggest = this._queryableDictionary[queryStatement.queryableProperty].suggest; + } + } + + this.isShowCommonFields = this.queryableOptionsCommon.indexOf(queryStatement.queryableProperty) > -1; + if (this.queryableOptionsCommon.indexOf(queryStatement.queryableProperty) > -1) { + this.isShowCommonFields = true; + this.queryablePropertiesControl.setValue(queryStatement.queryableProperty, { emitEvent: false }); + } else { + this.isShowCommonFields = false; + this.queryablePropertiesAutocompleteControl.setValue(queryStatement.queryableProperty, { emitEvent: false }); + } + // ***** LOAD PREVIOUS Query Properties (Any Name, CAS, etc) + this.processQueriablePropertyChange(queryStatement.queryableProperty); + // ***** LOAD PREVIOUS COMMAND (Contains, Exact, etc) + this.commandControl.setValue(queryStatement.command); + // **** LOAD PREVIOUS SEARCH TEXT BOX VALUE HERE FOR TYPE AHEAD TEXT BOX + if (queryStatement.commandInputValues) { + this.searchControl.setValue(queryStatement.commandInputValues); + } + } else { + this.queryablePropertiesControl.setValue('All'); + } + + // Type Ahead/Suggestion in Search Text box + this.searchControl.valueChanges.pipe( + debounceTime(500), + distinctUntilChanged(), + switchMap(searchValue => { + this.query = searchValue; + // const eventCategory = this.eventCategory || 'advancedSearchTypeAheadTextSearch'; + // const eventLabel = !this.configService.environment.isAnalyticsPrivate && searchValue || 'search term'; + // this.gaService.sendEvent(eventCategory, 'TypeAheadSearch:enter-term', eventLabel); + + // Search text by fieldName for Type Ahead + return this.advancedSearchService.getTypeAheadSearchText(this.categoryinput, this.selectedQueryableSuggest, searchValue) + }) + ).subscribe((response: SubstanceSuggestionsGroup) => { + this.substanceSuggestionsGroup = response; + this.suggestionsFields = Object.keys(this.substanceSuggestionsGroup); + }, error => { + // this.gaService.sendException('type ahead search suggestion error from API call'); + console.log(error); + }); + + } + + ngOnDestroy() { + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + + queryablePropertySelected(queryableProperty: string): void { + this.processQueriablePropertyChange(queryableProperty); + + if (this._queryableDictionary[queryableProperty]) { + // Get Suggestion/TypeAhead text from file + this.selectedQueryableSuggest = this._queryableDictionary[queryableProperty].suggest; + + if (!this._queryableDictionary[queryableProperty].cvDomain && this._queryableDictionary[queryableProperty].type === 'string') { + // this.commandControl.setValue('ALL of the following words in any order or position'); + this.commandControl.setValue('Contains'); + } else { + this.commandControl.setValue(this.commandOptions[0]); + } + } + } + + private processQueriablePropertyChange(queryableProperty: string): void { + let queryablePropertyType = 'string'; + this.selectedQueryablePropertyType = 'string'; + this.selectedQueryableProperty = queryableProperty; + if (this._queryableDictionary[queryableProperty]) { + // if CV + if (this._queryableDictionary[queryableProperty].cvDomain) { + this.setCvOptions(this._queryableDictionary[queryableProperty].cvDomain); + } + this.selectedLucenePath = this._queryableDictionary[queryableProperty].lucenePath; + if (this.selectedLucenePath) { + // In "Search in Field", replace space with '\ ' in the lucene path + this.selectedLucenePath = this.selectedLucenePath.replace(' ', '\\ '); + if (this.selectedLucenePath) + this.selectedLucenePath = this.selectedLucenePath + ':'; + } + this.selectedQueryablePropertyType = this._queryableDictionary[queryableProperty].type; + queryablePropertyType = this._queryableDictionary[queryableProperty].type; + } + this.commandOptions = Object.keys( + this.typeCommandOptions[queryablePropertyType] + ) + .filter( + option => { + let result = false; + // return this._queryableDictionary[queryableProperty].cvDomain || option !== 'the following exact default values' + // && (this.commandOptionsShowAll === false && (option === 'Contains' || option === 'Exact Match' || option === 'Starts With'))); + if (this.isShowAllCommandOptions === false) { + if (option === 'Contains' || option === 'Exact Match' || option === 'Starts With' || option === 'Ends With' || option === 'Manual Query Entry') { + result = true; + } else if (this._queryableDictionary[queryableProperty] && this._queryableDictionary[queryableProperty].cvDomain && option === 'the following exact default values') { + result = true; + } else if (this.selectedQueryablePropertyType === 'timestamp') { + result = true; + } else if (this.selectedQueryablePropertyType === 'number') { + result = true; + } else { + result = false; + } + } else { + if (this._queryableDictionary[queryableProperty]) { + if (this._queryableDictionary[queryableProperty].cvDomain || option !== 'the following exact default values') { + result = true; + } + } + } + return result; + } + ).sort((a, b) => { + if (a === 'the following exact default values') { + return -1; + } + if (b === 'the following exact default values') { + return 1; + } + if (a.toLowerCase() < b.toLowerCase()) { + return -1; + } + if (a.toLowerCase() > b.toLowerCase()) { + return 1; + } + return 0; + }); + } + + setCvOptions(cvDomain: string): void { + this.cvService.getDomainVocabulary(cvDomain).subscribe(response => { + this.cvOptions = response[cvDomain].list; + }); + } + + refreshQuery(): void { + if (this.commandInputs && this.commandInputs.length > 0) { + this.commandOptionsExample = this.commandInputs[0].example; + } + this.queryParts = []; + this.commandInputs.forEach((commandInput, index) => { + if (this.commandInputValueDict[commandInput.type] && this.commandInputValueDict[commandInput.type][index] != null) { + commandInput.constructQuery( + this.commandInputValueDict[commandInput.type][index], + this.selectedCondition, + this.selectedQueryableProperty, + this.selectedLucenePath, + this.queryUpdated, + this.queryParts + ); + } else { + this.queryUpdated.emit({ + condition: this.selectedCondition, + queryableProperty: this.selectedQueryableProperty, + command: this.selectedCommandOption, + commandInputValues: [], + query: '' + }); + } + }); + } + + queryablePropertiesAutocompleteClosed(): void { + this.decreaseOverlayZindex(); + const inputValue = this.queryablePropertiesAutocompleteControl.value; + + if (inputValue) { + for (let i = 0; i < this.allOptions.length; i++) { + if (this.allOptions[i].toLowerCase() === inputValue.toLowerCase()) { + this.queryablePropertySelected(this.allOptions[i]); + this.queryablePropertiesAutocompleteControl.setValue(this.allOptions[i]); + break; + } + } + } + + } + + queryablePropertiesAutocompleteBlurred(): void { + if (this.queryableOptionsAll.length === 0) { + this.queryablePropertiesAutocompleteControl.setValue('All'); + this.queryablePropertySelected('All'); + } + } + + clearSelectedQueryableProperty(): void { + this.queryablePropertiesAutocompleteControl.setValue(''); + } + + showAllFields(): void { + this.isShowCommonFields = false; + this.queryablePropertiesAutocompleteControl.setValue(this.selectedQueryableProperty || ''); + } + + showCommonFields(): void { + this.isShowCommonFields = true; + if (this.queryableOptionsCommon.indexOf(this.selectedQueryableProperty) > -1) { + this.queryablePropertiesControl.setValue(this.selectedQueryableProperty); + } else { + this.queryablePropertiesControl.setValue('All'); + } + } + + showAllCommandOptions(): void { + this.isShowAllCommandOptions = !this.isShowAllCommandOptions; + this.processQueriablePropertyChange(this.selectedQueryableProperty); + } + + private setCommand(command: string): void { + this.selectedCommandOption = command; + const commandObj = this.typeCommandOptions[this.selectedQueryablePropertyType][command] as any; + if (commandObj.commandInputs) { + this.commandInputs = commandObj.commandInputs; + this.refreshQuery(); + } else if (commandObj.constructQuery) { + this.commandInputs = []; + commandObj.constructQuery( + command.trim(), + this.selectedCondition, + this.selectedQueryableProperty, + this.selectedLucenePath, + this.queryUpdated, + this.queryParts + ); + } + } + + processSubstanceSearch(searchValue: string) { + this.navigateToSearchResults(searchValue); + } + + navigateToSearchResults(searchTerm: string) { + + const navigationExtras: NavigationExtras = { + queryParams: searchTerm ? { 'search': searchTerm } : null + }; + + this.router.navigate(['/browse-substance'], navigationExtras); + } + + increaseOverlayZindex(): void { + this.overlayContainer.style.zIndex = '1002'; + } + + decreaseOverlayZindex(): void { + this.overlayContainer.style.zIndex = null; + } + + openedChange(opened: boolean): void { + if (opened) { + this.increaseOverlayZindex(); + } else { + this.decreaseOverlayZindex(); + } + } + + searchOptionSelected(event?: MatAutocompleteSelectedEvent) { + // const eventCategory = this.eventCategory || 'advancedSearchTypeAheadTextSearch'; + const eventLabel = !this.configService.environment.isAnalyticsPrivate && event.option.value || 'auto-complete option'; + // this.gaService.sendEvent(eventCategory, 'select:auto-complete', eventLabel); + let searchTerm = event.option.value; + + this.searchPerformed.emit(searchTerm); + + if (searchTerm) { + this.queryParts = []; + this.commandInputs.forEach((commandInput, index) => { + // if (this.commandInputValueDict[commandInput.type] && this.commandInputValueDict[commandInput.type][index] != null) { + commandInput.constructQuery( + searchTerm, + this.selectedCondition, + this.selectedQueryableProperty, + this.selectedLucenePath, + this.queryUpdated, + this.queryParts + ); + }); + } + } + + highlight(field: string) { + if (!this.query) { + return field; + } else { + if (this.matOpen) { + this.testElem = document.querySelector('#overflow') as HTMLElement; + if (this.testElem != null) { + this.testElem.innerText = field; + if (this.testElem.scrollWidth > this.testElem.offsetWidth) { + const pos = field.toUpperCase().indexOf(this.query.toUpperCase()); + field = '...' + field.substring(pos - 15, field.length); + } + } + } + const query = this.query.replace(/(?=[() ])/g, '\\'); + return field.replace(new RegExp(query, 'gi'), match => { + return '' + match + ''; + }); + } + } + + focused(): void { + if (this.suggestionsFields != null && this.suggestionsFields.length > 0) { + this.matOpen = true; + this.opened.emit(); + } + } + + autoCompleteClosed(): void { + this.matOpen = false; + this.closed.emit(); + } + + processSearch(): void { + this.processSearchOut.emit(); + } +} diff --git a/src/app/fda/advanced-search/advanced-query-statement/advanced-query-statement.model.ts b/src/app/fda/advanced-search/advanced-query-statement/advanced-query-statement.model.ts new file mode 100644 index 000000000..9f3007bfb --- /dev/null +++ b/src/app/fda/advanced-search/advanced-query-statement/advanced-query-statement.model.ts @@ -0,0 +1,11 @@ +import { MatDatepickerInputEvent } from '@angular/material/datepicker'; + +export interface AdvancedQueryStatement { + condition?: string; + queryableProperty?: string; + command?: string; + commandInputValues?: Array; + query?: string; + queryParts?: Array; + queryHash?: number; +} diff --git a/src/app/fda/advanced-search/advanced-search.component.html b/src/app/fda/advanced-search/advanced-search.component.html new file mode 100644 index 000000000..247a69f93 --- /dev/null +++ b/src/app/fda/advanced-search/advanced-search.component.html @@ -0,0 +1,286 @@ +
    +
    + +

    + Advanced Search +

    + +
    + + +
    +
    + +
    +
    + + + + + +
    Substance
    +
    + ({{substanceCount}}) +
    +
    +
    +
    + + + +
    Application
    + +
    + ({{applicationCount}}) +
    +
    +
    +
    + + + +
    Product
    +
    + ({{productCount}}) +
    +
    +
    +
    + + + + + + +
    Adverse Event
    +
    + PT ({{adverseEventPtCount}})   DME ({{adverseEventDmeCount}})   CVM ({{adverseEventCvmCount}}) +
    +
    +
    +
    +
    + +
    + + + + + +
    +
    +
    + + +
    + +
    +
    + +
    + +
    + +
    + + +
    +
    +
    + +
    + Query +
    +
    +
    + {{queryDisplay}} +
    +
    + +
    +
    + + + + + +
    Filter by + {{category}} Facets({{numFacetsLoaded}}) +
    +
    +
    + +
    + +
    + + + +
    + +
    + + Facet View: + + + {{option}} + + + +
    + +
    + +
    +
    +
    + + +
    + + +
    +
    + + +
    +
    + + + + + + + + +
    +
    + + + + AND Search Structure + + + + +
    + +
    +
    + + +
    +
    +
    + + + + Substructure + + + Similarity + + + Exact + + + Flex + + + +
    +
    Similarity cutoff (tanimoto)
    + +
    {{similarityCutoff}}
    +
    +
    + + +
    + +
    +
    + +
    +
    +
    +
    +

    + Get Structure From Name +

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


    \ No newline at end of file diff --git a/src/app/fda/advanced-search/advanced-search.component.scss b/src/app/fda/advanced-search/advanced-search.component.scss new file mode 100644 index 000000000..2d9bb2dd2 --- /dev/null +++ b/src/app/fda/advanced-search/advanced-search.component.scss @@ -0,0 +1,588 @@ +.div-center { + min-width: 1200px; + max-width: 1200px; + margin: auto; + /*height: 200px; */ + /*margin: 0 auto;*/ +} + +.div-flex { + max-width: 1200px; + display: flex; + justify-content: space-between; +} + +.div-flex-right { + max-width: 1200px; + display: flex; + justify-content: flex-end; +} + +.content-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 0px 5px 0 5px; + border: 1px solid var(--grey-border-color-rgb); + margin-left: 150px; + margin-right: 150px; +} + +.mat-card { + margin-top: 0px; + display: flex; + flex-direction: column; + box-sizing: border-box; + max-width: 1250px; + width: 100%; +} + +.builder-container { + display: flex; + flex-direction: column; +} + +.header-row { + display: flex; + flex-direction: row; + margin-top: 10px; + padding-top: 7px; + padding-bottom: 10px; + font-size: 14px; + font-weight: 600; + height: 30px; + + .condition-column { + width: 100px; + } + + .queryable-property-column { + width: 400px; + } + + .command-column { + width: 190px + } + + .query-column { + flex-grow: 1; + padding-left: 10px; + } +} + +.builder-row { + display: flex; + width: 100%; + padding-bottom: 5px; +} + +.query-statement-container { + width: 94%; + max-width: 94%; + /* display: flex; */ + /* flex-grow: 1; */ +} + +.query-value { + padding: 10px 15px; + background-color: var(--query-bg-color); + border: solid 1px var(--query-border-color); + border-radius: 2px; + min-height: 40px; +} + +.actions-container { + width: 6%; + display: flex; + + .button-container { + width: 35px; + } +} + +.mat-card-actions { + padding-top: 10px 10px 0 10px; +} + +.query-builder-actions { + margin: 10px 0 15px 0; +} + +.title { + margin-top: 30px; + margin-bottom: 0; + padding-left: 10px; +} + +.titletab { + font-size: 14px; + color: var(--deep-blue-color-rgb); + padding: 10px 0px 5px 10px; +} + +.titlequery { + font-size: 20px; + font-weight: bold; + color: var(--deep-blue-color-rgb); + padding: 10px 0px 5px 10px; +} + +.font12px { + font-size: 12px; +} + +.colormaroon { + color: rgb(151, 87, 87) +} + +.colororange { + color: var(--orange-color-rgb); +} + +.colorblack { + color: var(--regular-black-color); +} + +.colorgray { + color: var(--maroon-color); +} + +.colorwhite { + color: var(--regular-white-color); +} + +.margintopneg80px { + margin-top: -80px; +} + +.marginneg10px { + margin-left: -10px; +} + +.margintop5px { + margin-top: 5px; +} + +.margintop10px { + margin-top: 10px; +} + +.margintop20px { + margin-top: 20px; +} + +.margintop30px { + margin-top: 30px; +} + +.margintop50px { + margin-top: 50px; +} + +.margintop70px { + margin-top: 70px; +} + +.marginleft20px { + margin-left: 20px; +} + +.marginleft125px { + margin-left: 125px; +} + +.marginleft200px { + margin-left: 200px; +} + +.marginright15px { + margin-right: 15px; +} + +.marginright10px { + margin-right: 10px; +} + +.marginright20px { + margin-right: 20px; +} + +.marginright25px { + margin-right: 25px; +} + +.marginbottom5px { + margin-bottom: 5px; +} + +.padtop10px { + padding-top: 50px; +} + +.width5percent { + width: 5%; + min-width: 5%; + max-width: 5%; +} + +.width10percent { + width: 10%; + min-width: 10%; + max-width: 10%; +} + +.width12percent { + width: 12%; + min-width: 12%; + max-width: 12%; +} + +.width15percent { + width: 15%; + min-width: 15%; + max-width: 15%; +} + +.width25percent { + width: 25%; + min-width: 25%; + max-width: 25%; +} + +.width27percent { + width: 27%; + min-width: 27%; + max-width: 27%; +} + +.width30percent { + width: 30%; + min-width: 30%; + max-width: 30%; +} + +.width33percent { + width: 33%; + min-width: 33%; + max-width: 33%; +} + +.width88percent { + width: 88%; + min-width: 88%; + max-width: 88%; +} + +.width100percent { + width: 100%; + min-width: 100%; + max-width: 100%; +} + +.height25px { + height: 25px; +} + +.text-align { + text-align: right; +} + +.bordergray { + border: 1px solid var(--regular-grey-color); +} + +.borderbottomdash { + border-bottom-style: dotted; + border-bottom-color: var(--pale-border-color-rgb-2); +} + +/* +.div-container { + margin-top: 15px; + box-sizing: border-box; + max-width: 1200px; + display: flex; + align-items: center; +} +*/ + +.clearboth { + clear: both; +} + +.spinnerstyle { + position: absolute; + top: 0; + left: 0; + bottom: 56px; + right: 0; + background: var(--spinner-bg-color); + z-index: 1; + display: flex; + align-items: top; /* Verticle align */ + justify-content: center; /* horizonal align */ +} + +/* label style */ +.mat-tab-label{ + background: var(--pale-border-color-rgb-3); + color: var(--red-label-color); + min-width: 60px!important; + +} + +:host ::ng-deep .mat-tab-label-active { + color: var(--regular-black-color); + font-weight: bold; + background-color: var(--pale-border-color-rgb-3); +} + +:host ::ng-deep .mat-ink-bar { + background-color: var(--primary-color, --orange-color-rgb) !important; + height: 2px; +} + +@media(max-width: 1175px) { + .structure-editor-actions-container { + flex-direction: column; + width: 100%; + } + + .editor-container { + width: 100%; + height: 390px; + } + + .search-actions { + flex-direction: row; + border-left: none; + padding-top: 10px; + padding-left: 0; + margin-left: 0; + + .one { + order: 1; + } + + .export-button { + margin-left: 7px; + } + + .two { + order: 2; + margin-left: auto; + display: flex; + padding-right: 10px; + + .similarity-cutoff { + margin: 0; + margin-top: 1px; + } + } + + .three { + order: 3; + } + } + + .action-button-container { + width: auto; + margin-top: 0; + align-self: flex-start; + + button { + width: auto; + border: 1px solid var(--regular-green-color); + } + } +} + +@media(max-width: 970px) { + .structure-editor-actions-container { + width: 100%; + overflow-x: auto; + } + + .editor-container, .search-actions { + min-width: 633px; + } + + .jsdraw { + .editor-container, .search-actions { + min-width: 710px; + } + } +} + +.tab-style { + position: relative; + z-index: 1; +} + +.search-button { + position: relative; + top: 30px; + z-index: 2; + width: 85px; + float: right; +} + +::ng-deep .row { + display: flex; + align-items: space-between; + flex-wrap: wrap; + margin-right: 125px; + width: 100%; +} + +::ng-deep .mat-expansion-panel { + margin-right: 40px; +} + +::ng-deep .col { + float: left; + width: 30%; + padding-right: 5px; + margin-right: 70px; + margin-left: 50px; +} + +::ng-deep .col:nth-child(n) { + margin-right: 22px; +} + +::ng-deep .col:nth-child(3n+1) { + clear: left; +} + +::ng-deep .panel-height { + height: 500px; + overflow: auto; + padding-right: 5px; +} + +::ng-deep .panel-height-scroll { + height: 800px; + overflow-y: scroll; +} + +/*******************************/ +/* Structure Editor CSS */ +/*******************************/ + +panel-style { + min-width: 100%; + max-width: 100%; + margin-top: 0px; + display: flex; + flex-direction: column; + box-sizing: border-box; + max-width: 1250px; +} + +.search-content-container { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 0px 5px 0 5px; +} + +.structure-editor-actions-container { + display: flex; + flex-direction: row; +} + +.editor-container { + width: 920px; + min-height: 460px; +} + +.ketcher { + .editor-container { + height: 460px; + } +} + +.jsdraw { + .editor-container { + min-height: 330px; + } +} + +.search-actions { + display: flex; + flex-direction: column; + align-items: flex-start; + padding-left: 15px; + margin-left: 10px; + border-left: 1px solid var(--textarea-light-border-color); + + .mat-slider-horizontal { + width: 100%; + } +} + +.similarity-cutoff { + margin-top: 20px; + margin-bottom: 20px; + text-align: center; +} + +.action-button-container { + margin-top: 20px; + align-self: flex-end; + width: 100%; + + button { + width: 100%; + } +} + +@media(max-width: 1175px) { + .structure-editor-actions-container { + flex-direction: column; + width: 100%; + } + + .editor-container { + width: 100%; + height: 390px; + } + + .search-actions { + flex-direction: row; + width: 100%; + } + + .two { + width: 50%; + display: flex; + flex-direction: column; + } + + .action-button-container { + width: 50%; + margin: auto; + margin-top: 20px; + } +} + +@media(max-width: 970px) { + .structure-editor-actions-container { + width: 100%; + overflow-x: auto; + } + + .editor-container, .search-actions { + min-width: 633px; + } + + .jsdraw { + .editor-container, .search-actions { + min-width: 710px; + } + } +} diff --git a/src/app/fda/advanced-search/advanced-search.component.spec.ts b/src/app/fda/advanced-search/advanced-search.component.spec.ts new file mode 100644 index 000000000..67a790e4f --- /dev/null +++ b/src/app/fda/advanced-search/advanced-search.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AdvancedSearchComponent } from './advanced-search.component'; + +describe('AdvancedSearchComponent', () => { + let component: AdvancedSearchComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ AdvancedSearchComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AdvancedSearchComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/fda/advanced-search/advanced-search.component.ts b/src/app/fda/advanced-search/advanced-search.component.ts new file mode 100644 index 000000000..ec65c4cb7 --- /dev/null +++ b/src/app/fda/advanced-search/advanced-search.component.ts @@ -0,0 +1,973 @@ +import { + Component, + OnInit, + Input, + Output, + EventEmitter, + PLATFORM_ID, + Inject, + OnDestroy, + ViewChild, + ElementRef, AfterViewInit +} from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { NavigationExtras, Router, ActivatedRoute } from '@angular/router'; +import { Location, formatNumber } from '@angular/common'; +import { Title } from '@angular/platform-browser'; +import { FormControl } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { MatTabChangeEvent } from '@angular/material/tabs'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { Subscription } from 'rxjs'; +import { transform } from 'lodash'; +import { environment } from '../../../environments/environment'; +import { ConfigService, LoadedComponents } from '@gsrs-core/config'; +import { UtilsService } from '@gsrs-core/utils'; +import { Ketcher } from 'ketcher-wrapper'; +import { JSDraw } from 'jsdraw-wrapper'; + +import { InterpretStructureResponse } from '@gsrs-core/structure/structure-post-response.model'; +import { StructureExportComponent } from '@gsrs-core/structure/structure-export/structure-export.component'; +import { StructureImportComponent } from '@gsrs-core/structure/structure-import/structure-import.component'; +import { StructureSearchComponent } from '@gsrs-core/structure-search/structure-search.component'; +import { StructureService } from '@gsrs-core/structure/structure.service'; +import { EditorImplementation } from '@gsrs-core/structure-editor/structure-editor-implementation.model'; + +import { Facet, FacetParam, FacetValue, FacetUpdateEvent, FacetsManagerService } from '@gsrs-core/facets-manager'; +import { DisplayFacet } from '@gsrs-core/facets-manager/display-facet'; +import { SubstanceDetail } from '@gsrs-core/substance/substance.model'; +import { QueryableSubstanceDictionary } from '@gsrs-core/guided-search/queryable-substance-dictionary.model'; +import { Editor } from '@gsrs-core/structure-editor/structure.editor.model'; +import { AdvancedQueryStatement } from './advanced-query-statement/advanced-query-statement.model'; +import { GoogleAnalyticsService } from '@gsrs-core/google-analytics/google-analytics.service'; +import { LoadingService } from '@gsrs-core/loading'; +import { SubstanceService } from '@gsrs-core/substance/substance.service'; +import { ApplicationService } from '../application/service/application.service'; +import { ProductService } from '../product/service/product.service'; +import { ClinicalTrialService } from '../clinical-trials/clinical-trial/clinical-trial.service'; +import { AdverseEventService } from '../adverse-event/service/adverseevent.service'; +import { AdvancedSearchService } from './service/advanced-search.service'; + +@Component({ + selector: 'app-advanced-search', + templateUrl: './advanced-search.component.html', + styleUrls: ['./advanced-search.component.scss'] +}) + +/* +export interface FacetValueAdvanced { + label: string; + count: number; + url: string; +} +*/ + +export class AdvancedSearchComponent implements OnInit, OnDestroy { + loadedComponents: LoadedComponents; + advancedSearchFacetDisplay = false; + query: string; + queryStatements: Array = []; + queryableSubstanceDict: QueryableSubstanceDictionary; + displayProperties: Array; + displayPropertiesCommon: Array; + facetViewControl = new FormControl(); + // editor: EditorImplementation; + // @Output() editorOnLoad = new EventEmitter(); + // @Output() loadedMolfile = new EventEmitter(); + private ketcher: Ketcher; + private jsdraw: JSDraw; + structureEditor: string; + anchorElement: HTMLAnchorElement; + smiles: string; + mol: string; + height = 0; + width = 0; + canvasToggle = true; + canvasMessage = ''; + tempClass = ''; + categoryOptions = [ + 'Substance', + 'Application', + 'Product', + 'Clinical Trial', + 'Adverse Event' + ]; + tabSelectedIndex = 0; + category = 'Substance'; + configName: 'substances'; + tabClicked = false; + @ViewChild('structure_canvas', { static: false }) myCanvas: ElementRef; + public context: CanvasRenderingContext2D; + public canvasCopy: HTMLCanvasElement; + private jsdrawScriptUrls = [ + `${environment.baseHref || ''}assets/dojo/dojo.js`, + `${environment.baseHref || ''}assets/jsdraw/Scilligence.JSDraw2.Pro.js`, + `${environment.baseHref || ''}assets/jsdraw/Scilligence.JSDraw2.Resources.js`, + `${environment.baseHref || ''}assets/jsdraw/JSDraw.extensions.js` + ]; + ketcherFilePath: string; + showSpinner = false; + dictionaryFileName: string; + private subscriptions: Array = []; + panelExpanded = false; + numFacetsLoaded = 0; + // queryHash: number; + queryStatementHashes: Array; + private privateFacetParams: FacetParam; + privateFacetParamsUrl: string; + facetKey = 'substances'; + rawFacets: Array = []; + rawFacetsSubstance: Array = []; + rawFacetsApplication: Array = []; + rawFacetsProduct: Array = []; + rawFacetsClinicalTrial: Array = []; + rawFacetsAdverseEventPt: Array = []; + rawFacetsAdverseEventDme: Array = []; + rawFacetsAdverseEventCvm: Array = []; + public displayFacets: Array = []; + facetViewCategorySelected = 'Default'; + facetViewCategory: Array = []; + substanceFacetsQuickSearch: Array = []; + applicationFacetsQuickSearch: Array = []; + productFacetsQuickSearch: Array = []; + clinicalTrialFacetsQuickSearch: Array = []; + navigationExtrasFacet: NavigationExtras = { + queryParams: {} + }; + queryFacet = ''; + queryDisplay = ''; + facetNameText = ''; + facetDisplayType = 'all'; + substanceFacetsDisplay = ['Record Status', 'Substance Class', 'Relationships', 'GInAS Tag']; + applicationFacetsDisplay = ['Center', 'Application Type', 'Application Status', 'Provenance (GSRS)']; + productFacetsDisplay = ['Provenance', 'Company Country', 'Product Type', 'Dosage Form Name']; + clinicalTrialFacetsDisplay = ['Intervention Type', 'Age Groups', 'Conditions', 'Study Types']; + + applicationFacetValue: Array = []; + + substances: Array; + applications: any; + products: any; + clinicalTrials: any; + adverseEventPt: any; + adverseEventDme: any; + adverseEventCvm: any; + + substanceCount = '0'; + applicationCount = '0'; + productCount = '0'; + clinicalTrialCount = '0'; + adverseEventPtCount = '0'; + adverseEventDmeCount = '0'; + adverseEventCvmCount = '0'; + + // Structure Editor + private editor: Editor; + private searchType: string; + _searchtype: string; + similarityCutoff?: number; + showSimilarityCutoff = false; + searchTypeControl = new FormControl(); + @ViewChild('contentContainer', { static: true }) contentContainer; + private overlayContainer: HTMLElement; + + constructor( + private http: HttpClient, + private router: Router, + private activatedRoute: ActivatedRoute, + private loadingService: LoadingService, + private advancedSearchService: AdvancedSearchService, + private adverseEventService: AdverseEventService, + private configService: ConfigService, + private utilitiesService: UtilsService, + private substanceService: SubstanceService, + public applicationService: ApplicationService, + public productService: ProductService, + private clinicalTrialService: ClinicalTrialService, + private facetManagerService: FacetsManagerService, + private gaService: GoogleAnalyticsService, + private titleService: Title, + private location: Location, + private dialog: MatDialog, + private structureService: StructureService + ) { + this.searchType = 'substructure'; + this._searchtype = 'substructure'; + } + + ngOnInit() { + this.loadingService.setLoading(true); + this.showSpinner = true; // Start progress spinner + + // Get configration values to hide/show Modules + this.loadedComponents = this.configService.configData.loadedComponents || null; + + // Get configuration value to hide/show Facets View in Advanced Search + this.advancedSearchFacetDisplay = this.configService.configData.advancedSearchFacetDisplay || false; + + this.titleService.setTitle(`Advanced Search`); + const advancedSearchHash = Number(this.activatedRoute.snapshot.queryParams['g-search-hash']) || null; + + // this.getStoredCriteriaFromLocalStorage(advancedSearchHash); + + // Get Stored Search Criteria + // For example: -681458537 + if (advancedSearchHash) { + const queryStatementHashesString = localStorage.getItem(advancedSearchHash.toString()); + // Found previous stored search criteria in local Storage + // For example: [-900654012,1838576297] + if (queryStatementHashesString != null) { + // For example: [-900654012,1838576297] + this.queryStatementHashes = JSON.parse(queryStatementHashesString); + // Get Category from Stored Cookies + if (this.queryStatementHashes[0] != null) { + const categoryStored = localStorage.getItem(this.queryStatementHashes[0].toString()); + if (categoryStored != null) { + this.category = categoryStored; + this.categoryOptions.forEach((cat, index) => { + if (cat != null) { + if (cat === categoryStored) { + this.tabSelectedIndex = index; + } + } + }); + this.queryStatementHashes.splice(0, 1); + } + } + } else { + // NOT FOUND: could not find this search criteria in local Storage. Use this search critieria + // with 'Manual Query Entry' in dropdown 'Search in Field'. + + } + } + + this.getBrowseSubstanceDetails(); + + if (this.loadedComponents) { + if (this.loadedComponents.applications) { + this.getBrowseApplicationDetails(); + } + if (this.loadedComponents.products) { + this.getBrowseProductDetails(); + } + if (this.loadedComponents.clinicaltrials) { + // this.getBrowseClinicalTrialDetails(); + } + if (this.loadedComponents.adverseevents) { + this.getBrowseAdverseEventPtDetails(); + this.getBrowseAdverseEventDmeDetails(); + this.getBrowseAdverseEventCvmDetails(); + } + } + + this.loadFileName(); + + this.loadFacetViewFromConfig(); + + this.showSpinner = false; // Stop progress spinner + this.loadingService.setLoading(false); + } + + ngOnDestroy() { + this.subscriptions.forEach(subscription => { + if (subscription) { + subscription.unsubscribe(); + } + }); + this.facetManagerService.unregisterFacetSearchHandler(); + } + + loadFacetViewFromConfig() { + this.facetViewControl.setValue(this.facetViewCategorySelected); + const facetConf = this.configService.configData.facets && this.configService.configData.facets['substances'] || {}; + facetConf['facetView'].forEach(categoryRow => { + const category = categoryRow['category']; + this.facetViewCategory.push(category); + }); + this.facetViewCategory.push('All'); + } + + getBrowseSubstanceDetails() { + const subscriptionBrowseSub = this.advancedSearchService.getSubstances( + 0, + 10, + null, + this.privateFacetParams, + ) + .subscribe(pagingResponse => { + this.substances = pagingResponse.content; + this.substanceCount = formatNumber(Number(pagingResponse.total), 'en-US', '1.0-0'); + if (pagingResponse.facets && pagingResponse.facets.length > 0) { + // this.substanceFacetsQuickSearch = this.populateFacets(pagingResponse.facets, this.substanceFacetsDisplay, 'browse-substance'); + + // this.rawFacets = this.populateFacets(pagingResponse.facets, this.substanceFacetsDisplay, 'browse-substance'); + this.rawFacetsSubstance = pagingResponse.facets; + this.rawFacets = pagingResponse.facets; + } + }); + this.subscriptions.push(subscriptionBrowseSub); + } + + getBrowseApplicationDetails() { + const subscriptionBrowseApp = this.advancedSearchService.getApplications( + 0, + 10, + null, + this.privateFacetParams, + ) + .subscribe(pagingResponse => { + this.applications = pagingResponse.content; + + // Convert Number with commas + this.applicationCount = formatNumber(Number(pagingResponse.total), 'en-US', '1.0-0'); + + if (pagingResponse.facets && pagingResponse.facets.length > 0) { + this.rawFacetsApplication = pagingResponse.facets; + } + }); + this.subscriptions.push(subscriptionBrowseApp); + } + + getBrowseProductDetails() { + const subscriptionBrowseProd = this.advancedSearchService.getProducts( + 0, + 10, + null, + this.privateFacetParams, + ) + .subscribe(pagingResponse => { + this.products = pagingResponse.content; + + this.productCount = formatNumber(Number(pagingResponse.total), 'en-US', '1.0-0'); + + if (pagingResponse.facets && pagingResponse.facets.length > 0) { + this.rawFacetsProduct = pagingResponse.facets; + // this.productFacetsQuickSearch = this.populateFacets(pagingResponse.facets, this.productFacetsDisplay, 'browse-products'); + } + }); + this.subscriptions.push(subscriptionBrowseProd); + } + + getBrowseClinicalTrialDetails() { + const subscriptionBrowseClinical = this.advancedSearchService.getClinicalTrials( + 0, + 10, + null, + this.privateFacetParams, + ) + .subscribe(pagingResponse => { + this.clinicalTrials = pagingResponse.content; + + this.clinicalTrialCount = formatNumber(Number(pagingResponse.total), 'en-US', '1.0-0'); + + if (pagingResponse.facets && pagingResponse.facets.length > 0) { + this.rawFacetsClinicalTrial = pagingResponse.facets; + // this.clinicalTrialFacetsQuickSearch = this.populateFacets(pagingResponse.facets + // , this.clinicalTrialFacetsDisplay, 'browse-clinical-trials'); + } + }); + this.subscriptions.push(subscriptionBrowseClinical); + } + + getBrowseAdverseEventPtDetails() { + const subscriptionBrowseAdvPt = this.adverseEventService.getAdverseEventPt( + null, + 0, + 10, + null, + this.privateFacetParams, + ) + .subscribe(pagingResponse => { + this.adverseEventPt = pagingResponse.content; + + this.adverseEventPtCount = formatNumber(Number(pagingResponse.total), 'en-US', '1.0-0'); + + if (pagingResponse.facets && pagingResponse.facets.length > 0) { + this.rawFacetsAdverseEventPt = pagingResponse.facets; + // this.productFacetsQuickSearch = this.populateFacets(pagingResponse.facets, this.productFacetsDisplay, 'browse-products'); + } + }); + this.subscriptions.push(subscriptionBrowseAdvPt); + } + + getBrowseAdverseEventDmeDetails() { + const subscriptionBrowseAdvDme = this.adverseEventService.getAdverseEventDme( + null, + 0, + 10, + null, + this.privateFacetParams, + ) + .subscribe(pagingResponse => { + this.adverseEventDme = pagingResponse.content; + + this.adverseEventDmeCount = formatNumber(Number(pagingResponse.total), 'en-US', '1.0-0'); + + if (pagingResponse.facets && pagingResponse.facets.length > 0) { + this.rawFacetsAdverseEventDme = pagingResponse.facets; + // this.productFacetsQuickSearch = this.populateFacets(pagingResponse.facets, this.productFacetsDisplay, 'browse-products'); + } + }); + this.subscriptions.push(subscriptionBrowseAdvDme); + } + + getBrowseAdverseEventCvmDetails() { + const subscriptionBrowseAdvCvm = this.adverseEventService.getAdverseEventCvm( + null, + 0, + 10, + null, + this.privateFacetParams, + ) + .subscribe(pagingResponse => { + this.adverseEventCvm = pagingResponse.content; + + this.adverseEventCvmCount = formatNumber(Number(pagingResponse.total), 'en-US', '1.0-0'); + + if (pagingResponse.facets && pagingResponse.facets.length > 0) { + this.rawFacetsAdverseEventCvm = pagingResponse.facets; + // this.productFacetsQuickSearch = this.populateFacets(pagingResponse.facets, this.productFacetsDisplay, 'browse-products'); + } + }); + this.subscriptions.push(subscriptionBrowseAdvCvm); + } + + + /* + private populateFacets(rawFacets: Array, facetDisplay: Array, browse: string): Array { + const facetQuickSearch: Array = []; + if (rawFacets && facetDisplay) { + const facetsCopy = rawFacets.slice(); + facetDisplay.forEach(facetKey => { + for (let facetIndex = 0; facetIndex < facetsCopy.length; facetIndex++) { + if (facetKey === facetsCopy[facetIndex].name) { + const facetValues = facetsCopy[facetIndex].values; + for (let valueIndex = 0; valueIndex < facetValues.length; valueIndex++) { + } + facetQuickSearch.push(facetsCopy[facetIndex]); + } + } + }); + } + return facetQuickSearch; + } + */ + + tabSelectedUpdated(event: MatTabChangeEvent) { + if (event) { + // When clicked on the tab or switching between tabs, save the previous Criteria in Local Storage to retrieve + // LATER: this.storeCriteriaInLocalStorage(); + + // this.tabSelectedIndex = event.index; + this.category = event.tab.textLabel; + if (this.category) { + this.tabClicked = true; + + // Create a HashCode for current tabSelectedIndex, and restore the criteria from Local Storage + // LATER: const tabSelectedHash = this.utilitiesService.hashCode(this.tabSelectedIndex); + // LATER: this.getStoredCriteriaFromLocalStorage(tabSelectedHash); + + // Load File based on Category selected on tab (Substance, Application, etc) + this.loadFileName(); + } + } + } + + private loadFileName() { + if (this.category) { + this.query = ''; + this.facetDisplayType = 'default'; + this.facetManagerService.clearSelections(); + this.facetManagerService.unregisterFacetSearchHandler(); + + if (this.category === 'Substance') { + this.dictionaryFileName = 'substance_dictionary.json'; + this.facetManagerService.registerGetFacetsHandler(this.substanceService.getSubstanceFacets); + this.rawFacets = this.rawFacetsSubstance; + this.facetKey = 'substances'; + this.facetDisplayType = 'facetView'; + } else if (this.category === 'Application') { + this.dictionaryFileName = 'application_dictionary.json'; + this.facetManagerService.registerGetFacetsHandler(this.applicationService.getApplicationFacets); + this.rawFacets = this.rawFacetsApplication; + this.facetKey = 'applications'; + } else if (this.category === 'Product') { + this.dictionaryFileName = 'productall_dictionary.json'; + this.facetManagerService.registerGetFacetsHandler(this.productService.getProductFacets); + this.rawFacets = this.rawFacetsProduct; + this.facetKey = 'products'; + } else if (this.category === 'Clinical Trial') { + this.dictionaryFileName = 'ctus_dictionary.json'; + // this.facetManagerService.registerGetFacetsHandler(this.clinicalTrialService.getClinicalTrialsFacets); + // this.rawFacets = this.rawFacetsClinicalTrial; + this.facetKey = 'ctclinicaltrial'; + } else if (this.category === 'Adverse Event') { + this.dictionaryFileName = 'adverseevent_dictionary.json'; + this.facetManagerService.registerGetFacetsHandler(this.adverseEventService.getAdverseEventPtFacets); + this.rawFacets = this.rawFacetsAdverseEventPt; + this.facetKey = 'adverseeventpt'; + // this.facetManagerService.registerGetFacetsHandler(this.adverseEventService.getAdverseEventDmeFacets); + // this.rawFacets = this.rawFacetsAdverseEventDme; + // this.facetKey = 'adverseeventDme'; + // this.rawFacets.splice(0, this.rawFacets.length); + } + } + this.getSearchField(); + } + + getSearchField() { + this.http.get(`${this.configService.environment.baseHref}assets/data/` + this.dictionaryFileName) + // this.http.get(`${this.configService.environment.baseHref}assets/data/substance_dictionary.json`) + .subscribe((response: QueryableSubstanceDictionary) => { + + response['All'] = { + lucenePath: '', + description: 'All substance fields', + type: 'string', + cvDomain: '' + }; + this.queryableSubstanceDict = response; + + const displayProperties = ['All']; + const displayPropertiesCommon = ['All', 'Manual Query Entry']; + Object.keys(this.queryableSubstanceDict).forEach(key => { + displayProperties.push(key); + if (this.queryableSubstanceDict[key].priority != null) { + displayPropertiesCommon.push(key); + } + }); + this.displayProperties = displayProperties; + this.displayPropertiesCommon = displayPropertiesCommon; + + // Get queryStatement from previous/stored local storage + if (this.queryStatementHashes != null) { + if (this.tabClicked === false) { + // [1838576297] + this.queryStatementHashes.forEach(queryStatementHash => { + this.queryStatements.push({ queryHash: queryStatementHash }); + }); + } + } + + if (this.queryStatements.length === 0) { + this.queryStatements.push({}); + } + // } + }); + } + + queryUpdated(queryStatement: AdvancedQueryStatement, index: number) { + setTimeout(() => { + Object.keys(queryStatement).forEach(key => { + this.queryStatements[index][key] = queryStatement[key]; + }); + this.query = ''; + this.query = this.queryStatements.map(statement => statement.query).join(' ').trim(); + + // Combine query and facetQuery + this.facetQueryConstruct(); + // this.queryDisplay = this.query + this.queryFacet; + }); + } + + addQueryStatement(): void { + this.queryStatements.push({ + condition: '', + queryableProperty: 'All', + command: '' + }); + } + + removeQueryStatement(index: number): void { + this.queryStatements.splice(index, 1); + this.query = this.queryStatements.map(statement => statement.query).join(' '); + + // Combine query and facetQuery + this.queryDisplay = this.query + this.queryFacet; + } + + togglePanelExpand() { + this.showSpinner = true; // Start progress spinner + this.panelExpanded = !this.panelExpanded; + this.showSpinner = false; // Stop progress spinner + } + + // for facets + facetsLoaded(numFacetsLoaded: number) { + this.numFacetsLoaded = numFacetsLoaded; + } + + facetViewChange(event): void { + this.facetViewCategorySelected = event.value; + } + + clearSearch(): void { + + // const eventLabel = environment.isAnalyticsPrivate ? 'search term' : this.privateSearchTerm; + // this.gaService.sendEvent('applicationFiltering', 'icon-button:clear-search', eventLabel); + + // this.privateSearchTerm = ''; + // this.pageIndex = 0; + // this.pageSize = 10; + + // this.populateUrlQueryParameters(); + // this.searchApplications(); + } + + clearFilters(): void { + // for facets + this.displayFacets.forEach(displayFacet => { + displayFacet.removeFacet(displayFacet.type, displayFacet.bool, displayFacet.val); + }); + this.clearSearch(); + + this.facetManagerService.clearSelections(); + } + + facetQueryConstruct(): void { + this.queryFacet = ''; + this.displayFacets.forEach((element, index) => { + if (element) { + const boolQuery = (this.query && index === 0) ? ' AND ' : ''; + const bool = (this.queryFacet) ? ' AND ' : ''; + this.queryFacet = boolQuery + this.queryFacet + bool + element.type + ':' + element.val; + + } + }); + // Combine query and facetQuery + this.queryDisplay = this.query + this.queryFacet; + } + + // for facets + facetsParamsUpdated(facetsUpdateEvent: FacetUpdateEvent): void { + + this.privateFacetParams = facetsUpdateEvent.facetParam; + this.displayFacets = facetsUpdateEvent.displayFacets; + + this.populateUrlQueryParameters(); + this.facetQueryConstruct(); + + let applyReady = false; + for (const key of Object.keys(this.privateFacetParams)) { + if ((this.privateFacetParams[key].isUpdated === false) && (this.displayFacets.length > 0)) { + applyReady = true; + } else { + applyReady = false; + break; + } + } + + // When Apply Button is clicked, Do Search] + if (applyReady === true) { // && this.privateFacetParamsUrl) { + this.setFacetLocationUrl(); + // Search Button, Search Browse + this.processSearch(); + } + } + private encodeValue(facetValue: string) { + let encFV = facetValue.replace('!', '!@'); + encFV = encFV.replace(/[.]/g, '!.'); + encFV = encFV.replace(/[\+]/g, '!+'); + encFV = encFV.replace(/[,]/g, '!,'); + encFV = encFV.replace(/[\*]/g, '!*'); + return encFV; + } + + populateUrlQueryParameters(deprecated?: boolean): void { + const catArr = []; + let facetString = ''; + for (const key of Object.keys(this.privateFacetParams)) { + if (this.privateFacetParams[key] !== undefined && + this.privateFacetParams[key].hasSelections === true && + !(this.privateFacetParams[key] !== undefined && + this.privateFacetParams[key].params && + this.privateFacetParams[key].params['Deprecated'] === true)) { + const cat = this.privateFacetParams[key]; + const valArr = []; + for (const subkey of Object.keys(cat.params)) { + if (typeof cat.params[subkey] === 'boolean') { + valArr.push(this.encodeValue(subkey) + '.' + cat.params[subkey]); + } + } + if (cat.isAllMatch) { + valArr.push('is_all_match.true'); + } + catArr.push(key + '*' + valArr.join('+')); + const paramsString = JSON.stringify(this.privateFacetParams[key].params); + // const newHash = this.utilsService.hashCode(paramsString, this.privateFacetParams[key].isAllMatch.toString()); + // this.privateFacetParams[key].currentStateHash = newHash; + // this.privateFacetParams[key].isUpdated = false; + } + } + facetString = catArr.join(','); + if (facetString !== '') { + this.navigationExtrasFacet.queryParams['facets'] = facetString; + } + /* + if (this.showDeprecated) { + navigationExtras.queryParams['showDeprecated'] = 'true'; + } else { + navigationExtras.queryParams['showDeprecated'] = null; + } + + this.previousState.push(this.router.url); + */ + + /* + setTimeout(() => { + const urlTree = this.router.createUrlTree([], { + queryParams: this.navigationExtrasFacet.queryParams, + queryParamsHandling: 'merge', + preserveFragment: true + }); + this.location.go(urlTree.toString()); + }); + */ + } + + setFacetLocationUrl() { + setTimeout(() => { + const urlTree = this.router.createUrlTree([], { + queryParams: this.navigationExtrasFacet.queryParams, + queryParamsHandling: 'merge', + preserveFragment: true + }); + this.location.go(urlTree.toString()); + }); + } + + processSearch(): void { + // this.storeCriteriaInLocalStorage(); + + const queryStatementHashes = []; + + // Store in cookies, Category tab (Substance, Application, etc) + const categoryHash = this.utilitiesService.hashCode(this.category); + localStorage.setItem(categoryHash.toString(), this.category); + queryStatementHashes.push(categoryHash); + + // Generate Hash Code for Query Statement + this.queryStatements.forEach(queryStatement => { + const queryStatementString = JSON.stringify(queryStatement); + const hash = this.utilitiesService.hashCode(queryStatementString); + + // Store in cookies, Each Query Statement is stored in separate hash + localStorage.setItem(hash.toString(), queryStatementString); + + // Push Query Statements Hashes in Array + queryStatementHashes.push(hash); + }); + + // Generate Hash Code for Query + const queryHash = this.utilitiesService.hashCode(this.query); + + const queryStatementHashesString = JSON.stringify(queryStatementHashes); + + // Store in cookies, store in Query Hash - Query Statement Hashes Array + localStorage.setItem(queryHash.toString(), queryStatementHashesString); + + const navigationExtras: NavigationExtras = { + queryParams: {} + }; + + if ((this.query) || (Object.keys(this.privateFacetParams).length > 0)) { + + if (this.query) { + if (this.category === 'Clinical Trial') { + navigationExtras.queryParams['searchTerm'] = this.query; + } else { + navigationExtras.queryParams['search'] = this.query; + navigationExtras.queryParams['g-search-hash'] = queryHash.toString(); + } + } else if ((this.privateFacetParams && Object.keys(this.privateFacetParams).length > 0)) { + navigationExtras.queryParams['facets'] = this.navigationExtrasFacet.queryParams['facets']; + } + + // const navigationExtrasClinical: NavigationExtras = { + // queryParams: this.query ? { 'searchTerm': this.query, 'facets': this.privateFacetParamsUrl } : null + // }; + + const navigationExtras2: NavigationExtras = { + queryParams: { + 'g-search-hash': queryHash.toString() + } + }; + + // this is a test of the push state needed + // to keep the back button working as desired + window.history.pushState({}, 'Advanced Search', '/advanced-search' + + '?g-search-hash=' + navigationExtras2.queryParams['g-search-hash']); + + /*****************************************************************************/ + /* STRUCTURE SEARCH */ + /*****************************************************************************/ + + if (this.category === 'Substance') { + const mol = this.editor.getMolfile(); + if (mol && mol.length > 72) { + this.structureService.interpretStructure(mol).subscribe((response: InterpretStructureResponse) => { + const eventLabel = !environment.isAnalyticsPrivate && response.structure.smiles || 'structure search term'; + // this.gaService.sendEvent('structureSearch', 'button:search', eventLabel); + + const navigationExtrasStructure: NavigationExtras = { + queryParams: {} + }; + + const structureSearchTerm = response.structure.id; + const smiles = response.structure.smiles; + + navigationExtras.queryParams['structure_search'] = structureSearchTerm || null; + navigationExtras.queryParams['type'] = this.searchType || null; + + navigationExtras2.queryParams['structure'] = structureSearchTerm; + navigationExtras2.queryParams['type'] = this.searchType || null; + + if (this.searchType === 'similarity') { + navigationExtras.queryParams['cutoff'] = this.similarityCutoff || 0; + navigationExtras2.queryParams['cutoff'] = this.similarityCutoff || 0; + } + + if (smiles != null) { + navigationExtras.queryParams['smiles'] = smiles; + } + + // this is a test of the push state needed + // to keep the back button working as desired + window.history.pushState({}, 'Structure Search', '/structure-search' + + '?structure=' + navigationExtras2.queryParams['structure'] + + '&type=' + navigationExtras2.queryParams['type'] + + '&cutoff=' + navigationExtras2.queryParams['cutoff']); + + this.router.navigate(['/browse-substance'], navigationExtras); + }, () => { }); + + } + /********* STRUCTURE QUERY **********/ + + // If no structure search, do this + else { + this.router.navigate(['/browse-substance'], navigationExtras); + // } + } + } + else if (this.category === 'Application') { + this.router.navigate(['/browse-applications'], navigationExtras); + } else if (this.category === 'Product') { + this.router.navigate(['/browse-products'], navigationExtras); + } else if (this.category === 'Clinical Trial') { + this.router.navigate(['/browse-clinical-trials'], navigationExtras); + } else if (this.category === 'Adverse Event') { + this.router.navigate(['/browse-adverse-events'], navigationExtras); + } else { + this.router.navigate(['/browse-substance'], navigationExtras); + } + } else { + alert('Please select any criteria to search'); + } + } + + processSearchOut(): void { + this.processSearch(); + } + + /***************************************/ + /*** STRUCTURE FUNCTIONS BELOW *****/ + + editorOnLoad(editor: Editor): void { + this.loadingService.setLoading(false); + this.editor = editor; + setTimeout(() => { + this.activatedRoute + .queryParamMap + .subscribe(params => { + if (params.has('structure')) { + this.structureService.getMolfile(params.get('structure')).subscribe(molfile => { + this.editor.setMolecule(molfile); + }); + } + if (params.has('type')) { + this.searchType = params.get('type'); + } + + if (this.searchType === 'similarity') { + this.showSimilarityCutoff = true; + this.similarityCutoff = params.has('cutoff') && Number(params.get('cutoff')) || 0.8; + } + + this.searchTypeControl.setValue(this.searchType); + }); + }); + } + + searchTypeSelected(event): void { + this.searchType = event.value; + + this.gaService.sendEvent('structureSearch', 'select:search-type', this.searchType); + + if (this.searchType === 'similarity') { + this.showSimilarityCutoff = true; + this.similarityCutoff = 0.8; + } else { + this.showSimilarityCutoff = false; + } + } + + molvecUpdate(mol: any) { + this.editor.setMolecule(mol); + } + + openStructureImportDialog(): void { + this.gaService.sendEvent('structureSearch', 'button:import', 'import structure'); + const dialogRef = this.dialog.open(StructureImportComponent, { + height: 'auto', + width: '650px', + data: {} + }); + // this.overlayContainer.style.zIndex = '1002'; + + dialogRef.afterClosed().subscribe((structurePostResponse?: InterpretStructureResponse) => { + // this.overlayContainer.style.zIndex = null; + if (structurePostResponse && structurePostResponse.structure && structurePostResponse.structure.molfile) { + this.editor.setMolecule(structurePostResponse.structure.molfile); + } + }, () => { + this.overlayContainer.style.zIndex = null; + }); + } + + openStructureExportDialog(): void { + this.gaService.sendEvent('structureSearch', 'button:export', 'export structure'); + const dialogRef = this.dialog.open(StructureExportComponent, { + height: 'auto', + width: '650px', + data: { + molfile: this.editor.getMolfile(), + smiles: this.editor.getSmiles() + } + }); + this.overlayContainer.style.zIndex = '1002'; + dialogRef.afterClosed().subscribe(() => { + this.overlayContainer.style.zIndex = null; + }, () => { + this.overlayContainer.style.zIndex = null; + }); + } + + searchCutoffChanged(event): void { + this.similarityCutoff = event.value; + this.gaService.sendEvent('structureSearch', 'slider', 'similarity-cutoff', this.similarityCutoff); + } + + get _editor(): Editor { + return this.editor; + } + + get _searchType(): string { + return this.searchType; + } + + nameResolved(molfile: string): void { + this.editor.setMolecule(molfile); + } + +} diff --git a/src/app/fda/advanced-search/advanced-search.module.ts b/src/app/fda/advanced-search/advanced-search.module.ts new file mode 100644 index 000000000..0f68ac473 --- /dev/null +++ b/src/app/fda/advanced-search/advanced-search.module.ts @@ -0,0 +1,117 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Router, Routes, RouterModule } from '@angular/router'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { AdvancedSearchService } from './service/advanced-search.service'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { MatSidenavModule } from '@angular/material/sidenav'; +import { MatCardModule } from '@angular/material/card'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatBadgeModule } from '@angular/material/badge'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatTableModule } from '@angular/material/table'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatSelectModule } from '@angular/material/select'; +import { MatSliderModule } from '@angular/material/slider'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatListModule } from '@angular/material/list'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatButtonToggleModule } from '@angular/material/button-toggle'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { OverlayModule } from '@angular/cdk/overlay'; +import { MatBottomSheetModule } from '@angular/material/bottom-sheet'; +import { FacetsManagerModule } from '@gsrs-core/facets-manager'; +import { SubstanceTextSearchService } from '@gsrs-core/substance-text-search/substance-text-search.service'; +import { SubstanceSearchSelectorModule } from '../substance-search-select/substance-search-selector.module'; +import { SubstanceSelectorModule } from '@gsrs-core/substance-selector/substance-selector.module'; +import { SubstanceFormModule } from '../../core/substance-form/substance-form.module'; +import { AdvancedSearchComponent } from '../advanced-search/advanced-search.component'; +import { AdvancedQueryStatementComponent } from './advanced-query-statement/advanced-query-statement.component'; +import { StructureEditorModule } from '@gsrs-core/structure-editor/structure-editor.module'; +import { NameResolverModule } from '@gsrs-core/name-resolver/name-resolver.module'; +import { MatDatepickerModule } from '@angular/material/datepicker'; +import { FacetDisplayPipe } from '@gsrs-core/facets-manager/facet-display.pipe'; +import { StructureModule } from '@gsrs-core/structure/structure.module'; + +const advSearchRoutes: Routes = [ + { + path: 'advanced-search', + component: AdvancedSearchComponent + } +]; + +@NgModule({ + imports: [ + CommonModule, + RouterModule.forChild(advSearchRoutes), + MatToolbarModule, + MatSidenavModule, + MatCardModule, + MatAutocompleteModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + MatIconModule, + MatChipsModule, + MatBadgeModule, + MatExpansionModule, + MatCheckboxModule, + MatTableModule, + MatPaginatorModule, + MatSelectModule, + MatSliderModule, + MatDialogModule, + MatListModule, + MatMenuModule, + MatButtonToggleModule, + MatTooltipModule, + MatTabsModule, + MatBottomSheetModule, + MatProgressSpinnerModule, + MatDatepickerModule, + FormsModule, + ReactiveFormsModule, + OverlayModule, + SubstanceSearchSelectorModule, + SubstanceFormModule, + FacetsManagerModule, + StructureEditorModule, + NameResolverModule, + StructureModule + ], + declarations: [ + AdvancedSearchComponent, + AdvancedQueryStatementComponent + ], + exports: [ + AdvancedSearchComponent + ] +}) + +export class AdvancedSearchModule { + constructor(router: Router) { + advSearchRoutes.forEach(route => { + router.config[0].children.push(route); + }); + } + + static forRoot(): ModuleWithProviders { + return { + ngModule: AdvancedSearchModule, + providers: [ + AdvancedSearchService + ] + }; + } + +} + + diff --git a/src/app/fda/advanced-search/service/advanced-search.service.spec.ts b/src/app/fda/advanced-search/service/advanced-search.service.spec.ts new file mode 100644 index 000000000..90add6ebb --- /dev/null +++ b/src/app/fda/advanced-search/service/advanced-search.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { AdvancedSearchService } from './advanced-search.service'; + +describe('AdvancedSearchService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [AdvancedSearchService] + }); + }); + + it('should be created', inject([AdvancedSearchService], (service: AdvancedSearchService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/app/fda/advanced-search/service/advanced-search.service.ts b/src/app/fda/advanced-search/service/advanced-search.service.ts new file mode 100644 index 000000000..80ab1335d --- /dev/null +++ b/src/app/fda/advanced-search/service/advanced-search.service.ts @@ -0,0 +1,219 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpParams } from '@angular/common/http'; +import { Observable, } from 'rxjs'; +import { ConfigService } from '@gsrs-core/config'; +import { BaseHttpService } from '@gsrs-core/base'; +import { PagingResponse } from '@gsrs-core/utils'; +import { FacetParam, FacetHttpParams, FacetQueryResponse } from '@gsrs-core/facets-manager'; +import { SubstanceDetail, SubstanceName, SubstanceCode } from '@gsrs-core/substance/substance.model'; +import { ProductAll } from '../../product/model/product.model'; +import { ClinicalTrial } from '../../clinical-trials/clinical-trial/clinical-trial.model'; +// import { map, switchMap, tap } from 'rxjs/operators'; +// import { Facet } from '@gsrs-core/facets-manager'; +import { Application, ApplicationIngredient } from '../../application/model/application.model'; + +@Injectable( + { providedIn: 'root' } +) + +export class AdvancedSearchService extends BaseHttpService { + + totalRecords: 0; + baseHref: ''; + + apiBaseUrlWithSubstanceEntityUrl = this.configService.configData.apiBaseUrl || '' + 'api/v1/substances' + '/'; + apiBaseUrlWithApplicationEntityUrl = this.configService.configData.apiBaseUrl + 'api/v1/applications' + '/'; + apiBaseUrlWithApplicationAllEntityUrl = this.configService.configData.apiBaseUrl + 'api/v1/applicationsall' + '/'; + apiBaseUrlWithApplicationDarrtsEntityUrl = this.configService.configData.apiBaseUrl + 'api/v1/applicationsdarrts' + '/'; + + apiBaseUrlWithProductEntityUrl = this.configService.configData.apiBaseUrl + 'api/v1/products' + '/'; + apiBaseUrlWithProductBrowseEntityUrl = this.configService.configData.apiBaseUrl + 'api/v1/productsall' + '/'; + apiBaseUrlWithProductElistEntityUrl = this.configService.configData.apiBaseUrl + 'api/v1/productselist' + '/'; + + apiBaseUrlWithClinicalTrialEntityUrl = this.configService.configData.apiBaseUrl + 'api/v1/clinicaltrialsus' + '/'; + + apiBaseUrlWithEntityPtContext = this.configService.configData.apiBaseUrl + 'api/v1/adverseeventpt' + '/'; + apiBaseUrlWithEntityDmeContext = this.configService.configData.apiBaseUrl + 'api/v1/adverseeventdme' + '/'; + apiBaseUrlWithEntityCvmContext = this.configService.configData.apiBaseUrl + 'api/v1/adverseeventcvm' + '/'; + + + constructor( + public http: HttpClient, + public configService: ConfigService, + ) { + super(configService); + } + + getBaseHref(): string { + return this.configService.environment.baseHref; + } + + getSubstanceCount(): Observable { + const url = `${this.configService.configData.apiBaseUrl}api/v1/substances/@count`; + return this.http.get(url); + } + + getApplicationCount(): Observable { + const url = this.apiBaseUrlWithApplicationEntityUrl + `@count`; + return this.http.get(url); + } + + getProductCount(): Observable { + const url = this.apiBaseUrlWithProductBrowseEntityUrl + `@count`; + return this.http.get(url); + } + + getClinicalTrialCount(): Observable { + const url = this.apiBaseUrlWithClinicalTrialEntityUrl + `@count`; + return this.http.get(url); + } + + getSubstances( + skip: number = 0, + pageSize: number = 10, + searchTerm?: string, + facets?: FacetParam + ): Observable> { + let params = new FacetHttpParams(); + params = params.append('skip', skip.toString()); + params = params.append('top', '1'); // setting top=1, faster result, no content + params = params.append('view','key'); // setting view=key, faster result, no content + if (searchTerm !== null && searchTerm !== '') { + params = params.append('q', searchTerm); + } + params = params.appendFacetParams(facets); + + const url = `${this.apiBaseUrl}substances/search`; + const options = { + params: params + }; + + return this.http.get>(url, options); + } + + getApplications( + skip: number = 0, + pageSize: number = 10, + searchTerm?: string, + facets?: FacetParam + ): Observable> { + let params = new FacetHttpParams(); + params = params.append('skip', skip.toString()); + params = params.append('top', '1'); // setting top=1, faster result, no content + params = params.append('view','key'); // setting view=key, faster result, no content + if (searchTerm !== null && searchTerm !== '') { + params = params.append('q', searchTerm); + } + params = params.appendFacetParams(facets); + + const url = this.apiBaseUrlWithApplicationEntityUrl + 'search'; + const options = { + params: params + }; + + return this.http.get>(url, options); + } + + getProducts( + skip: number = 0, + pageSize: number = 10, + searchTerm?: string, + facets?: FacetParam + ): Observable> { + let params = new FacetHttpParams(); + params = params.append('skip', skip.toString()); + params = params.append('top', '1'); // setting top=1, faster result, no content + params = params.append('view','key'); // setting view=key, faster result, no content + if (searchTerm !== null && searchTerm !== '') { + params = params.append('q', searchTerm); + } + params = params.appendFacetParams(facets); + + const url = this.apiBaseUrlWithProductBrowseEntityUrl + 'search'; + const options = { + params: params + }; + + return this.http.get>(url, options); + } + + getClinicalTrials( + skip: number = 0, + pageSize: number = 10, + searchTerm?: string, + facets?: FacetParam + ): Observable> { + let params = new FacetHttpParams(); + params = params.append('skip', skip.toString()); + params = params.append('top', '1'); // setting top=1, faster result, no content + params = params.append('view','key'); // setting view=key, faster result, no content + if (searchTerm !== null && searchTerm !== '') { + params = params.append('q', searchTerm); + } + params = params.appendFacetParams(facets); + + const url = this.apiBaseUrlWithClinicalTrialEntityUrl + 'search'; + const options = { + params: params + }; + + return this.http.get>(url, options); + } + + getTypeAheadSearchText(category: string, fieldName: string, searchTerm: string): Observable { + let url: string; + let entityUrl: string; + let slash = '/'; + if (category) { + if (category === 'Substance') { + entityUrl = `${this.configService.configData.apiBaseUrl}api/v1/`; + } + if (category === 'Application') { + entityUrl = this.apiBaseUrlWithApplicationEntityUrl; + } + if (category === 'Product') { + entityUrl = this.apiBaseUrlWithProductBrowseEntityUrl; + } + if (category === 'Clinical Trial') { + entityUrl = this.apiBaseUrlWithClinicalTrialEntityUrl; + } + if (category === 'Adverse Event') { + if (fieldName) { + if (fieldName === 'Ingredient_Name') { + entityUrl = `${this.configService.configData.apiBaseUrl}api/v1/`; + fieldName = 'Name'; + } + if (fieldName === 'PT_Term') { + entityUrl = this.apiBaseUrlWithEntityPtContext; + } + if (fieldName === 'Prim_SOC') { + entityUrl = this.apiBaseUrlWithEntityPtContext; + } + if (fieldName === 'DME_Reactions') { + entityUrl = this.apiBaseUrlWithEntityDmeContext; + } + if (fieldName === 'PTTerm_Meddra') { + entityUrl = this.apiBaseUrlWithEntityDmeContext; + } + if (fieldName === 'Adverse_Event') { + entityUrl = this.apiBaseUrlWithEntityCvmContext; + } + if (fieldName === 'Species') { + entityUrl = this.apiBaseUrlWithEntityCvmContext; + } + } + } + + url = entityUrl + 'suggest' + slash + fieldName + '?q=' + searchTerm; + } + return this.http.get(url); + } + + /* + getSearchSuggestions(searchTerm: string): Observable { + const url = `${(this.configService.configData && this.configService.configData.apiBaseUrl) || '/' }api/v1/`; + + return this.http.get(url + 'suggest?q=' + searchTerm); + } + */ +} diff --git a/src/app/fda/adverse-event/activate-adverse-events.component.ts b/src/app/fda/adverse-event/activate-adverse-events.component.ts new file mode 100644 index 000000000..b228e2745 --- /dev/null +++ b/src/app/fda/adverse-event/activate-adverse-events.component.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@angular/core'; +import { CanActivate, Router, ActivatedRouteSnapshot, NavigationExtras, RouterStateSnapshot, UrlTree } from '@angular/router'; +import { ConfigService } from '@gsrs-core/config'; +import { Observable } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class ActivateAdverseeventsComponent implements CanActivate { + constructor( + private configService: ConfigService, + private router: Router + ) {} + canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot + ): Observable | Promise | (boolean | UrlTree) { + return new Observable(observer => { + const loadedComponents = this.configService.configData.loadedComponents || null; + if (loadedComponents && loadedComponents.adverseevents) { + observer.next(true); + observer.complete(); + } else { + observer.next(this.router.parseUrl('/home')); + observer.complete(); + } + }); + + } +} diff --git a/src/app/fda/adverse-event/adverse-event-text-search/adverse-event-text-search.component.html b/src/app/fda/adverse-event/adverse-event-text-search/adverse-event-text-search.component.html new file mode 100644 index 000000000..16caf24cf --- /dev/null +++ b/src/app/fda/adverse-event/adverse-event-text-search/adverse-event-text-search.component.html @@ -0,0 +1,32 @@ +
    + + + + {{hintMessage}} + {{errorMessage}} + +
    +
    {{field['display']}}
    + +
    {{suggestion.key}}
    +
    +
    + +
    +
    + + + +
    diff --git a/src/app/fda/adverse-event/adverse-event-text-search/adverse-event-text-search.component.scss b/src/app/fda/adverse-event/adverse-event-text-search/adverse-event-text-search.component.scss new file mode 100644 index 000000000..79481b3b1 --- /dev/null +++ b/src/app/fda/adverse-event/adverse-event-text-search/adverse-event-text-search.component.scss @@ -0,0 +1,196 @@ +form { + display: flex; + align-items: center; + justify-content: flex-end; +} + +.search-form-field { + + .mat-input-element { + font-size: 16px; + } + +} + +.substance-suggestion-field { + padding: 8px 6px; + color: var(--regular-white-color); + overflow: hidden; +} + +.field-styling { + padding: 5px; + border-radius: 5px; + background-color: var(--link-primary-color); + color: var(--regular-white-color); +} + +.suggestion-styling { + overflow-x: hidden; + white-space: nowrap !important; + text-overflow: ellipsis; +} + +.overflow-comp { + visibility: hidden; + max-width: 2000px; + height: 1px; + margin: 0px; + padding: 0px; +} + + +.search-container { + width: 100%; + + mat-form-field { + overflow: hidden; + width: 100%; + } +} + +.close-button { + display: none; + width: 0; + transition: all 250ms linear; +} + +@media(min-width: 750px) { + + .activate-search-button { + display: none; + } +} + +@media(max-width: 1024px) { + + .search-container { + mat-form-field { + width: 0; + } + } + + .search-button { + display: none; + } + + .active-search { + + &.search-container { + position: fixed; + top: 0; + right: 0; + width: 100%; + left: 0; + height: 64px; + padding-right: 210px; + padding-left: 16px; + background-color: var(--primary-color); + overflow: hidden; + + form { + max-height: 100%; + } + + .mat-form-field { + width: 100%; + flex-flow: 1; + animation-name: expandWidth; + animation-duration: 300ms; + } + + .search-button { + display: inline-block; + } + + .activate-search-button { + display: none; + } + + .close-button { + display: inline-block; + width: auto; + } + } + } + + .form-search { + width: 100% !important; + } + + .active-form-search { + &.search-container { + position: relative; + // top: 0; + // right: 0; + width: 100%; + // left: 0; + height: 48px; + padding-right: 0px; + padding-left: 0px; + background-color: var(--regular-white-color); + overflow: hidden; + mat-form-field { + width: 100% !important; + } + + form { + max-height: 100%; + } + + .mat-form-field { + width: 100%; + flex-flow: 1; + animation-name: expandWidth; + animation-duration: 300ms; + } + + .search-button { + display: inline-block; + } + + .activate-search-button { + display: inline-block; + } + + .close-button { + display: inline-block; + width: auto; + } + } + } + + .deactivate-search { + + &.search-container { + .mat-form-field { + animation-name: reduceWidth; + animation-duration: 300ms; + } + } + } +} + +@keyframes expandWidth { + from { + width: 0; + } + + to { + width: 100%; + } +} + +@keyframes reduceWidth { + from { + width: 100%; + } + + to { + width: 0; + } +} + +::ng-deep .substance-select { + max-width: 500px !important; +} diff --git a/src/app/fda/adverse-event/adverse-event-text-search/adverse-event-text-search.component.spec.ts b/src/app/fda/adverse-event/adverse-event-text-search/adverse-event-text-search.component.spec.ts new file mode 100644 index 000000000..09997b5ad --- /dev/null +++ b/src/app/fda/adverse-event/adverse-event-text-search/adverse-event-text-search.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AdverseEventTextSearchComponent } from './adverse-event-text-search.component'; + +describe('AdverseEventTextSearchComponent', () => { + let component: AdverseEventTextSearchComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AdverseEventTextSearchComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AdverseEventTextSearchComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/fda/adverse-event/adverse-event-text-search/adverse-event-text-search.component.ts b/src/app/fda/adverse-event/adverse-event-text-search/adverse-event-text-search.component.ts new file mode 100644 index 000000000..1cf5ccfd9 --- /dev/null +++ b/src/app/fda/adverse-event/adverse-event-text-search/adverse-event-text-search.component.ts @@ -0,0 +1,249 @@ +import { Component, OnInit, ElementRef, AfterViewInit, Input, Output, EventEmitter, OnDestroy } from '@angular/core'; +import { FormControl } from '@angular/forms'; +import { MatAutocompleteSelectedEvent, MatAutocomplete } from '@angular/material/autocomplete'; +import { debounceTime, distinctUntilChanged, switchMap, take } from 'rxjs/operators'; +import { SubstanceSuggestionsGroup } from '@gsrs-core/utils/substance-suggestions-group.model'; +import { UtilsService } from '@gsrs-core/utils/utils.service'; +import { GoogleAnalyticsService } from '@gsrs-core/google-analytics/google-analytics.service'; +import { ConfigService } from '@gsrs-core/config'; +import { ControlledVocabularyService } from '@gsrs-core/controlled-vocabulary'; +import { AdverseEventService } from '../service/adverseevent.service'; + +@Component({ + selector: 'app-adverse-event-text-search', + templateUrl: './adverse-event-text-search.component.html', + styleUrls: ['./adverse-event-text-search.component.scss'] +}) + +export class AdverseEventTextSearchComponent implements OnInit, AfterViewInit, OnDestroy { + searchControl = new FormControl(); + substanceSuggestionsGroup: SubstanceSuggestionsGroup; + suggestionsFields: Array; + matOpen = true; + private testElem: HTMLElement; + private searchContainerElement: HTMLElement; + private query: string; + @Input() eventCategory: string; + @Input() styling?: string; + @Output() searchPerformed = new EventEmitter(); + @Output() searchValueOut = new EventEmitter(); + @Input() placeholder = 'Search'; + @Input() hintMessage = ''; + private privateErrorMessage = ''; + @Output() opened = new EventEmitter(); + @Output() closed = new EventEmitter(); + @Input() source?: string; + private CasDisplay = 'CAS'; + codeSystemVocab?: any; + + constructor( + public adverseEventService: AdverseEventService, + private element: ElementRef, + public gaService: GoogleAnalyticsService, + public configService: ConfigService, + private cvService: ControlledVocabularyService + ) { } + + ngOnInit() { + this.searchControl.valueChanges.pipe( + debounceTime(500), + distinctUntilChanged(), + switchMap(searchValue => { + this.query = searchValue; + this.searchValueOut.emit(this.query); + const eventCategory = this.eventCategory || 'substanceTextSearch'; + const eventLabel = !this.configService.environment.isAnalyticsPrivate && searchValue || 'search term'; + this.gaService.sendEvent(eventCategory, 'search:enter-term', eventLabel); + return this.adverseEventService.getAdverseEventSearchSuggestions(searchValue.toUpperCase(), this.eventCategory); + }) + ).subscribe((response: SubstanceSuggestionsGroup) => { + this.substanceSuggestionsGroup = response; + let showTypes = []; + if (this.eventCategory && this.eventCategory === 'adverseEventPtSearch') { + showTypes = ['PT_Term', 'Prim_SOC', 'Ingredient_Name', 'ATC_Level_1', 'ATC_Level_2', 'ATC_Level_3', 'ATC_Level_4']; + } else if (this.eventCategory && this.eventCategory === 'adverseEventDmeSearch') { + showTypes = ['DME_Reactions', 'PTTerm_Meddra', 'Ingredient_Name', 'ATC_Level_1', 'ATC_Level_2', 'ATC_Level_3', 'ATC_Level_4']; + } else if (this.eventCategory && this.eventCategory === 'adverseEventCvmSearch') { + showTypes = ['Adverse_Event', 'Route_of_Administration', 'Ingredient_Name', 'Species', 'ATC_Level_1', 'ATC_Level_2', 'ATC_Level_3', 'ATC_Level_4']; + } + + this.suggestionsFields = Object.keys(this.substanceSuggestionsGroup).filter(function (item) { + return showTypes.indexOf(item) > -1; + }); + + this.suggestionsFields.forEach((value, index) => { + if (value === 'DME_Reactions') { + this.suggestionsFields[index] = { value: 'DME_Reactions', display: 'DME Reactions' }; + } else if (value === 'PTTerm_Meddra') { + this.suggestionsFields[index] = { value: 'PTTerm_Meddra', display: 'PTTerm Meddra' }; + } else if (value === 'PT_Term') { + this.suggestionsFields[index] = { value: 'PT_Term', display: 'PT Term' }; + } else if (value === 'Adverse_Event') { + this.suggestionsFields[index] = { value: 'Adverse_Event', display: 'Adverse Event' }; + } else if (value === 'Prim_SOC') { + this.suggestionsFields[index] = { value: 'Prim_SOC', display: 'Prim SOC' }; + } else if (value === 'Route_of_Administration') { + this.suggestionsFields[index] = { value: 'Route_of_Administration', display: 'Route of Administration' }; + } else if (value === 'Species') { + this.suggestionsFields[index] = { value: 'Species', display: 'Species' }; + } else if (value === 'Ingredient_Name') { + this.suggestionsFields[index] = { value: 'Ingredient_Name', display: 'Ingredient Name' }; + } else if (value === 'ATC_Level_1') { + this.suggestionsFields[index] = { value: 'ATC_Level_1', display: 'ATC Level 1' }; + } else if (value === 'ATC_Level_2') { + this.suggestionsFields[index] = { value: 'ATC_Level_2', display: 'ATC Level 2' }; + } else if (value === 'ATC_Level_3') { + this.suggestionsFields[index] = { value: 'ATC_Level_3', display: 'ATC Level 3' }; + } else if (value === 'ATC_Level_4') { + this.suggestionsFields[index] = { value: 'ATC_Level_4', display: 'ATC Level 4' }; + } else { + this.suggestionsFields[index] = { value: value, display: value }; + } + }); + + if (this.suggestionsFields != null && this.suggestionsFields.length > 0) { + this.matOpen = true; + this.opened.emit(); + } + + }, error => { + this.gaService.sendException('search suggestion error from API call'); + console.log(error); + }); + + } + + @Input() + set searchValue(searchValue: string) { + this.searchControl.setValue(searchValue); + } + + @Input() + set errorMessage(errorMessage: string) { + this.searchControl.markAsTouched(); + if (errorMessage) { + this.searchControl.setErrors({ + error: true + }); + } else { + this.searchControl.setErrors(null); + } + this.privateErrorMessage = errorMessage; + } + + get errorMessage(): string { + return this.privateErrorMessage; + } + + ngOnDestroy() { } + + autoCompleteClosed(): void { + this.matOpen = false; + this.closed.emit(); + } + + focused(): void { + if (this.suggestionsFields != null && this.suggestionsFields.length > 0) { + this.matOpen = true; + this.opened.emit(); + } + } + + ngAfterViewInit() { + this.searchContainerElement = this.element.nativeElement.querySelector('.search-container'); + } + + substanceSearchOptionSelected(event?: MatAutocompleteSelectedEvent) { + const eventCategory = this.eventCategory || 'substanceTextSearch'; + const eventLabel = !this.configService.environment.isAnalyticsPrivate && event.option.value || 'auto-complete option'; + this.gaService.sendEvent(eventCategory, 'select:auto-complete', eventLabel); + let searchTerm = event.option.value; + + searchTerm = this.topSearchClean(searchTerm); + + this.searchPerformed.emit(searchTerm); + } + + highlight(field: string) { + if (!this.query) { + return field; + } else { + if (this.matOpen) { + this.testElem = document.querySelector('#overflow') as HTMLElement; + if (this.testElem != null) { + this.testElem.innerText = field; + if (this.testElem.scrollWidth > this.testElem.offsetWidth) { + const pos = field.toUpperCase().indexOf(this.query.toUpperCase()); + field = '...' + field.substring(pos - 15, field.length); + } + } + } + const query = this.query.replace(/(?=[() ])/g, '\\'); + return field.replace(new RegExp(query, 'gi'), match => { + return '' + match + ''; + }); + } + } + + processSubstanceSearch() { + let searchTerm = this.searchControl.value; + const eventCategory = this.eventCategory || 'substanceTextSearch'; + const eventLabel = !this.configService.environment.isAnalyticsPrivate && searchTerm || 'search term option'; + this.gaService.sendEvent(eventCategory, 'search:submit', eventLabel); + + // Clean up searchTerm + searchTerm = this.topSearchClean(searchTerm); + + this.searchPerformed.emit(searchTerm); + } + + activateSearch(): void { + if (this.source) { + this.searchContainerElement.classList.add('active-' + this.source); + } else { + this.searchContainerElement.classList.add('active-search'); + } + } + + deactivateSearch(): void { + this.searchContainerElement.classList.add('deactivate-search'); + setTimeout(() => { + if (this.source) { + + this.searchContainerElement.classList.remove('active-' + this.source); + this.searchContainerElement.classList.remove('deactivate-search'); + } else { + this.searchContainerElement.classList.remove('active-search'); + this.searchContainerElement.classList.remove('deactivate-search'); + } + }, 300); + } + + topSearchClean(searchTerm): string { + if (searchTerm && searchTerm.length > 0) { + searchTerm = searchTerm.trim(); + if (searchTerm.indexOf('"') < 0 && searchTerm.indexOf('*') < 0 && searchTerm.indexOf(':') < 0 + && searchTerm.indexOf(' AND ') < 0 && searchTerm.indexOf(' OR ') < 0) { + // Put slash in front of brackets, for example: + // 1. [INN] to \[INN\] + // 2. IBUPROFEN [INN] to IBUPROFEN \[INN\] + // 3. *[INN] to *\[INN\] + // 4. [INN]* to \[INN\]* + // 5. "[INN]" to "\[INN\]" + // 6. "IBUPROFEN [INN]" to "IBUPROFEN \[INN\]" + // 7. "*[INN]" to "*\[INN\]" + // 8. [INN]* to \[INN\]* + searchTerm = '"' + searchTerm + .replace(/([^\\])\[/g, "$1\\[").replace(/^\[/g, "\\[") + .replace(/([^\\])\]/g, "$1\\]").replace(/^\]/g, "\\]") + + '"'; + } else if (searchTerm.indexOf(':') < 0) { + searchTerm = searchTerm + .replace(/([^\\])\[/g, "$1\\[").replace(/^\[/g, "\\[") + .replace(/([^\\])\]/g, "$1\\]").replace(/^\]/g, "\\]") + } + this.searchControl.setValue(searchTerm); + } + return searchTerm; + } +} diff --git a/src/app/fda/adverse-event/adverse-event-text-search/adverse-event-text-search.module.ts b/src/app/fda/adverse-event/adverse-event-text-search/adverse-event-text-search.module.ts new file mode 100644 index 000000000..8e72a4741 --- /dev/null +++ b/src/app/fda/adverse-event/adverse-event-text-search/adverse-event-text-search.module.ts @@ -0,0 +1,29 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { AdverseEventTextSearchComponent } from './adverse-event-text-search.component'; +import { MatIconModule } from '@angular/material/icon'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatButtonModule } from '@angular/material/button'; + +@NgModule({ + imports: [ + CommonModule, + MatIconModule, + MatAutocompleteModule, + ReactiveFormsModule, + FormsModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule + ], + declarations: [ + AdverseEventTextSearchComponent + ], + exports: [ + AdverseEventTextSearchComponent + ] +}) +export class AdverseEventTextSearchModule { } diff --git a/src/app/fda/adverse-event/adverse-events-browse.module.ts b/src/app/fda/adverse-event/adverse-events-browse.module.ts new file mode 100644 index 000000000..e6fe4b781 --- /dev/null +++ b/src/app/fda/adverse-event/adverse-events-browse.module.ts @@ -0,0 +1,133 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Router, Routes, RouterModule } from '@angular/router'; +import { OverlayModule } from '@angular/cdk/overlay'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { MatSidenavModule } from '@angular/material/sidenav'; +import { MatCardModule } from '@angular/material/card'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatBadgeModule } from '@angular/material/badge'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatTableModule } from '@angular/material/table'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatSelectModule } from '@angular/material/select'; +import { MatSliderModule } from '@angular/material/slider'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatListModule } from '@angular/material/list'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatButtonToggleModule } from '@angular/material/button-toggle'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatBottomSheetModule } from '@angular/material/bottom-sheet'; +import { MatSortModule } from '@angular/material/sort'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { SubstanceImageModule } from '@gsrs-core/substance/substance-image.module'; +import { SubstanceSearchSelectorModule } from '../substance-search-select/substance-search-selector.module'; +import { FacetsManagerModule } from '@gsrs-core/facets-manager'; +import { SubstanceFormModule } from '../../core/substance-form/substance-form.module'; +import { AdverseEventTextSearchModule } from './adverse-event-text-search/adverse-event-text-search.module'; +import { AdverseEventService } from './service/adverseevent.service'; +import { AdverseEventsPtBrowseComponent } from './adverse-events-pt-browse/adverse-events-pt-browse.component'; +import { AdverseEventsDmeBrowseComponent } from './adverse-events-dme-browse/adverse-events-dme-browse.component'; +import { AdverseEventsCvmBrowseComponent } from './adverse-events-cvm-browse/adverse-events-cvm-browse.component'; +import { AdverseEventsBrowseComponent } from './adverse-events-browse/adverse-events-browse.component'; +import { ActivateAdverseeventsComponent } from './activate-adverse-events.component'; + +const advEventRoutes: Routes = [ + { + path: 'browse-adverse-events', + component: AdverseEventsBrowseComponent, + canActivate: [ActivateAdverseeventsComponent] + }, + { + path: 'adverse-event-pt-browse', + component: AdverseEventsPtBrowseComponent, + canActivate: [ActivateAdverseeventsComponent] + }, + { + path: 'adverse-event-dme-browse', + component: AdverseEventsDmeBrowseComponent, + canActivate: [ActivateAdverseeventsComponent] + }, + { + path: 'adverse-event-cvm-browse', + component: AdverseEventsCvmBrowseComponent, + canActivate: [ActivateAdverseeventsComponent] + } +]; + +@NgModule({ + imports: [ + CommonModule, + RouterModule.forChild(advEventRoutes), + MatToolbarModule, + MatSidenavModule, + MatCardModule, + MatAutocompleteModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + MatIconModule, + MatChipsModule, + MatBadgeModule, + MatExpansionModule, + MatCheckboxModule, + MatTableModule, + MatSortModule, + MatPaginatorModule, + MatSelectModule, + MatSliderModule, + MatDialogModule, + MatListModule, + MatMenuModule, + MatButtonToggleModule, + MatTooltipModule, + MatTabsModule, + MatBottomSheetModule, + // MatProgressSpinnerModule, + FormsModule, + ReactiveFormsModule, + OverlayModule, + SubstanceSearchSelectorModule, + SubstanceFormModule, + FacetsManagerModule, + AdverseEventTextSearchModule + ], + declarations: [ + AdverseEventsPtBrowseComponent, + AdverseEventsDmeBrowseComponent, + AdverseEventsCvmBrowseComponent, + AdverseEventsBrowseComponent, + AdverseEventsBrowseComponent + ], + exports: [ + ], + providers: [ + ActivateAdverseeventsComponent + ] +}) + +export class AdverseEventsBrowseModule { + constructor(router: Router) { + advEventRoutes.forEach(route => { + router.config[0].children.push(route); + }); + } + + static forRoot(): ModuleWithProviders { + return { + ngModule: AdverseEventsBrowseModule, + providers: [ + AdverseEventService, + ActivateAdverseeventsComponent + ] + }; + } +} diff --git a/src/app/fda/adverse-event/adverse-events-browse/adverse-events-browse.component.html b/src/app/fda/adverse-event/adverse-events-browse/adverse-events-browse.component.html new file mode 100644 index 000000000..e06717474 --- /dev/null +++ b/src/app/fda/adverse-event/adverse-events-browse/adverse-events-browse.component.html @@ -0,0 +1,40 @@ +




    + + + + +
    Adverse Event PT +
    + ({{adverseEventPtCount}}) +
    +
    +
    + + +
    + + + +
    Adverse Event DME +
    + ({{adverseEventDmeCount}}) +
    +
    +
    + + +
    + + + +
    Adverse Event CVM +
    + ({{adverseEventCvmCount}}) +
    +
    +
    + + +
    + +
    \ No newline at end of file diff --git a/src/app/fda/adverse-event/adverse-events-browse/adverse-events-browse.component.scss b/src/app/fda/adverse-event/adverse-events-browse/adverse-events-browse.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/fda/adverse-event/adverse-events-browse/adverse-events-browse.component.spec.ts b/src/app/fda/adverse-event/adverse-events-browse/adverse-events-browse.component.spec.ts new file mode 100644 index 000000000..6c5ac422e --- /dev/null +++ b/src/app/fda/adverse-event/adverse-events-browse/adverse-events-browse.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AdverseEventsBrowseComponent } from './adverse-events-browse.component'; + +describe('AdverseEventsBrowseComponent', () => { + let component: AdverseEventsBrowseComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ AdverseEventsBrowseComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AdverseEventsBrowseComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/fda/adverse-event/adverse-events-browse/adverse-events-browse.component.ts b/src/app/fda/adverse-event/adverse-events-browse/adverse-events-browse.component.ts new file mode 100644 index 000000000..efea001e3 --- /dev/null +++ b/src/app/fda/adverse-event/adverse-events-browse/adverse-events-browse.component.ts @@ -0,0 +1,76 @@ +import { Component, OnInit, AfterViewInit } from '@angular/core'; +import { MatTabChangeEvent } from '@angular/material/tabs'; +import { MatDialog } from '@angular/material/dialog'; +import { Facet, FacetsManagerService, FacetUpdateEvent } from '@gsrs-core/facets-manager'; +import { AdverseEventService } from '../service/adverseevent.service'; + +@Component({ + selector: 'app-adverse-events-browse', + templateUrl: './adverse-events-browse.component.html', + styleUrls: ['./adverse-events-browse.component.scss'] +}) +export class AdverseEventsBrowseComponent implements OnInit, AfterViewInit { + adverseEventPtCount = 0; + adverseEventDmeCount = 0; + adverseEventCvmCount = 0; + tabSelectedIndex = 0; + category = 'Adverse Event PT'; + + constructor( + public adverseEventService: AdverseEventService, + private facetManagerService: FacetsManagerService) { } + + ngOnInit() { + this.facetManagerService.registerGetFacetsHandler(this.adverseEventService.getAdverseEventPtFacets); + } + + ngAfterViewInit() { + this.facetManagerService.registerGetFacetsHandler(this.adverseEventService.getAdverseEventPtFacets); + } + + tabSelectedUpdated(event: MatTabChangeEvent) { + if (event) { + this.category = event.tab.textLabel; + + this.setFacetsforTabs(); + } + } + + setFacetsforTabs() { + if (this.category) { + if (this.category === 'Adverse Event PT') { + this.facetManagerService.registerGetFacetsHandler(this.adverseEventService.getAdverseEventPtFacets); + } + if (this.category === 'Adverse Event DME') { + this.facetManagerService.registerGetFacetsHandler(this.adverseEventService.getAdverseEventDmeFacets); + } + if (this.category === 'Adverse Event CVM') { + this.facetManagerService.registerGetFacetsHandler(this.adverseEventService.getAdverseEventCvmFacets); + } + } + } + + getAdverseEventPtCount($event: any) { + this.adverseEventPtCount = $event; + if (this.adverseEventPtCount > 0) { + this.tabSelectedIndex = 0; + } + } + + getAdverseEventDmeCount($event: any) { + this.adverseEventDmeCount = $event; + // if PT and CVM counts are 0, and DME count is greater than 0, set the tab to DME + if (((this.adverseEventPtCount == 0) && (this.adverseEventCvmCount == 0)) && (this.adverseEventDmeCount > 0)) { + this.tabSelectedIndex = 1; + } + } + + getAdverseEventCvmCount($event: any) { + this.adverseEventCvmCount = $event; + // if PT and DME counts are 0, and CVM count is greater than 0, set the tab to CVM + if (((this.adverseEventPtCount == 0) && (this.adverseEventDmeCount == 0)) && (this.adverseEventCvmCount > 0)) { + this.tabSelectedIndex = 2; + } + } + +} diff --git a/src/app/fda/adverse-event/adverse-events-cvm-browse/adverse-events-cvm-browse.component.html b/src/app/fda/adverse-event/adverse-events-cvm-browse/adverse-events-cvm-browse.component.html new file mode 100644 index 000000000..2ca9ce44d --- /dev/null +++ b/src/app/fda/adverse-event/adverse-events-cvm-browse/adverse-events-cvm-browse.component.html @@ -0,0 +1,324 @@ + + + + + + + + + +
    + + +
    +
    +
    +
    + Would you like to restrict this search to a field? +
      + + + + +
    +
    +
    + {{matchType == 'WORD' ? 'Contains Match' : 'Exact Match'}} +
    + +
    +
    +
    +
    +
    +
    +
    + For more options use the Advanced Search +
    +
    +
    + + + +
    + +
    +
    + Search Query:  + {{searchTerm}} +
    +
    + + +
    +
    + + +
    +
    + + + {{facet.type}}: + + + {{facet.val}} + +
    +
    + +
    +
    + +
    +
    + + +
    + +
    + Browse Adverse Event CVM +
    + + + + + + + + + + + + + + +
    + +
    + +
    + +
    + + + +
    + Page: + + + + of {{lastPage}} +
    +
    + +
    + +
    + + + + + + +
    + + + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Adverse Event {{adverse.adverseEvent}} Species {{adverse.species}} Ingredient Name + + {{adverse.name}} + + Adverse Event Count {{adverse.aeCount}} Route of Administration {{adverse.routeOfAdmin}}
    +
    + + + +
    + + +
    + Adverse Event:  + {{adverseEventCvm.adverseEvent}} +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    + Ingredient Name: +
    + +
    + +
    +
    + Substance Key: +
    +
    + {{adverseEventCvm.substanceKey}} +
    +
    +
    + +
    +
    +
    + Species: +
    +
    + {{adverseEventCvm.species}} +
    +
    + +
    +
    + Route of Administration: +
    +
    + {{adverseEventCvm.name}} +
    +
    +
    + +
    +
    +
    + Adverse Event Count: +
    +
    + {{adverseEventCvm.aeCount}} +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    + + + +
    + Page: + + + + of {{lastPage}} +
    +
    +
    + + + + + +
    +
    +
    \ No newline at end of file diff --git a/src/app/fda/adverse-event/adverse-events-cvm-browse/adverse-events-cvm-browse.component.scss b/src/app/fda/adverse-event/adverse-events-cvm-browse/adverse-events-cvm-browse.component.scss new file mode 100644 index 000000000..e61600ced --- /dev/null +++ b/src/app/fda/adverse-event/adverse-events-cvm-browse/adverse-events-cvm-browse.component.scss @@ -0,0 +1,1222 @@ +::ng-deep .mat-expansion-panel-content > .mat-expansion-panel-body { + padding: 0 12px 10px; +} + +.mat-expansion-panel-header { + padding: 0 12px; +} + +.filter-box { + padding: 10px; + + &:not(:last-child) { + border-bottom: 1px solid #d9d9d9; + } +} + +.include { + ::ng-deep .mat-checkbox-frame { + border-color: var(--include-checkbox-border-color); + } + + ::ng-deep &.mat-checkbox-indeterminate.mat-accent .mat-checkbox-background, + ::ng-deep &.mat-checkbox-checked.mat-accent .mat-checkbox-background { + background-color: var(--include-checkbox-bg-color); + } +} + +.exclude { + margin-left: 5px; + ::ng-deep .mat-checkbox-frame { + border-color: var(--exclude-checkbox-border-color); + } + + ::ng-deep &.mat-checkbox-indeterminate.mat-accent .mat-checkbox-background, + ::ng-deep &.mat-checkbox-checked.mat-accent .mat-checkbox-background { + background-color: var(--exclude-checkbox-bg-color); + } +} + +.advanced-container { + width:55%; + align-content:right; +} + +.selected-container { + font-size: 14px; +} + +.selected-label { + padding-right: 5px; +} + +.reset-facets-button { + margin-left: 5px; + margin-bottom: 5px; +} + +.selected-parameter { + height:auto; + padding-top: 3px; + padding-bottom: 3px; +} + +.selected-container { + max-width:700px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.selected-label { + max-width:200px; + overflow: hidden; + text-overflow: ellipsis; +} + +.search-parameters { + display: flex; + width: 100%; + flex-wrap: wrap; + + &.center { + margin-top: 10px; + justify-content: center; + } + + & > div { + + padding-left:10px; + padding-right: 2px; + margin-left:5px; + margin-bottom: 5px; + display: flex; + align-items: center; + } + + .actions-container { + margin-left: auto; + flex-shrink: 0; + } + +.selected-parameter { + padding-right:5px; + background-color: var(--regular-white-color); + font-size:14px; +} +} + +.controls-container-right { + display: flex; + flex-direction: row; + justify-content: flex-end; +} + +.mat-card { + max-width: 928px; + margin-bottom: 20px; +} + +.mat-card, .controls-container { + width: 100%; + box-sizing: border-box; +} + +.controls-container { + display: flex; + align-items: center; + justify-content: space-between; + + .mat-paginator { + margin-left: auto; + } +} + +::ng-deep .mat-paginator-range-label { + font-weight: 550; +} + +.main-title { + font-size: 26px; + padding-left: 10px; + padding-right: 25px; + font-weight: 550; + padding-top: 10px; + color: var(--primary-color); + width: 285px; + /* + font-size: 26px; + padding-left: 10px; + padding-right: 25px; + font-weight: 550; + padding-top: 22px; + color: var(--primary-color); + width: 285px; + margin-top: 35px; + margin-right: 30px; + */ +} + +.break { + flex-basis: 0%; + height: 0; + width: 400px; +} + +.export { + margin: auto; +} + +.export-button { + color: var(--regular-black-color); + border-radius: 4px; +} + +.not-icon { + height:18px !important; + width: 18px !important; + vertical-align: bottom; +} + +.page-select { + width:100px; +} + +::ng-deep .auto-width{ + ::ng-deep .mat-form-field { + width: auto !important; + } + ::ng-deep .mat-select-value { + max-width: 100%; + width: auto; + } +} + +.page-label { + color:var(--dark-label-color); + display:block; + font-family:Roboto, "Helvetica Neue", sans-serif; + font-size:14px; + padding-right: 12px; +} + +.page-selector { + display: flex; + flex-direction: row; + align-items: center; + margin-left: 20px; +} + +:host ::ng-deep .mat-paginator-range-label { + margin: 0 5px 0 5px !important; +} + +.mat-paginator { + font-size:16px; +} + +.page-input { + width: 50px; + font-size:14px; +} + +.bad-page { + color: var(--regular-red-color); +} + +.full-paginator { + display: flex; + min-width: 660px; +} + +@media(max-width: 1615px) { + .full-paginator { + width: 100%; + align-content: center; + } + + .break { + flex-basis: 100%; + height: 0; + width: auto; + } + + .controls-container { + flex-wrap: wrap; + /*justify-content: space-between;*/ + } + + .export { + margin: auto; + } +} + +@media(max-width: 1100px) { + .controls-container, .search-parameters { + padding-left: 30px; + } + + .controls-container { + flex-wrap: wrap; + } + + .side-nav-content { + padding-top: 10px; + } + + .export { + margin-right:30px; + } +} + +@media(max-width: 730px) { + .mat-card-title { + flex-direction: column; + align-items: flex-start; + + .substance-name { + margin-bottom: 10px; + } + } + + .page-selector { + display:none !important; + } + + .full-paginator { + min-width: 500px !important; + border: 1px solid var(--regular-grey-color); + } + + .substance-data { + flex-direction: column; + + .label { + margin-bottom: 10px; + } + + .value, .code-system { + padding-left: 20px; + } + } + + .search-parameters { + font-size: .9em; + + .mat-icon-button { + width: 35px; + height: 35px; + line-height: 35px; + } + } + + .side-nav-content { + padding: 5px; + } +} + +.lock-icon { + color: #f0ad4e; +} + +.narrow-search-suggestions-container { + display: flex; + align-content: center; + align-items: center; + margin-bottom: 10px; + justify-content: center; +} + +.narrow-search-suggestions { + padding: 12px 20px; + width: auto; + display: flex; + align-content: center; + align-items: center; + + .mat-flat-button { + margin-left: 7px; + } +} + +@media(max-width: 600px) { + .substance-content { + flex-direction: column; + + .image-thumbnail { + margin: 0 auto; + } + } + + .controls-container { + ::ng-deep { + .mat-paginator-range-l.references-link { + display: inline-flex; + vertical-align: middle; + }abel { + margin: 0 3px 0 0; + } + + .mat-paginator-page-size-select { + margin-left: 0; + } + + .mat-paginator-container { + padding-right: 0; + padding-left: 0; + } + + .mat-paginator-page-size { + margin-right: 4px; + } + + .mat-paginator-range-actions { + + .mat-icon-button { + width: 35px; + height: 35px; + line-height: 35px; + } + } + } + } +} + +@media(max-width: 505px) { + .full-paginator { + min-width: 0px !important; + border: 1px solid var(--regular-grey-color); + } + + .controls-container { + ::ng-deep { + .mat-paginator-page-size-label { + display: none; + } + } + } +} + +.zoom:hover{ + cursor:zoom-in; +} + +.icon-align{ + margin-top: -10px; + vertical-align: bottom; +} + +.ext-link{ + color: #448aff; + text-decoration-style: unset; +} + +.sort{ + margin:auto; + margin-bottom: -7px; +} + +.advanced { + padding-left:20px; + color:#448aff; + +} + +.facet-options { + width: 100%; + display:flex; +} + +.tile-button-container { + display: flex; + flex-direction: row; + justify-content: space-evenly +} + +.more-content { + width:45%; +} + + +/* +.search-parameters { + display: flex; + width: 100%; + flex-wrap: wrap; + font-size: 13px; + + &.center { + margin-top: 10px; + justify-content: center; + } + + & > div { + + // padding: 5px 10px; + // margin-bottom: 5px; + // display: flex; + // align-items: center; + + + padding-left:10px; + padding-right: 2px; + margin-left:5px; + margin-bottom: 5px; + display: flex; + align-items: center; + } + + .actions-container { + margin-left: auto; + flex-shrink: 0; + } +} +*/ + +.cards { + .cards-view { + display: block; + } + + .table-view { + display: none; + } + + .tiles-view{ + display: none; + } +} + +.table { + .cards-view { + display: none; + } + + .table-view { + display: block; + } + + .tiles-view{ + display: none; + } +} + +.tiles { + .cards-view { + display: none; + } + + .table-view { + display: none; + } + + .tiles-view{ + display: flex; + } +} + +.mat-card-title, .facet-value { + display: flex; + box-sizing: border-box; + width: 100%; + flex-direction: row; + align-items: center; + white-space: nowrap; +} + +.mat-card-title { + white-space: normal; + + .substance-name { + color: #448aff; + padding-right: 10px; + } + + .approval-id { + font-size: 16px; + color: var(--pink-span-color); + } + + .center { + font-size: 15px; + color: var(--regular-blue-color); + display: inline-block; +} +} + +.facet-value { + padding: 6px 0; + overflow: hidden; + + .facet-value-checkbox { + padding: 0 3px 0 0; + } + + .facet-value-label { + padding: 0 3px; + max-width: 150px; + overflow: hidden; + color: var(--label-color); + white-space: normal; + } + + .facet-value-count { + padding: 0 0 0 3px; + overflow: hidden; + font-weight: 500; + } +} + +.facet-actions { + display: flex; + align-items: center; + margin-top: 10px; + + .pull-right { + margin-left: auto; + } + + .mat-flat-button { + min-width: 70px; + + &:not(:first-child) { + margin-left: 5px; + } + } +} + +.mat-card-content { + + .mat-chip-list-container { + margin-left: -20px; + margin-bottom: 10px; + } +} + + +.substance-content { + display: flex; + flex-direction: row; +} + +.tile { +height:300px; +margin:10px; +padding:10px; +width: calc(25% - 20px); +min-width:200px; +max-width:230px; +align-content:center; +overflow:hidden; +text-overflow: ellipsis; +} + +.tile .image-thumbnail { +margin:auto; +margin-bottom:20px; +height:175px; +width:175px; +} + +.tile .structure-container { +width:100%; +padding-right:0px; +} + +.tile-name { +white-space:nowrap; +text-overflow: ellipsis; +overflow: hidden; +width:100%; +height:25px; +} + +.substance-tiles{ +flex-flow: row wrap; +} + +.image-other { +width:175px; +height:175px; +} + +.substance-data { + display: flex; + flex-direction: row; + + &:not(:last-child) { + margin-bottom: 15px; + } + + .label { + font-weight: bold; + min-width: 120px; + } +} + +.structure-container { + padding-right: 10px; +} + +.no-results { + text-align: center; + margin-top: 30px; + font-size: 1.2em; +} + +.mat-paginator { + background: var(--regular-transparent-color); +} + +.image-thumbnail{ +height:150px; +width:150px; +} + +.space-bottom { + margin-bottom: 5px; +} + +.facet-advanced-options-link { + margin-top: 7px; + color: #448aff; +} + +.facet-search-container { + display: flex; + + .mat-form-field { + flex-grow: 1; + } +} + +.facet-search-loading.mat-progress-bar { + margin-top: -18px; + margin-bottom: 1.09em; +} + +.full-paginator { + display: flex; + min-width: 660px; + /*align-items: flex-end;*/ +} + +.mat-card { + max-width: 1228px; + margin-bottom: 20px; +} + +.mat-card-title { + display: flex; + box-sizing: border-box; + width: 100%; + flex-direction: row; + align-items: center; + justify-content: space-between; + white-space: nowrap; +} + +.substance-content { + display: flex; + flex-direction: row; +} + +.tile .structure-container { + width:100%; + padding-right:0px; +} + +.structure-container { + padding-right: 10px; +} + +.mat-card-content { + + .mat-chip-list-container { + margin-left: -10px; + margin-bottom: 10px; + } +} + +.tile .image-thumbnail { + margin:auto; + margin-bottom:20px; + height:175px; + width:175px; +} + +.image-thumbnail{ + height:150px; + width:150px; +} + +.zoom:hover{ + cursor:zoom-in; +} + +.substance-data { + display: flex; + flex-direction: row; + + &:not(:last-child) { + margin-bottom: 15px; + } + + .label { + font-weight: bold; + min-width: 100px; + } +} + +.icon-align{ + margin-top: -10px; + vertical-align: bottom; +} + +.ext-link{ + color: #448aff; + text-decoration-style: unset; +} + +@media(max-width: 700px) { + .mat-card-title { + flex-direction: column; + align-items: flex-start; + + .substance-name { + margin-bottom: 10px; + } + } + + .substance-data { + flex-direction: column; + + .label { + margin-bottom: 10px; + } + + .value, .code-system { + padding-left: 20px; + } + } +} + +.lock-icon { + color: #f0ad4e; +} + +.search-text { + display: flex; + padding-left: 10px; +} + +/* +@media(max-width: 1100px) { + .controls-container, .search-parameters { + padding-left: 30px; + } + + .side-nav-content { + padding-top: 10px; + } +} + +@media(max-width: 700px) { + .mat-card-title { + flex-direction: column; + align-items: flex-start; + + .substance-name { + margin-bottom: 10px; + } + } + + .substance-data { + flex-direction: column; + + .label { + margin-bottom: 10px; + } + + .value, .code-system { + padding-left: 20px; + } + } + + .search-parameters { + font-size: .9em; + + .mat-icon-button { + width: 35px; + height: 35px; + line-height: 35px; + } + } + + .side-nav-content { + padding: 5px; + } +} + +@media(max-width: 600px) { + + .substance-content { + flex-direction: column; + + .image-thumbnail { + margin: 0 auto; + } + } + + .controls-container { + + ::ng-deep { + + .mat-paginator-range-l.references-link{ +display: inline-flex; +vertical-align: middle; +}abel { + margin: 0 3px 0 0; + } + + .mat-paginator-page-size-select { + margin-left: 0; + } + + .mat-paginator-container { + padding-right: 0; + padding-left: 0; + } + + .mat-paginator-page-size { + margin-right: 4px; + } + + .mat-paginator-range-actions { + + .mat-icon-button { + width: 35px; + height: 35px; + line-height: 35px; + } + } + } + } +} + +@media(max-width: 505px) { + .controls-container { + + ::ng-deep { + + .mat-paginator-page-size-label { + display: none; + } + } + } +} + +.zoom{ +cursor:zoom-in; +} + +.icon-align{ +margin-top: -10px; +vertical-align: bottom; +} + +.ext-link{ +color: #448aff; +text-decoration-style: unset; +} + +.sort{ +margin:auto; +} + +.titlePosition { + position:absolute; + z-index:100; + left:0px; + top:20px; +} + +.paginationPosition { + position:absolute; + z-index:200; + left:500px; + top:20px; +} +*/ + +.title { + color: var(--secondary-title-color); + font-size: 24px; + font-weight: 600px; + padding-left:15px; + padding-top: 10px; +} + +.row { + display: flex; + width: 100%; +} + +.row-property { + display: flex; + width: 50%; +} + +.row-property-key { + min-width: 32%; + max-width: 32%; + padding: 7px; + font-weight: bold; +} + +.row-property-value { + min-width: 68%; + max-width: 68%; + padding: 5px; +} + +.row-property-2 { + display: flex; + width: 100%; +} + +.row-property-key-2 { + min-width: 15%; + max-width: 15%; + padding: 7px; + font-weight: bold; +} + +.row-property-value-2 { + min-width: 85%; + max-width: 85%; + padding: 5px; +} + +.row-property-3 { + display: flex; + width: 33%; +} + +.row-property-key-3 { + min-width: 45%; + max-width: 45%; + padding: 7px; + font-weight: bold; + font-family: Roboto, "Helvetica Neue", sans-serif; +} + +.row-property-value-3 { + min-width: 55%; + max-width: 55%; + padding: 5px; +} + +.row-property-4 { + display: flex; + width: 66%; +} + +.row-property-key-4 { + min-width: 23%; + max-width: 23%; + padding: 7px; + font-weight: bold; + font-family: Roboto, "Helvetica Neue", sans-serif; +} + +.row-property-value-4 { + min-width: 77%; + max-width: 77%; + padding: 5px; +} + +.row-property-key-5 { + min-width: 35%; + max-width: 35%; + padding: 7px; + font-weight: 600; +} + +.row-property-value-5 { + min-width: 65%; + max-width: 65%; + padding: 5px; +} + +.font9px { + font-size: 9px; +} + +.font10px { + font-size: 10px; +} + +.font12px { + font-size: 12px; +} + +.font13px { + font-size: 13px; +} + +.font14px { + font-size: 14px; +} + +.font17px { + font-size: 17px; +} + +.fontweight600 { + font-weight: 600; +} + +.colorgray { + color: var(--regular-grey-color); +} + +.colorred { + color: var(--regular-red-color); +} + +.colorgreen { + color: var(--regular-green-color); +} + +.colororange { + color: var(--regular-orange-color); +} + +.colorlightblue { + color: var(--blue-color); +} + +.bordergray { + border:1px solid var(--regular-grey-color); +} + +.borderlightorange-bottom { + border-bottom:1px solid var(--light-orange-color); +} + +.borderlightgray { + border:1px solid var(--light-green-color); +} + +.padtop5px { + padding-top: 5px; +} + +.padleft10px { + padding-left: 10px; +} + +.padleft20px { + padding-left: 20px; +} + +.marginleft20px { + margin-left: 20px; +} + +.marginleft40px { + margin-left: 40px; +} + +.marginright40px { + margin-right: 40px; +} + +.margintop90px { + margin-top: 90px; +} + +.totalApp { + display:inline-block; + color:var(--white-color); + border:1px solid var(--grey-border-color); + background:var(--dark-grey-bg-color); + box-shadow: 0 0 5px -1px var(--box-shadow-color); + vertical-align:middle; + max-width: 100px; + font-weight: 500; + border-radius: 5px; + padding: 5px; + text-align: center; +} + +.exportStyle { + display: block; + width: 50px; + height: 50px; + padding: 0px; + border: 10px solid var(--regular-blue-color); +} + +.width100percent { + width: 100%; +} + +.width70percent { + width: 70%; +} + +.width30percent { + width: 30%; +} + +.width350px { + width: 350px; +} + +.divflex { + display: flex; +} + +.sidenav-container-size { + width: 100%; +} + +.top-search { + flex-grow: 1; + max-width: 500px; + + ::ng-deep .mat-form-field { + + .mat-form-field-label { + font-size: 16px; + } + } +} + +.narrow-search-suggestions-container { + display: flex; + align-content: center; + align-items: center; + margin-bottom: 10px; + justify-content: center; + flex-direction: column; +} + +.flex-row { + width: 100%; + margin: auto; +} + +.narrow-search-suggestions { + padding: 12px 20px; + width: auto; + display: flex; + align-content: center; + align-items: center; + + .mat-flat-button { + margin-left: 7px; + } +} diff --git a/src/app/fda/adverse-event/adverse-events-cvm-browse/adverse-events-cvm-browse.component.spec.ts b/src/app/fda/adverse-event/adverse-events-cvm-browse/adverse-events-cvm-browse.component.spec.ts new file mode 100644 index 000000000..7c687357e --- /dev/null +++ b/src/app/fda/adverse-event/adverse-events-cvm-browse/adverse-events-cvm-browse.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AdverseEventsCvmBrowseComponent } from './adverse-events-cvm-browse.component'; + +describe('AdverseEventsCvmBrowseComponent', () => { + let component: AdverseEventCvmBrowseComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ AdverseEventCvmBrowseComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AdverseEventCvmBrowseComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/fda/adverse-event/adverse-events-cvm-browse/adverse-events-cvm-browse.component.ts b/src/app/fda/adverse-event/adverse-events-cvm-browse/adverse-events-cvm-browse.component.ts new file mode 100644 index 000000000..b408586a6 --- /dev/null +++ b/src/app/fda/adverse-event/adverse-events-cvm-browse/adverse-events-cvm-browse.component.ts @@ -0,0 +1,489 @@ +import { Component, OnInit, AfterViewInit, OnDestroy, Output, EventEmitter, HostListener } from '@angular/core'; +import { ActivatedRoute, Router, NavigationExtras } from '@angular/router'; +import { PageEvent } from '@angular/material/paginator'; +import { MatDialog } from '@angular/material/dialog'; +import { Location, LocationStrategy } from '@angular/common'; +import { DomSanitizer } from '@angular/platform-browser'; +import { Subscription } from 'rxjs'; +import { Title } from '@angular/platform-browser'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import * as _ from 'lodash'; +import { Sort } from '@angular/material/sort'; +import { Facet, FacetsManagerService, FacetUpdateEvent } from '@gsrs-core/facets-manager'; +import { LoadingService } from '@gsrs-core/loading'; +import { MainNotificationService } from '@gsrs-core/main-notification'; +import { AppNotification, NotificationType } from '@gsrs-core/main-notification'; +import { ConfigService } from '@gsrs-core/config'; +import { AuthService } from '@gsrs-core/auth/auth.service'; +import { GoogleAnalyticsService } from '../../../../app/core/google-analytics/google-analytics.service'; +import { FacetParam } from '@gsrs-core/facets-manager'; +import { NarrowSearchSuggestion } from '@gsrs-core/utils'; +import { ExportDialogComponent } from '@gsrs-core/substances-browse/export-dialog/export-dialog.component'; +import { DisplayFacet } from '@gsrs-core/facets-manager/display-facet'; +import { environment } from '../../../../environments/environment'; +// import { adverseEventSearchSortValues } from './application-search-sort-values'; +import { UtilsService } from '@gsrs-core/utils/utils.service'; +import { AdverseEventService } from '../service/adverseevent.service'; +import { GeneralService } from '../../service/general.service'; +import { AdverseEventCvm } from '../model/adverse-event.model'; +import { adverseEventCvmSearchSortValues } from './adverse-events-cvm-search-sort-values'; + +@Component({ + selector: 'app-adverse-events-cvm-browse', + templateUrl: './adverse-events-cvm-browse.component.html', + styleUrls: ['./adverse-events-cvm-browse.component.scss'] +}) + +export class AdverseEventsCvmBrowseComponent implements OnInit, AfterViewInit, OnDestroy { + @Output() countAdverseEventCvmOut: EventEmitter = new EventEmitter(); + isAdmin: boolean; + isLoggedIn = false; + isLoading = true; + isError = false; + invalidPage = false; + private isComponentInit = false; + privateExport = false; + isSearchEditable = false; + environment: any; + narrowSearchSuggestions?: { [matchType: string]: Array } = {}; + matchTypes?: Array = []; + narrowSearchSuggestionsCount = 0; + searchValue: string; + previousState: Array = []; + private overlayContainer: HTMLElement; + + // needed for facets + ascDescDir = 'desc'; + private isFacetsParamsInit = false; + private privateFacetParams: FacetParam; + rawFacets: Array; + private searchTermHash: number; + public displayFacets: Array = []; + private subscriptions: Array = []; + + view = 'table'; + order = '$root_aeCount'; + etag = ''; + totalAdverseEventCvm = 0; + pageIndex: number; + pageSize: number; + lastPage: number; + public sortValues = adverseEventCvmSearchSortValues; + public privateSearchTerm?: string; + public adverseEventCvm: Array; + displayedColumns: string[] = [ + 'adverseEvent', + 'species', + 'ingredientName', + 'adverseEventCount', + 'routeOfAdmin' + ]; + + constructor( + public adverseEventService: AdverseEventService, + public generalService: GeneralService, + private activatedRoute: ActivatedRoute, + private location: Location, + private locationStrategy: LocationStrategy, + private router: Router, + private sanitizer: DomSanitizer, + public gaService: GoogleAnalyticsService, + public configService: ConfigService, + private loadingService: LoadingService, + private notificationService: MainNotificationService, + private authService: AuthService, + private overlayContainerService: OverlayContainer, + private facetManagerService: FacetsManagerService, + private utilsService: UtilsService, + private dialog: MatDialog, + private titleService: Title + ) { } + + @HostListener('window:popstate', ['$event']) + onPopState(event) { + setTimeout(() => { + if (this.router.url === this.previousState[0]) { + this.ngOnInit(); + } + }, 50); + } + + ngOnInit() { + this.facetManagerService.registerGetFacetsHandler(this.adverseEventService.getAdverseEventCvmFacets); + // this.gaService.sendPageView('Browse Adverse Event Cvm'); + + this.titleService.setTitle(`AE:Browse Adverse Events`); + + this.pageSize = 10; + this.pageIndex = 0; + + const navigationExtras: NavigationExtras = { + queryParams: {} + }; + + this.privateSearchTerm = this.activatedRoute.snapshot.queryParams['search'] || ''; + + if (this.privateSearchTerm) { + this.searchTermHash = this.utilsService.hashCode(this.privateSearchTerm); + this.isSearchEditable = localStorage.getItem(this.searchTermHash.toString()) != null; + } + + this.order = this.activatedRoute.snapshot.queryParams['order'] || '$root_aeCount'; + this.pageSize = parseInt(this.activatedRoute.snapshot.queryParams['pageSize'], null) || 10; + this.pageIndex = parseInt(this.activatedRoute.snapshot.queryParams['pageIndex'], null) || 0; + this.overlayContainer = this.overlayContainerService.getContainerElement(); + const authSubscription = this.authService.getAuth().subscribe(auth => { + if (auth) { + this.isLoggedIn = true; + } + this.isAdmin = this.authService.hasAnyRoles('Admin', 'Updater', 'SuperUpdater'); + }); + this.subscriptions.push(authSubscription); + + this.isComponentInit = true; + this.loadComponent(); + + } + + ngAfterViewInit() { + } + + ngOnDestroy() { + this.subscriptions.forEach(subscription => { + if (subscription) { + subscription.unsubscribe(); + } + }); + this.facetManagerService.unregisterFacetSearchHandler(); + } + + private loadComponent(): void { + if (this.isFacetsParamsInit && this.isComponentInit) { + this.searchAdverseEventCvm(); + } + } + + searchAdverseEventCvm() { + this.loadingService.setLoading(true); + const skip = this.pageIndex * this.pageSize; + const subscription = this.adverseEventService.getAdverseEventCvm( + this.order, + skip, + this.pageSize, + this.privateSearchTerm, + this.privateFacetParams, + ) + .subscribe(pagingResponse => { + this.isError = false; + + this.adverseEventCvm = pagingResponse.content; + // didn't work unless I did it like this instead of + // below export statement + // this.dataSource = this.adverseEventCvm; + this.totalAdverseEventCvm = pagingResponse.total; + this.countAdverseEventCvmOut.emit(pagingResponse.total); + this.etag = pagingResponse.etag; + + if (pagingResponse.total % this.pageSize === 0) { + this.lastPage = (pagingResponse.total / this.pageSize); + } else { + this.lastPage = Math.floor(pagingResponse.total / this.pageSize + 1); + } + if (pagingResponse.facets && pagingResponse.facets.length > 0) { + this.rawFacets = pagingResponse.facets; + } + + // Narrow Suggest Search Begin + this.narrowSearchSuggestions = {}; + this.matchTypes = []; + this.narrowSearchSuggestionsCount = 0; + if (pagingResponse.narrowSearchSuggestions && pagingResponse.narrowSearchSuggestions.length) { + pagingResponse.narrowSearchSuggestions.forEach(suggestion => { + if (this.narrowSearchSuggestions[suggestion.matchType] == null) { + this.narrowSearchSuggestions[suggestion.matchType] = []; + if (suggestion.matchType === 'WORD') { + this.matchTypes.unshift(suggestion.matchType); + } else { + this.matchTypes.push(suggestion.matchType); + } + } + this.narrowSearchSuggestions[suggestion.matchType].push(suggestion); + this.narrowSearchSuggestionsCount++; + }); + } + this.matchTypes.sort(); + // Narrow Suggest Search End + + // this.getSubstanceBySubstanceKey(); + // this.applicationService.getClinicalTrialApplication(this.applications); + }, error => { + console.log('error'); + const notification: AppNotification = { + message: 'There was an error trying to retrieve Adverse Event CVM. Please refresh and try again.', + type: NotificationType.error, + milisecondsToShow: 6000 + }; + this.isError = true; + this.isLoading = false; + this.loadingService.setLoading(this.isLoading); + this.notificationService.setNotification(notification); + }, () => { + subscription.unsubscribe(); + this.isLoading = false; + this.loadingService.setLoading(this.isLoading); + }); + } + + setSearchTermValue() { + this.pageSize = 10; + this.pageIndex = 0; + this.searchAdverseEventCvm(); + } + + clearSearch(): void { + const eventLabel = environment.isAnalyticsPrivate ? 'search term' : this.privateSearchTerm; + this.gaService.sendEvent('adverseEventFiltering', 'icon-button:clear-search', eventLabel); + + this.privateSearchTerm = ''; + this.pageIndex = 0; + this.pageSize = 10; + + this.populateUrlQueryParameters(); + this.searchAdverseEventCvm(); + } + + clearFilters(): void { + // for facets + this.displayFacets.forEach(displayFacet => { + displayFacet.removeFacet(displayFacet.type, displayFacet.bool, displayFacet.val); + }); + this.clearSearch(); + + this.facetManagerService.clearSelections(); + } + + populateUrlQueryParameters(): void { + const navigationExtras: NavigationExtras = { + queryParams: {} + }; + navigationExtras.queryParams['searchTerm'] = this.privateSearchTerm; + navigationExtras.queryParams['pageSize'] = this.pageSize; + navigationExtras.queryParams['pageIndex'] = this.pageIndex; + navigationExtras.queryParams['skip'] = this.pageIndex * this.pageSize; + + this.previousState.push(this.router.url); + const urlTree = this.router.createUrlTree([], { + queryParams: navigationExtras.queryParams, + queryParamsHandling: 'merge', + preserveFragment: true + }); + this.location.go(urlTree.toString()); + } + + get searchTerm(): string { + return this.privateSearchTerm; + } + + sortData(sort: Sort) { + if (sort.active) { + const orderIndex = this.displayedColumns.indexOf(sort.active).toString(); + this.ascDescDir = sort.direction; + this.sortValues.forEach(sortValue => { + if (sortValue.displayedColumns && sortValue.direction) { + if (this.displayedColumns[orderIndex] === sortValue.displayedColumns && this.ascDescDir === sortValue.direction) { + this.order = sortValue.value; + } + } + }); + // Search Adverse Event + this.searchAdverseEventCvm(); + } + return; + } + + updateView(event): void { + // this.gaService.sendEvent('adverseeventptsContent', 'button:view-update', event.value); + this.view = event.value; + } + + changePage(pageEvent: PageEvent) { + + let eventAction; + let eventValue; + + if (this.pageSize !== pageEvent.pageSize) { + eventAction = 'select:page-size'; + eventValue = pageEvent.pageSize; + } else if (this.pageIndex !== pageEvent.pageIndex) { + eventAction = 'icon-button:page-number'; + eventValue = pageEvent.pageIndex + 1; + } + + this.gaService.sendEvent('applicationsContent', eventAction, 'pager', eventValue); + + this.pageSize = pageEvent.pageSize; + this.pageIndex = pageEvent.pageIndex; + this.populateUrlQueryParameters(); + this.searchAdverseEventCvm(); + } + + customPage(event: any): void { + if (this.validatePageInput(event)) { + this.invalidPage = false; + const newpage = Number(event.target.value) - 1; + this.pageIndex = newpage; + this.gaService.sendEvent('adverseEventPtContent', 'select:page-number', 'pager', newpage); + this.populateUrlQueryParameters(); + this.searchAdverseEventCvm(); + } + } + + validatePageInput(event: any): boolean { + if (event && event.target) { + const newpage = Number(event.target.value); + if (!isNaN(Number(newpage))) { + if ((Number.isInteger(newpage)) && (newpage <= this.lastPage) && (newpage > 0)) { + return true; + } + } + } + return false; + } + + // for facets + facetsParamsUpdated(facetsUpdateEvent: FacetUpdateEvent): void { + this.pageIndex = 0; + this.privateFacetParams = facetsUpdateEvent.facetParam; + this.displayFacets = facetsUpdateEvent.displayFacets; + if (!this.isFacetsParamsInit) { + this.isFacetsParamsInit = true; + this.loadComponent(); + } else { + this.searchAdverseEventCvm(); + } + } + + // for facets + facetsLoaded(numFacetsLoaded: number) { + } + + editAdvancedSearch(): void { + const eventLabel = environment.isAnalyticsPrivate ? 'Browse Application search term' : + `${this.privateSearchTerm}`; + this.gaService.sendEvent('AdverseEventPt Filtering', 'icon-button:edit-advanced-search', eventLabel); + + const navigationExtras: NavigationExtras = { + queryParams: { + 'g-search-hash': this.searchTermHash.toString() + } + }; + + this.router.navigate(['/advanced-search'], navigationExtras); + } + + getSubstanceBySubstanceKey(): void { + // let bdnumName: any; + // let relationship: any; + // let substanceId: string; + + /* + this.adverseEventPt.forEach((element, index) => { + + element.applicationProductList.forEach((elementProd, indexProd) => { + + // Sort Product Name by create date descending + elementProd.applicationProductNameList.sort((a, b) => { + return new Date(b.createDate) - new Date(a.createDate); + }); + + elementProd.applicationIngredientList.forEach((elementIngred, indexIngred) => { + if (elementIngred.substanceKey != null) { + + const substanceSubscription = this.generalService.getSubstanceByAnyId(elementIngred.substanceKey).subscribe(response => { + if (response) { + // Get Substance Details, uuid, approval_id, substance name + if (elementIngred.substanceKey) { + this.generalService.getSubstanceByAnyId(elementIngred.substanceKey).subscribe(responseInactive => { + if (responseInactive) { + elementIngred._substanceUuid = responseInactive.uuid; + elementIngred._ingredientName = responseInactive._name; + } + }); + } + + // Get Active Moiety - Relationship + + } + }); + this.subscriptions.push(substanceSubscription); + } + }); // Ingredient forEach + + }); // Product forEach + + }); // Application forEach + */ + } + + restricSearh(searchTerm: string): void { + this.privateSearchTerm = searchTerm; + this.searchTermHash = this.utilsService.hashCode(this.privateSearchTerm); + this.isSearchEditable = localStorage.getItem(this.searchTermHash.toString()) != null; + this.populateUrlQueryParameters(); + this.searchAdverseEventCvm(); + // this.substanceTextSearchService.setSearchValue('main-substance-search', this.privateSearchTerm); + } + + export() { + if (this.etag) { + const extension = 'xlsx'; + const url = this.getApiExportUrl(this.etag, extension); + // if (this.authService.getUser() !== '') { + const dialogReference = this.dialog.open(ExportDialogComponent, { + // height: '215x', + width: '700px', + data: { 'extension': extension, 'type': 'browseAdverseEventCvm','entity': 'adverseeventcvm', 'hideOptionButtons': true } + }); + // this.overlayContainer.style.zIndex = '1002'; + dialogReference.afterClosed().subscribe(response => { + // this.overlayContainer.style.zIndex = null; + const name = response.name; + const id = response.id; + if (name && name !== '') { + this.loadingService.setLoading(true); + const fullname = name + '.' + extension; + this.authService.startUserDownload(url, this.privateExport, fullname, id).subscribe(response => { + // this.authService.startUserDownload(url, this.privateExport, fullname).subscribe(response => { + this.loadingService.setLoading(false); + const navigationExtras: NavigationExtras = { + queryParams: { + totalSub: this.totalAdverseEventCvm + } + }; + const params = { 'total': this.totalAdverseEventCvm }; + this.router.navigate(['/user-downloads/', response.id]); + }, error => this.loadingService.setLoading(false)); + } + }); + // } + } + } + + getApiExportUrl(etag: string, extension: string): string { + return this.adverseEventService.getApiExportUrlCvm(etag, extension); + } + + processSubstanceSearch(searchValue: string) { + this.privateSearchTerm = searchValue; + this.setSearchTermValue(); + } + + increaseOverlayZindex(): void { + this.overlayContainer.style.zIndex = '1002'; + } + + decreaseOverlayZindex(): void { + this.overlayContainer.style.zIndex = null; + } + +} diff --git a/src/app/fda/adverse-event/adverse-events-cvm-browse/adverse-events-cvm-search-sort-values.ts b/src/app/fda/adverse-event/adverse-events-cvm-browse/adverse-events-cvm-search-sort-values.ts new file mode 100644 index 000000000..4eac6782d --- /dev/null +++ b/src/app/fda/adverse-event/adverse-events-cvm-browse/adverse-events-cvm-search-sort-values.ts @@ -0,0 +1,68 @@ +export const adverseEventCvmSearchSortValues = [ + { + 'value': 'default', + 'display': 'Relevance', + 'displayedColumns': 'default', + 'direction': 'asc' + }, + { + 'value': '^root_adverseEvent', + 'display': 'Adverse Events, Ascending', + 'displayedColumns': 'adverseEvent', + 'direction': 'asc' + }, + { + 'value': '$root_adverseEvent', + 'display': 'Adverse Events, Descending', + 'displayedColumns': 'adverseEvent', + 'direction': 'desc' + }, + { + 'value': '^root_species', + 'display': 'Species, Ascending', + 'displayedColumns': 'species', + 'direction': 'asc' + }, + { + 'value': '$root_species', + 'display': 'Species, Descending', + 'displayedColumns': 'species', + 'direction': 'desc' + }, + { + 'value': '^root_aeCount', + 'display': 'Adverse Event Count, Ascending', + 'displayedColumns': 'adverseEventCount', + 'direction': 'asc' + }, + { + 'value': '$root_aeCount', + 'display': 'Adverse Event Count, Descending', + 'displayedColumns': 'adverseEventCount', + 'direction': 'desc' + }, + { + 'value': '^root_routeOfAdmin', + 'display': 'Route of Admin, Ascending', + 'displayedColumns': 'routeOfAdmin', + 'direction': 'asc' + }, + { + 'value': '$root_routeOfAdmin', + 'display': 'Route of Admin, Descending', + 'displayedColumns': 'routeOfAdmin', + 'direction': 'desc' + }, + { + 'value': '^root_name', + 'display': 'Ingredient, Ascending', + 'displayedColumns': 'ingredientName', + 'direction': 'asc' + }, + { + 'value': '$root_name', + 'display': 'Ingredient Name, Descending', + 'displayedColumns': 'ingredientName', + 'direction': 'desc' + } +]; diff --git a/src/app/fda/adverse-event/adverse-events-dme-browse/adverse-events-dme-browse.component.html b/src/app/fda/adverse-event/adverse-events-dme-browse/adverse-events-dme-browse.component.html new file mode 100644 index 000000000..9179dd8b4 --- /dev/null +++ b/src/app/fda/adverse-event/adverse-events-dme-browse/adverse-events-dme-browse.component.html @@ -0,0 +1,347 @@ + + + + + + + +
    + + +
    +
    +
    +
    + Would you like to restrict this search to a field? +
      + + + + +
    +
    +
    + {{matchType == 'WORD' ? 'Contains Match' : 'Exact Match'}} +
    + +
    +
    +
    +
    +
    +
    +
    + For more options use the Advanced Search +
    +
    +
    + + + +
    +
    +
    + Search Query:  + {{searchTerm}} +
    +
    + + +
    +
    + + +
    +
    + + + {{facet.type}}: + + + {{facet.val}} + +
    +
    + +
    +
    + +
    +
    + + +
    + +
    + Browse Adverse Event DME +
    + + + + + + + + + + + + + + +
    + +
    + +
    + +
    + + + +
    + Page: + + + + of {{lastPage}} +
    +
    + +
    + +
    + + + + + + +
    + + + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DME Reactions {{adverse.dmeReactions}} PT Term Meddra {{adverse.ptTermMeddra}} Ingredient Name + + {{adverse.name}} + + Case Count {{adverse.caseCount}} Dme Count {{adverse.dmeCount}} Dme Count Percent {{adverse.dmeCountPercent}} Weighted Average PRR {{adverse.weightedAvgPrr | number : '.2-2'}}
    +
    + + +
    + + +
    + DME Reactions:  + {{adverseEventDme.dmeReactions}} +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    + Ingredient Name: +
    + +
    +
    +
    + Substance Key: +
    +
    + {{adverseEventDme.substanceKey}} +
    +
    +
    + +
    +
    +
    + PTTerm Meddra: +
    +
    + {{ adverseEventDme.ptTermMeddra}} +
    +
    +
    +
    + Case Count: +
    +
    + {{ adverseEventDme.caseCount}} +
    +
    +
    + +
    +
    +
    + DME Count: +
    +
    + {{ adverseEventDme.dmeCount}} +
    +
    +
    +
    + DME Count Percent: +
    +
    + {{ adverseEventDme.dmeCountPercent}} +
    +
    +
    + +
    +
    +
    + Weighted Average PRR: +
    +
    + {{adverseEventDme.weightedAvgPrr | number : '.2-2'}} +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    + + + +
    + Page: + + + + of {{lastPage}} +
    +
    +
    + + + + + +
    +
    +
    \ No newline at end of file diff --git a/src/app/fda/adverse-event/adverse-events-dme-browse/adverse-events-dme-browse.component.scss b/src/app/fda/adverse-event/adverse-events-dme-browse/adverse-events-dme-browse.component.scss new file mode 100644 index 000000000..41fe3b696 --- /dev/null +++ b/src/app/fda/adverse-event/adverse-events-dme-browse/adverse-events-dme-browse.component.scss @@ -0,0 +1,1253 @@ +::ng-deep .mat-expansion-panel-content > .mat-expansion-panel-body { + padding: 0 12px 10px; +} + +.mat-expansion-panel-header { + padding: 0 12px; +} + +.filter-box { + padding: 10px; + + &:not(:last-child) { + border-bottom: 1px solid #d9d9d9; + } +} + +.include { + ::ng-deep .mat-checkbox-frame { + border-color: var(--include-checkbox-border-color); + } + + ::ng-deep &.mat-checkbox-indeterminate.mat-accent .mat-checkbox-background, + ::ng-deep &.mat-checkbox-checked.mat-accent .mat-checkbox-background { + background-color: var(--include-checkbox-bg-color); + } +} + +.exclude { + margin-left: 5px; + ::ng-deep .mat-checkbox-frame { + border-color: var(--exclude-checkbox-border-color); + } + + ::ng-deep &.mat-checkbox-indeterminate.mat-accent .mat-checkbox-background, + ::ng-deep &.mat-checkbox-checked.mat-accent .mat-checkbox-background { + background-color: var(--exclude-checkbox-bg-color); + } +} + +.advanced-container { + width:55%; + align-content:right; +} + +.selected-container { + font-size: 14px; +} + +.selected-label { + padding-right: 5px; +} + +.reset-facets-button { + margin-left: 5px; + margin-bottom: 5px; +} + +.selected-parameter { + height:auto; + padding-top: 3px; + padding-bottom: 3px; +} + +.selected-container { + max-width:700px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.selected-label { + max-width:200px; + overflow: hidden; + text-overflow: ellipsis; +} + +.search-parameters { + display: flex; + width: 100%; + flex-wrap: wrap; + + &.center { + margin-top: 10px; + justify-content: center; + } + + & > div { + + padding-left:10px; + padding-right: 2px; + margin-left:5px; + margin-bottom: 5px; + display: flex; + align-items: center; + } + + .actions-container { + margin-left: auto; + flex-shrink: 0; + } + +.selected-parameter { + padding-right:5px; + background-color: var(--regular-white-color); + font-size:14px; +} +} + +.controls-container-right { + display: flex; + flex-direction: row; + justify-content: flex-end; +} + +.mat-card { + max-width: 928px; + margin-bottom: 20px; +} + +.mat-card, .controls-container { + width: 100%; + box-sizing: border-box; +} + +.controls-container { + display: flex; + align-items: center; + justify-content: space-between; + + .mat-paginator { + margin-left: auto; + } +} + +::ng-deep .mat-paginator-range-label { + font-weight: 550; +} + +.main-title { + font-size: 26px; + padding-left: 10px; + padding-right: 25px; + font-weight: 550; + padding-top: 10px; + color: var(--primary-color); + width: 285px; + /* + font-size: 26px; + padding-left: 10px; + padding-right: 25px; + font-weight: 550; + padding-top: 22px; + color: var(--primary-color); + width: 285px; + margin-top: 35px; + margin-right: 30px; + */ +} + +.break { + flex-basis: 0%; + height: 0; + width: 400px; +} + +.export { + margin: auto; +} + +.export-button { + color: var(--regular-black-color); + border-radius: 4px; +} + +.not-icon { + height:18px !important; + width: 18px !important; + vertical-align: bottom; +} + +.page-select { + width:100px; +} + +::ng-deep .auto-width{ + ::ng-deep .mat-form-field { + width: auto !important; + } + ::ng-deep .mat-select-value { + max-width: 100%; + width: auto; + } +} + +.page-label { + color:var(--dark-label-color); + display:block; + font-family:Roboto, "Helvetica Neue", sans-serif; + font-size:14px; + padding-right: 12px; +} + +.page-selector { + display: flex; + flex-direction: row; + align-items: center; + margin-left: 20px; +} + +:host ::ng-deep .mat-paginator-range-label { + margin: 0 5px 0 5px !important; +} + +.mat-paginator { + font-size:16px; +} + +.page-input { + width: 50px; + font-size:14px; +} + +.bad-page { + color: var(--regular-red-color); +} + +.full-paginator { + display: flex; + min-width: 660px; +} + +@media(max-width: 1615px) { + .full-paginator { + width: 100%; + align-content: center; + } + + .break { + flex-basis: 100%; + height: 0; + width: auto; + } + + .controls-container { + flex-wrap: wrap; + /*justify-content: space-between;*/ + } + + .export { + margin: auto; + } +} + +@media(max-width: 1100px) { + .controls-container, .search-parameters { + padding-left: 30px; + } + + .controls-container { + flex-wrap: wrap; + } + + .side-nav-content { + padding-top: 10px; + } + + .export { + margin-right:30px; + } +} + +@media(max-width: 730px) { + .mat-card-title { + flex-direction: column; + align-items: flex-start; + + .substance-name { + margin-bottom: 10px; + } + } + + .page-selector { + display:none !important; + } + + .full-paginator { + min-width: 500px !important; + border: 1px solid var(--regular-grey-color); + } + + .substance-data { + flex-direction: column; + + .label { + margin-bottom: 10px; + } + + .value, .code-system { + padding-left: 20px; + } + } + + .search-parameters { + font-size: .9em; + + .mat-icon-button { + width: 35px; + height: 35px; + line-height: 35px; + } + } + + .side-nav-content { + padding: 5px; + } +} + +.lock-icon { + color: #f0ad4e; +} + +.narrow-search-suggestions-container { + display: flex; + align-content: center; + align-items: center; + margin-bottom: 10px; + justify-content: center; +} + +.narrow-search-suggestions { + padding: 12px 20px; + width: auto; + display: flex; + align-content: center; + align-items: center; + + .mat-flat-button { + margin-left: 7px; + } +} + +@media(max-width: 600px) { + .substance-content { + flex-direction: column; + + .image-thumbnail { + margin: 0 auto; + } + } + + .controls-container { + ::ng-deep { + .mat-paginator-range-l.references-link { + display: inline-flex; + vertical-align: middle; + }abel { + margin: 0 3px 0 0; + } + + .mat-paginator-page-size-select { + margin-left: 0; + } + + .mat-paginator-container { + padding-right: 0; + padding-left: 0; + } + + .mat-paginator-page-size { + margin-right: 4px; + } + + .mat-paginator-range-actions { + + .mat-icon-button { + width: 35px; + height: 35px; + line-height: 35px; + } + } + } + } +} + +@media(max-width: 505px) { + .full-paginator { + min-width: 0px !important; + border: 1px solid var(--regular-grey-color); + } + + .controls-container { + ::ng-deep { + .mat-paginator-page-size-label { + display: none; + } + } + } +} + +.zoom:hover{ + cursor:zoom-in; +} + +.icon-align{ + margin-top: -10px; + vertical-align: bottom; +} + +.ext-link{ + color: #448aff; + text-decoration-style: unset; +} + +.sort{ + margin:auto; + margin-bottom: -7px; +} + +.advanced { + padding-left:20px; + color:#448aff; + +} + +.facet-options { + width: 100%; + display:flex; +} + +.tile-button-container { + display: flex; + flex-direction: row; + justify-content: space-evenly +} + +.more-content { + width:45%; +} + + +/* +.search-parameters { + display: flex; + width: 100%; + flex-wrap: wrap; + font-size: 13px; + + &.center { + margin-top: 10px; + justify-content: center; + } + + & > div { + + // padding: 5px 10px; + // margin-bottom: 5px; + // display: flex; + // align-items: center; + + + padding-left:10px; + padding-right: 2px; + margin-left:5px; + margin-bottom: 5px; + display: flex; + align-items: center; + } + + .actions-container { + margin-left: auto; + flex-shrink: 0; + } +} +*/ + +.cards { + .cards-view { + display: block; + } + + .table-view { + display: none; + } + + .tiles-view{ + display: none; + } +} + +.table { + .cards-view { + display: none; + } + + .table-view { + display: block; + } + + .tiles-view{ + display: none; + } +} + +.tiles { + .cards-view { + display: none; + } + + .table-view { + display: none; + } + + .tiles-view{ + display: flex; + } +} + +.mat-card-title, .facet-value { + display: flex; + box-sizing: border-box; + width: 100%; + flex-direction: row; + align-items: center; + white-space: nowrap; +} + +.mat-card-title { + white-space: normal; + + .substance-name { + color: #448aff; + padding-right: 10px; + } + + .approval-id { + font-size: 16px; + color: var(--pink-span-color); + } + + .center { + font-size: 15px; + color: var(--regular-blue-color); + display: inline-block; +} +} + +.facet-value { + padding: 6px 0; + overflow: hidden; + + .facet-value-checkbox { + padding: 0 3px 0 0; + } + + .facet-value-label { + padding: 0 3px; + max-width: 150px; + overflow: hidden; + color: var(--label-color); + white-space: normal; + } + + .facet-value-count { + padding: 0 0 0 3px; + overflow: hidden; + font-weight: 500; + } +} + +.facet-actions { + display: flex; + align-items: center; + margin-top: 10px; + + .pull-right { + margin-left: auto; + } + + .mat-flat-button { + min-width: 70px; + + &:not(:first-child) { + margin-left: 5px; + } + } +} + +.mat-card-content { + + .mat-chip-list-container { + margin-left: -20px; + margin-bottom: 10px; + } +} + + +.substance-content { + display: flex; + flex-direction: row; +} + +.tile { +height:300px; +margin:10px; +padding:10px; +width: calc(25% - 20px); +min-width:200px; +max-width:230px; +align-content:center; +overflow:hidden; +text-overflow: ellipsis; +} + +.tile .image-thumbnail { +margin:auto; +margin-bottom:20px; +height:175px; +width:175px; +} + +.tile .structure-container { +width:100%; +padding-right:0px; +} + +.tile-name { +white-space:nowrap; +text-overflow: ellipsis; +overflow: hidden; +width:100%; +height:25px; +} + +.substance-tiles{ +flex-flow: row wrap; +} + +.image-other { +width:175px; +height:175px; +} + +.substance-data { + display: flex; + flex-direction: row; + + &:not(:last-child) { + margin-bottom: 15px; + } + + .label { + font-weight: bold; + min-width: 120px; + } +} + +.structure-container { + padding-right: 10px; +} + +.no-results { + text-align: center; + margin-top: 30px; + font-size: 1.2em; +} + +.mat-paginator { + background: var(--regular-transparent-color); +} + +.image-thumbnail{ +height:150px; +width:150px; +} + +.space-bottom { + margin-bottom: 5px; +} + +.facet-advanced-options-link { + margin-top: 7px; + color: #448aff; +} + +.facet-search-container { + display: flex; + + .mat-form-field { + flex-grow: 1; + } +} + +.facet-search-loading.mat-progress-bar { + margin-top: -18px; + margin-bottom: 1.09em; +} + +.full-paginator { + display: flex; + min-width: 660px; + /*align-items: flex-end;*/ +} + +.mat-card { + max-width: 1228px; + margin-bottom: 20px; +} + +.mat-card-title { + display: flex; + box-sizing: border-box; + width: 100%; + flex-direction: row; + align-items: center; + justify-content: space-between; + white-space: nowrap; +} + +.substance-content { + display: flex; + flex-direction: row; +} + +.tile .structure-container { + width:100%; + padding-right:0px; +} + +.structure-container { + padding-right: 10px; +} + +.mat-card-content { + + .mat-chip-list-container { + margin-left: -10px; + margin-bottom: 10px; + } +} + +.tile .image-thumbnail { + margin:auto; + margin-bottom:20px; + height:175px; + width:175px; +} + +.image-thumbnail{ + height:150px; + width:150px; +} + +.zoom:hover{ + cursor:zoom-in; +} + +.substance-data { + display: flex; + flex-direction: row; + + &:not(:last-child) { + margin-bottom: 15px; + } + + .label { + font-weight: bold; + min-width: 100px; + } +} + +.icon-align{ + margin-top: -10px; + vertical-align: bottom; +} + +.ext-link{ + color: #448aff; + text-decoration-style: unset; +} + +@media(max-width: 700px) { + .mat-card-title { + flex-direction: column; + align-items: flex-start; + + .substance-name { + margin-bottom: 10px; + } + } + + .substance-data { + flex-direction: column; + + .label { + margin-bottom: 10px; + } + + .value, .code-system { + padding-left: 20px; + } + } +} + +.lock-icon { + color: #f0ad4e; +} + +.search-text { + display: flex; + padding-left: 10px; +} + +/* +@media(max-width: 1100px) { + .controls-container, .search-parameters { + padding-left: 30px; + } + + .side-nav-content { + padding-top: 10px; + } +} + +@media(max-width: 700px) { + .mat-card-title { + flex-direction: column; + align-items: flex-start; + + .substance-name { + margin-bottom: 10px; + } + } + + .substance-data { + flex-direction: column; + + .label { + margin-bottom: 10px; + } + + .value, .code-system { + padding-left: 20px; + } + } + + .search-parameters { + font-size: .9em; + + .mat-icon-button { + width: 35px; + height: 35px; + line-height: 35px; + } + } + + .side-nav-content { + padding: 5px; + } +} + +@media(max-width: 600px) { + + .substance-content { + flex-direction: column; + + .image-thumbnail { + margin: 0 auto; + } + } + + .controls-container { + + ::ng-deep { + + .mat-paginator-range-l.references-link{ +display: inline-flex; +vertical-align: middle; +}abel { + margin: 0 3px 0 0; + } + + .mat-paginator-page-size-select { + margin-left: 0; + } + + .mat-paginator-container { + padding-right: 0; + padding-left: 0; + } + + .mat-paginator-page-size { + margin-right: 4px; + } + + .mat-paginator-range-actions { + + .mat-icon-button { + width: 35px; + height: 35px; + line-height: 35px; + } + } + } + } +} + +@media(max-width: 505px) { + .controls-container { + + ::ng-deep { + + .mat-paginator-page-size-label { + display: none; + } + } + } +} + +.zoom{ +cursor:zoom-in; +} + +.icon-align{ +margin-top: -10px; +vertical-align: bottom; +} + +.ext-link{ +color: #448aff; +text-decoration-style: unset; +} + +.sort{ +margin:auto; +} + +.titlePosition { + position:absolute; + z-index:100; + left:0px; + top:20px; +} + +.paginationPosition { + position:absolute; + z-index:200; + left:500px; + top:20px; +} +*/ + +.title { + color: var(--secondary-title-color); + font-size: 24px; + font-weight: 600px; + padding-left:15px; + padding-top: 10px; +} + +.row { + display: flex; + width: 100%; +} + +.row-property { + display: flex; + width: 50%; +} + +.row-property-key { + min-width: 32%; + max-width: 32%; + padding: 7px; + font-weight: bold; +} + +.row-property-value { + min-width: 68%; + max-width: 68%; + padding: 5px; +} + +.row-property-2 { + display: flex; + width: 100%; +} + +.row-property-key-2 { + min-width: 15%; + max-width: 15%; + padding: 7px; + font-weight: bold; +} + +.row-property-value-2 { + min-width: 85%; + max-width: 85%; + padding: 5px; +} + +.row-property-3 { + display: flex; + width: 33%; +} + +.row-property-key-3 { + min-width: 45%; + max-width: 45%; + padding: 7px; + font-weight: bold; + font-family: Roboto, "Helvetica Neue", sans-serif; +} + +.row-property-value-3 { + min-width: 55%; + max-width: 55%; + padding: 5px; +} + +.row-property-4 { + display: flex; + width: 66%; +} + +.row-property-key-4 { + min-width: 23%; + max-width: 23%; + padding: 7px; + font-weight: bold; + font-family: Roboto, "Helvetica Neue", sans-serif; +} + +.row-property-value-4 { + min-width: 77%; + max-width: 77%; + padding: 5px; +} + +.row-property-key-5 { + min-width: 35%; + max-width: 35%; + padding: 7px; + font-weight: 600; +} + +.row-property-value-5 { + min-width: 65%; + max-width: 65%; + padding: 5px; +} + +.font9px { + font-size: 9px; +} + +.font10px { + font-size: 10px; +} + +.font12px { + font-size: 12px; +} + +.font13px { + font-size: 13px; +} + +.font14px { + font-size: 14px; +} + +.font17px { + font-size: 17px; +} + +.fontweight600 { + font-weight: 600; +} + +.colorgray { + color: var(--regular-grey-color); +} + +.colorred { + color: var(--regular-red-color); +} + +.colorgreen { + color: var(--regular-green-color); +} + +.colororange { + color: var(--regular-orange-color); +} + +.colorlightblue { + color: var(--blue-color); +} + +.bordergray { + border:1px solid var(--regular-grey-color); +} + +.borderlightorange-bottom { + border-bottom:1px solid var(--light-orange-color); +} + +.borderlightgray { + border:1px solid var(--light-green-color); +} + +.padtop5px { + padding-top: 5px; +} + +.padleft10px { + padding-left: 10px; +} + +.padleft20px { + padding-left: 20px; +} + +.marginleft20px { + margin-left: 20px; +} + +.marginleft40px { + margin-left: 40px; +} + +.marginright40px { + margin-right: 40px; +} + +.margintop90px { + margin-top: 90px; +} + +.totalApp { + display:inline-block; + color:var(--white-color); + border:1px solid var(--grey-border-color); + background:var(--dark-grey-bg-color); + box-shadow: 0 0 5px -1px var(--box-shadow-color); + vertical-align:middle; + max-width: 100px; + font-weight: 500; + border-radius: 5px; + padding: 5px; + text-align: center; +} + +.exportStyle { + display: block; + width: 50px; + height: 50px; + padding: 0px; + border: 10px solid var(--regular-blue-color); +} + +.width100percent { + width: 100%; +} + +.width70percent { + width: 70%; +} + +.width30percent { + width: 30%; +} + +.width350px { + width: 350px; +} + +.divflex { + display: flex; +} + +.sidenav-container-size { + width: 100%; +} + +.top-search { + flex-grow: 1; + max-width: 500px; + + ::ng-deep .mat-form-field { + + .mat-form-field-label { + font-size: 16px; + } + } +} + +.narrow-search-suggestions-container { + display: flex; + align-content: center; + align-items: center; + margin-bottom: 10px; + justify-content: center; + flex-direction: column; +} + +.flex-row { + width: 100%; + margin: auto; +} + +.narrow-search-suggestions { + padding: 12px 20px; + width: auto; + display: flex; + align-content: center; + align-items: center; + + .mat-flat-button { + margin-left: 7px; + } +} + +/* +mat-sidenav { + margin: 16px; + width: 200px; + border-right: none; + background: var(--side-nav-bg-color); + color: var(--regular-white-color); + border-radius: 10px; + padding: 16px; + text-align: center; +} + +.content { + height: calc(100vh - 98px); + border-radius: 10px; + margin: 16px; + margin-left: 32px; + + display: flex; + justify-content: center; + align-items: center; + + font-size: 2rem; + color: var(--regular-lightgray-color); +} + +mat-sidenav-container { + height: calc(100vh - 65px); +} +*/ diff --git a/src/app/fda/adverse-event/adverse-events-dme-browse/adverse-events-dme-browse.component.spec.ts b/src/app/fda/adverse-event/adverse-events-dme-browse/adverse-events-dme-browse.component.spec.ts new file mode 100644 index 000000000..6ffd5a8a9 --- /dev/null +++ b/src/app/fda/adverse-event/adverse-events-dme-browse/adverse-events-dme-browse.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AdverseEventsDmeBrowseComponent } from './adverse-events-dme-browse.component'; + +describe('AdverseEventsDmeBrowseComponent', () => { + let component: AdverseEventsDmeBrowseComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ AdverseEventsDmeBrowseComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AdverseEventsDmeBrowseComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/fda/adverse-event/adverse-events-dme-browse/adverse-events-dme-browse.component.ts b/src/app/fda/adverse-event/adverse-events-dme-browse/adverse-events-dme-browse.component.ts new file mode 100644 index 000000000..ca0220a95 --- /dev/null +++ b/src/app/fda/adverse-event/adverse-events-dme-browse/adverse-events-dme-browse.component.ts @@ -0,0 +1,492 @@ +import { Component, OnInit, AfterViewInit, OnDestroy, Output, EventEmitter, HostListener } from '@angular/core'; +import { ActivatedRoute, Router, NavigationExtras } from '@angular/router'; +import { PageEvent } from '@angular/material/paginator'; +import { MatDialog } from '@angular/material/dialog'; +import { Location, LocationStrategy } from '@angular/common'; +import { DomSanitizer } from '@angular/platform-browser'; +import { Subscription } from 'rxjs'; +import { Title } from '@angular/platform-browser'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import * as _ from 'lodash'; +import { Sort } from '@angular/material/sort'; +import { Facet, FacetsManagerService, FacetUpdateEvent } from '@gsrs-core/facets-manager'; +import { LoadingService } from '@gsrs-core/loading'; +import { MainNotificationService } from '@gsrs-core/main-notification'; +import { AppNotification, NotificationType } from '@gsrs-core/main-notification'; +import { ConfigService } from '@gsrs-core/config'; +import { AuthService } from '@gsrs-core/auth/auth.service'; +import { GoogleAnalyticsService } from '../../../../app/core/google-analytics/google-analytics.service'; +import { FacetParam } from '@gsrs-core/facets-manager'; +import { NarrowSearchSuggestion } from '@gsrs-core/utils'; +import { ExportDialogComponent } from '@gsrs-core/substances-browse/export-dialog/export-dialog.component'; +import { DisplayFacet } from '@gsrs-core/facets-manager/display-facet'; +import { environment } from '../../../../environments/environment'; +// import { adverseEventSearchSortValues } from './application-search-sort-values'; +import { UtilsService } from '@gsrs-core/utils/utils.service'; +import { AdverseEventService } from '../service/adverseevent.service'; +import { GeneralService } from '../../service/general.service'; +import { AdverseEventDme } from '../model/adverse-event.model'; +import { adverseEventDmeSearchSortValues } from './adverse-events-dme-search-sort-values'; + +@Component({ + selector: 'app-adverse-events-dme-browse', + templateUrl: './adverse-events-dme-browse.component.html', + styleUrls: ['./adverse-events-dme-browse.component.scss'] +}) + +export class AdverseEventsDmeBrowseComponent implements OnInit, AfterViewInit, OnDestroy { + @Output() countAdverseEventDmeOut: EventEmitter = new EventEmitter(); + isAdmin: boolean; + isLoggedIn = false; + isLoading = true; + isError = false; + invalidPage = false; + private isComponentInit = false; + privateExport = false; + isSearchEditable = false; + environment: any; + narrowSearchSuggestions?: { [matchType: string]: Array } = {}; + matchTypes?: Array = []; + narrowSearchSuggestionsCount = 0; + searchValue: string; + previousState: Array = []; + private overlayContainer: HTMLElement; + + // needed for facets + ascDescDir = 'desc'; + private isFacetsParamsInit = false; + private privateFacetParams: FacetParam; + rawFacets: Array; + private searchTermHash: number; + public displayFacets: Array = []; + private subscriptions: Array = []; + + view = 'table'; + order = '$root_dmeCount'; + etag = ''; + totalAdverseEventDme = 0; + pageIndex: number; + pageSize: number; + lastPage: number; + public sortValues = adverseEventDmeSearchSortValues; + public privateSearchTerm?: string; + public adverseEventDme: Array; + displayedColumns: string[] = [ + 'dmeReactions', + 'ptTermMeddra', + 'ingredientName', + 'caseCount', + 'dmeCount', + 'dmeCountPercent', + 'weightedAvgPrr' + ]; + + constructor( + public adverseEventService: AdverseEventService, + public generalService: GeneralService, + private activatedRoute: ActivatedRoute, + private location: Location, + private locationStrategy: LocationStrategy, + private router: Router, + private sanitizer: DomSanitizer, + public gaService: GoogleAnalyticsService, + public configService: ConfigService, + private loadingService: LoadingService, + private notificationService: MainNotificationService, + private authService: AuthService, + private overlayContainerService: OverlayContainer, + private facetManagerService: FacetsManagerService, + private utilsService: UtilsService, + private dialog: MatDialog, + private titleService: Title + ) { } + + @HostListener('window:popstate', ['$event']) + onPopState(event) { + setTimeout(() => { + if (this.router.url === this.previousState[0]) { + this.ngOnInit(); + } + + }, 50); + } + + ngOnInit() { + this.facetManagerService.registerGetFacetsHandler(this.adverseEventService.getAdverseEventDmeFacets); + // this.gaService.sendPageView('Browse Adverse Event Dme'); + + this.titleService.setTitle(`AE:Browse Adverse Events`); + + this.pageSize = 10; + this.pageIndex = 0; + + const navigationExtras: NavigationExtras = { + queryParams: {} + }; + + this.privateSearchTerm = this.activatedRoute.snapshot.queryParams['search'] || ''; + + if (this.privateSearchTerm) { + this.searchTermHash = this.utilsService.hashCode(this.privateSearchTerm); + this.isSearchEditable = localStorage.getItem(this.searchTermHash.toString()) != null; + } + + this.order = this.activatedRoute.snapshot.queryParams['order'] || '$root_dmeCount'; + this.pageSize = parseInt(this.activatedRoute.snapshot.queryParams['pageSize'], null) || 10; + this.pageIndex = parseInt(this.activatedRoute.snapshot.queryParams['pageIndex'], null) || 0; + this.overlayContainer = this.overlayContainerService.getContainerElement(); + const authSubscription = this.authService.getAuth().subscribe(auth => { + if (auth) { + this.isLoggedIn = true; + } + this.isAdmin = this.authService.hasAnyRoles('Admin', 'Updater', 'SuperUpdater'); + }); + this.subscriptions.push(authSubscription); + + this.isComponentInit = true; + this.loadComponent(); + + } + + ngAfterViewInit() { + } + + ngOnDestroy() { + this.subscriptions.forEach(subscription => { + if (subscription) { + subscription.unsubscribe(); + } + }); + this.facetManagerService.unregisterFacetSearchHandler(); + } + + private loadComponent(): void { + if (this.isFacetsParamsInit && this.isComponentInit) { + this.searchAdverseEventDme(); + } + } + + searchAdverseEventDme() { + this.loadingService.setLoading(true); + const skip = this.pageIndex * this.pageSize; + const subscription = this.adverseEventService.getAdverseEventDme( + this.order, + skip, + this.pageSize, + this.privateSearchTerm, + this.privateFacetParams, + ) + .subscribe(pagingResponse => { + this.isError = false; + + this.adverseEventDme = pagingResponse.content; + // didn't work unless I did it like this instead of + // below export statement + // this.dataSource = this.adverseEventDme; + this.totalAdverseEventDme = pagingResponse.total; + this.countAdverseEventDmeOut.emit(pagingResponse.total); + this.etag = pagingResponse.etag; + + if (pagingResponse.total % this.pageSize === 0) { + this.lastPage = (pagingResponse.total / this.pageSize); + } else { + this.lastPage = Math.floor(pagingResponse.total / this.pageSize + 1); + } + if (pagingResponse.facets && pagingResponse.facets.length > 0) { + this.rawFacets = pagingResponse.facets; + } + + // Narrow Suggest Search Begin + this.narrowSearchSuggestions = {}; + this.matchTypes = []; + this.narrowSearchSuggestionsCount = 0; + if (pagingResponse.narrowSearchSuggestions && pagingResponse.narrowSearchSuggestions.length) { + pagingResponse.narrowSearchSuggestions.forEach(suggestion => { + if (this.narrowSearchSuggestions[suggestion.matchType] == null) { + this.narrowSearchSuggestions[suggestion.matchType] = []; + if (suggestion.matchType === 'WORD') { + this.matchTypes.unshift(suggestion.matchType); + } else { + this.matchTypes.push(suggestion.matchType); + } + } + this.narrowSearchSuggestions[suggestion.matchType].push(suggestion); + this.narrowSearchSuggestionsCount++; + }); + } + this.matchTypes.sort(); + // Narrow Suggest Search End + + // this.getSubstanceBySubstanceKey(); + // this.applicationService.getClinicalTrialApplication(this.applications); + }, error => { + console.log('error'); + const notification: AppNotification = { + message: 'There was an error trying to retrieve Adverse Event DME. Please refresh and try again.', + type: NotificationType.error, + milisecondsToShow: 6000 + }; + this.isError = true; + this.isLoading = false; + this.loadingService.setLoading(this.isLoading); + this.notificationService.setNotification(notification); + }, () => { + subscription.unsubscribe(); + this.isLoading = false; + this.loadingService.setLoading(this.isLoading); + }); + } + + setSearchTermValue() { + this.pageSize = 10; + this.pageIndex = 0; + this.searchAdverseEventDme(); + } + + clearSearch(): void { + const eventLabel = environment.isAnalyticsPrivate ? 'search term' : this.privateSearchTerm; + this.gaService.sendEvent('adverseEventFiltering', 'icon-button:clear-search', eventLabel); + + this.privateSearchTerm = ''; + this.pageIndex = 0; + this.pageSize = 10; + + this.populateUrlQueryParameters(); + this.searchAdverseEventDme(); + } + + clearFilters(): void { + // for facets + this.displayFacets.forEach(displayFacet => { + displayFacet.removeFacet(displayFacet.type, displayFacet.bool, displayFacet.val); + }); + this.clearSearch(); + + this.facetManagerService.clearSelections(); + } + + populateUrlQueryParameters(): void { + const navigationExtras: NavigationExtras = { + queryParams: {} + }; + navigationExtras.queryParams['searchTerm'] = this.privateSearchTerm; + navigationExtras.queryParams['pageSize'] = this.pageSize; + navigationExtras.queryParams['pageIndex'] = this.pageIndex; + navigationExtras.queryParams['skip'] = this.pageIndex * this.pageSize; + + this.previousState.push(this.router.url); + const urlTree = this.router.createUrlTree([], { + queryParams: navigationExtras.queryParams, + queryParamsHandling: 'merge', + preserveFragment: true + }); + this.location.go(urlTree.toString()); + } + + get searchTerm(): string { + return this.privateSearchTerm; + } + + sortData(sort: Sort) { + if (sort.active) { + const orderIndex = this.displayedColumns.indexOf(sort.active).toString(); + this.ascDescDir = sort.direction; + this.sortValues.forEach(sortValue => { + if (sortValue.displayedColumns && sortValue.direction) { + if (this.displayedColumns[orderIndex] === sortValue.displayedColumns && this.ascDescDir === sortValue.direction) { + this.order = sortValue.value; + } + } + }); + // Search Adverse Event + this.searchAdverseEventDme(); + } + return; + } + + updateView(event): void { + // this.gaService.sendEvent('adverseeventptsContent', 'button:view-update', event.value); + this.view = event.value; + } + + changePage(pageEvent: PageEvent) { + + let eventAction; + let eventValue; + + if (this.pageSize !== pageEvent.pageSize) { + eventAction = 'select:page-size'; + eventValue = pageEvent.pageSize; + } else if (this.pageIndex !== pageEvent.pageIndex) { + eventAction = 'icon-button:page-number'; + eventValue = pageEvent.pageIndex + 1; + } + + this.gaService.sendEvent('applicationsContent', eventAction, 'pager', eventValue); + + this.pageSize = pageEvent.pageSize; + this.pageIndex = pageEvent.pageIndex; + this.populateUrlQueryParameters(); + this.searchAdverseEventDme(); + } + + customPage(event: any): void { + if (this.validatePageInput(event)) { + this.invalidPage = false; + const newpage = Number(event.target.value) - 1; + this.pageIndex = newpage; + this.gaService.sendEvent('adverseEventPtContent', 'select:page-number', 'pager', newpage); + this.populateUrlQueryParameters(); + this.searchAdverseEventDme(); + } + } + + validatePageInput(event: any): boolean { + if (event && event.target) { + const newpage = Number(event.target.value); + if (!isNaN(Number(newpage))) { + if ((Number.isInteger(newpage)) && (newpage <= this.lastPage) && (newpage > 0)) { + return true; + } + } + } + return false; + } + + // for facets + facetsParamsUpdated(facetsUpdateEvent: FacetUpdateEvent): void { + this.pageIndex = 0; + this.privateFacetParams = facetsUpdateEvent.facetParam; + this.displayFacets = facetsUpdateEvent.displayFacets; + if (!this.isFacetsParamsInit) { + this.isFacetsParamsInit = true; + this.loadComponent(); + } else { + this.searchAdverseEventDme(); + } + } + + // for facets + facetsLoaded(numFacetsLoaded: number) { + } + + editAdvancedSearch(): void { + const eventLabel = environment.isAnalyticsPrivate ? 'Browse Application search term' : + `${this.privateSearchTerm}`; + this.gaService.sendEvent('AdverseEventPt Filtering', 'icon-button:edit-advanced-search', eventLabel); + + const navigationExtras: NavigationExtras = { + queryParams: { + 'g-search-hash': this.searchTermHash.toString() + } + }; + + this.router.navigate(['/advanced-search'], navigationExtras); + } + + getSubstanceBySubstanceKey(): void { + // let bdnumName: any; + // let relationship: any; + // let substanceId: string; + + /* + this.adverseEventPt.forEach((element, index) => { + + element.applicationProductList.forEach((elementProd, indexProd) => { + + // Sort Product Name by create date descending + elementProd.applicationProductNameList.sort((a, b) => { + return new Date(b.createDate) - new Date(a.createDate); + }); + + elementProd.applicationIngredientList.forEach((elementIngred, indexIngred) => { + if (elementIngred.substanceKey != null) { + + const substanceSubscription = this.generalService.getSubstanceByAnyId(elementIngred.substanceKey).subscribe(response => { + if (response) { + // Get Substance Details, uuid, approval_id, substance name + if (elementIngred.substanceKey) { + this.generalService.getSubstanceByAnyId(elementIngred.substanceKey).subscribe(responseInactive => { + if (responseInactive) { + elementIngred._substanceUuid = responseInactive.uuid; + elementIngred._ingredientName = responseInactive._name; + } + }); + } + + // Get Active Moiety - Relationship + + } + }); + this.subscriptions.push(substanceSubscription); + } + }); // Ingredient forEach + + }); // Product forEach + + }); // Application forEach + */ + } + + restricSearh(searchTerm: string): void { + this.privateSearchTerm = searchTerm; + this.searchTermHash = this.utilsService.hashCode(this.privateSearchTerm); + this.isSearchEditable = localStorage.getItem(this.searchTermHash.toString()) != null; + this.populateUrlQueryParameters(); + this.searchAdverseEventDme(); + // this.substanceTextSearchService.setSearchValue('main-substance-search', this.privateSearchTerm); + } + + export() { + if (this.etag) { + const extension = 'xlsx'; + const url = this.getApiExportUrl(this.etag, extension); + // if (this.authService.getUser() !== '') { + const dialogReference = this.dialog.open(ExportDialogComponent, { + // height: '215x', + width: '700px', + data: { 'extension': extension, 'type': 'browseAdverseEventDme', 'entity': 'adverseeventdme', 'hideOptionButtons': true } + }); + // this.overlayContainer.style.zIndex = '1002'; + dialogReference.afterClosed().subscribe(response => { + // this.overlayContainer.style.zIndex = null; + const name = response.name; + const id = response.id; + if (name && name !== '') { + this.loadingService.setLoading(true); + const fullname = name + '.' + extension; + this.authService.startUserDownload(url, this.privateExport, fullname, id).subscribe(response => { + // this.authService.startUserDownload(url, this.privateExport, fullname).subscribe(response => { + this.loadingService.setLoading(false); + const navigationExtras: NavigationExtras = { + queryParams: { + totalSub: this.totalAdverseEventDme + } + }; + const params = { 'total': this.totalAdverseEventDme }; + this.router.navigate(['/user-downloads/', response.id]); + }, error => this.loadingService.setLoading(false)); + } + }); + // } + } + } + + getApiExportUrl(etag: string, extension: string): string { + return this.adverseEventService.getApiExportUrlDme(etag, extension); + } + + processSubstanceSearch(searchValue: string) { + this.privateSearchTerm = searchValue; + this.setSearchTermValue(); + } + + increaseOverlayZindex(): void { + this.overlayContainer.style.zIndex = '1002'; + } + + decreaseOverlayZindex(): void { + this.overlayContainer.style.zIndex = null; + } + +} diff --git a/src/app/fda/adverse-event/adverse-events-dme-browse/adverse-events-dme-search-sort-values.ts b/src/app/fda/adverse-event/adverse-events-dme-browse/adverse-events-dme-search-sort-values.ts new file mode 100644 index 000000000..24c45afe7 --- /dev/null +++ b/src/app/fda/adverse-event/adverse-events-dme-browse/adverse-events-dme-search-sort-values.ts @@ -0,0 +1,92 @@ +export const adverseEventDmeSearchSortValues = [ + { + 'value': 'default', + 'display': 'Relevance', + 'displayedColumns': 'default', + 'direction': 'asc' + }, + { + 'value': '^root_dmeReactions', + 'display': 'Dme Reactions, Ascending', + 'displayedColumns': 'dmeReactions', + 'direction': 'asc' + }, + { + 'value': '$root_dmeReactions', + 'display': 'Dme Reactions, Descending', + 'displayedColumns': 'dmeReactions', + 'direction': 'desc' + }, + { + 'value': '^root_ptTermMeddra', + 'display': 'PT Term Meddra, Ascending', + 'displayedColumns': 'ptTermMeddra', + 'direction': 'asc' + }, + { + 'value': '$root_ptTermMeddra', + 'display': 'PT Term Meddra, Descending', + 'displayedColumns': 'ptTermMeddra', + 'direction': 'desc' + }, + { + 'value': '^root_caseCount', + 'display': 'Case Count, Ascending', + 'displayedColumns': 'caseCount', + 'direction': 'asc' + }, + { + 'value': '$root_caseCount', + 'display': 'Case Count, Descending', + 'displayedColumns': 'caseCount', + 'direction': 'desc' + }, + { + 'value': '^root_dmeCount', + 'display': 'Dme Count, Ascending', + 'displayedColumns': 'dmeCount', + 'direction': 'asc' + }, + { + 'value': '$root_dmeCount', + 'display': 'Dme Count, Descending', + 'displayedColumns': 'dmeCount', + 'direction': 'desc' + }, + { + 'value': '$root_dmeCountPercent', + 'display': 'Dme Count Percent, Descending', + 'displayedColumns': 'dmeCountPercent', + 'direction': 'desc' + }, + { + 'value': '^root_dmeCountPercent', + 'display': 'Dme Count Percent, Ascending', + 'displayedColumns': 'dmeCountPercent', + 'direction': 'asc' + }, + { + 'value': '^root_weightedAvgPrr', + 'display': 'Weighted Avg Prr, Ascending', + 'displayedColumns': 'weightedAvgPrr', + 'direction': 'asc' + }, + { + 'value': '$root_weightedAvgPrr', + 'display': 'Weighted Avg Prr, Descending', + 'displayedColumns': 'weightedAvgPrr', + 'direction': 'desc' + }, + { + 'value': '^root_name', + 'display': 'Ingredient, Ascending', + 'displayedColumns': 'ingredientName', + 'direction': 'asc' + }, + { + 'value': '$root_name', + 'display': 'Ingredient Name, Descending', + 'displayedColumns': 'ingredientName', + 'direction': 'desc' + } +]; diff --git a/src/app/fda/adverse-event/adverse-events-pt-browse/adverse-events-pt-browse.component.html b/src/app/fda/adverse-event/adverse-events-pt-browse/adverse-events-pt-browse.component.html new file mode 100644 index 000000000..9b31b8344 --- /dev/null +++ b/src/app/fda/adverse-event/adverse-events-pt-browse/adverse-events-pt-browse.component.html @@ -0,0 +1,418 @@ + + + + + + + + + +
    + + +
    +
    +
    +
    + Would you like to restrict this search to a field? +
      + + + + +
    +
    +
    + {{matchType == 'WORD' ? 'Contains Match' : 'Exact Match'}} +
    + +
    +
    +
    +
    +
    +
    +
    + For more options use the Advanced Search +
    +
    +
    + + + +
    + +
    +
    + Search Query:  + {{searchTerm}} +
    +
    + + +
    +
    + + +
    +
    + + + {{facet.type}}: + + + {{facet.val}} + +
    +
    + +
    +
    + +
    +
    + + +
    + +
    + Browse Adverse Event PT +
    + + + + + + + + + + + + + + + +
    + +
    + +
    + +
    + + + +
    + Page: + + + + of {{lastPage}} +
    +
    + +
    + +
    + + + + + + +
    + + +
    + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PT Term + + Prim SOC {{adverse.primSoc}} Ingredient Name + + {{adverse.name}} + + Case Count {{adverse.caseCount}} PT Count +
    +
    +
    + +
    +
    +
    +
      + + +
    +
    + FAERS Public Dashboard +
    + +
    +
    +
    PRR {{adverse.prr | number : '.2-2'}}
    +
    +
    + + + +
    + + +
    + Adverse Event:  + {{adverse.ptTerm}} +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    + Ingredient Name: +
    + +
    +
    +
    + Substance Key: +
    +
    + {{adverse.substanceKey}} +
    +
    +
    + +
    +
    +
    + Prim SOC: +
    +
    + {{ adverse.primSoc}} +
    +
    +
    +
    + PRR: +
    +
    + {{adverse.prr | number : '.2-2'}} +
    +
    +
    + +
    +
    +
    + Case Count: +
    +
    + {{adverse.caseCount}} +
    +
    +
    +
    + SOC Count: +
    +
    + {{adverse.socCount}} +
    +
    +
    + +
    +
    +
    + PT Count: +
    +
    +
    +
    +
    + +
    +
    +
    +
      + + +
    +
    + FAERS Public Dashboard +
    + +
    +
    +
    +
    +
    +
    + SOC Count Percent: +
    +
    + {{adverse.socCountPercent}} +
    +
    +
    + +
    +
    +
    + PT Count Percent: +
    +
    + {{adverse.ptCountPercent}} +
    +
    +
    +
    + PT Count Total Vs Drug: +
    +
    + {{adverse.ptCountTotalVsDrug}} +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    + + + +
    + Page: + + + + of {{lastPage}} +
    +
    +
    + + + + + +
    +
    +
    \ No newline at end of file diff --git a/src/app/fda/adverse-event/adverse-events-pt-browse/adverse-events-pt-browse.component.scss b/src/app/fda/adverse-event/adverse-events-pt-browse/adverse-events-pt-browse.component.scss new file mode 100644 index 000000000..cd4a3608f --- /dev/null +++ b/src/app/fda/adverse-event/adverse-events-pt-browse/adverse-events-pt-browse.component.scss @@ -0,0 +1,1216 @@ +::ng-deep .mat-expansion-panel-content > .mat-expansion-panel-body { + padding: 0 12px 10px; +} + +.mat-expansion-panel-header { + padding: 0 12px; +} + +.filter-box { + padding: 10px; + + &:not(:last-child) { + border-bottom: 1px solid #d9d9d9; + } +} + +.include { + ::ng-deep .mat-checkbox-frame { + border-color: var(--include-checkbox-border-color); + } + + ::ng-deep &.mat-checkbox-indeterminate.mat-accent .mat-checkbox-background, + ::ng-deep &.mat-checkbox-checked.mat-accent .mat-checkbox-background { + background-color: var(--include-checkbox-bg-color); + } +} + +.exclude { + margin-left: 5px; + ::ng-deep .mat-checkbox-frame { + border-color: var(--exclude-checkbox-border-color); + } + + ::ng-deep &.mat-checkbox-indeterminate.mat-accent .mat-checkbox-background, + ::ng-deep &.mat-checkbox-checked.mat-accent .mat-checkbox-background { + background-color: var(--exclude-checkbox-bg-color); + } +} + +.advanced-container { + width:55%; + align-content:right; +} + +.selected-container { + font-size: 14px; +} + +.selected-label { + padding-right: 5px; +} + +.reset-facets-button { + margin-left: 5px; + margin-bottom: 5px; +} + +.selected-parameter { + height:auto; + padding-top: 3px; + padding-bottom: 3px; +} + +.selected-container { + max-width:700px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.selected-label { + max-width:200px; + overflow: hidden; + text-overflow: ellipsis; +} + +.search-parameters { + display: flex; + width: 100%; + flex-wrap: wrap; + + &.center { + margin-top: 10px; + justify-content: center; + } + + & > div { + + padding-left:10px; + padding-right: 2px; + margin-left:5px; + margin-bottom: 5px; + display: flex; + align-items: center; + } + + .actions-container { + margin-left: auto; + flex-shrink: 0; + } + +.selected-parameter { + padding-right:5px; + background-color: var(--regular-white-color); + font-size:14px; +} +} + +.controls-container-right { + display: flex; + flex-direction: row; + justify-content: flex-end; +} + +.mat-card { + max-width: 928px; + margin-bottom: 20px; +} + +.mat-card, .controls-container { + width: 100%; + box-sizing: border-box; +} + +.controls-container { + display: flex; + align-items: center; + justify-content: space-between; + + .mat-paginator { + margin-left: auto; + } +} + +::ng-deep .mat-paginator-range-label { + font-weight: 550; +} + +.main-title { + font-size: 26px; + padding-left: 10px; + padding-right: 25px; + font-weight: 550; + padding-top: 10px; + color: var(--primary-color); + width: 285px; +} + +.break { + flex-basis: 0%; + height: 0; + width: 400px; +} + +.export { + margin: auto; +} + +.export-button { + color: var(--regular-black-color); + border-radius: 4px; +} + +.not-icon { + height:18px !important; + width: 18px !important; + vertical-align: bottom; +} + +.page-select { + width:100px; +} + +::ng-deep .auto-width{ + ::ng-deep .mat-form-field { + width: auto !important; + } + ::ng-deep .mat-select-value { + max-width: 100%; + width: auto; + } +} + +.page-label { + color:var(--dark-label-color); + display:block; + font-family:Roboto, "Helvetica Neue", sans-serif; + font-size:14px; + padding-right: 12px; +} + +.page-selector { + display: flex; + flex-direction: row; + align-items: center; + margin-left: 20px; +} + +:host ::ng-deep .mat-paginator-range-label { + margin: 0 5px 0 5px !important; +} + +.mat-paginator { + font-size:16px; +} + +.page-input { + width: 50px; + font-size:14px; +} + +.bad-page { + color: var(--regular-red-color); +} + +.full-paginator { + display: flex; + min-width: 660px; +} + +@media(max-width: 1615px) { + .full-paginator { + width: 100%; + align-content: center; + } + + .break { + flex-basis: 100%; + height: 0; + width: auto; + } + + .controls-container { + flex-wrap: wrap; + /*justify-content: space-between;*/ + } + + .export { + margin: auto; + } +} + +@media(max-width: 1100px) { + .controls-container, .search-parameters { + padding-left: 30px; + } + + .controls-container { + flex-wrap: wrap; + } + + .side-nav-content { + padding-top: 10px; + } + + .export { + margin-right:30px; + } +} + +@media(max-width: 730px) { + .mat-card-title { + flex-direction: column; + align-items: flex-start; + + .substance-name { + margin-bottom: 10px; + } + } + + .page-selector { + display:none !important; + } + + .full-paginator { + min-width: 500px !important; + border: 1px solid var(--regular-grey-color); + } + + .substance-data { + flex-direction: column; + + .label { + margin-bottom: 10px; + } + + .value, .code-system { + padding-left: 20px; + } + } + + .search-parameters { + font-size: .9em; + + .mat-icon-button { + width: 35px; + height: 35px; + line-height: 35px; + } + } + + .side-nav-content { + padding: 5px; + } +} + +.lock-icon { + color: #f0ad4e; +} + +.narrow-search-suggestions-container { + display: flex; + align-content: center; + align-items: center; + margin-bottom: 10px; + justify-content: center; +} + +.narrow-search-suggestions { + padding: 12px 20px; + width: auto; + display: flex; + align-content: center; + align-items: center; + + .mat-flat-button { + margin-left: 7px; + } +} + +@media(max-width: 600px) { + .substance-content { + flex-direction: column; + + .image-thumbnail { + margin: 0 auto; + } + } + + .controls-container { + ::ng-deep { + .mat-paginator-range-l.references-link { + display: inline-flex; + vertical-align: middle; + }abel { + margin: 0 3px 0 0; + } + + .mat-paginator-page-size-select { + margin-left: 0; + } + + .mat-paginator-container { + padding-right: 0; + padding-left: 0; + } + + .mat-paginator-page-size { + margin-right: 4px; + } + + .mat-paginator-range-actions { + + .mat-icon-button { + width: 35px; + height: 35px; + line-height: 35px; + } + } + } + } +} + +@media(max-width: 505px) { + .full-paginator { + min-width: 0px !important; + border: 1px solid var(--regular-grey-color); + } + + .controls-container { + ::ng-deep { + .mat-paginator-page-size-label { + display: none; + } + } + } +} + +.zoom:hover{ + cursor:zoom-in; +} + +.icon-align{ + margin-top: -10px; + vertical-align: bottom; +} + +.ext-link{ + color: #448aff; + text-decoration-style: unset; +} + +.sort{ + margin:auto; + margin-bottom: -7px; +} + +.advanced { + padding-left:20px; + color:#448aff; + +} + +.facet-options { + width: 100%; + display:flex; +} + +.tile-button-container { + display: flex; + flex-direction: row; + justify-content: space-evenly +} + +.more-content { + width:45%; +} + + +/* +.search-parameters { + display: flex; + width: 100%; + flex-wrap: wrap; + font-size: 13px; + + &.center { + margin-top: 10px; + justify-content: center; + } + + & > div { + + // padding: 5px 10px; + // margin-bottom: 5px; + // display: flex; + // align-items: center; + + + padding-left:10px; + padding-right: 2px; + margin-left:5px; + margin-bottom: 5px; + display: flex; + align-items: center; + } + + .actions-container { + margin-left: auto; + flex-shrink: 0; + } +} +*/ + +.cards { + .cards-view { + display: block; + } + + .table-view { + display: none; + } + + .tiles-view{ + display: none; + } +} + +.table { + .cards-view { + display: none; + } + + .table-view { + display: block; + } + + .tiles-view{ + display: none; + } +} + +.tiles { + .cards-view { + display: none; + } + + .table-view { + display: none; + } + + .tiles-view{ + display: flex; + } +} + +.mat-card-title, .facet-value { + display: flex; + box-sizing: border-box; + width: 100%; + flex-direction: row; + align-items: center; + white-space: nowrap; +} + +.mat-card-title { + white-space: normal; + + .substance-name { + color: #448aff; + padding-right: 10px; + } + + .approval-id { + font-size: 16px; + color: var(--pink-span-color); + } + + .center { + font-size: 15px; + color: var(--regular-blue-color); + display: inline-block; +} +} + +.facet-value { + padding: 6px 0; + overflow: hidden; + + .facet-value-checkbox { + padding: 0 3px 0 0; + } + + .facet-value-label { + padding: 0 3px; + max-width: 150px; + overflow: hidden; + color: var(--label-color); + white-space: normal; + } + + .facet-value-count { + padding: 0 0 0 3px; + overflow: hidden; + font-weight: 500; + } +} + +.facet-actions { + display: flex; + align-items: center; + margin-top: 10px; + + .pull-right { + margin-left: auto; + } + + .mat-flat-button { + min-width: 70px; + + &:not(:first-child) { + margin-left: 5px; + } + } +} + +.mat-card-content { + + .mat-chip-list-container { + margin-left: -20px; + margin-bottom: 10px; + } +} + +.substance-content { + display: flex; + flex-direction: row; +} + +.tile { +height:300px; +margin:10px; +padding:10px; +width: calc(25% - 20px); +min-width:200px; +max-width:230px; +align-content:center; +overflow:hidden; +text-overflow: ellipsis; +} + +.tile .image-thumbnail { +margin:auto; +margin-bottom:20px; +height:175px; +width:175px; +} + +.tile .structure-container { +width:100%; +padding-right:0px; +} + +.tile-name { +white-space:nowrap; +text-overflow: ellipsis; +overflow: hidden; +width:100%; +height:25px; +} + +.substance-tiles{ +flex-flow: row wrap; +} + +.image-other { +width:175px; +height:175px; +} + +.substance-data { + display: flex; + flex-direction: row; + + &:not(:last-child) { + margin-bottom: 15px; + } + + .label { + font-weight: bold; + min-width: 120px; + } +} + +.structure-container { + padding-right: 10px; +} + +.no-results { + text-align: center; + margin-top: 30px; + font-size: 1.2em; +} + +.mat-paginator { + background: var(--regular-transparent-color); +} + +.image-thumbnail{ +height:150px; +width:150px; +} + +.space-bottom { + margin-bottom: 5px; +} + +.facet-advanced-options-link { + margin-top: 7px; + color: #448aff; +} + +.facet-search-container { + display: flex; + + .mat-form-field { + flex-grow: 1; + } +} + +.facet-search-loading.mat-progress-bar { + margin-top: -18px; + margin-bottom: 1.09em; +} + +.full-paginator { + display: flex; + min-width: 660px; + /*align-items: flex-end;*/ +} + +.mat-card { + max-width: 1228px; + margin-bottom: 20px; +} + +.mat-card-title { + display: flex; + box-sizing: border-box; + width: 100%; + flex-direction: row; + align-items: center; + justify-content: space-between; + white-space: nowrap; +} + +.substance-content { + display: flex; + flex-direction: row; +} + +.tile .structure-container { + width:100%; + padding-right:0px; +} + +.structure-container { + padding-right: 10px; +} + +.mat-card-content { + + .mat-chip-list-container { + margin-left: -10px; + margin-bottom: 10px; + } +} + +.tile .image-thumbnail { + margin:auto; + margin-bottom:20px; + height:175px; + width:175px; +} + +.image-thumbnail{ + height:150px; + width:150px; +} + +.zoom:hover{ + cursor:zoom-in; +} + +.substance-data { + display: flex; + flex-direction: row; + + &:not(:last-child) { + margin-bottom: 15px; + } + + .label { + font-weight: bold; + min-width: 100px; + } +} + +.icon-align{ + margin-top: -10px; + vertical-align: bottom; +} + +.ext-link{ + color: #448aff; + text-decoration-style: unset; +} + +@media(max-width: 700px) { + .mat-card-title { + flex-direction: column; + align-items: flex-start; + + .substance-name { + margin-bottom: 10px; + } + } + + .substance-data { + flex-direction: column; + + .label { + margin-bottom: 10px; + } + + .value, .code-system { + padding-left: 20px; + } + } +} + +.lock-icon { + color: #f0ad4e; +} + +.search-text { + display: flex; + padding-left: 10px; +} + +/* +@media(max-width: 1100px) { + .controls-container, .search-parameters { + padding-left: 30px; + } + + .side-nav-content { + padding-top: 10px; + } +} + +@media(max-width: 700px) { + .mat-card-title { + flex-direction: column; + align-items: flex-start; + + .substance-name { + margin-bottom: 10px; + } + } + + .substance-data { + flex-direction: column; + + .label { + margin-bottom: 10px; + } + + .value, .code-system { + padding-left: 20px; + } + } + + .search-parameters { + font-size: .9em; + + .mat-icon-button { + width: 35px; + height: 35px; + line-height: 35px; + } + } + + .side-nav-content { + padding: 5px; + } +} + +@media(max-width: 600px) { + + .substance-content { + flex-direction: column; + + .image-thumbnail { + margin: 0 auto; + } + } + + .controls-container { + + ::ng-deep { + + .mat-paginator-range-l.references-link{ +display: inline-flex; +vertical-align: middle; +}abel { + margin: 0 3px 0 0; + } + + .mat-paginator-page-size-select { + margin-left: 0; + } + + .mat-paginator-container { + padding-right: 0; + padding-left: 0; + } + + .mat-paginator-page-size { + margin-right: 4px; + } + + .mat-paginator-range-actions { + + .mat-icon-button { + width: 35px; + height: 35px; + line-height: 35px; + } + } + } + } +} + +@media(max-width: 505px) { + .controls-container { + + ::ng-deep { + + .mat-paginator-page-size-label { + display: none; + } + } + } +} + +.zoom{ +cursor:zoom-in; +} + +.icon-align{ +margin-top: -10px; +vertical-align: bottom; +} + +.ext-link{ +color: #448aff; +text-decoration-style: unset; +} + +.sort{ +margin:auto; +} + +.titlePosition { + position:absolute; + z-index:100; + left:0px; + top:20px; +} + +.paginationPosition { + position:absolute; + z-index:200; + left:500px; + top:20px; +} +*/ + +.title { + color: var(--secondary-title-color); + font-size: 24px; + font-weight: 600px; + padding-left:15px; + padding-top: 10px; +} + +.row { + display: flex; + width: 100%; +} + +.row-property { + display: flex; + width: 50%; +} + +.row-property-key { + min-width: 32%; + max-width: 32%; + padding: 7px; + font-weight: bold; +} + +.row-property-value { + min-width: 68%; + max-width: 68%; + padding: 5px; +} + +.row-property-2 { + display: flex; + width: 100%; +} + +.row-property-key-2 { + min-width: 15%; + max-width: 15%; + padding: 7px; + font-weight: bold; +} + +.row-property-value-2 { + min-width: 85%; + max-width: 85%; + padding: 5px; +} + +.row-property-3 { + display: flex; + width: 33%; +} + +.row-property-key-3 { + min-width: 45%; + max-width: 45%; + padding: 7px; + font-weight: bold; + font-family: Roboto, "Helvetica Neue", sans-serif; +} + +.row-property-value-3 { + min-width: 55%; + max-width: 55%; + padding: 5px; +} + +.row-property-4 { + display: flex; + width: 66%; +} + +.row-property-key-4 { + min-width: 23%; + max-width: 23%; + padding: 7px; + font-weight: bold; + font-family: Roboto, "Helvetica Neue", sans-serif; +} + +.row-property-value-4 { + min-width: 77%; + max-width: 77%; + padding: 5px; +} + +.row-property-key-5 { + min-width: 35%; + max-width: 35%; + padding: 7px; + font-weight: 600; +} + +.row-property-value-5 { + min-width: 65%; + max-width: 65%; + padding: 5px; +} + +.font9px { + font-size: 9px; +} + +.font10px { + font-size: 10px; +} + +.font12px { + font-size: 12px; +} + +.font13px { + font-size: 13px; +} + +.font14px { + font-size: 14px; +} + +.font17px { + font-size: 17px; +} + +.fontweight600 { + font-weight: 600; +} + +.colorgray { + color: var(--regular-grey-color); +} + +.colorred { + color: var(--regular-red-color); +} + +.colorgreen { + color: var(--regular-green-color); +} + +.colororange { + color: var(--regular-orange-color); +} + +.colorlightblue { + color: var(--blue-color); +} + +.bordergray { + border:1px solid var(--regular-grey-color); +} + +.borderlightorange-bottom { + border-bottom:1px solid var(--light-orange-color); +} + +.borderlightgray { + border:1px solid var(--light-green-color); +} + +.padtop5px { + padding-top: 5px; +} + +.padleft10px { + padding-left: 10px; +} + +.padleft20px { + padding-left: 20px; +} + +.marginleft20px { + margin-left: 20px; +} + +.marginleft40px { + margin-left: 40px; +} + +.marginright40px { + margin-right: 40px; +} + +.margintop90px { + margin-top: 90px; +} + +.totalApp { + display:inline-block; + color:var(--white-color); + border:1px solid var(--grey-border-color); + background:var(--dark-grey-bg-color); + box-shadow: 0 0 5px -1px var(--box-shadow-color); + vertical-align:middle; + max-width: 100px; + font-weight: 500; + border-radius: 5px; + padding: 5px; + text-align: center; +} + +.exportStyle { + display: block; + width: 50px; + height: 50px; + padding: 0px; + border: 10px solid var(--regular-blue-color); +} + +.width100percent { + width: 100%; +} + +.width70percent { + width: 70%; +} + +.width30percent { + width: 30%; +} + +.width350px { + width: 350px; +} + +.divflex { + display: flex; +} + +.small-icon { + width: 15px; + height: 15px; + padding-left: 5px; +} + +.sidenav-container-size { + width: 100%; +} + +.top-search { + flex-grow: 1; + max-width: 500px; + + ::ng-deep .mat-form-field { + + .mat-form-field-label { + font-size: 16px; + } + } +} + +.narrow-search-suggestions-container { + display: flex; + align-content: center; + align-items: center; + margin-bottom: 10px; + justify-content: center; + flex-direction: column; +} + +.flex-row { + width: 100%; + margin: auto; +} + +.narrow-search-suggestions { + padding: 12px 20px; + width: auto; + display: flex; + align-content: center; + align-items: center; + + .mat-flat-button { + margin-left: 7px; + } +} diff --git a/src/app/fda/adverse-event/adverse-events-pt-browse/adverse-events-pt-browse.component.spec.ts b/src/app/fda/adverse-event/adverse-events-pt-browse/adverse-events-pt-browse.component.spec.ts new file mode 100644 index 000000000..719a22d20 --- /dev/null +++ b/src/app/fda/adverse-event/adverse-events-pt-browse/adverse-events-pt-browse.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AdverseEventsPtBrowseComponent } from './adverse-events-pt-browse.component'; + +describe('AdverseEventsBrowseComponent', () => { + let component: AdverseEventsPtBrowseComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ AdverseEventsPtBrowseComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AdverseEventsPtBrowseComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/fda/adverse-event/adverse-events-pt-browse/adverse-events-pt-browse.component.ts b/src/app/fda/adverse-event/adverse-events-pt-browse/adverse-events-pt-browse.component.ts new file mode 100644 index 000000000..70cc58472 --- /dev/null +++ b/src/app/fda/adverse-event/adverse-events-pt-browse/adverse-events-pt-browse.component.ts @@ -0,0 +1,542 @@ +import { Component, OnInit, AfterViewInit, OnDestroy, Output, EventEmitter, HostListener } from '@angular/core'; +import { ActivatedRoute, Router, NavigationExtras } from '@angular/router'; +import { PageEvent } from '@angular/material/paginator'; +import { MatDialog } from '@angular/material/dialog'; +import { Location, LocationStrategy } from '@angular/common'; +import { DomSanitizer } from '@angular/platform-browser'; +import { Subscription } from 'rxjs'; +import { Title } from '@angular/platform-browser'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import * as _ from 'lodash'; +import { Sort } from '@angular/material/sort'; +import { Facet, FacetsManagerService, FacetUpdateEvent } from '@gsrs-core/facets-manager'; +import { LoadingService } from '@gsrs-core/loading'; +import { MainNotificationService } from '@gsrs-core/main-notification'; +import { AppNotification, NotificationType } from '@gsrs-core/main-notification'; +import { ConfigService } from '@gsrs-core/config'; +import { AuthService } from '@gsrs-core/auth/auth.service'; +import { GoogleAnalyticsService } from '../../../../app/core/google-analytics/google-analytics.service'; +import { FacetParam } from '@gsrs-core/facets-manager'; +import { NarrowSearchSuggestion } from '@gsrs-core/utils'; +import { ExportDialogComponent } from '@gsrs-core/substances-browse/export-dialog/export-dialog.component'; +import { DisplayFacet } from '@gsrs-core/facets-manager/display-facet'; +import { environment } from '../../../../environments/environment'; +import { UtilsService } from '@gsrs-core/utils/utils.service'; +import { AdverseEventService } from '../service/adverseevent.service'; +import { GeneralService } from '../../service/general.service'; +import { AdverseEventPt } from '../model/adverse-event.model'; +import { adverseEventPtSearchSortValues } from './adverse-events-pt-search-sort-values'; + +@Component({ + selector: 'app-adverse-events-pt-browse', + templateUrl: './adverse-events-pt-browse.component.html', + styleUrls: ['./adverse-events-pt-browse.component.scss'] +}) + +export class AdverseEventsPtBrowseComponent implements OnInit, AfterViewInit, OnDestroy { + @Output() countAdverseEventPtOut: EventEmitter = new EventEmitter(); + isAdmin: boolean; + isLoggedIn = false; + isLoading = true; + isError = false; + invalidPage = false; + private isComponentInit = false; + privateExport = false; + isSearchEditable = false; + environment: any; + narrowSearchSuggestions?: { [matchType: string]: Array } = {}; + matchTypes?: Array = []; + narrowSearchSuggestionsCount = 0; + searchValue: string; + previousState: Array = []; + private overlayContainer: HTMLElement; + + // needed for facets + ascDescDir = 'desc'; + private isFacetsParamsInit = false; + private privateFacetParams: FacetParam; + rawFacets: Array; + private searchTermHash: number; + public displayFacets: Array = []; + private subscriptions: Array = []; + + view = 'table'; + order = '$root_ptCount'; + etag = ''; + totalAdverseEventPt = 0; + pageIndex: number; + pageSize: number; + lastPage: number; + public sortValues = adverseEventPtSearchSortValues; + public privateSearchTerm?: string; + public adverseEventPtList: Array; + displayedColumns: string[] = [ + 'ptTerm', + 'primSoc', + 'ingredientName', + 'caseCount', + 'ptCount', + 'prr' + ]; + + adverseEventShinySubstanceNameDisplay = false; + adverseEventShinyAdverseEventDisplay = false; + adverseEventShinySubstanceNameURL: string; + adverseEventShinyAdverseEventURL: string; + adverseEventShinySubstanceNameURLWithParam: string; + adverseEventShinyAdverseEventURLWithParam: string; + + // FAERS DASHBOARD + FAERSDashboardAdverseEventUrl: string; + FAERSDashboardSubstanceName: string; + FAERSDashboardSearchTerm = "/select/Search%20Term/"; // FAERS Adverse Event 'Substance Name' + FAERSDashboardReactionTerm = "/select/Reaction%20Term/"; // GSRS Adverse Event 'PT Term' + FAERSDashboardReactionGroup = "/select/Reaction%20Group/"; // GSRS Adverse Event 'Prim SOC' + + constructor( + public adverseEventService: AdverseEventService, + public generalService: GeneralService, + private activatedRoute: ActivatedRoute, + private location: Location, + private locationStrategy: LocationStrategy, + private router: Router, + private sanitizer: DomSanitizer, + public gaService: GoogleAnalyticsService, + public configService: ConfigService, + private loadingService: LoadingService, + private notificationService: MainNotificationService, + private authService: AuthService, + private overlayContainerService: OverlayContainer, + private facetManagerService: FacetsManagerService, + private utilsService: UtilsService, + private dialog: MatDialog, + private titleService: Title + ) { } + + @HostListener('window:popstate', ['$event']) + onPopState(event) { + setTimeout(() => { + if (this.router.url === this.previousState[0]) { + this.ngOnInit(); + } + }, 50); + } + + ngOnInit() { + this.facetManagerService.registerGetFacetsHandler(this.adverseEventService.getAdverseEventPtFacets); + // this.gaService.sendPageView('Browse Adverse Event'); + + this.titleService.setTitle(`AE:Browse Adverse Events`); + + this.pageSize = 10; + this.pageIndex = 0; + + const navigationExtras: NavigationExtras = { + queryParams: {} + }; + + this.privateSearchTerm = this.activatedRoute.snapshot.queryParams['search'] || ''; + + if (this.privateSearchTerm) { + this.searchTermHash = this.utilsService.hashCode(this.privateSearchTerm); + this.isSearchEditable = localStorage.getItem(this.searchTermHash.toString()) != null; + } + + this.order = this.activatedRoute.snapshot.queryParams['order'] || '$root_ptCount'; + this.pageSize = parseInt(this.activatedRoute.snapshot.queryParams['pageSize'], null) || 10; + this.pageIndex = parseInt(this.activatedRoute.snapshot.queryParams['pageIndex'], null) || 0; + this.overlayContainer = this.overlayContainerService.getContainerElement(); + const authSubscription = this.authService.getAuth().subscribe(auth => { + if (auth) { + this.isLoggedIn = true; + } + this.isAdmin = this.authService.hasAnyRoles('Admin', 'Updater', 'SuperUpdater'); + }); + this.subscriptions.push(authSubscription); + + this.isComponentInit = true; + this.loadComponent(); + + // FAERS DASHBOARD + this.getFaersDashboardUrl(); + + } + + ngAfterViewInit() { + } + + ngOnDestroy() { + this.subscriptions.forEach(subscription => { + if (subscription) { + subscription.unsubscribe(); + } + }); + this.facetManagerService.unregisterFacetSearchHandler(); + } + + private loadComponent(): void { + if (this.isFacetsParamsInit && this.isComponentInit) { + this.searchAdverseEventPt(); + } + } + + // For Table View + /* + getAdverseEventPt(pageEvent?: PageEvent) { + this.loadingService.setLoading(true); + this.setPageEvent(pageEvent); + // this.showSpinner = true; // Start progress spinner + const skip = this.pageIndex * this.pageSize; + // const privateSearch = 'root_substanceKey:' + this.bdnum; + const subscription = this.adverseEventService.getAdverseEventPt( + this.order, + skip, + this.pageSize, + this.privateSearchTerm, + this.privateFacetParams + ) + .subscribe(pagingResponse => { + this.totalAdverseEventPt = pagingResponse.total; + this.adverseEventService.totalRecords = pagingResponse.total; + // this.adverseEventCount = pagingResponse.total; + // this.setResultData(pagingResponse.content); + // this.paged = pagingResponse.content; + // this.results = results; + // this.filtered = results; + // this.totalRecords = this.service.totalRecords; + // this.pageChangeFda(); + this.etag = pagingResponse.etag; + }, error => { + console.log('error'); + }, () => { + subscription.unsubscribe(); + this.isLoading = false; + this.loadingService.setLoading(false); + }); + // this.loadingStatus = ''; + // this.showSpinner = false; // Stop progress spinner + // this.loadingService.setLoading(false); + } + */ + + searchAdverseEventPt() { + this.loadingService.setLoading(true); + const skip = this.pageIndex * this.pageSize; + const subscription = this.adverseEventService.getAdverseEventPt( + this.order, + skip, + this.pageSize, + this.privateSearchTerm, + this.privateFacetParams, + ) + .subscribe(pagingResponse => { + this.isError = false; + this.adverseEventPtList = pagingResponse.content; + this.totalAdverseEventPt = pagingResponse.total; + this.countAdverseEventPtOut.emit(pagingResponse.total); + this.etag = pagingResponse.etag; + if (pagingResponse.total % this.pageSize === 0) { + this.lastPage = (pagingResponse.total / this.pageSize); + } else { + this.lastPage = Math.floor(pagingResponse.total / this.pageSize + 1); + } + if (pagingResponse.facets && pagingResponse.facets.length > 0) { + this.rawFacets = pagingResponse.facets; + } + + // Narrow Suggest Search Begin + this.narrowSearchSuggestions = {}; + this.matchTypes = []; + this.narrowSearchSuggestionsCount = 0; + if (pagingResponse.narrowSearchSuggestions && pagingResponse.narrowSearchSuggestions.length) { + pagingResponse.narrowSearchSuggestions.forEach(suggestion => { + if (this.narrowSearchSuggestions[suggestion.matchType] == null) { + this.narrowSearchSuggestions[suggestion.matchType] = []; + if (suggestion.matchType === 'WORD') { + this.matchTypes.unshift(suggestion.matchType); + } else { + this.matchTypes.push(suggestion.matchType); + } + } + this.narrowSearchSuggestions[suggestion.matchType].push(suggestion); + this.narrowSearchSuggestionsCount++; + }); + } + this.matchTypes.sort(); + // Narrow Suggest Search End + + // Loop through the AE PT search results and get the FAERS name from the database + this.getFaersDashboardRecordByName(); + + }, error => { + console.log('error'); + const notification: AppNotification = { + message: 'There was an error trying to retrieve Adverse Event PT. Please refresh and try again.', + type: NotificationType.error, + milisecondsToShow: 6000 + }; + this.isError = true; + this.isLoading = false; + this.loadingService.setLoading(this.isLoading); + this.notificationService.setNotification(notification); + }, () => { + subscription.unsubscribe(); + this.isLoading = false; + this.loadingService.setLoading(this.isLoading); + }); + } + + setPageEvent(pageEvent?: PageEvent): void { + if (pageEvent != null) { + this.pageIndex = pageEvent.pageIndex; + this.pageSize = pageEvent.pageSize; + } + } + + setSearchTermValue() { + this.pageSize = 10; + this.pageIndex = 0; + this.searchAdverseEventPt(); + } + + clearSearch(): void { + const eventLabel = environment.isAnalyticsPrivate ? 'search term' : this.privateSearchTerm; + this.gaService.sendEvent('adverseEventFiltering', 'icon-button:clear-search', eventLabel); + + this.privateSearchTerm = ''; + this.pageIndex = 0; + this.pageSize = 10; + + this.populateUrlQueryParameters(); + this.searchAdverseEventPt(); + } + + clearFilters(): void { + // for facets + this.displayFacets.forEach(displayFacet => { + displayFacet.removeFacet(displayFacet.type, displayFacet.bool, displayFacet.val); + }); + this.clearSearch(); + + this.facetManagerService.clearSelections(); + } + + populateUrlQueryParameters(): void { + const navigationExtras: NavigationExtras = { + queryParams: {} + }; + navigationExtras.queryParams['searchTerm'] = this.privateSearchTerm; + navigationExtras.queryParams['pageSize'] = this.pageSize; + navigationExtras.queryParams['pageIndex'] = this.pageIndex; + navigationExtras.queryParams['skip'] = this.pageIndex * this.pageSize; + + this.previousState.push(this.router.url); + const urlTree = this.router.createUrlTree([], { + queryParams: navigationExtras.queryParams, + queryParamsHandling: 'merge', + preserveFragment: true + }); + this.location.go(urlTree.toString()); + } + + get searchTerm(): string { + return this.privateSearchTerm; + } + + sortData(sort: Sort) { + if (sort.active) { + const orderIndex = this.displayedColumns.indexOf(sort.active).toString(); + this.ascDescDir = sort.direction; + this.sortValues.forEach(sortValue => { + if (sortValue.displayedColumns && sortValue.direction) { + if (this.displayedColumns[orderIndex] === sortValue.displayedColumns && this.ascDescDir === sortValue.direction) { + this.order = sortValue.value; + } + } + }); + // Search Adverse Event + this.searchAdverseEventPt(); + } + return; + } + + updateView(event): void { + // this.gaService.sendEvent('adverseeventptsContent', 'button:view-update', event.value); + this.view = event.value; + } + + changePage(pageEvent: PageEvent) { + + let eventAction; + let eventValue; + + if (this.pageSize !== pageEvent.pageSize) { + eventAction = 'select:page-size'; + eventValue = pageEvent.pageSize; + } else if (this.pageIndex !== pageEvent.pageIndex) { + eventAction = 'icon-button:page-number'; + eventValue = pageEvent.pageIndex + 1; + } + + this.gaService.sendEvent('applicationsContent', eventAction, 'pager', eventValue); + + this.pageSize = pageEvent.pageSize; + this.pageIndex = pageEvent.pageIndex; + this.populateUrlQueryParameters(); + this.searchAdverseEventPt(); + } + + customPage(event: any): void { + if (this.validatePageInput(event)) { + this.invalidPage = false; + const newpage = Number(event.target.value) - 1; + this.pageIndex = newpage; + this.gaService.sendEvent('adverseEventPtContent', 'select:page-number', 'pager', newpage); + this.populateUrlQueryParameters(); + this.searchAdverseEventPt(); + } + } + + validatePageInput(event: any): boolean { + if (event && event.target) { + const newpage = Number(event.target.value); + if (!isNaN(Number(newpage))) { + if ((Number.isInteger(newpage)) && (newpage <= this.lastPage) && (newpage > 0)) { + return true; + } + } + } + return false; + } + + // for facets + facetsParamsUpdated(facetsUpdateEvent: FacetUpdateEvent): void { + this.pageIndex = 0; + this.privateFacetParams = facetsUpdateEvent.facetParam; + this.displayFacets = facetsUpdateEvent.displayFacets; + if (!this.isFacetsParamsInit) { + this.isFacetsParamsInit = true; + this.loadComponent(); + } else { + this.searchAdverseEventPt(); + } + } + + // for facets + facetsLoaded(numFacetsLoaded: number) { + } + + editAdvancedSearch(): void { + const eventLabel = environment.isAnalyticsPrivate ? 'Browse Application search term' : + `${this.privateSearchTerm}`; + this.gaService.sendEvent('AdverseEventPt Filtering', 'icon-button:edit-advanced-search', eventLabel); + + const navigationExtras: NavigationExtras = { + queryParams: { + 'g-search-hash': this.searchTermHash.toString() + } + }; + + this.router.navigate(['/advanced-search'], navigationExtras); + } + + restricSearh(searchTerm: string): void { + this.privateSearchTerm = searchTerm; + this.searchTermHash = this.utilsService.hashCode(this.privateSearchTerm); + this.isSearchEditable = localStorage.getItem(this.searchTermHash.toString()) != null; + this.populateUrlQueryParameters(); + this.searchAdverseEventPt(); + // this.substanceTextSearchService.setSearchValue('main-substance-search', this.privateSearchTerm); + } + + export() { + if (this.etag) { + const extension = 'xlsx'; + const url = this.getApiExportUrl(this.etag, extension); + if (this.authService.getUser() !== '') { + const dialogReference = this.dialog.open(ExportDialogComponent, { + // height: '215x', + width: '700px', + data: { 'extension': extension, 'type': 'browseAdverseEventPt', 'entity': 'adverseeventpt', 'hideOptionButtons': true } + }); + // this.overlayContainer.style.zIndex = '1002'; + dialogReference.afterClosed().subscribe(response => { + // this.overlayContainer.style.zIndex = null; + const name = response.name; + const id = response.id; + if (name && name !== '') { + this.loadingService.setLoading(true); + const fullname = name + '.' + extension; + this.authService.startUserDownload(url, this.privateExport, fullname, id).subscribe(response => { + // this.authService.startUserDownload(url, this.privateExport, fullname).subscribe(response => { + this.loadingService.setLoading(false); + const navigationExtras: NavigationExtras = { + queryParams: { + totalSub: this.totalAdverseEventPt + } + }; + const params = { 'total': this.totalAdverseEventPt }; + this.router.navigate(['/user-downloads/', response.id]); + }, error => this.loadingService.setLoading(false)); + } + }); + } + } + } + + getApiExportUrl(etag: string, extension: string): string { + return this.adverseEventService.getApiExportUrlPt(etag, extension); + } + + processSubstanceSearch(searchValue: string) { + this.privateSearchTerm = searchValue; + this.setSearchTermValue(); + } + + increaseOverlayZindex(): void { + this.overlayContainer.style.zIndex = '1002'; + } + + decreaseOverlayZindex(): void { + this.overlayContainer.style.zIndex = null; + } + + getFaersDashboardRecordByName(): void { + // Get FAERS Name from database table that contains 'P' and 'G' in name. + // Example: Acetazolamide (G) instead of GSRS name Acetazolamide + this.adverseEventPtList.forEach((element, index) => { + if (element.name != null) { + const faersNameSubscription = this.adverseEventService.getFaersDashboardRecordByName(element.name).subscribe(results => { + if (results) { + if (results.name) { + element._faersDashboardUrl = this.FAERSDashboardAdverseEventUrl + results.name + this.FAERSDashboardReactionTerm; + } + } + }); + this.subscriptions.push(faersNameSubscription); + } + }); + } + + getFaersDashboardUrl(): void { + if (this.configService.configData) { + if (this.configService.configData.FAERSDashboardAdverseEventUrl + && this.configService.configData.FAERSDashboardAdverseEventUrl !== null) { + const faersUrlConfig = this.configService.configData.FAERSDashboardAdverseEventUrl; + + // FULL FAERS DASHBOARD URL + // faersUrl + /select/Search%20Term/ + FaersName + /select/Reaction%20Term/ + ptTerm + /select/Reaction%20Group/ + primSoc; + this.FAERSDashboardAdverseEventUrl = faersUrlConfig + this.FAERSDashboardSearchTerm; + } + } + } + + getDecodeURL(value: string): string { + let result = ''; + if (value !== null) { + result = decodeURIComponent(value); + } + return result; + } + +} diff --git a/src/app/fda/adverse-event/adverse-events-pt-browse/adverse-events-pt-search-sort-values.ts b/src/app/fda/adverse-event/adverse-events-pt-browse/adverse-events-pt-search-sort-values.ts new file mode 100644 index 000000000..09d8ec93c --- /dev/null +++ b/src/app/fda/adverse-event/adverse-events-pt-browse/adverse-events-pt-search-sort-values.ts @@ -0,0 +1,80 @@ +export const adverseEventPtSearchSortValues = [ + { + 'value': 'default', + 'display': 'Relevance', + 'displayedColumns': 'default', + 'direction': 'asc' + }, + { + 'value': '^root_ptTerm', + 'display': 'PT Term, Ascending', + 'displayedColumns': 'ptTerm', + 'direction': 'asc' + }, + { + 'value': '$root_ptTerm', + 'display': 'PT Term, Descending', + 'displayedColumns': 'ptTerm', + 'direction': 'desc' + }, + { + 'value': '^root_primSoc', + 'display': 'Prim SOC, Ascending', + 'displayedColumns': 'primSoc', + 'direction': 'asc' + }, + { + 'value': '$root_primSoc', + 'display': 'Prim SOC, Descending', + 'displayedColumns': 'primSoc', + 'direction': 'desc' + }, + { + 'value': '^root_caseCount', + 'display': 'Case Count, Ascending', + 'displayedColumns': 'caseCount', + 'direction': 'asc' + }, + { + 'value': '$root_caseCount', + 'display': 'Case Count, Descending', + 'displayedColumns': 'caseCount', + 'direction': 'desc' + }, + { + 'value': '^root_ptCount', + 'display': 'PT Count, Ascending', + 'displayedColumns': 'ptCount', + 'direction': 'asc' + }, + { + 'value': '$root_ptCount', + 'display': 'PT Count, Descending', + 'displayedColumns': 'ptCount', + 'direction': 'desc' + }, + { + 'value': '^root_prr', + 'display': 'PRR, Ascending', + 'displayedColumns': 'prr', + 'direction': 'asc' + }, + { + 'value': '$root_prr', + 'display': 'PRR, Descending', + 'displayedColumns': 'prr', + 'direction': 'desc' + }, + { + 'value': '^root_name', + 'display': 'Ingredient, Ascending', + 'displayedColumns': 'ingredientName', + 'direction': 'asc' + }, + { + 'value': '$root_name', + 'display': 'Ingredient Name, Descending', + 'displayedColumns': 'ingredientName', + 'direction': 'desc' + } +]; diff --git a/src/app/fda/adverse-event/model/adverse-event.model.ts b/src/app/fda/adverse-event/model/adverse-event.model.ts new file mode 100644 index 000000000..6ba3fb170 --- /dev/null +++ b/src/app/fda/adverse-event/model/adverse-event.model.ts @@ -0,0 +1,39 @@ +export interface AdverseEventPt { + id?: number; + substanceId?: string; + substanceKey?: string; + name?: string; + ptTerm?: string; + primSoc?: string; + caseCount?: string; + socCount?: string; + ptCount?: string; + socCountPercent?: string; + ptCountPercent?: string; + ptCountTotalVsDrug?: string; + prr?: string; + _faersDashboardUrl?: string; +} + +export interface AdverseEventDme { + id?: number; + substanceId?: string; + substanceKey?: string; + name?: string; + dmeReactions?: string; + ptTermMeddra?: string; + caseCount?: string; + dmeCount?: string; + dmeCountPercent?: string; +} + +export interface AdverseEventCvm { + id?: number; + substanceId?: string; + substanceKey?: string; + name?: string; + routeOfAdmin?: string; + species?: string; + adverseEvent?: string; + aeCount?: string; +} diff --git a/src/app/fda/adverseevent/service/adverseevent.service.spec.ts b/src/app/fda/adverse-event/service/adverseevent.service.spec.ts similarity index 100% rename from src/app/fda/adverseevent/service/adverseevent.service.spec.ts rename to src/app/fda/adverse-event/service/adverseevent.service.spec.ts diff --git a/src/app/fda/adverse-event/service/adverseevent.service.ts b/src/app/fda/adverse-event/service/adverseevent.service.ts new file mode 100644 index 000000000..29b81fa77 --- /dev/null +++ b/src/app/fda/adverse-event/service/adverseevent.service.ts @@ -0,0 +1,314 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http'; +import { Observable, throwError, of } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { BaseHttpService } from '@gsrs-core/base'; +import { ConfigService } from '@gsrs-core/config'; +import { PagingResponse } from '@gsrs-core/utils'; +import { Facet } from '@gsrs-core/facets-manager'; +import { FacetParam, FacetHttpParams, FacetQueryResponse } from '@gsrs-core/facets-manager'; +import { AdverseEventPt, AdverseEventDme, AdverseEventCvm } from '../model/adverse-event.model'; +import { SubstanceSuggestionsGroup } from '@gsrs-core/utils/substance-suggestions-group.model'; + +// import { SubstanceFacetParam } from '../../../core/substance/substance-facet-param.model'; +// import { SubstanceHttpParams } from '../../../core/substance/substance-http-params'; + + +@Injectable( + { + providedIn: 'root', + } +) + +export class AdverseEventService extends BaseHttpService { + + totalRecords = 0; + + apiBaseUrlWithEntityPtContext = this.configService.configData.apiBaseUrl + 'api/v1/adverseeventpt' + '/'; + apiBaseUrlWithEntityDmeContext = this.configService.configData.apiBaseUrl + 'api/v1/adverseeventdme' + '/'; + apiBaseUrlWithEntityCvmContext = this.configService.configData.apiBaseUrl + 'api/v1/adverseeventcvm' + '/'; + + constructor( + public http: HttpClient, + public configService: ConfigService + ) { + super(configService); + } + + getAdverseEventPt( + order: string, + skip: number = 0, + pageSize: number = 10, + searchTerm?: string, + facets?: FacetParam, + adverseEventType?: string, + ): Observable> { + let params = new FacetHttpParams(); + params = params.append('skip', skip.toString()); + params = params.append('top', pageSize.toString()); + if (searchTerm !== null && searchTerm !== '') { + params = params.append('q', searchTerm); + } + + params = params.appendFacetParams(facets); + + if (order != null && order !== '') { + params = params.append('order', order); + } + + /* + let selectedTypeContext = this.apiBaseUrlWithEntityPtContext; + + if (adverseEventType) { + if (adverseEventType === 'pt') { + selectedTypeContext = this.apiBaseUrlWithEntityPtContext; + } else if (adverseEventType === 'dme') { + selectedTypeContext = this.apiBaseUrlWithEntityDmeContext; + } else if (adverseEventType === 'cvm') { + selectedTypeContext = this.apiBaseUrlWithEntityCvmContext; + } + } + */ + const url = this.apiBaseUrlWithEntityPtContext + 'search'; + const options = { + params: params + }; + + return this.http.get>(url, options); + } + + getAdverseEventDme( + order: string, + skip: number = 0, + pageSize: number = 10, + searchTerm?: string, + facets?: FacetParam, + adverseEventType?: string, + ): Observable> { + let params = new FacetHttpParams(); + params = params.append('skip', skip.toString()); + params = params.append('top', pageSize.toString()); + if (searchTerm !== null && searchTerm !== '') { + params = params.append('q', searchTerm); + } + + params = params.appendFacetParams(facets); + + if (order != null && order !== '') { + params = params.append('order', order); + } + + const url = this.apiBaseUrlWithEntityDmeContext + 'search'; + const options = { + params: params + }; + + return this.http.get>(url, options); + } + + getAdverseEventCvm( + order: string, + skip: number = 0, + pageSize: number = 10, + searchTerm?: string, + facets?: FacetParam, + adverseEventType?: string, + ): Observable> { + let params = new FacetHttpParams(); + params = params.append('skip', skip.toString()); + params = params.append('top', pageSize.toString()); + if (searchTerm !== null && searchTerm !== '') { + params = params.append('q', searchTerm); + } + + params = params.appendFacetParams(facets); + + if (order != null && order !== '') { + params = params.append('order', order); + } + + const url = this.apiBaseUrlWithEntityCvmContext + 'search'; + const options = { + params: params + }; + + return this.http.get>(url, options); + } + + getAdverseEventPtFacets(facet: Facet, searchTerm?: string, nextUrl?: string): Observable { + let url: string; + if (searchTerm) { + url = `${this.configService.configData.apiBaseUrl}api/v1/adverseeventpt/search/@facets?wait=false&kind=gov.hhs.gsrs.adverseevents.adverseeventpt.models.AdverseEventPt&skip=0&fdim=200&sideway=true&field=${facet.name.replace(' ', '+')}&top=14448&fskip=0&fetch=100&termfilter=SubstanceDeprecated%3Afalse&order=%24lastEdited&ffilter=${searchTerm}`; + } else if (nextUrl != null) { + url = nextUrl; + } else { + url = facet._self; + } + return this.http.get(url); + } + + getAdverseEventDmeFacets(facet: Facet, searchTerm?: string, nextUrl?: string): Observable { + let url: string; + if (searchTerm) { + url = `${this.configService.configData.apiBaseUrl}api/v1/adverseeventdme/search/@facets?wait=false&kind=gov.hhs.gsrs.adverseevents.adverseeventdme.models.AdverseEventDme&skip=0&fdim=200&sideway=true&field=${facet.name.replace(' ', '+')}&top=14448&fskip=0&fetch=100&termfilter=SubstanceDeprecated%3Afalse&order=%24lastEdited&ffilter=${searchTerm}`; + } else if (nextUrl != null) { + url = nextUrl; + } else { + url = facet._self; + } + return this.http.get(url); + } + + getAdverseEventCvmFacets(facet: Facet, searchTerm?: string, nextUrl?: string): Observable { + let url: string; + if (searchTerm) { + url = `${this.configService.configData.apiBaseUrl}api/v1/adverseeventcvm/search/@facets?wait=false&kind=gov.hhs.gsrs.adverseevents.adverseeventcvm.models.AdverseEventCvm&skip=0&fdim=200&sideway=true&field=${facet.name.replace(' ', '+')}&top=14448&fskip=0&fetch=100&termfilter=SubstanceDeprecated%3Afalse&order=%24lastEdited&ffilter=${searchTerm}`; + } else if (nextUrl != null) { + url = nextUrl; + } else { + url = facet._self; + } + return this.http.get(url); + } + + exportBrowseApplicationsUrl( + skip: number = 0, + pageSize: number = 10, + searchTerm?: string, + facets?: FacetParam + ): string { + let params = new FacetHttpParams(); + // params = params.append('skip', skip.toString()); + // params = params.append('top', '1000'); + params = params.append('page', '1'); + if (searchTerm !== null && searchTerm !== '') { + params = params.append('q', searchTerm); + } + + params = params.appendFacetParams(facets); + + const url = this.baseUrl + 'exportApplications?' + params; + const options = { + params: params + }; + + return url; + } + + getAdverseEventSearchSuggestions(searchTerm: string, eventCategory: string): Observable { + if (eventCategory && eventCategory === 'adverseEventPtSearch') { + return this.http.get(this.apiBaseUrlWithEntityPtContext + 'suggest?q=' + searchTerm); + } else if (eventCategory && eventCategory === 'adverseEventDmeSearch') { + return this.http.get(this.apiBaseUrlWithEntityDmeContext + 'suggest?q=' + searchTerm); + } else if (eventCategory && eventCategory === 'adverseEventCvmSearch') { + return this.http.get(this.apiBaseUrlWithEntityCvmContext + 'suggest?q=' + searchTerm); + } else { + return null; + } + } + + getSubstanceAdverseEventPt( + bdnum: string, page: number, pageSize: number, orderBy: string, ascDescDir: string + ): Observable> { + const url = this.baseUrl + 'adverseEventPtListByBdnum?bdnum=' + bdnum + '&page=' + (page + 1) + '&pageSize=' + + pageSize + '&orderBy=' + orderBy + '&ascDescDir=' + ascDescDir; + + return this.http.get>(url) + .pipe( + map(results => { + this.totalRecords = results['totalRecords']; + return results['data']; + }) + ); + } + + getSubstanceAdverseEventPtAdv( + bdnum: string, page: number, pageSize: number, orderBy: number, ascDescDir: string + ): Observable> { + const orderByParam = 'order[0][column]=' + orderBy; + const ascDescDirParam = 'order[0][dir]=' + ascDescDir; + const start = 'start=' + '0'; + const length = 'length=' + pageSize; + const url = this.baseUrl + 'advSearchResult?searchCategory=adversept&searchBy=bdnum&matchType=IN&q=' + bdnum + '&disp=d&dispFrom=detail&' + start + '&' + length + '&' + orderByParam + '&' + ascDescDirParam; + + // const url = this.baseUrl + 'adverseEventPtListByBdnum?bdnum=' + bdnum + '&page=' + (page + 1) + '&pageSize=' + // + pageSize + '&orderBy=' + orderBy + '&ascDescDir=' + ascDescDir; + + return this.http.get>(url) + .pipe( + map(results => { + this.totalRecords = results['recordsTotal']; + return results['data']; + }) + ); + } + + getSubstanceAdverseEventDme( + bdnum: string, page: number, pageSize: number + ): Observable> { + const url = this.baseUrl + 'adverseEventDmeListByBdnum?bdnum=' + bdnum + '&page=' + (page + 1) + '&pageSize=' + pageSize; + + return this.http.get>(url) + .pipe( + map(results => { + this.totalRecords = results['totalRecords']; + return results['data']; + }) + ); + + } + + getSubstanceAdverseEventCvm( + bdnum: string, page: number, pageSize: number + ): Observable> { + const url = this.baseUrl + 'adverseEventCvmListByBdnum?bdnum=' + bdnum + '&page=' + (page + 1) + '&pageSize=' + pageSize; + + return this.http.get>(url) + .pipe( + map(results => { + this.totalRecords = results['totalRecords']; + return results['data']; + }) + ); + + } + + getFaersDashboardRecordByName( + name: string + ): Observable { + const url = this.apiBaseUrlWithEntityPtContext + 'faersdashboard/' + name; + return this.http.get(url).pipe( + map(results => { + return results; + }) + ); + } + + getApiExportUrlPt(etag: string, extension: string): string { + const url = this.apiBaseUrlWithEntityPtContext + 'export/' + etag + '/' + extension; + return url; + } + + getApiExportUrlDme(etag: string, extension: string): string { + const url = this.apiBaseUrlWithEntityDmeContext + 'export/' + etag + '/' + extension; + return url; + } + + getApiExportUrlCvm(etag: string, extension: string): string { + const url = this.apiBaseUrlWithEntityCvmContext + 'export/' + etag + '/' + extension; + return url; + } + + getAdverseEventPtListExportUrl(bdnum: string): string { + return this.baseUrl + 'adverseEventPtListExport?bdnum=' + bdnum; + } + + getAdverseEventDmeListExportUrl(bdnum: string): string { + return this.baseUrl + 'adverseEventDmeListExport?bdnum=' + bdnum; + } + + getAdverseEventCvmListExportUrl(bdnum: string): string { + return this.baseUrl + 'adverseEventCvmListExport?bdnum=' + bdnum; + } + +} // class diff --git a/src/app/fda/adverseevent/service/adverseevent.service.ts b/src/app/fda/adverseevent/service/adverseevent.service.ts deleted file mode 100644 index b52152d5a..000000000 --- a/src/app/fda/adverseevent/service/adverseevent.service.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { BaseHttpService } from '@gsrs-core/base'; -import { ConfigService } from '@gsrs-core/config'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; - -@Injectable( - { - providedIn: 'root', - } -) - -export class AdverseEventService extends BaseHttpService { - - totalRecords: 0; - - constructor( - public http: HttpClient, - public configService: ConfigService - ) { - super(configService); - } - - getSubstanceAdverseEventPt( - bdnum: string, page: number, pageSize: number, orderBy: string, ascDescDir: string - ): Observable> { - const url = this.baseUrl + 'adverseEventPtListByBdnum?bdnum=' + bdnum + '&page=' + (page + 1) + '&pageSize=' - + pageSize + '&orderBy=' + orderBy + '&ascDescDir=' + ascDescDir; - - return this.http.get>(url) - .pipe( - map(results => { - this.totalRecords = results['totalRecords']; - return results['data']; - }) - ); - } - - getSubstanceAdverseEventPtAdv( - bdnum: string, page: number, pageSize: number, orderBy: number, ascDescDir: string - ): Observable> { - const orderByParam = 'order[0][column]=' + orderBy; - const ascDescDirParam = 'order[0][dir]=' + ascDescDir; - const start = 'start=' + '0'; - const length = 'length=' + pageSize; - const url = this.baseUrl + 'advSearchResult?searchCategory=adversept&searchBy=bdnum&matchType=IN&q=' + bdnum + '&disp=d&dispFrom=detail&' + start + '&' + length + '&' + orderByParam + '&' + ascDescDirParam; - - // const url = this.baseUrl + 'adverseEventPtListByBdnum?bdnum=' + bdnum + '&page=' + (page + 1) + '&pageSize=' - // + pageSize + '&orderBy=' + orderBy + '&ascDescDir=' + ascDescDir; - - return this.http.get>(url) - .pipe( - map(results => { - this.totalRecords = results['recordsTotal']; - return results['data']; - }) - ); - } - - getSubstanceAdverseEventDme( - bdnum: string, page: number, pageSize: number - ): Observable> { - const url = this.baseUrl + 'adverseEventDmeListByBdnum?bdnum=' + bdnum + '&page=' + (page + 1) + '&pageSize=' + pageSize; - - return this.http.get>(url) - .pipe( - map(results => { - this.totalRecords = results['totalRecords']; - return results['data']; - }) - ); - - } - - getSubstanceAdverseEventCvm( - bdnum: string, page: number, pageSize: number - ): Observable> { - const url = this.baseUrl + 'adverseEventCvmListByBdnum?bdnum=' + bdnum + '&page=' + (page + 1) + '&pageSize=' + pageSize; - - return this.http.get>(url) - .pipe( - map(results => { - this.totalRecords = results['totalRecords']; - return results['data']; - }) - ); - - } - - getAdverseEventPtListExportUrl(bdnum: string): string { - return this.baseUrl + 'adverseEventPtListExport?bdnum=' + bdnum; - } - - getAdverseEventDmeListExportUrl(bdnum: string): string { - return this.baseUrl + 'adverseEventDmeListExport?bdnum=' + bdnum; - } - - getAdverseEventCvmListExportUrl(bdnum: string): string { - return this.baseUrl + 'adverseEventCvmListExport?bdnum=' + bdnum; - } - -} // class diff --git a/src/app/fda/application/application-details/application-darrts-details/application-darrts-details.component.html b/src/app/fda/application/application-details/application-darrts-details/application-darrts-details.component.html index cee814640..b063622c1 100644 --- a/src/app/fda/application/application-details/application-darrts-details/application-darrts-details.component.html +++ b/src/app/fda/application/application-details/application-darrts-details/application-darrts-details.component.html @@ -1,5 +1,9 @@ +
    +



    + {{message}} +
    -
    +
    @@ -22,7 +26,7 @@ Application Type Number:
    - {{application.appType}} {{application.appNumber}} + {{application.appType}} {{application.appNumber}}
    @@ -60,7 +64,7 @@ Status Date:
    - {{application.statusDate}} + {{application.statusDate |date: 'MM/dd/yyyy'}}
    @@ -95,10 +99,10 @@
    - + Route of Administration:
    - + {{application.routeOfAdmin}}
    @@ -110,7 +114,7 @@
    - • {{ind}}
    + • {{ind.indication}}
    @@ -134,7 +138,7 @@
    Part No Substance Name StructureBdnumSubstance Key Unii Activity Potency{{ x.productNo }} {{ x.partNo }} -
    - {{x.name}} +
    + + + {{x._ingredientname}} +
    -
    - +
    +
    {{ x.bdnum }}{{ x.unii }}{{ x.substanceKey }}{{ x._approvalID }} {{ x.activity }} {{ x.potency }}{{ x.parentBdnum }}{{ x.parentDisplayTerm }}{{ x._parentSubstanceKey }}{{ x._parentDisplayTerm }}
    @@ -170,8 +179,10 @@
    + \ No newline at end of file diff --git a/src/app/fda/application/application-details/application-darrts-details/application-darrts-details.component.scss b/src/app/fda/application/application-details/application-darrts-details/application-darrts-details.component.scss index 05f2214ec..ff8e1bdfb 100644 --- a/src/app/fda/application/application-details/application-darrts-details/application-darrts-details.component.scss +++ b/src/app/fda/application/application-details/application-darrts-details/application-darrts-details.component.scss @@ -3,28 +3,28 @@ display: flex; align-items: center; justify-content: center; - /*padding: 20px;*/ + margin-top: 20px; } - + .details-box { max-width: 1028px; width: 100%; box-sizing: border-box; margin-bottom: 20px; } - + .box { display: flex; align-items: top; } - + .mat-card { max-width: 1028px; width: 100%; /*box-sizing: border-box;*/ /*margin-bottom: 20px;*/ } - + .mat-card-title { display: flex; box-sizing: border-box; @@ -34,45 +34,44 @@ white-space: nowrap; white-space: normal; } - + .row { display: flex; width: 100%; - border-bottom: solid 1px rgba(0, 0, 0, 0.12) + border-bottom: solid 1px var(--box-shadow-color-3); /* &:not(:last-child) { - border-bottom: solid 1px rgba(0, 0, 0, 0.12); + border-bottom: solid 1px var(--box-shadow-color-3); } */ } - + .row-property { display: flex; width: 50%; /*padding: 8px;*/ } - + .row-property-key { - min-width: 35%; - max-width: 35%; - padding: 7px; - font-size: 12px; - font-weight: 500; - /*background-color: #edeff2;*/ + min-width: 35%; + max-width: 35%; + padding: 7px; + font-size: 12px; + font-weight: 500; } - + .row-property-value { min-width: 65%; max-width: 65%; padding: 7px; font-size: 12px; } - + .row-property-2 { display: flex; width: 100%; } - + .row-property-key-2 { min-width: 17%; max-width: 17%; @@ -80,24 +79,24 @@ font-size: 12px; font-weight: 500; } - + .row-property-value-2 { min-width: 83%; max-width: 83%; padding: 6px; font-size: 12px; } - + .title { font-size: 15px; font-weight: bold; font-family: Verdana; - color: #0857d6; + color: var(--primary-title-color); padding-right: 5px; text-align: top; margin-bottom: 9px; } - + .title2 { display: flex; box-sizing: border-box; @@ -112,23 +111,23 @@ .margintopneg10px { margin-top: -10px; } - + .padtop50px { padding-top: 50px; } - + .margintop90px { margin-top: 90px; } - + .padtop17px { padding-top: 17px; } - + .padleft20px { padding-left: 20px; } - + .padleft50px { padding-left: 50px; } @@ -136,144 +135,150 @@ .padleft200px { padding-left: 200px; } - + .padbottom10px { padding-bottom: -10px; } - + .font9px { font-size: 9px; } - + .font10px { font-size: 10px; } - + .font14px { font-size: 14px; } - + +.font16px { + font-size: 16px; +} + .font20px { font-size: 20px; } .colorgray { - color: gray; - } - - .fontbold { - font-weight: 500; - } - - .colororange { - color: #E55913; - } - - .bordergray { - border: 1px solid gray; + color: var(--regular-grey-color); } - - table.blueTable { + +.fontbold { + font-weight: 500; +} + +.colororange { + color: var(--orange-color); +} + +.colorblue { + color: var(--primary-title-color); +} + +.bordergray { + border: 1px solid var(--regular-grey-color); +} + +table.blueTable { font-family: Roboto, "Helvetica Neue", sans-serif; - /*font-family: Verdana, Geneva, sans-serif;*/ - border: 1px solid #1C6EA4; - background-color: #f2f2f2; + border: 1px solid var(--secondary-blue-color); + background-color: var(--table-bg-color); width: 100%; text-align: left; border-collapse: collapse; } - + table.blueTable td, table.blueTable th { - border: 1px solid #AAAAAA; + border: 1px solid var(--table-th-border-color); padding: 3px 2px; } - + table.blueTable tbody td { font-size: 12px; vertical-align: top; padding: 10px 10px; } - + table.blueTable tr:nth-child(even) { - background: #f5faee; + background: var(--table-tr-even-bg-color); } - + table.blueTable thead { - background: #e8ebe1; - border-bottom: 1px solid #444444; + background: var(--table-thead-bg-color); + border-bottom: 1px solid var(--table-thead-border-color); } - + table.blueTable thead th { font-size: 12px; font-weight: 700; - color: #1a1a1a; - border-left: 1px solid #D0E4F5; + color: var(--table-th-color); + border-left: 1px solid var(--table-th-border-color-2); padding: 10px 10px; } - + table.blueTable thead th:first-child { border-left: none; } - + table.blueTable tfoot { font-size: 14px; font-weight: bold; - color: #FFFFFF; - background: #D0E4F5; - border-top: 2px solid #444444; + color: var(--white-color); + background: var(--table-th-border-color-2); + border-top: 2px solid var(--table-thead-border-color); } - + table.blueTable tfoot td { font-size: 14px; } - + table.blueTable tfoot .links { text-align: right; } - + table.blueTable tfoot .links a{ display: inline-block; - background: #1C6EA4; - color: #FFFFFF; + background: var(--secondary-blue-color); + color: var(--white-color); padding: 2px 8px; border-radius: 5px; } - + @media(max-width: 700px) { .mat-card-title { flex-direction: column; align-items: flex-start; } - + .product-details-container { padding: 10px; } } - + @media(max-width: 918px) { .product-property { width: 100%; - border:1px solid red; + border:1px solid var(--regular-red-color); } - + .row { flex-direction: column; - + &:not(:last-child) { border-bottom: none; - + .product-property { - border-bottom: solid 1px rgba(0, 0, 0, 0.12); + border-bottom: solid 1px var(--box-shadow-color-3); } } &:last-child { .row { &:not(:last-child) { - border-bottom: solid 1px rgba(0, 0, 0, 0.12); + border-bottom: solid 1px var(--box-shadow-color-3); } } } } } - - \ No newline at end of file + diff --git a/src/app/fda/application/application-details/application-darrts-details/application-darrts-details.component.ts b/src/app/fda/application/application-details/application-darrts-details/application-darrts-details.component.ts index a3312c5f2..9532facdb 100644 --- a/src/app/fda/application/application-details/application-darrts-details/application-darrts-details.component.ts +++ b/src/app/fda/application/application-details/application-darrts-details/application-darrts-details.component.ts @@ -1,12 +1,15 @@ import { Component, OnInit } from '@angular/core'; import { ApplicationService } from '../../service/application.service'; +import { GeneralService } from '../../../service/general.service'; import { ActivatedRoute, Router } from '@angular/router'; import { LoadingService } from '@gsrs-core/loading'; +import { Title } from '@angular/platform-browser'; import { MainNotificationService } from '@gsrs-core/main-notification'; import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; import { UtilsService } from '../../../../core/utils/utils.service'; // import { AuthService } from '@gsrs-core/auth/auth.service'; -import { ApplicationDetailsBaseComponent} from '../application-details-base.component'; +import { ApplicationDetailsBaseComponent } from '../application-details-base.component'; +import { element } from 'protractor'; @Component({ selector: 'app-application-darrts-details', @@ -18,16 +21,18 @@ export class ApplicationDarrtsDetailsComponent extends ApplicationDetailsBaseCom constructor( public applicationService: ApplicationService, + public generalService: GeneralService, activatedRoute: ActivatedRoute, loadingService: LoadingService, mainNotificationService: MainNotificationService, router: Router, gaService: GoogleAnalyticsService, utilsService: UtilsService, + titleService: Title // authService: AuthService, ) { - super(applicationService, activatedRoute, loadingService, mainNotificationService, router, gaService, utilsService); - // , authService); + super(applicationService, generalService, activatedRoute, loadingService, mainNotificationService, router, gaService, utilsService, titleService); + // , authService); } ngOnInit() { @@ -43,13 +48,52 @@ export class ApplicationDarrtsDetailsComponent extends ApplicationDetailsBaseCom } getApplicationDarrtsDetails(): void { - this.applicationService.getApplicationDarrtsDetails(this.appType, this.appNumber).subscribe(response => { + const appDarrtsSubscription = this.applicationService.getApplicationDarrtsDetails(this.appType, this.appNumber).subscribe(response => { this.application = response; + if (response) { + this.titleService.setTitle(`Application ` + this.application.appType + ' ' + this.application.appNumber); + this.getSubstanceBySubstanceKey(); + } this.loadingService.setLoading(false); }, error => { - this.handleSubstanceRetrivalError(); + // this.handleSubstanceRetrivalError(); + this.message = 'No Application (Darrts) record found'; + this.loadingService.setLoading(false); }); + this.subscriptions.push(appDarrtsSubscription); + + } + + getSubstanceBySubstanceKey() { + if (this.application != null) { + this.application.ingredientList.forEach(elementIngred => { + if (elementIngred != null) { + // Get Substance Details, uuid, approval_id, substance name + if (elementIngred.substanceKey) { + const subSubscription = this.generalService.getSubstanceByAnyId(elementIngred.substanceKey).subscribe(response => { + if (response) { + elementIngred._substanceUuid = response.uuid; + elementIngred._ingredientname = response._name; + elementIngred._approvalID = response._approvalIDDisplay; + } + }); + this.subscriptions.push(subSubscription); + + + // Get Substance Key Parent Concept + const conceptSubscription = this.applicationService.getSubstanceParentConcept(elementIngred.substanceKey).subscribe(response => { + if (response) { + elementIngred._parentSubstanceKey = response.parentSubstanceKey; + elementIngred._parentDisplayTerm = response.parentDisplayTerm; + } + }); + this.subscriptions.push(conceptSubscription); + + } + } + }); + } } } diff --git a/src/app/fda/application/application-details/application-details-base.component.ts b/src/app/fda/application/application-details/application-details-base.component.ts index 008157098..8c4e12bb1 100644 --- a/src/app/fda/application/application-details/application-details-base.component.ts +++ b/src/app/fda/application/application-details/application-details-base.component.ts @@ -1,12 +1,15 @@ import { Component, OnInit } from '@angular/core'; -import { ApplicationService } from '../service/application.service'; import { ActivatedRoute, Router } from '@angular/router'; -import { LoadingService } from '@gsrs-core/loading'; -import { MainNotificationService } from '@gsrs-core/main-notification'; +import { SafeUrl } from '@angular/platform-browser'; +import { Subscription } from 'rxjs'; +import { Title } from '@angular/platform-browser'; import { AppNotification, NotificationType } from '@gsrs-core/main-notification'; +import { LoadingService } from '@gsrs-core/loading'; import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; import { UtilsService } from '../../../core/utils/utils.service'; -import { SafeUrl } from '@angular/platform-browser'; +import { MainNotificationService } from '@gsrs-core/main-notification'; +import { ApplicationService } from '../service/application.service'; +import { GeneralService } from '../../service/general.service'; @Component({ selector: 'app-application-details-base', @@ -24,15 +27,18 @@ export class ApplicationDetailsBaseComponent implements OnInit { isAdmin = false; updateApplicationUrl: string; message = ''; + subscriptions: Array = []; constructor( public applicationService: ApplicationService, + public generalService: GeneralService, public activatedRoute: ActivatedRoute, public loadingService: LoadingService, private mainNotificationService: MainNotificationService, private router: Router, private gaService: GoogleAnalyticsService, private utilsService: UtilsService, + public titleService: Title // private authService: AuthService, ) { } @@ -43,53 +49,90 @@ export class ApplicationDetailsBaseComponent implements OnInit { this.getApplicationDetails(); } else { this.message = 'The application Id in url should be a number'; - this.loadingService.setLoading(false); } + } else if ((this.appType != null) && (this.appNumber != null)) { + // get Application Id by Type and Number. To be done soon. } else { this.handleSubstanceRetrivalError(); } + this.loadingService.setLoading(false); + } + + ngOnDestroy(): void { + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); } getApplicationDetails(): void { - this.applicationService.getApplicationDetails(this.id).subscribe(response => { + const appIdSubscription = this.applicationService.getApplicationById(this.id).subscribe(response => { this.application = response; if (Object.keys(this.application).length > 0) { - this.getSubstanceDetails(); + this.titleService.setTitle(`Application ` + this.application.appType + ' ' + this.application.appNumber); + // Get Substance by Substance Key + this.getSubstanceBySubstanceKey(); + + // Get Application History + const appHistSubscription = this.applicationService.getApplicationHistory(this.id).subscribe(response => { + this.application.applicationHistoryList = []; + this.application.applicationHistoryList = response; + }); + this.subscriptions.push(appHistSubscription); + + // Get Product Technical Effect + const prodTechEffectSubscription = this.applicationService.getProductTechnicalEffect(this.id).subscribe(response => { + this.application.productTechEffectList = []; + this.application.productTechEffectList = response; + }); + this.subscriptions.push(prodTechEffectSubscription); + + // Get Product Effected + const prodEffectedSubscription = this.applicationService.getProductEffected(this.id).subscribe(response => { + this.application.productEffectedList = []; + this.application.productEffectedList = response; + }); + this.subscriptions.push(prodEffectedSubscription); + + // Get Clinical Trial Application + const clinicalSubscription = this.applicationService.getClinicalTrialApplication(this.id).subscribe(response => { + this.application.clinicalTrialList = []; + this.application.clinicalTrialList = response; + }); + this.subscriptions.push(clinicalSubscription); } - this.loadingService.setLoading(false); }, error => { - this.handleSubstanceRetrivalError(); + // this.message = 'No Application record found'; + // this.handleSubstanceRetrivalError(); }); + this.subscriptions.push(appIdSubscription); } - getSubstanceDetails() { + getSubstanceBySubstanceKey() { if (this.application != null) { this.application.applicationProductList.forEach(elementProd => { if (elementProd != null) { elementProd.applicationIngredientList.forEach(elementIngred => { if (elementIngred != null) { - // Get Ingredient Name - if (elementIngred.bdnum) { - this.applicationService.getSubstanceDetailsByBdnum(elementIngred.bdnum).subscribe(response => { + // Get Substance Details, uuid, approval_id, substance name + if (elementIngred.substanceKey) { + const ingSubscription = this.generalService.getSubstanceByAnyId(elementIngred.substanceKey).subscribe(response => { if (response) { - if (response.substanceId) { - elementIngred.substanceId = response.substanceId; - elementIngred.ingredientName = response.name; - } + elementIngred._substanceUuid = response.uuid; + elementIngred._ingredientName = response._name; } }); + this.subscriptions.push(ingSubscription); } // Get Basis of Strength - if (elementIngred.basisOfStrengthBdnum) { - this.applicationService.getSubstanceDetailsByBdnum(elementIngred.basisOfStrengthBdnum).subscribe(response => { + if (elementIngred.basisOfStrengthSubstanceKey) { + const basisSubscription = this.generalService.getSubstanceByAnyId(elementIngred.basisOfStrengthSubstanceKey).subscribe(response => { if (response) { - if (response.substanceId) { - elementIngred.basisOfStrengthSubstanceId = response.substanceId; - elementIngred.basisOfStrengthIngredientName = response.name; - } + elementIngred._basisOfStrengthSubstanceUuid = response.uuid; + elementIngred._basisOfStrengthIngredientName = response._name; } }); + this.subscriptions.push(basisSubscription); } } }); @@ -116,7 +159,7 @@ export class ApplicationDetailsBaseComponent implements OnInit { }; this.mainNotificationService.setNotification(notification); setTimeout(() => { - this.router.navigate(['/browse-substance']); + // this.router.navigate(['/browse-substance']); }, 5000); } diff --git a/src/app/fda/application/application-details/application-details/application-details.component.html b/src/app/fda/application/application-details/application-details/application-details.component.html index 7298c5377..59d0568e4 100644 --- a/src/app/fda/application/application-details/application-details/application-details.component.html +++ b/src/app/fda/application/application-details/application-details/application-details.component.html @@ -2,15 +2,14 @@



    {{message}} -
    -
    -
    - - -
    +
    +
    + + +
    +
    Application Details -    -   +     @@ -20,472 +19,484 @@
    -
    + +
    Created By: {{application.createdBy}}    Create Date: - {{application.createDate|date: 'MM/dd/yyyy hh:mm:ss a'}}    + {{application.creationDate|date: 'MM/dd/yyyy hh:mm:ss a'}}    Modified By: {{application.modifiedBy}}    - Modify Date: {{application.modifyDate|date: 'MM/dd/yyyy hh:mm:ss a'}} + Modify Date: {{application.lastModifiedDate|date: 'MM/dd/yyyy hh:mm:ss a'}}
    - +
    + - + -
    -
    -
    - Application Type and Number: -
    -
    - {{application.appType}} {{application.appNumber}} -
    +
    +
    +
    + Application Type and Number:
    -
    -
    - Center: -
    -
    - {{application.center}} -
    +
    + {{application.appType}} {{application.appNumber}} +
    +
    +
    +
    + Center: +
    +
    + {{application.center}}
    +
    -
    -
    -
    - Sponsor Name: -
    -
    - {{application.sponsorName}} -
    +
    +
    +
    + Sponsor Name:
    -
    -
    - Non Proprietary Name: -
    -
    - {{application.nonProprietaryName}} -
    +
    + {{application.sponsorName}}
    +
    +
    + Non Proprietary Name: +
    +
    + {{application.nonProprietaryName}} +
    +
    +
    -
    -
    -
    - Title: -
    -
    - {{application.title}} -
    +
    +
    +
    + Title:
    -
    -
    - External Title: -
    -
    - {{application.externalTitle}} -
    +
    + {{application.title}} +
    +
    +
    +
    + External Title: +
    +
    + {{application.externalTitle}}
    +
    -
    -
    -
    - Public Domain: -
    -
    - {{application.publicDomain}} -
    +
    +
    +
    + Application Sub Type:
    -
    -
    - Version: -
    -
    - {{application.version}} -
    +
    + {{application.appSubType}} +
    +
    +
    +
    + Division Class Desc: +
    +
    + {{application.divisionClassDesc}}
    +
    -
    -
    -
    - Application Sub Type: -
    -
    - {{application.appSubType}} -
    +
    +
    +
    + Application Status:
    -
    -
    - Division Class Desc: -
    -
    - {{application.divisionClassDesc}} -
    +
    + {{application.status}}
    +
    +
    + Submit Date: +
    +
    + {{application.submitDate}} +
    +
    +
    -
    -
    -
    - Application Status: -
    -
    - {{application.status}} -
    +
    +
    +
    + Provenance:
    -
    -
    - Submit Date: -
    -
    - {{application.submitDate}} -
    +
    + {{application.provenance}}
    +
    +
    + Status Date: +
    +
    + {{application.statusDate}} +
    +
    +
    -
    -
    -
    - Source: -
    -
    - {{application.source}} -
    +
    +
    +
    + Public Domain: +
    +
    + {{application.publicDomain}}
    +
    + +
    -
    -
    -
    - Indications: -
    -
    -
    - - • {{ind.indication}}
    -
    -
    +
    +
    +
    + Indications: +
    +
    +
    + + • {{ind.indication}}
    +
    +
    -
    -
    -
    - Clinical Trials: -
    -
    -
    - - - - - - - - - - - - - - - -
    NCT NumberClinical Trials Gov WebsiteTitle
    - {{clinical.nctNumber}} - - {{ clinical.url }} - - {{ clinical.title }} -
    -
    +
    +
    +
    + Clinical Trials: +
    +
    +
    + + + + + + + + + + + + + + + + + +
    #NCT NumberClinical Trials Gov WebsiteTitle
    {{(i+1)}} + {{clinical.trialNumber}} + + + {{ clinical.url }} + + {{ clinical.title }} +
    -
    +
    - - -
    - - -
    -
    - - - Application History ({{application.applicationHistoryList.length}}) - - - - - - - - - - - - - - - - - - - - - -
    Product NameSponsor NameStatusStatus DateCreate Date
    {{ ingred.productName }}{{ ingred.sponsorName }}{{ ingred.status }}{{ ingred.statusDate|date: 'MM/dd/yyyy' }}{{ ingred.createDate|date: 'MM/dd/yyyy' }}
    -
    -
    -
    - + + +
    - -
    -
    - - - Technical Effect ({{application.productTechEffectList.length}}) - - - - - - - - - - - - - -
    Technical Effect
    {{ ingred.technicalEffect }}
    -
    -
    -
    - + +
    +
    + + + Application History ({{application.applicationHistoryList.length}}) + + + + + + + + + + + + + + + + + + + + + +
    Product NameSponsor NameStatusStatus DateCreate Date
    {{ ingred.productName }}{{ ingred.sponsorName }}{{ ingred.status }}{{ ingred.statusDate|date: 'MM/dd/yyyy' }}{{ ingred.createDate|date: 'MM/dd/yyyy' }}
    +
    +
    +
    + - -
    -
    - - - Effected Product ({{application.productEffectedList.length}}) - - - - - - - - - - - - - -
    Effected Product
    {{ ingred.effectedProduct }}
    -
    -
    -
    - + +
    +
    + + + Technical Effect ({{application.productTechEffectList.length}}) + + + + + + + + + + + + + +
    Technical Effect
    {{ ingred.technicalEffect }}
    +
    +
    +
    + + +

    -
    - - -
    -
    - Product {{i + 1}} -
    + + + Effected Product ({{application.productEffectedList.length}}) + + + + + + + + + + + + + +
    Effected Product
    {{ ingred.effectedProduct }}
    +
    +
    +
    + - -
    -
    -
    -
    - Product Name: -
    -
    - {{prodName.productName}} -
    -
    -
    -
    - Product Name Type: -
    -
    - {{prodName.productNameType}} -
    -
    -
    -
    - -
    -
    -
    - Product Name: -
    -
    -
    -
    -
    -
    - Product Name Type: -
    -
    -
    -
    -
    -
    +
    +
    + + +
    +
    + Product {{i + 1}} +
    -
    + +
    +
    - Dosage Form: + Product Name:
    - {{prod.dosageForm}} + {{prodName.productName}}
    - Route of Administration: + Product Name Type:
    - {{prod.routeAdmin}} + {{prodName.productNameType}}
    - +
    +
    - Amount: + Product Name:
    - {{prod.amount}}
    - Unit of Presentation: + Product Name Type:
    - {{prod.unitPresentation}}
    +
    -
    -
    -
    - Unit: -
    -
    - {{prod.unit}} -
    +
    +
    +
    + Dosage Form:
    -
    -
    - Reviewed By: -
    -
    - {{prod.reviewedBy}} {{prod.reviewDate|date: 'MM/dd/yyyy'}} -
    +
    + {{prod.dosageForm}} +
    +
    +
    +
    + Route of Administration: +
    +
    + {{prod.routeAdmin}}
    +
    - - - - Substances in Application ({{prod.applicationIngredientList.length}}) - - - - - - - - - - - - - - - - - - - - - - - - +
    +
    +
    + Amount: +
    +
    + {{prod.amount}} +
    +
    +
    +
    + Unit of Presentation: +
    +
    + {{prod.unitPresentation}} +
    +
    +
    - - - - - - - - - - - - - -
    Applicant Ingredient NameIngredient NameBasis Of StrengthIngredient TypeAverageLowHighLow LimitHigh LimitUnitNon Numeric ValueGradeReviewed By
    {{ ingred.applicantIngredName }} - -
    - - ({{ ingred.bdnum }}) - -
    - -
    - - ({{ ingred.basisOfStrengthBdnum }}) - -
    {{ ingred.ingredientType }}{{ ingred.average }}{{ ingred.low }}{{ ingred.high }}{{ ingred.lowLimit }}{{ ingred.highLimit }}{{ ingred.unit }}{{ ingred.nonNumericValue }}{{ ingred.grade }}{{ ingred.reviewedBy }} {{ ingred.reviewDate|date: 'MM/dd/yyyy' }}
    -
    -
    -
    - +
    +
    +
    + Unit: +
    +
    + {{prod.unit}} +
    +
    +
    +
    + Reviewed By: +
    +
    + {{prod.reviewedBy}} {{prod.reviewDate|date: 'MM/dd/yyyy hh:mm:ss a' }} +
    +
    +
    + + + + Substances in Application ({{prod.applicationIngredientList.length}}) + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + +
    Applicant Ingredient NameIngredient NameBasis Of StrengthIngredient TypeAverageLowHighLow LimitHigh LimitUnitNon Numeric ValueGradeReviewed By
    {{ ingred.applicantIngredName }} + + + + + + {{ ingred.ingredientType }}{{ ingred.average }}{{ ingred.low }}{{ ingred.high }}{{ ingred.lowLimit }}{{ ingred.highLimit }}{{ ingred.unit }}{{ ingred.nonNumericValue }}{{ ingred.grade }}{{ ingred.reviewedBy }}  {{ ingred.reviewDate|date: 'MM/dd/yyyy hh:mm:ss a' }}
    +
    +
    + +

    +
    + +
    - -



    -
    No Application record found.
    -
    +
    +



    \ No newline at end of file diff --git a/src/app/fda/application/application-details/application-details/application-details.component.scss b/src/app/fda/application/application-details/application-details/application-details.component.scss index e70657dde..41830aced 100644 --- a/src/app/fda/application/application-details/application-details/application-details.component.scss +++ b/src/app/fda/application/application-details/application-details/application-details.component.scss @@ -1,30 +1,38 @@ .details-container { - width: 100%; - display: flex; - align-items: center; - justify-content: center; - /*padding: 20px;*/ - } - - .details-box { - max-width: 1028px; - width: 100%; - box-sizing: border-box; - margin-bottom: 20px; - } - - .box { - display: flex; - align-items: top; - } - - .mat-card { - max-width: 1028px; - width: 100%; - /*box-sizing: border-box;*/ - /*margin-bottom: 20px;*/ - } - + width: 100%; + display: flex; + align-items: center; + justify-content: center; + /*padding: 20px;*/ +} + +.details-box { + max-width: 1028px; + width: 100%; + box-sizing: border-box; + margin-bottom: 20px; +} + +.box { + display: flex; + align-items: top; +} + +.divflexrow { + display: flex; + flex-direction: row; + justify-content: space-between; + width: 100%; + max-width: 100%; +} + +.mat-card { + max-width: 1028px; + width: 100%; + /*box-sizing: border-box;*/ + /*margin-bottom: 20px;*/ +} + .mat-card-title { display: flex; box-sizing: border-box; @@ -34,70 +42,68 @@ white-space: nowrap; white-space: normal; } - + .row { display: flex; width: 100%; - border-bottom: solid 1px rgba(0, 0, 0, 0.12) + border-bottom: solid 1px var(--box-shadow-color-3); /* &:not(:last-child) { - border-bottom: solid 1px rgba(0, 0, 0, 0.12); + border-bottom: solid 1px var(--box-shadow-color-3); } */ } - + .row-property { display: flex; - width: 50%; - /*padding: 8px;*/ + width: 50%; } - + .row-property-key { min-width: 40%; max-width: 40%; padding: 7px; font-size: 12px; font-weight: 500; - /*background-color: #edeff2;*/ } - + .row-property-value { min-width: 60%; max-width: 60%; padding: 7px; font-size: 12px; } - + .row-property-2 { display: flex; width: 100%; } - + .row-property-key-2 { min-width: 20%; max-width: 20%; padding: 6px; - font-size: 12px; - font-weight: 500; + font-size: 12px; + font-weight: 500; } - + .row-property-value-2 { min-width: 80%; max-width: 80%; padding: 6px; font-size: 12px; } - + .title { font-size: 15px; font-weight: bold; font-family: Verdana; - color: #0857d6; + color: var(--primary-title-color); padding-right: 5px; text-align: top; margin-bottom: 9px; } - + .title2 { display: flex; box-sizing: border-box; @@ -120,13 +126,13 @@ white-space: nowrap; white-space: normal; font-size:14px; - color: navy; + color: var(--regular-navy-color); } .margintopneg10px { margin-top: -10px; } - + .margintop10px { margin-top: 10px; } @@ -134,7 +140,7 @@ .margintop90px { margin-top: 90px; } - + .padtop17px { padding-top: 17px; } @@ -142,35 +148,35 @@ .padtop50px { padding-top: 50px; } - + .padleft20px { padding-left: 20px; } - + .padleft30px { padding-left: 30px; } - + .padleft50px { padding-left: 50px; } - + .padleft200px { padding-left: 200px; } - + .padbottom10px { padding-bottom: -10px; } - + .font9px { font-size: 9px; } - + .font10px { font-size: 10px; } - + .font11px { font-size: 11px; } @@ -183,38 +189,55 @@ font-size: 16px; } + .font18px { + font-size: 18px; + } + .font20px { font-size: 20px; } .colorgray { - color: gray; + color: var(--regular-grey-color); } - + .fontbold { - font-weight: 500; + font-weight: 500; } - + .colororange { - color: #E55913; + color: var(--orange-color); } - + + .colorblue { + color: var(--primary-title-color); + } + .bordergray { - border: 1px solid gray; + border: 1px solid var(--regular-grey-color); } - + + .width650px { + width: 650px; + max-width: 650px; + } + + .textalignright { + text-align: right; + } + table.blueTable { font-family: Roboto, "Helvetica Neue", sans-serif; /*font-family: Verdana, Geneva, sans-serif;*/ - border: 1px solid #1C6EA4; - background-color: #f7f7f7; + border: 1px solid var(--secondary-blue-color); + background-color: var(--table-bg-color-2); width: 100%; text-align: left; border-collapse: collapse; } table.blueTable td, table.blueTable th { - border: 1px solid #AAAAAA; + border: 1px solid var(--table-th-border-color); padding: 3px 2px; } @@ -224,31 +247,31 @@ } table.blueTable tr:nth-child(even) { - background: #DAE9F5; + background: var(--table-tr-even-bg-color-2); } table.blueTable thead { - background: #e8ebe1; - border-bottom: 1px solid #444444; + background: var(--table-thead-bg-color); + border-bottom: 1px solid var(--table-thead-border-color); } table.blueTable thead th { font-size: 12px; font-weight: 500; - color: #1a1a1a; - border-left: 1px solid #D0E4F5; + color: var(--table-th-color); + border-left: 1px solid var(--table-th-border-color-2); } table.blueTable thead th:first-child { border-left: none; } - + table.blueTable tfoot { font-size: 14px; font-weight: bold; - color: #FFFFFF; - background: #D0E4F5; - border-top: 2px solid #444444; + color: var(--white-color); + background: var(--table-th-border-color-2); + border-top: 2px solid var(--table-thead-border-color); } table.blueTable tfoot td { @@ -261,12 +284,12 @@ table.blueTable tfoot .links a{ display: inline-block; - background: #1C6EA4; - color: #FFFFFF; + background: var(--secondary-blue-color); + color: var(--white-color); padding: 2px 8px; border-radius: 5px; } - + .small-icon { width: 15px; height: 15px; @@ -282,36 +305,35 @@ flex-direction: column; align-items: flex-start; } - + .product-details-container { padding: 10px; } } - + @media(max-width: 918px) { .product-property { width: 100%; - border:1px solid red; + border:1px solid var(--regular-red-color); } - + .row { flex-direction: column; - + &:not(:last-child) { border-bottom: none; - + .product-property { - border-bottom: solid 1px rgba(0, 0, 0, 0.12); + border-bottom: solid 1px var(--box-shadow-color-3); } } &:last-child { .row { &:not(:last-child) { - border-bottom: solid 1px rgba(0, 0, 0, 0.12); + border-bottom: solid 1px var(--box-shadow-color-3); } } } } } - - \ No newline at end of file + diff --git a/src/app/fda/application/application-details/application-details/application-details.component.ts b/src/app/fda/application/application-details/application-details/application-details.component.ts index a61b85edf..a4d7fac69 100644 --- a/src/app/fda/application/application-details/application-details/application-details.component.ts +++ b/src/app/fda/application/application-details/application-details/application-details.component.ts @@ -1,13 +1,15 @@ import { Component, OnInit } from '@angular/core'; -import { ApplicationService } from '../../service/application.service'; import { ActivatedRoute, Router } from '@angular/router'; import { LoadingService } from '@gsrs-core/loading'; import { MainNotificationService } from '@gsrs-core/main-notification'; +import { Title } from '@angular/platform-browser'; import { AppNotification, NotificationType } from '@gsrs-core/main-notification'; import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; import { UtilsService } from '../../../../core/utils/utils.service'; import { AuthService } from '@gsrs-core/auth/auth.service'; import { ApplicationDetailsBaseComponent } from '../application-details-base.component'; +import { ApplicationService } from '../../service/application.service'; +import { GeneralService } from '../../../service/general.service'; @Component({ selector: 'app-application-details', @@ -19,6 +21,7 @@ export class ApplicationDetailsComponent extends ApplicationDetailsBaseComponent constructor( applicationService: ApplicationService, + public generalService: GeneralService, public activatedRoute: ActivatedRoute, loadingService: LoadingService, mainNotificationService: MainNotificationService, @@ -26,9 +29,9 @@ export class ApplicationDetailsComponent extends ApplicationDetailsBaseComponent gaService: GoogleAnalyticsService, utilsService: UtilsService, public authService: AuthService, + titleService: Title ) { - super(applicationService, activatedRoute, loadingService, mainNotificationService, router, gaService, utilsService); - // , authService); + super(applicationService, generalService, activatedRoute, loadingService, mainNotificationService, router, gaService, utilsService, titleService); } ngOnInit() { @@ -37,8 +40,10 @@ export class ApplicationDetailsComponent extends ApplicationDetailsBaseComponent }); this.id = this.activatedRoute.snapshot.params['id']; - + this.appType = this.activatedRoute.snapshot.params['appType']; + this.appNumber = this.activatedRoute.snapshot.params['appNumber']; super.ngOnInit(); + } } diff --git a/src/app/fda/application/application-form/application-form.component.html b/src/app/fda/application/application-form/application-form.component.html index f00e6fe88..090e69dff 100644 --- a/src/app/fda/application/application-form/application-form.component.html +++ b/src/app/fda/application/application-form/application-form.component.html @@ -1,6 +1,10 @@
    +     -     + --> + + + View Application +    
    @@ -137,7 +145,7 @@ - {{submitDateMessage}} + {{submitDateMessage}} @@ -168,7 +176,7 @@ - {{statusDateMessage}} + {{statusDateMessage}}
    @@ -262,7 +270,8 @@
    - +
    diff --git a/src/app/fda/application/application-form/application-form.component.scss b/src/app/fda/application/application-form/application-form.component.scss index 6730dcf18..ad6358d4a 100644 --- a/src/app/fda/application/application-form/application-form.component.scss +++ b/src/app/fda/application/application-form/application-form.component.scss @@ -4,10 +4,10 @@ flex-direction: column; top: 64px; width: 100%; - background-color: white; + background-color: var(--regular-white-color); align-items: center; justify-content: center; - box-shadow: 0px 3px 3px -2px rgba(0, 0, 0, 0.2), 0px 3px 4px 0px rgba(0, 0, 0, 0.14), 0px 1px 8px 0px rgba(0, 0, 0, 0.12); + box-shadow: 0px 3px 3px -2px var(--box-shadow-color), 0px 3px 4px 0px var(--box-shadow-color-2), 0px 1px 8px 0px var(--box-shadow-color-3); z-index: 1001; } @@ -70,50 +70,50 @@ .col-1:last-child { margin-right: 0px; -} +} .col-2 { /* width: 348.667px; calc() value*/ - width: calc((100% - 30px * 2) / 3); + width: calc((100% - 30px * 2) / 3); margin-right: 30px; } .col-2:last-child { margin-right: 0px; -} +} .col-3 { - /* width: 538px; calc() value*/ + /* width: 538px; calc() value*/ width: calc((100% - 30px * 1) / 2); margin-right: 30px; } .col-3:last-child { margin-right: 0px; -} +} .col-4 { - /* width: 724px; calc() value*/ + /* width: 724px; calc() value*/ width: calc((100% - 20px) / (6/4)); margin-right: 20px; } .col-4:last-child { margin-right: 0px; -} +} .col-5 { - /* width: 913.333px; calc() value*/ + /* width: 913.333px; calc() value*/ width: calc((100% - 10px) / (6/5)); margin-right: 10px; } .col-5:last-child { margin-right: 0px; -} +} .col-5-more { - /* width: calc() value*/ + /* width: calc() value*/ width: calc((100% + 100px) / (6/5)); margin-right: 10px; } @@ -128,9 +128,9 @@ } .actions-container { - max-width: 1028px; - width: 100%; - background-color: white; + max-width: 1200px; + min-width: 1200px; + background-color: var(--regular-white-color); padding: 10px; display: flex; } @@ -150,7 +150,7 @@ transition: all 500ms ease-out; max-width: 1028px; width: 100%; - background-color: white; + background-color: var(--regular-white-color); display: flex; flex-direction: column; @@ -188,14 +188,14 @@ } .warning-message { - color: #8a6d3b; - background-color: #fcf8e3; + color: var(--warning-dialog-color); + background-color: var(--warning-dialog-bg-color); } .error-message { - color: #a94442; - background-color: #f2dede; + color: var(--error-dialog-color); + background-color: var(--error-dialog-bg-color); } } @@ -242,11 +242,11 @@ } .bordergray { - border: 1px solid gray; + border: 1px solid var(--regular-grey-color); } .bordergreen { - border: 1px solid green; + border: 1px solid var(--regular-green-color); } .width40px { @@ -258,11 +258,11 @@ } .colorgray { - color: gray; + color: var(--regular-grey-color); } .colorred { - color: red; + color: var(--regular-red-color); } .font11px { @@ -281,14 +281,14 @@ float: left; } -.disabled { - cursor: not-allowed; -} +.disabled { + cursor: not-allowed; +} -.disabledfield { +.disabledfield { cursor: not-allowed; - color: red; -} + color: var(--regular-red-color); +} /* CV INPUT OVERWRITE WIDTH */ .cvwidth { @@ -305,50 +305,50 @@ /* OVERWRITE MATERIAL INPUT FIELDS */ .mat-form-field-infix { - color: blue; + color: var(--regular-blue-color); } .mat-select-value { - color: blue; - } + color: var(--regular-blue-color); + } .mat-form-field-label { - color: rgb(127, 127, 133) !important; + color: var(--mat-form-field-label-color) !important; } .mat-form-field-underline { - background-color: rgb(68, 68, 218) !important; - } + background-color: var(--mat-form-field-underline-bg-color) !important; + } /*Focused: change color of label*/ .mat-focused .mat-form-field-label { - color: rgb(70, 116, 106) !important; + color: var(--mat-form-field-focused-color) !important; } /*Focused: change color of underline*/ .mat-form-field-ripple { - background-color: rgb(70, 116, 106) !important;; - } + background-color: var(--mat-form-field-focused-color) !important;; + } - .mat-form-field-disabled .mat-form-field-underline - { - background-image: linear-gradient( to right, rgba(0, 0, 0, 1) 0, rgba(0, 0, 0, 0.42) 10%, #c2c7cc 0 ) !important; + .mat-form-field-disabled .mat-form-field-underline + { + background-image: linear-gradient( to right, var(--img-linear-gradient-start-color) 0, var(--textarea-dark-border-color) 10%, var(--img-linear-gradient-color) 0 ) !important; background-size: 1px 100% !important; - background-repeat: repeat-x !important; + background-repeat: repeat-x !important; cursor: not-allowed; - } + } .mat-form-field-disabled:hover { cursor: not-allowed; } mat-hint { - color: red !important; + color: var(--regular-red-color) !important; } - + } .errortext { - color: red; + color: var(--regular-red-color); font-size: 11px; -} \ No newline at end of file +} diff --git a/src/app/fda/application/application-form/application-form.component.ts b/src/app/fda/application/application-form/application-form.component.ts index 4c43e9f54..8cff542af 100644 --- a/src/app/fda/application/application-form/application-form.component.ts +++ b/src/app/fda/application/application-form/application-form.component.ts @@ -9,8 +9,9 @@ import { UtilsService } from '@gsrs-core/utils/utils.service'; import { AuthService } from '@gsrs-core/auth/auth.service'; import { ControlledVocabularyService } from '../../../core/controlled-vocabulary/controlled-vocabulary.service'; import { VocabularyTerm } from '../../../core/controlled-vocabulary/vocabulary.model'; -import { ApplicationSrs, ValidationMessage } from '../model/application.model'; +import { Application, ValidationMessage } from '../model/application.model'; import { Subscription } from 'rxjs'; +import { Title } from '@angular/platform-browser'; import { take } from 'rxjs/operators'; import { MatDialog } from '@angular/material/dialog'; import { OverlayContainer } from '@angular/cdk/overlay'; @@ -23,19 +24,20 @@ import { DatePipe } from '@angular/common'; import { MatDatepickerInputEvent } from '@angular/material/datepicker'; import { FormBuilder } from '@angular/forms'; import { FormControl, FormGroup, Validators } from '@angular/forms'; -import { NativeDateAdapter, DateAdapter, MAT_NATIVE_DATE_FORMATS } from '@angular/material'; +import { NativeDateAdapter, DateAdapter, MAT_NATIVE_DATE_FORMATS } from '@angular/material/core'; import { element } from 'protractor'; +import { GeneralService } from '../../service/general.service'; @Component({ selector: 'app-application-form', templateUrl: './application-form.component.html', styleUrls: ['./application-form.component.scss'], - // encapsulation: ViewEncapsulation.None + // encapsulation: ViewEncapsulation.None }) export class ApplicationFormComponent implements OnInit, AfterViewInit, OnDestroy { - application: ApplicationSrs; + application: Application; /* centerList: Array = []; appTypeList: Array = []; @@ -64,6 +66,7 @@ export class ApplicationFormComponent implements OnInit, AfterViewInit, OnDestro constructor( private applicationService: ApplicationService, + private generalService: GeneralService, private authService: AuthService, private loadingService: LoadingService, private mainNotificationService: MainNotificationService, @@ -74,7 +77,8 @@ export class ApplicationFormComponent implements OnInit, AfterViewInit, OnDestro private router: Router, private overlayContainerService: OverlayContainer, private dialog: MatDialog, - private fb: FormBuilder) { } + private fb: FormBuilder, + private titleService: Title) { } // get submitDateControl() { return this.appForm.get('submitDateControl'); } @@ -94,19 +98,20 @@ export class ApplicationFormComponent implements OnInit, AfterViewInit, OnDestro this.id = id; this.gaService.sendPageView(`Application Edit`); this.getApplicationDetails(); - // this.getVocabularies(); } } else { this.title = 'Register New Application'; setTimeout(() => { this.gaService.sendPageView(`Application Register`); + this.titleService.setTitle(`Register Application`); this.applicationService.loadApplication(); this.application = this.applicationService.application; - // this.getVocabularies(); this.loadingService.setLoading(false); this.isLoading = false; }); } + }, error => { + this.loadingService.setLoading(false); }); this.subscriptions.push(routeSubscription); } @@ -122,13 +127,14 @@ export class ApplicationFormComponent implements OnInit, AfterViewInit, OnDestro } getApplicationDetails(newType?: string): void { - this.applicationService.getApplicationDetails(this.id).subscribe(response => { + this.applicationService.getApplicationById(this.id).subscribe(response => { if (response) { this.applicationService.loadApplication(response); this.application = this.applicationService.application; // Check if Data is from external source, and Disable some fields. if (this.application) { + this.titleService.setTitle(`Edit Application ` + this.application.appType + ' ' + this.application.appNumber); if (this.application.provenance) { if (this.application.provenance.toLowerCase() === 'darrts') { this.isDisableData = true; @@ -172,9 +178,7 @@ export class ApplicationFormComponent implements OnInit, AfterViewInit, OnDestro this.loadingService.setLoading(false); this.isLoading = false; }); - } - } setValidationMessage(message: string) { @@ -187,6 +191,7 @@ export class ApplicationFormComponent implements OnInit, AfterViewInit, OnDestro // Validate data in client side first validateClient(): void { + this.submissionMessage = null; this.validationMessages = []; this.validationResult = true; @@ -253,6 +258,14 @@ export class ApplicationFormComponent implements OnInit, AfterViewInit, OnDestro this.setValidationMessage('High Limit must be a number'); } } + // Ingredient Name Validation + if (elementIngred.$$ingredientNameValidation) { + this.setValidationMessage(elementIngred.$$ingredientNameValidation); + } + // Basis of Strength Validation + if (elementIngred.$$basisOfStrengthValidation) { + this.setValidationMessage(elementIngred.$$basisOfStrengthValidation); + } } }); @@ -265,7 +278,6 @@ export class ApplicationFormComponent implements OnInit, AfterViewInit, OnDestro this.loadingService.setLoading(false); this.isLoading = false; } - } toggleValidation(): void { @@ -299,6 +311,20 @@ export class ApplicationFormComponent implements OnInit, AfterViewInit, OnDestro submit(): void { this.isLoading = true; this.loadingService.setLoading(true); + // remove non-field form property/field/key from Application object + this.application = this.cleanApplication(); + if (this.application) { + if (this.application.id) { + } else { + if (this.application.provenance === null || this.application.provenance === undefined) { + // Set Provenance to GSRS + this.application.provenance = 'GSRS'; + } + } + // Set service application + this.applicationService.application = this.application; + } + this.applicationService.saveApplication().subscribe(response => { this.loadingService.setLoading(false); this.isLoading = false; @@ -310,13 +336,17 @@ export class ApplicationFormComponent implements OnInit, AfterViewInit, OnDestro this.showSubmissionMessages = false; this.submissionMessage = ''; if (response.id) { + this.applicationService.bypassUpdateCheck(); const id = response.id; this.router.routeReuseStrategy.shouldReuseRoute = () => false; this.router.onSameUrlNavigation = 'reload'; this.router.navigate(['/application', id, 'edit']); } }, 4000); + }, error => { + this.loadingService.setLoading(false); } + /* , (error: SubstanceFormResults) => { this.showSubmissionMessages = true; @@ -337,7 +367,9 @@ export class ApplicationFormComponent implements OnInit, AfterViewInit, OnDestro }, 8000); } }*/ + ); + } private handleApplicationRetrivalError() { @@ -379,16 +411,18 @@ export class ApplicationFormComponent implements OnInit, AfterViewInit, OnDestro deleteApplication(): void { this.applicationService.deleteApplication().subscribe(response => { - if (response) { - this.displayMessageAfterDeleteApp(); - } - }); + this.applicationService.bypassUpdateCheck(); + this.displayMessageAfterDeleteApp(); + }, (err) => { + console.log(err); + } + ); } displayMessageAfterDeleteApp() { const dialogRef = this.dialog.open(ConfirmDialogComponent, { data: { - message: 'The application has been deleted', + message: 'This application record was deleted successfully', type: 'home' } }); @@ -398,18 +432,40 @@ export class ApplicationFormComponent implements OnInit, AfterViewInit, OnDestro }); } + cleanApplication(): Application { + let applicationStr = JSON.stringify(this.application); + let applicationCopy: Application = JSON.parse(applicationStr); + applicationCopy.applicationProductList.forEach(elementProd => { + if (elementProd != null) { + elementProd.applicationIngredientList.forEach(elementIngred => { + if (elementIngred != null) { + // remove property for Ingredient Name Validation. Do not need in the form JSON + if (elementIngred.$$ingredientNameValidation || elementIngred.$$ingredientNameValidation === "") { + delete elementIngred.$$ingredientNameValidation; + } + // remove property for Basis of Strength Validation. Do not need in the form JSON + if (elementIngred.$$basisOfStrengthValidation || elementIngred.$$basisOfStrengthValidation === "") { + delete elementIngred.$$basisOfStrengthValidation; + } + } + }); + } + }); + return applicationCopy; + } + showJSON(): void { + let cleanApplication = this.cleanApplication(); const dialogRef = this.dialog.open(JsonDialogFdaComponent, { width: '90%', height: '90%', - data: this.application + data: cleanApplication }); // this.overlayContainer.style.zIndex = '1002'; const dialogSubscription = dialogRef.afterClosed().subscribe(response => { }); this.subscriptions.push(dialogSubscription); - } addNewIndication() { @@ -432,53 +488,67 @@ export class ApplicationFormComponent implements OnInit, AfterViewInit, OnDestro this.applicationService.deleteIndication(indIndex); } - public generateFormContorls() { - /* - this.appForm = this.fb.group({ - submitDateControl: ['', [Validators.minLength(8), Validators.maxLength(10)]] - // , Validators.minLength(6), Validators.maxLength(15), Validators.pattern('^.*(?=.{4,10})(?=.*\\d)(?=.*[a-zA-Z]).*$')]) - }); - */ - } - /* - dateInputEventSubmitDate(event: MatDatepickerInputEvent) { - this.validateSumitDate(event); - } - dateChangeEventSubmitDate(event: MatDatepickerInputEvent) { - this.validateSumitDate(event); - } - */ - validateSubmitDate() { this.submitDateMessage = ''; const isValid = this.validateDate(this.application.submitDate); if (isValid === false) { this.submitDateMessage = 'Submit Date is invalid'; - } - /* - // const targetInput = event.targetElement as HTMLInputElement; - // console.log('target value: ' + targetInput.value); - const submitDateValue = targetInput.value; - this.submitDateMessage = ''; - if (submitDateValue !== null) { - if ((submitDateValue.length < 8) || (submitDateValue.length > 10)) { - this.submitDateMessage = 'Invalid Submit Date'; - } - if (this.application.submitDate !== null) { - this.convertDateFormat(this.application.submitDate); + } else { + const isValid = this.validateSubmitDateWithStatusDate(this.application.submitDate, this.application.statusDate); + if (isValid === false) { + this.submitDateMessage = 'Submit Date should be earlier than Status Date;'; } } - */ } validateStatusDate() { this.statusDateMessage = ''; - const isValid = this.validateDate(this.application.statusDate); + let isValid = this.validateDate(this.application.statusDate); if (isValid === false) { this.statusDateMessage = 'Status Date is invalid'; + } else { + isValid = this.validateFutureDate(this.application.statusDate); + if (isValid === false) { + this.statusDateMessage = 'Status Date should not be a future date'; + } else { + isValid = this.validateSubmitDateWithStatusDate(this.application.submitDate, this.application.statusDate); + if (isValid === false) { + this.submitDateMessage = 'Submit Date should be earlier than Status Date;'; + } + } } } + validateFutureDate(dateinput: any): boolean { + let isValid = true; + // compare if the entered date is future date or not + const now = new Date(); + const nowDate = now.setHours(0, 0, 0); + if ((dateinput !== null) && (dateinput.length > 0)) { + if ((dateinput.length >= 8) || (dateinput.length <= 10)) { + const enteredDate = new Date(dateinput); + const enteredDateOnly = enteredDate.setHours(0, 0, 0); + if (enteredDateOnly > nowDate) { + isValid = false; + } + } + } + return isValid; + } + + validateSubmitDateWithStatusDate(submitDateInput: any, statusDateInput: any): boolean { + let isValid = true; + // Submit Date should not be later than Status Date. + if ((submitDateInput) && (statusDateInput)) { + const submitDt = new Date(submitDateInput).setHours(0, 0, 0); + const statusDt = new Date(statusDateInput).setHours(0, 0, 0); + if (submitDt > statusDt) { + isValid = false; + } + } + return isValid; + } + validateDate(dateinput: any): boolean { let isValid = true; if ((dateinput !== null) && (dateinput.length > 0)) { @@ -514,13 +584,6 @@ export class ApplicationFormComponent implements OnInit, AfterViewInit, OnDestro } convertDateFormat(formDate: any): any { - // alert(this.application.submitDate); - /* - var convertDate = new Date(e.target.value).toISOString().substring(0, 10); - this.myForm.get('dob').setValue(convertDate, { - onlyself: true - }) - */ if (formDate !== null) { const datepipe = new DatePipe('en-US'); const date = new Date(formDate); @@ -528,5 +591,4 @@ export class ApplicationFormComponent implements OnInit, AfterViewInit, OnDestro return formattedDate; } } - } diff --git a/src/app/fda/application/application-form/application-product-form/application-product-form.component.html b/src/app/fda/application/application-form/application-product-form/application-product-form.component.html index 39db4865b..e339a2e3e 100644 --- a/src/app/fda/application/application-form/application-product-form/application-product-form.component.html +++ b/src/app/fda/application/application-form/application-product-form/application-product-form.component.html @@ -44,14 +44,14 @@
    - +
    - +    @@ -74,21 +74,30 @@ [model]="product.dosageForm" (valueChange)="product.dosageForm = $event"> + +
    + + +
    + + - + - +
    @@ -108,12 +117,12 @@
    + [totalIngredient]="product.applicationIngredientList.length" (ingredientMessageOut)="ingredientMessageOutChange($event)" + (basisOfStrengthMessageOut)="basisOfStrengthMessageOutChange($event)">
    -


    diff --git a/src/app/fda/application/application-form/application-product-form/application-product-form.component.scss b/src/app/fda/application/application-form/application-product-form/application-product-form.component.scss index 52842bc3a..e378d6556 100644 --- a/src/app/fda/application/application-form/application-product-form/application-product-form.component.scss +++ b/src/app/fda/application/application-form/application-product-form/application-product-form.component.scss @@ -1,13 +1,7 @@ .details-container { width: 100%; - /* - display: flex; - align-items: center; - justify-content: center; - */ - /*padding: 20px;*/ } - + .details-box { max-width: 1028px; width: 100%; @@ -32,7 +26,7 @@ .form-row-prodname { display: flex; - justify-content:space-between; + justify-content:space-between; /* grid-column-gap: 20px; */ /* align-items: flex-end; */ } @@ -43,23 +37,11 @@ } .bordergray { - border: 1px solid gray; + border: 1px solid var(--regular-grey-color); } .bordergreen { - border: 1px solid green; -} - -.margintop20px { - margin-top: 20px; -} - -.margintop30px { - margin-top: 30px; -} - -.pad10px { - padding: 10px; + border: 1px solid var(--regular-green-color); } .font12px { @@ -71,29 +53,41 @@ } .colorgreen { - color: green; + color: var(--regular-green-color); } .colorred { - color: red; + color: var(--regular-red-color); } .colorgray { - color: gray; + color: var(--regular-grey-color); } .titlegreen { - color: green; + color: var(--regular-green-color); font-size: 17px; font-weight: 500; } .titleorange { - color: orangered; + color: var(--regular-orangered-color); font-size: 17px; font-weight: 500; } +.margintop20px { + margin-top: 20px; +} + +.margintop30px { + margin-top: 30px; +} + +.marginright30px { + margin-right: 30px; +} + .margintop30px { margin-top: 30px; } @@ -110,28 +104,32 @@ padding-left: 250px; } +.pad10px { + padding: 10px; +} + .padbottom0px { padding: 0px; } hr { border: none; - border-top: 3px dotted green; - color: #333; + border-top: 3px dotted var(--regular-green-color); + color: var(--hr-color); overflow: visible; text-align: center; height: 5px; } fieldset.border { - border: solid 1px green !important; + border: solid 1px var(--regular-green-color) !important; padding: 0 10px 10px 10px; border-bottom: none; border-radius: 8px; min-width: 0; /* override the default value of min-content */ - -webkit-box-shadow: 3px 6px 11px 1px rgba(110,104,110,0.64); - -moz-box-shadow: 3px 6px 11px 1px rgba(110,104,110,0.64); - box-shadow: 3px 4px 5px 1px rgba(110,104,110,0.64); + -webkit-box-shadow: 3px 6px 11px 1px var(--fieldset-box-shadow-color); + -moz-box-shadow: 3px 6px 11px 1px var(--fieldset-box-shadow-color); + box-shadow: 3px 4px 5px 1px var(--fieldset-box-shadow-color); } legend.border { @@ -139,7 +137,7 @@ legend.border { border: none; border-bottom: none; font-size: 13px; - color: #174793; + color: var(--legend-blue-border-color); font-family: Verdana; font-weight: bold; margin-bottom: 10px; @@ -156,7 +154,7 @@ legend.border { .formfield3columnwidth { width: 540px !important; -} +} .formfieldfullpagewidth { width: 900px; @@ -166,9 +164,9 @@ legend.border { width: 40px; } -.disabled { - cursor: not-allowed; -} +.disabled { + cursor: not-allowed; +} .displayinlineblock { display: inline-block; diff --git a/src/app/fda/application/application-form/application-product-form/application-product-form.component.ts b/src/app/fda/application/application-form/application-product-form/application-product-form.component.ts index 9242cf92c..23b427339 100644 --- a/src/app/fda/application/application-form/application-product-form/application-product-form.component.ts +++ b/src/app/fda/application/application-form/application-product-form/application-product-form.component.ts @@ -1,11 +1,12 @@ -import { Component, OnInit, Input } from '@angular/core'; -import { ApplicationSrs } from '../../model/application.model'; +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { Application } from '../../model/application.model'; import { ControlledVocabularyService } from '../../../../core/controlled-vocabulary/controlled-vocabulary.service'; import { VocabularyTerm } from '../../../../core/controlled-vocabulary/vocabulary.model'; import { ApplicationService } from '../../service/application.service'; import { MatDialog } from '@angular/material/dialog'; import { ConfirmDialogComponent } from '../../../confirm-dialog/confirm-dialog.component'; import { AuthService } from '@gsrs-core/auth/auth.service'; +import { GeneralService } from 'src/app/fda/service/general.service'; @Component({ selector: 'app-application-product-form', @@ -13,8 +14,7 @@ import { AuthService } from '@gsrs-core/auth/auth.service'; styleUrls: ['./application-product-form.component.scss'] }) export class ApplicationProductFormComponent implements OnInit { - - @Input() application: ApplicationSrs; + @Input() application: Application; reviewProductMessage: Array = []; productMessage = ''; username = null; @@ -23,6 +23,7 @@ export class ApplicationProductFormComponent implements OnInit { private applicationService: ApplicationService, public cvService: ControlledVocabularyService, private authService: AuthService, + private generalService: GeneralService, private dialog: MatDialog) { } ngOnInit() { @@ -39,7 +40,7 @@ export class ApplicationProductFormComponent implements OnInit { confirmDeleteProduct(prodIndex: number) { const dialogRef = this.dialog.open(ConfirmDialogComponent, { - data: {message: 'Are you sure you want to delete Product Details ' + (prodIndex + 1) + ' data?'} + data: { message: 'Are you sure you want to delete Product Details ' + (prodIndex + 1) + ' data?' } }); dialogRef.afterClosed().subscribe(result => { @@ -55,7 +56,7 @@ export class ApplicationProductFormComponent implements OnInit { confirmDeleteProductName(prodIndex: number, prodNameIndex: number) { const dialogRef = this.dialog.open(ConfirmDialogComponent, { - data: {message: 'Are you sure you want to delete Product Name ' + (prodNameIndex + 1) + ' ?'} + data: { message: 'Are you sure you want to delete Product Name ' + (prodNameIndex + 1) + ' ?' } }); dialogRef.afterClosed().subscribe(result => { @@ -76,7 +77,7 @@ export class ApplicationProductFormComponent implements OnInit { confirmReviewProduct(prodIndex: number) { if (this.application.applicationProductList[prodIndex].reviewDate) { const dialogRef = this.dialog.open(ConfirmDialogComponent, { - data: {message: 'Are you sure you want to overwrite Reviewed By and Review Date?'} + data: { message: 'Are you sure you want to overwrite Reviewed By and Review Date?' } }); dialogRef.afterClosed().subscribe(result => { @@ -90,16 +91,28 @@ export class ApplicationProductFormComponent implements OnInit { } reviewProduct(prodIndex: number) { - this.applicationService.getCurrentDate().subscribe(response => { - if (response) { - this.application.applicationProductList[prodIndex].reviewDate = response.date; - this.application.applicationProductList[prodIndex].reviewedBy = this.username; - } - }); + const currentDate = this.generalService.getCurrentDate(); + this.application.applicationProductList[prodIndex].reviewDate = currentDate; + this.application.applicationProductList[prodIndex].reviewedBy = this.username; } addNewIngredient(prodIndex: number) { this.applicationService.addNewIngredient(prodIndex); } + /* + loadRouteAdmin() { + this.application.applicationProductList.forEach((elementProd, prodIndex) => { + if (elementProd != null) { + if (elementProd.routeAdmin) { + } + } + }); + } + */ + + updateRouteAdmin(routeAdmins: Array, prodIndex: number): void { + this.application.applicationProductList[prodIndex].routeAdmin = routeAdmins.toString(); + } + } diff --git a/src/app/fda/application/application-form/can-activate-register-application-form.component.ts b/src/app/fda/application/application-form/can-activate-register-application-form.component.ts new file mode 100644 index 000000000..000c4a7c1 --- /dev/null +++ b/src/app/fda/application/application-form/can-activate-register-application-form.component.ts @@ -0,0 +1,43 @@ +import { Injectable } from '@angular/core'; +import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, NavigationExtras, UrlTree } from '@angular/router'; +import { take } from 'rxjs/operators'; +import { AuthService } from '@gsrs-core/auth/auth.service'; +import { Observable } from 'rxjs'; + +@Injectable() +export class CanActivateRegisterApplicationFormComponent implements CanActivate { + + constructor( + private router: Router, + private authService: AuthService + ) {} + + canActivate( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): Observable | Promise | (boolean | UrlTree) { + return new Observable(observer => { + this.authService.getAuth().pipe(take(1)).subscribe(auth => { + if (auth) { + this.authService.hasAnyRolesAsync('DataEntry', 'SuperDataEntry').pipe(take(1)).subscribe(response => { + if (response) { + observer.next(true); + observer.complete(); + } else { + observer.next(this.router.parseUrl('/browse-applications')); + observer.complete(); + } + }); + } else { + const navigationExtras: NavigationExtras = { + queryParams: { + path: state.url + } + }; + observer.next(this.router.createUrlTree(['/login'], navigationExtras)); + observer.complete(); + } + }); + }); + } +} diff --git a/src/app/fda/application/application-form/can-activate-update-application-form.component.ts b/src/app/fda/application/application-form/can-activate-update-application-form.component.ts new file mode 100644 index 000000000..d2faa88a9 --- /dev/null +++ b/src/app/fda/application/application-form/can-activate-update-application-form.component.ts @@ -0,0 +1,52 @@ +import { Injectable } from '@angular/core'; +import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, NavigationExtras, UrlTree } from '@angular/router'; +import { take } from 'rxjs/operators'; +import { AuthService } from '@gsrs-core/auth/auth.service'; +import { Observable } from 'rxjs'; +import { ConfigService } from '@gsrs-core/config'; + +@Injectable() +export class CanActivateUpdateApplicationFormComponent implements CanActivate { + + constructor( + private router: Router, + private authService: AuthService, + private configService: ConfigService, + + ) { } + + canActivate( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): Observable | Promise | (boolean | UrlTree) { + return new Observable(observer => { + const loadedComponents = this.configService.configData.loadedComponents || null; + if (loadedComponents && loadedComponents.applications) { + this.authService.getAuth().pipe(take(1)).subscribe(auth => { + if (auth) { + this.authService.hasAnyRolesAsync('Updater', 'SuperUpdater').pipe(take(1)).subscribe(response => { + if (response) { + observer.next(true); + observer.complete(); + } else { + observer.next(this.router.parseUrl('/browse-applications')); + observer.complete(); + } + }); + } else { + const navigationExtras: NavigationExtras = { + queryParams: { + path: state.url + } + }; + observer.next(this.router.createUrlTree(['/login'], navigationExtras)); + observer.complete(); + } + }); + } else { + observer.next(this.router.parseUrl('/home')); + observer.complete(); + } + }); + } +} diff --git a/src/app/fda/application/application-form/can-deactivate-application-form.component.spec.ts b/src/app/fda/application/application-form/can-deactivate-application-form.component.spec.ts new file mode 100644 index 000000000..7994ab008 --- /dev/null +++ b/src/app/fda/application/application-form/can-deactivate-application-form.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CanDeactivateApplicationFormComponent } from './can-deactivate-application-form.component'; + +describe('CanDeactivateApplicationFormComponent', () => { + let component: CanDeactivateApplicationFormComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CanDeactivateApplicationFormComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CanDeactivateApplicationFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/fda/application/application-form/can-deactivate-application-form.component.ts b/src/app/fda/application/application-form/can-deactivate-application-form.component.ts new file mode 100644 index 000000000..0bda9833e --- /dev/null +++ b/src/app/fda/application/application-form/can-deactivate-application-form.component.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@angular/core'; +import { CanDeactivate } from '@angular/router'; +import { ApplicationFormComponent } from './application-form.component'; +import { ApplicationService } from '../service/application.service'; + +@Injectable({ + providedIn: 'root' +}) +export class CanDeactivateApplicationFormComponent implements CanDeactivate { + constructor( + private applicationService: ApplicationService + ) {} + canDeactivate(component: ApplicationFormComponent): boolean { + if (this.applicationService.isApplicationUpdated) { + if (confirm('You have unsaved changes! If you leave, your changes will be lost.')) { + return true; + } else { + return false; + } + } + return true; + } +} diff --git a/src/app/fda/application/application-form/ingredient-form/ingredient-form.component.html b/src/app/fda/application/application-form/ingredient-form/ingredient-form.component.html index 7f482ef9f..00afa7526 100644 --- a/src/app/fda/application/application-form/ingredient-form/ingredient-form.component.html +++ b/src/app/fda/application/application-form/ingredient-form/ingredient-form.component.html @@ -16,16 +16,16 @@ check_circle Reviewed - By:{{ingredient.reviewedBy}} {{ingredient.reviewDate|date: 'MM/dd/yyyy hh:mm:ss a'}} + By: {{ingredient.reviewedBy}} {{ingredient.reviewDate|date: 'MM/dd/yyyy hh:mm:ss a'}}     Created By: {{ingredient.createdBy}}    - {{ingredient.createDate|date: 'MM/dd/yyyy hh:mm:ss a'}}    + {{ingredient.creationDate|date: 'MM/dd/yyyy hh:mm:ss a'}}    Modified By: {{ingredient.modifiedBy}}    - {{ingredient.modifyDate|date: 'MM/dd/yyyy hh:mm:ss a'}} + {{ingredient.lastModifiedDate|date: 'MM/dd/yyyy hh:mm:ss a'}}
    @@ -44,8 +44,8 @@
    {{element.productName}} Application Bdnum diff --git a/src/app/fda/substance-browse/substance-application-match-list/substance-application-match-list.component.scss b/src/app/fda/substance-browse/substance-application-match-list/substance-application-match-list.component.scss index 1d83c8aa9..8fd078e3f 100644 --- a/src/app/fda/substance-browse/substance-application-match-list/substance-application-match-list.component.scss +++ b/src/app/fda/substance-browse/substance-application-match-list/substance-application-match-list.component.scss @@ -15,24 +15,28 @@ font-size: 14px; } +.font16px { + font-size: 16px; +} + .font18px { font-size: 18px; } .colorred { - color: red; + color: var(--regular-red-color); } .colorgreen { - color: green; + color: var(--regular-green-color); } .colorpurple { - color: purple; + color: var(--regular-purple-color); } .bordergray { - border: 1px solid gray; + border: 1px solid var(--regular-grey-color); } .padtop20px { @@ -66,11 +70,11 @@ .headerstyle { font-size: 13px; vertical-align: top; - color: #241983; + color: var(--secondary-title-color); padding-left: 3px; - background-color: rgb(236, 236, 236); - border-bottom: 1px solid gray; - border-right: 1px solid gray; + background-color: var(--pale-border-color-rgb-2); + border-bottom: 1px solid var(--regular-grey-color); + border-right: 1px solid var(--regular-grey-color); } .cellstyle { @@ -78,11 +82,11 @@ text-align: top; padding-left: 3px; padding-top: 3px; - border-right: 1px solid gray; + border-right: 1px solid var(--regular-grey-color); } .backgroundyellow { - background-color: rgba(255, 255, 158, 0.705); + background-color: var(--light-yellow-color-2); } .divflex { diff --git a/src/app/fda/substance-browse/substance-application-match-list/substance-application-match-list.component.ts b/src/app/fda/substance-browse/substance-application-match-list/substance-application-match-list.component.ts index fd60012c6..961822335 100644 --- a/src/app/fda/substance-browse/substance-application-match-list/substance-application-match-list.component.ts +++ b/src/app/fda/substance-browse/substance-application-match-list/substance-application-match-list.component.ts @@ -6,7 +6,7 @@ import { UtilsService } from '../../../core/utils/utils.service'; import { AuthService } from '@gsrs-core/auth/auth.service'; import { ApplicationService } from '../../application/service/application.service'; import { Subscription } from 'rxjs'; -import { ApplicationSrs, ApplicationIngredient } from '../../application/model/application.model'; +import { Application, SubstanceApplicationMatch } from '../../application/model/application.model'; @Component({ selector: 'app-substance-application-match-list', @@ -20,13 +20,17 @@ export class SubstanceApplicationMatchListComponent implements OnInit, AfterView isAdmin = false; appMatchList: any; substanceNames: any; - displayedColumns: string[] = ['Num', 'Action', 'Application Type', 'Application Number', 'Status', 'Application Sub Type', 'Product Name', 'Application Bdnum', 'Exact Match']; + displayedColumns: string[] = ['Num', 'Action', 'Application Type', 'Application Number', 'Status', 'Application Sub Type', 'Product Name', 'Application Substance Key', 'Exact Match']; dataSource = null; updated = 'false'; autoUpdateSavedSuccess = false; private subscriptions: Array = []; - application: ApplicationSrs; + applications: Array; + substanceApplicationMatchList: Array = []; preferredTerm = ''; + fullFacetField = ''; + total = 0; + application: Application; constructor( public generalService: GeneralService, @@ -46,7 +50,6 @@ export class SubstanceApplicationMatchListComponent implements OnInit, AfterView this.id = this.activatedRoute.snapshot.params['id']; if (this.id) { - this.getApplicationIngredientMatchList(this.id); this.getSubstanceNames(this.id); } this.loadingService.setLoading(false); @@ -55,40 +58,157 @@ export class SubstanceApplicationMatchListComponent implements OnInit, AfterView ngAfterViewInit() { } - getApplicationIngredientMatchList(substanceId: string): void { - this.generalService.getApplicationIngredientMatchList(substanceId).subscribe(appMatchList => { - this.dataSource = appMatchList; - }); - } - getSubstanceNames(substanceId: string): void { - this.generalService.getSubstanceNames(substanceId).subscribe(substanceNames => { + this.generalService.getSubstanceNamesBySubstanceUuid(substanceId).subscribe(substanceNames => { this.substanceNames = substanceNames; // Get Preferred Term or DisplayName == true - this.substanceNames.forEach((names) => { + this.substanceNames.forEach((names, index) => { if (names.displayName === true) { this.preferredTerm = names.name; } + const facetField = 'root_applicationProductList_applicationProductNameList_productName:'; + if (names) { + if (names.name) { + if (index > 0) { + this.fullFacetField = this.fullFacetField + ' OR '; + } + this.fullFacetField = this.fullFacetField + facetField + "\"" + names.name + "\""; + } + } }); + this.getApplicationIngredientMatchList(); }); } + getApplicationIngredientMatchList(): void { + // Facet Search for "Has No Ingredient" + const facetParam = { 'Has Ingredients': { 'params': { 'Has No Ingredient': true }, 'isAllMatch': false } }; + + const subscription = this.applicationService.getApplications( + null, + 0, + 200, + this.fullFacetField, + facetParam + ) + .subscribe(pagingResponse => { + this.applications = pagingResponse.content; + this.total = pagingResponse.count; + if (this.applications.length > 0) { + this.applications.forEach((application, index) => { + if (application) { + + // Create object locally to display selected columns in tabular format into the table + const matchObject: SubstanceApplicationMatch = {}; + matchObject.id = application.id; + matchObject.appType = application.appType; + matchObject.appNumber = application.appNumber; + matchObject.status = application.status; + matchObject.appSubType = application.appSubType; + + let productName = ''; + let bdnum = ''; + const newline = ' || '; + let exactMatchName = ''; + let exactMatchBdnum = ''; + + // Product + application.applicationProductList.forEach((elementProd, indexProd) => { + + // Need to think about this + let bdnum = ''; + + // Product Name + elementProd.applicationProductNameList.forEach((elementProdName, indexProdName) => { + if (elementProdName) { + if (indexProdName > 0) { + productName = productName + newline; + } + productName = productName + elementProdName.productName; + + // look for if Substance Name and Product Name have Exact Match + this.substanceNames.forEach((names, indexNames) => { + if (names) { + if (names.name) { + if (elementProdName.productName) { + if (names.name === elementProdName.productName.trim()) { + exactMatchName = names.name; + + // Get Substance Key for the Name + this.generalService.getSubstanceCodesBySubstanceUuid(this.id).subscribe(response => { + if (response) { + const substanceCodes = response; + for (let index = 0; index < substanceCodes.length; index++) { + if (substanceCodes[index].codeSystem) { + if ((substanceCodes[index].codeSystem === this.generalService.getSubstanceKeyType()) && + (substanceCodes[index].type === 'PRIMARY')) { + exactMatchBdnum = substanceCodes[index].code; + matchObject.exactMatchName = exactMatchName; + + } + } + } + } + }); + } + } + } + } + }); + } + }); + + // Ingredient + elementProd.applicationIngredientList.forEach((elementIngred, indexIngred) => { + if (elementIngred) { + if (elementIngred.substanceKey) { + if (indexIngred > 0) { + bdnum = bdnum + '|'; + } + bdnum = bdnum + elementIngred.substanceKey; + } else { + bdnum = bdnum + '(No Substance Key)'; + } + } else { + bdnum = bdnum + '(No Substance Key)'; + } + }); // Ingredient forEach + + matchObject.productName = productName; + matchObject.exactMatchName = exactMatchName; + // matchObject.exactMatchBdnum = exactMatchBdnum; + matchObject.bdnum = bdnum; + + // alert('later ' + exactMatchBdnum); + + }); // Product forEach + + + this.substanceApplicationMatchList.push(matchObject); + } + }); // Application forEach + + this.dataSource = this.substanceApplicationMatchList; + } + }); + } + autoUpdateApp(index: number, applicationId: number, bdnum: string): void { this.loadingService.setLoading(true); this.dataSource[index].autoUpdateMessage = 'Saving....Please wait.'; this.dataSource[index].isDisableButton = true; - this.applicationService.getApplicationDetails(applicationId).subscribe(response => { + this.applicationService.getApplicationById(applicationId).subscribe(response => { if (response) { this.application = response; - if (this.application) { this.application.applicationProductList.forEach((elementProd, indexProd) => { - elementProd.applicationIngredientList.forEach((elementIngred, indexIngred) => { - if (elementIngred.bdnum === null) { - elementIngred.bdnum = bdnum; - elementIngred.basisOfStrengthBdnum = bdnum; + if (!elementIngred.substanceKey) { + elementIngred.substanceKey = bdnum; + elementIngred.basisOfStrengthSubstanceKeyType = this.generalService.getSubstanceKeyType(); + elementIngred.basisOfStrengthSubstanceKey = bdnum; + elementIngred.basisOfStrengthSubstanceKeyType = this.generalService.getSubstanceKeyType(); elementIngred.ingredientType = 'Active Ingredient'; elementIngred.applicantIngredName = this.preferredTerm; @@ -99,7 +219,6 @@ export class SubstanceApplicationMatchListComponent implements OnInit, AfterView this.router.routeReuseStrategy.shouldReuseRoute = () => false; this.router.onSameUrlNavigation = 'reload'; this.router.navigate(['/sub-app-match-list', this.id]); - }); } }); diff --git a/src/app/fda/substance-browse/substance-application-match-list/substance-application-match-list.module.ts b/src/app/fda/substance-browse/substance-application-match-list/substance-application-match-list.module.ts index b6d0cad21..4adadf37d 100644 --- a/src/app/fda/substance-browse/substance-application-match-list/substance-application-match-list.module.ts +++ b/src/app/fda/substance-browse/substance-application-match-list/substance-application-match-list.module.ts @@ -2,11 +2,11 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Router, Routes, RouterModule } from '@angular/router'; import { SubstanceApplicationMatchListComponent } from './substance-application-match-list.component'; -import { MatTableModule } from '@angular/material'; +import { MatTableModule } from '@angular/material/table'; import { MatCardModule } from '@angular/material/card'; -import { MatButtonModule } from '@angular/material'; -import { MatIconModule } from '@angular/material'; -import { MatTooltipModule } from '@angular/material'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; @NgModule({ declarations: [SubstanceApplicationMatchListComponent], diff --git a/src/app/fda/substance-browse/substance-counts/substance-counts.component.html b/src/app/fda/substance-browse/substance-counts/substance-counts.component.html index 928eb29a6..896dcdaf2 100644 --- a/src/app/fda/substance-browse/substance-counts/substance-counts.component.html +++ b/src/app/fda/substance-browse/substance-counts/substance-counts.component.html @@ -1,5 +1,5 @@ -
    -
    +
    +
    Application Count:
    @@ -24,10 +24,30 @@
    -
    +
    Product Count:
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    +
    + + {{searchCount.prodCountConcat}} + +
    + +
    -
    +
    Clinical Trial Count:
    @@ -74,9 +95,9 @@
    -
    +
    - Adverse Event Count: + Adverse Event Case Count:
    @@ -94,12 +115,13 @@
    -
    -
    - - Matching Application Names  - - +
    +
    +
    + +

    -
    +
    \ No newline at end of file diff --git a/src/app/fda/substance-browse/substance-counts/substance-counts.component.scss b/src/app/fda/substance-browse/substance-counts/substance-counts.component.scss index 77d4a3e66..8661bed15 100644 --- a/src/app/fda/substance-browse/substance-counts/substance-counts.component.scss +++ b/src/app/fda/substance-browse/substance-counts/substance-counts.component.scss @@ -35,5 +35,16 @@ } .colorred { - color: red; -} \ No newline at end of file + color: var(--regular-red-color); +} + +.button-style { + background-color:var(--blue-bg-color-rgb); + color: var(--white-color); + text-decoration: none; + vertical-align: middle; + font-weight: 600; + text-align: center; + line-height: 25px; +} + diff --git a/src/app/fda/substance-browse/substance-counts/substance-counts.component.ts b/src/app/fda/substance-browse/substance-counts/substance-counts.component.ts index ab0ab782d..0927f8e02 100644 --- a/src/app/fda/substance-browse/substance-counts/substance-counts.component.ts +++ b/src/app/fda/substance-browse/substance-counts/substance-counts.component.ts @@ -1,9 +1,12 @@ import { Component, OnInit, Input } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; +import { FacetParam } from '@gsrs-core/facets-manager'; import { SubstanceSummaryDynamicContent } from '@gsrs-core/substances-browse'; import { SubstanceDetail } from '@gsrs-core/substance'; import { GeneralService } from '../../service/general.service'; import { ConfigService } from '../../../core/config/config.service'; +import { ApplicationService } from '../../application/service/application.service'; +import { LoadedComponents } from '@gsrs-core/config'; @Component({ selector: 'app-substance-counts', @@ -11,14 +14,26 @@ import { ConfigService } from '../../../core/config/config.service'; styleUrls: ['./substance-counts.component.scss'] }) export class SubstanceCountsComponent implements OnInit, SubstanceSummaryDynamicContent { + substanceNames: any; + substanceKey: string; substance: SubstanceDetail; searchCount: any; - appMatchListCount: 0; + appMatchListCount = 0; appMatchList: any; substanceId: string; + fullFacetField = ''; + total = 0; + privateSearch: string; + privateFacetParams: FacetParam; + pageSize = 0; + isShowMatchList = 'false'; + // application: Application; displayMatchApplicationConfig = false; + loadedComponents: LoadedComponents; + appCountConcat = '0'; constructor( + private applicationService: ApplicationService, private generalService: GeneralService, public activatedRoute: ActivatedRoute, private router: Router, @@ -26,25 +41,120 @@ export class SubstanceCountsComponent implements OnInit, SubstanceSummaryDynamic ngOnInit() { this.substanceId = this.substance.uuid; - this.getSearchCount(); - this.getAppIngredMatchListCount(); + this.loadedComponents = (this.configService.configData && this.configService.configData.loadedComponents) || null; + if (this.loadedComponents && this.loadedComponents.applications) { + this.getSearchCount(); + this.getAppIngredMatchListCount(); + } + // Get Search Count for Application (Commenting below line, do not need) + // this.getApplicationBySubstanceKeyCenter(); + } + + getSubstanceKey() { + if (this.substance) { + // Get Substance Name + // this.substanceName = this.substance._name; + if (this.substance.codes.length > 0) { + this.substance.codes.forEach(element => { + if (element.codeSystem && element.codeSystem === 'BDNUM') { + if (element.type && element.type === 'PRIMARY') { + this.substanceKey = element.code; + } + } + }); + } + } } getSearchCount(): void { this.generalService.getSearchCount(this.substance.uuid).subscribe(searchCount => { - this.searchCount = searchCount; + if (searchCount) { + this.searchCount = searchCount; + } }); } // Application Match Lists functions getAppIngredMatchListCount(): void { - const data = sessionStorage.getItem('matchAppCheckBoxValueSess'); - if ((data !== null) && (data === 'true')) { - this.generalService.getAppIngredtMatchListCount(this.substance.uuid).subscribe(appMatchCount => { - this.appMatchListCount = appMatchCount.total; + this.isShowMatchList = sessionStorage.getItem('matchAppCheckBoxValueSess'); + if ((this.isShowMatchList !== null) && (this.isShowMatchList === 'true')) { + this.getSubstanceNames(this.substance.uuid); + // this.generalService.getAppIngredtMatchListCount(this.substance.uuid).subscribe(response => { + // this.appMatchListCount = response.count; + // }); + } + } + + getSubstanceNames(substanceId: string): void { + if (substanceId) { + this.generalService.getSubstanceNamesBySubstanceUuid(substanceId).subscribe(substanceNames => { + this.substanceNames = substanceNames; + + // Get Preferred Term or DisplayName == true + this.substanceNames.forEach((names, index) => { + if (names.displayName === true) { + // this.preferredTerm = names.name; + } + const facetField = 'root_applicationProductList_applicationProductNameList_productName:'; + if (names) { + if (names.name) { + if (index > 0) { + this.fullFacetField = this.fullFacetField + ' OR '; + } + this.fullFacetField = this.fullFacetField + facetField + "\"" + names.name + "\""; + } + } + }); + this.getApplicationIngredientMatchList(); }); } } + getApplicationIngredientMatchList(): void { + // Facet Search for "Has No Ingredient" + const facetParam = { 'Has Ingredients': { 'params': { 'Has No Ingredient': true }, 'isAllMatch': false } }; + + const subscription = this.applicationService.getApplications( + null, + 0, + 200, + this.fullFacetField, + facetParam + ) + .subscribe(pagingResponse => { + this.appMatchListCount = pagingResponse.count; + }); + } + + // GSRS 3.0 + getApplicationBySubstanceKeyCenter() { + const skip = 5000; + this.pageSize = 5000; + this.privateSearch = this.applicationService.APPALL_SEARCH_SUBSTANCE_KEY + this.substanceKey; + // + this.bdnum + ' AND root_center:' + this.center + ' AND root_fromTable: ' + this.fromTable; + + // this.privateSearch = "http://localhost:8083/api/v1/applicationsall/search?q=root_applicationProductList_applicationIngredientList_substanceKey:"; + + // if (searchType && searchType === 'initial') { + // this.pageSize = 100; + // } + const subscription = this.applicationService.getApplicationAll( + 'default', + skip, + this.pageSize, + this.privateSearch, + this.privateFacetParams + ).subscribe(pagingResponse => { + this.appCountConcat = pagingResponse.total.toString(); + }, error => { + console.log('error'); + }, () => { + subscription.unsubscribe(); + }); + } + + launchApplicationMatchingList(substanceId: string) { + this.router.navigate(['/sub-app-match-list', substanceId]); + } } diff --git a/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventcvm/substance-adverseeventcvm.component.html b/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventcvm/substance-adverseeventcvm.component.html index 8de33ffc1..833a6499f 100644 --- a/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventcvm/substance-adverseeventcvm.component.html +++ b/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventcvm/substance-adverseeventcvm.component.html @@ -1,11 +1,20 @@
    Adverse Event CVM     + + + + +
    -
    + +
    - - + +
    - + - + - + - + @@ -51,6 +61,6 @@
    Adverse Event Adverse Event {{adverse.adverseEvent}} Species Species {{adverse.species}} Adverse Event Count Adverse Event Count {{adverse.aeCount}} Route of Administration Route of Administration {{adverse.routeOfAdmin}}
    - + \ No newline at end of file diff --git a/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventcvm/substance-adverseeventcvm.component.scss b/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventcvm/substance-adverseeventcvm.component.scss index 3abec2a0c..9a0f6226b 100644 --- a/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventcvm/substance-adverseeventcvm.component.scss +++ b/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventcvm/substance-adverseeventcvm.component.scss @@ -17,12 +17,12 @@ padding-top: 5px; } -.colorblue { - color: #4b4572; +.colorpurple { + color: var(--deep-purple-color); } .colorgray { - color: gray; + color: var(--regular-grey-color); } .spinnerstyle { @@ -31,7 +31,7 @@ left: 0; bottom: 56px; right: 0; - background: rgba(150, 148, 148, 0.15); + background: var(--spinner-bg-color); z-index: 1; display: flex; align-items: top; /* Verticle align */ diff --git a/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventcvm/substance-adverseeventcvm.component.ts b/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventcvm/substance-adverseeventcvm.component.ts index b3e7e1ed1..1b34b9fbb 100644 --- a/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventcvm/substance-adverseeventcvm.component.ts +++ b/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventcvm/substance-adverseeventcvm.component.ts @@ -1,9 +1,18 @@ import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { ActivatedRoute, Router, NavigationExtras } from '@angular/router'; +import { MatDialog } from '@angular/material/dialog'; +import { Sort } from '@angular/material/sort'; import { SubstanceCardBaseFilteredList } from '@gsrs-core/substance-details'; import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; -import { AdverseEventService } from '../../../../adverseevent/service/adverseevent.service'; +import { AdverseEventService } from '../../../../adverse-event/service/adverseevent.service'; import { SubstanceDetailsBaseTableDisplay } from '../../../substance-products/substance-details-base-table-display'; import { PageEvent } from '@angular/material/paginator'; +import { FacetParam } from '@gsrs-core/facets-manager'; +import { ExportDialogComponent } from '@gsrs-core/substances-browse/export-dialog/export-dialog.component'; +import { AuthService } from '@gsrs-core/auth'; +import { LoadingService } from '@gsrs-core/loading/loading.service'; +import { Subscription } from 'rxjs'; +import { adverseEventCvmSearchSortValues } from '../../../../adverse-event/adverse-events-cvm-browse/adverse-events-cvm-search-sort-values'; @Component({ selector: 'app-substance-adverseeventcvm', @@ -13,48 +22,137 @@ import { PageEvent } from '@angular/material/paginator'; export class SubstanceAdverseEventCvmComponent extends SubstanceDetailsBaseTableDisplay implements OnInit { - advCvmCount = 0; - showSpinner = false; - + @Input() bdnum: string; @Output() countAdvCvmOut: EventEmitter = new EventEmitter(); + adverseEventCount = 0; + order = '$root_aeCount'; + ascDescDir = 'desc'; + showSpinner = false; + public privateSearchTerm?: string; + private privateFacetParams: FacetParam; + privateExport = false; + disableExport = false; + etag = ''; + loadingStatus = ''; + public sortValues = adverseEventCvmSearchSortValues; + private subscriptions: Array = []; + displayedColumns: string[] = [ 'adverseEvent', 'species', 'adverseEventCount', 'routeOfAdmin' ]; constructor( + private router: Router, public gaService: GoogleAnalyticsService, - private adverseEventService: AdverseEventService + private adverseEventService: AdverseEventService, + private authService: AuthService, + private loadingService: LoadingService, + private dialog: MatDialog ) { super(gaService, adverseEventService); } ngOnInit() { + const rolesSubscription = this.authService.hasAnyRolesAsync('Admin', 'Updater', 'SuperUpdater').subscribe(response => { + this.isAdmin = response; + }); + this.subscriptions.push(rolesSubscription); if (this.bdnum) { - this.getSubstanceAdverseEventCvm(); + this.getAdverseEventCvm(); + // this.getSubstanceAdverseEventCvm(); this.adverseEventCvmListExportUrl(); } } - getSubstanceAdverseEventCvm(pageEvent?: PageEvent): void { - this.setPageEvent(pageEvent); + ngOnDestroy(): void { + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + getAdverseEventCvm(pageEvent?: PageEvent) { + this.setPageEvent(pageEvent); this.showSpinner = true; // Start progress spinner - this.adverseEventService.getSubstanceAdverseEventCvm(this.bdnum, this.page, this.pageSize).subscribe(results => { - this.setResultData(results); - this.advCvmCount = this.totalRecords; - this.countAdvCvmOut.emit(this.advCvmCount); - this.showSpinner = false; // Stop progress spinner - }); - /* - this.searchControl.valueChanges.subscribe(value => { - this.filterList(value, this.adverseevents, this.analyticsEventCategory); + const skip = this.page * this.pageSize; + const privateSearch = 'root_substanceKey:' + this.bdnum; + const subscription = this.adverseEventService.getAdverseEventCvm( + this.order, + skip, + this.pageSize, + privateSearch, + this.privateFacetParams + ) + .subscribe(pagingResponse => { + this.adverseEventService.totalRecords = pagingResponse.total; + this.adverseEventCount = pagingResponse.total; + this.setResultData(pagingResponse.content); + this.etag = pagingResponse.etag; + this.countAdvCvmOut.emit(this.adverseEventCount); }, error => { - console.log(error); + this.showSpinner = false; // Stop progress spinner + console.log('error'); + }, () => { + this.showSpinner = false; // Stop progress spinner + subscription.unsubscribe(); }); - this.countUpdate.emit(adverseevents.length); - }); - */ + this.loadingStatus = ''; + // this.showSpinner = false; // Stop progress spinner + } + + sortData(sort: Sort) { + if (sort.active) { + const orderIndex = this.displayedColumns.indexOf(sort.active).toString(); // + 2; // Adding 2, for name and bdnum. + this.ascDescDir = sort.direction; + this.sortValues.forEach(sortValue => { + if (sortValue.displayedColumns && sortValue.direction) { + if (this.displayedColumns[orderIndex] === sortValue.displayedColumns && this.ascDescDir === sortValue.direction) { + this.order = sortValue.value; + } + } + }); + this.getAdverseEventCvm(); + } + return; + } + + export() { + if (this.etag) { + const extension = 'xlsx'; + const url = this.getApiExportUrl(this.etag, extension); + if (this.authService.getUser() !== '') { + const dialogReference = this.dialog.open(ExportDialogComponent, { + // height: '215x', + width: '700px', + data: { 'extension': extension, 'type': 'substanceAdverseEventCvm', 'entity': 'adverseeventcvm', 'hideOptionButtons': true } + }); + // this.overlayContainer.style.zIndex = '1002'; + dialogReference.afterClosed().subscribe(response => { + // this.overlayContainer.style.zIndex = null; + const name = response.name; + const id = response.id; + if (name && name !== '') { + this.loadingService.setLoading(true); + const fullname = name + '.' + extension; + this.authService.startUserDownload(url, this.privateExport, fullname, id).subscribe(response => { + // this.authService.startUserDownload(url, this.privateExport, fullname).subscribe(response => { + this.loadingService.setLoading(false); + const navigationExtras: NavigationExtras = { + queryParams: { + totalSub: this.adverseEventCount + } + }; + const params = { 'total': this.adverseEventCount }; + this.router.navigate(['/user-downloads/', response.id]); + }, error => this.loadingService.setLoading(false)); + } + }); + } + } + } + + getApiExportUrl(etag: string, extension: string): string { + return this.adverseEventService.getApiExportUrlCvm(etag, extension); } adverseEventCvmListExportUrl() { diff --git a/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventdme/substance-adverseeventdme.component.html b/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventdme/substance-adverseeventdme.component.html index ccc99a7b0..f0f8bb2c2 100644 --- a/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventdme/substance-adverseeventdme.component.html +++ b/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventdme/substance-adverseeventdme.component.html @@ -1,11 +1,20 @@
    Adverse Event DME     + + + + +
    - -
    + +
    - - + +
    - + - + - + - + - + - + @@ -59,6 +69,6 @@
    DME Reactions DME Reactions {{adverse.dmeReactions}} PT Term Meddra PT Term Meddra {{adverse.ptTermMeddra}} Case Count Case Count {{adverse.caseCount}} Dme Count Dme Count {{adverse.dmeCount}} Dme Count Percent Dme Count Percent {{adverse.dmeCountPercent}} Weighted Average PRR Weighted Average PRR {{adverse.weightedAvgPrr | number : '.2-2'}}
    - + \ No newline at end of file diff --git a/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventdme/substance-adverseeventdme.component.scss b/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventdme/substance-adverseeventdme.component.scss index 3abec2a0c..9a0f6226b 100644 --- a/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventdme/substance-adverseeventdme.component.scss +++ b/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventdme/substance-adverseeventdme.component.scss @@ -17,12 +17,12 @@ padding-top: 5px; } -.colorblue { - color: #4b4572; +.colorpurple { + color: var(--deep-purple-color); } .colorgray { - color: gray; + color: var(--regular-grey-color); } .spinnerstyle { @@ -31,7 +31,7 @@ left: 0; bottom: 56px; right: 0; - background: rgba(150, 148, 148, 0.15); + background: var(--spinner-bg-color); z-index: 1; display: flex; align-items: top; /* Verticle align */ diff --git a/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventdme/substance-adverseeventdme.component.ts b/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventdme/substance-adverseeventdme.component.ts index 04c81e35b..c176ba898 100644 --- a/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventdme/substance-adverseeventdme.component.ts +++ b/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventdme/substance-adverseeventdme.component.ts @@ -1,9 +1,18 @@ import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { ActivatedRoute, Router, NavigationExtras } from '@angular/router'; +import { MatDialog } from '@angular/material/dialog'; +import { Sort } from '@angular/material/sort'; import { SubstanceCardBaseFilteredList } from '@gsrs-core/substance-details'; import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; -import { AdverseEventService } from '../../../../adverseevent/service/adverseevent.service'; +import { AdverseEventService } from '../../../../adverse-event/service/adverseevent.service'; import { SubstanceDetailsBaseTableDisplay } from '../../../substance-products/substance-details-base-table-display'; import { PageEvent } from '@angular/material/paginator'; +import { FacetParam } from '@gsrs-core/facets-manager'; +import { ExportDialogComponent } from '@gsrs-core/substances-browse/export-dialog/export-dialog.component'; +import { AuthService } from '@gsrs-core/auth'; +import { LoadingService } from '@gsrs-core/loading/loading.service'; +import { Subscription } from 'rxjs'; +import { adverseEventDmeSearchSortValues } from '../../../../adverse-event/adverse-events-dme-browse/adverse-events-dme-search-sort-values'; @Component({ selector: 'app-substance-adverseeventdme', @@ -13,29 +22,86 @@ import { PageEvent } from '@angular/material/paginator'; export class SubstanceAdverseEventDmeComponent extends SubstanceDetailsBaseTableDisplay implements OnInit { - advDmeCount = 0; - showSpinner = false; - + @Input() bdnum: string; @Output() countAdvDmeOut: EventEmitter = new EventEmitter(); + adverseEventCount = 0; + order = '$root_dmeCount'; + ascDescDir = 'desc'; + showSpinner = false; + public privateSearchTerm?: string; + private privateFacetParams: FacetParam; + privateExport = false; + disableExport = false; + etag = ''; + loadingStatus = ''; + public sortValues = adverseEventDmeSearchSortValues; + private subscriptions: Array = []; + displayedColumns: string[] = [ 'dmeReactions', 'ptTermMeddra', 'caseCount', 'dmeCount', 'dmeCountPercent', 'weightedAvgPrr' ]; constructor( + private router: Router, public gaService: GoogleAnalyticsService, - private adverseEventService: AdverseEventService + private adverseEventService: AdverseEventService, + private authService: AuthService, + private loadingService: LoadingService, + private dialog: MatDialog ) { super(gaService, adverseEventService); } ngOnInit() { + const rolesSubscription = this.authService.hasAnyRolesAsync('Admin', 'Updater', 'SuperUpdater').subscribe(response => { + this.isAdmin = response; + }); + this.subscriptions.push(rolesSubscription); + if (this.bdnum) { - this.getSubstanceAdverseEventDme(); + this.getAdverseEventDme(); + // this.getSubstanceAdverseEventDme(); this.adverseEventDmeListExportUrl(); } } + ngOnDestroy(): void { + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + + getAdverseEventDme(pageEvent?: PageEvent) { + this.setPageEvent(pageEvent); + this.showSpinner = true; // Start progress spinner + const skip = this.page * this.pageSize; + const privateSearch = 'root_substanceKey:' + this.bdnum; + const subscription = this.adverseEventService.getAdverseEventDme( + this.order, + skip, + this.pageSize, + privateSearch, + this.privateFacetParams + ) + .subscribe(pagingResponse => { + this.adverseEventService.totalRecords = pagingResponse.total; + this.adverseEventCount = pagingResponse.total; + this.setResultData(pagingResponse.content); + this.etag = pagingResponse.etag; + this.countAdvDmeOut.emit(this.adverseEventCount); + }, error => { + this.showSpinner = false; // Stop progress spinner + console.log('error'); + }, () => { + this.showSpinner = false; // Stop progress spinner + subscription.unsubscribe(); + }); + this.loadingStatus = ''; + // this.showSpinner = false; // Stop progress spinner + } + + /* getSubstanceAdverseEventDme(pageEvent?: PageEvent): void { this.setPageEvent(pageEvent); @@ -46,15 +112,62 @@ export class SubstanceAdverseEventDmeComponent extends SubstanceDetailsBaseTable this.countAdvDmeOut.emit(this.advDmeCount); this.showSpinner = false; // Stop progress spinner }); - /* - this.searchControl.valueChanges.subscribe(value => { - this.filterList(value, this.adverseevents, this.analyticsEventCategory); - }, error => { - console.log(error); + } + */ + + sortData(sort: Sort) { + if (sort.active) { + const orderIndex = this.displayedColumns.indexOf(sort.active).toString(); + this.ascDescDir = sort.direction; + this.sortValues.forEach(sortValue => { + if (sortValue.displayedColumns && sortValue.direction) { + if (this.displayedColumns[orderIndex] === sortValue.displayedColumns && this.ascDescDir === sortValue.direction) { + this.order = sortValue.value; + } + } }); - this.countUpdate.emit(adverseevents.length); - }); - */ + this.getAdverseEventDme(); + } + return; + } + + export() { + if (this.etag) { + const extension = 'xlsx'; + const url = this.getApiExportUrl(this.etag, extension); + if (this.authService.getUser() !== '') { + const dialogReference = this.dialog.open(ExportDialogComponent, { + // height: '215x', + width: '700px', + data: { 'extension': extension, 'type': 'substanceAdverseEventDme','entity': 'adverseeventdme', 'hideOptionButtons': true } + }); + // this.overlayContainer.style.zIndex = '1002'; + dialogReference.afterClosed().subscribe(response => { + // this.overlayContainer.style.zIndex = null; + const name = response.name; + const id = response.id; + if (name && name !== '') { + this.loadingService.setLoading(true); + const fullname = name + '.' + extension; + this.authService.startUserDownload(url, this.privateExport, fullname, id).subscribe(response => { + // this.authService.startUserDownload(url, this.privateExport, fullname).subscribe(response => { + this.loadingService.setLoading(false); + const navigationExtras: NavigationExtras = { + queryParams: { + totalSub: this.adverseEventCount + } + }; + const params = { 'total': this.adverseEventCount }; + this.router.navigate(['/user-downloads/', response.id]); + }, error => this.loadingService.setLoading(false)); + } + }); + } + } + } + + getApiExportUrl(etag: string, extension: string): string { + return this.adverseEventService.getApiExportUrlDme(etag, extension); } adverseEventDmeListExportUrl() { diff --git a/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventpt/substance-adverseeventpt.component.html b/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventpt/substance-adverseeventpt.component.html index cc5beeb9c..ce54b80cc 100644 --- a/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventpt/substance-adverseeventpt.component.html +++ b/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventpt/substance-adverseeventpt.component.html @@ -5,11 +5,20 @@ Adverse Event PT
    + + + + +
    - + + +
    + + +
    + +
    - @@ -49,7 +66,16 @@ @@ -62,11 +88,6 @@
    PT Term - {{adverse.ptTerm}} -
    +
    + {{adverse.ptTerm}} + - Analysis by Adverse Event + title="Analysis by Adverse Event/PT Term on Shiny Server"> + analytics - + PT Count -
    +
    + +
    +
    + FAERS Public Dashboard +
    + +
    +
    - - - -
    - - -
    \ No newline at end of file + + \ No newline at end of file diff --git a/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventpt/substance-adverseeventpt.component.scss b/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventpt/substance-adverseeventpt.component.scss index 487637cb7..60530fedb 100644 --- a/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventpt/substance-adverseeventpt.component.scss +++ b/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventpt/substance-adverseeventpt.component.scss @@ -14,6 +14,10 @@ font-size: 8px; } +.font10px { + font-size: 10px; +} + .font15px { font-size: 15px; } @@ -33,7 +37,7 @@ marginleft50px { .padleft25px { padding-left: 25px; } - + .padtop5px { padding-top: 5px; } @@ -47,23 +51,23 @@ marginleft50px { } .colorblue { - color: #4b4572; + color: var(--deep-purple-color); } - + .colorgray { - color: gray; + color: var(--regular-grey-color); } .colorred { - color: red; + color: var(--regular-red-color); } .colororange { - color: rgb(228, 134, 46); + color: var(--orange-color-rgb-2); } .bordergray { - border: 1px solid gray; + border: 1px solid var(--regular-grey-color); } .width120px { @@ -82,7 +86,7 @@ marginleft50px { left: 0; bottom: 56px; right: 0; - background: rgba(150, 148, 148, 0.15); + background: var(--spinner-bg-color); z-index: 1; display: flex; align-items: top; /* Verticle align */ diff --git a/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventpt/substance-adverseeventpt.component.ts b/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventpt/substance-adverseeventpt.component.ts index 926b78189..b00508161 100644 --- a/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventpt/substance-adverseeventpt.component.ts +++ b/src/app/fda/substance-details/substance-products/substance-adverseevent/adverseeventpt/substance-adverseeventpt.component.ts @@ -1,11 +1,19 @@ -import { Component, OnInit, Output, EventEmitter, Input } from '@angular/core'; +import { Component, OnInit, OnDestroy, Output, EventEmitter, Input } from '@angular/core'; +import { TitleCasePipe } from '@angular/common'; +import { ActivatedRoute, Router, NavigationExtras } from '@angular/router'; import { PageEvent } from '@angular/material/paginator'; +import { MatDialog } from '@angular/material/dialog'; import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; -import { AdverseEventService } from '../../../../adverseevent/service/adverseevent.service'; +import { AdverseEventService } from '../../../../adverse-event/service/adverseevent.service'; import { SubstanceDetailsBaseTableDisplay } from '../../../substance-products/substance-details-base-table-display'; -import { Sort } from '@angular/material'; +import { Sort } from '@angular/material/sort'; import { LoadingService } from '@gsrs-core/loading/loading.service'; import { ConfigService } from '@gsrs-core/config'; +import { FacetParam } from '@gsrs-core/facets-manager'; +import { ExportDialogComponent } from '@gsrs-core/substances-browse/export-dialog/export-dialog.component'; +import { AuthService } from '@gsrs-core/auth'; +import { Subscription } from 'rxjs'; +import { adverseEventPtSearchSortValues } from '../../../../adverse-event/adverse-events-pt-browse/adverse-events-pt-search-sort-values'; @Component({ selector: 'app-substance-adverseeventpt', @@ -13,15 +21,23 @@ import { ConfigService } from '@gsrs-core/config'; styleUrls: ['./substance-adverseeventpt.component.scss'] }) -export class SubstanceAdverseEventPtComponent extends SubstanceDetailsBaseTableDisplay implements OnInit { +export class SubstanceAdverseEventPtComponent extends SubstanceDetailsBaseTableDisplay implements OnInit, OnDestroy { + @Input() bdnum: string; + @Input() substanceName: string; + @Output() countAdvPtOut: EventEmitter = new EventEmitter(); - advPtCount = 0; - orderBy = 5; + adverseEventCount = 0; + order = '$root_ptCount'; ascDescDir = 'desc'; showSpinner = false; - - @Input() substanceName: string; - @Output() countAdvPtOut: EventEmitter = new EventEmitter(); + public privateSearchTerm?: string; + private privateFacetParams: FacetParam; + public sortValues = adverseEventPtSearchSortValues; + privateExport = false; + disableExport = false; + etag = ''; + loadingStatus = ''; + private subscriptions: Array = []; adverseEventShinySubstanceNameDisplay = false; adverseEventShinyAdverseEventDisplay = false; @@ -30,6 +46,13 @@ export class SubstanceAdverseEventPtComponent extends SubstanceDetailsBaseTableD adverseEventShinySubstanceNameURLWithParam: string; adverseEventShinyAdverseEventURLWithParam: string; + // FAERS DASHBOARD + FAERSDashboardAdverseEventUrl: string; + FAERSDashboardSubstanceName: string; + FAERSDashboardSearchTerm = "/select/Search%20Term/"; // FAERS Adverse Event 'Substance Name' + FAERSDashboardReactionTerm = "/select/Reaction%20Term/"; // GSRS Adverse Event 'PT Term' + FAERSDashboardReactionGroup = "/select/Reaction%20Group/"; // GSRS Adverse Event 'Prim SOC' + filtered: Array; displayedColumns: string[] = [ 'ptTerm', @@ -38,24 +61,72 @@ export class SubstanceAdverseEventPtComponent extends SubstanceDetailsBaseTableD 'ptCount', 'prr' ]; - constructor( + private router: Router, public gaService: GoogleAnalyticsService, private adverseEventService: AdverseEventService, private loadingService: LoadingService, private configService: ConfigService, + private authService: AuthService, + private dialog: MatDialog ) { super(gaService, adverseEventService); } ngOnInit() { + const rolesSubscription = this.authService.hasAnyRolesAsync('Admin', 'Updater', 'SuperUpdater').subscribe(response => { + this.isAdmin = response; + }); + this.subscriptions.push(rolesSubscription); + if (this.bdnum) { - this.getSubstanceAdverseEventPt(); + this.getAdverseEventPt(); + + // FAERS DASHBOARD + this.getFaersDashboardUrl(); + this.getFaersDashboardRecordByName(); + this.adverseEventPtListExportUrl(); this.getAdverseEventShinyConfig(); } } + ngOnDestroy(): void { + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + + getAdverseEventPt(pageEvent?: PageEvent) { + this.setPageEvent(pageEvent); + this.showSpinner = true; // Start progress spinner + const skip = this.page * this.pageSize; + const privateSearch = 'root_substanceKey:' + this.bdnum; + const subscription = this.adverseEventService.getAdverseEventPt( + this.order, + skip, + this.pageSize, + privateSearch, + this.privateFacetParams + ) + .subscribe(pagingResponse => { + this.adverseEventService.totalRecords = pagingResponse.total; + this.adverseEventCount = pagingResponse.total; + this.setResultData(pagingResponse.content); + this.etag = pagingResponse.etag; + this.countAdvPtOut.emit(this.adverseEventCount); + }, error => { + this.showSpinner = false; // Stop progress spinner + console.log('error'); + }, () => { + this.showSpinner = false; // Stop progress spinner + subscription.unsubscribe(); + }); + this.loadingStatus = ''; + // this.showSpinner = false; // Stop progress spinner + } + + /* getSubstanceAdverseEventPt(pageEvent?: PageEvent): void { this.setPageEvent(pageEvent); this.showSpinner = true; // Start progress spinner @@ -67,6 +138,7 @@ export class SubstanceAdverseEventPtComponent extends SubstanceDetailsBaseTableD this.showSpinner = false; // Stop progress spinner }); } + */ adverseEventPtListExportUrl() { if (this.bdnum != null) { @@ -76,22 +148,95 @@ export class SubstanceAdverseEventPtComponent extends SubstanceDetailsBaseTableD sortData(sort: Sort) { if (sort.active) { - this.orderBy = this.displayedColumns.indexOf(sort.active) + 2; // Adding 2, for name and bdnum. + const orderIndex = this.displayedColumns.indexOf(sort.active).toString(); // + 2; // Adding 2, for name and bdnum. this.ascDescDir = sort.direction; - this.getSubstanceAdverseEventPt(); + this.sortValues.forEach(sortValue => { + if (sortValue.displayedColumns && sortValue.direction) { + if (this.displayedColumns[orderIndex] === sortValue.displayedColumns && this.ascDescDir === sortValue.direction) { + this.order = sortValue.value; + } + } + }); + this.getAdverseEventPt(); } return; } + export() { + if (this.etag) { + const extension = 'xlsx'; + const url = this.getApiExportUrl(this.etag, extension); + if (this.authService.getUser() !== '') { + const dialogReference = this.dialog.open(ExportDialogComponent, { + // height: '215x', + width: '700px', + data: { 'extension': extension, 'type': 'substanceAdverseEventPt', 'entity': 'adverseeventpt', 'hideOptionButtons': true } + }); + // this.overlayContainer.style.zIndex = '1002'; + dialogReference.afterClosed().subscribe(response => { + // this.overlayContainer.style.zIndex = null; + const name = response.name; + const id = response.id; + if (name && name !== '') { + this.loadingService.setLoading(true); + const fullname = name + '.' + extension; + this.authService.startUserDownload(url, this.privateExport, fullname, id).subscribe(response => { + // this.authService.startUserDownload(url, this.privateExport, fullname).subscribe(response => { + this.loadingService.setLoading(false); + const navigationExtras: NavigationExtras = { + queryParams: { + totalSub: this.adverseEventCount + } + }; + const params = { 'total': this.adverseEventCount }; + this.router.navigate(['/user-downloads/', response.id]); + }, error => this.loadingService.setLoading(false)); + } + }); + } + } + } + + getApiExportUrl(etag: string, extension: string): string { + return this.adverseEventService.getApiExportUrlPt(etag, extension); + } + + getFaersDashboardRecordByName(): void { + // Get FAERS Name from database table that contains 'P' and 'G' in name. + // Example: Acetazolamide (G) instead of GSRS name Acetazolamide + const faersNameSubscription = this.adverseEventService.getFaersDashboardRecordByName(this.substanceName).subscribe(results => { + if (results) { + if (results.name) { + this.FAERSDashboardSubstanceName = results.name; + this.FAERSDashboardAdverseEventUrl = this.FAERSDashboardAdverseEventUrl + results.name + this.FAERSDashboardReactionTerm; + } + } + }); + this.subscriptions.push(faersNameSubscription); + } + + getFaersDashboardUrl(): void { + if (this.configService.configData) { + if (this.configService.configData.FAERSDashboardAdverseEventUrl + && this.configService.configData.FAERSDashboardAdverseEventUrl !== null) { + const faersUrlConfig = this.configService.configData.FAERSDashboardAdverseEventUrl; + + // FULL FAERS DASHBOARD URL + // faersUrl + /select/Search%20Term/ + FaersName + /select/Reaction%20Term/ + ptTerm + /select/Reaction%20Group/ + primSoc; + this.FAERSDashboardAdverseEventUrl = faersUrlConfig + this.FAERSDashboardSearchTerm; + } + } + } + getAdverseEventShinyConfig(): void { if (this.configService.configData) { // Analysis by Substance in Shiny Config if (this.configService.configData.adverseEventShinySubstanceNameDisplay - && this.configService.configData.adverseEventShinySubstanceNameDisplay !== null) { + && this.configService.configData.adverseEventShinySubstanceNameDisplay !== null) { this.adverseEventShinySubstanceNameDisplay = JSON.parse(this.configService.configData.adverseEventShinySubstanceNameDisplay); } - if (this.configService.configData.adverseEventShinySubstanceNameURL + if (this.configService.configData.adverseEventShinySubstanceNameURL && this.configService.configData.adverseEventShinySubstanceNameURL !== null) { this.adverseEventShinySubstanceNameURL = this.configService.configData.adverseEventShinySubstanceNameURL; this.adverseEventShinySubstanceNameURLWithParam = this.adverseEventShinySubstanceNameURL + decodeURIComponent(this.substanceName); diff --git a/src/app/fda/substance-details/substance-products/substance-application/substance-application.component.html b/src/app/fda/substance-details/substance-products/substance-application/substance-application.component.html index 34c62044c..a42536b1b 100644 --- a/src/app/fda/substance-details/substance-products/substance-application/substance-application.component.html +++ b/src/app/fda/substance-details/substance-products/substance-application/substance-application.component.html @@ -12,11 +12,21 @@     {{loadingStatus}}     + + + + + +
    @@ -27,20 +37,21 @@
    - +
    - + + - + + - - + + - + @@ -85,7 +96,7 @@
    Application Type Application Type - - {{application.appType}} -    - - - + + {{application.appType}} +    + + + - - + {{application.appType}}
    (Integrity)
    @@ -48,12 +59,12 @@ -
    Application Number Application Number {{application.appNumber}} Product Name Product Name
    @@ -66,17 +77,17 @@ -
    Sponsor Name Sponsor Name {{application.sponsorName}} Application Status Application Status {{application.appStatus}} Application Sub Type Application Sub Type {{application.appSubType}}
    + (page)="getApplicationBySubstanceKeyCenter($event)" [pageSizeOptions]="[5, 10, 25, 100]"> diff --git a/src/app/fda/substance-details/substance-products/substance-application/substance-application.component.scss b/src/app/fda/substance-details/substance-products/substance-application/substance-application.component.scss index 1b28c6409..d0b49fed0 100644 --- a/src/app/fda/substance-details/substance-products/substance-application/substance-application.component.scss +++ b/src/app/fda/substance-details/substance-products/substance-application/substance-application.component.scss @@ -30,15 +30,15 @@ } .colorblue { - color: #4b4572; + color: var(--deep-purple-color); } .colorgray { - color: gray; + color: var(--regular-grey-color); } .colorred { - color: red; + color: var(--regular-red-color); } @@ -48,7 +48,7 @@ left: 0; bottom: 56px; right: 0; - background: rgba(150, 148, 148, 0.15); + background: var(--spinner-bg-color); z-index: 1; display: flex; align-items: top; /* Verticle align */ diff --git a/src/app/fda/substance-details/substance-products/substance-application/substance-application.component.ts b/src/app/fda/substance-details/substance-products/substance-application/substance-application.component.ts index 7988cfd6a..d8632c2b9 100644 --- a/src/app/fda/substance-details/substance-products/substance-application/substance-application.component.ts +++ b/src/app/fda/substance-details/substance-products/substance-application/substance-application.component.ts @@ -1,11 +1,20 @@ import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; -import { SubstanceCardBaseFilteredList } from '@gsrs-core/substance-details'; +import { ActivatedRoute, Router, NavigationExtras } from '@angular/router'; +import { MatDialog } from '@angular/material/dialog'; +import { PageEvent } from '@angular/material/paginator'; +import { take } from 'rxjs/operators'; +import { Sort } from '@angular/material/sort'; +import { AuthService } from '@gsrs-core/auth'; +import { LoadingService } from '@gsrs-core/loading/loading.service'; import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; +import { FacetParam } from '@gsrs-core/facets-manager'; +import { ExportDialogComponent } from '@gsrs-core/substances-browse/export-dialog/export-dialog.component'; import { ApplicationService } from '../../../application/service/application.service'; +import { GeneralService } from '../../../service/general.service'; +import { Application } from '../../../application/model/application.model'; import { SubstanceDetailsBaseTableDisplay } from '../../substance-products/substance-details-base-table-display'; -import { PageEvent } from '@angular/material/paginator'; -import { AuthService } from '@gsrs-core/auth'; -import { take } from 'rxjs/operators'; +import { SubstanceCardBaseFilteredList } from '@gsrs-core/substance-details'; +import { applicationSearchSortValues } from '../../../application/applications-browse/application-search-sort-values'; @Component({ selector: 'app-substance-application', @@ -14,51 +23,89 @@ import { take } from 'rxjs/operators'; }) export class SubstanceApplicationComponent extends SubstanceDetailsBaseTableDisplay implements OnInit { - + application: any; applicationCount = 0; - centerList = ''; + totalApplication = 0; + centerList: Array = []; + centerListOriginal: Array = []; center = ''; fromTable = ''; loadingStatus = ''; showSpinner = false; foundCenterList = false; loadingComplete = false; - + // result: any; + public privateSearch?: string; + private privateFacetParams: FacetParam; + privateExport = false; + disableExport = false; + etag = ''; + etagAllExport = ''; + @Input() bdnum: string; @Output() countApplicationOut: EventEmitter = new EventEmitter(); - + public sortValues = applicationSearchSortValues; + order = '$root_appNumber'; + ascDescDir = 'desc'; displayedColumns: string[] = [ - 'appType', 'appNumber', 'productName', 'sponsorName', 'applicationStatus', 'applicationSubType']; + 'appType', + 'appNumber', + 'productName', + 'sponsorName', + 'appStatus', + 'applicationSubType' + ]; constructor( + private router: Router, + public authService: AuthService, + private loadingService: LoadingService, public gaService: GoogleAnalyticsService, private applicationService: ApplicationService, - public authService: AuthService + private generalService: GeneralService, + private dialog: MatDialog ) { super(gaService, applicationService); } ngOnInit() { - this.authService.hasAnyRolesAsync('Admin', 'Updater', 'SuperUpdater').pipe(take(1)).subscribe(response => { this.isAdmin = response; }); if (this.bdnum) { - this.getApplicationCenterByBdnum(); - // this.getSubstanceApplications(); - this.applicationListExportUrl(); + this.getApplicationCenterList(); + + this.privateSearch = 'root_applicationProductList_applicationIngredientList_substanceKey:' + + this.bdnum; + this.getApplicationBySubstanceKeyCenter(null, 'initial'); } } - getApplicationCenterByBdnum(): string { - this.applicationService.getApplicationCenterByBdnum(this.bdnum).subscribe(results => { - this.centerList = results.centerList; - if (this.centerList.length > 0) { + getApplicationCenterList(): void { + this.applicationService.getApplicationCenterList(this.bdnum).subscribe(results => { + this.centerListOriginal = results; + this.centerList = results; + if (this.centerList && this.centerList.length > 0) { this.foundCenterList = true; + + // Replace 'Darrts' to 'Integrity' and 'SRS' to 'GSRS' + /* this.centerList.forEach((cent, index) => { + if (cent != null) { + let centerReplace = ''; + if (cent.indexOf('Darrts') > 0) { + centerReplace = cent.replace('Darrts', 'Integrity'); + } else if (cent.indexOf('SRS') > 0) { + centerReplace = cent.replace('SRS', 'GSRS'); + } + + if (centerReplace.length > 0) { + this.centerList[index] = centerReplace; + } + } + }); */ } this.loadingComplete = true; }); - return this.centerList; } applicationTabSelected($event) { @@ -72,15 +119,70 @@ export class SubstanceApplicationComponent extends SubstanceDetailsBaseTableDisp this.center = textLabel.slice(0, index); this.fromTable = textLabel.slice(index + 1, textLabel.length); - // set the current result data to empty or null. - this.paged = []; - - this.getSubstanceApplications(); + //let fromReplace = ''; + /* + if (this.fromTable.indexOf('Integrity') >= 0) { + fromReplace = this.fromTable.replace('Integrity', 'Darrts'); + } else if (this.fromTable.indexOf('GSRS') >= 0) { + fromReplace = this.fromTable.replace('GSRS', 'SRS'); + } + if (fromReplace && fromReplace.length > 0) { + this.fromTable = fromReplace; + } + */ } + + // set the current result data to empty or null. + this.paged = []; + + this.privateSearch = 'root_applicationProductList_applicationIngredientList_substanceKey:' + + this.bdnum + ' AND root_center:' + this.center + ' AND root_fromTable: ' + this.fromTable; + + this.getApplicationBySubstanceKeyCenter(); } } - getSubstanceApplications(pageEvent?: PageEvent): void { + // GSRS 3.0 + getApplicationBySubstanceKeyCenter(pageEvent?: PageEvent, searchType?: string) { + this.setPageEvent(pageEvent); + this.showSpinner = true; // Start progress spinner + const skip = this.page * this.pageSize; + + // if (searchType && searchType === 'initial') { + // this.pageSize = 100; + // } + const subscription = this.applicationService.getApplicationAll( + this.order, + skip, + this.pageSize, + this.privateSearch, + this.privateFacetParams + ) + .subscribe(pagingResponse => { + if (searchType && searchType === 'initial') { + this.etagAllExport = pagingResponse.etag; + } else { + this.applicationService.totalRecords = pagingResponse.total; + this.applicationCount = pagingResponse.total; + + this.setResultData(pagingResponse.content); + + this.etag = pagingResponse.etag; + this.countApplicationOut.emit(this.applicationCount); + } + }, error => { + this.showSpinner = false; // Stop progress spinner + console.log('error'); + }, () => { + this.showSpinner = false; // Stop progress spinner + subscription.unsubscribe(); + }); + this.loadingStatus = ''; + // this.showSpinner = false; // Stop progress spinner + } + + /* + getSubstanceApplications(pageEvent ?: PageEvent): void { this.setPageEvent(pageEvent); this.showSpinner = true; // Start progress spinner @@ -92,16 +194,56 @@ export class SubstanceApplicationComponent extends SubstanceDetailsBaseTableDisp this.loadingStatus = ''; this.showSpinner = false; // Stop progress spinner }); + */ + /* + this.searchControl.valueChanges.subscribe(value => { + this.filterList(value, this.clinicaltrials, this.analyticsEventCategory); + }, error => { + console.log(error); + }); + this.countUpdate.emit(clinicaltrials.length); + }); - /* - this.searchControl.valueChanges.subscribe(value => { - this.filterList(value, this.clinicaltrials, this.analyticsEventCategory); - }, error => { - console.log(error); +} +*/ + + export() { + if (this.etagAllExport) { + const extension = 'xlsx'; + const url = this.getApiExportUrl(this.etagAllExport, extension); + if (this.authService.getUser() !== '') { + const dialogReference = this.dialog.open(ExportDialogComponent, { + // height: '215x', + width: '700px', + data: { 'extension': extension, 'type': 'substanceApplication', 'entity': 'applications', 'hideOptionButtons': true } }); - this.countUpdate.emit(clinicaltrials.length); - }); - */ + // this.overlayContainer.style.zIndex = '1002'; + dialogReference.afterClosed().subscribe(response => { + // this.overlayContainer.style.zIndex = null; + const name = response.name; + const id = response.id; + if (name && name !== '') { + this.loadingService.setLoading(true); + const fullname = name + '.' + extension; + this.authService.startUserDownload(url, this.privateExport, fullname, id).subscribe(response => { + // this.authService.startUserDownload(url, this.privateExport, fullname).subscribe(response => { + this.loadingService.setLoading(false); + const navigationExtras: NavigationExtras = { + queryParams: { + totalSub: this.applicationCount + } + }; + const params = { 'total': this.applicationCount }; + this.router.navigate(['/user-downloads/', response.id]); + }, error => this.loadingService.setLoading(false)); + } + }); + } + } + } + + getApiExportUrl(etag: string, extension: string): string { + return this.applicationService.getAppAllApiExportUrl(etag, extension); } get updateApplicationUrl(): string { @@ -114,4 +256,20 @@ export class SubstanceApplicationComponent extends SubstanceDetailsBaseTableDisp } } + sortData(sort: Sort) { + if (sort.active) { + const orderIndex = this.displayedColumns.indexOf(sort.active).toString(); // + 2; // Adding 2, for name and bdnum. + this.ascDescDir = sort.direction; + // Get Sort Values from applicationSearchSortValues + this.sortValues.forEach(sortValue => { + if (sortValue.displayedColumns && sortValue.direction) { + if (this.displayedColumns[orderIndex] === sortValue.displayedColumns && this.ascDescDir === sortValue.direction) { + this.order = sortValue.value; + } + } + }); + this.getApplicationBySubstanceKeyCenter(); + } + return; + } } diff --git a/src/app/fda/substance-details/substance-products/substance-clinical-trials-eu/substance-clinical-trials-eu.component.html b/src/app/fda/substance-details/substance-products/substance-clinical-trials-eu/substance-clinical-trials-eu.component.html index 3387907fb..2ca26a268 100644 --- a/src/app/fda/substance-details/substance-products/substance-clinical-trials-eu/substance-clinical-trials-eu.component.html +++ b/src/app/fda/substance-details/substance-products/substance-clinical-trials-eu/substance-clinical-trials-eu.component.html @@ -1,11 +1,19 @@ -
    @@ -15,34 +23,31 @@
    - +
    - - + + - + - + - - - + + + @@ -51,5 +56,6 @@ - \ No newline at end of file + (page)="getSubstanceClinicalTrialsEurope($event)" [pageSizeOptions]="[5, 10, 25, 100]"> + + diff --git a/src/app/fda/substance-details/substance-products/substance-clinical-trials-eu/substance-clinical-trials-eu.component.scss b/src/app/fda/substance-details/substance-products/substance-clinical-trials-eu/substance-clinical-trials-eu.component.scss index ab457f2a4..226cd4047 100644 --- a/src/app/fda/substance-details/substance-products/substance-clinical-trials-eu/substance-clinical-trials-eu.component.scss +++ b/src/app/fda/substance-details/substance-products/substance-clinical-trials-eu/substance-clinical-trials-eu.component.scss @@ -1,3 +1,11 @@ +.mat-column-trialNumber { + word-wrap: break-word !important; + white-space: unset !important; + flex: 0 0 20% !important; + width: 20% !important; + white-space: nowrap +} + .mat-table-style { td.mat-cell { vertical-align: top; @@ -14,11 +22,11 @@ } .colorgray { - color: gray; + color: var(--regular-grey-color); } .colorred { - color: red; + color: var(--regular-red-color); } .padleft25px { @@ -30,7 +38,7 @@ } .colorblue { - color: #4b4572; + color: var(--deep-purple-color); } .spinnerstyle { @@ -39,7 +47,7 @@ left: 0; bottom: 56px; right: 0; - background: rgba(150, 148, 148, 0.15); + background: var(--spinner-bg-color); z-index: 1; display: flex; align-items: top; /* Verticle align */ diff --git a/src/app/fda/substance-details/substance-products/substance-clinical-trials-eu/substance-clinical-trials-eu.component.ts b/src/app/fda/substance-details/substance-products/substance-clinical-trials-eu/substance-clinical-trials-eu.component.ts index 0ef016838..b81ce98a0 100644 --- a/src/app/fda/substance-details/substance-products/substance-clinical-trials-eu/substance-clinical-trials-eu.component.ts +++ b/src/app/fda/substance-details/substance-products/substance-clinical-trials-eu/substance-clinical-trials-eu.component.ts @@ -4,6 +4,18 @@ import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; import { ClinicalTrialService } from '../../../clinical-trials/clinical-trial/clinical-trial.service'; import { SubstanceDetailsBaseTableDisplay } from '../../substance-products/substance-details-base-table-display'; import { PageEvent } from '@angular/material/paginator'; +import { FacetParam } from '@gsrs-core/facets-manager'; +import { Sort } from '@angular/material/sort'; +import { ActivatedRoute, Router, NavigationExtras } from '@angular/router'; +import { Subscription, Observable, Subject } from 'rxjs'; +import { MatDialog } from '@angular/material/dialog'; +import { AuthService } from '@gsrs-core/auth'; +import { LoadingService } from '@gsrs-core/loading/loading.service'; +import { ConfigService, LoadedComponents } from '@gsrs-core/config'; +import { ExportDialogComponent } from '@gsrs-core/substances-browse/export-dialog/export-dialog.component'; +import { take } from 'rxjs/operators'; +import * as _ from 'lodash'; +import { clinicalTrialSearchSortValues } from '../../../clinical-trials/clinical-trial-search-sort-values'; @Component({ selector: 'app-substance-clinical-trials-eu', @@ -12,43 +24,78 @@ import { PageEvent } from '@angular/material/paginator'; }) export class SubstanceClinicalTrialsEuropeComponent extends SubstanceDetailsBaseTableDisplay implements OnInit { - + private privateFacetParams: FacetParam; clinicalTrialEuCount = 0; showSpinner = false; + private subscriptions: Array = []; + + privateExport = false; + disableExport = false; + etag = ''; + etagAllExport = ''; + loadedComponents: LoadedComponents; + loadingStatus = ''; + public sortValues = clinicalTrialSearchSortValues; + order = '$root_trialNumber'; + ascDescDir = 'desc'; + + @Input() substanceUuid: string; @Output() countClinicalTrialEuOut: EventEmitter = new EventEmitter(); displayedColumns: string[] = [ - 'nctNumber', + 'trialNumber', 'title', 'sponsorName', - 'productName' + 'conditionsEU' ]; constructor( public gaService: GoogleAnalyticsService, - private clinicalTrialService: ClinicalTrialService + private clinicalTrialService: ClinicalTrialService, + private configService: ConfigService, + public authService: AuthService, + private loadingService: LoadingService, + private router: Router, + private dialog: MatDialog ) { super(gaService, clinicalTrialService); } ngOnInit() { - if (this.bdnum) { - this.getSubstanceClinicalTrials(); - this.clinicalTrialListExportUrl(); + this.loadedComponents = this.configService.configData.loadedComponents || null; + this.authService.hasAnyRolesAsync('Admin', 'Updater', 'SuperUpdater').pipe(take(1)).subscribe(response => { + this.isAdmin = response; + }); + if (this.substanceUuid) { + this.getSubstanceClinicalTrialsEurope(null, 'initial'); } } - getSubstanceClinicalTrials(pageEvent?: PageEvent): void { - this.setPageEvent(pageEvent); + ngOnDestroy() { + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + getSubstanceClinicalTrialsEurope(pageEvent?: PageEvent, searchType?: string): void { + this.setPageEvent(pageEvent); this.showSpinner = true; // Start progress spinner - this.clinicalTrialService.getSubstanceClinicalTrialsEurope(this.bdnum, this.page, this.pageSize).subscribe(results => { - this.setResultData(results); - this.clinicalTrialEuCount = this.totalRecords; - this.countClinicalTrialEuOut.emit(this.clinicalTrialEuCount); - this.showSpinner = false; // Stop progress spinner - }); + const subscriptionClinical = this.clinicalTrialService.getSubstanceClinicalTrialsEurope( + this.substanceUuid, + this.page, + this.pageSize, + this.order + ) + .subscribe(pagingResponse => { + if (searchType && searchType === 'initial') { + this.etagAllExport = pagingResponse['etag']; + } + this.setResultData(pagingResponse['content']); + this.clinicalTrialEuCount = pagingResponse['total']; + this.countClinicalTrialEuOut.emit(this.clinicalTrialEuCount); + this.showSpinner = false; // Stop progress spinner + }); /* this.searchControl.valueChanges.subscribe(value => { this.filterList(value, this.clinicaltrials, this.analyticsEventCategory); @@ -58,13 +105,91 @@ export class SubstanceClinicalTrialsEuropeComponent extends SubstanceDetailsBase this.countUpdate.emit(clinicaltrials.length); }); */ + this.subscriptions.push(subscriptionClinical); } - clinicalTrialListExportUrl() { - if (this.bdnum != null) { - this.exportUrl = this.clinicalTrialService.getClinicalTrialEuropeListExportUrl(this.bdnum); + + export() { + if (this.etagAllExport) { + const extension = 'cteu.xlsx'; + const url = this.getApiExportUrl(this.etagAllExport, extension); + if (this.authService.getUser() !== '') { + const dialogReference = this.dialog.open(ExportDialogComponent, { + // height: '215x', + width: '700px', + data: { 'extension': extension, 'type': 'substanceClinicalTrialEU', 'entity' : 'clinicaltrialseurope', 'hideOptionButtons': true } + }); + // this.overlayContainer.style.zIndex = '1002'; + dialogReference.afterClosed().subscribe(response => { + // this.overlayContainer.style.zIndex = null; + const name = response.name; + const id = response.id; + if (name && name !== '') { + this.loadingService.setLoading(true); + const fullname = name + '.' + extension; + this.authService.startUserDownload(url, this.privateExport, fullname, id).subscribe(response => { + // this.authService.startUserDownload(url, this.privateExport, fullname).subscribe(response => { + this.loadingService.setLoading(false); + const navigationExtras: NavigationExtras = { + queryParams: { + totalSub: this.clinicalTrialEuCount + } + }; + const params = { 'total': this.clinicalTrialEuCount }; + this.router.navigate(['/user-downloads/', response.id]); + }, error => this.loadingService.setLoading(false)); + } + }); + } } } -} + getApiExportUrl(etag: string, extension: string): string { + return this.clinicalTrialService.getApiEuropeExportUrl(etag, extension); + } + +// delete this? +// clinicalTrialListExportUrl() { +// if (this.substanceUuid != null) { +// this.exportUrl = this.clinicalTrialService.getClinicalTrialEuropeListExportUrl(this.substanceUuid); +// } +// } + + /* + // copied from products but has no effect. Make approaoch uniform in future. + tabSelected($event) { + if ($event) { + console.log("EVENT"); + const evt: any = $event.tab; + const textLabel: string = evt.textLabel; + if (textLabel != null) { + this.loadingStatus = 'Loading data...'; + this.paged = []; + this.getSubstanceClinicalTrials(); + } + + */ + joinMeddraTerms(cteu: any) { + if(cteu) { + // const a =[{"meddraTerm": "meddraTerm1"}, {"meddraTerm": "meddraTerm2"},{"meddraTerm": "meddraTerm3"},{"meddraTerm": "meddraTerm4"}]; + return _.map(cteu.clinicalTrialEuropeMeddraList, 'meddraTerm').join("|"); + } + } + + sortData(sort: Sort) { + if (sort.active) { + const orderIndex = this.displayedColumns.indexOf(sort.active).toString(); // + 2; // Adding 2, for name and bdnum. + this.ascDescDir = sort.direction; + this.sortValues.forEach(sortValue => { + if (sortValue.displayedColumns && sortValue.direction) { + if (this.displayedColumns[orderIndex] === sortValue.displayedColumns && this.ascDescDir === sortValue.direction) { + this.order = sortValue.value; + } + } + }); + this.getSubstanceClinicalTrialsEurope(); + } + return; + } +} \ No newline at end of file diff --git a/src/app/fda/substance-details/substance-products/substance-clinical-trials/substance-clinical-trials.component.html b/src/app/fda/substance-details/substance-products/substance-clinical-trials/substance-clinical-trials.component.html index 29a646396..c5a1e7671 100644 --- a/src/app/fda/substance-details/substance-products/substance-clinical-trials/substance-clinical-trials.component.html +++ b/src/app/fda/substance-details/substance-products/substance-clinical-trials/substance-clinical-trials.component.html @@ -1,56 +1,60 @@ -
    -
    - -
    Eudract NumberEudract Number - Title Title {{clinical.title}} Sponsor Name Sponsor Name {{clinical.sponsorName}} Product Name/Trade Name - Substances {{clinical.sponsorName}} Conditions{{joinMeddraTerms(clinical)}}
    - - +
    NCT Number
    + + + + -
    - {{clinical.nctNumber}} -
    (Europe) -
    - + + + - + - - + + - + @@ -66,4 +70,5 @@ - \ No newline at end of file + + diff --git a/src/app/fda/substance-details/substance-products/substance-clinical-trials/substance-clinical-trials.component.scss b/src/app/fda/substance-details/substance-products/substance-clinical-trials/substance-clinical-trials.component.scss index 8a819b756..b41f246bf 100644 --- a/src/app/fda/substance-details/substance-products/substance-clinical-trials/substance-clinical-trials.component.scss +++ b/src/app/fda/substance-details/substance-products/substance-clinical-trials/substance-clinical-trials.component.scss @@ -1,3 +1,17 @@ + +.clinical-trials-browse-edit1 { + text-decoration: none; +} + +.clinical-trials-browse-edit2 { + text-decoration: none; +} +.clinical-trials-browse-edit1, +.clinical-trials-browse-edit2 { + padding-left: .5em; + padding-right: .5em; +} + .mat-table-style { td.mat-cell { vertical-align: top; @@ -14,7 +28,7 @@ } .colorgray { - color: gray; + color: var(--regular-grey-color); } .padleft25px { @@ -26,16 +40,16 @@ } .colorblue { - color: #4b4572; + color: var(--deep-purple-color); } .colorred { - color: red; + color: var(--regular-red-color); } .btn1 { - color : rgb(49, 46, 46); - background-color: rgba(255, 222, 121, 0.96); + color : var(--maroon-color); + background-color: var(--light-yellow-color-3); border-radius: 10px 0 0 10px; font-size: 16px; } @@ -46,16 +60,9 @@ left: 0; bottom: 56px; right: 0; - background: rgba(150, 148, 148, 0.15); + background: var(--spinner-bg-color); z-index: 1; display: flex; align-items: top; /* Verticle align */ justify-content: center; /* horizonal align */ } - -/* -.mat-raised-button.mat-primary { - line-height: 25px; - padding: 2px 8px; -} -*/ \ No newline at end of file diff --git a/src/app/fda/substance-details/substance-products/substance-clinical-trials/substance-clinical-trials.component.ts b/src/app/fda/substance-details/substance-products/substance-clinical-trials/substance-clinical-trials.component.ts index b883328d6..32ad6a8ff 100644 --- a/src/app/fda/substance-details/substance-products/substance-clinical-trials/substance-clinical-trials.component.ts +++ b/src/app/fda/substance-details/substance-products/substance-clinical-trials/substance-clinical-trials.component.ts @@ -4,6 +4,17 @@ import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; import { ClinicalTrialService } from '../../../clinical-trials/clinical-trial/clinical-trial.service'; import { SubstanceDetailsBaseTableDisplay } from '../../substance-products/substance-details-base-table-display'; import { PageEvent } from '@angular/material/paginator'; +import { FacetParam } from '@gsrs-core/facets-manager'; +import { Sort } from '@angular/material/sort'; +import { ActivatedRoute, Router, NavigationExtras } from '@angular/router'; +import { Subscription, Observable, Subject } from 'rxjs'; +import { MatDialog } from '@angular/material/dialog'; +import { AuthService } from '@gsrs-core/auth'; +import { LoadingService } from '@gsrs-core/loading/loading.service'; +import { ConfigService, LoadedComponents } from '@gsrs-core/config'; +import { ExportDialogComponent } from '@gsrs-core/substances-browse/export-dialog/export-dialog.component'; +import { take } from 'rxjs/operators'; +import { clinicalTrialSearchSortValues } from '../../../clinical-trials/clinical-trial-search-sort-values'; @Component({ selector: 'app-substance-clinical-trials', @@ -13,42 +24,91 @@ import { PageEvent } from '@angular/material/paginator'; export class SubstanceClinicalTrialsComponent extends SubstanceDetailsBaseTableDisplay implements OnInit { + private privateFacetParams: FacetParam; clinicalTrialCount = 0; showSpinner = false; + private subscriptions: Array = []; + + privateExport = false; + disableExport = false; + etag = ''; + etagAllExport = ''; + loadedComponents: LoadedComponents; + loadingStatus = ''; + public sortValues = clinicalTrialSearchSortValues; + order = '$root_trialNumber'; + ascDescDir = 'desc'; + + @Input() substanceUuid: string; @Output() countClinicalTrialOut: EventEmitter = new EventEmitter(); displayedColumns: string[] = [ - 'nctNumber', + 'edit', + 'trialNumber', 'title', - 'sponsorName', + 'sponsor', 'conditions', 'outcomemeasures' ]; constructor( public gaService: GoogleAnalyticsService, - private clinicalTrialService: ClinicalTrialService + private clinicalTrialService: ClinicalTrialService, + private configService: ConfigService, + public authService: AuthService, + private loadingService: LoadingService, + private router: Router, + private dialog: MatDialog ) { super(gaService, clinicalTrialService); } ngOnInit() { - if (this.bdnum) { - this.getSubstanceClinicalTrials(); - this.clinicalTrialListExportUrl(); + this.loadedComponents = this.configService.configData.loadedComponents || null; + this.authService.hasAnyRolesAsync('Admin', 'Updater', 'SuperUpdater').pipe(take(1)).subscribe(response => { + this.isAdmin = response; + }); + if (this.substanceUuid) { + this.getSubstanceClinicalTrials(null, 'initial'); } } - getSubstanceClinicalTrials(pageEvent?: PageEvent): void { - this.setPageEvent(pageEvent); + ngOnDestroy() { + this.subscriptions.forEach(subscription => { + subscription.unsubscribe(); + }); + } + getSubstanceClinicalTrials(pageEvent?: PageEvent, searchType?: string): void { + this.setPageEvent(pageEvent); + const skip = this.page * this.pageSize; this.showSpinner = true; // Start progress spinner - this.clinicalTrialService.getSubstanceClinicalTrials(this.bdnum, this.page, this.pageSize).subscribe(results => { - this.setResultData(results); - this.clinicalTrialCount = this.totalRecords; - this.countClinicalTrialOut.emit(this.clinicalTrialCount); - this.showSpinner = false; // Stop progress spinner - }); + const subscriptionClinical = this.clinicalTrialService.getClinicalTrials({ + searchTerm: this.substanceUuid, + cutoff: null, + type: "substanceKey", + order: this.order, + pageSize: this.pageSize, + facets: this.privateFacetParams, + skip: skip + }) + .subscribe(pagingResponse => { + if (searchType && searchType === 'initial') { + this.etagAllExport = pagingResponse.etag; + } + // AW removed else clause so this runs every time. + // This makes it work, but AW might need to understand the + // intention better. + // else { + this.clinicalTrialService.totalRecords = pagingResponse.total; + this.setResultData(pagingResponse.content); + this.clinicalTrialCount = pagingResponse.total; + this.etag = pagingResponse.etag; + // } + + this.countClinicalTrialOut.emit(this.clinicalTrialCount); + this.showSpinner = false; // Stop progress spinner + }); /* this.searchControl.valueChanges.subscribe(value => { @@ -59,12 +119,88 @@ export class SubstanceClinicalTrialsComponent extends SubstanceDetailsBaseTableD this.countUpdate.emit(clinicaltrials.length); }); */ + this.subscriptions.push(subscriptionClinical); + } + + + export() { + if (this.etagAllExport) { + const extension = 'ctus.xlsx'; + const url = this.getApiExportUrl(this.etagAllExport, extension); + if (this.authService.getUser() !== '') { + const dialogReference = this.dialog.open(ExportDialogComponent, { + // height: '215x', + width: '700px', + data: { 'extension': extension, 'type': 'substanceClinicalTrialUS', 'entity': 'clinicaltrialsus', 'hideOptionButtons': true } + }); + // this.overlayContainer.style.zIndex = '1002'; + dialogReference.afterClosed().subscribe(response => { + // this.overlayContainer.style.zIndex = null; + const name = response.name; + const id = response.id; + if (name && name !== '') { + this.loadingService.setLoading(true); + const fullname = name + '.' + extension; + this.authService.startUserDownload(url, this.privateExport, fullname, id).subscribe(response => { + // this.authService.startUserDownload(url, this.privateExport, fullname).subscribe(response => { + this.loadingService.setLoading(false); + const navigationExtras: NavigationExtras = { + queryParams: { + totalSub: this.clinicalTrialCount + } + }; + const params = { 'total': this.clinicalTrialCount }; + this.router.navigate(['/user-downloads/', response.id]); + }, error => this.loadingService.setLoading(false)); + } + }); + } + } + } + + getApiExportUrl(etag: string, extension: string): string { + return this.clinicalTrialService.getApiExportUrl(etag, extension); } + + clinicalTrialListExportUrl() { - if (this.bdnum != null) { - this.exportUrl = this.clinicalTrialService.getClinicalTrialListExportUrl(this.bdnum); + if (this.substanceUuid != null) { + this.exportUrl = this.clinicalTrialService.getClinicalTrialListExportUrl(this.substanceUuid); + } + } + + sortData(sort: Sort) { + if (sort.active) { + const orderIndex = this.displayedColumns.indexOf(sort.active).toString(); // + 2; // Adding 2, for name and bdnum. + this.ascDescDir = sort.direction; + this.sortValues.forEach(sortValue => { + if (sortValue.displayedColumns && sortValue.direction) { + if (this.displayedColumns[orderIndex] === sortValue.displayedColumns && this.ascDescDir === sortValue.direction) { + this.order = sortValue.value; + } + } + }); + this.getSubstanceClinicalTrials(); + } + return; + } + + /* + // copied from products but has no effect. Make approaoch uniform in future. + tabSelected($event) { + if ($event) { + console.log("EVENT"); + const evt: any = $event.tab; + const textLabel: string = evt.textLabel; + if (textLabel != null) { + this.loadingStatus = 'Loading data...'; + this.paged = []; + this.getSubstanceClinicalTrials(); + } + } } +*/ } diff --git a/src/app/fda/substance-details/substance-products/substance-details-base-table-display.ts b/src/app/fda/substance-details/substance-products/substance-details-base-table-display.ts index d41ad14c2..426c756b8 100644 --- a/src/app/fda/substance-details/substance-products/substance-details-base-table-display.ts +++ b/src/app/fda/substance-details/substance-products/substance-details-base-table-display.ts @@ -1,9 +1,10 @@ -import { Component, OnInit, Input } from '@angular/core'; +import { Component, OnInit, Input, Injectable } from '@angular/core'; import { PageEvent } from '@angular/material/paginator'; import { FormControl } from '@angular/forms'; import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; import { SubstanceCardBaseFilteredList } from '@gsrs-core/substance-details'; +@Injectable() export class SubstanceDetailsBaseTableDisplay extends SubstanceCardBaseFilteredList implements OnInit { totalRecords: 0; diff --git a/src/app/fda/substance-details/substance-products/substance-impurities/substance-impurities.component.html b/src/app/fda/substance-details/substance-products/substance-impurities/substance-impurities.component.html index 5cca56d55..14aa8b064 100644 --- a/src/app/fda/substance-details/substance-products/substance-impurities/substance-impurities.component.html +++ b/src/app/fda/substance-details/substance-products/substance-impurities/substance-impurities.component.html @@ -1,74 +1,108 @@
    - Impurities Specs -     - - - + Impurities Specs +     + + + + + +
    + +
    + +
    + +
    - -
    - -
    - - -
    - -
    + + + + Trial Number + + {{clinical.trialNumber}} Title Title {{clinical.title}} Sponsor Name Sponsor Name {{clinical.sponsor}} Conditions Conditions {{clinical.conditions}}
    - - - - - - - - - +
    Details -
    - View -    - - - -
    -
    Source Type {{impurities.sourceType}}
    - - - - + + + + - - - - - - - - - + + + + - - - - + + + + - + + + + - - -
    Source {{impurities.source}} Product/Substance Name + + Source ID {{impurities.sourceId}} Type {{impurities.type}} Source Type {{impurities.sourceType}} Spec Type {{impurities.specType}} Source {{impurities.source}} Source ID {{impurities.sourceId}}
    -
    - - - \ No newline at end of file + +
    Type {{impurities.type}} Spec Type {{impurities.specType}} Parent Substance + + Impurities Related Substance +
    + +
    +
    +
    + +     {{impDetail.limitType}}  + {{impDetail.limitValue}} {{impDetail.unit}} + +
        {{impDetail.impurityType}} +
    +
    +
    +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/src/app/fda/substance-details/substance-products/substance-impurities/substance-impurities.component.scss b/src/app/fda/substance-details/substance-products/substance-impurities/substance-impurities.component.scss index ab457f2a4..67355d5b3 100644 --- a/src/app/fda/substance-details/substance-products/substance-impurities/substance-impurities.component.scss +++ b/src/app/fda/substance-details/substance-products/substance-impurities/substance-impurities.component.scss @@ -5,20 +5,40 @@ } } +.bordergray { + border:1px solid var(--regular-grey-color); +} + .font10px { font-size: 10px; } +.font12px { + font-size: 12px; +} + .font15px { font-size: 15px; } .colorgray { - color: gray; + color: var(--regular-grey-color); } .colorred { - color: red; + color: var(--regular-red-color); +} + +.colorgreen { + color: var(--regular-green-color); +} + +.colorpurple { + color: var(--regular-purple-color); +} + +.colorblue { + color: var(--deep-purple-color); } .padleft25px { @@ -29,17 +49,26 @@ padding-top: 5px; } -.colorblue { - color: #4b4572; +.marginbottom10px { + margin-bottom: 10px; } +.width100px { + width: 100px; +} + +ol, li { + margin-left:0px; + padding-left:0px +}​ + .spinnerstyle { position: absolute; top: 0; left: 0; bottom: 56px; right: 0; - background: rgba(150, 148, 148, 0.15); + background: var(--spinner-bg-color); z-index: 1; display: flex; align-items: top; /* Verticle align */ diff --git a/src/app/fda/substance-details/substance-products/substance-impurities/substance-impurities.component.ts b/src/app/fda/substance-details/substance-products/substance-impurities/substance-impurities.component.ts index 2ef7039c8..1edaafede 100644 --- a/src/app/fda/substance-details/substance-products/substance-impurities/substance-impurities.component.ts +++ b/src/app/fda/substance-details/substance-products/substance-impurities/substance-impurities.component.ts @@ -1,14 +1,23 @@ import { Component, OnInit, OnDestroy, Input, Output, EventEmitter } from '@angular/core'; -import { SubstanceCardBaseFilteredList } from '@gsrs-core/substance-details'; +import { ActivatedRoute, Router, NavigationExtras } from '@angular/router'; +import { PageEvent } from '@angular/material/paginator'; +import { MatDialog } from '@angular/material/dialog'; +import { Sort } from '@angular/material/sort'; +import { Subscription } from 'rxjs'; +import { take } from 'rxjs/operators'; +import { ConfigService } from '@gsrs-core/config'; +import { LoadingService } from '@gsrs-core/loading/loading.service'; +import { AuthService } from '@gsrs-core/auth'; import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; import { ImpuritiesService } from '../../../impurities/service/impurities.service'; +import { GeneralService } from '../../../service/general.service'; +import { SubstanceCardBaseFilteredList } from '@gsrs-core/substance-details'; import { SubstanceDetailsBaseTableDisplay } from '../substance-details-base-table-display'; import { Impurities, ImpuritiesTesting, ImpuritiesDetails, IdentityCriteria } from '../../../impurities/model/impurities.model'; -import { ConfigService } from '@gsrs-core/config'; -import { AuthService } from '@gsrs-core/auth'; -import { take } from 'rxjs/operators'; -import { PageEvent } from '@angular/material/paginator'; -import { Subscription } from 'rxjs'; +import { Facet } from '@gsrs-core/facets-manager'; +import { FacetParam, FacetHttpParams, FacetQueryResponse } from '@gsrs-core/facets-manager'; +import { ExportDialogComponent } from '@gsrs-core/substances-browse/export-dialog/export-dialog.component'; +import { impuritiesSearchSortValues } from '../../../impurities/impurities-search-sort-values'; @Component({ selector: 'app-substance-impurities', @@ -18,25 +27,45 @@ import { Subscription } from 'rxjs'; export class SubstanceImpuritiesComponent extends SubstanceDetailsBaseTableDisplay implements OnInit, OnDestroy { @Input() substanceUuid: string; + @Input() substanceName: string; @Output() countImpuritiesOut: EventEmitter = new EventEmitter(); private subscriptions: Array = []; + parentSubstance: string; + parentSubstanceUuid: string; showSpinner = false; - impurities: any; + impurities: Array; + totalImpurities = 0; impuritiesCount = 0; impuritiesTestTotal = 0; + pageIndex = 0; + pageSize = 5; + public privateSearchTerm?: string; + private privateFacetParams: FacetParam; + privateExport = false; + disableExport = false; + etag = ''; + public sortValues = impuritiesSearchSortValues; + order = '$root_productSubstanceName'; + ascDescDir = 'desc'; displayedColumns: string[] = [ - 'details', + 'productSubstanceName', 'sourceType', 'source', - 'sourceid', + 'sourceId', 'type', - 'specType' + 'specType', + 'parentSubstance', + 'relatedSubstance' ]; constructor( + private router: Router, public gaService: GoogleAnalyticsService, private impuritiesService: ImpuritiesService, - private authService: AuthService + private generalService: GeneralService, + private authService: AuthService, + private loadingService: LoadingService, + private dialog: MatDialog ) { super(gaService, impuritiesService); } @@ -48,7 +77,7 @@ export class SubstanceImpuritiesComponent extends SubstanceDetailsBaseTableDispl this.subscriptions.push(rolesSubscription); if (this.substanceUuid) { - this.getSubstanceImpurities(); + this.getImpuritiesBySubstanceUuid(); this.impuritiesListExportUrl(); } } @@ -61,6 +90,184 @@ export class SubstanceImpuritiesComponent extends SubstanceDetailsBaseTableDispl }); } + /* + getSubstanceImpuritiesNEW(pageEvent?: PageEvent): void { + this.setPageEvent(pageEvent); + + this.showSpinner = true; // Start progress spinner + this.impuritiesService.searchImpurities(this.substanceUuid, this.page, this.pageSize).subscribe(results => { + this.setResultData(results); + this.impurities = results; + this.getImpuritiesTestTotal(); + this.impuritiesCount = this.totalRecords; + this.countImpuritiesOut.emit(this.impuritiesCount); + this.showSpinner = false; // Stop progress spinner + }); + } + */ + + searchImpurities() { + this.privateSearchTerm = this.substanceUuid; + // this.loadingService.setLoading(true); + const skip = this.pageIndex * this.pageSize; + const subscription = this.impuritiesService.searchImpurities( + skip, + this.pageSize, + this.privateSearchTerm, + this.privateFacetParams, + ) + .subscribe(pagingResponse => { + // this.isError = false; + + this.setResultData(pagingResponse.content); + this.impurities = pagingResponse.content; + this.impuritiesCount = pagingResponse.total; + this.countImpuritiesOut.emit(this.impuritiesCount); + this.etag = pagingResponse.etag; + + /* + if (pagingResponse.total % this.pageSize === 0) { + this.lastPage = (pagingResponse.total / this.pageSize); + } else { + this.lastPage = Math.floor(pagingResponse.total / this.pageSize + 1); + } + */ + // Set Facets from paging response + /* if (pagingResponse.facets && pagingResponse.facets.length > 0) { + this.rawFacets = pagingResponse.facets; + } + */ + }, error => { + /* + console.log('error'); + const notification: AppNotification = { + message: 'There was an error trying to retrieve Products. Please refresh and try again.', + type: NotificationType.error, + milisecondsToShow: 6000 + }; + this.isError = true; + this.isLoading = false; + this.loadingService.setLoading(this.isLoading); + this.notificationService.setNotification(notification); + */ + }, () => { + subscription.unsubscribe(); + // this.isLoading = false; + // this.loadingService.setLoading(this.isLoading); + }); + } + + + getImpuritiesBySubstanceUuid(pageEvent?: PageEvent): void { + this.showSpinner = true; // Start progress spinner + + this.setPageEvent(pageEvent); + const skip = this.page * this.pageSize; + + // , this.page, this.pageSize + this.impuritiesService.getImpuritiesBySubstanceUuid( + this.order, + skip, + this.pageSize, + this.substanceUuid, + this.privateFacetParams).subscribe(results => { + this.impuritiesService.totalRecords = results.total; + this.impurities = results.content; + + // Load Impurities Test Details by Substance Uuid + this.loadImpuritiesTestDetails(); + + this.setResultData(this.impurities); + + this.totalImpurities = results.total; + + this.etag = results.etag; + this.countImpuritiesOut.emit(this.totalImpurities); + }); + this.showSpinner = false; // Stop progress spinner + } + + /* + getImpuritiesByTestImpuritiesDetails(pageEvent?: PageEvent): void { + this.setPageEvent(pageEvent); + + this.showSpinner = true; // Start progress spinner + // , this.page, this.pageSize + this.impuritiesService.getImpuritiesBySubstanceUuid(this.substanceUuid).subscribe(results => { + this.impuritiesService.totalRecords = results.total; + this.impurities = results.content; + + //Load Impurities Test Details by Substance Uuid + this.loadImpuritiesTestDetails(); + + this.setResultData(this.impurities); + + this.totalImpurities = results.total; + + this.etag = results.etag; + this.countImpuritiesOut.emit(this.totalImpurities); + }); + this.showSpinner = false; // Stop progress spinner + } + */ + + loadImpuritiesTestDetails() { + this.impurities.forEach((element, index) => { + element.impuritiesSubstanceList.forEach((elementSub, indexSub) => { + + if (elementSub.substanceUuid) { + // if current Substance is same as Parent Substance of Impurities + if (elementSub.substanceUuid === this.substanceUuid) { + elementSub._parentSubstanceName = this.substanceName; + elementSub._parentSubstanceUuid = elementSub.substanceUuid; + } + } + elementSub.impuritiesTestList.forEach((elementTest, indexTest) => { + + elementTest.impuritiesDetailsList.forEach((elementDetail, indexDetail) => { + + if (elementDetail.relatedSubstanceUuid != null) { + // if current Substance is same as Impurities Details of Impurities + if (elementDetail.relatedSubstanceUuid === this.substanceUuid) { + const subSubscription = this.generalService.getSubstanceNamesBySubstanceUuid(elementSub.substanceUuid).subscribe(substanceNames => { + let subNames = substanceNames; + + // Get Preferred Term or DisplayName == true + subNames.forEach((names, index) => { + if (names.displayName === true) { + elementSub._parentSubstanceName = names.name; + elementSub._parentSubstanceUuid = elementSub.substanceUuid; + } + }); + }); + this.subscriptions.push(subSubscription); + } + } + }); // Impurities Details forEach + }); // Test forEach + }); // Substance forEach + }); // Impurities forEach + } + + getSubstanceNames(substanceUuid: string): string { + let preferredTerm; + if (substanceUuid) { + const subSubscription = this.generalService.getSubstanceNamesBySubstanceUuid(substanceUuid).subscribe(substanceNames => { + let subNames = substanceNames; + + // Get Preferred Term or DisplayName == true + subNames.forEach((names, index) => { + if (names.displayName === true) { + preferredTerm = names.name; + } + }); + }); + this.subscriptions.push(subSubscription); + return preferredTerm; + } + } + + /* getSubstanceImpurities(pageEvent?: PageEvent): void { this.setPageEvent(pageEvent); @@ -68,16 +275,51 @@ export class SubstanceImpuritiesComponent extends SubstanceDetailsBaseTableDispl this.impuritiesService.getSubstanceImpurities(this.substanceUuid, this.page, this.pageSize).subscribe(results => { this.setResultData(results); this.impurities = results; - this.getImpuritiesTestTotal(); this.impuritiesCount = this.totalRecords; + this.impuritiesService.totalRecords = this.totalRecords; this.countImpuritiesOut.emit(this.impuritiesCount); this.showSpinner = false; // Stop progress spinner }); } + */ + + export() { + if (this.etag) { + const extension = 'xlsx'; + const url = this.getApiExportUrl(this.etag, extension); + if (this.authService.getUser() !== '') { + const dialogReference = this.dialog.open(ExportDialogComponent, { + // height: '215x', + width: '700px', + data: { 'extension': extension, 'type': 'substanceImpurities', 'entity': 'impurities', 'hideOptionButtons': true } + }); + // this.overlayContainer.style.zIndex = '1002'; + dialogReference.afterClosed().subscribe(response => { + // this.overlayContainer.style.zIndex = null; + const name = response.name; + const id = response.id; + if (name && name !== '') { + this.loadingService.setLoading(true); + const fullname = name + '.' + extension; + this.authService.startUserDownload(url, this.privateExport, fullname, id).subscribe(response => { + // this.authService.startUserDownload(url, this.privateExport, fullname).subscribe(response => { + this.loadingService.setLoading(false); + const navigationExtras: NavigationExtras = { + queryParams: { + totalSub: this.totalImpurities + } + }; + const params = { 'total': this.totalImpurities }; + this.router.navigate(['/user-downloads/', response.id]); + }, error => this.loadingService.setLoading(false)); + } + }); + } + } + } - getImpuritiesTestTotal(): void { - // Get Impurities Test Count - // if (Object.keys(this.impurities).length > 0) {} + getApiExportUrl(etag: string, extension: string): string { + return this.impuritiesService.getApiExportUrl(etag, extension); } impuritiesListExportUrl() { @@ -86,4 +328,19 @@ export class SubstanceImpuritiesComponent extends SubstanceDetailsBaseTableDispl } } + sortData(sort: Sort) { + if (sort.active) { + const orderIndex = this.displayedColumns.indexOf(sort.active).toString(); // + 2; // Adding 2, for name and bdnum. + this.ascDescDir = sort.direction; + this.sortValues.forEach(sortValue => { + if (sortValue.displayedColumns && sortValue.direction) { + if (this.displayedColumns[orderIndex] === sortValue.displayedColumns && this.ascDescDir === sortValue.direction) { + this.order = sortValue.value; + } + } + }); + this.getImpuritiesBySubstanceUuid(); + } + return; + } } diff --git a/src/app/fda/substance-details/substance-products/substance-products.component.html b/src/app/fda/substance-details/substance-products/substance-products.component.html index c218083dd..14111209b 100644 --- a/src/app/fda/substance-details/substance-products/substance-products.component.html +++ b/src/app/fda/substance-details/substance-products/substance-products.component.html @@ -1,50 +1,59 @@
    - - +

    No Product Found

    - - -
    +
    {{prov}}     {{loadingStatus}}     + + + + + +
    -
    - +
    - + - + + - - + + - - + + - + - + - - - - - - - + + - + @@ -121,116 +145,58 @@ - - - - - - + -
    +
    - +
    - - + + + - - + + - + - + - - + + - + + + + + +
    \ No newline at end of file diff --git a/src/app/fda/substance-details/substance-products/substance-products.component.scss b/src/app/fda/substance-details/substance-products/substance-products.component.scss index 67f334e41..938911da9 100644 --- a/src/app/fda/substance-details/substance-products/substance-products.component.scss +++ b/src/app/fda/substance-details/substance-products/substance-products.component.scss @@ -8,13 +8,13 @@ .mat-tab-style { .mat-tab-labels, .mat-tab-label, .mat-tab-link { - color: blue; + color: var(--regular-blue-color); } } .bordergray { - border: 1px solid gray; + border: 1px solid var(--regular-grey-color); } .font10px { @@ -26,7 +26,7 @@ } .colorgray { - color: gray; + color: var(--regular-grey-color); } .padleft15px { @@ -42,18 +42,18 @@ } .colorblue { - color: #4b4572; + color: var(--deep-purple-color); } .colorred { - color: red; + color: var(--regular-red-color); } .info-container { margin-bottom: 15px; a { - color: rgb(0, 0, 255);; + color: var(--deep-blue-color-rgb-2); text-decoration: none; } } @@ -64,16 +64,17 @@ left: 0; bottom: 56px; right: 0; - background: rgba(150, 148, 148, 0.15); + background: var(--spinner-bg-color); z-index: 1; display: flex; align-items: top; /* Verticle align */ justify-content: center; /* horizonal align */ } -/* -.mat-tab-group.mat-primary .mat-ink-bar { - background: white; - height: 5px; +.divflex { + display: flex; +} + +.middle-fill { + flex: 1 1 auto; } -*/ diff --git a/src/app/fda/substance-details/substance-products/substance-products.component.ts b/src/app/fda/substance-details/substance-products/substance-products.component.ts index 08252a79a..2e4d4371b 100644 --- a/src/app/fda/substance-details/substance-products/substance-products.component.ts +++ b/src/app/fda/substance-details/substance-products/substance-products.component.ts @@ -1,14 +1,23 @@ import { Component, OnInit, AfterViewInit, ViewChild, ViewEncapsulation } from '@angular/core'; +import { ActivatedRoute, Router, NavigationExtras } from '@angular/router'; +import { MatDialog } from '@angular/material/dialog'; import { SubstanceCardBaseFilteredList } from '@gsrs-core/substance-details'; import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; import { ProductService } from '../../product/service/product.service'; -import { MatTableDataSource, MatPaginator, MatSort } from '@angular/material'; +import { MatTableDataSource } from '@angular/material/table'; +import { Sort } from '@angular/material/sort'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort } from '@angular/material/sort'; import { PageEvent } from '@angular/material/paginator'; import { SubstanceDetailsBaseTableDisplay } from './substance-details-base-table-display'; import { SubstanceAdverseEventCvmComponent } from './substance-adverseevent/adverseeventcvm/substance-adverseeventcvm.component'; -import { ConfigService } from '@gsrs-core/config'; +import { ConfigService, LoadedComponents } from '@gsrs-core/config'; import { AuthService } from '@gsrs-core/auth'; import { take } from 'rxjs/operators'; +import { FacetParam } from '@gsrs-core/facets-manager'; +import { LoadingService } from '@gsrs-core/loading/loading.service'; +import { ExportDialogComponent } from '@gsrs-core/substances-browse/export-dialog/export-dialog.component'; +import { productSearchSortValues } from '../../product/products-browse/product-search-sort-values'; @Component({ selector: 'app-substance-products', @@ -26,6 +35,7 @@ export class SubstanceProductsComponent extends SubstanceDetailsBaseTableDisplay advDmeCount = 0; advCvmCount = 0; impuritiesCount = 0; + ssg4mCount = 0; provenance = ''; provenanceList = ''; datasourceList = ''; @@ -35,40 +45,66 @@ export class SubstanceProductsComponent extends SubstanceDetailsBaseTableDisplay foundProvenanceList = false; loadingComplete = false; substanceName = ''; - + public privateSearch?: string; + private privateFacetParams: FacetParam; + public privateSearchTerm?: string; + privateExport = false; + disableExport = false; + etag = ''; + etagAllExport = ''; + loadedComponents: LoadedComponents; + public sortValues = productSearchSortValues; + order = '$root_productNDC'; + ascDescDir = 'desc'; public displayedColumns: string[] = [ 'productNDC', - // 'name', 'productName', 'labelerName', 'country', 'status', 'productNameType', 'ingredientType', - 'activeMoiety', - 'applicationNumber', + 'applicationNumber' ]; constructor( + private router: Router, public gaService: GoogleAnalyticsService, private productService: ProductService, private configService: ConfigService, - public authService: AuthService + public authService: AuthService, + private loadingService: LoadingService, + private dialog: MatDialog ) { super(gaService, productService); } ngOnInit() { - + this.loadedComponents = this.configService.configData.loadedComponents || null; this.authService.hasAnyRolesAsync('Admin', 'Updater', 'SuperUpdater').pipe(take(1)).subscribe(response => { this.isAdmin = response; }); if (this.substance && this.substance.uuid) { - this.getBdnum(); + this.getSubstanceKey(); + // Get Provenance List to Display in Tab this.getProductProvenanceList(); - this.productListExportUrl(); + + this.privateSearch = 'root_productIngredientAllList_substanceUuid:\"' + this.substance.uuid + '"'; + this.getSubstanceProducts(null, 'initial'); + + /* + this.searchControl.valueChanges.subscribe(value => { + if (value) { + this.privateSearch = '\"' + value + '\" AND ' + 'root_productIngredientAllList_substanceUuid:\"' + + this.substance.uuid + '\" AND root_provenance:' + this.provenance; + this.getSubstanceProducts(null); + } + }, error => { + console.log(error); + }); + */ } this.baseDomain = this.configService.configData.apiUrlDomain; @@ -105,7 +141,11 @@ export class SubstanceProductsComponent extends SubstanceDetailsBaseTableDisplay this.impuritiesCount = $event; } - getBdnum() { + getSsg4mCount($event: any) { + this.ssg4mCount = $event; + } + + getSubstanceKey() { if (this.substance) { // Get Substance Name this.substanceName = this.substance._name; @@ -123,31 +163,84 @@ export class SubstanceProductsComponent extends SubstanceDetailsBaseTableDisplay getProductProvenanceList(): void { this.productService.getProductProvenanceList(this.substance.uuid).subscribe(results => { - this.provenanceList = results.provenanceList; - if (this.provenanceList.length > 0) { + this.provenanceList = results; + if (this.provenanceList && this.provenanceList.length > 0) { this.foundProvenanceList = true; } this.loadingComplete = true; }); } - getSubstanceProducts(pageEvent?: PageEvent): void { + getSubstanceProducts(pageEvent?: PageEvent, searchType?: string) { this.setPageEvent(pageEvent); this.showSpinner = true; // Start progress spinner - this.productService.getSubstanceProducts(this.substance.uuid, this.provenance, this.page, this.pageSize).subscribe(results => { - this.setResultData(results); - this.productCount = this.totalRecords; - this.loadingStatus = ''; + const skip = this.page * this.pageSize; + + const subscription = this.productService.getProducts( + this.order, + skip, + this.pageSize, + this.privateSearch, + this.privateFacetParams + ).subscribe(pagingResponse => { + if (searchType && searchType === 'initial') { + this.etagAllExport = pagingResponse.etag; + } else { + this.productService.totalRecords = pagingResponse.total; + this.setResultData(pagingResponse.content); + this.productCount = pagingResponse.total; + this.etag = pagingResponse.etag; + } + }, error => { this.showSpinner = false; // Stop progress spinner + console.log('error'); + }, () => { + this.showSpinner = false; // Stop progress spinner + subscription.unsubscribe(); }); + this.loadingStatus = ''; + // this.showSpinner = false; // Stop progress spinner } - productListExportUrl() { - if (this.substance && this.substance.uuid) { - this.exportUrl = this.productService.getProductListExportUrl(this.substance.uuid); + export() { + if (this.etagAllExport) { + const extension = 'xlsx'; + const url = this.getApiExportUrl(this.etagAllExport, extension); + if (this.authService.getUser() !== '') { + const dialogReference = this.dialog.open(ExportDialogComponent, { + // height: '215x', + width: '700px', + data: { 'extension': extension, 'type': 'substanceProduct', 'entity': 'products', 'hideOptionButtons': true } + }); + // this.overlayContainer.style.zIndex = '1002'; + dialogReference.afterClosed().subscribe(response => { + // this.overlayContainer.style.zIndex = null; + const name = response.name; + const id = response.id; + if (name && name !== '') { + this.loadingService.setLoading(true); + const fullname = name + '.' + extension; + this.authService.startUserDownload(url, this.privateExport, fullname, id).subscribe(response => { + // this.authService.startUserDownload(url, this.privateExport, fullname).subscribe(response => { + this.loadingService.setLoading(false); + const navigationExtras: NavigationExtras = { + queryParams: { + totalSub: this.productCount + } + }; + const params = { 'total': this.productCount }; + this.router.navigate(['/user-downloads/', response.id]); + }, error => this.loadingService.setLoading(false)); + } + }); + } } } + getApiExportUrl(etag: string, extension: string): string { + return this.productService.getApiExportUrl(etag, extension); + } + tabSelected($event) { if ($event) { const evt: any = $event.tab; @@ -162,6 +255,9 @@ export class SubstanceProductsComponent extends SubstanceDetailsBaseTableDisplay // set the current result data to empty or null. this.paged = []; + this.privateSearch = 'root_productIngredientAllList_substanceUuid:\"' + + this.substance.uuid + '\" AND root_provenance:' + this.provenance; + this.getSubstanceProducts(); } @@ -169,5 +265,20 @@ export class SubstanceProductsComponent extends SubstanceDetailsBaseTableDisplay } } + sortData(sort: Sort) { + if (sort.active) { + const orderIndex = this.displayedColumns.indexOf(sort.active).toString(); // + 2; // Adding 2, for name and bdnum. + this.ascDescDir = sort.direction; + this.sortValues.forEach(sortValue => { + if (sortValue.displayedColumns && sortValue.direction) { + if (this.displayedColumns[orderIndex] === sortValue.displayedColumns && this.ascDescDir === sortValue.direction) { + this.order = sortValue.value; + } + } + }); + this.getSubstanceProducts(); + } + return; + } } diff --git a/src/app/fda/substance-details/substance-products/substance-products.module.ts b/src/app/fda/substance-details/substance-products/substance-products.module.ts index 90167e849..d86ae912d 100644 --- a/src/app/fda/substance-details/substance-products/substance-products.module.ts +++ b/src/app/fda/substance-details/substance-products/substance-products.module.ts @@ -20,6 +20,8 @@ import { SubstanceAdverseEventDmeComponent } from './substance-adverseevent/adve import { SubstanceAdverseEventCvmComponent } from './substance-adverseevent/adverseeventcvm/substance-adverseeventcvm.component'; import { SubstanceClinicalTrialsEuropeComponent } from './substance-clinical-trials-eu/substance-clinical-trials-eu.component'; import { SubstanceImpuritiesComponent } from './substance-impurities/substance-impurities.component'; +import { SubstanceSsg4mComponent } from './substance-ssg4m/substance-ssg4m.component'; + @NgModule({ imports: [ CommonModule, @@ -46,6 +48,7 @@ import { SubstanceImpuritiesComponent } from './substance-impurities/substance-i SubstanceAdverseEventCvmComponent, SubstanceClinicalTrialsEuropeComponent, SubstanceImpuritiesComponent, + SubstanceSsg4mComponent, ] }) export class SubstanceProductsModule { } diff --git a/src/app/fda/substance-details/substance-products/substance-ssg4m/substance-ssg4m.component.html b/src/app/fda/substance-details/substance-products/substance-ssg4m/substance-ssg4m.component.html new file mode 100644 index 000000000..f6cf374ea --- /dev/null +++ b/src/app/fda/substance-details/substance-products/substance-ssg4m/substance-ssg4m.component.html @@ -0,0 +1,77 @@ +
    + SSG4 Manufacturing +     + + +
    + +
    + +
    + + +
    + +
    Product NDC Product ID - + {{product.productNDC}} @@ -59,7 +68,7 @@ - + {{product.productNDC}} @@ -73,42 +82,57 @@ - Product Name {{product.productName}} Product Name +
    + {{prodName.productName}} +
    +
    Labeler Name {{product.labelerName}} Labeler Name {{product.labelerName}} +
    + {{prodComp.labelerName}} +
    +
    Country {{product.country}} Country +
    + {{prodComp.countryWithoutCode}} +
    +
    Status Status {{product.status}} Product Type Product Type {{product.productType}} Active Moiety {{product.activeMoietyName}} Ingredient Type {{product.ingredientType}} Ingredient Type +
    +
    +
    + {{prodIng.ingredientType}} +
    +
    +
    +
    Application Number Application Number {{product.appTypeNumber}}
    + + + + + + + + + + + + + + + + +
    View/Edit +
    + + + Please Login to View/Edit + +
    +
    Substance Reaction/Role +
    + +
    +
    + + {{ssg4Detail.sbstncReactnSectNm}} + +
    + {{ssg4Detail.sbstncRoleNm}} +
    +
    +
    + + + \ No newline at end of file diff --git a/src/app/fda/substance-details/substance-products/substance-ssg4m/substance-ssg4m.component.scss b/src/app/fda/substance-details/substance-products/substance-ssg4m/substance-ssg4m.component.scss new file mode 100644 index 000000000..67355d5b3 --- /dev/null +++ b/src/app/fda/substance-details/substance-products/substance-ssg4m/substance-ssg4m.component.scss @@ -0,0 +1,76 @@ +.mat-table-style { + td.mat-cell { + vertical-align: top; + font-size: 12px; + } +} + +.bordergray { + border:1px solid var(--regular-grey-color); +} + +.font10px { + font-size: 10px; +} + +.font12px { + font-size: 12px; +} + +.font15px { + font-size: 15px; +} + +.colorgray { + color: var(--regular-grey-color); +} + +.colorred { + color: var(--regular-red-color); +} + +.colorgreen { + color: var(--regular-green-color); +} + +.colorpurple { + color: var(--regular-purple-color); +} + +.colorblue { + color: var(--deep-purple-color); +} + +.padleft25px { + padding-left: 25px; +} + +.padtop5px { + padding-top: 5px; +} + +.marginbottom10px { + margin-bottom: 10px; +} + +.width100px { + width: 100px; +} + +ol, li { + margin-left:0px; + padding-left:0px +}​ + +.spinnerstyle { + position: absolute; + top: 0; + left: 0; + bottom: 56px; + right: 0; + background: var(--spinner-bg-color); + z-index: 1; + display: flex; + align-items: top; /* Verticle align */ + justify-content: center; /* horizonal align */ +} diff --git a/src/app/fda/substance-details/substance-products/substance-ssg4m/substance-ssg4m.component.spec.ts b/src/app/fda/substance-details/substance-products/substance-ssg4m/substance-ssg4m.component.spec.ts new file mode 100644 index 000000000..c125244c1 --- /dev/null +++ b/src/app/fda/substance-details/substance-products/substance-ssg4m/substance-ssg4m.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SubstanceSsg4mComponent } from './substance-ssg4m.component'; + +describe('SubstanceSsg4mComponent', () => { + let component: SubstanceSsg4mComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ SubstanceSsg4mComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(SubstanceSsg4mComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/fda/substance-details/substance-products/substance-ssg4m/substance-ssg4m.component.ts b/src/app/fda/substance-details/substance-products/substance-ssg4m/substance-ssg4m.component.ts new file mode 100644 index 000000000..48e2493f6 --- /dev/null +++ b/src/app/fda/substance-details/substance-products/substance-ssg4m/substance-ssg4m.component.ts @@ -0,0 +1,123 @@ +import { Component, OnInit, OnDestroy, Input, Output, EventEmitter } from '@angular/core'; +import { ActivatedRoute, Router, NavigationExtras } from '@angular/router'; +import { PageEvent } from '@angular/material/paginator'; +import { MatDialog } from '@angular/material/dialog'; +import { Sort } from '@angular/material/sort'; +import { Subscription } from 'rxjs'; +import { take } from 'rxjs/operators'; +import { ConfigService } from '@gsrs-core/config'; +import { LoadingService } from '@gsrs-core/loading/loading.service'; +import { AuthService } from '@gsrs-core/auth'; +import { GoogleAnalyticsService } from '@gsrs-core/google-analytics'; +import { ImpuritiesService } from '../../../impurities/service/impurities.service'; +import { GeneralService } from '../../../service/general.service'; +import { SubstanceCardBaseFilteredList } from '@gsrs-core/substance-details'; +import { SubstanceDetailsBaseTableDisplay } from '../substance-details-base-table-display'; +import { Facet } from '@gsrs-core/facets-manager'; +import { FacetParam, FacetHttpParams, FacetQueryResponse } from '@gsrs-core/facets-manager'; +import { SubstanceSsg4mService } from '@gsrs-core/substance-ssg4m/substance-ssg4m-form.service'; + +@Component({ + selector: 'app-substance-ssg4m', + templateUrl: './substance-ssg4m.component.html', + styleUrls: ['./substance-ssg4m.component.scss'] +}) +export class SubstanceSsg4mComponent extends SubstanceDetailsBaseTableDisplay implements OnInit, OnDestroy { + + @Input() substanceUuid: string; + @Input() substanceName: string; + @Output() countSsg4mOut: EventEmitter = new EventEmitter(); + private subscriptions: Array = []; + ssg4mTotalRecords = 0; + showSpinner = false; + pageIndex = 0; + pageSize = 5; + public privateSearchTerm?: string; + private privateFacetParams: FacetParam; + privateExport = false; + disableExport = false; + etag = ''; + ascDescDir = 'desc'; + displayedColumns: string[] = [ + 'view', + 'substanceReaction' + ]; + + constructor( + private router: Router, + public gaService: GoogleAnalyticsService, + private ssg4mService: SubstanceSsg4mService, + private generalService: GeneralService, + private authService: AuthService, + private loadingService: LoadingService, + private dialog: MatDialog + ) { + super(gaService, ssg4mService); + } + + ngOnInit() { + const rolesSubscription = this.authService.hasAnyRolesAsync('Admin', 'Updater', 'SuperUpdater').subscribe(response => { + this.isAdmin = response; + }); + this.subscriptions.push(rolesSubscription); + + if (this.substanceUuid) { + this.getSsg4mBySubstanceUuid(); + } + } + + ngOnDestroy(): void { + this.subscriptions.forEach(subscription => { + if (subscription) { + subscription.unsubscribe(); + } + }); + } + + getSsg4mBySubstanceUuid(pageEvent?: PageEvent) { + this.showSpinner = true; // Start progress spinner + + this.setPageEvent(pageEvent); + const skip = this.page * this.pageSize; + + const subscription = this.ssg4mService.getSyntheticPathwayIndexBySubUuid(this.substanceUuid).subscribe(results => { + let synthResultsOrganized: Array = []; + + if (results.length > 0) { + // Loop through the results and organize the data to display in the table. + results.forEach(rec => { + if (rec.synthPathwaySkey) { + let found = false; + synthResultsOrganized.forEach(synthOrg => { + // If found the key, append Reaction in the same record, or else create a new record + if (synthOrg['synthPathwaySkey'] === rec.synthPathwaySkey) { + synthOrg.synthDetails.push({'sbstncReactnSectNm': rec.sbstncReactnSectNm, 'sbstncRoleNm': rec.sbstncRoleNm}); + // alert("FOUND: " + synthOrg['synthPathwaySkey'] + ' ' + rec.synthPathwaySkey); + found = true; + } + }); + + // else { // a new record + if (found === false) { + const newSynth: any = {synthDetails: []}; + newSynth.synthPathwaySkey = rec.synthPathwaySkey; + newSynth.synthDetails.push({'sbstncReactnSectNm': rec.sbstncReactnSectNm, 'sbstncRoleNm': rec.sbstncRoleNm}); + synthResultsOrganized.push(newSynth); + } + } + }); + } + this.ssg4mTotalRecords = synthResultsOrganized.length; + this.paged = synthResultsOrganized; + this.countSsg4mOut.emit(synthResultsOrganized.length); + }, error => { + this.showSpinner = false; // Stop progress spinner + console.log('error'); + }, () => { + this.showSpinner = false; // Stop progress spinner + subscription.unsubscribe(); + }); + // this.loadingStatus = ''; + // this.showSpinner = false; // Stop progress spinner + } +} diff --git a/src/app/fda/substance-search-select/substance-search-selector.component.html b/src/app/fda/substance-search-select/substance-search-selector.component.html index 1a2fe8e0c..4afd84f5a 100644 --- a/src/app/fda/substance-search-select/substance-search-selector.component.html +++ b/src/app/fda/substance-search-select/substance-search-selector.component.html @@ -1,6 +1,6 @@
    + (searchPerformed)="processSubstanceSearch($event)" (searchValueOut)="searchValueOutChange($event)" [eventCategory]="eventCategory" [placeholder]="placeholder">
    diff --git a/src/app/fda/substance-search-select/substance-search-selector.component.scss b/src/app/fda/substance-search-select/substance-search-selector.component.scss index 9c129b777..98d5244e4 100644 --- a/src/app/fda/substance-search-select/substance-search-selector.component.scss +++ b/src/app/fda/substance-search-select/substance-search-selector.component.scss @@ -24,8 +24,8 @@ flex-direction: column; .mat-mini-fab { - background-color: rgba(242, 242, 242, .85); - color: #404040; + background-color: var(--mat-icon-button-bg-color); + color: var(--label-color); width: 35px; height: 35px; @@ -44,5 +44,5 @@ } .mat-progress-spinner circle, .mat-spinner circle { - stroke: #3fb53f; -} \ No newline at end of file + stroke: var(--spinner-stroke-color); +} diff --git a/src/app/fda/substance-search-select/substance-search-selector.component.ts b/src/app/fda/substance-search-select/substance-search-selector.component.ts index fd05d5617..a3268f76f 100644 --- a/src/app/fda/substance-search-select/substance-search-selector.component.ts +++ b/src/app/fda/substance-search-select/substance-search-selector.component.ts @@ -1,6 +1,7 @@ import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; import { SubstanceService } from '@gsrs-core/substance/substance.service'; import { SubstanceSummary } from '@gsrs-core/substance/substance.model'; +import { ConfigService } from '@gsrs-core/config'; @Component({ selector: 'app-substance-search-selector', @@ -12,6 +13,7 @@ export class SubstanceSearchSelectorComponent implements OnInit { @Input() eventCategory: string; @Output() selectionUpdated = new EventEmitter(); @Output() showMessage = new EventEmitter(); + @Output() searchValueOut = new EventEmitter(); @Input() placeholder = 'Search'; @Input() hintMessage = ''; @Input() header = 'Substance'; @@ -21,12 +23,19 @@ export class SubstanceSearchSelectorComponent implements OnInit { displayName: string; searchValue: string = null; loadingStructure = false; + private substanceSelectorProperties: Array = null; constructor( public substanceService: SubstanceService, + public configService: ConfigService, ) { } ngOnInit() { + if (this.configService.configData.substanceSelectorProperties != null) { + this.substanceSelectorProperties = this.configService.configData.substanceSelectorProperties; + } else { + console.log("The config value for substanceSelectorProperties is null."); + } } @Input() @@ -50,14 +59,18 @@ export class SubstanceSearchSelectorComponent implements OnInit { } processSubstanceSearch(searchValue: string = ''): void { - this.searchValue = searchValue; const q = searchValue.replace('\"', ''); - - const searchStr = `root_names_name:\"^${q}$\" OR ` + + // Changed to configuration approach. + const searchStr = this.substanceSelectorProperties.map(property => `${property}:\"^${q}$\"`).join(' OR '); + /* + const searchStr = + `root_names_name:\"^${q}$\" OR ` + + `root_names_stdName:\"^${q}$\" OR ` + `root_approvalID:\"^${q}$\" OR ` + - `root_codes_BDNUM:\"^${q}$\"`; - + `root_codes_BDNUM:\"^${q}$\"` + ; + */ this.substanceService.getQuickSubstancesSummaries(searchStr, true).subscribe(response => { this.loadingStructure = true; if (response.content && response.content.length) { @@ -65,7 +78,7 @@ export class SubstanceSearchSelectorComponent implements OnInit { this.selectionUpdated.emit(this.selectedSubstance); this.errorMessage = ''; } else { - this.showMessage.emit('No substances found'); + this.showMessage.emit('No substances found for ' + this.searchValue); } this.loadingStructure = false; }); @@ -76,4 +89,7 @@ export class SubstanceSearchSelectorComponent implements OnInit { this.selectionUpdated.emit(this.selectedSubstance); } + searchValueOutChange(searchValue: string) { + this.searchValueOut.emit(searchValue); + } } diff --git a/src/app/fda/user-manual/user-manual.component.scss b/src/app/fda/user-manual/user-manual.component.scss index 4661c225c..9b75cd63e 100644 --- a/src/app/fda/user-manual/user-manual.component.scss +++ b/src/app/fda/user-manual/user-manual.component.scss @@ -1,6 +1,6 @@ .colorblue { - color: blue; + color: var(--regular-blue-color); } .padleft40px { @@ -9,4 +9,4 @@ .padtop110px { padding-top: 110px; -} \ No newline at end of file +} diff --git a/src/environments/environment.cbg.prod.ts b/src/environments/environment.cbg.prod.ts index 61ceb4a6f..71a62ee25 100644 --- a/src/environments/environment.cbg.prod.ts +++ b/src/environments/environment.cbg.prod.ts @@ -3,7 +3,7 @@ import { baseEnvironment } from './_base-environment'; export const environment = baseEnvironment; environment.apiBaseUrl = '/gsrs/app/'; environment.production = true; -environment.baseHref = '/gsrs/app/beta/'; +environment.baseHref = ''; environment.clasicBaseHref = '/gsrs/app/'; environment.appId = 'cbg'; environment.googleAnalyticsId = 'UA-136176848-1'; diff --git a/src/environments/environment.fda.local.ts b/src/environments/environment.fda.local.ts index 2a04b5c7b..572f6592d 100644 --- a/src/environments/environment.fda.local.ts +++ b/src/environments/environment.fda.local.ts @@ -7,6 +7,8 @@ environment.clasicBaseHref = '/ginas/app/'; environment.appId = 'fda'; environment.isAnalyticsPrivate = true; -environment.apiBaseUrl = 'http://localhost:9000/ginas/app/'; +// environment.apiBaseUrl = 'http://localhost:9000/ginas/app/'; + // environment.apiBaseUrl = 'http://localhost:8080/'; +// environment.apiBaseUrl = 'http://localhost:8081/'; export { FdaModule as EnvironmentModule } from '../app/fda/fda.module'; diff --git a/src/environments/environment.fda.prod.ts b/src/environments/environment.fda.prod.ts index be3481fd6..98b6d677e 100644 --- a/src/environments/environment.fda.prod.ts +++ b/src/environments/environment.fda.prod.ts @@ -3,10 +3,10 @@ import { baseEnvironment } from 'src/environments'; export const environment = baseEnvironment; environment.apiBaseUrl = '/ginas/app/'; environment.production = true; -environment.baseHref = '/ginas/app/beta/'; +environment.baseHref = ''; environment.clasicBaseHref = '/ginas/app/'; environment.appId = 'fda'; -environment.googleAnalyticsId = 'UA-136176848-3'; +/*environment.googleAnalyticsId = 'UA-136176848-3';*/ environment.isAnalyticsPrivate = true; export { FdaModule as EnvironmentModule } from '../app/fda/fda.module'; diff --git a/src/environments/environment.gsrs.prod.ts b/src/environments/environment.gsrs.prod.ts index 50aa6fa6f..5eac0a5b9 100644 --- a/src/environments/environment.gsrs.prod.ts +++ b/src/environments/environment.gsrs.prod.ts @@ -5,7 +5,8 @@ environment.apiBaseUrl = '/ginas/app/'; environment.production = true; environment.baseHref = '/ginas/app/beta/'; environment.clasicBaseHref = '/ginas/app/'; -environment.appId = 'gsrs'; -environment.googleAnalyticsId = 'UA-136176848-1'; +environment.appId = 'fda'; +environment.googleAnalyticsId = null; +environment.isAnalyticsPrivate = true; -export { GsrsModule as EnvironmentModule } from '../app/core/gsrs.module'; +export { FdaModule as EnvironmentModule } from '../app/fda/fda.module'; diff --git a/src/index.html b/src/index.html index 455241888..6ecf3bad3 100644 --- a/src/index.html +++ b/src/index.html @@ -28,6 +28,7 @@ + diff --git a/src/polyfills.ts b/src/polyfills.ts index f510e716b..b6eb29e7c 100644 --- a/src/polyfills.ts +++ b/src/polyfills.ts @@ -55,7 +55,9 @@ import 'core-js/es6/reflect'; import 'zone.js/dist/zone'; // Included with Angular CLI. (window as any).global = window; - +(window as any).process = { + env: { DEBUG: undefined }, +}; /*************************************************************************************************** diff --git a/src/styles/_base.scss b/src/styles/_base.scss index 780585d14..12b32334b 100644 --- a/src/styles/_base.scss +++ b/src/styles/_base.scss @@ -21,7 +21,7 @@ a:hover, button:hover { .multiselect-dropdown .dropdown-btn { display: inline-block; border: 0px !important; - border-bottom: 1px solid rgba(0, 0, 0, .87) !important; + border-bottom: 1px solid var(--mat-list-color) !important; width: 100%; padding: 6px 12px; margin-bottom: 0; @@ -34,9 +34,9 @@ a:hover, button:hover { background-image: none; .dropdown-down { - border-top: 5px solid rgba(0, 0, 0, 0.54) !important; - border-left: 5px solid transparent !important; - border-right: 5px solid transparent !important; + border-top: 5px solid var(--dark-label-color) !important; + border-left: 5px solid var(--regular-transparent-color) !important; + border-right: 5px solid var(--regular-transparent-color) !important; } } @@ -47,7 +47,7 @@ a:hover, button:hover { .mat-button { - color: #1565c0; + color: var(--link-color); } .mat-form-field-type-mat-input.mat-focused { @@ -55,6 +55,6 @@ a:hover, button:hover { .mat-form-field-infix { transition: all 500ms; padding-top: 5px; - border: 1px solid #4793d1 + border: 1px solid var(--primary-color); } -} \ No newline at end of file +} diff --git a/src/styles/_card.scss b/src/styles/_card.scss index 98de43fe4..ccdeff23c 100644 --- a/src/styles/_card.scss +++ b/src/styles/_card.scss @@ -26,12 +26,12 @@ .alternate-backgrounds { &:nth-child(odd) { - background-color: rgba(68, 138, 255, .07); + background-color: var(--nth-child-color-2); ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(68, 138, 255, .15); + background-color: var(--nth-child-color-3); } } } @@ -40,14 +40,14 @@ ::ng-deep { .mat-expansion-panel:not(.mat-expanded):not([aria-disabled="true"]) .mat-expansion-panel-header:hover { - background-color: rgba(128, 128, 128, .15); + background-color: var(--nth-child-color-1); } } } ::ng-deep { .mat-expansion-panel, .mat-table, textarea { - background-color: transparent; + background-color: var(--regular-transparent-color); } } } diff --git a/src/styles/_mat-side-nav.scss b/src/styles/_mat-side-nav.scss index 81c064f83..648abbd52 100644 --- a/src/styles/_mat-side-nav.scss +++ b/src/styles/_mat-side-nav.scss @@ -4,7 +4,7 @@ left: 0; right: 0; bottom: 0; - background-color: transparent; + background-color: var(--regular-transparent-color); } mat-sidenav { @@ -13,7 +13,7 @@ mat-sidenav { } .mat-drawer-content { - background-color: transparent; + background-color: var(--regular-transparent-color); } .expand-sidenav { @@ -21,7 +21,7 @@ mat-sidenav { left: 0; top: 80px; z-index: 10000; - background-color: white; + background-color: var(--regular-white-color); border: none; border-top-right-radius: 25%; border-bottom-right-radius: 25%; @@ -48,6 +48,6 @@ mat-sidenav { } mat-sidenav.mat-drawer { - background-color: transparent; + background-color: var(--regular-transparent-color); } -} \ No newline at end of file +} diff --git a/src/styles/_misc.scss b/src/styles/_misc.scss index 5bd6ee225..336d65e0c 100644 --- a/src/styles/_misc.scss +++ b/src/styles/_misc.scss @@ -11,6 +11,14 @@ margin-bottom: 15px; } +.mat-autocomplete-panel { + min-width: 200px; +} + + +body { + background-color: var(--body-bg-color); +} .name-value { display: flex; flex-direction: row; @@ -37,7 +45,11 @@ } .blue-font { - color: #1565C0 + color: var(--link-color); +} + +.grey-font { + color: var(--regular-grey-color); } .capitalized { @@ -79,7 +91,7 @@ } .link { - color: #1E88E5; + color: var(--link-color); } .font-medium-bold { @@ -117,30 +129,30 @@ font-size: 12px; font-weight: 700; line-height: 1; - color: #fff; + color: var(--tabstyle-bg-img-start-color); text-align: center; white-space: nowrap; vertical-align: middle; - background-color: #777; + background-color: var(--light-grey-color); border-radius: 10px; } .black-pill, .gray-pill, .blue-pill { - color: white; + color: var(--regular-white-color); padding: 1px 7px; border-radius: 11px; } .black-pill { - background-color: black; + background-color: var(--regular-black-color); } .gray-pill { - background-color: gray; + background-color: var(--regular-grey-color); } .blue-pill { - background-color:#448aff; + background-color: var(--link-primary-color); } .dialog-close{ @@ -166,6 +178,10 @@ flex-direction: column; } +.cards-container.maximize-width-card { + display: block; +} + .scrollable-container { height: 100%; overflow: auto; @@ -223,7 +239,7 @@ font-size: 11px; padding-bottom: 3.5px; line-height: 11px; - color: rgba(0, 0, 0, 0.54); + color: var(--dark-label-color); font-weight: 400; font-family: Roboto, "Helvetica Neue", sans-serif; } @@ -231,12 +247,12 @@ textarea { box-sizing: border-box; width: 100%; - border: solid 1.5px rgba(0, 0, 0, 0.42); + border: solid 1.5px var(--textarea-dark-border-color); border-radius: 3px; padding: 10px; ::placeholder { - color: rgba(0, 0, 0, 0.54); + color: var(--dark-label-color); font-weight: 400; font-size: 14px; } @@ -274,12 +290,12 @@ textarea { left: 0; display: flex; z-index: 10; - background-color: rgba(255, 255, 255, .8); + background-color: var(--notif-backdrop-bg-color); justify-content: center; align-items: center; font-size: 30px; font-weight: bold; - color: #666; + color: var(--notif-backdrop-color); } .hidden { @@ -298,7 +314,7 @@ textarea { .disabled { pointer-events: none !important; cursor: default !important; - color: #595959 !important; + color: var(--structure-import-color) !important; text-decoration: none; } diff --git a/src/styles/_styles.scss b/src/styles/_styles.scss new file mode 100644 index 000000000..3172e6bc6 --- /dev/null +++ b/src/styles/_styles.scss @@ -0,0 +1,176 @@ +@import '~@angular/material/theming'; + +:root { + + --primary-color: #4793d1; + --secondary-blue: #229fdd; + --side-nav-bg-color: #002b5c; + --white-color: #ffffff; + --regular-red-color: red; + --regular-black-color: black; + --regular-white-color: white; + --regular-lightgray-color: lightgray; + --regular-gainsboro-color: gainsboro; + --regular-grey-color: grey; + --regular-transparent-color: transparent; + --regular-blue-color: blue; + --regular-navy-color: navy; + --regular-yellow-color: yellow; + --regular-orange-color: orange; + --regular-orangered-color: orangered; + --regular-green-color: green; + --regular-darkgreen-color: darkgreen; + --regular-darkolivegreen-color: darkolivegreen; + --regular-turquoise-color: turquoise; + --regular-lightskyblue-color: lightskyblue; + --regular-darkslateblue-color: darkslateblue; + --regular-purple-color: purple; + --regular-magenta-color: magenta; + --regular-maroon-color: maroon; + --primary-title-color: #0857d6; + --secondary-title-color: #27288a; + --lighter-title-color: rgba(0, 0, 0, 0.87); + --blue-color: #1945bd; + --blue-bg-color-rgb: rgba(22, 84, 218, 0.753); + --secondary-blue-color: #1C6EA4; + --royal-blue-color: #3c19d6; + --bluish-purple-color: rgb(70, 101, 194); + --darkgrey-color: #4e4e44; + --deep-blue-color-rgb: rgb(34, 34, 175); + --deep-blue-color-rgb-2: rgb(0, 0, 255); + --secondary-rgb-blue: rgb(71, 147, 209); + --purple-color: #7a0abbe0; + --deep-purple-color: #4b4572; + --brown-color: #rgb(122, 45, 17); + --maroon-color: rgb(90, 68, 68); + --orange-color: #E55913; + --orange-color-2: #d35e04; + --orange-color-rgb: rgb(207, 157, 63); + --orange-color-rgb-2: rgb(230, 115, 22); + --burnt-orange-color: rgb(158, 56, 19); + --light-orange-color: #f8cf88; + --green-color: rgb(9, 121, 33); + --light-green-color: #bbc2a4; + --yellow-color: rgb(231, 197, 2); + --mustard-color: rgb(172, 141, 4); + --light-grey-color: #777; + --dark-grey-color: #262626; + --light-yellow-color: rgb(251, 247, 206); + --light-yellow-color-2: rgba(251, 251, 180, 0.705); + --light-yellow-color-3: rgba(255, 222, 121, 0.96); + --red-label-color: rgb(155, 39, 39); + --dark-grey-bg-color: #60615e; + --error: rgb(173, 26, 26); + --link-color: #1565C0; + --link-primary-color: #448aff; + --pink-span-color: #c7254e; + --table-bg-color: #FFFFFF; + --table-bg-color-2: #f7f7f7; + --table-th-border-color: #224998; + --table-th-border-color-2: #D0E4F5; + --table-th-border-color-3: #d1d1d1; + --table-th-border-color-4: #5f656b; + --table-tr-even-bg-color: #f3f3f3; + --table-tr-even-bg-color-2: #DAE9F5; + --table-tr-even-bg-color-3: #fbfdf7; + --table-thead-bg-color: #0B6FA4; + --table-thead-bg-color-2: #c0ccee; + --table-thead-bg-color-3: #eeeadc; + --table-thead-border-color: #444444; + --table-th-color: #FFFFFF; + --nth-child-color-1: rgba(128, 128, 128, .15); + --nth-child-color-2: rgba(68, 138, 255, .07); + --nth-child-color-3: rgba(68, 138, 255, .15); + --error-dialog-color: #a94442; + --error-dialog-bg-color: #f2dede; + --warning-dialog-color: #8a6d3b; + --warning-dialog-bg-color: #fcf8e3; + --notice-dialog-color: black; + --notice-dialog-bg-color: lightgray; + --progress-bar-buffer-bg-color: rgba(0, 0, 0, 0.01); + --progress-spinner-buffer-bg-color: rgb(212, 212, 212); + --text-color: rgba(0, 0, 0, .5); + --text-shadow-color: #000; + --label-color: #404040; + --dark-label-color: rgba(0, 0, 0, 0.54); + --spinner-bg-color: rgba(150, 148, 148, 0.15); + --spinner-stroke-color: #3fb53f; + --border-color: rgba(0, 0, 0, .1); + --pale-border-color: #E0E0E0; + --pale-border-color-rgb: rgb(228, 218, 218); + --pale-border-color-rgb-2: rgb(247, 241, 241); + --pale-border-color-rgb-3: rgb(241, 241, 250); + --pale-border-color-rgb-4: rgb(182, 175, 175); + --grey-border-color: #CCC; + --grey-border-color-rgb: rgb(182, 177, 177); + --deep-purple-border-color-rgb: rgb(67, 70, 148); + --box-shadow-color: rgba(0, 0, 0, 0.2); + --box-shadow-color-2: rgba(0, 0, 0, 0.14); + --box-shadow-color-3: rgba(0, 0, 0, 0.12); + --box-shadow-color-4: rgba(0, 0, 0, 0.35); + --box-shadow-color-5: rgba(0, 0, 0, 0.26); + --box-shadow-color-6: rgba(0, 0, 0, 0.45); + --box-shadow-grey-color: rgba(145, 140, 145, 1); + --deprecated-color: rgba(0, 0, 0, .70); + --lightgray-color: #f1f1f1; + --lightlightgray-color: #f2f3f4 ; + --gray-color: #666666; + --version-text-color: #182c4d; + --nav-item-a-color: #e6e6e6; + --chevron-color: rgba(0, 0, 0, 0.6); + --include-checkbox-border-color: #80CBC4; + --include-checkbox-bg-color: #26A69A; + --exclude-checkbox-border-color: #EF9A9A; + --exclude-checkbox-bg-color: #EF5350; + --query-bg-color: #ECECEC; + --query-border-color: #DBDBDB; + --mat-list-color: rgba(0, 0, 0, 0.87); + --mat-form-field-label-color: rgb(127, 127, 133); + --mat-form-field-underline-bg-color: rgb(68, 68, 218); + --mat-form-field-focused-color: rgb(70, 116, 106); + --hover-bg-color: rgba(94, 145, 186, 0.2); + --lock-icon-color: #f0ad4e; + --notif-success-bg-color: #A7FFEB; + --notif-error-bg-color: #E57373; + --success-green-color: #3f914c; + --success-green-bg-color: #def2de; + --ref-manager-mat-row-nth-child-color: #f9f9f9; + --img-linear-gradient-start-color: rgba(0, 0, 0, 1); + --img-linear-gradient-color: #c2c7cc; + --textarea-dark-border-color: rgba(0, 0, 0, 0.42); + --textarea-light-border-color: #b3b3b3; + --mat-icon-button-bg-color: rgba(242, 242, 242, .85); + --structure-import-color: #595959; + --structure-import-default-bg-color: rgba(68, 138, 255, .4); + --structure-import-success-bg-color: rgba(167, 255, 235, .4); + --structure-import-error-bg-color: rgba(229, 115, 115, .4); + --notif-backdrop-bg-color: rgba(255, 255, 255, .8); + --notif-backdrop-color: #666; + --hr-color: #333; + --legend-green-border-color: #16997d; + --legend-blue-border-color: #174793; + --legend-blue-border-color-2: #007cba; + --legend-orange-border-color: #b36d13; + --legend-red-border-color: #700819; + --legend-bg-color: #eff8ff; + --legend-bg-green-color: #f8fdf8; + --sub-hierarchy-bg-color: #cee8fb; + --sub-hierarchy-odd-bg-color: rgba(0, 0, 0, .05); + --disulfide-color: #cca300; + --glycosylation-color: #608000; + --n-glycosylation-color: #0066cc; + --o-glycosylation-color: #ff6666; + --fieldset-green-border-color: rgb(33, 136, 20); + --fieldset-blue-border-color: rgb(27, 15, 184); + --fieldset-orange-border-color: rgb(179, 101, 12); + --fieldset-red-border-color: rgb(172, 63, 63); + --fieldset-brown-border-color: #5f391c; + --fieldset-box-shadow-color: rgba(110, 104, 110, 0.64); + --tabstyle-bg-color: #e9f3d9; + --tabstyle-bg-color-2: #fcf8eb; + --tabstyle-bg-color-3: #eee2b9; + --tabstyle-bg-color-4: #cde3fd; + --tabstyle-bg-img-start-color: #fff; + --tabstyle-bg-img-end-color: #b2b2b2; + --body-bg-color: rgba(94, 145, 186, 0.12); +} diff --git a/src/styles/_table.scss b/src/styles/_table.scss index b788d775a..cf0722f88 100644 --- a/src/styles/_table.scss +++ b/src/styles/_table.scss @@ -18,4 +18,8 @@ td.mat-cell, td.mat-footer-cell, th.mat-header-cell { } } -} \ No newline at end of file +} + +.mat-sort-header-content { + display: block !important; +} diff --git a/src/styles/main.scss b/src/styles/main.scss index 2227bf864..462434eba 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -4,4 +4,5 @@ @import 'mat-side-nav'; @import 'table'; @import 'card'; -@import 'expand-details'; \ No newline at end of file +@import 'expand-details'; +@import 'styles'; diff --git a/src/testing/mat-icon-mock.component.ts b/src/testing/mat-icon-mock.component.ts index 180d28905..1079813ae 100644 --- a/src/testing/mat-icon-mock.component.ts +++ b/src/testing/mat-icon-mock.component.ts @@ -2,9 +2,9 @@ import { Component } from '@angular/core'; @Component({ template: '', - // tslint:disable-next-line + // eslint-disable-next-line selector: 'md-icon, mat-icon', }) -// tslint:disable-next-line +// eslint-disable-next-line export class MatIconMock { } diff --git a/src/testing/router-link-mock.directive.ts b/src/testing/router-link-mock.directive.ts index a003b820d..d591a70b3 100644 --- a/src/testing/router-link-mock.directive.ts +++ b/src/testing/router-link-mock.directive.ts @@ -1,9 +1,9 @@ import { Directive, Input } from '@angular/core'; -/* tslint:disable:directive-selector */ -/* tslint:disable:use-host-property-decorator */ -/* tslint:disable:directive-class-suffix */ -/* tslint:disable:no-input-rename */ +/* eslint-disable @angular-eslint/directive-selector */ +/* eslint-disable */ +/* eslint-disable @angular-eslint/directive-class-suffix */ +/* eslint-disable @angular-eslint/no-input-rename */ @Directive({ selector: '[routerLink]', host: { '(click)': 'onClick()' } diff --git a/src/testing/router-outlet-mock.component.ts b/src/testing/router-outlet-mock.component.ts index b9cd2929c..0a434708e 100644 --- a/src/testing/router-outlet-mock.component.ts +++ b/src/testing/router-outlet-mock.component.ts @@ -1,6 +1,6 @@ import { Component } from '@angular/core'; -/* tslint:disable:component-selector */ +/* eslint-disable @angular-eslint/component-selector */ @Component({ selector: 'router-outlet', template: '
    ' diff --git a/src/testing/structure-search-response-test-data.ts b/src/testing/structure-search-response-test-data.ts index 5a93e03a5..84f5fd438 100644 --- a/src/testing/structure-search-response-test-data.ts +++ b/src/testing/structure-search-response-test-data.ts @@ -1,6 +1,6 @@ import { StructureSearchResponse } from '../app/substance/structure-search-response.model'; -/* tslint:disable:max-line-length quotemark */ +/* eslint-disable max-len, @typescript-eslint/quotes */ export const StructureSearchResponseTestData: StructureSearchResponse = { "start": 1544373508714, "id": "bhdtblgdkn", diff --git a/src/testing/substance-details-list-test-data.ts b/src/testing/substance-details-list-test-data.ts index 724e76252..63f06ff31 100644 --- a/src/testing/substance-details-list-test-data.ts +++ b/src/testing/substance-details-list-test-data.ts @@ -1,7 +1,7 @@ import { PagingResponse } from '../app/utils/paging-response.model'; import { SubstanceDetail, SubstanceSummary } from '../app/substance/substance.model'; -/* tslint:disable:max-line-length */ +/* eslint-disable max-len */ export const SubstanceDetailsListData: PagingResponse = { 'id': 36883704, 'version': 1, diff --git a/src/testing/substance-suggestion-test-data.ts b/src/testing/substance-suggestion-test-data.ts index 0a117acea..e59aebe39 100644 --- a/src/testing/substance-suggestion-test-data.ts +++ b/src/testing/substance-suggestion-test-data.ts @@ -1,6 +1,6 @@ import { SubstanceSuggestionsGroup } from '../app/utils/substance-suggestions-group.model'; -/* tslint:disable:max-line-length quotemark */ +/* eslint-disable max-len, @typescript-eslint/quotes */ export const SubstanceData: SubstanceSuggestionsGroup = { "Name": [{ "key": "BUTYRIC ACID, 4-(P-ARSENOSOPHENYL)-", diff --git a/src/testing/substance-summary-list-test-data.ts b/src/testing/substance-summary-list-test-data.ts index d8b9433fb..869509b05 100644 --- a/src/testing/substance-summary-list-test-data.ts +++ b/src/testing/substance-summary-list-test-data.ts @@ -1,7 +1,7 @@ import { PagingResponse } from '../app/utils/paging-response.model'; import { SubstanceSummary } from '../app/substance/substance.model'; -/* tslint:disable:max-line-length */ +/* eslint-disable max-len */ export const SubstanceSummaryListData: PagingResponse = { 'id': 37192136, 'version': 1, diff --git a/substance_dictionary/create_queryable_dictionary.py b/substance_dictionary/create_queryable_dictionary.py index 4ef2465e3..4e31effff 100644 --- a/substance_dictionary/create_queryable_dictionary.py +++ b/substance_dictionary/create_queryable_dictionary.py @@ -2,12 +2,7 @@ from io import BytesIO import requests -# this part can be uncommented if you want to pull straight from the google sheet -# r = requests.get('https://docs.google.com/spreadsheet/ccc?key=1eNhVzNUSyAyq_o0GbxD25uBH_FDI4W2-Bni5eQdvcBQ&output=csv') -# data = r.content -# dictionary_df = pd.read_csv(BytesIO(data)) - -dictionary_df = pd.read_excel('dictionary_current.xlsx') +dictionary_df = pd.read_excel('dictionary_current_all_entities.xlsx') print(dictionary_df.head()) @@ -19,7 +14,10 @@ | (dictionary_df['Data Type'] == 'array ')) ] -dictionary_df = dictionary_df.loc[:, ['Lucene Path', 'Display Name', 'Description', 'Data Type', 'CV DOMAIN', 'Data to include']] +dictionary_df = dictionary_df.loc[:, ['Lucene Path', 'Display Name', 'Description', 'Data Type', 'CV DOMAIN', 'Included in Advanced Search', 'Entity', 'Suggest Field Name']] + +# While technically a number, we should add a "format" field to specify the +# timestamp rather than rely on the description def set_type(df_properties): if 'timestamp' in df_properties['Description'].lower(): @@ -28,19 +26,36 @@ def set_type(df_properties): return 'string' else: return df_properties['Data Type'] - dictionary_df['type'] = dictionary_df.loc[:, ['Lucene Path', 'Data Type', 'Description']].apply(set_type, axis=1) -dictionary_df.rename(columns={"Lucene Path": "lucenePath", "Description": "description", "Display Name": "displayName", "CV DOMAIN": "cvDomain", "Data to include": "priority"}, inplace=True) +dictionary_df.rename(columns={"Lucene Path": "lucenePath", "Description": "description", "Display Name": "displayName", "CV DOMAIN": "cvDomain", "Included in Advanced Search": "priority", "Suggest Field Name": "suggest"}, inplace=True) -columns_to_include = ['lucenePath', 'description', 'type', 'cvDomain', 'priority'] +columns_to_include = ['lucenePath', 'description', 'type', 'cvDomain', 'priority', 'suggest'] +dictionary_df['priority'] = dictionary_df['priority'].replace(['TRUE', '1.0', 1.0],'x'); dictionary_df.drop(columns=['Data Type'], inplace=True) dictionary_df.sort_values('displayName', inplace=True) dictionary_df.set_index('displayName', inplace=True) -dictionary_df.loc[:, columns_to_include].to_json('../src/app/core/assets/data/substance_dictionary.json', orient='index') -data_types_df = dictionary_df.loc[:, 'type'][dictionary_df['type'].unique()] +application_df = dictionary_df[dictionary_df['Entity']=='Application'] +substance_df = dictionary_df[dictionary_df['Entity']=='Substance'] +productall_df = dictionary_df[dictionary_df['Entity']=='ProductAll'] +product_df = dictionary_df[dictionary_df['Entity']=='Product'] +ctus_df = dictionary_df[dictionary_df['Entity']=='ClinicalTrialUS'] +cteu_df = dictionary_df[dictionary_df['Entity']=='ClinicalTrialEurope'] + +# You can preview the sub dictionary here +print(cteu_df) + +application_df.loc[:, columns_to_include].to_json('application_dictionary.json',orient='index', indent=4) +substance_df.loc[:, columns_to_include].to_json('substance_dictionary.json',orient='index', indent=4) + +#TODO: fix the conventions for the filenames below +productall_df.loc[:, columns_to_include].to_json('productall_dictionary.json',orient='index', indent=4) +product_df.loc[:, columns_to_include].to_json('product_dictionary.json',orient='index', indent=4) + + +ctus_df.loc[:, columns_to_include].to_json('ctus_dictionary.json',orient='index', indent=4) +cteu_df.loc[:, columns_to_include].to_json('cteu_dictionary.json',orient='index', indent=4) -data_types_df.to_csv('./data_types.csv') diff --git a/substance_dictionary/dictionary_current.xlsx b/substance_dictionary/dictionary_current.xlsx deleted file mode 100644 index 1d531f65f..000000000 Binary files a/substance_dictionary/dictionary_current.xlsx and /dev/null differ diff --git a/substance_dictionary/dictionary_current_all_entities.xlsx b/substance_dictionary/dictionary_current_all_entities.xlsx new file mode 100644 index 000000000..ba28221a2 Binary files /dev/null and b/substance_dictionary/dictionary_current_all_entities.xlsx differ diff --git a/substance_dictionary/dictionary_v1.xlsx b/substance_dictionary/dictionary_v1.xlsx deleted file mode 100644 index 4dc0b7e08..000000000 Binary files a/substance_dictionary/dictionary_v1.xlsx and /dev/null differ diff --git a/substance_dictionary/requirements.txt b/substance_dictionary/requirements.txt index ea1cedc88..dd999de9e 100644 --- a/substance_dictionary/requirements.txt +++ b/substance_dictionary/requirements.txt @@ -1,5 +1,25 @@ -numpy==1.18.4 +// - commenting this out for ITRB automated scan - numpy==1.21 pandas==1.0.3 python-dateutil==2.8.1 pytz==2020.1 six==1.14.0 +openpyxl + +====================================================== +Creating Data Dictionary JSON INSTRUCTION +Added on March 22, 2021 +------------------------------------------------------ + +-download python-3.9.2-amd64.exe or higher version from https://www.python.org/downloads/ +-install python .exe file in your computer. +-in DOS prompt, go to directory where python is installed + C:\Users\\AppData\Local\Programs\Python\Python39\Scripts +-intall the following libraries +-In DOS prompt type, 'pip install pandas' +-In DOS prompt type 'pip install xlrd' +-IN DOS prompt type 'pip install openpyxl' +-copy files create_dictionary.py and data_dictionary.xlsx to C:\Users\\AppData\Local\Programs\Python\Python39 +-IN DOS prompt, type 'python create_dictionary.py' + + + diff --git a/travisBuild.sh b/travisBuild.sh new file mode 100644 index 000000000..dd7049058 --- /dev/null +++ b/travisBuild.sh @@ -0,0 +1,62 @@ + #!/bin/bash +setup_git() { + cd ../ +ls + git clone https://GsrsBot:${GIT_ACCESS_TOKEN}@github.com/ncats/gsrs-ci.git +cd gsrs-ci +ls +git status +git fetch +git branch -a +git status +git checkout fda_staged_sync + git pull + git merge origin/fda + cd gsrs-ci + +cp -r frontend/src/main/resources/static/substanceRelationshipVisualizer ./ +rm -rf frontend/src/main/resources/static +mkdir frontend/src/main/resources/static +cp -r ../GSRSFrontend/dist/browser/* frontend/src/main/resources/static/ +cp -r ./substanceRelationshipVisualizer frontend/src/main/resources/static/ +rm -rf ./substanceRelationshipVisualizer +git add frontend/src/main/resources/static +git add -u +git commit -m "pushing new frontend build" +git status +ls +git push -u origin fda_staged_sync + + + cd ../ + rm -rf gsrs-ci + ls -l + git clone https://GsrsBot:${GIT_ACCESS_TOKEN}@github.com/ncats/gsrs-ci.git +cd gsrs-ci +git status +git fetch +git branch -a +git status +git checkout master_staged_sync +git pull +git merge origin/master +cp -r frontend/src/main/resources/static/substanceRelationshipVisualizer ./ +cp frontend/src/main/resources/static/assets/data/config.json ./ +rm -rf frontend/src/main/resources/static +mkdir frontend/src/main/resources/static + +cp -r ../GSRSFrontend/dist/browser/* frontend/src/main/resources/static/ +cp -r ./substanceRelationshipVisualizer frontend/src/main/resources/static/ +cp ./config.json frontend/src/main/resources/static/assets/data/ +rm ./config.json +rm -rf ./substanceRelationshipVisualizer +git add frontend/src/main/resources/static +git add -u +git commit -m "travis test before build" +git status +ls +git push -u origin master_staged_sync +} + + +setup_git diff --git a/travisScript.sh b/travisScript.sh new file mode 100644 index 000000000..1aaba9c68 --- /dev/null +++ b/travisScript.sh @@ -0,0 +1,65 @@ +#!/bin/bash +setup_git() { + cd ../ +ls + git clone https://GsrsBot:${GIT_ACCESS_TOKEN}@github.com/ncats/gsrs-ci.git +ls +cd gsrs-ci +ls +git status +git fetch +git branch -a +git status +git checkout fda_staged_sync + git pull + git merge origin/fda + cd gsrs-ci + +cp -r frontend/src/main/resources/static/substanceRelationshipVisualizer ./ +rm -rf frontend/src/main/resources/static +mkdir frontend/src/main/resources/static +cp -r ../GSRSFrontend/dist/browser/* frontend/src/main/resources/static/ +cp -r ./substanceRelationshipVisualizer frontend/src/main/resources/static/ +rm -rf ./substanceRelationshipVisualizer +git status +git add frontend/src/main/resources/static +git add -u +git commit -m "frontend commit ${TRAVIS_COMMIT} build of branch ${TRAVIS_BRANCH} " +git status +ls +echo "pushing" +git push -u origin fda_staged_sync + + + cd ../ + rm -rf gsrs-ci + ls -l + git clone https://GsrsBot:${GIT_ACCESS_TOKEN}@github.com/ncats/gsrs-ci.git +cd gsrs-ci +git status +git fetch +git branch -a +git status +git checkout master_staged_sync +git pull +git merge origin/master +cp -r frontend/src/main/resources/static/substanceRelationshipVisualizer ./ +cp frontend/src/main/resources/static/assets/data/config.json ./ +rm -rf frontend/src/main/resources/static +mkdir frontend/src/main/resources/static + +cp -r ../GSRSFrontend/dist/browser/* frontend/src/main/resources/static/ +cp -r ./substanceRelationshipVisualizer frontend/src/main/resources/static/ +cp ./config.json frontend/src/main/resources/static/assets/data/ +rm ./config.json +rm -rf ./substanceRelationshipVisualizer +git add frontend/src/main/resources/static +git add -u +git commit -m "frontend commit ${TRAVIS_COMMIT} build of branch ${TRAVIS_BRANCH} " +git status +ls +git push -u origin master_staged_sync +} + + +setup_git diff --git a/tsconfig.json b/tsconfig.json index 247fc57b6..51c82ec0d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -51,5 +51,6 @@ "test.ts", "**/*.spec.ts", "src/testing/**/*" - ] + ], + "angularCompilerOptions": { "fullTemplateTypeCheck": false, "strictInjectionParameters": false, "enableIvy": false } } \ No newline at end of file diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 97b375b82..000000000 --- a/tslint.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "rulesDirectory": [ - "node_modules/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-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-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, - "no-inputs-metadata-property": true, - "no-outputs-metadata-property": true, - "no-host-metadata-property": true, - "no-input-rename": true, - "no-output-rename": true, - "use-lifecycle-interface": true, - "use-pipe-transform-interface": true, - "component-class-suffix": true, - "directive-class-suffix": true - } -} diff --git a/webpack.server.config.js b/webpack.server.config.js index 2c841d929..38f5b20ce 100644 --- a/webpack.server.config.js +++ b/webpack.server.config.js @@ -3,7 +3,12 @@ const webpack = require('webpack'); module.exports = { entry: { server: './server.ts' }, - resolve: { extensions: ['.js', '.ts'] }, + resolve: { + extensions: ['.js', '.ts'], + fallback: { + "util": require.resolve("util/") + } + }, target: 'node', mode: 'none', // this makes sure we include node_modules and other 3rd party libraries @@ -13,7 +18,18 @@ module.exports = { filename: '[name].js' }, module: { - rules: [{ test: /\.ts$/, loader: 'ts-loader' }] + rules: [ + { test: /\.ts$/, loader: 'ts-loader' }, + { + test: /\.(sass|less|css)$/, + use: [ + "style-loader", // 3. Inject styles into DOM + "css-loader", // 2. Turns css into commonjs + "sass-loader", // 1. Turns sass into css + ] + }, + { test: /\.txt$/, use: 'raw-loader' } + ] }, plugins: [ // Temporary Fix for issue: https://github.com/angular/angular/issues/11580