From d97c24cc7d9bfbf5295f0d8f0afa16dcdaf83379 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Thu, 1 Aug 2019 15:32:28 +0200 Subject: [PATCH 01/35] feat: Add advance search button on result page card to access to germplasm result page. GNP-4309. --- frontend/src/app/app-routing.module.ts | 2 + frontend/src/app/app.module.ts | 2 + .../germplasm-result-page.component.html | 3 + .../germplasm-result-page.component.scss | 0 .../germplasm-result-page.component.spec.ts | 25 ++++++++ .../germplasm-result-page.component.ts | 16 +++++ .../result-page/facets/facets.component.html | 7 ++ .../facets/facets.component.spec.ts | 60 ++++++++++++++++-- .../result-page/facets/facets.component.ts | 13 ++++ .../assets/faidare/images/advance_search.png | Bin 0 -> 4181 bytes frontend/src/styles.scss | 10 +-- 11 files changed, 128 insertions(+), 10 deletions(-) create mode 100644 frontend/src/app/germplasm-result-page/germplasm-result-page.component.html create mode 100644 frontend/src/app/germplasm-result-page/germplasm-result-page.component.scss create mode 100644 frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts create mode 100644 frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts create mode 100644 frontend/src/assets/faidare/images/advance_search.png diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 8c9af89d..01ef7b6a 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -6,12 +6,14 @@ import { StudyCardComponent } from './study-card/study-card.component'; import { SiteCardComponent } from './site-card/site-card.component'; import { MarkdownPageComponent } from './markdown-page/markdown-page.component'; import { environment } from '../environments/environment'; +import { GermplasmResultPageComponent } from './germplasm-result-page/germplasm-result-page.component'; export const routes: Routes = [ { path: 'studies/:id', component: StudyCardComponent }, { path: 'sites/:id', component: SiteCardComponent }, { path: '', component: ResultPageComponent }, { path: 'germplasm', component: GermplasmCardComponent }, + { path: 'germplasm-result-page', component: GermplasmResultPageComponent }, { path: 'about', component: MarkdownPageComponent, data: { mdFile: environment.aboutUsMdFile } }, { path: 'join', component: MarkdownPageComponent, data: { mdFile: environment.joinUsMdFile } }, { path: 'legal', component: MarkdownPageComponent, data: { mdFile: environment.legalMentionsMdFile } }, diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 92d86528..2ece1696 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -5,6 +5,7 @@ import { AppComponent } from './app.component'; import { FormComponent } from './form/form.component'; import { ResultPageComponent } from './result-page/result-page.component'; import { GermplasmCardComponent } from './germplasm-card/germplasm-card.component'; +import { GermplasmResultPageComponent } from './germplasm-result-page/germplasm-result-page.component'; import { StudyCardComponent } from './study-card/study-card.component'; import { SiteCardComponent } from './site-card/site-card.component'; import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from '@angular/common/http'; @@ -35,6 +36,7 @@ import { MarkdownPageComponent } from './markdown-page/markdown-page.component'; FormComponent, ResultPageComponent, GermplasmCardComponent, + GermplasmResultPageComponent, StudyCardComponent, SiteCardComponent, NavbarComponent, diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html new file mode 100644 index 00000000..960f60b2 --- /dev/null +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html @@ -0,0 +1,3 @@ +<p> + germplasm-result-page works! +</p> diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.scss b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts new file mode 100644 index 00000000..dc6b03f9 --- /dev/null +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { GermplasmResultPageComponent } from './germplasm-result-page.component'; + +describe('GermplasmResultPageComponent', () => { + let component: GermplasmResultPageComponent; + let fixture: ComponentFixture<GermplasmResultPageComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [GermplasmResultPageComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(GermplasmResultPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts new file mode 100644 index 00000000..5ca99949 --- /dev/null +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts @@ -0,0 +1,16 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'faidare-germplasm-result-page', + templateUrl: './germplasm-result-page.component.html', + styleUrls: ['./germplasm-result-page.component.scss'] +}) +export class GermplasmResultPageComponent implements OnInit { + + constructor() { + } + + ngOnInit() { + } + +} diff --git a/frontend/src/app/result-page/facets/facets.component.html b/frontend/src/app/result-page/facets/facets.component.html index 88998bf3..40ae6679 100644 --- a/frontend/src/app/result-page/facets/facets.component.html +++ b/frontend/src/app/result-page/facets/facets.component.html @@ -11,6 +11,13 @@ /> <label class="form-check-label" for="{{ term.term }}"> {{ term.label }} ({{ term.count | number }}) + <button type="button" class="btn btn-sm btn-outline-secondary" + id="advanceSearch" + [routerLink]="'germplasm-result-page'" + *ngIf="term.term == 'Germplasm' && displayAdvanceGermplasmSearchButton"> + Advance search + <img src="assets/faidare/images/advance_search.png" alt="advance germplasm search" title="" height="20px"/> + </button> </label> </div> </form> diff --git a/frontend/src/app/result-page/facets/facets.component.spec.ts b/frontend/src/app/result-page/facets/facets.component.spec.ts index b00fa981..3e4ad674 100644 --- a/frontend/src/app/result-page/facets/facets.component.spec.ts +++ b/frontend/src/app/result-page/facets/facets.component.spec.ts @@ -6,6 +6,7 @@ import { ComponentTester, speculoosMatchers } from 'ngx-speculoos'; import { DataDiscoveryCriteria, DataDiscoveryCriteriaUtils, DataDiscoveryFacet } from '../../models/data-discovery.model'; import { BehaviorSubject } from 'rxjs'; import { take } from 'rxjs/operators'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('FacetsComponent', () => { @@ -26,9 +27,13 @@ describe('FacetsComponent', () => { return this.elements('input'); } + get advanceSearchButton() { + return this.elements('button'); + } + } - const exampleFacet: DataDiscoveryFacet = { + const exampleFacet1: DataDiscoveryFacet = { field: 'sources', terms: [ { @@ -43,9 +48,21 @@ describe('FacetsComponent', () => { ] }; + const exampleFacet2: DataDiscoveryFacet = { + field: 'types', + terms: [ + { + term: 'Germplasm', + label: 'GERMPLASM', + count: 10 + } + ] + }; + beforeEach(() => TestBed.configureTestingModule({ imports: [ReactiveFormsModule], - declarations: [FacetsComponent] + declarations: [FacetsComponent], + schemas: [NO_ERRORS_SCHEMA] })); beforeEach(() => jasmine.addMatchers(speculoosMatchers)); @@ -54,7 +71,7 @@ describe('FacetsComponent', () => { const tester = new FacetsComponentTester(); const component = tester.componentInstance; - component.facet = exampleFacet; + component.facet = exampleFacet1; component.criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(DataDiscoveryCriteriaUtils.emptyCriteria()); tester.detectChanges(); @@ -73,7 +90,7 @@ describe('FacetsComponent', () => { const tester = new FacetsComponentTester(); const component = tester.componentInstance; - component.facet = exampleFacet; + component.facet = exampleFacet1; const criteria = { ...DataDiscoveryCriteriaUtils.emptyCriteria(), @@ -92,7 +109,7 @@ describe('FacetsComponent', () => { const tester = new FacetsComponentTester(); const component = tester.componentInstance; - component.facet = exampleFacet; + component.facet = exampleFacet1; component.criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(DataDiscoveryCriteriaUtils.emptyCriteria()); tester.detectChanges(); @@ -111,4 +128,37 @@ describe('FacetsComponent', () => { expect(criteria.sources).toEqual(['source 2']); }); }); + + it('should display advance search button for germplasm', () => { + const tester = new FacetsComponentTester(); + + const component = tester.componentInstance; + const criteria = { + ...DataDiscoveryCriteriaUtils.emptyCriteria(), + types: ['Germplasm'] + }; + component.criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(criteria); + component.facet = exampleFacet2; + tester.detectChanges(); + + expect(tester.advanceSearchButton.length).toEqual(1); + expect(tester.advanceSearchButton[0]).toContainText('Advance search'); + + }); + + it('should not display advance search button for germplasm', () => { + const tester = new FacetsComponentTester(); + + const component = tester.componentInstance; + const criteria = { + ...DataDiscoveryCriteriaUtils.emptyCriteria(), + types: ['Germplasm', 'Phenotyping Study'] + }; + component.criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(criteria); + component.facet = exampleFacet2; + tester.detectChanges(); + + expect(tester.advanceSearchButton).toEqual([]); + + }); }); diff --git a/frontend/src/app/result-page/facets/facets.component.ts b/frontend/src/app/result-page/facets/facets.component.ts index d78745ed..b35b60e0 100644 --- a/frontend/src/app/result-page/facets/facets.component.ts +++ b/frontend/src/app/result-page/facets/facets.component.ts @@ -17,6 +17,7 @@ export class FacetsComponent implements OnInit { localCriteria: DataDiscoveryCriteria; checkBoxes: FormGroup = new FormGroup({}); + displayAdvanceGermplasmSearchButton: boolean; constructor() { } @@ -36,10 +37,16 @@ export class FacetsComponent implements OnInit { const isSelected = selectedTerms.indexOf(key) >= 0; control.setValue(isSelected, { emitEvent: false }); } + + if (criteria.types) { + this.showANDHideAdvanceGermplasmSearch(criteria.types); + } + }); this.checkBoxes.valueChanges.subscribe(values => { const selectedTerms = Object.keys(values).filter(key => values[key]); + this.showANDHideAdvanceGermplasmSearch(selectedTerms); this.localCriteria = { ...this.localCriteria, [this.facet.field]: selectedTerms @@ -48,5 +55,11 @@ export class FacetsComponent implements OnInit { }); } + showANDHideAdvanceGermplasmSearch(typeList: String[]) { + const facetIsTypes = this.facet.field === 'types'; + const onlyGermplasmSelected = typeList.length === 1 && typeList.includes('Germplasm'); + this.displayAdvanceGermplasmSearchButton = facetIsTypes && onlyGermplasmSelected; + } + } diff --git a/frontend/src/assets/faidare/images/advance_search.png b/frontend/src/assets/faidare/images/advance_search.png new file mode 100644 index 0000000000000000000000000000000000000000..549f0a9c761da810782ac97751d627ddc6642fb0 GIT binary patch literal 4181 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 literal 0 HcmV?d00001 diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index a86efa94..a0804b17 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -61,21 +61,21 @@ $fa-font-path: '~font-awesome/fonts'; .popover { width: 100%; - max-width:500px; + max-width: 500px; } -.mt-custom{ +.mt-custom { margin-top: 75px; } -h1{ +h1 { border-bottom: solid; font-weight: bold; color: #0f6191; margin-bottom: 20px; } -h2{ +h2 { border-bottom: solid; color: #0f6191; margin-bottom: 16px; @@ -86,7 +86,7 @@ h3 { color: #0f6191; } -blockquote{ +blockquote { border-left-style: solid; border-left-width: 3px; color: grey; -- GitLab From e5aca47520a2550e2cfabf0324880a79cc4afef6 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Tue, 20 Aug 2019 17:09:47 +0200 Subject: [PATCH 02/35] feat: Create the component card-sortable-table to make the sort table more generic. GNP-4309. --- frontend/src/app/app.module.ts | 8 +- .../card-sortable-table.component.html | 51 ++++++++ .../card-sortable-table.component.scss | 0 .../card-sortable-table.component.spec.ts | 25 ++++ .../card-sortable-table.component.ts | 44 +++++++ .../src/app/card-sortable-table/countries.ts | 95 +++++++++++++++ .../card-sortable-table/country.services.ts | 112 ++++++++++++++++++ .../src/app/card-sortable-table/country.ts | 7 ++ .../card-sortable-table/sortable.directive.ts | 29 +++++ .../germplasm-result-page.component.html | 19 ++- .../germplasm-result-page.component.ts | 12 +- 11 files changed, 397 insertions(+), 5 deletions(-) create mode 100644 frontend/src/app/card-sortable-table/card-sortable-table.component.html create mode 100644 frontend/src/app/card-sortable-table/card-sortable-table.component.scss create mode 100644 frontend/src/app/card-sortable-table/card-sortable-table.component.spec.ts create mode 100644 frontend/src/app/card-sortable-table/card-sortable-table.component.ts create mode 100644 frontend/src/app/card-sortable-table/countries.ts create mode 100644 frontend/src/app/card-sortable-table/country.services.ts create mode 100644 frontend/src/app/card-sortable-table/country.ts create mode 100644 frontend/src/app/card-sortable-table/sortable.directive.ts diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 2ece1696..c2fea05c 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -23,12 +23,15 @@ import { CardRowComponent } from './card-row/card-row.component'; import { CardSectionComponent } from './card-section/card-section.component'; import { LoadingSpinnerComponent } from './loading-spinner/loading-spinner.component'; import { CardTableComponent } from './card-table/card-table.component'; +import { CardSortableTableComponent } from './card-sortable-table/card-sortable-table.component'; import { MomentModule } from 'ngx-moment'; import { XrefsComponent } from './xrefs/xrefs.component'; import { CoordinatesModule } from 'angular-coordinates'; import { CardGenericDocumentComponent } from './card-generic-document/card-generic-document.component'; import { MarkdownModule, MarkedOptions, MarkedRenderer } from 'ngx-markdown'; import { MarkdownPageComponent } from './markdown-page/markdown-page.component'; +import { NgbdSortableHeader } from './card-sortable-table/sortable.directive'; +import { DecimalPipe } from '@angular/common'; @NgModule({ declarations: [ @@ -37,6 +40,7 @@ import { MarkdownPageComponent } from './markdown-page/markdown-page.component'; ResultPageComponent, GermplasmCardComponent, GermplasmResultPageComponent, + NgbdSortableHeader, StudyCardComponent, SiteCardComponent, NavbarComponent, @@ -50,6 +54,7 @@ import { MarkdownPageComponent } from './markdown-page/markdown-page.component'; CardSectionComponent, LoadingSpinnerComponent, CardTableComponent, + CardSortableTableComponent, XrefsComponent, CardGenericDocumentComponent, MarkdownPageComponent @@ -86,7 +91,8 @@ import { MarkdownPageComponent } from './markdown-page/markdown-page.component'; ], providers: [ - { provide: HTTP_INTERCEPTORS, useExisting: ErrorInterceptorService, multi: true } + { provide: HTTP_INTERCEPTORS, useExisting: ErrorInterceptorService, multi: true }, + DecimalPipe ], bootstrap: [AppComponent] }) diff --git a/frontend/src/app/card-sortable-table/card-sortable-table.component.html b/frontend/src/app/card-sortable-table/card-sortable-table.component.html new file mode 100644 index 00000000..7ece3902 --- /dev/null +++ b/frontend/src/app/card-sortable-table/card-sortable-table.component.html @@ -0,0 +1,51 @@ +<p>This is a more complete example with a service that simulates server calling:</p> + +<ul> + <li>an observable async service to fetch a list of countries</li> + <li>sorting, filtering and pagination</li> + <li>simulated delay and loading indicator</li> + <li>debouncing of search requests</li> +</ul> + +<form> + <div class="form-group form-inline"> + Full text search: <input class="form-control ml-2" type="text" name="searchTerm" [(ngModel)]="service.searchTerm"/> + <span class="ml-3" *ngIf="service.loading$ | async">Loading...</span> + </div> + + <table class="table table-striped"> + <thead> + <tr> + <th scope="col" sortable="{{ header }}" (sort)="onSort($event)" *ngFor="let header of tableHeaders ">{{ header }}</th> + <!--<th *ngFor="let header of tableHeaders " scope="col">#</th> + <th scope="col" sortable="name" (sort)="onSort($event)">Country</th> + <th scope="col" sortable="area" (sort)="onSort($event)">Area</th> + <th scope="col" sortable="population" (sort)="onSort($event)">Population</th>--> + </tr> + </thead> + <tbody> + <tr *ngFor="let country of countries$ | async"> + <th scope="row">{{ country.id }}</th> + <td> + <img [src]="'https://upload.wikimedia.org/wikipedia/commons/' + country.flag" class="mr-2" style="width: 20px"> + <ngb-highlight [result]="country.name" [term]="service.searchTerm"></ngb-highlight> + </td> + <td><ngb-highlight [result]="country.area | number" [term]="service.searchTerm"></ngb-highlight></td> + <td><ngb-highlight [result]="country.population | number" [term]="service.searchTerm"></ngb-highlight></td> + </tr> + </tbody> + </table> + + <div class="d-flex justify-content-between p-2"> + <ngb-pagination + [collectionSize]="total$ | async" [(page)]="service.page" [pageSize]="service.pageSize"> + </ngb-pagination> + + <select class="custom-select" style="width: auto" name="pageSize" [(ngModel)]="service.pageSize"> + <option [ngValue]="5">5 items per page</option> + <option [ngValue]="10">10 items per page</option> + <option [ngValue]="15">15 items per page</option> + </select> + </div> + +</form> diff --git a/frontend/src/app/card-sortable-table/card-sortable-table.component.scss b/frontend/src/app/card-sortable-table/card-sortable-table.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/frontend/src/app/card-sortable-table/card-sortable-table.component.spec.ts b/frontend/src/app/card-sortable-table/card-sortable-table.component.spec.ts new file mode 100644 index 00000000..7bd6690b --- /dev/null +++ b/frontend/src/app/card-sortable-table/card-sortable-table.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CardSortableTableComponent } from './card-sortable-table.component'; + +describe('CardSortableTableComponent', () => { + let component: CardSortableTableComponent; + let fixture: ComponentFixture<CardSortableTableComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CardSortableTableComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CardSortableTableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/card-sortable-table/card-sortable-table.component.ts b/frontend/src/app/card-sortable-table/card-sortable-table.component.ts new file mode 100644 index 00000000..47cdb0df --- /dev/null +++ b/frontend/src/app/card-sortable-table/card-sortable-table.component.ts @@ -0,0 +1,44 @@ +import { Component, ContentChild, Input, OnInit, QueryList, TemplateRef, ViewChildren } from '@angular/core'; +import { Observable } from 'rxjs'; +import { Country } from './country'; +import { CountryService } from './country.services'; +import { NgbdSortableHeader, SortEvent } from './sortable.directive'; + +@Component({ + selector: 'faidare-card-sortable-table', + templateUrl: './card-sortable-table.component.html', + styleUrls: ['./card-sortable-table.component.scss'] +}) +export class CardSortableTableComponent { + + countries$: Observable<Country[]>; + total$: Observable<number>; + + @Input() tableHeaders: String[]; + @Input() rows: any; + + @ContentChild(TemplateRef) template: TemplateRef<any>; + + constructor(public service: CountryService) { + this.countries$ = service.countries$; + this.total$ = service.total$; + } + + + @ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>; + + + onSort({column, direction}: SortEvent) { + // resetting other headers + this.headers.forEach(header => { + console.log(header); + if (header.sortable !== column) { + header.direction = ''; + } + }); + + this.service.sortColumn = column; + this.service.sortDirection = direction; + } + +} diff --git a/frontend/src/app/card-sortable-table/countries.ts b/frontend/src/app/card-sortable-table/countries.ts new file mode 100644 index 00000000..847d5cf5 --- /dev/null +++ b/frontend/src/app/card-sortable-table/countries.ts @@ -0,0 +1,95 @@ +import {Country} from './country'; + +export const COUNTRIES: Country[] = [ + { + id: 1, + name: 'Russia', + flag: 'f/f3/Flag_of_Russia.svg', + area: 17075200, + population: 146989754 + }, + { + id: 2, + name: 'France', + flag: 'c/c3/Flag_of_France.svg', + area: 640679, + population: 64979548 + }, + { + id: 3, + name: 'Germany', + flag: 'b/ba/Flag_of_Germany.svg', + area: 357114, + population: 82114224 + }, + { + id: 4, + name: 'Portugal', + flag: '5/5c/Flag_of_Portugal.svg', + area: 92090, + population: 10329506 + }, + { + id: 5, + name: 'Canada', + flag: 'c/cf/Flag_of_Canada.svg', + area: 9976140, + population: 36624199 + }, + { + id: 6, + name: 'Vietnam', + flag: '2/21/Flag_of_Vietnam.svg', + area: 331212, + population: 95540800 + }, + { + id: 7, + name: 'Brazil', + flag: '0/05/Flag_of_Brazil.svg', + area: 8515767, + population: 209288278 + }, + { + id: 8, + name: 'Mexico', + flag: 'f/fc/Flag_of_Mexico.svg', + area: 1964375, + population: 129163276 + }, + { + id: 9, + name: 'United States', + flag: 'a/a4/Flag_of_the_United_States.svg', + area: 9629091, + population: 324459463 + }, + { + id: 10, + name: 'India', + flag: '4/41/Flag_of_India.svg', + area: 3287263, + population: 1324171354 + }, + { + id: 11, + name: 'Indonesia', + flag: '9/9f/Flag_of_Indonesia.svg', + area: 1910931, + population: 263991379 + }, + { + id: 12, + name: 'Tuvalu', + flag: '3/38/Flag_of_Tuvalu.svg', + area: 26, + population: 11097 + }, + { + id: 13, + name: 'China', + flag: 'f/fa/Flag_of_the_People%27s_Republic_of_China.svg', + area: 9596960, + population: 1409517397 + } +]; diff --git a/frontend/src/app/card-sortable-table/country.services.ts b/frontend/src/app/card-sortable-table/country.services.ts new file mode 100644 index 00000000..ad5a8ecf --- /dev/null +++ b/frontend/src/app/card-sortable-table/country.services.ts @@ -0,0 +1,112 @@ +import {Injectable, PipeTransform} from '@angular/core'; + +import {BehaviorSubject, Observable, of, Subject} from 'rxjs'; + +import {Country} from './country'; +import {COUNTRIES} from './countries'; +import {DecimalPipe} from '@angular/common'; +import {debounceTime, delay, switchMap, tap} from 'rxjs/operators'; +import {SortDirection} from './sortable.directive'; + +interface SearchResult { + countries: Country[]; + total: number; +} + +interface State { + page: number; + pageSize: number; + searchTerm: string; + sortColumn: string; + sortDirection: SortDirection; +} + +function compare(v1, v2) { + return v1 < v2 ? -1 : v1 > v2 ? 1 : 0; +} + +function sort(countries: Country[], column: string, direction: string): Country[] { + console.log(countries); + console.log(column); + console.log(direction); + + if (direction === '') { + return countries; + } else { + return [...countries].sort((a, b) => { + const res = compare(a[column], b[column]); + return direction === 'asc' ? res : -res; + }); + } +} + +function matches(country: Country, term: string, pipe: PipeTransform) { + return country.name.toLowerCase().includes(term.toLowerCase()) + || pipe.transform(country.area).includes(term) + || pipe.transform(country.population).includes(term); +} + +@Injectable({providedIn: 'root'}) +export class CountryService { + private _loading$ = new BehaviorSubject<boolean>(true); + private _search$ = new Subject<void>(); + private _countries$ = new BehaviorSubject<Country[]>([]); + private _total$ = new BehaviorSubject<number>(0); + + private _state: State = { + page: 1, + pageSize: 5, + searchTerm: '', + sortColumn: '', + sortDirection: '' + }; + + constructor(private pipe: DecimalPipe) { + this._search$.pipe( + tap(() => this._loading$.next(true)), + debounceTime(200), + switchMap(() => this._search()), + delay(200), + tap(() => this._loading$.next(false)) + ).subscribe(result => { + this._countries$.next(result.countries); + this._total$.next(result.total); + }); + + this._search$.next(); + } + + get countries$() { return this._countries$.asObservable(); } + get total$() { return this._total$.asObservable(); } + get loading$() { return this._loading$.asObservable(); } + get page() { return this._state.page; } + get pageSize() { return this._state.pageSize; } + get searchTerm() { return this._state.searchTerm; } + + set page(page: number) { this._set({page}); } + set pageSize(pageSize: number) { this._set({pageSize}); } + set searchTerm(searchTerm: string) { this._set({searchTerm}); } + set sortColumn(sortColumn: string) { this._set({sortColumn}); } + set sortDirection(sortDirection: SortDirection) { this._set({sortDirection}); } + + private _set(patch: Partial<State>) { + Object.assign(this._state, patch); + this._search$.next(); + } + + private _search(): Observable<SearchResult> { + const {sortColumn, sortDirection, pageSize, page, searchTerm} = this._state; + + // 1. sort + let countries = sort(COUNTRIES, sortColumn, sortDirection); + + // 2. filter + countries = countries.filter(country => matches(country, searchTerm, this.pipe)); + const total = countries.length; + + // 3. paginate + countries = countries.slice((page - 1) * pageSize, (page - 1) * pageSize + pageSize); + return of({countries, total}); + } +} + diff --git a/frontend/src/app/card-sortable-table/country.ts b/frontend/src/app/card-sortable-table/country.ts new file mode 100644 index 00000000..93930c04 --- /dev/null +++ b/frontend/src/app/card-sortable-table/country.ts @@ -0,0 +1,7 @@ +export interface Country { + id: number; + name: string; + flag: string; + area: number; + population: number; +} diff --git a/frontend/src/app/card-sortable-table/sortable.directive.ts b/frontend/src/app/card-sortable-table/sortable.directive.ts new file mode 100644 index 00000000..917b2eba --- /dev/null +++ b/frontend/src/app/card-sortable-table/sortable.directive.ts @@ -0,0 +1,29 @@ +import {Directive, EventEmitter, Input, Output} from '@angular/core'; + +export type SortDirection = 'asc' | 'desc' | ''; +const rotate: {[key: string]: SortDirection} = { 'asc': 'desc', 'desc': '', '': 'asc' }; + +export interface SortEvent { + column: string; + direction: SortDirection; +} + +@Directive({ + selector: 'th[sortable]', + host: { + '[class.asc]': 'direction === "asc"', + '[class.desc]': 'direction === "desc"', + '(click)': 'rotate()' + } +}) +export class NgbdSortableHeader { + + @Input() sortable: string; + @Input() direction: SortDirection = ''; + @Output() sort = new EventEmitter<SortEvent>(); + + rotate() { + this.direction = rotate[this.direction]; + this.sort.emit({column: this.sortable, direction: this.direction}); + } +} diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html index 960f60b2..b04045f2 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html @@ -1,3 +1,16 @@ -<p> - germplasm-result-page works! -</p> +<faidare-card-sortable-table +[tableHeaders]="['id','name', 'area', 'population']" +[rows]="countries"> + + <ng-template let-row> + <tr> + + <th scope="row">{{ row.id }}</th> + <td><ngb-highlight [result]="row.name" [term]="row.searchTerm"></ngb-highlight></td> + <td><ngb-highlight [result]="row.area | number" [term]="service.searchTerm"></ngb-highlight></td> + <td><ngb-highlight [result]="row.population | number" [term]="service.searchTerm"></ngb-highlight></td> + + </tr> + </ng-template> + +</faidare-card-sortable-table> diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts index 5ca99949..a9b945dc 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts @@ -1,4 +1,6 @@ import { Component, OnInit } from '@angular/core'; +import { CountryService } from '../card-sortable-table/country.services'; +import { Country } from '../card-sortable-table/country'; @Component({ selector: 'faidare-germplasm-result-page', @@ -7,10 +9,18 @@ import { Component, OnInit } from '@angular/core'; }) export class GermplasmResultPageComponent implements OnInit { - constructor() { + + countries: Country[]; + + constructor(public service: CountryService) { + + this.service.countries$.subscribe(countries => { + this.countries = countries; + }); } ngOnInit() { + } } -- GitLab From cad4285d27fac27d335954a371bdeb23ac9ef1f8 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Wed, 28 Aug 2019 16:37:42 +0200 Subject: [PATCH 03/35] feat: Create a generic sortable table component. GNP-4309. --- .../card-sortable-table.component.html | 11 +++++++++-- .../card-sortable-table.component.ts | 10 ++++++++-- .../app/card-sortable-table/country.services.ts | 17 +++++++---------- .../germplasm-result-page.component.html | 7 ++++--- .../germplasm-result-page.component.ts | 9 +++------ 5 files changed, 31 insertions(+), 23 deletions(-) diff --git a/frontend/src/app/card-sortable-table/card-sortable-table.component.html b/frontend/src/app/card-sortable-table/card-sortable-table.component.html index 7ece3902..caf0a8f7 100644 --- a/frontend/src/app/card-sortable-table/card-sortable-table.component.html +++ b/frontend/src/app/card-sortable-table/card-sortable-table.component.html @@ -24,7 +24,14 @@ </tr> </thead> <tbody> - <tr *ngFor="let country of countries$ | async"> + + + <ng-container *ngFor="let row of rows" + [ngTemplateOutlet]="template" + [ngTemplateOutletContext]="{$implicit: row}"> + </ng-container> + + <!--<tr *ngFor="let country of countries$ | async"> <th scope="row">{{ country.id }}</th> <td> <img [src]="'https://upload.wikimedia.org/wikipedia/commons/' + country.flag" class="mr-2" style="width: 20px"> @@ -32,7 +39,7 @@ </td> <td><ngb-highlight [result]="country.area | number" [term]="service.searchTerm"></ngb-highlight></td> <td><ngb-highlight [result]="country.population | number" [term]="service.searchTerm"></ngb-highlight></td> - </tr> + </tr>--> </tbody> </table> diff --git a/frontend/src/app/card-sortable-table/card-sortable-table.component.ts b/frontend/src/app/card-sortable-table/card-sortable-table.component.ts index 47cdb0df..d4267cc0 100644 --- a/frontend/src/app/card-sortable-table/card-sortable-table.component.ts +++ b/frontend/src/app/card-sortable-table/card-sortable-table.component.ts @@ -1,4 +1,11 @@ -import { Component, ContentChild, Input, OnInit, QueryList, TemplateRef, ViewChildren } from '@angular/core'; +import { + Component, + ContentChild, + Input, + QueryList, + TemplateRef, + ViewChildren +} from '@angular/core'; import { Observable } from 'rxjs'; import { Country } from './country'; import { CountryService } from './country.services'; @@ -31,7 +38,6 @@ export class CardSortableTableComponent { onSort({column, direction}: SortEvent) { // resetting other headers this.headers.forEach(header => { - console.log(header); if (header.sortable !== column) { header.direction = ''; } diff --git a/frontend/src/app/card-sortable-table/country.services.ts b/frontend/src/app/card-sortable-table/country.services.ts index ad5a8ecf..9d5eba54 100644 --- a/frontend/src/app/card-sortable-table/country.services.ts +++ b/frontend/src/app/card-sortable-table/country.services.ts @@ -1,12 +1,12 @@ -import {Injectable, PipeTransform} from '@angular/core'; +import { Injectable, PipeTransform } from '@angular/core'; -import {BehaviorSubject, Observable, of, Subject} from 'rxjs'; +import { BehaviorSubject, Observable, of, Subject } from 'rxjs'; -import {Country} from './country'; -import {COUNTRIES} from './countries'; -import {DecimalPipe} from '@angular/common'; -import {debounceTime, delay, switchMap, tap} from 'rxjs/operators'; -import {SortDirection} from './sortable.directive'; +import { Country } from './country'; +import { COUNTRIES } from './countries'; +import { DecimalPipe } from '@angular/common'; +import { debounceTime, delay, switchMap, tap } from 'rxjs/operators'; +import { SortDirection } from './sortable.directive'; interface SearchResult { countries: Country[]; @@ -26,9 +26,6 @@ function compare(v1, v2) { } function sort(countries: Country[], column: string, direction: string): Country[] { - console.log(countries); - console.log(column); - console.log(direction); if (direction === '') { return countries; diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html index b04045f2..012c70d0 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html @@ -4,12 +4,13 @@ <ng-template let-row> <tr> - <th scope="row">{{ row.id }}</th> - <td><ngb-highlight [result]="row.name" [term]="row.searchTerm"></ngb-highlight></td> + <td> + <img [src]="'https://upload.wikimedia.org/wikipedia/commons/' + row.flag" class="mr-2" style="width: 20px"> + <ngb-highlight [result]="row.name" [term]="service.searchTerm"></ngb-highlight> + </td> <td><ngb-highlight [result]="row.area | number" [term]="service.searchTerm"></ngb-highlight></td> <td><ngb-highlight [result]="row.population | number" [term]="service.searchTerm"></ngb-highlight></td> - </tr> </ng-template> diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts index a9b945dc..3409914a 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts @@ -12,15 +12,12 @@ export class GermplasmResultPageComponent implements OnInit { countries: Country[]; - constructor(public service: CountryService) { + constructor(public service: CountryService) { } + + ngOnInit() { this.service.countries$.subscribe(countries => { this.countries = countries; }); } - - ngOnInit() { - - } - } -- GitLab From 8f668eba6e435c78895dfc6f17f34bc1c9f65256 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Thu, 1 Aug 2019 15:32:28 +0200 Subject: [PATCH 04/35] feat: Add advance search button on result page card to access to germplasm result page. GNP-4309. --- frontend/src/app/app.module.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index c2fea05c..c97fca4e 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -40,6 +40,7 @@ import { DecimalPipe } from '@angular/common'; ResultPageComponent, GermplasmCardComponent, GermplasmResultPageComponent, + GermplasmResultPageComponent, NgbdSortableHeader, StudyCardComponent, SiteCardComponent, -- GitLab From aefa727cea6ecbbf44f30c48080e74994640ef0a Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Tue, 24 Sep 2019 16:22:50 +0200 Subject: [PATCH 05/35] feat: Display germplasm data and start the suggestion when focus on search box. GNP-4309. --- frontend/src/app/brapi.service.ts | 9 +- .../card-sortable-table.component.html | 75 ++++++++------- .../card-sortable-table.component.ts | 7 +- .../src/app/card-sortable-table/countries.ts | 95 ------------------- .../src/app/card-sortable-table/country.ts | 7 -- ...ntry.services.ts => germplasm.services.ts} | 42 ++++---- .../src/app/card-sortable-table/germplasm.ts | 90 ++++++++++++++++++ .../suggestion-field.component.ts | 18 ++-- .../germplasm-result-page.component.html | 55 ++++++++--- .../germplasm-result-page.component.ts | 46 +++++++-- frontend/src/app/models/brapi.model.ts | 32 ++++++- .../result-page/facets/facets.component.ts | 11 ++- 12 files changed, 287 insertions(+), 200 deletions(-) delete mode 100644 frontend/src/app/card-sortable-table/countries.ts delete mode 100644 frontend/src/app/card-sortable-table/country.ts rename frontend/src/app/card-sortable-table/{country.services.ts => germplasm.services.ts} (65%) create mode 100644 frontend/src/app/card-sortable-table/germplasm.ts diff --git a/frontend/src/app/brapi.service.ts b/frontend/src/app/brapi.service.ts index 706b7615..9f39ca24 100644 --- a/frontend/src/app/brapi.service.ts +++ b/frontend/src/app/brapi.service.ts @@ -10,8 +10,10 @@ import { BrapiResult, BrapiResults, BrapiStudy, - BrapiTrial + BrapiTrial, + GermplasmCriteria } from './models/brapi.model'; +import { Germplasm } from './models/gnpis.model'; export const BASE_URL = 'brapi/v1'; @@ -61,6 +63,11 @@ export class BrapiService { .get<BrapiResult<BrapiTrial>>(`${BASE_URL}/trials/${trialsId}`); } + germplasmSearch(criteria: GermplasmCriteria): Observable<BrapiResults<BrapiGermplasm>>{ + return this.http.post<BrapiResults<Germplasm>>(`${BASE_URL}/germplasm-search`,criteria) + } + + /** * Get BrAPI single result response and replace the 'schema:includedInDataCatalog' URI value to the actual source object value. */ diff --git a/frontend/src/app/card-sortable-table/card-sortable-table.component.html b/frontend/src/app/card-sortable-table/card-sortable-table.component.html index caf0a8f7..01007528 100644 --- a/frontend/src/app/card-sortable-table/card-sortable-table.component.html +++ b/frontend/src/app/card-sortable-table/card-sortable-table.component.html @@ -1,54 +1,61 @@ -<p>This is a more complete example with a service that simulates server calling:</p> +<!--<p>This is a more complete example with a service that simulates server calling:</p> <ul> <li>an observable async service to fetch a list of countries</li> <li>sorting, filtering and pagination</li> <li>simulated delay and loading indicator</li> <li>debouncing of search requests</li> -</ul> +</ul>--> <form> <div class="form-group form-inline"> - Full text search: <input class="form-control ml-2" type="text" name="searchTerm" [(ngModel)]="service.searchTerm"/> + Full text search: <input class="form-control ml-2" type="text" + name="searchTerm" + [(ngModel)]="service.searchTerm"/> <span class="ml-3" *ngIf="service.loading$ | async">Loading...</span> </div> - <table class="table table-striped"> - <thead> - <tr> - <th scope="col" sortable="{{ header }}" (sort)="onSort($event)" *ngFor="let header of tableHeaders ">{{ header }}</th> - <!--<th *ngFor="let header of tableHeaders " scope="col">#</th> - <th scope="col" sortable="name" (sort)="onSort($event)">Country</th> - <th scope="col" sortable="area" (sort)="onSort($event)">Area</th> - <th scope="col" sortable="population" (sort)="onSort($event)">Population</th>--> - </tr> - </thead> - <tbody> - - - <ng-container *ngFor="let row of rows" - [ngTemplateOutlet]="template" - [ngTemplateOutletContext]="{$implicit: row}"> - </ng-container> - - <!--<tr *ngFor="let country of countries$ | async"> - <th scope="row">{{ country.id }}</th> - <td> - <img [src]="'https://upload.wikimedia.org/wikipedia/commons/' + country.flag" class="mr-2" style="width: 20px"> - <ngb-highlight [result]="country.name" [term]="service.searchTerm"></ngb-highlight> - </td> - <td><ngb-highlight [result]="country.area | number" [term]="service.searchTerm"></ngb-highlight></td> - <td><ngb-highlight [result]="country.population | number" [term]="service.searchTerm"></ngb-highlight></td> - </tr>--> - </tbody> - </table> + <div class="card"> + <table class="table table-striped mt-1"> + <thead> + <tr> + <th scope="col" sortable="{{ header }}" (sort)="onSort($event)" + *ngFor="let header of tableHeaders ">{{ header }}</th> + <!--<th *ngFor="let header of tableHeaders " scope="col">#</th> + <th scope="col" sortable="name" (sort)="onSort($event)">Country</th> + <th scope="col" sortable="area" (sort)="onSort($event)">Area</th> + <th scope="col" sortable="population" (sort)="onSort($event)">Population</th>--> + </tr> + </thead> + <tbody> + + + <ng-container *ngFor="let row of rows" + [ngTemplateOutlet]="template" + [ngTemplateOutletContext]="{$implicit: row}"> + </ng-container> + + <!--<tr *ngFor="let country of countries$ | async"> + <th scope="row">{{ country.id }}</th> + <td> + <img [src]="'https://upload.wikimedia.org/wikipedia/commons/' + country.flag" class="mr-2" style="width: 20px"> + <ngb-highlight [result]="country.name" [term]="service.searchTerm"></ngb-highlight> + </td> + <td><ngb-highlight [result]="country.area | number" [term]="service.searchTerm"></ngb-highlight></td> + <td><ngb-highlight [result]="country.population | number" [term]="service.searchTerm"></ngb-highlight></td> + </tr>--> + </tbody> + </table> + </div> <div class="d-flex justify-content-between p-2"> <ngb-pagination - [collectionSize]="total$ | async" [(page)]="service.page" [pageSize]="service.pageSize"> + [collectionSize]="total$ | async" [(page)]="service.page" + [pageSize]="service.pageSize"> </ngb-pagination> - <select class="custom-select" style="width: auto" name="pageSize" [(ngModel)]="service.pageSize"> + <select class="custom-select" style="width: auto" name="pageSize" + [(ngModel)]="service.pageSize"> <option [ngValue]="5">5 items per page</option> <option [ngValue]="10">10 items per page</option> <option [ngValue]="15">15 items per page</option> diff --git a/frontend/src/app/card-sortable-table/card-sortable-table.component.ts b/frontend/src/app/card-sortable-table/card-sortable-table.component.ts index d4267cc0..72d9eeaa 100644 --- a/frontend/src/app/card-sortable-table/card-sortable-table.component.ts +++ b/frontend/src/app/card-sortable-table/card-sortable-table.component.ts @@ -7,8 +7,7 @@ import { ViewChildren } from '@angular/core'; import { Observable } from 'rxjs'; -import { Country } from './country'; -import { CountryService } from './country.services'; +import { GermplasmService } from './germplasm.services'; import { NgbdSortableHeader, SortEvent } from './sortable.directive'; @Component({ @@ -18,7 +17,6 @@ import { NgbdSortableHeader, SortEvent } from './sortable.directive'; }) export class CardSortableTableComponent { - countries$: Observable<Country[]>; total$: Observable<number>; @Input() tableHeaders: String[]; @@ -26,8 +24,7 @@ export class CardSortableTableComponent { @ContentChild(TemplateRef) template: TemplateRef<any>; - constructor(public service: CountryService) { - this.countries$ = service.countries$; + constructor(public service: GermplasmService) { this.total$ = service.total$; } diff --git a/frontend/src/app/card-sortable-table/countries.ts b/frontend/src/app/card-sortable-table/countries.ts deleted file mode 100644 index 847d5cf5..00000000 --- a/frontend/src/app/card-sortable-table/countries.ts +++ /dev/null @@ -1,95 +0,0 @@ -import {Country} from './country'; - -export const COUNTRIES: Country[] = [ - { - id: 1, - name: 'Russia', - flag: 'f/f3/Flag_of_Russia.svg', - area: 17075200, - population: 146989754 - }, - { - id: 2, - name: 'France', - flag: 'c/c3/Flag_of_France.svg', - area: 640679, - population: 64979548 - }, - { - id: 3, - name: 'Germany', - flag: 'b/ba/Flag_of_Germany.svg', - area: 357114, - population: 82114224 - }, - { - id: 4, - name: 'Portugal', - flag: '5/5c/Flag_of_Portugal.svg', - area: 92090, - population: 10329506 - }, - { - id: 5, - name: 'Canada', - flag: 'c/cf/Flag_of_Canada.svg', - area: 9976140, - population: 36624199 - }, - { - id: 6, - name: 'Vietnam', - flag: '2/21/Flag_of_Vietnam.svg', - area: 331212, - population: 95540800 - }, - { - id: 7, - name: 'Brazil', - flag: '0/05/Flag_of_Brazil.svg', - area: 8515767, - population: 209288278 - }, - { - id: 8, - name: 'Mexico', - flag: 'f/fc/Flag_of_Mexico.svg', - area: 1964375, - population: 129163276 - }, - { - id: 9, - name: 'United States', - flag: 'a/a4/Flag_of_the_United_States.svg', - area: 9629091, - population: 324459463 - }, - { - id: 10, - name: 'India', - flag: '4/41/Flag_of_India.svg', - area: 3287263, - population: 1324171354 - }, - { - id: 11, - name: 'Indonesia', - flag: '9/9f/Flag_of_Indonesia.svg', - area: 1910931, - population: 263991379 - }, - { - id: 12, - name: 'Tuvalu', - flag: '3/38/Flag_of_Tuvalu.svg', - area: 26, - population: 11097 - }, - { - id: 13, - name: 'China', - flag: 'f/fa/Flag_of_the_People%27s_Republic_of_China.svg', - area: 9596960, - population: 1409517397 - } -]; diff --git a/frontend/src/app/card-sortable-table/country.ts b/frontend/src/app/card-sortable-table/country.ts deleted file mode 100644 index 93930c04..00000000 --- a/frontend/src/app/card-sortable-table/country.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface Country { - id: number; - name: string; - flag: string; - area: number; - population: number; -} diff --git a/frontend/src/app/card-sortable-table/country.services.ts b/frontend/src/app/card-sortable-table/germplasm.services.ts similarity index 65% rename from frontend/src/app/card-sortable-table/country.services.ts rename to frontend/src/app/card-sortable-table/germplasm.services.ts index 9d5eba54..fe5c9c34 100644 --- a/frontend/src/app/card-sortable-table/country.services.ts +++ b/frontend/src/app/card-sortable-table/germplasm.services.ts @@ -1,15 +1,14 @@ import { Injectable, PipeTransform } from '@angular/core'; import { BehaviorSubject, Observable, of, Subject } from 'rxjs'; - -import { Country } from './country'; -import { COUNTRIES } from './countries'; +import { GERMPLASM } from './germplasm'; import { DecimalPipe } from '@angular/common'; import { debounceTime, delay, switchMap, tap } from 'rxjs/operators'; import { SortDirection } from './sortable.directive'; +import { BrapiGermplasm } from '../models/brapi.model'; interface SearchResult { - countries: Country[]; + germplasm: BrapiGermplasm[]; total: number; } @@ -25,29 +24,30 @@ function compare(v1, v2) { return v1 < v2 ? -1 : v1 > v2 ? 1 : 0; } -function sort(countries: Country[], column: string, direction: string): Country[] { +function sort(germplasm: BrapiGermplasm[], column: string, direction: string): BrapiGermplasm[] { if (direction === '') { - return countries; + return germplasm; } else { - return [...countries].sort((a, b) => { + return [...germplasm].sort((a, b) => { const res = compare(a[column], b[column]); return direction === 'asc' ? res : -res; }); } } -function matches(country: Country, term: string, pipe: PipeTransform) { - return country.name.toLowerCase().includes(term.toLowerCase()) - || pipe.transform(country.area).includes(term) - || pipe.transform(country.population).includes(term); +function matches(germplasm: BrapiGermplasm, term: string, pipe: PipeTransform) { + return germplasm.germplasmDbId.toLowerCase().includes(term.toLowerCase()) + || germplasm.accessionNumber.toLowerCase().includes(term.toLowerCase()) + || germplasm.instituteName.toLowerCase().includes(term.toLowerCase()) + || germplasm.commonCropName.toLowerCase().includes(term.toLowerCase()); } @Injectable({providedIn: 'root'}) -export class CountryService { +export class GermplasmService { private _loading$ = new BehaviorSubject<boolean>(true); private _search$ = new Subject<void>(); - private _countries$ = new BehaviorSubject<Country[]>([]); + private _data$ = new BehaviorSubject<any[]>([]); private _total$ = new BehaviorSubject<number>(0); private _state: State = { @@ -66,14 +66,14 @@ export class CountryService { delay(200), tap(() => this._loading$.next(false)) ).subscribe(result => { - this._countries$.next(result.countries); + this._data$.next(result['germplasm']); this._total$.next(result.total); }); this._search$.next(); } - get countries$() { return this._countries$.asObservable(); } + get data$() { return this._data$.asObservable(); } get total$() { return this._total$.asObservable(); } get loading$() { return this._loading$.asObservable(); } get page() { return this._state.page; } @@ -91,19 +91,19 @@ export class CountryService { this._search$.next(); } - private _search(): Observable<SearchResult> { + private _search(): Observable<any> { const {sortColumn, sortDirection, pageSize, page, searchTerm} = this._state; // 1. sort - let countries = sort(COUNTRIES, sortColumn, sortDirection); + let data = sort(GERMPLASM, sortColumn, sortDirection); // 2. filter - countries = countries.filter(country => matches(country, searchTerm, this.pipe)); - const total = countries.length; + data = data.filter(data => matches(data, searchTerm, this.pipe)); + const total = data.length; // 3. paginate - countries = countries.slice((page - 1) * pageSize, (page - 1) * pageSize + pageSize); - return of({countries, total}); + data = data.slice((page - 1) * pageSize, (page - 1) * pageSize + pageSize); + return of({germplasm: data, total}); } } diff --git a/frontend/src/app/card-sortable-table/germplasm.ts b/frontend/src/app/card-sortable-table/germplasm.ts new file mode 100644 index 00000000..ba751687 --- /dev/null +++ b/frontend/src/app/card-sortable-table/germplasm.ts @@ -0,0 +1,90 @@ +import { BrapiGermplasm } from '../models/brapi.model'; + +export const GERMPLASM: BrapiGermplasm[] = [ + { + germplasmDbId: "aHR0cHM6Ly9kb2kub3JnLzEwLjE1NDU0L0hET0Y4Qw==", + defaultDisplayName: "FANNETTE", + accessionNumber: "10936", + germplasmName: "FANNETTE", + pedigree: "RIKA/VOLLA//EMIR", + seedSource: null, + synonyms: [ + "FRA051:4278" + ], + commonCropName: "Barley", + instituteCode: "FRA040", + instituteName: "UMR Génétique, Diversité et Ecophysiologie des Céréales, INRA-Clermont", + biologicalStatusOfAccessionCode: "Advanced or improved cultivar", + countryOfOriginCode: null, + typeOfGermplasmStorageCode: null, + taxonIds: null, + donors: [ + { + donorGermplasmPUI: null, + donorAccessionNumber: "15955DA", + donorInstituteCode: "FRA018", + donationDate: null, + } + ], + genus: "Hordeum", + species: "vulgare", + speciesAuthority: null, + subtaxa: "subsp. vulgare", + subtaxaAuthority: null, + acquisitionDate: null, + germplasmPUI: "https://doi.org/10.15454/HDOF8C", + documentationURL: null + }, + { + germplasmDbId: "dXJuOlVSR0kvZ25waXNfcHVpJTNBdW5rbm93biUzQVdoZWF0JTNBUkUwMDExOQ==", + defaultDisplayName: "RE00119", + accessionNumber: "RE00119", + germplasmName: "RE00119", + pedigree: null, + seedSource: null, + synonyms: null, + commonCropName: "Wheat", + instituteCode: "FRA015", + instituteName: "INRA", + biologicalStatusOfAccessionCode: null, + countryOfOriginCode: null, + typeOfGermplasmStorageCode: null, + taxonIds: null, + donors: null, + genus: "Triticum", + species: "aestivum", + speciesAuthority: null, + subtaxa: "subsp. aestivum", + subtaxaAuthority: null, + acquisitionDate: null, + germplasmPUI: "urn:URGI/gnpis_pui%3Aunknown%3AWheat%3ARE00119", + documentationURL: null + }, + { + germplasmDbId: "aHR0cHM6Ly9kb2kub3JnLzEwLjE1NDU0L0VVMk1LWA==", + defaultDisplayName: "AKER_11467", + accessionNumber: "PI 26545", + germplasmName: "AKER_11467", + pedigree: null, + seedSource: null, + synonyms: [ + "PI 26545" + ], + commonCropName: "Beta vulgaris vulgaris cv. Sugar beet", + instituteCode: "USA126", + instituteName: "Germplasm Resources Information Network", + biologicalStatusOfAccessionCode: null, + countryOfOriginCode: null, + typeOfGermplasmStorageCode: null, + taxonIds: null, + donors: null, + genus: "Beta", + species: "vulgaris", + speciesAuthority: null, + subtaxa: "subsp. vulgaris cv. Sugar beet", + subtaxaAuthority: null, + acquisitionDate: null, + germplasmPUI: "https://doi.org/10.15454/EU2MKX", + documentationURL: null + } +]; diff --git a/frontend/src/app/form/suggestion-field/suggestion-field.component.ts b/frontend/src/app/form/suggestion-field/suggestion-field.component.ts index bbac52ab..91484320 100644 --- a/frontend/src/app/form/suggestion-field/suggestion-field.component.ts +++ b/frontend/src/app/form/suggestion-field/suggestion-field.component.ts @@ -1,7 +1,10 @@ import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; -import { BehaviorSubject, merge, Observable, of, Subject } from 'rxjs'; +import { BehaviorSubject, merge, Observable, Subject } from 'rxjs'; import { FormControl } from '@angular/forms'; -import { NgbTypeahead, NgbTypeaheadSelectItemEvent } from '@ng-bootstrap/ng-bootstrap'; +import { + NgbTypeahead, + NgbTypeaheadSelectItemEvent +} from '@ng-bootstrap/ng-bootstrap'; import { GnpisService } from '../../gnpis.service'; import { debounceTime, filter, map, switchMap } from 'rxjs/operators'; import { DataDiscoveryCriteria } from '../../models/data-discovery.model'; @@ -25,7 +28,6 @@ export class SuggestionFieldComponent implements OnInit { input = new FormControl(); @ViewChild('inputElement') inputElement: ElementRef; - @ViewChild('typeahead') typeahead: NgbTypeahead; private localCriteria: DataDiscoveryCriteria = null; @@ -60,18 +62,18 @@ export class SuggestionFieldComponent implements OnInit { const clicksWithClosedPopup$ = this.click$.pipe( filter(() => !this.typeahead.isPopupOpen()) ); - const text2$ = text$.pipe( + /*const text2$ = text$.pipe( filter(term => term.length >= 2) - ); + );*/ // When new text or focus or click with popup closed - return merge(text2$, clicksWithClosedPopup$).pipe( + return merge(text$, clicksWithClosedPopup$).pipe( debounceTime(250), switchMap((term: string) => { - if (!term) { + /*if (!term) { // No term to search return of([]); - } + }*/ // Otherwise, we fetch new suggestions return this.fetchSuggestion(term); diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html index 012c70d0..ad8fc54e 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html @@ -1,17 +1,42 @@ -<faidare-card-sortable-table -[tableHeaders]="['id','name', 'area', 'population']" -[rows]="countries"> +<!--<faidare-germplasm-form + inputId="accessions" + criteriaField="accessions" + [criteria$]="criteria"> +</faidare-germplasm-form>--> +<ng-container> + <h3 class="mb-4"> + Germplasm result page + </h3> - <ng-template let-row> - <tr> - <th scope="row">{{ row.id }}</th> - <td> - <img [src]="'https://upload.wikimedia.org/wikipedia/commons/' + row.flag" class="mr-2" style="width: 20px"> - <ngb-highlight [result]="row.name" [term]="service.searchTerm"></ngb-highlight> - </td> - <td><ngb-highlight [result]="row.area | number" [term]="service.searchTerm"></ngb-highlight></td> - <td><ngb-highlight [result]="row.population | number" [term]="service.searchTerm"></ngb-highlight></td> - </tr> - </ng-template> + <div class="col-sm-8 mb-3"> + <faidare-suggestion-field + inputId="accessions" + criteriaField="accessions" + [criteria$]="Germplasmcriteria$" + placeholder="Search germplasm name"> + </faidare-suggestion-field> + </div> -</faidare-card-sortable-table> + <faidare-card-sortable-table + [tableHeaders]="['germplasmName', 'accessionNumber', 'commonCropName']" + [rows]="germplasm"> + + <ng-template let-row> + <tr> + <td> + <ngb-highlight [result]="row.germplasmName" + [term]="service2.searchTerm"></ngb-highlight> + </td> + <td> + <ngb-highlight [result]="row.accessionNumber" + [term]="service2.searchTerm"></ngb-highlight> + </td> + <td> + <ngb-highlight [result]="row.commonCropName" + [term]="service2.searchTerm"></ngb-highlight> + </td> + </tr> + </ng-template> + + </faidare-card-sortable-table> +</ng-container> diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts index 3409914a..69071b7a 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts @@ -1,6 +1,14 @@ import { Component, OnInit } from '@angular/core'; -import { CountryService } from '../card-sortable-table/country.services'; -import { Country } from '../card-sortable-table/country'; +import { GermplasmService } from '../card-sortable-table/germplasm.services'; + +import { BrapiService } from '../brapi.service'; +import { + BrapiCriteriaUtils, + BrapiGermplasm, + GermplasmCriteria +} from '../models/brapi.model'; +import { BehaviorSubject } from 'rxjs'; +import { filter } from 'rxjs/operators'; @Component({ selector: 'faidare-germplasm-result-page', @@ -10,14 +18,38 @@ import { Country } from '../card-sortable-table/country'; export class GermplasmResultPageComponent implements OnInit { - countries: Country[]; + germplasm: BrapiGermplasm[]; + Germplasmcriteria$ = new BehaviorSubject<GermplasmCriteria> (BrapiCriteriaUtils.emptyCriteria()); + private localCriteria: GermplasmCriteria = BrapiCriteriaUtils.emptyCriteria(); - constructor(public service: CountryService) { } + constructor(public service2: GermplasmService, public service: BrapiService) { } ngOnInit() { - this.service.countries$.subscribe(countries => { - this.countries = countries; - }); + this.Germplasmcriteria$.pipe(filter(c => c !== this.localCriteria)) + .subscribe(newCriteria => { + newCriteria.accessionNumbers = ["10936"]; + for (const field in newCriteria){ + if (newCriteria[field]){ + // this.localCriteria[field] = newCriteria[field]; + } + } + this.searchGermplasm(); + + }); + + this.localCriteria.accessionNumbers = ["10936"]; + this.service2.data$.subscribe(germplasm =>{ + this.germplasm = germplasm; + }) + + } + + searchGermplasm() { + this.service.germplasmSearch(this.localCriteria) + .subscribe(response => { + this.germplasm = response.result.data; + }); } } + diff --git a/frontend/src/app/models/brapi.model.ts b/frontend/src/app/models/brapi.model.ts index c758338c..539af06c 100644 --- a/frontend/src/app/models/brapi.model.ts +++ b/frontend/src/app/models/brapi.model.ts @@ -1,5 +1,33 @@ import * as schema from './schema.org.model'; +export interface GermplasmCriteria { + accessionNumbers: string[]; + germplasmDbIds: string[]; + germplasmGenus: string[]; + germplasmNames: string[]; + germplasmPUIs: string[]; + germplasmSpecies: string[]; + page: number; + pageSize: number; +} + +export const DEFAULT_PAGE_SIZE = 10; + +export class BrapiCriteriaUtils { + static emptyCriteria(): GermplasmCriteria { + return { + accessionNumbers: null, + germplasmDbIds: null, + germplasmGenus: null, + germplasmNames: null, + germplasmPUIs: null, + germplasmSpecies: null, + page: 0, + pageSize: DEFAULT_PAGE_SIZE + }; + } +} + export interface BrapiMetaData { pagination: { pageSize: number; @@ -84,7 +112,7 @@ export interface BrapiAdditionalInfo { } -export interface BrapiObservationVariable extends BrapiHasDocumentationURL { +export interface BrapiObservationVariable extends BrapiHasDocumentationURL { observationVariableDbId: string; contextOfUse: string[]; institution: string; @@ -192,7 +220,7 @@ export interface BrapiGermplasmAttributes { data: BrapiAttributeData[]; } -export interface BrapiAttributeData { +export interface BrapiAttributeData { attributeDbId: string; attributeName: string; attributeCode: string; diff --git a/frontend/src/app/result-page/facets/facets.component.ts b/frontend/src/app/result-page/facets/facets.component.ts index b35b60e0..f7c1d92d 100644 --- a/frontend/src/app/result-page/facets/facets.component.ts +++ b/frontend/src/app/result-page/facets/facets.component.ts @@ -1,5 +1,8 @@ import { Component, Input, OnInit } from '@angular/core'; -import { DataDiscoveryCriteria, DataDiscoveryFacet } from '../../models/data-discovery.model'; +import { + DataDiscoveryCriteria, + DataDiscoveryFacet +} from '../../models/data-discovery.model'; import { FormControl, FormGroup } from '@angular/forms'; import { BehaviorSubject } from 'rxjs'; import { filter } from 'rxjs/operators'; @@ -57,9 +60,7 @@ export class FacetsComponent implements OnInit { showANDHideAdvanceGermplasmSearch(typeList: String[]) { const facetIsTypes = this.facet.field === 'types'; - const onlyGermplasmSelected = typeList.length === 1 && typeList.includes('Germplasm'); - this.displayAdvanceGermplasmSearchButton = facetIsTypes && onlyGermplasmSelected; + const GermplasmSelected = typeList.includes('Germplasm'); + this.displayAdvanceGermplasmSearchButton = facetIsTypes && GermplasmSelected; } - - } -- GitLab From 19bd3a372bedb54dae90cc810328aa6446d4fb49 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Wed, 4 Dec 2019 15:16:57 +0100 Subject: [PATCH 06/35] feat: Put the germplasm result table on the result page with a switch button to change the view. --- .../faidare/v1/GnpISGermplasmController.java | 56 ++++- .../FaidareGermplasmPOSTShearchCriteria.java | 190 ++++++++++++++++ .../data/germplasm/ExtendedGermplasm.java | 6 + .../domain/data/germplasm/GermplasmVO.java | 18 ++ .../response/GermplasmSearchResponse.java | 25 +++ .../domain/response/ApiResponseFactory.java | 10 + .../response/GermplasmSearchResponseImpl.java | 24 ++ .../elasticsearch/query/ESQueryFactory.java | 2 + .../query/impl/ESGenericQueryFactory.java | 80 ++++++- .../es/DataDiscoveryRepositoryImpl.java | 2 +- .../repository/es/GermplasmRepository.java | 6 + .../es/GermplasmRepositoryImpl.java | 8 + .../faidare/service/es/GermplasmService.java | 11 + .../service/es/GermplasmServiceImpl.java | 183 +++++++++++++++ frontend/package.json | 7 +- frontend/src/app/app.module.ts | 24 +- frontend/src/app/brapi.service.ts | 12 +- .../card-sortable-table.component.html | 65 ------ .../card-sortable-table.component.scss | 0 .../card-sortable-table.component.spec.ts | 25 --- .../card-sortable-table.component.ts | 47 ---- .../card-sortable-table/germplasm.services.ts | 109 --------- .../src/app/card-sortable-table/germplasm.ts | 90 -------- .../card-sortable-table/sortable.directive.ts | 29 --- .../app/card-table/card-table.component.html | 32 +-- frontend/src/app/form/form.component.html | 3 + frontend/src/app/form/form.component.ts | 1 + .../suggestion-field.component.ts | 6 + .../germplasm-card.component.ts | 7 +- .../germplasm-result-page.component.html | 130 ++++++++--- .../germplasm-result-page.component.scss | 37 +++ .../germplasm-result-page.component.ts | 210 +++++++++++++++--- frontend/src/app/gnpis.service.spec.ts | 52 ++++- frontend/src/app/gnpis.service.ts | 77 +++++-- frontend/src/app/models/brapi.model.ts | 11 + .../src/app/models/data-discovery.model.ts | 56 +++++ frontend/src/app/models/gnpis.model.ts | 33 +++ .../result-page/facets/facets.component.html | 26 ++- .../result-page/facets/facets.component.scss | 69 ++++++ .../result-page/facets/facets.component.ts | 121 ++++++++-- .../result-page/result-page.component.html | 77 ++++--- .../app/result-page/result-page.component.ts | 43 +++- .../src/assets/faidare/images/csv-logo.png | Bin 0 -> 32999 bytes frontend/src/tslint.json | 2 +- 44 files changed, 1471 insertions(+), 551 deletions(-) create mode 100644 backend/src/main/java/fr/inra/urgi/faidare/domain/criteria/FaidareGermplasmPOSTShearchCriteria.java create mode 100644 backend/src/main/java/fr/inra/urgi/faidare/domain/datadiscovery/response/GermplasmSearchResponse.java create mode 100644 backend/src/main/java/fr/inra/urgi/faidare/domain/response/GermplasmSearchResponseImpl.java delete mode 100644 frontend/src/app/card-sortable-table/card-sortable-table.component.html delete mode 100644 frontend/src/app/card-sortable-table/card-sortable-table.component.scss delete mode 100644 frontend/src/app/card-sortable-table/card-sortable-table.component.spec.ts delete mode 100644 frontend/src/app/card-sortable-table/card-sortable-table.component.ts delete mode 100644 frontend/src/app/card-sortable-table/germplasm.services.ts delete mode 100644 frontend/src/app/card-sortable-table/germplasm.ts delete mode 100644 frontend/src/app/card-sortable-table/sortable.directive.ts create mode 100644 frontend/src/assets/faidare/images/csv-logo.png diff --git a/backend/src/main/java/fr/inra/urgi/faidare/api/faidare/v1/GnpISGermplasmController.java b/backend/src/main/java/fr/inra/urgi/faidare/api/faidare/v1/GnpISGermplasmController.java index 7e6a21ff..5658be57 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/api/faidare/v1/GnpISGermplasmController.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/api/faidare/v1/GnpISGermplasmController.java @@ -3,25 +3,28 @@ package fr.inra.urgi.faidare.api.faidare.v1; import com.google.common.base.Strings; import fr.inra.urgi.faidare.api.BadRequestException; import fr.inra.urgi.faidare.api.NotFoundException; +import fr.inra.urgi.faidare.domain.criteria.FaidareGermplasmPOSTShearchCriteria; import fr.inra.urgi.faidare.domain.criteria.GermplasmGETSearchCriteria; import fr.inra.urgi.faidare.domain.criteria.GermplasmPOSTSearchCriteria; import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO; +import fr.inra.urgi.faidare.domain.datadiscovery.response.GermplasmSearchResponse; import fr.inra.urgi.faidare.domain.response.PaginatedList; import fr.inra.urgi.faidare.service.es.GermplasmService; +import fr.inra.urgi.faidare.utils.StringFunctions; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.FileSystemResource; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; import java.io.File; +import java.io.UnsupportedEncodingException; import java.util.Collections; +import java.util.LinkedHashSet; -import static org.springframework.web.bind.annotation.RequestMethod.GET; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @Api(tags = {"FAIDARE API"}, description = "Extended FAIDARE API") @RestController @@ -89,8 +92,9 @@ public class GnpISGermplasmController { * resp.setContentType("application/zip"); * </pre> */ - @RequestMapping(value = "/csv", method = GET, produces = "text/csv") - public FileSystemResource export(GermplasmPOSTSearchCriteria criteria, HttpServletResponse response) { + @PostMapping(value = "/csv", produces = "text/csv", consumes = APPLICATION_JSON_VALUE) + public FileSystemResource export(@RequestBody @Valid GermplasmPOSTSearchCriteria criteria, HttpServletResponse response) { + try { File exportFile = germplasmService.exportCSV(criteria); response.setHeader("Content-Disposition", "attachment; filename=germplasm.gnpis.csv"); @@ -101,4 +105,42 @@ public class GnpISGermplasmController { } } + @PostMapping(value = "/germplasm-list-csv", produces = "text/csv", consumes = APPLICATION_JSON_VALUE) + public FileSystemResource export(@RequestBody @Valid FaidareGermplasmPOSTShearchCriteria criteria, HttpServletResponse response) { + + try { + File exportFile = germplasmService.exportListGermplasmCSV(criteria); + response.setHeader("Content-Disposition", "attachment; filename=germplasm.gnpis.csv"); + return new FileSystemResource(exportFile); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("An error occurred when exporting germplasm: " + e.getMessage() + ".", e); + } + } + + @ApiOperation("Search list of germplasm") + @PostMapping(value = "/search", consumes = APPLICATION_JSON_VALUE) + public GermplasmSearchResponse germplasmSearch(@RequestBody @Valid FaidareGermplasmPOSTShearchCriteria criteria) { + try { + return germplasmService.esShouldFind(criteria); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(); + } + } + + @ApiOperation("Suggest germplasm document field values") + @PostMapping(value = "/suggest", consumes = APPLICATION_JSON_VALUE) + public LinkedHashSet<String> germplasmSuggest( + @RequestParam String field, + @RequestParam(required = false) String text, + @RequestParam(required = false) Integer fetchSize, + @RequestBody(required = false) @Valid FaidareGermplasmPOSTShearchCriteria criteria) + throws UnsupportedEncodingException { + if (fetchSize == null) { + fetchSize = Integer.MAX_VALUE; + } + return germplasmService.suggest(field, StringFunctions.asUTF8(text), fetchSize, criteria); + + } } diff --git a/backend/src/main/java/fr/inra/urgi/faidare/domain/criteria/FaidareGermplasmPOSTShearchCriteria.java b/backend/src/main/java/fr/inra/urgi/faidare/domain/criteria/FaidareGermplasmPOSTShearchCriteria.java new file mode 100644 index 00000000..e697867a --- /dev/null +++ b/backend/src/main/java/fr/inra/urgi/faidare/domain/criteria/FaidareGermplasmPOSTShearchCriteria.java @@ -0,0 +1,190 @@ +package fr.inra.urgi.faidare.domain.criteria; + +import fr.inra.urgi.faidare.domain.criteria.base.SortCriteria; +import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO; +import fr.inra.urgi.faidare.elasticsearch.criteria.annotation.CriteriaForDocument; +import fr.inra.urgi.faidare.elasticsearch.criteria.annotation.DocumentPath; +import fr.inra.urgi.faidare.elasticsearch.criteria.annotation.NoDocumentMapping; + +import java.util.List; + +@CriteriaForDocument(GermplasmVO.class) +public class FaidareGermplasmPOSTShearchCriteria extends GermplasmPOSTSearchCriteria implements SortCriteria { + + @DocumentPath(value = "synonyms") + private List<String> synonyms; + + /*@DocumentPath(value = {"panel", "name"}, objectsValue = CollPopVO.class) + private List<String> panel; + + @DocumentPath(value = {"collection", "name"}, objectsValue = CollPopVO.class) + private List<String> collection; + + @DocumentPath(value = {"population", "name"}, objectsValue = CollPopVO.class) + private List<String> population;*/ + + @DocumentPath(value = "commonCropName") + private List<String> commonCropName; + + @DocumentPath(value = "species") + private List<String> species; + + @DocumentPath(value = "genus") + private List<String> genus; + + @DocumentPath(value = "genusSpecies") + private List<String> genusSpecies; + + @DocumentPath(value = "subtaxa") + private List<String> subtaxa; + + @DocumentPath(value = "genusSpeciesSubtaxa") + private List<String> genusSpeciesSubtaxa; + + @DocumentPath("taxonSynonyms") + private List<String> taxonSynonyms; + + @DocumentPath(value = {"holdingInstitute", "organisation"}) + private List<String> holdingInstitute; + + @DocumentPath("sourceUri") + private List<String> sources; + + @DocumentPath("biologicalStatusOfAccessionCode") + private List<String> biologicalStatus; + + @DocumentPath("geneticNature") + private List<String> geneticNature; + + @DocumentPath("countryOfOriginCode") + private List<String> country; + + @NoDocumentMapping + private List<String> facetFields; + + @NoDocumentMapping + private String sortBy = null;// = "schema:name"; + + @NoDocumentMapping + private String sortOrder = null;// = SortOrder.ASC.name(); + + + + + /*public List<String> getPanel() { return panel; } + + public void setPanel(List<String> panel) { this.panel = panel; } + + public List<String> getCollection() { return collection; } + + public void setCollection(List<String> collection) { this.collection = collection; } + + public List<String> getPopulation() { return population; } + + public void setPopulation(List<String> population) { this.population = population; }*/ + + public List<String> getCommonCropName() { return commonCropName; } + + public void setCommonCropName(List<String> commonCropName) { + this.commonCropName = commonCropName; + } + + public List<String> getSpecies() { return species; } + + public void setSpecies(List<String> species) { this.species = species; } + + public List<String> getGenus() { return genus; } + + public void setGenus(List<String> genus) { this.genus = genus; } + + public List<String> getGenusSpecies() { return genusSpecies; } + + public void setGenusSpecies(List<String> genusSpecies) { + this.genusSpecies = genusSpecies; + } + + public List<String> getSubtaxa() { return subtaxa; } + + public void setSubtaxa(List<String> subtaxa) { this.subtaxa = subtaxa; } + + public List<String> getGenusSpeciesSubtaxa() { return genusSpeciesSubtaxa; } + + public void setGenusSpeciesSubtaxa(List<String> genusSpeciesSubtaxa) { + this.genusSpeciesSubtaxa = genusSpeciesSubtaxa; + } + + public List<String> getTaxonSynonyms() { return taxonSynonyms; } + + public void setTaxonSynonyms(List<String> taxonSynonyms) { + this.taxonSynonyms = taxonSynonyms; + } + + public List<String> getSynonyms() { return synonyms; } + + public void setSynonyms(List<String> synonyms) { this.synonyms = synonyms; } + + public List<String> getHoldingInstitute() { + return holdingInstitute; + } + + public void setHoldingInstitute(List<String> holdingInstitute) { + this.holdingInstitute = holdingInstitute; + } + + public List<String> getSources() { + return sources; + } + + public void setSources(List<String> sources) { + this.sources = sources; + } + + public List<String> getBiologicalStatus() { return biologicalStatus; } + + public void setBiologicalStatus(List<String> biologicalStatus) { + this.biologicalStatus = biologicalStatus; + } + + public List<String> getGeneticNature() { + return geneticNature; + } + + public void setGeneticNature(List<String> geneticNature) { + this.geneticNature = geneticNature; + } + + public List<String> getCountry() { return country; } + + public void setCountry(List<String> country) { + this.country = country; + } + + + public void setSortBy(String sortBy) { + this.sortBy = sortBy; + } + + + public void setSortOrder(String sortOrder) { + this.sortOrder = sortOrder; + } + + @Override + public String getSortBy() { + return sortBy; + } + + @Override + public String getSortOrder() { + return sortOrder; + } + + + public List<String> getFacetFields() { + return facetFields; + } + + public void setFacetFields(List<String> facetFields) { + this.facetFields = facetFields; + } +} diff --git a/backend/src/main/java/fr/inra/urgi/faidare/domain/data/germplasm/ExtendedGermplasm.java b/backend/src/main/java/fr/inra/urgi/faidare/domain/data/germplasm/ExtendedGermplasm.java index 4f2c1565..c0d850ca 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/domain/data/germplasm/ExtendedGermplasm.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/domain/data/germplasm/ExtendedGermplasm.java @@ -23,6 +23,12 @@ public interface ExtendedGermplasm extends BrapiGermplasm, GnpISInternal { @JsonView(JSONView.GnpISFields.class) String getTaxonComment(); + @JsonView(JSONView.GnpISFields.class) + public String getGenusSpecies(); + + @JsonView(JSONView.GnpISFields.class) + public String getGenusSpeciesSubtaxa(); + @JsonView(JSONView.GnpISFields.class) String getGeneticNature(); diff --git a/backend/src/main/java/fr/inra/urgi/faidare/domain/data/germplasm/GermplasmVO.java b/backend/src/main/java/fr/inra/urgi/faidare/domain/data/germplasm/GermplasmVO.java index 6e3312fb..f6954e78 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/domain/data/germplasm/GermplasmVO.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/domain/data/germplasm/GermplasmVO.java @@ -48,8 +48,10 @@ public class GermplasmVO private String genus; private String species; + private String genusSpecies; private String speciesAuthority; private String subtaxa; + private String genusSpeciesSubtaxa; private String subtaxaAuthority; private String acquisitionDate; @@ -292,6 +294,13 @@ public class GermplasmVO this.species = species; } + @Override + public String getGenusSpecies() { return genusSpecies; } + + public void setGenusSpecies(String genusSpecies) { + this.genusSpecies = genusSpecies; + } + @Override public String getSpeciesAuthority() { return speciesAuthority; @@ -310,6 +319,15 @@ public class GermplasmVO this.subtaxa = subtaxa; } + @Override + public String getGenusSpeciesSubtaxa() { + return genusSpeciesSubtaxa; + } + + public void setGenusSpeciesSubtaxa(String genusSpeciesSubtaxa) { + this.genusSpeciesSubtaxa = genusSpeciesSubtaxa; + } + @Override public String getSubtaxaAuthority() { return subtaxaAuthority; diff --git a/backend/src/main/java/fr/inra/urgi/faidare/domain/datadiscovery/response/GermplasmSearchResponse.java b/backend/src/main/java/fr/inra/urgi/faidare/domain/datadiscovery/response/GermplasmSearchResponse.java new file mode 100644 index 00000000..1daacd4e --- /dev/null +++ b/backend/src/main/java/fr/inra/urgi/faidare/domain/datadiscovery/response/GermplasmSearchResponse.java @@ -0,0 +1,25 @@ +package fr.inra.urgi.faidare.domain.datadiscovery.response; + +import com.fasterxml.jackson.annotation.JsonView; +import fr.inra.urgi.faidare.domain.JSONView; +import fr.inra.urgi.faidare.domain.brapi.v1.response.BrapiData; +import fr.inra.urgi.faidare.domain.brapi.v1.response.BrapiListResponse; +import fr.inra.urgi.faidare.domain.brapi.v1.response.BrapiMetadata; +import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO; +import fr.inra.urgi.faidare.domain.datadiscovery.data.Facet; + +import java.util.List; + +public interface GermplasmSearchResponse extends BrapiListResponse<GermplasmVO> { + + @Override + @JsonView(JSONView.GnpISFields.class) + BrapiMetadata getMetadata(); + + @Override + @JsonView(JSONView.GnpISFields.class) + BrapiData<GermplasmVO> getResult(); + + @JsonView(JSONView.GnpISFields.class) + List<? extends Facet> getFacets(); +} diff --git a/backend/src/main/java/fr/inra/urgi/faidare/domain/response/ApiResponseFactory.java b/backend/src/main/java/fr/inra/urgi/faidare/domain/response/ApiResponseFactory.java index a8a26095..b51e1123 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/domain/response/ApiResponseFactory.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/domain/response/ApiResponseFactory.java @@ -2,9 +2,11 @@ package fr.inra.urgi.faidare.domain.response; import fr.inra.urgi.faidare.api.brapi.v1.exception.BrapiPaginationException; import fr.inra.urgi.faidare.domain.brapi.v1.response.*; +import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO; import fr.inra.urgi.faidare.domain.datadiscovery.data.DataDiscoveryDocument; import fr.inra.urgi.faidare.domain.datadiscovery.data.FacetImpl; import fr.inra.urgi.faidare.domain.datadiscovery.response.DataDiscoveryResponse; +import fr.inra.urgi.faidare.domain.datadiscovery.response.GermplasmSearchResponse; import org.springframework.http.HttpStatus; import java.util.ArrayList; @@ -114,4 +116,12 @@ public class ApiResponseFactory { BrapiMetadata metadata = ApiResponseFactory.createMetadata(pagination, null); return new DataDiscoveryResponseImpl(metadata, results, facets); } + + /** + * Create germplasm search response (brapi list response with facets) + */ + public static GermplasmSearchResponse createGermplasmListResponseWithFacets(Pagination pagination, List<GermplasmVO> results, List<FacetImpl> facets) { + BrapiMetadata metadata = ApiResponseFactory.createMetadata(pagination, null); + return new GermplasmSearchResponseImpl(metadata, results, facets); + } } diff --git a/backend/src/main/java/fr/inra/urgi/faidare/domain/response/GermplasmSearchResponseImpl.java b/backend/src/main/java/fr/inra/urgi/faidare/domain/response/GermplasmSearchResponseImpl.java new file mode 100644 index 00000000..43013019 --- /dev/null +++ b/backend/src/main/java/fr/inra/urgi/faidare/domain/response/GermplasmSearchResponseImpl.java @@ -0,0 +1,24 @@ +package fr.inra.urgi.faidare.domain.response; + +import fr.inra.urgi.faidare.domain.brapi.v1.response.BrapiMetadata; +import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO; +import fr.inra.urgi.faidare.domain.datadiscovery.data.Facet; +import fr.inra.urgi.faidare.domain.datadiscovery.data.FacetImpl; +import fr.inra.urgi.faidare.domain.datadiscovery.response.GermplasmSearchResponse; + +import java.util.List; + +public class GermplasmSearchResponseImpl extends ApiListResponseImpl<GermplasmVO> implements GermplasmSearchResponse { + + private final List<FacetImpl> facets; + + public GermplasmSearchResponseImpl(BrapiMetadata metadata, List<GermplasmVO> result, List<FacetImpl> facets) { + super(metadata, result); + this.facets = facets; + } + + @Override + public List<? extends Facet> getFacets() { + return facets; + } +} diff --git a/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/ESQueryFactory.java b/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/ESQueryFactory.java index 3b20638a..d92d23ef 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/ESQueryFactory.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/ESQueryFactory.java @@ -12,4 +12,6 @@ public interface ESQueryFactory<C> { */ QueryBuilder createQuery(C criteria); + QueryBuilder createEsShouldQuery(C criteria); + } diff --git a/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactory.java b/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactory.java index e6cbe313..f35b89df 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactory.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactory.java @@ -55,6 +55,24 @@ public class ESGenericQueryFactory<C> implements ESQueryFactory<C> { } } + @Override + public QueryBuilder createEsShouldQuery(C criteria) { + try { + CriteriaMapping voMappingToCriteria = AnnotatedCriteriaMapper.getMapping(criteria.getClass()); + DocumentMetadata<?> documentMetadata = voMappingToCriteria.getDocumentMetadata(); + + List<QueryBuilder> queries = createQueryFromMapping(criteria, null, null, voMappingToCriteria, documentMetadata); + + if (!queries.isEmpty()) { + return orQueries(queries); + } else { + return matchAllQuery(); + } + } catch (Exception e) { + throw new ESQueryGenerationException(e); + } + } + /** * Same as {@link ESGenericQueryFactory#createQuery(Object)} but with a list of document fields to exclude from query */ @@ -76,6 +94,24 @@ public class ESGenericQueryFactory<C> implements ESQueryFactory<C> { } } + public QueryBuilder createEsShouldQueryExcludingFields(C criteria, String... excludeDocumentFields) { + try { + CriteriaMapping voMappingToCriteria = AnnotatedCriteriaMapper.getMapping(criteria.getClass()); + DocumentMetadata<?> documentMetadata = voMappingToCriteria.getDocumentMetadata(); + Set<String> excludedDocumentFields = ImmutableSet.copyOf(excludeDocumentFields); + + List<QueryBuilder> queries = createQueryFromMapping(criteria, null, excludedDocumentFields, voMappingToCriteria, documentMetadata); + + if (!queries.isEmpty()) { + return orQueries(queries); + } else { + return matchAllQuery(); + } + } catch (Exception e) { + throw new ESQueryGenerationException(e); + } + } + /** * Same as {@link ESGenericQueryFactory#createQuery(Object)} but with a list of document fields to include from query (excluding all others) */ @@ -97,6 +133,24 @@ public class ESGenericQueryFactory<C> implements ESQueryFactory<C> { } } + public QueryBuilder createEsShouldQueryIncludingFields(C criteria, String... includeDocumentFields) { + try { + CriteriaMapping voMappingToCriteria = AnnotatedCriteriaMapper.getMapping(criteria.getClass()); + DocumentMetadata<?> documentMetadata = voMappingToCriteria.getDocumentMetadata(); + Set<String> includedDocumentFields = ImmutableSet.copyOf(includeDocumentFields); + + List<QueryBuilder> queries = createQueryFromMapping(criteria, includedDocumentFields, null, voMappingToCriteria, documentMetadata); + + if (!queries.isEmpty()) { + return orQueries(queries); + } else { + return matchAllQuery(); + } + } catch (Exception e) { + throw new ESQueryGenerationException(e); + } + } + /** * Generate a query from a criteria */ @@ -287,7 +341,7 @@ public class ESGenericQueryFactory<C> implements ESQueryFactory<C> { /** * Combine queries into a bool must query * - * @return null if not queries given; the first query if only only query is given; a bool query otherwise + * @return null if not queries given; the first query if only one query is given; a bool query otherwise */ public static QueryBuilder andQueries(List<QueryBuilder> queries) { if (queries == null || queries.isEmpty()) { @@ -306,4 +360,28 @@ public class ESGenericQueryFactory<C> implements ESQueryFactory<C> { public static QueryBuilder andQueries(QueryBuilder... queries) { return andQueries(Arrays.asList(queries)); } + + + /** + * Combine queries into a bool should query + * + * @return null if not queries given; the first query if only one query is given; a bool query otherwise + */ + public static QueryBuilder orQueries(List<QueryBuilder> queries) { + if (queries == null || queries.isEmpty()) { + return null; + } else if (queries.size() == 1) { + return queries.get(0); + } else { + BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); + for (QueryBuilder query : queries) { + boolQueryBuilder.should(query); + } + return boolQueryBuilder; + } + } + + public static QueryBuilder orQueries(QueryBuilder... queries) { + return andQueries(Arrays.asList(queries)); + } } diff --git a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/DataDiscoveryRepositoryImpl.java b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/DataDiscoveryRepositoryImpl.java index 73662c5d..559c7141 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/DataDiscoveryRepositoryImpl.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/DataDiscoveryRepositoryImpl.java @@ -177,7 +177,7 @@ public class DataDiscoveryRepositoryImpl implements DataDiscoveryRepository { return ApiResponseFactory.createListResponseWithFacets(pagination, resultList, facets); } - private String[] criteriaFieldsToDocumentFields(List<String> criteriaFields) { + public String[] criteriaFieldsToDocumentFields(List<String> criteriaFields) { List<String> fields = new ArrayList<>(); if (criteriaFields != null) { for (String facetField : criteriaFields) { diff --git a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepository.java b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepository.java index d7652aab..db79c910 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepository.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepository.java @@ -1,5 +1,6 @@ package fr.inra.urgi.faidare.repository.es; +import fr.inra.urgi.faidare.domain.criteria.FaidareGermplasmPOSTShearchCriteria; import fr.inra.urgi.faidare.domain.criteria.GermplasmSearchCriteria; import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO; import fr.inra.urgi.faidare.domain.data.germplasm.PedigreeVO; @@ -28,6 +29,11 @@ public interface GermplasmRepository */ Iterator<GermplasmVO> scrollAll(GermplasmSearchCriteria criteria); + /** + * Scroll through all germplasm matching the given FAIDARE search criteria. + */ + Iterator<GermplasmVO> scrollAllGermplasm(FaidareGermplasmPOSTShearchCriteria criteria); + /** * Find pedigree for germplasm by id. */ diff --git a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java index e89383e1..53c1d18a 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java @@ -1,6 +1,7 @@ package fr.inra.urgi.faidare.repository.es; import com.fasterxml.jackson.databind.ObjectMapper; +import fr.inra.urgi.faidare.domain.criteria.FaidareGermplasmPOSTShearchCriteria; import fr.inra.urgi.faidare.domain.criteria.GermplasmSearchCriteria; import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO; import fr.inra.urgi.faidare.domain.data.germplasm.PedigreeVO; @@ -68,6 +69,13 @@ public class GermplasmRepositoryImpl implements GermplasmRepository { return new ESScrollIterator<>(client, requestFactory, parser, GermplasmVO.class, query, fetchSize); } + @Override + public Iterator<GermplasmVO> scrollAllGermplasm(FaidareGermplasmPOSTShearchCriteria criteria) { + QueryBuilder query = queryFactory.createEsShouldQuery(criteria); + int fetchSize = criteria.getPageSize().intValue(); + return new ESScrollIterator<>(client, requestFactory, parser, GermplasmVO.class, query, fetchSize); + } + @Override public GermplasmVO getById(String germplasmDbId) { return getByIdRepository.getById(germplasmDbId); diff --git a/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmService.java b/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmService.java index 97ac680e..db3d920f 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmService.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmService.java @@ -1,12 +1,15 @@ package fr.inra.urgi.faidare.service.es; +import fr.inra.urgi.faidare.domain.criteria.FaidareGermplasmPOSTShearchCriteria; import fr.inra.urgi.faidare.domain.criteria.GermplasmSearchCriteria; import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO; import fr.inra.urgi.faidare.domain.data.germplasm.PedigreeVO; import fr.inra.urgi.faidare.domain.data.germplasm.ProgenyVO; +import fr.inra.urgi.faidare.domain.datadiscovery.response.GermplasmSearchResponse; import fr.inra.urgi.faidare.domain.response.PaginatedList; import java.io.File; +import java.util.LinkedHashSet; public interface GermplasmService { @@ -15,8 +18,16 @@ public interface GermplasmService { PaginatedList<GermplasmVO> find(GermplasmSearchCriteria criteria); + GermplasmSearchResponse esShouldFind(FaidareGermplasmPOSTShearchCriteria criteria); + + LinkedHashSet<String> suggest( + String criteriaField, String searchText, Integer fetchSize, FaidareGermplasmPOSTShearchCriteria criteria + ); + File exportCSV(GermplasmSearchCriteria criteria); + File exportListGermplasmCSV(FaidareGermplasmPOSTShearchCriteria criteria); + PedigreeVO getPedigree(String germplasmDbId); ProgenyVO getProgeny(String germplasmDbId); diff --git a/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java b/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java index f6217f52..3f8fba0d 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java @@ -2,12 +2,36 @@ package fr.inra.urgi.faidare.service.es; import com.opencsv.CSVWriter; import fr.inra.urgi.faidare.api.faidare.v1.GnpISGermplasmController; +import fr.inra.urgi.faidare.domain.criteria.FaidareGermplasmPOSTShearchCriteria; import fr.inra.urgi.faidare.domain.criteria.GermplasmSearchCriteria; import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO; import fr.inra.urgi.faidare.domain.data.germplasm.PedigreeVO; import fr.inra.urgi.faidare.domain.data.germplasm.ProgenyVO; +import fr.inra.urgi.faidare.domain.datadiscovery.data.FacetImpl; +import fr.inra.urgi.faidare.domain.datadiscovery.data.FacetTermImpl; +import fr.inra.urgi.faidare.domain.datadiscovery.response.GermplasmSearchResponse; +import fr.inra.urgi.faidare.domain.response.ApiResponseFactory; import fr.inra.urgi.faidare.domain.response.PaginatedList; +import fr.inra.urgi.faidare.domain.response.Pagination; +import fr.inra.urgi.faidare.domain.response.PaginationImpl; +import fr.inra.urgi.faidare.elasticsearch.ESRequestFactory; +import fr.inra.urgi.faidare.elasticsearch.ESResponseParser; +import fr.inra.urgi.faidare.elasticsearch.criteria.AnnotatedCriteriaMapper; +import fr.inra.urgi.faidare.elasticsearch.criteria.mapping.CriteriaMapping; +import fr.inra.urgi.faidare.elasticsearch.document.DocumentAnnotationUtil; +import fr.inra.urgi.faidare.elasticsearch.document.DocumentMetadata; +import fr.inra.urgi.faidare.elasticsearch.query.impl.ESGenericQueryFactory; +import fr.inra.urgi.faidare.elasticsearch.repository.ESSuggestRepository; +import fr.inra.urgi.faidare.elasticsearch.repository.impl.ESGenericFindRepository; +import fr.inra.urgi.faidare.elasticsearch.repository.impl.ESGenericSuggestRepository; import fr.inra.urgi.faidare.repository.es.GermplasmRepository; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @@ -17,7 +41,13 @@ import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; + +import static org.elasticsearch.search.aggregations.AggregationBuilders.filter; +import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; /** * @author cpommier, gcornut @@ -27,6 +57,34 @@ public class GermplasmServiceImpl implements GermplasmService { private final static Logger LOGGER = LoggerFactory.getLogger(GnpISGermplasmController.class); + private final DocumentMetadata<GermplasmVO> documentMetadata; + private final CriteriaMapping criteriaMapping; + + private final RestHighLevelClient client; + private final ESRequestFactory requestFactory; + private final ESSuggestRepository<FaidareGermplasmPOSTShearchCriteria> suggestRepository; + private final ESGenericQueryFactory<FaidareGermplasmPOSTShearchCriteria> queryFactory; + private final ESResponseParser parser; + + public GermplasmServiceImpl ( + RestHighLevelClient client, + ESRequestFactory requestFactory, + ESResponseParser parser + ){ + this.client = client; + this.parser = parser; + + Class<GermplasmVO> documentClass = GermplasmVO.class; + Class<FaidareGermplasmPOSTShearchCriteria> criteriaClass = FaidareGermplasmPOSTShearchCriteria.class; + + this.requestFactory = requestFactory; + this.documentMetadata = DocumentAnnotationUtil.getDocumentObjectMetadata(documentClass); + + this.criteriaMapping = AnnotatedCriteriaMapper.getMapping(criteriaClass); + this.queryFactory = new ESGenericQueryFactory<>(); + this.suggestRepository = new ESGenericSuggestRepository<>(client, requestFactory, documentClass, queryFactory, parser); + } + @Resource GermplasmRepository germplasmRepository; @@ -43,6 +101,19 @@ public class GermplasmServiceImpl implements GermplasmService { } } + @Override + public File exportListGermplasmCSV(FaidareGermplasmPOSTShearchCriteria criteria) { + try { + Iterator<GermplasmVO> allGermplasm = germplasmRepository.scrollAllGermplasm(criteria); + Path tmpDirPath = Files.createTempDirectory("germplasm"); + Path tmpFile = Files.createTempFile(tmpDirPath, "germplasm_", ".csv"); + writeToCSV(tmpFile, allGermplasm); + return tmpFile.toFile(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + private void writeToCSV(Path tmpFile, Iterator<GermplasmVO> germplasms) throws IOException { Writer fileStream = new OutputStreamWriter(new FileOutputStream(tmpFile.toFile()), StandardCharsets.UTF_8); CSVWriter csvWriter = new CSVWriter(fileStream, ';', '"', '\\', "\n"); @@ -74,6 +145,34 @@ public class GermplasmServiceImpl implements GermplasmService { return germplasmRepository.find(sCrit); } + @Override + public GermplasmSearchResponse esShouldFind(FaidareGermplasmPOSTShearchCriteria germSearCrit) { + try { + // QueryBuilder query = queryFactory.createOrQuery(germSearCrit); + + // Prepare search request + SearchRequest request = prepareSearchRequest(germSearCrit); + + // Execute request + LOGGER.debug(request.toString()); + SearchResponse response = client.search(request, RequestOptions.DEFAULT); + + // Return paginated list + return parseResponse (germSearCrit, response); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public LinkedHashSet<String> suggest( + String criteriaField, String searchText, Integer fetchSize, FaidareGermplasmPOSTShearchCriteria criteria + ) { + String documentFieldPath = criteriaMapping.getDocumentPath(criteriaField, true); + return suggestRepository.suggest(documentFieldPath, searchText, fetchSize, criteria); + } + @Override public PedigreeVO getPedigree(String germplasmDbId) { return germplasmRepository.findPedigree(germplasmDbId); @@ -83,4 +182,88 @@ public class GermplasmServiceImpl implements GermplasmService { public ProgenyVO getProgeny(String germplasmDbId) { return germplasmRepository.findProgeny(germplasmDbId); } + + + private SearchRequest prepareSearchRequest(FaidareGermplasmPOSTShearchCriteria criteria) { + List<String> facetFields = criteria.getFacetFields(); + String[] documentFieldsInFacets = criteriaFieldsToDocumentFields(facetFields); + + // Build search query (excluding document fields used in facets) + QueryBuilder query = queryFactory.createEsShouldQueryExcludingFields(criteria, documentFieldsInFacets); + + // Prepare search request with query + SearchRequest request = ESGenericFindRepository.prepareSearchRequest( + query, criteria, documentMetadata, requestFactory); + + // Build facet aggregations + if (facetFields != null) { + for (String facetField : facetFields) { + String documentPath = criteriaMapping.getDocumentPath(facetField, true); + + String filterAggName = facetField + "Filter"; + + // Create facet term agg + TermsAggregationBuilder termAgg = terms(facetField) + .field(documentPath) + .size(ESRequestFactory.MAX_TERM_AGG_SIZE); + + // Create filter agg for this facet excluding it self + QueryBuilder facetFilter = queryFactory.createEsShouldQueryExcludingFields(criteria, documentPath); + if (facetFilter == null) { + facetFilter = QueryBuilders.matchAllQuery(); + } + + request.source().aggregation( + filter(filterAggName, facetFilter).subAggregation(termAgg) + ); + } + } + + // Build post filter (including document fields used in facets) + QueryBuilder postFilter = queryFactory.createQueryIncludingFields(criteria, documentFieldsInFacets); + request.source().postFilter(postFilter); + + return request; + } + + private String[] criteriaFieldsToDocumentFields(List<String> criteriaFields) { + List<String> fields = new ArrayList<>(); + if (criteriaFields != null) { + for (String facetField : criteriaFields) { + fields.add(criteriaMapping.getDocumentPath(facetField, true)); + } + } + return fields.toArray(new String[]{}); + } + + /** + * Parse Elasticsearch search response into data discovery response + */ + private GermplasmSearchResponse parseResponse( + FaidareGermplasmPOSTShearchCriteria criteria, SearchResponse response + ) throws IOException, ReflectiveOperationException { + // Parse pagination + Pagination pagination = PaginationImpl.create(criteria, parser.parseTotalHits(response)); + + // Parse result list + List<GermplasmVO> resultList = parser.parseHits(response, + documentMetadata.getDocumentClass()); + + // Parse facet terms + List<String> facetFields = criteria.getFacetFields(); + List<FacetImpl> facets = null; + if (facetFields != null) { + facets = new ArrayList<>(); + for (String facetField : facetFields) { + String filterAggName = facetField + "Filter"; + List<FacetTermImpl> terms = parser.parseFacetTerms( + response, filterAggName, facetField + ); + facets.add(new FacetImpl(facetField, terms)); + } + } + + // Return paginated list + return ApiResponseFactory.createGermplasmListResponseWithFacets(pagination, resultList, facets); + } } diff --git a/frontend/package.json b/frontend/package.json index e4dc6e1e..e7e0486b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,19 +21,20 @@ "@angular/platform-browser": "7.2.7", "@angular/platform-browser-dynamic": "7.2.7", "@angular/router": "7.2.7", - "@ng-bootstrap/ng-bootstrap": "4.0.0", + "@ng-bootstrap/ng-bootstrap": "4.2.2", "@types/leaflet": "1.2.14", "@types/leaflet.markercluster": "1.0.3", "angular-coordinates": "1.0.0", "angular-mocks": "1.7.8", - "bootstrap": "4.1.3", + "bootstrap": "4.3.1", "core-js": "2.5.7", + "file-saver": "2.0.2", "font-awesome": "4.7.0", "leaflet": "1.3.4", "leaflet.markercluster": "1.4.1", "moment": "2.24.0", "ng-mocks": "7.6.0", - "ngx-markdown": "8.0.2", + "ngx-markdown": "8.2.1", "ngx-moment": "3.3.0", "popper.js": "1.14.6", "rxjs": "6.4.0", diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index c97fca4e..bb1bb434 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -8,10 +8,20 @@ import { GermplasmCardComponent } from './germplasm-card/germplasm-card.componen import { GermplasmResultPageComponent } from './germplasm-result-page/germplasm-result-page.component'; import { StudyCardComponent } from './study-card/study-card.component'; import { SiteCardComponent } from './site-card/site-card.component'; -import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from '@angular/common/http'; +import { + HTTP_INTERCEPTORS, + HttpClient, + HttpClientModule +} from '@angular/common/http'; import { NavbarComponent } from './navbar/navbar.component'; import { MapComponent } from './map/map.component'; -import { NgbAlertModule, NgbDropdownModule, NgbPaginationModule, NgbPopoverModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap'; +import { + NgbAlertModule, + NgbDropdownModule, + NgbPaginationModule, + NgbPopoverModule, + NgbTypeaheadModule +} from '@ng-bootstrap/ng-bootstrap'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { SuggestionFieldComponent } from './form/suggestion-field/suggestion-field.component'; import { DocumentComponent } from './result-page/document/document.component'; @@ -23,14 +33,12 @@ import { CardRowComponent } from './card-row/card-row.component'; import { CardSectionComponent } from './card-section/card-section.component'; import { LoadingSpinnerComponent } from './loading-spinner/loading-spinner.component'; import { CardTableComponent } from './card-table/card-table.component'; -import { CardSortableTableComponent } from './card-sortable-table/card-sortable-table.component'; import { MomentModule } from 'ngx-moment'; import { XrefsComponent } from './xrefs/xrefs.component'; import { CoordinatesModule } from 'angular-coordinates'; import { CardGenericDocumentComponent } from './card-generic-document/card-generic-document.component'; import { MarkdownModule, MarkedOptions, MarkedRenderer } from 'ngx-markdown'; import { MarkdownPageComponent } from './markdown-page/markdown-page.component'; -import { NgbdSortableHeader } from './card-sortable-table/sortable.directive'; import { DecimalPipe } from '@angular/common'; @NgModule({ @@ -41,7 +49,6 @@ import { DecimalPipe } from '@angular/common'; GermplasmCardComponent, GermplasmResultPageComponent, GermplasmResultPageComponent, - NgbdSortableHeader, StudyCardComponent, SiteCardComponent, NavbarComponent, @@ -55,7 +62,6 @@ import { DecimalPipe } from '@angular/common'; CardSectionComponent, LoadingSpinnerComponent, CardTableComponent, - CardSortableTableComponent, XrefsComponent, CardGenericDocumentComponent, MarkdownPageComponent @@ -92,7 +98,11 @@ import { DecimalPipe } from '@angular/common'; ], providers: [ - { provide: HTTP_INTERCEPTORS, useExisting: ErrorInterceptorService, multi: true }, + { + provide: HTTP_INTERCEPTORS, + useExisting: ErrorInterceptorService, + multi: true + }, DecimalPipe ], bootstrap: [AppComponent] diff --git a/frontend/src/app/brapi.service.ts b/frontend/src/app/brapi.service.ts index 9f39ca24..e1b0b584 100644 --- a/frontend/src/app/brapi.service.ts +++ b/frontend/src/app/brapi.service.ts @@ -10,10 +10,8 @@ import { BrapiResult, BrapiResults, BrapiStudy, - BrapiTrial, - GermplasmCriteria + BrapiTrial } from './models/brapi.model'; -import { Germplasm } from './models/gnpis.model'; export const BASE_URL = 'brapi/v1'; @@ -31,9 +29,9 @@ export class BrapiService { } // TODO use the progeny call when the information about parent will be added - /*germplasmProgeny(germplasmDbId: string): Observable<GermplasmResult<BrapiGermplasmProgeny>> { + /* germplasmProgeny(germplasmDbId: string): Observable<GermplasmResult<BrapiGermplasmProgeny>> { return this.http.get<GermplasmResult<BrapiGermplasmProgeny>>(`${BASE_URL}/germplasm/${germplasmDbId}/progeny`); - }*/ + } */ germplasmAttributes(germplasmDbId: string): Observable<BrapiResult<BrapiGermplasmAttributes>> { return this.http @@ -63,10 +61,6 @@ export class BrapiService { .get<BrapiResult<BrapiTrial>>(`${BASE_URL}/trials/${trialsId}`); } - germplasmSearch(criteria: GermplasmCriteria): Observable<BrapiResults<BrapiGermplasm>>{ - return this.http.post<BrapiResults<Germplasm>>(`${BASE_URL}/germplasm-search`,criteria) - } - /** * Get BrAPI single result response and replace the 'schema:includedInDataCatalog' URI value to the actual source object value. diff --git a/frontend/src/app/card-sortable-table/card-sortable-table.component.html b/frontend/src/app/card-sortable-table/card-sortable-table.component.html deleted file mode 100644 index 01007528..00000000 --- a/frontend/src/app/card-sortable-table/card-sortable-table.component.html +++ /dev/null @@ -1,65 +0,0 @@ -<!--<p>This is a more complete example with a service that simulates server calling:</p> - -<ul> - <li>an observable async service to fetch a list of countries</li> - <li>sorting, filtering and pagination</li> - <li>simulated delay and loading indicator</li> - <li>debouncing of search requests</li> -</ul>--> - -<form> - <div class="form-group form-inline"> - Full text search: <input class="form-control ml-2" type="text" - name="searchTerm" - [(ngModel)]="service.searchTerm"/> - <span class="ml-3" *ngIf="service.loading$ | async">Loading...</span> - </div> - - <div class="card"> - <table class="table table-striped mt-1"> - <thead> - <tr> - <th scope="col" sortable="{{ header }}" (sort)="onSort($event)" - *ngFor="let header of tableHeaders ">{{ header }}</th> - <!--<th *ngFor="let header of tableHeaders " scope="col">#</th> - <th scope="col" sortable="name" (sort)="onSort($event)">Country</th> - <th scope="col" sortable="area" (sort)="onSort($event)">Area</th> - <th scope="col" sortable="population" (sort)="onSort($event)">Population</th>--> - </tr> - </thead> - <tbody> - - - <ng-container *ngFor="let row of rows" - [ngTemplateOutlet]="template" - [ngTemplateOutletContext]="{$implicit: row}"> - </ng-container> - - <!--<tr *ngFor="let country of countries$ | async"> - <th scope="row">{{ country.id }}</th> - <td> - <img [src]="'https://upload.wikimedia.org/wikipedia/commons/' + country.flag" class="mr-2" style="width: 20px"> - <ngb-highlight [result]="country.name" [term]="service.searchTerm"></ngb-highlight> - </td> - <td><ngb-highlight [result]="country.area | number" [term]="service.searchTerm"></ngb-highlight></td> - <td><ngb-highlight [result]="country.population | number" [term]="service.searchTerm"></ngb-highlight></td> - </tr>--> - </tbody> - </table> - </div> - - <div class="d-flex justify-content-between p-2"> - <ngb-pagination - [collectionSize]="total$ | async" [(page)]="service.page" - [pageSize]="service.pageSize"> - </ngb-pagination> - - <select class="custom-select" style="width: auto" name="pageSize" - [(ngModel)]="service.pageSize"> - <option [ngValue]="5">5 items per page</option> - <option [ngValue]="10">10 items per page</option> - <option [ngValue]="15">15 items per page</option> - </select> - </div> - -</form> diff --git a/frontend/src/app/card-sortable-table/card-sortable-table.component.scss b/frontend/src/app/card-sortable-table/card-sortable-table.component.scss deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/src/app/card-sortable-table/card-sortable-table.component.spec.ts b/frontend/src/app/card-sortable-table/card-sortable-table.component.spec.ts deleted file mode 100644 index 7bd6690b..00000000 --- a/frontend/src/app/card-sortable-table/card-sortable-table.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { CardSortableTableComponent } from './card-sortable-table.component'; - -describe('CardSortableTableComponent', () => { - let component: CardSortableTableComponent; - let fixture: ComponentFixture<CardSortableTableComponent>; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ CardSortableTableComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(CardSortableTableComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/frontend/src/app/card-sortable-table/card-sortable-table.component.ts b/frontend/src/app/card-sortable-table/card-sortable-table.component.ts deleted file mode 100644 index 72d9eeaa..00000000 --- a/frontend/src/app/card-sortable-table/card-sortable-table.component.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { - Component, - ContentChild, - Input, - QueryList, - TemplateRef, - ViewChildren -} from '@angular/core'; -import { Observable } from 'rxjs'; -import { GermplasmService } from './germplasm.services'; -import { NgbdSortableHeader, SortEvent } from './sortable.directive'; - -@Component({ - selector: 'faidare-card-sortable-table', - templateUrl: './card-sortable-table.component.html', - styleUrls: ['./card-sortable-table.component.scss'] -}) -export class CardSortableTableComponent { - - total$: Observable<number>; - - @Input() tableHeaders: String[]; - @Input() rows: any; - - @ContentChild(TemplateRef) template: TemplateRef<any>; - - constructor(public service: GermplasmService) { - this.total$ = service.total$; - } - - - @ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>; - - - onSort({column, direction}: SortEvent) { - // resetting other headers - this.headers.forEach(header => { - if (header.sortable !== column) { - header.direction = ''; - } - }); - - this.service.sortColumn = column; - this.service.sortDirection = direction; - } - -} diff --git a/frontend/src/app/card-sortable-table/germplasm.services.ts b/frontend/src/app/card-sortable-table/germplasm.services.ts deleted file mode 100644 index fe5c9c34..00000000 --- a/frontend/src/app/card-sortable-table/germplasm.services.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { Injectable, PipeTransform } from '@angular/core'; - -import { BehaviorSubject, Observable, of, Subject } from 'rxjs'; -import { GERMPLASM } from './germplasm'; -import { DecimalPipe } from '@angular/common'; -import { debounceTime, delay, switchMap, tap } from 'rxjs/operators'; -import { SortDirection } from './sortable.directive'; -import { BrapiGermplasm } from '../models/brapi.model'; - -interface SearchResult { - germplasm: BrapiGermplasm[]; - total: number; -} - -interface State { - page: number; - pageSize: number; - searchTerm: string; - sortColumn: string; - sortDirection: SortDirection; -} - -function compare(v1, v2) { - return v1 < v2 ? -1 : v1 > v2 ? 1 : 0; -} - -function sort(germplasm: BrapiGermplasm[], column: string, direction: string): BrapiGermplasm[] { - - if (direction === '') { - return germplasm; - } else { - return [...germplasm].sort((a, b) => { - const res = compare(a[column], b[column]); - return direction === 'asc' ? res : -res; - }); - } -} - -function matches(germplasm: BrapiGermplasm, term: string, pipe: PipeTransform) { - return germplasm.germplasmDbId.toLowerCase().includes(term.toLowerCase()) - || germplasm.accessionNumber.toLowerCase().includes(term.toLowerCase()) - || germplasm.instituteName.toLowerCase().includes(term.toLowerCase()) - || germplasm.commonCropName.toLowerCase().includes(term.toLowerCase()); -} - -@Injectable({providedIn: 'root'}) -export class GermplasmService { - private _loading$ = new BehaviorSubject<boolean>(true); - private _search$ = new Subject<void>(); - private _data$ = new BehaviorSubject<any[]>([]); - private _total$ = new BehaviorSubject<number>(0); - - private _state: State = { - page: 1, - pageSize: 5, - searchTerm: '', - sortColumn: '', - sortDirection: '' - }; - - constructor(private pipe: DecimalPipe) { - this._search$.pipe( - tap(() => this._loading$.next(true)), - debounceTime(200), - switchMap(() => this._search()), - delay(200), - tap(() => this._loading$.next(false)) - ).subscribe(result => { - this._data$.next(result['germplasm']); - this._total$.next(result.total); - }); - - this._search$.next(); - } - - get data$() { return this._data$.asObservable(); } - get total$() { return this._total$.asObservable(); } - get loading$() { return this._loading$.asObservable(); } - get page() { return this._state.page; } - get pageSize() { return this._state.pageSize; } - get searchTerm() { return this._state.searchTerm; } - - set page(page: number) { this._set({page}); } - set pageSize(pageSize: number) { this._set({pageSize}); } - set searchTerm(searchTerm: string) { this._set({searchTerm}); } - set sortColumn(sortColumn: string) { this._set({sortColumn}); } - set sortDirection(sortDirection: SortDirection) { this._set({sortDirection}); } - - private _set(patch: Partial<State>) { - Object.assign(this._state, patch); - this._search$.next(); - } - - private _search(): Observable<any> { - const {sortColumn, sortDirection, pageSize, page, searchTerm} = this._state; - - // 1. sort - let data = sort(GERMPLASM, sortColumn, sortDirection); - - // 2. filter - data = data.filter(data => matches(data, searchTerm, this.pipe)); - const total = data.length; - - // 3. paginate - data = data.slice((page - 1) * pageSize, (page - 1) * pageSize + pageSize); - return of({germplasm: data, total}); - } -} - diff --git a/frontend/src/app/card-sortable-table/germplasm.ts b/frontend/src/app/card-sortable-table/germplasm.ts deleted file mode 100644 index ba751687..00000000 --- a/frontend/src/app/card-sortable-table/germplasm.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { BrapiGermplasm } from '../models/brapi.model'; - -export const GERMPLASM: BrapiGermplasm[] = [ - { - germplasmDbId: "aHR0cHM6Ly9kb2kub3JnLzEwLjE1NDU0L0hET0Y4Qw==", - defaultDisplayName: "FANNETTE", - accessionNumber: "10936", - germplasmName: "FANNETTE", - pedigree: "RIKA/VOLLA//EMIR", - seedSource: null, - synonyms: [ - "FRA051:4278" - ], - commonCropName: "Barley", - instituteCode: "FRA040", - instituteName: "UMR Génétique, Diversité et Ecophysiologie des Céréales, INRA-Clermont", - biologicalStatusOfAccessionCode: "Advanced or improved cultivar", - countryOfOriginCode: null, - typeOfGermplasmStorageCode: null, - taxonIds: null, - donors: [ - { - donorGermplasmPUI: null, - donorAccessionNumber: "15955DA", - donorInstituteCode: "FRA018", - donationDate: null, - } - ], - genus: "Hordeum", - species: "vulgare", - speciesAuthority: null, - subtaxa: "subsp. vulgare", - subtaxaAuthority: null, - acquisitionDate: null, - germplasmPUI: "https://doi.org/10.15454/HDOF8C", - documentationURL: null - }, - { - germplasmDbId: "dXJuOlVSR0kvZ25waXNfcHVpJTNBdW5rbm93biUzQVdoZWF0JTNBUkUwMDExOQ==", - defaultDisplayName: "RE00119", - accessionNumber: "RE00119", - germplasmName: "RE00119", - pedigree: null, - seedSource: null, - synonyms: null, - commonCropName: "Wheat", - instituteCode: "FRA015", - instituteName: "INRA", - biologicalStatusOfAccessionCode: null, - countryOfOriginCode: null, - typeOfGermplasmStorageCode: null, - taxonIds: null, - donors: null, - genus: "Triticum", - species: "aestivum", - speciesAuthority: null, - subtaxa: "subsp. aestivum", - subtaxaAuthority: null, - acquisitionDate: null, - germplasmPUI: "urn:URGI/gnpis_pui%3Aunknown%3AWheat%3ARE00119", - documentationURL: null - }, - { - germplasmDbId: "aHR0cHM6Ly9kb2kub3JnLzEwLjE1NDU0L0VVMk1LWA==", - defaultDisplayName: "AKER_11467", - accessionNumber: "PI 26545", - germplasmName: "AKER_11467", - pedigree: null, - seedSource: null, - synonyms: [ - "PI 26545" - ], - commonCropName: "Beta vulgaris vulgaris cv. Sugar beet", - instituteCode: "USA126", - instituteName: "Germplasm Resources Information Network", - biologicalStatusOfAccessionCode: null, - countryOfOriginCode: null, - typeOfGermplasmStorageCode: null, - taxonIds: null, - donors: null, - genus: "Beta", - species: "vulgaris", - speciesAuthority: null, - subtaxa: "subsp. vulgaris cv. Sugar beet", - subtaxaAuthority: null, - acquisitionDate: null, - germplasmPUI: "https://doi.org/10.15454/EU2MKX", - documentationURL: null - } -]; diff --git a/frontend/src/app/card-sortable-table/sortable.directive.ts b/frontend/src/app/card-sortable-table/sortable.directive.ts deleted file mode 100644 index 917b2eba..00000000 --- a/frontend/src/app/card-sortable-table/sortable.directive.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {Directive, EventEmitter, Input, Output} from '@angular/core'; - -export type SortDirection = 'asc' | 'desc' | ''; -const rotate: {[key: string]: SortDirection} = { 'asc': 'desc', 'desc': '', '': 'asc' }; - -export interface SortEvent { - column: string; - direction: SortDirection; -} - -@Directive({ - selector: 'th[sortable]', - host: { - '[class.asc]': 'direction === "asc"', - '[class.desc]': 'direction === "desc"', - '(click)': 'rotate()' - } -}) -export class NgbdSortableHeader { - - @Input() sortable: string; - @Input() direction: SortDirection = ''; - @Output() sort = new EventEmitter<SortEvent>(); - - rotate() { - this.direction = rotate[this.direction]; - this.sort.emit({column: this.sortable, direction: this.direction}); - } -} diff --git a/frontend/src/app/card-table/card-table.component.html b/frontend/src/app/card-table/card-table.component.html index ad60e888..fff69973 100644 --- a/frontend/src/app/card-table/card-table.component.html +++ b/frontend/src/app/card-table/card-table.component.html @@ -1,15 +1,17 @@ -<table class="table table-sm table-striped"> - <thead *ngIf="headers"> - <tr> - <th *ngFor="let header of headers" scope="col"> - {{ header }} - </th> - </tr> - </thead> - <tbody> - <ng-container *ngFor="let row of rows" - [ngTemplateOutlet]="template" - [ngTemplateOutletContext]="{$implicit: row}"> - </ng-container> - </tbody> -</table> +<div class="card"> + <table class="table table-sm table-striped"> + <thead *ngIf="headers"> + <tr> + <th *ngFor="let header of headers" scope="col"> + {{ header }} + </th> + </tr> + </thead> + <tbody> + <ng-container *ngFor="let row of rows" + [ngTemplateOutlet]="template" + [ngTemplateOutletContext]="{$implicit: row}"> + </ng-container> + </tbody> + </table> +</div> diff --git a/frontend/src/app/form/form.component.html b/frontend/src/app/form/form.component.html index d699f8bc..8f8de923 100644 --- a/frontend/src/app/form/form.component.html +++ b/frontend/src/app/form/form.component.html @@ -30,6 +30,7 @@ inputId="crops" criteriaField="crops" [criteria$]="criteria$" + [displayGermplasmResult$]="displayGermplasmResult$" placeholder="Search crops"> </faidare-suggestion-field> </div> @@ -47,6 +48,7 @@ inputId="germplasmList" criteriaField="germplasmLists" [criteria$]="criteria$" + [displayGermplasmResult$]="displayGermplasmResult$" placeholder="Search germplasm lists"> </faidare-suggestion-field> </div> @@ -64,6 +66,7 @@ inputId="accessions" criteriaField="accessions" [criteria$]="criteria$" + [displayGermplasmResult$]="displayGermplasmResult$" placeholder="Search germplasm accession"> </faidare-suggestion-field> </div> diff --git a/frontend/src/app/form/form.component.ts b/frontend/src/app/form/form.component.ts index fe09236a..9ec5f90a 100644 --- a/frontend/src/app/form/form.component.ts +++ b/frontend/src/app/form/form.component.ts @@ -14,6 +14,7 @@ enum Tabs { }) export class FormComponent { @Input() criteria$: BehaviorSubject<DataDiscoveryCriteria>; + @Input() displayGermplasmResult$: BehaviorSubject<boolean>; @Output() traitWidgetInitialized = new EventEmitter(); // Default active tab diff --git a/frontend/src/app/form/suggestion-field/suggestion-field.component.ts b/frontend/src/app/form/suggestion-field/suggestion-field.component.ts index 91484320..5b43b56d 100644 --- a/frontend/src/app/form/suggestion-field/suggestion-field.component.ts +++ b/frontend/src/app/form/suggestion-field/suggestion-field.component.ts @@ -19,6 +19,7 @@ export class SuggestionFieldComponent implements OnInit { @Input() criteriaField: string; @Input() inputId: string; @Input() criteria$: BehaviorSubject<DataDiscoveryCriteria>; + @Input() displayGermplasmResult$: BehaviorSubject<boolean>; @Input() placeholder: string; selectedKeys: string[] = []; @@ -147,5 +148,10 @@ export class SuggestionFieldComponent implements OnInit { [this.criteriaField]: [...this.selectedKeys] }; this.criteria$.next(this.localCriteria); + let displayGermplasmResult = false; + this.displayGermplasmResult$.subscribe(germplasmResultState => { + displayGermplasmResult = germplasmResultState; + }); + this.displayGermplasmResult$.next(displayGermplasmResult); } } diff --git a/frontend/src/app/germplasm-card/germplasm-card.component.ts b/frontend/src/app/germplasm-card/germplasm-card.component.ts index 73616b70..6c8d9108 100644 --- a/frontend/src/app/germplasm-card/germplasm-card.component.ts +++ b/frontend/src/app/germplasm-card/germplasm-card.component.ts @@ -2,7 +2,12 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { BrapiService } from '../brapi.service'; import { GnpisService } from '../gnpis.service'; -import { BrapiAttributeData, BrapiGermplasmPedigree, BrapiLocation, BrapiTaxonIds } from '../models/brapi.model'; +import { + BrapiAttributeData, + BrapiGermplasmPedigree, + BrapiLocation, + BrapiTaxonIds +} from '../models/brapi.model'; import { Children, Germplasm, Site } from '../models/gnpis.model'; import { environment } from '../../environments/environment'; diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html index ad8fc54e..2c8a8c7d 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html @@ -1,42 +1,102 @@ -<!--<faidare-germplasm-form - inputId="accessions" - criteriaField="accessions" - [criteria$]="criteria"> -</faidare-germplasm-form>--> -<ng-container> - <h3 class="mb-4"> - Germplasm result page - </h3> +<ng-container *ngIf="germplasm && germplasm.length>0"> - <div class="col-sm-8 mb-3"> - <faidare-suggestion-field - inputId="accessions" - criteriaField="accessions" - [criteria$]="Germplasmcriteria$" - placeholder="Search germplasm name"> - </faidare-suggestion-field> + <div class="container mb-3"> + <div class="row result align-content-center"> + <span class="col mt-2 bolder"> + Results: + </span> + + <div class="row align-content-center"> + <span *ngIf="pagination.totalResult" style="margin-right: 5px" + class="col align-self-end small text-muted mt-3"> + From {{ pagination.startResult | number }} + to {{ pagination.endResult | number }} + over {{ pagination.totalResult | number }} documents + <span *ngIf="pagination.totalResult > pagination.maxResults"> + (limited to {{ pagination.maxResults | number }}) + </span> + </span> + </div> + </div> </div> - <faidare-card-sortable-table - [tableHeaders]="['germplasmName', 'accessionNumber', 'commonCropName']" - [rows]="germplasm"> + <button type="button" class="btn btn-outline-success mb-2" + (click)="exportPlantMaterial(localCriteria)"> + Export + <img src="assets/faidare/images/csv-logo.png" alt="csv logo" title="" + height="20px"/> + </button> - <ng-template let-row> - <tr> - <td> - <ngb-highlight [result]="row.germplasmName" - [term]="service2.searchTerm"></ngb-highlight> - </td> - <td> - <ngb-highlight [result]="row.accessionNumber" - [term]="service2.searchTerm"></ngb-highlight> - </td> - <td> - <ngb-highlight [result]="row.commonCropName" - [term]="service2.searchTerm"></ngb-highlight> - </td> - </tr> + <faidare-card-section + class="col-12 col-lg" + header="Germplasm data: " + [test]="germplasm"> + <ng-template> + <div class="table-responsive table-card-body"> + <table class="table table-sm table-striped"> + <thead> + <tr> + <th id="germplasmResultThead" + *ngFor="let header of headers" scope="col"> + <label id="tabHeader" (click)="getTabField(header)">{{ header }} + <i *ngIf="!fieldSortState[header]" class="fa fa-sort" + aria-hidden="true"></i> + <i *ngIf="fieldSortState[header] =='desc'" + class="fa fa-sort-desc" + aria-hidden="true"></i> + <i *ngIf="fieldSortState[header] == 'asc'" + class="fa fa-sort-asc" + aria-hidden="true"></i> + </label> + </th> + </tr> + </thead> + <tbody> + <ng-container *ngFor="let accession of germplasm"> + <tr> + <td>{{ accession.germplasmName }}</td> + <td>{{ accession.accessionNumber }}</td> + <td>{{ accession.commonCropName }}</td> + <td>{{ accession.instituteName }}</td> + </tr> + </ng-container> + </tbody> + </table> + </div> </ng-template> + </faidare-card-section> + + <div class="container text-right" + style="margin-top: -30px" + *ngIf="germplasm"> + <div ngbDropdown class="dropdown-container"> + <button class="btn btn-outline-secondary btn-sm" + ngbDropdownToggle>Results per page : {{ pagination.pageSize }} + </button> + <div ngbDropdownMenu class="dropdown-menu" id="pageSizes"> + <div *ngFor="let pageSize of elementPerPage"> + <button type="button" + class="btn btn-light" + (click)="changeNbElementPerPage(pageSize)" + style="width: 160px;" ngbDropdownItem>{{ pageSize }} + </button> + </div> + </div> + </div> + </div> + + <div class="d-flex justify-content-center mt-2 mb-5" + *ngIf="pagination.totalPages > 1"> + <!-- we add 1 to the page because ngb-pagination is 1 based --> + <ngb-pagination [page]="pagination.currentPage + 1" + (pageChange)="changePage($event)" + [collectionSize]="resultCount()" + [pageSize]="pagination.pageSize" + [maxSize]="5" + [boundaryLinks]="true" + [ellipses]="false" + size="sm"> + </ngb-pagination> + </div> - </faidare-card-sortable-table> </ng-container> diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.scss b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.scss index e69de29b..bcaa4682 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.scss +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.scss @@ -0,0 +1,37 @@ +@import "theme"; +@import '../../styles.scss'; + + +#tabHeader { + cursor: pointer; +} + +#germplasmResultThead { + margin-top: 26px; + border-bottom-width: 3px; + border-bottom-color: #d0d8e1; +} + + +.result { + border-top: 4.5px solid #c2c2c2; + border-bottom: 1px solid #c2c2c2; +} + +.bolder { + font-size: 1.2rem; + font-weight: bold; +} + +thead th { + position: sticky; + top: 0; + background-color: white; +} + +label { + span { + font-weight: bold; + display: block; + } +} diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts index 69071b7a..99092ac4 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts @@ -1,14 +1,20 @@ -import { Component, OnInit } from '@angular/core'; -import { GermplasmService } from '../card-sortable-table/germplasm.services'; +import { Component, Input, OnInit } from '@angular/core'; +import { BrapiGermplasm } from '../models/brapi.model'; +import { GnpisService } from '../gnpis.service'; +import { GermplasmSearchCriteria } from '../models/gnpis.model'; -import { BrapiService } from '../brapi.service'; + +import { saveAs } from 'file-saver'; +import { ActivatedRoute, Router } from '@angular/router'; import { - BrapiCriteriaUtils, - BrapiGermplasm, - GermplasmCriteria -} from '../models/brapi.model'; + DataDiscoveryCriteria, + DataDiscoveryCriteriaUtils, + DataDiscoveryFacet, + DEFAULT_PAGE_SIZE, + MAX_RESULTS +} from '../models/data-discovery.model'; +import { asArray } from '../utils'; import { BehaviorSubject } from 'rxjs'; -import { filter } from 'rxjs/operators'; @Component({ selector: 'faidare-germplasm-result-page', @@ -19,37 +25,183 @@ export class GermplasmResultPageComponent implements OnInit { germplasm: BrapiGermplasm[]; - Germplasmcriteria$ = new BehaviorSubject<GermplasmCriteria> (BrapiCriteriaUtils.emptyCriteria()); - private localCriteria: GermplasmCriteria = BrapiCriteriaUtils.emptyCriteria(); + localCriteria: GermplasmSearchCriteria = DataDiscoveryCriteriaUtils.emptyGermplasmSearchCriteria(); + + @Input() criteriaFromForm$: BehaviorSubject<DataDiscoveryCriteria>; + @Input() germplasmSearchCriteria$: BehaviorSubject<GermplasmSearchCriteria>; + @Input() germplasmFacets$: BehaviorSubject<DataDiscoveryFacet[]>; + + headers: string[] = ['germplasmName', 'accessionNumber', 'commonCropName', 'instituteName']; + elementPerPage: number[] = [15, 20, 25]; + fieldSortState: object = { + germplasmName: null, + accessionNumber: null, + commonCropName: null, + instituteName: null + }; - constructor(public service2: GermplasmService, public service: BrapiService) { } + pagination = { + startResult: 1, + endResult: DEFAULT_PAGE_SIZE, + totalResult: null, + currentPage: 0, + pageSize: DEFAULT_PAGE_SIZE, + totalPages: null, + maxResults: MAX_RESULTS + }; + + constructor(public service: GnpisService, private route: ActivatedRoute, private router: Router) { + } ngOnInit() { - this.Germplasmcriteria$.pipe(filter(c => c !== this.localCriteria)) - .subscribe(newCriteria => { - newCriteria.accessionNumbers = ["10936"]; - for (const field in newCriteria){ - if (newCriteria[field]){ - // this.localCriteria[field] = newCriteria[field]; - } - } - this.searchGermplasm(); + const queryParams = this.route.snapshot.queryParams; + this.reassignCriteriaFieldFromDataDiscoveryFields(queryParams); + + this.criteriaFromForm$.subscribe(criteria => { + this.reassignCriteriaFieldFromDataDiscoveryFields(criteria); + this.germplasmSearchCriteria$.next(this.localCriteria); + }); + this.germplasmSearchCriteria$ + .subscribe(criteria => { + this.localCriteria = criteria; + this.searchGermplasm(criteria); }); - this.localCriteria.accessionNumbers = ["10936"]; - this.service2.data$.subscribe(germplasm =>{ - this.germplasm = germplasm; - }) + } + searchGermplasm(criteria: GermplasmSearchCriteria) { + this.service.germplasmSearch(criteria) + .subscribe(({ metadata, facets, result }) => { + this.germplasm = result.data; + this.germplasmFacets$.next(this.formatFacets(facets)); + DataDiscoveryCriteriaUtils.updatePagination(this.pagination, metadata.pagination); + }); } - searchGermplasm() { - this.service.germplasmSearch(this.localCriteria) - .subscribe(response => { - this.germplasm = response.result.data; + + reassignCriteriaFieldFromDataDiscoveryFields(criteria) { + + this.localCriteria = { + ...this.localCriteria, + commonCropName: asArray(criteria.crops), + species: asArray(criteria.crops), + germplasmGenus: asArray(criteria.crops), + genusSpecies: asArray(criteria.crops), + subtaxa: asArray(criteria.crops), + genusSpeciesSubtaxa: asArray(criteria.crops), + taxonSynonyms: asArray(criteria.crops), + + panel: asArray(criteria.germplasmLists), + collection: asArray(criteria.germplasmLists), + population: asArray(criteria.germplasmLists), + + germplasmNames: asArray(criteria.accessions), + accessionNumbers: asArray(criteria.accessions), + synonyms: asArray(criteria.accessions), + + sources: asArray(criteria.sources) + }; + + this.germplasmSearchCriteria$.next(this.localCriteria); + + } + + exportPlantMaterial(criteria: GermplasmSearchCriteria) { + this.service.plantMaterialExport(criteria).subscribe( + result => { + const blob = new Blob([result], { type: 'text/plain;charset=utf-8' }); + saveAs(blob, 'germplasm.gnpis.csv'); + }, + error => { + console.log(error); }); } -} + getTabField(tabField) { + + this.switchSortOrder(tabField); + this.resetOtherFieldSort(tabField); + + this.localCriteria.sortOrder = this.fieldSortState[tabField]; + + this.germplasmSearchCriteria$.next(this.localCriteria); + + return tabField; + } + + switchSortOrder(tabField) { + if (this.fieldSortState[tabField] === 'asc') { + this.localCriteria.sortBy = null; + return this.fieldSortState[tabField] = null; + } + if (this.fieldSortState[tabField] === 'desc') { + this.localCriteria.sortBy = tabField; + return this.fieldSortState[tabField] = 'asc'; + } + if (!this.fieldSortState[tabField]) { + this.localCriteria.sortBy = tabField; + return this.fieldSortState[tabField] = 'desc'; + } + } + + resetOtherFieldSort(tabField) { + const otherHeaders = this.headers.filter(header => header !== tabField); + for (const header of otherHeaders) { + this.fieldSortState[header] = null; + } + } + + resultCount() { + return Math.min( + this.pagination.totalResult, + MAX_RESULTS - DEFAULT_PAGE_SIZE + ); + } + + changePage(page: number) { + this.localCriteria.page = page - 1; + this.germplasmSearchCriteria$.next(this.localCriteria); + /*this.router.navigate(['.'], { + relativeTo: this.route, + queryParams: { page }, + queryParamsHandling: 'merge' + });*/ + } + + changeNbElementPerPage(pageSize: number) { + this.pagination.pageSize = pageSize; + this.elementPerPage = [10, 15, 20, 25]; + this.elementPerPage = this.elementPerPage + .filter(otherPageSize => otherPageSize !== pageSize); + this.localCriteria.pageSize = pageSize; + this.germplasmSearchCriteria$.next(this.localCriteria); + } + + formatFacets(facets: DataDiscoveryFacet[]): DataDiscoveryFacet[] { + const bioStatusAndGeneticNature = []; + let newFacets: DataDiscoveryFacet[] = []; + for (const facet of facets) { + if (facet.field === 'biologicalStatus' || facet.field === 'geneticNature') { + for (const term of facet.terms) { + bioStatusAndGeneticNature + .push(term); + } + } else if (facet.field === 'holdingInstitute') { + facet.field = 'holding institute'; + newFacets.push(facet); + } else { + newFacets.push(facet); + } + } + newFacets = [ + { + field: 'Biological status / Genetic nature', + terms: bioStatusAndGeneticNature + }, + ...newFacets + ]; + return newFacets; + } +} diff --git a/frontend/src/app/gnpis.service.spec.ts b/frontend/src/app/gnpis.service.spec.ts index 60327439..27e81fe0 100644 --- a/frontend/src/app/gnpis.service.spec.ts +++ b/frontend/src/app/gnpis.service.spec.ts @@ -1,9 +1,22 @@ import { BASE_URL, GnpisService } from './gnpis.service'; import { BrapiMetaData, BrapiResults } from './models/brapi.model'; -import { DataDiscoveryCriteria, DataDiscoverySource } from './models/data-discovery.model'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { + DataDiscoveryCriteria, + DataDiscoverySource +} from './models/data-discovery.model'; +import { + HttpClientTestingModule, + HttpTestingController +} from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; -import { Donor, Germplasm, GermplasmInstitute, GermplasmSet, Institute, Site } from './models/gnpis.model'; +import { + Donor, + Germplasm, + GermplasmInstitute, + GermplasmSet, + Institute, + Site +} from './models/gnpis.model'; describe('GnpisService', () => { @@ -115,6 +128,26 @@ describe('GnpisService', () => { population: [germplasmSet] }; + const germplasmExportCriteria: GermplasmExportCriteria = { + accessionNumbers: ['VCR010'], + germplasmDbIds: [], + germplasmGenus: [], + germplasmNames: [], + germplasmPUIs: [], + germplasmSpecies: [] + }; + + const exportFile: string = '"DOI";"AccessionNumber";' + + '"AccessionName";"TaxonGroup";' + + '"HoldingInstitution";"LotName";' + + '"LotSynonym";' + + '"CollectionName";' + + '"CollectionType";' + + '"PanelName";' + + '"PanelSize"\n' + + '"https://germplasmdoi";"germplasm01";GermplasmTest;Pea;INRA-URGI;;;;;;'; + + let gnpisService: GnpisService; let http: HttpTestingController; beforeEach(() => { @@ -228,5 +261,18 @@ describe('GnpisService', () => { }); }); }); + + it('should export germplasm as PlantMaterial', () => { + gnpisService.plantMaterialExport(germplasmExportCriteria).subscribe( + plantMaterialExport => { + expect(plantMaterialExport).toEqual(exportFile); + } + ); + const req = http.expectOne({ + url: `${BASE_URL}/germplasm/csv`, + method: 'POST' + }); + req.flush(exportFile); + }); }) ; diff --git a/frontend/src/app/gnpis.service.ts b/frontend/src/app/gnpis.service.ts index 5146ebdb..ed80102f 100644 --- a/frontend/src/app/gnpis.service.ts +++ b/frontend/src/app/gnpis.service.ts @@ -7,9 +7,14 @@ import { DataDiscoveryResults, DataDiscoverySource } from './models/data-discovery.model'; -import { BrapiResults } from './models/brapi.model'; +import { + BrapiGermplasm, + BrapiResults, + GermplasmCriteria, + GermplasmResults +} from './models/brapi.model'; import { map } from 'rxjs/operators'; -import { Germplasm } from './models/gnpis.model'; +import { Germplasm, GermplasmSearchCriteria } from './models/gnpis.model'; import { XrefResponse } from './models/xref.model'; import { removeNullUndefined } from './utils'; @@ -82,17 +87,7 @@ export class GnpisService { return document; }); if (response.facets) { - response.facets = response.facets.map((facet: DataDiscoveryFacet) => { - facet.terms = facet.terms.map(term => { - if (facet.field === 'sources') { - term.label = sourceByURI[term.term]['schema:name']; - } else { - term.label = term.term; - } - return term; - }); - return facet; - }); + this.getSourcesName(sourceByURI, response); } return response; })); @@ -112,6 +107,36 @@ export class GnpisService { ); } + germplasmSearch(criteria: GermplasmCriteria): Observable<GermplasmResults<BrapiGermplasm>> { + + /*return this.http.post<GermplasmResults<Germplasm>>(`${BASE_URL}/germplasm/search`, + criteria, + { headers: { 'Accept': 'application/ld+json,application/json' } });*/ + + return zip( + // Get source by URI + this.sourceByURI$, + // Get documents by criteria + this.http.post<GermplasmResults<Germplasm>>(`${BASE_URL}/germplasm/search`, + criteria, + { headers: { 'Accept': 'application/ld+json,application/json' } })) + .pipe(map(([sourceByURI, response]) => { + // Extract BrAPI documents from result + const germplasm = response.result.data; + + // Transform document to have the source details in place of the source URI + response.result.data = germplasm.map(data => { + const sourceURI = data['schema:includedInDataCatalog']; + data['schema:includedInDataCatalog'] = sourceByURI[sourceURI]; + return data; + }); + if (response.facets) { + this.getSourcesName(sourceByURI, response); + } + return response; + })); + } + /** * Get data source by URI */ @@ -123,4 +148,30 @@ export class GnpisService { return this.http.get<XrefResponse>(`${BASE_URL}/xref/documentbyfulltextid?linkedRessourcesID=${xrefId}`); } + plantMaterialExport(criteria: GermplasmSearchCriteria): Observable<any> { + const requestOptions: Object = { + /* other options here */ + responseType: 'text' + }; + return this.http.post<any>( + `${BASE_URL}/germplasm/germplasm-list-csv`, + criteria, + requestOptions + ); + } + + getSourcesName(sourceByURI, response) { + response.facets = response.facets.map((facet: DataDiscoveryFacet) => { + facet.terms = facet.terms.map(term => { + if (facet.field === 'sources') { + term.label = sourceByURI[term.term]['schema:name']; + } else { + term.label = term.term; + } + return term; + }); + return facet; + }); + } + } diff --git a/frontend/src/app/models/brapi.model.ts b/frontend/src/app/models/brapi.model.ts index 539af06c..f8630ff0 100644 --- a/frontend/src/app/models/brapi.model.ts +++ b/frontend/src/app/models/brapi.model.ts @@ -1,4 +1,5 @@ import * as schema from './schema.org.model'; +import { DataDiscoveryFacet } from './data-discovery.model'; export interface GermplasmCriteria { accessionNumbers: string[]; @@ -45,6 +46,12 @@ export interface BrapiResult<T> { result: T; } +export interface GermplasmResult<T> { + metadata: BrapiMetaData; + facets: DataDiscoveryFacet[]; + result: T; +} + /** * BrAPI list response */ @@ -52,6 +59,10 @@ export type BrapiResults<T> = BrapiResult<{ data: T[]; }>; +export type GermplasmResults<T> = GermplasmResult<{ + data: T[]; +}>; + interface BrapiHasDocumentationURL { documentationURL?: string; diff --git a/frontend/src/app/models/data-discovery.model.ts b/frontend/src/app/models/data-discovery.model.ts index d8682915..77e6fcef 100644 --- a/frontend/src/app/models/data-discovery.model.ts +++ b/frontend/src/app/models/data-discovery.model.ts @@ -2,6 +2,7 @@ import { BrapiResults } from './brapi.model'; import { Params } from '@angular/router'; import { asArray } from '../utils'; import * as schema from './schema.org.model'; +import { GermplasmSearchCriteria } from './gnpis.model'; export const MAX_RESULTS = 10000; @@ -42,6 +43,52 @@ export class DataDiscoveryCriteriaUtils { }; } + static emptyGermplasmSearchCriteria(): GermplasmSearchCriteria { + return { + accessionNumbers: null, + germplasmDbIds: null, + germplasmGenus: null, + germplasmNames: null, + germplasmPUIs: null, + germplasmSpecies: null, + holdingInstitute: null, + synonyms: null, + panel: null, + collection: null, + population: null, + commonCropName: null, + species: null, + genusSpecies: null, + subtaxa: null, + genusSpeciesSubtaxa: null, + taxonSynonyms: null, + biologicalStatus: null, + geneticNature: null, + sources: null, + + facetFields: ['holdingInstitute', + 'biologicalStatus', 'geneticNature', 'country'], + sortBy: null, + sortOrder: null, + page: 0, + pageSize: 10, + }; + } + + static checkCriteriaIsEmpty(criteria): boolean { + for (const field of Object.keys(criteria)) { + if (field === 'facetFields') { + // Ignore facet fields criteria + continue; + } + if (criteria[field] && criteria[field].length) { + return false; + } + + } + return true; + } + static fromQueryParams(queryParams: Params): DataDiscoveryCriteria { return { ...DataDiscoveryCriteriaUtils.emptyCriteria(), @@ -73,6 +120,15 @@ export class DataDiscoveryCriteriaUtils { page: newCriteria.page + 1 }; } + + static updatePagination(previousPagination, { currentPage, pageSize, totalCount, totalPages }) { + previousPagination.currentPage = currentPage; + previousPagination.pageSize = pageSize; + previousPagination.totalPages = totalPages; + previousPagination.startResult = pageSize * currentPage + 1; + previousPagination.endResult = previousPagination.startResult + pageSize - 1; + previousPagination.totalResult = totalCount; + } } export type DataDiscoverySource = schema.DataCatalog; diff --git a/frontend/src/app/models/gnpis.model.ts b/frontend/src/app/models/gnpis.model.ts index edee6ddf..0396e0ef 100644 --- a/frontend/src/app/models/gnpis.model.ts +++ b/frontend/src/app/models/gnpis.model.ts @@ -1,5 +1,37 @@ import { BrapiDonor, BrapiGermplasm } from './brapi.model'; + +export interface GermplasmSearchCriteria { + accessionNumbers: string[]; + germplasmDbIds: string[]; + germplasmGenus: string[]; + germplasmNames: string[]; + germplasmPUIs: string[]; + germplasmSpecies: string[]; + + synonyms: string[]; + panel: string[]; + collection: string[]; + population: string[]; + commonCropName: string[]; + species: string[]; + genusSpecies: string[]; + subtaxa: string[]; + genusSpeciesSubtaxa: string[]; + taxonSynonyms: string[]; + biologicalStatus: string[]; + geneticNature: string[]; + holdingInstitute: string[]; + sources: string[]; + + facetFields: string[]; + sortBy: string; + sortOrder: string; + page: number; + pageSize: number; +} + + export interface Germplasm extends BrapiGermplasm { genusSpecies: string; genusSpeciesSubtaxa: string; @@ -23,6 +55,7 @@ export interface Germplasm extends BrapiGermplasm { panel: GermplasmSet[]; collection: GermplasmSet[]; population: GermplasmSet[]; + 'schema:includedInDataCatalog': any; } export interface Site { diff --git a/frontend/src/app/result-page/facets/facets.component.html b/frontend/src/app/result-page/facets/facets.component.html index 40ae6679..d6d41abf 100644 --- a/frontend/src/app/result-page/facets/facets.component.html +++ b/frontend/src/app/result-page/facets/facets.component.html @@ -1,4 +1,4 @@ -<div class="card mb-1"> +<div class="card mb-1" *ngIf="facet.terms.length"> <div class="card-body"> <h3 class="card-title">{{ facet.field | titlecase }}</h3> @@ -11,14 +11,24 @@ /> <label class="form-check-label" for="{{ term.term }}"> {{ term.label }} ({{ term.count | number }}) - <button type="button" class="btn btn-sm btn-outline-secondary" - id="advanceSearch" - [routerLink]="'germplasm-result-page'" - *ngIf="term.term == 'Germplasm' && displayAdvanceGermplasmSearchButton"> - Advance search - <img src="assets/faidare/images/advance_search.png" alt="advance germplasm search" title="" height="20px"/> - </button> + </label> + + <div id="germplasmSearch" + title="Focus on germplasm details with export data link" + *ngIf="term.term == 'Germplasm' && !criteriaIsEmpty"> + <label for="selectSwitchButton" style="margin-right: 5px" + id="switchTitle" + > + Focus + </label> + <label class="switch" id="switchButton"> + <input type="checkbox" id="selectSwitchButton" + (change)="switchToGermplasmResult()"> + <span class="slider round"></span> + </label> + + </div> </div> </form> </div> diff --git a/frontend/src/app/result-page/facets/facets.component.scss b/frontend/src/app/result-page/facets/facets.component.scss index b5e79bd7..8efac972 100644 --- a/frontend/src/app/result-page/facets/facets.component.scss +++ b/frontend/src/app/result-page/facets/facets.component.scss @@ -7,3 +7,72 @@ .card h3 { font-weight: bold; } + +#switchTitle { + cursor: pointer; +} + + + +/* The switch - the box around the slider */ + +.switch { + position: relative; + display: inline-block; + width: 30px; + height: 17px; +} + +/* Hide default HTML checkbox */ +.switch input { + opacity: 0; + width: 0; + height: 0; +} +/* The slider */ +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: .4s; + transition: .4s; +} + +.slider:before { + position: absolute; + content: ""; + height: 13px; + width: 13px; + left: 2px; + bottom: 2px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; +} + +input:checked + .slider { + background-color: #2196F3; +} + +input:focus + .slider { + box-shadow: 0 0 1px #2196F3; +} + +input:checked + .slider:before { + -webkit-transform: translateX(15px); + -ms-transform: translateX(15px); + transform: translateX(15px); +} + +/* Rounded sliders */ +.slider.round { + border-radius: 17px; +} + +.slider.round:before { + border-radius: 50%; +} diff --git a/frontend/src/app/result-page/facets/facets.component.ts b/frontend/src/app/result-page/facets/facets.component.ts index f7c1d92d..57250405 100644 --- a/frontend/src/app/result-page/facets/facets.component.ts +++ b/frontend/src/app/result-page/facets/facets.component.ts @@ -1,11 +1,16 @@ import { Component, Input, OnInit } from '@angular/core'; import { DataDiscoveryCriteria, + DataDiscoveryCriteriaUtils, DataDiscoveryFacet } from '../../models/data-discovery.model'; import { FormControl, FormGroup } from '@angular/forms'; import { BehaviorSubject } from 'rxjs'; import { filter } from 'rxjs/operators'; +import { GnpisService } from '../../gnpis.service'; +import { Params } from '@angular/router'; +import { asArray } from '../../utils'; +import { GermplasmSearchCriteria } from '../../models/gnpis.model'; @Component({ selector: 'faidare-facets', @@ -16,13 +21,18 @@ export class FacetsComponent implements OnInit { @Input() facet: DataDiscoveryFacet; @Input() criteria$: BehaviorSubject<DataDiscoveryCriteria>; + @Input() germplasmSearchCriteria$: BehaviorSubject<GermplasmSearchCriteria>; + @Input() displayGermplasmResult$: BehaviorSubject<boolean>; localCriteria: DataDiscoveryCriteria; + germplasmLocalCriteria: GermplasmSearchCriteria; + criteriaIsEmpty = true; + queryParams: Params; checkBoxes: FormGroup = new FormGroup({}); displayAdvanceGermplasmSearchButton: boolean; - constructor() { + constructor(private gnpisService: GnpisService) { } ngOnInit(): void { @@ -30,37 +40,106 @@ export class FacetsComponent implements OnInit { const control = new FormControl(false); this.checkBoxes.addControl(term.term, control); } + if (this.facet.field === 'types') { + const switchControl = new FormControl(false); + this.checkBoxes.addControl('selectSwitchButton', switchControl); + } - this.criteria$.pipe(filter(c => c !== this.localCriteria)) - .subscribe(criteria => { - this.localCriteria = criteria; - const selectedTerms = criteria[this.facet.field] || []; - - for (const [key, control] of Object.entries(this.checkBoxes.controls)) { - const isSelected = selectedTerms.indexOf(key) >= 0; - control.setValue(isSelected, { emitEvent: false }); - } + if (this.criteria$) { + this.criteria$.pipe(filter(c => c !== this.localCriteria)) + .subscribe(criteria => { + this.localCriteria = criteria; + this.getSelectedTerms(criteria); - if (criteria.types) { - this.showANDHideAdvanceGermplasmSearch(criteria.types); - } + if (criteria.types) { + this.showAndHideAdvanceGermplasmSearch(criteria.types); + } + }); + } - }); + if (this.germplasmSearchCriteria$) { + this.germplasmSearchCriteria$.pipe(filter(c => c !== this.germplasmLocalCriteria)) + .subscribe(germplasmCriteria => { + this.germplasmLocalCriteria = germplasmCriteria; + this.getSelectedTerms(germplasmCriteria); + }); + } this.checkBoxes.valueChanges.subscribe(values => { const selectedTerms = Object.keys(values).filter(key => values[key]); - this.showANDHideAdvanceGermplasmSearch(selectedTerms); - this.localCriteria = { - ...this.localCriteria, - [this.facet.field]: selectedTerms - }; - this.criteria$.next(this.localCriteria); + this.showAndHideAdvanceGermplasmSearch(selectedTerms); + if (this.criteria$) { + this.localCriteria = { + ...this.localCriteria, + [this.facet.field]: selectedTerms + }; + this.criteria$.next(this.localCriteria); + } + if (this.germplasmSearchCriteria$) { + const field = this.facet.field; + // console.log(field); + if (field === 'holding institute') { + this.germplasmLocalCriteria = { + ...this.germplasmLocalCriteria, + holdingInstitute: selectedTerms + }; + } + if (field === 'Biological status / Genetic nature') { + this.germplasmLocalCriteria = { + ...this.germplasmLocalCriteria, + biologicalStatus: selectedTerms, + geneticNature: selectedTerms + }; + } + if (field !== 'Biological status / Genetic nature' && field !== 'holding institute') { + this.germplasmLocalCriteria = { + ...this.germplasmLocalCriteria, + [this.facet.field]: selectedTerms + }; + } + this.germplasmSearchCriteria$.next(this.germplasmLocalCriteria); + } }); } - showANDHideAdvanceGermplasmSearch(typeList: String[]) { + getSelectedTerms(criteria) { + const selectedTerms = criteria[this.facet.field] || []; + + for (const [key, control] of Object.entries(this.checkBoxes.controls)) { + const isSelected = selectedTerms.indexOf(key) >= 0; + control.setValue(isSelected, { emitEvent: false }); + } + + // this.queryParams = this.queryParamsForGermplasmPage(criteria); + + this.criteriaIsEmpty = DataDiscoveryCriteriaUtils.checkCriteriaIsEmpty(criteria); + } + + showAndHideAdvanceGermplasmSearch(typeList: String[]) { const facetIsTypes = this.facet.field === 'types'; const GermplasmSelected = typeList.includes('Germplasm'); this.displayAdvanceGermplasmSearchButton = facetIsTypes && GermplasmSelected; } + + switchToGermplasmResult() { + let currentState = false; + this.displayGermplasmResult$.subscribe(value => { + currentState = value; + }); + for (const [key, control] of Object.entries(this.checkBoxes.controls)) { + if (key === 'selectSwitchButton') { + control.setValue(currentState, { emitEvent: false }); + } + } + this.displayGermplasmResult$.next(!currentState); + } + + queryParamsForGermplasmPage(criteria: DataDiscoveryCriteria) { + return { + crops: asArray(criteria.crops), + germplasmLists: asArray(criteria.germplasmLists), + accessions: asArray(criteria.accessions), + sources: asArray(criteria.sources) + }; + } } diff --git a/frontend/src/app/result-page/result-page.component.html b/frontend/src/app/result-page/result-page.component.html index 234ba4a4..979195c8 100644 --- a/frontend/src/app/result-page/result-page.component.html +++ b/frontend/src/app/result-page/result-page.component.html @@ -1,22 +1,6 @@ <h3 align="center" class="mb-4">{{ appTitle }}</h3> <div class="row justify-content-end"> - <!-- Column for form --> - <div class="col-lg-9"> - <!-- Reset all button --> - <div class="text-right reset-all-div"> - <button type="button" class="btn btn-sm btn-danger mt-1" (click)="resetAll()"> - Reset all - </button> - </div> - - <!-- Form --> - <faidare-form - #form - [criteria$]="criteria$"> - </faidare-form> - </div> - <!-- Column for facets --> <div class="col-lg-3 order-lg-first"> <div class="row"> @@ -24,34 +8,63 @@ class="col-12 col-lg-12 col-sm-6" *ngFor="let facet of facets" [criteria$]="criteria$" - [facet]="facet"> + [facet]="facet" + [displayGermplasmResult$]="displayGermplasmResult$"> + </faidare-facets> + </div> + <div class="row" *ngIf="germplasmfacets.length && displayGermplasmResult"> + <faidare-facets + class="col-12 col-lg-12 col-sm-6" + *ngFor="let facet of germplasmfacets" + [germplasmSearchCriteria$]="germplasmSearchCriteria$" + [facet]="facet" + [displayGermplasmResult$]="displayGermplasmResult$"> </faidare-facets> </div> </div> - <!-- Column for result --> + <!-- Column for form and results--> <div class="col-lg-9"> - <!-- Loading spinner--> + <!-- Reset all button --> + <div class="text-right reset-all-div"> + <button type="button" class="btn btn-sm btn-danger mt-1" + (click)="resetAll()"> + Reset all + </button> + </div> + + <!-- Form --> + <faidare-form + #form + [criteria$]="criteria$" + [displayGermplasmResult$]="displayGermplasmResult$"> + </faidare-form> + + <!-- Loading spinner--> <div class="text-center"> <faidare-loading-spinner [loading]="loading"></faidare-loading-spinner> </div> <!-- No criteria selected --> - <div *ngIf="criteriaIsEmpty && !loading" class="text-center text-muted mt-5 bolder"> + <div *ngIf="criteriaIsEmpty && !loading" + class="text-center text-muted mt-5 bolder"> Select criteria to get results. </div> <!-- Display results when possible --> - <ng-container *ngIf="documents.length && !criteriaIsEmpty && !loading"> + <ng-container + *ngIf="documents.length && !criteriaIsEmpty && !loading && !displayGermplasmResult"> <!-- Pagination status --> <div class="container"> <div class="row result align-content-center"> <span class="col-4 mt-2 bolder"> Results: </span> - <span *ngIf="pagination.totalResult" class="col-8 text-right small text-muted mt-3"> - From {{ pagination.startResult | number }} to {{ pagination.endResult | number }} + <span *ngIf="pagination.totalResult" + class="col-8 text-right small text-muted mt-3"> + From {{ pagination.startResult | number }} + to {{ pagination.endResult | number }} over {{ pagination.totalResult | number }} documents <span *ngIf="pagination.totalResult > pagination.maxResults"> (limited to {{ pagination.maxResults | number }}) @@ -61,7 +74,8 @@ </div> <!--Top page navigator--> - <div class="d-flex justify-content-center mt-3" *ngIf="pagination.totalPages > 1"> + <div class="d-flex justify-content-center mt-3" + *ngIf="pagination.totalPages > 1"> <!-- we add 1 to the page because ngb-pagination is 1 based --> <ngb-pagination [page]="pagination.currentPage + 1" (pageChange)="changePage($event)" @@ -82,7 +96,8 @@ <!-- Pagination --> <!--Bottom page navigator--> - <div class="d-flex justify-content-center mt-4 mb-5" *ngIf="pagination.totalPages > 1"> + <div class="d-flex justify-content-center mt-4 mb-5" + *ngIf="pagination.totalPages > 1"> <!-- we add 1 to the page because ngb-pagination is 1 based --> <ngb-pagination [page]="pagination.currentPage + 1" (pageChange)="changePage($event)" @@ -96,8 +111,18 @@ </div> </ng-container> + <ng-container *ngIf="displayGermplasmResult"> + + <faidare-germplasm-result-page + [criteriaFromForm$]=criteria$ + [germplasmSearchCriteria$]="germplasmSearchCriteria$" + [germplasmFacets$]="germplasmfacets$"> + </faidare-germplasm-result-page> + </ng-container> + <!-- Else we display a simple message when no result found --> - <div *ngIf="pagination.totalResult == 0 && !loading && !criteriaIsEmpty" id="no-results" class="text-center"> + <div *ngIf="pagination.totalResult == 0 && !loading && !criteriaIsEmpty" + id="no-results" class="text-center"> <div class="no-result-icon"> <span class="fa fa-meh-o"></span> </div> diff --git a/frontend/src/app/result-page/result-page.component.ts b/frontend/src/app/result-page/result-page.component.ts index 9f9d7dbd..9bc1569b 100644 --- a/frontend/src/app/result-page/result-page.component.ts +++ b/frontend/src/app/result-page/result-page.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, ViewChild } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, NavigationStart, Router } from '@angular/router'; import { DataDiscoveryCriteria, DataDiscoveryCriteriaUtils, @@ -13,6 +13,7 @@ import { GnpisService } from '../gnpis.service'; import { filter } from 'rxjs/operators'; import { FormComponent } from '../form/form.component'; import { environment } from '../../environments/environment'; +import { GermplasmSearchCriteria } from '../models/gnpis.model'; @Component({ @@ -28,6 +29,9 @@ export class ResultPageComponent implements OnInit { criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(DataDiscoveryCriteriaUtils.emptyCriteria()); documents: DataDiscoveryDocument[] = []; facets: DataDiscoveryFacet[] = []; + germplasmSearchCriteria$ = new BehaviorSubject<GermplasmSearchCriteria>(DataDiscoveryCriteriaUtils.emptyGermplasmSearchCriteria()); + germplasmfacets$ = new BehaviorSubject<DataDiscoveryFacet[]>([]); + germplasmfacets: DataDiscoveryFacet[]; pagination = { startResult: 1, endResult: DEFAULT_PAGE_SIZE, @@ -39,6 +43,8 @@ export class ResultPageComponent implements OnInit { }; criteriaIsEmpty = true; + displayGermplasmResult$ = new BehaviorSubject(false); + displayGermplasmResult = false; loading = true; constructor(private route: ActivatedRoute, @@ -54,28 +60,38 @@ export class ResultPageComponent implements OnInit { .subscribe(({ metadata, result, facets }) => { this.loading = false; this.documents = result.data; - this.updatePagination(metadata.pagination); + DataDiscoveryCriteriaUtils.updatePagination(this.pagination, metadata.pagination); this.facets = facets; }); } - private updatePagination({ currentPage, pageSize, totalCount, totalPages }) { + // TODO : delete because move to DataDiscoveryCriteriaUtils + /*private updatePagination({ currentPage, pageSize, totalCount, totalPages }) { this.pagination.currentPage = currentPage; this.pagination.pageSize = pageSize; this.pagination.totalPages = totalPages; this.pagination.startResult = pageSize * currentPage + 1; this.pagination.endResult = this.pagination.startResult + pageSize - 1; this.pagination.totalResult = totalCount; - } + }*/ ngOnInit(): void { const queryParams = this.route.snapshot.queryParams; + this.router.events.subscribe((event) => { + if (event instanceof NavigationStart) { + this.displayGermplasmResult = false; + } + }); // Parse criteria from URL query params const initialCriteria = DataDiscoveryCriteriaUtils.fromQueryParams(queryParams); this.criteria$.next(initialCriteria); - this.criteria$.subscribe(criteria => { + this.criteriaIsEmpty = DataDiscoveryCriteriaUtils.checkCriteriaIsEmpty(initialCriteria); + + + // TODO : delete because move to DataDiscoveryCriteriaUtils + /*this.criteria$.subscribe(criteria => { this.criteriaIsEmpty = true; for (const field of Object.keys(criteria)) { if (field === 'facetFields') { @@ -88,7 +104,7 @@ export class ResultPageComponent implements OnInit { break; } } - }); + });*/ this.form.traitWidgetInitialized.subscribe(() => { this.fetchDocumentsAndFacets(); @@ -97,17 +113,31 @@ export class ResultPageComponent implements OnInit { this.criteria$ .pipe(filter(c => c !== initialCriteria)) .subscribe(newCriteria => { + this.displayGermplasmResult = false; // Reset pagination newCriteria.page = 0; // Fetch documents and facets this.fetchDocumentsAndFacets(); + this.criteriaIsEmpty = DataDiscoveryCriteriaUtils.checkCriteriaIsEmpty(newCriteria); // Update URL query params this.router.navigate(['.'], { relativeTo: this.route, queryParams: DataDiscoveryCriteriaUtils.toQueryParams(newCriteria) }); + this.displayGermplasmResult$.subscribe(value => { + this.displayGermplasmResult = value; + }); + this.displayGermplasmResult$.next(this.displayGermplasmResult); }); + + this.displayGermplasmResult$.subscribe(value => { + this.displayGermplasmResult = value; + }); + + this.germplasmfacets$.subscribe(facets => { + this.germplasmfacets = facets; + }); } resultCount() { @@ -124,6 +154,7 @@ export class ResultPageComponent implements OnInit { // Use of empty param to force re-parse of URL in ngOnInit queryParams: { empty: [] } }); + this.displayGermplasmResult$.next(false); this.criteria$.next(DataDiscoveryCriteriaUtils.emptyCriteria()); } diff --git a/frontend/src/assets/faidare/images/csv-logo.png b/frontend/src/assets/faidare/images/csv-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..abf670cf659222944f7e717d563ddea72715d75e GIT binary patch literal 32999 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 literal 0 HcmV?d00001 diff --git a/frontend/src/tslint.json b/frontend/src/tslint.json index 97be2aaf..f1b7fa4d 100644 --- a/frontend/src/tslint.json +++ b/frontend/src/tslint.json @@ -15,7 +15,7 @@ ], "template-cyclomatic-complexity": [ true, - 11 + 12 ] } } -- GitLab From 07ae625897404377de2d81acbb1d4772c0d1ff0c Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Thu, 5 Dec 2019 14:22:46 +0100 Subject: [PATCH 07/35] feat: Create component to manage large facets number. GNP-4309 --- frontend/src/app/app.module.ts | 4 +- frontend/src/app/gnpis.service.ts | 2 + .../result-page/facets/facets.component.scss | 2 +- .../result-page/facets/facets.component.ts | 14 +- .../large-facets/large-facets.component.html | 29 ++++ .../large-facets/large-facets.component.scss | 17 ++ .../large-facets.component.spec.ts | 25 +++ .../large-facets/large-facets.component.ts | 160 ++++++++++++++++++ .../result-page/result-page.component.html | 23 ++- .../app/result-page/result-page.component.ts | 2 +- 10 files changed, 257 insertions(+), 21 deletions(-) create mode 100644 frontend/src/app/result-page/large-facets/large-facets.component.html create mode 100644 frontend/src/app/result-page/large-facets/large-facets.component.scss create mode 100644 frontend/src/app/result-page/large-facets/large-facets.component.spec.ts create mode 100644 frontend/src/app/result-page/large-facets/large-facets.component.ts diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index bb1bb434..868b75c6 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -40,6 +40,7 @@ import { CardGenericDocumentComponent } from './card-generic-document/card-gener import { MarkdownModule, MarkedOptions, MarkedRenderer } from 'ngx-markdown'; import { MarkdownPageComponent } from './markdown-page/markdown-page.component'; import { DecimalPipe } from '@angular/common'; +import { LargeFacetsComponent } from './result-page/large-facets/large-facets.component'; @NgModule({ declarations: [ @@ -64,7 +65,8 @@ import { DecimalPipe } from '@angular/common'; CardTableComponent, XrefsComponent, CardGenericDocumentComponent, - MarkdownPageComponent + MarkdownPageComponent, + LargeFacetsComponent ], imports: [ BrowserModule, diff --git a/frontend/src/app/gnpis.service.ts b/frontend/src/app/gnpis.service.ts index ed80102f..8561ef5c 100644 --- a/frontend/src/app/gnpis.service.ts +++ b/frontend/src/app/gnpis.service.ts @@ -28,6 +28,7 @@ export class GnpisService { static URGI_SOURCE_URI = 'https://urgi.versailles.inra.fr'; sourceByURI$ = new ReplaySubject<Record<string, DataDiscoverySource>>(1); + sources$ = new ReplaySubject<DataDiscoverySource[]>(1); constructor(private http: HttpClient) { // Get data sources @@ -39,6 +40,7 @@ export class GnpisService { for (const dataSource of dataSources) { sourceByURI[dataSource['@id']] = dataSource; } + this.sources$.next(dataSources); this.sourceByURI$.next(sourceByURI); }); } diff --git a/frontend/src/app/result-page/facets/facets.component.scss b/frontend/src/app/result-page/facets/facets.component.scss index 8efac972..3fdb775c 100644 --- a/frontend/src/app/result-page/facets/facets.component.scss +++ b/frontend/src/app/result-page/facets/facets.component.scss @@ -1,7 +1,7 @@ .card * { - font-size: 0.98em; + font-size: 1em; } .card h3 { diff --git a/frontend/src/app/result-page/facets/facets.component.ts b/frontend/src/app/result-page/facets/facets.component.ts index 57250405..0f1750ff 100644 --- a/frontend/src/app/result-page/facets/facets.component.ts +++ b/frontend/src/app/result-page/facets/facets.component.ts @@ -7,9 +7,7 @@ import { import { FormControl, FormGroup } from '@angular/forms'; import { BehaviorSubject } from 'rxjs'; import { filter } from 'rxjs/operators'; -import { GnpisService } from '../../gnpis.service'; import { Params } from '@angular/router'; -import { asArray } from '../../utils'; import { GermplasmSearchCriteria } from '../../models/gnpis.model'; @Component({ @@ -32,7 +30,7 @@ export class FacetsComponent implements OnInit { checkBoxes: FormGroup = new FormGroup({}); displayAdvanceGermplasmSearchButton: boolean; - constructor(private gnpisService: GnpisService) { + constructor() { } ngOnInit(): void { @@ -77,7 +75,6 @@ export class FacetsComponent implements OnInit { } if (this.germplasmSearchCriteria$) { const field = this.facet.field; - // console.log(field); if (field === 'holding institute') { this.germplasmLocalCriteria = { ...this.germplasmLocalCriteria, @@ -133,13 +130,4 @@ export class FacetsComponent implements OnInit { } this.displayGermplasmResult$.next(!currentState); } - - queryParamsForGermplasmPage(criteria: DataDiscoveryCriteria) { - return { - crops: asArray(criteria.crops), - germplasmLists: asArray(criteria.germplasmLists), - accessions: asArray(criteria.accessions), - sources: asArray(criteria.sources) - }; - } } diff --git a/frontend/src/app/result-page/large-facets/large-facets.component.html b/frontend/src/app/result-page/large-facets/large-facets.component.html new file mode 100644 index 00000000..1f6ed4ee --- /dev/null +++ b/frontend/src/app/result-page/large-facets/large-facets.component.html @@ -0,0 +1,29 @@ +<ng-template #resultTemplate let-facet="result" let-t="term"> + <ng-container *ngIf="facet !== 'REFINE'; else refine"> + <ngb-highlight [result]="displayableKey(facet.label)" [term]="t"></ngb-highlight> + <small class="ml-1 text-muted">({{ facet.count | number }})</small> + </ng-container> + <ng-template #refine> + <div class="text-muted small">Other results are available.<br/>Refine your search.</div> + </ng-template> +</ng-template> + + +<div class="mb-2"> + <span class="badge badge-pill badge-secondary mr-1" *ngFor="let term of selectedTerms[facet.field]" tabindex="0" + (keydown.delete)="removeKey(term)" + (keydown.backspace)="removeKey(term)"> {{ displaySourceName(term) }} + <button tabindex="-1" type="button" class="btn btn-link" (click)="removeKey(term)">×</button> + </span> +</div> +<div class="card mb-1" *ngIf="facet.terms.length"> + <div class="card-body"> + <h3 class="card-title">{{ facet.field | titlecase }}</h3> + <input #typeahead class="form-control" [formControl]="criterion" + [ngbTypeahead]="search" + (selectItem)="selectKey($event)" [resultTemplate]="resultTemplate" + + placeholder="Filter on {{ facet.field.toLowerCase() }}..." + (focus)="focus$.next($event.target.value)"/> + </div> +</div> diff --git a/frontend/src/app/result-page/large-facets/large-facets.component.scss b/frontend/src/app/result-page/large-facets/large-facets.component.scss new file mode 100644 index 00000000..21fbbb70 --- /dev/null +++ b/frontend/src/app/result-page/large-facets/large-facets.component.scss @@ -0,0 +1,17 @@ + +.card * { + font-size: 1em; +} + +.btn-link { + border-top: 0; + border-bottom: 0; + margin-top: 0; + margin-bottom: 0; + padding: 0; + color: white; + vertical-align: baseline; + text-decoration: none; + font-size: inherit; +} + diff --git a/frontend/src/app/result-page/large-facets/large-facets.component.spec.ts b/frontend/src/app/result-page/large-facets/large-facets.component.spec.ts new file mode 100644 index 00000000..5d800998 --- /dev/null +++ b/frontend/src/app/result-page/large-facets/large-facets.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LargeFacetsComponent } from './large-facets.component'; + +describe('LargeFacetsComponent', () => { + let component: LargeFacetsComponent; + let fixture: ComponentFixture<LargeFacetsComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ LargeFacetsComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LargeFacetsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/result-page/large-facets/large-facets.component.ts b/frontend/src/app/result-page/large-facets/large-facets.component.ts new file mode 100644 index 00000000..6f178ff2 --- /dev/null +++ b/frontend/src/app/result-page/large-facets/large-facets.component.ts @@ -0,0 +1,160 @@ +import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; +import { + DataDiscoveryCriteria, + DataDiscoveryFacet, + DataDiscoverySource +} from '../../models/data-discovery.model'; +import { BehaviorSubject, merge, Observable, Subject } from 'rxjs'; +import { distinctUntilChanged, filter, map } from 'rxjs/operators'; +import { FormControl } from '@angular/forms'; +import { NgbTypeaheadSelectItemEvent } from '@ng-bootstrap/ng-bootstrap'; +import { GermplasmSearchCriteria } from '../../models/gnpis.model'; +import { GnpisService } from '../../gnpis.service'; + +export type FacetTermOrRefine = { + term: string; + label: string; + count: number; +} | 'REFINE'; +const maxResultsDisplayed = 8; + +@Component({ + selector: 'faidare-large-facets', + templateUrl: './large-facets.component.html', + styleUrls: ['./large-facets.component.scss'] +}) +export class LargeFacetsComponent implements OnInit { + + @Input() facet: DataDiscoveryFacet; + @Input() criteria$: BehaviorSubject<DataDiscoveryCriteria>; + @Input() germplasmSearchCriteria$: BehaviorSubject<GermplasmSearchCriteria>; + + @ViewChild('typeahead') typeahead: ElementRef<HTMLInputElement>; + + localCriteria: DataDiscoveryCriteria; + germplasmLocalCriteria: GermplasmSearchCriteria; + focus$ = new Subject<string>(); + selectedTerms: { [key: string]: string[]; } = {}; + criterion = new FormControl(''); + sources: DataDiscoverySource[]; + + constructor(private gnpisService: GnpisService) { + } + + ngOnInit(): void { + + this.gnpisService.sources$.subscribe(sources => { + this.sources = sources; + }); + + if (this.criteria$) { + this.criteria$.pipe(filter(c => c !== this.localCriteria)) + .subscribe(criteria => { + this.localCriteria = { ...criteria }; + this.selectedTerms[this.facet.field] = criteria[this.facet.field] || []; + }); + } + + if (this.germplasmSearchCriteria$) { + this.germplasmSearchCriteria$.pipe(filter(c => c !== this.germplasmLocalCriteria)) + .subscribe(germplasmCriteria => { + this.germplasmLocalCriteria = germplasmCriteria; + this.selectedTerms[this.facet.field] = germplasmCriteria[this.facet.field] || []; + }); + } + } + + search = (text$: Observable<string>): Observable<Array<FacetTermOrRefine>> => { + const inputFocus$ = this.focus$; + const merged$ = merge(text$, inputFocus$) + .pipe( + distinctUntilChanged(), + map(searchTerm => { + const allMatchingBuckets = this.facet.terms + // returns values not already selected + .filter(terms => !this.selectedTerms[this.facet.field].includes(terms.term) + // and that contains the term, ignoring the case + && this.displayableKey(terms.label).toLowerCase().includes(searchTerm.toString().toLowerCase())); + + // return the first N results + const result: Array<FacetTermOrRefine> = allMatchingBuckets.slice(0, maxResultsDisplayed); + + // if more results exist, add a fake refine bucket + if (allMatchingBuckets.length > maxResultsDisplayed) { + result.push('REFINE'); + } + + return result; + })); + return merged$; + }; + + displayableKey(key: string): string { + return key === 'NULL' ? 'None' : key; + } + + displaySourceName(sourceId: string) { + for (const source of this.sources) { + if (source['@id'] === sourceId) { + return source['schema:name']; + } + } + return sourceId; + } + + selectKey(event: NgbTypeaheadSelectItemEvent) { + event.preventDefault(); + const selected: FacetTermOrRefine = event.item; + if (selected !== 'REFINE') { + // the item field of the event contains the facet term + // we push the selected key to our collection of keys + if (this.criteria$) { + if (this.localCriteria[this.facet.field]) { + this.localCriteria[this.facet.field].push(event.item.term); + } else { + this.localCriteria[this.facet.field] = [event.item.term]; + } + } + if (this.germplasmSearchCriteria$) { + if (this.germplasmLocalCriteria[this.facet.field]) { + this.germplasmLocalCriteria[this.facet.field].push(event.item.term); + } else { + this.germplasmLocalCriteria[this.facet.field] = [event.item.term]; + } + } + this.emitChanges(); + } + this.typeahead.nativeElement.blur(); + } + + emitChanges() { + if (this.criteria$) { + this.criteria$.next(this.localCriteria); + } + if (this.germplasmSearchCriteria$) { + this.germplasmSearchCriteria$.next(this.germplasmLocalCriteria); + } + } + + removeKey(key: string) { + this.selectedTerms[this.facet.field] = + this.removeFromList(this.selectedTerms[this.facet.field], key); + if (this.criteria$) { + this.localCriteria[this.facet.field] = + this.removeFromList(this.localCriteria[this.facet.field], key); + } + if (this.germplasmSearchCriteria$) { + this.germplasmLocalCriteria[this.facet.field] = + this.removeFromList(this.germplasmLocalCriteria[this.facet.field], key); + } + this.emitChanges(); + } + + removeFromList(list: string[], elem: string) { + const index = list.indexOf(elem); + return [ + ...list.slice(0, index), + ...list.slice(index + 1)]; + } + +} diff --git a/frontend/src/app/result-page/result-page.component.html b/frontend/src/app/result-page/result-page.component.html index 979195c8..f0bc8585 100644 --- a/frontend/src/app/result-page/result-page.component.html +++ b/frontend/src/app/result-page/result-page.component.html @@ -12,7 +12,9 @@ [displayGermplasmResult$]="displayGermplasmResult$"> </faidare-facets> </div> - <div class="row" *ngIf="germplasmfacets.length && displayGermplasmResult"> + + + <!--<div class="row" *ngIf="germplasmfacets.length && displayGermplasmResult"> <faidare-facets class="col-12 col-lg-12 col-sm-6" *ngFor="let facet of germplasmfacets" @@ -20,8 +22,19 @@ [facet]="facet" [displayGermplasmResult$]="displayGermplasmResult$"> </faidare-facets> + </div>--> + + <div class="row" *ngIf="germplasmfacets.length && displayGermplasmResult"> + <faidare-large-facets + class="col-12 col-lg-12 col-sm-6" + *ngFor="let facet of germplasmfacets" + [criteria$]="criteria$" + [facet]="facet" + [germplasmSearchCriteria$]="germplasmSearchCriteria$"> + </faidare-large-facets> </div> + </div> <!-- Column for form and results--> @@ -36,12 +49,12 @@ <!-- Form --> <faidare-form - #form - [criteria$]="criteria$" - [displayGermplasmResult$]="displayGermplasmResult$"> + #form + [criteria$]="criteria$" + [displayGermplasmResult$]="displayGermplasmResult$"> </faidare-form> - <!-- Loading spinner--> + <!-- Loading spinner--> <div class="text-center"> <faidare-loading-spinner [loading]="loading"></faidare-loading-spinner> </div> diff --git a/frontend/src/app/result-page/result-page.component.ts b/frontend/src/app/result-page/result-page.component.ts index 9bc1569b..53514f92 100644 --- a/frontend/src/app/result-page/result-page.component.ts +++ b/frontend/src/app/result-page/result-page.component.ts @@ -31,7 +31,7 @@ export class ResultPageComponent implements OnInit { facets: DataDiscoveryFacet[] = []; germplasmSearchCriteria$ = new BehaviorSubject<GermplasmSearchCriteria>(DataDiscoveryCriteriaUtils.emptyGermplasmSearchCriteria()); germplasmfacets$ = new BehaviorSubject<DataDiscoveryFacet[]>([]); - germplasmfacets: DataDiscoveryFacet[]; + germplasmfacets: DataDiscoveryFacet[] = []; pagination = { startResult: 1, endResult: DEFAULT_PAGE_SIZE, -- GitLab From 51c7f314751edf7737674fb9b0438087e38180e4 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Tue, 17 Dec 2019 17:20:45 +0100 Subject: [PATCH 08/35] fix: Fix the consistency between the switch on/off button and the display of the germplasm results. Isolate the facets in a component to manage the large or small display of the facets. GNP-4309. --- frontend/src/app/app.module.ts | 8 ++- frontend/src/app/facets/facets.component.html | 22 ++++++++ frontend/src/app/facets/facets.component.scss | 0 .../src/app/facets/facets.component.spec.ts | 25 +++++++++ frontend/src/app/facets/facets.component.ts | 26 +++++++++ .../large-facets/large-facets.component.html | 29 ++++++---- .../large-facets/large-facets.component.scss | 0 .../large-facets.component.spec.ts | 25 +++++++++ .../large-facets/large-facets.component.ts | 6 +- .../small-facets/small-facets.component.html} | 8 +-- .../small-facets/small-facets.component.scss} | 3 +- .../small-facets.component.spec.ts} | 17 +++--- .../small-facets/small-facets.component.ts} | 55 ++++++++----------- .../germplasm-result-page.component.html | 5 ++ .../germplasm-result-page.component.ts | 5 +- .../large-facets.component.spec.ts | 25 --------- .../result-page/result-page.component.html | 46 +++++----------- .../app/result-page/result-page.component.ts | 4 +- 18 files changed, 186 insertions(+), 123 deletions(-) create mode 100644 frontend/src/app/facets/facets.component.html create mode 100644 frontend/src/app/facets/facets.component.scss create mode 100644 frontend/src/app/facets/facets.component.spec.ts create mode 100644 frontend/src/app/facets/facets.component.ts rename frontend/src/app/{result-page => facets}/large-facets/large-facets.component.html (58%) rename frontend/src/app/{result-page => facets}/large-facets/large-facets.component.scss (100%) create mode 100644 frontend/src/app/facets/large-facets/large-facets.component.spec.ts rename frontend/src/app/{result-page => facets}/large-facets/large-facets.component.ts (98%) rename frontend/src/app/{result-page/facets/facets.component.html => facets/small-facets/small-facets.component.html} (86%) rename frontend/src/app/{result-page/facets/facets.component.scss => facets/small-facets/small-facets.component.scss} (99%) rename frontend/src/app/{result-page/facets/facets.component.spec.ts => facets/small-facets/small-facets.component.spec.ts} (92%) rename frontend/src/app/{result-page/facets/facets.component.ts => facets/small-facets/small-facets.component.ts} (71%) delete mode 100644 frontend/src/app/result-page/large-facets/large-facets.component.spec.ts diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 868b75c6..f10c08e8 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -28,7 +28,7 @@ import { DocumentComponent } from './result-page/document/document.component'; import { ErrorComponent } from './error/error.component'; import { ErrorInterceptorService } from './error-interceptor.service'; import { TraitOntologyWidgetComponent } from './form/trait-ontology-widget/trait-ontology-widget.component'; -import { FacetsComponent } from './result-page/facets/facets.component'; +import { FacetsComponent } from './facets/facets.component'; import { CardRowComponent } from './card-row/card-row.component'; import { CardSectionComponent } from './card-section/card-section.component'; import { LoadingSpinnerComponent } from './loading-spinner/loading-spinner.component'; @@ -40,7 +40,8 @@ import { CardGenericDocumentComponent } from './card-generic-document/card-gener import { MarkdownModule, MarkedOptions, MarkedRenderer } from 'ngx-markdown'; import { MarkdownPageComponent } from './markdown-page/markdown-page.component'; import { DecimalPipe } from '@angular/common'; -import { LargeFacetsComponent } from './result-page/large-facets/large-facets.component'; +import { LargeFacetsComponent } from './facets/large-facets/large-facets.component'; +import { SmallFacetsComponent } from './facets/small-facets/small-facets.component'; @NgModule({ declarations: [ @@ -66,7 +67,8 @@ import { LargeFacetsComponent } from './result-page/large-facets/large-facets.co XrefsComponent, CardGenericDocumentComponent, MarkdownPageComponent, - LargeFacetsComponent + LargeFacetsComponent, + SmallFacetsComponent ], imports: [ BrowserModule, diff --git a/frontend/src/app/facets/facets.component.html b/frontend/src/app/facets/facets.component.html new file mode 100644 index 00000000..4b7d99ff --- /dev/null +++ b/frontend/src/app/facets/facets.component.html @@ -0,0 +1,22 @@ + + +<div class="row"> + <faidare-small-facets + class="col-12 col-lg-12 col-sm-6" + *ngFor="let facet of facets" + [criteria$]="criteria$" + [facet]="facet" + [germplasmSearchCriteria$]="germplasmSearchCriteria$" + [displayGermplasmResult$]="displayGermplasmResult$"> + </faidare-small-facets> +</div> + +<div class="row"> + <faidare-large-facets + class="col-12 col-lg-12 col-sm-6" + *ngFor="let facet of facets" + [criteria$]="criteria$" + [facet]="facet" + [germplasmSearchCriteria$]="germplasmSearchCriteria$"> + </faidare-large-facets> +</div> diff --git a/frontend/src/app/facets/facets.component.scss b/frontend/src/app/facets/facets.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/frontend/src/app/facets/facets.component.spec.ts b/frontend/src/app/facets/facets.component.spec.ts new file mode 100644 index 00000000..5a1e3ba8 --- /dev/null +++ b/frontend/src/app/facets/facets.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { FacetsComponent } from './facets.component'; + + +describe('LargeFacetsComponent', () => { + let component: FacetsComponent; + let fixture: ComponentFixture<FacetsComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [FacetsComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FacetsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/facets/facets.component.ts b/frontend/src/app/facets/facets.component.ts new file mode 100644 index 00000000..2f6f3264 --- /dev/null +++ b/frontend/src/app/facets/facets.component.ts @@ -0,0 +1,26 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { + DataDiscoveryCriteria, + DataDiscoveryFacet +} from '../models/data-discovery.model'; +import { BehaviorSubject } from 'rxjs'; +import { GermplasmSearchCriteria } from '../models/gnpis.model'; + +@Component({ + selector: 'faidare-facets', + templateUrl: './facets.component.html', + styleUrls: ['./facets.component.scss'] +}) +export class FacetsComponent implements OnInit { + + @Input() facets: DataDiscoveryFacet[]; + @Input() criteria$: BehaviorSubject<DataDiscoveryCriteria>; + @Input() germplasmSearchCriteria$: BehaviorSubject<GermplasmSearchCriteria>; + @Input() displayGermplasmResult$: BehaviorSubject<boolean>; + + constructor() { + } + + ngOnInit(): void { + } +} diff --git a/frontend/src/app/result-page/large-facets/large-facets.component.html b/frontend/src/app/facets/large-facets/large-facets.component.html similarity index 58% rename from frontend/src/app/result-page/large-facets/large-facets.component.html rename to frontend/src/app/facets/large-facets/large-facets.component.html index 1f6ed4ee..62d0207a 100644 --- a/frontend/src/app/result-page/large-facets/large-facets.component.html +++ b/frontend/src/app/facets/large-facets/large-facets.component.html @@ -1,29 +1,36 @@ <ng-template #resultTemplate let-facet="result" let-t="term"> <ng-container *ngIf="facet !== 'REFINE'; else refine"> - <ngb-highlight [result]="displayableKey(facet.label)" [term]="t"></ngb-highlight> + <ngb-highlight [result]="displayableKey(facet.label)" + [term]="t"></ngb-highlight> <small class="ml-1 text-muted">({{ facet.count | number }})</small> </ng-container> <ng-template #refine> - <div class="text-muted small">Other results are available.<br/>Refine your search.</div> + <div class="text-muted small">Other results are available.<br/>Refine your + search. + </div> </ng-template> </ng-template> - -<div class="mb-2"> - <span class="badge badge-pill badge-secondary mr-1" *ngFor="let term of selectedTerms[facet.field]" tabindex="0" +<ng-container *ngIf="facet.terms.length && facet.terms.length > 8"> + <div class="mb-2"> + <span class="badge badge-pill badge-secondary mr-1" + *ngFor="let term of selectedTerms[facet.field]" tabindex="0" (keydown.delete)="removeKey(term)" (keydown.backspace)="removeKey(term)"> {{ displaySourceName(term) }} - <button tabindex="-1" type="button" class="btn btn-link" (click)="removeKey(term)">×</button> + <button tabindex="-1" type="button" class="btn btn-link" + (click)="removeKey(term)">×</button> </span> -</div> -<div class="card mb-1" *ngIf="facet.terms.length"> - <div class="card-body"> - <h3 class="card-title">{{ facet.field | titlecase }}</h3> + </div> + <div class="card mb-1" *ngIf="facet.terms.length"> + <div class="card-body"> + <h3 class="card-title">{{ facet.field | titlecase }}</h3> <input #typeahead class="form-control" [formControl]="criterion" [ngbTypeahead]="search" (selectItem)="selectKey($event)" [resultTemplate]="resultTemplate" placeholder="Filter on {{ facet.field.toLowerCase() }}..." (focus)="focus$.next($event.target.value)"/> + </div> </div> -</div> +</ng-container> + diff --git a/frontend/src/app/result-page/large-facets/large-facets.component.scss b/frontend/src/app/facets/large-facets/large-facets.component.scss similarity index 100% rename from frontend/src/app/result-page/large-facets/large-facets.component.scss rename to frontend/src/app/facets/large-facets/large-facets.component.scss diff --git a/frontend/src/app/facets/large-facets/large-facets.component.spec.ts b/frontend/src/app/facets/large-facets/large-facets.component.spec.ts new file mode 100644 index 00000000..d1032589 --- /dev/null +++ b/frontend/src/app/facets/large-facets/large-facets.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LargeFacetsComponent } from './large-facets.component'; + +describe('LargeFacetsComponent', () => { + let component: LargeFacetsComponent; + let fixture: ComponentFixture<LargeFacetsComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [LargeFacetsComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LargeFacetsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/result-page/large-facets/large-facets.component.ts b/frontend/src/app/facets/large-facets/large-facets.component.ts similarity index 98% rename from frontend/src/app/result-page/large-facets/large-facets.component.ts rename to frontend/src/app/facets/large-facets/large-facets.component.ts index 6f178ff2..196b5a89 100644 --- a/frontend/src/app/result-page/large-facets/large-facets.component.ts +++ b/frontend/src/app/facets/large-facets/large-facets.component.ts @@ -116,10 +116,10 @@ export class LargeFacetsComponent implements OnInit { } } if (this.germplasmSearchCriteria$) { - if (this.germplasmLocalCriteria[this.facet.field]) { - this.germplasmLocalCriteria[this.facet.field].push(event.item.term); - } else { + if (!this.germplasmLocalCriteria[this.facet.field]) { this.germplasmLocalCriteria[this.facet.field] = [event.item.term]; + } else { + this.germplasmLocalCriteria[this.facet.field].push(event.item.term); } } this.emitChanges(); diff --git a/frontend/src/app/result-page/facets/facets.component.html b/frontend/src/app/facets/small-facets/small-facets.component.html similarity index 86% rename from frontend/src/app/result-page/facets/facets.component.html rename to frontend/src/app/facets/small-facets/small-facets.component.html index d6d41abf..fcc3de2f 100644 --- a/frontend/src/app/result-page/facets/facets.component.html +++ b/frontend/src/app/facets/small-facets/small-facets.component.html @@ -1,4 +1,4 @@ -<div class="card mb-1" *ngIf="facet.terms.length"> +<div class="card mb-1" *ngIf="facet.terms.length && facet.terms.length < 8"> <div class="card-body"> <h3 class="card-title">{{ facet.field | titlecase }}</h3> @@ -11,23 +11,21 @@ /> <label class="form-check-label" for="{{ term.term }}"> {{ term.label }} ({{ term.count | number }}) - </label> <div id="germplasmSearch" title="Focus on germplasm details with export data link" *ngIf="term.term == 'Germplasm' && !criteriaIsEmpty"> <label for="selectSwitchButton" style="margin-right: 5px" - id="switchTitle" - > + id="switchTitle"> Focus </label> <label class="switch" id="switchButton"> <input type="checkbox" id="selectSwitchButton" + [checked]="displayGermplasmCurrentState" (change)="switchToGermplasmResult()"> <span class="slider round"></span> </label> - </div> </div> </form> diff --git a/frontend/src/app/result-page/facets/facets.component.scss b/frontend/src/app/facets/small-facets/small-facets.component.scss similarity index 99% rename from frontend/src/app/result-page/facets/facets.component.scss rename to frontend/src/app/facets/small-facets/small-facets.component.scss index 3fdb775c..de4ff0c5 100644 --- a/frontend/src/app/result-page/facets/facets.component.scss +++ b/frontend/src/app/facets/small-facets/small-facets.component.scss @@ -12,8 +12,6 @@ cursor: pointer; } - - /* The switch - the box around the slider */ .switch { @@ -76,3 +74,4 @@ input:checked + .slider:before { .slider.round:before { border-radius: 50%; } + diff --git a/frontend/src/app/result-page/facets/facets.component.spec.ts b/frontend/src/app/facets/small-facets/small-facets.component.spec.ts similarity index 92% rename from frontend/src/app/result-page/facets/facets.component.spec.ts rename to frontend/src/app/facets/small-facets/small-facets.component.spec.ts index 3e4ad674..44f14c37 100644 --- a/frontend/src/app/result-page/facets/facets.component.spec.ts +++ b/frontend/src/app/facets/small-facets/small-facets.component.spec.ts @@ -1,18 +1,21 @@ import { TestBed } from '@angular/core/testing'; -import { FacetsComponent } from './facets.component'; +import { SmallFacetsComponent } from './small-facets.component'; import { ReactiveFormsModule } from '@angular/forms'; import { ComponentTester, speculoosMatchers } from 'ngx-speculoos'; -import { DataDiscoveryCriteria, DataDiscoveryCriteriaUtils, DataDiscoveryFacet } from '../../models/data-discovery.model'; +import { + DataDiscoveryCriteria, + DataDiscoveryCriteriaUtils, + DataDiscoveryFacet +} from '../../models/data-discovery.model'; import { BehaviorSubject } from 'rxjs'; import { take } from 'rxjs/operators'; import { NO_ERRORS_SCHEMA } from '@angular/core'; -describe('FacetsComponent', () => { - - class FacetsComponentTester extends ComponentTester<FacetsComponent> { +describe('SmallFacetsComponent', () => { + class FacetsComponentTester extends ComponentTester<SmallFacetsComponent> { constructor() { - super(FacetsComponent); + super(SmallFacetsComponent); } get title() { @@ -61,7 +64,7 @@ describe('FacetsComponent', () => { beforeEach(() => TestBed.configureTestingModule({ imports: [ReactiveFormsModule], - declarations: [FacetsComponent], + declarations: [SmallFacetsComponent], schemas: [NO_ERRORS_SCHEMA] })); diff --git a/frontend/src/app/result-page/facets/facets.component.ts b/frontend/src/app/facets/small-facets/small-facets.component.ts similarity index 71% rename from frontend/src/app/result-page/facets/facets.component.ts rename to frontend/src/app/facets/small-facets/small-facets.component.ts index 0f1750ff..05de4b62 100644 --- a/frontend/src/app/result-page/facets/facets.component.ts +++ b/frontend/src/app/facets/small-facets/small-facets.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { DataDiscoveryCriteria, DataDiscoveryCriteriaUtils, @@ -11,17 +11,19 @@ import { Params } from '@angular/router'; import { GermplasmSearchCriteria } from '../../models/gnpis.model'; @Component({ - selector: 'faidare-facets', - templateUrl: './facets.component.html', - styleUrls: ['./facets.component.scss'] + selector: 'faidare-small-facets', + templateUrl: './small-facets.component.html', + styleUrls: ['./small-facets.component.scss'] }) -export class FacetsComponent implements OnInit { +export class SmallFacetsComponent implements OnInit { @Input() facet: DataDiscoveryFacet; @Input() criteria$: BehaviorSubject<DataDiscoveryCriteria>; @Input() germplasmSearchCriteria$: BehaviorSubject<GermplasmSearchCriteria>; @Input() displayGermplasmResult$: BehaviorSubject<boolean>; + @Output() changed = new EventEmitter<boolean>(); + localCriteria: DataDiscoveryCriteria; germplasmLocalCriteria: GermplasmSearchCriteria; @@ -29,6 +31,7 @@ export class FacetsComponent implements OnInit { queryParams: Params; checkBoxes: FormGroup = new FormGroup({}); displayAdvanceGermplasmSearchButton: boolean; + displayGermplasmCurrentState = false; constructor() { } @@ -74,28 +77,16 @@ export class FacetsComponent implements OnInit { this.criteria$.next(this.localCriteria); } if (this.germplasmSearchCriteria$) { - const field = this.facet.field; - if (field === 'holding institute') { - this.germplasmLocalCriteria = { - ...this.germplasmLocalCriteria, - holdingInstitute: selectedTerms - }; - } - if (field === 'Biological status / Genetic nature') { - this.germplasmLocalCriteria = { - ...this.germplasmLocalCriteria, - biologicalStatus: selectedTerms, - geneticNature: selectedTerms - }; - } - if (field !== 'Biological status / Genetic nature' && field !== 'holding institute') { - this.germplasmLocalCriteria = { - ...this.germplasmLocalCriteria, - [this.facet.field]: selectedTerms - }; - } - this.germplasmSearchCriteria$.next(this.germplasmLocalCriteria); + this.germplasmLocalCriteria = { + ...this.germplasmLocalCriteria, + [this.facet.field]: selectedTerms + }; } + this.germplasmSearchCriteria$.next(this.germplasmLocalCriteria); + }); + + this.displayGermplasmResult$.subscribe(value => { + this.displayGermplasmCurrentState = value; }); } @@ -119,15 +110,13 @@ export class FacetsComponent implements OnInit { } switchToGermplasmResult() { - let currentState = false; - this.displayGermplasmResult$.subscribe(value => { - currentState = value; - }); - for (const [key, control] of Object.entries(this.checkBoxes.controls)) { + + /*for (const [key, control] of Object.entries(this.checkBoxes.controls)) { if (key === 'selectSwitchButton') { control.setValue(currentState, { emitEvent: false }); } - } - this.displayGermplasmResult$.next(!currentState); + }*/ + this.displayGermplasmResult$.next(!this.displayGermplasmCurrentState); } + } diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html index 2c8a8c7d..4c6b3e2d 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html @@ -27,6 +27,11 @@ height="20px"/> </button> + <!-- Loading spinner--> + <div class="text-center"> + <faidare-loading-spinner [loading]="loading"></faidare-loading-spinner> + </div> + <faidare-card-section class="col-12 col-lg" header="Germplasm data: " diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts index 99092ac4..42fd18e0 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts @@ -33,6 +33,7 @@ export class GermplasmResultPageComponent implements OnInit { headers: string[] = ['germplasmName', 'accessionNumber', 'commonCropName', 'instituteName']; elementPerPage: number[] = [15, 20, 25]; + loading: boolean; fieldSortState: object = { germplasmName: null, accessionNumber: null, @@ -75,7 +76,7 @@ export class GermplasmResultPageComponent implements OnInit { this.service.germplasmSearch(criteria) .subscribe(({ metadata, facets, result }) => { this.germplasm = result.data; - this.germplasmFacets$.next(this.formatFacets(facets)); + this.germplasmFacets$.next(facets); DataDiscoveryCriteriaUtils.updatePagination(this.pagination, metadata.pagination); }); } @@ -109,10 +110,12 @@ export class GermplasmResultPageComponent implements OnInit { } exportPlantMaterial(criteria: GermplasmSearchCriteria) { + this.loading = true; this.service.plantMaterialExport(criteria).subscribe( result => { const blob = new Blob([result], { type: 'text/plain;charset=utf-8' }); saveAs(blob, 'germplasm.gnpis.csv'); + this.loading = false; }, error => { console.log(error); diff --git a/frontend/src/app/result-page/large-facets/large-facets.component.spec.ts b/frontend/src/app/result-page/large-facets/large-facets.component.spec.ts deleted file mode 100644 index 5d800998..00000000 --- a/frontend/src/app/result-page/large-facets/large-facets.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { LargeFacetsComponent } from './large-facets.component'; - -describe('LargeFacetsComponent', () => { - let component: LargeFacetsComponent; - let fixture: ComponentFixture<LargeFacetsComponent>; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ LargeFacetsComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(LargeFacetsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/frontend/src/app/result-page/result-page.component.html b/frontend/src/app/result-page/result-page.component.html index f0bc8585..13f98721 100644 --- a/frontend/src/app/result-page/result-page.component.html +++ b/frontend/src/app/result-page/result-page.component.html @@ -3,38 +3,21 @@ <div class="row justify-content-end"> <!-- Column for facets --> <div class="col-lg-3 order-lg-first"> - <div class="row"> - <faidare-facets - class="col-12 col-lg-12 col-sm-6" - *ngFor="let facet of facets" - [criteria$]="criteria$" - [facet]="facet" - [displayGermplasmResult$]="displayGermplasmResult$"> - </faidare-facets> - </div> - - - <!--<div class="row" *ngIf="germplasmfacets.length && displayGermplasmResult"> - <faidare-facets - class="col-12 col-lg-12 col-sm-6" - *ngFor="let facet of germplasmfacets" - [germplasmSearchCriteria$]="germplasmSearchCriteria$" - [facet]="facet" - [displayGermplasmResult$]="displayGermplasmResult$"> - </faidare-facets> - </div>--> - - <div class="row" *ngIf="germplasmfacets.length && displayGermplasmResult"> - <faidare-large-facets - class="col-12 col-lg-12 col-sm-6" - *ngFor="let facet of germplasmfacets" - [criteria$]="criteria$" - [facet]="facet" - [germplasmSearchCriteria$]="germplasmSearchCriteria$"> - </faidare-large-facets> - </div> + <faidare-facets + [criteria$]="criteria$" + [facets]="facets" + [displayGermplasmResult$]="displayGermplasmResult$" + [germplasmSearchCriteria$]="germplasmSearchCriteria$"> + </faidare-facets> + <faidare-facets + *ngIf="displayGermplasmResult && germplasmfacets.length" + [criteria$]="criteria$" + [facets]="germplasmfacets" + [displayGermplasmResult$]="displayGermplasmResult$" + [germplasmSearchCriteria$]="germplasmSearchCriteria$"> + </faidare-facets> </div> <!-- Column for form and results--> @@ -134,7 +117,8 @@ </ng-container> <!-- Else we display a simple message when no result found --> - <div *ngIf="pagination.totalResult == 0 && !loading && !criteriaIsEmpty" + <div *ngIf="pagination.totalResult == 0 && !loading + && (!criteriaIsEmpty && !displayGermplasmResult)" id="no-results" class="text-center"> <div class="no-result-icon"> <span class="fa fa-meh-o"></span> diff --git a/frontend/src/app/result-page/result-page.component.ts b/frontend/src/app/result-page/result-page.component.ts index 53514f92..bdda07dc 100644 --- a/frontend/src/app/result-page/result-page.component.ts +++ b/frontend/src/app/result-page/result-page.component.ts @@ -113,7 +113,6 @@ export class ResultPageComponent implements OnInit { this.criteria$ .pipe(filter(c => c !== initialCriteria)) .subscribe(newCriteria => { - this.displayGermplasmResult = false; // Reset pagination newCriteria.page = 0; // Fetch documents and facets @@ -131,9 +130,10 @@ export class ResultPageComponent implements OnInit { this.displayGermplasmResult$.next(this.displayGermplasmResult); }); - this.displayGermplasmResult$.subscribe(value => { + /*this.displayGermplasmResult$.subscribe(value => { this.displayGermplasmResult = value; }); + this.displayGermplasmResult$.next(this.displayGermplasmResult);*/ this.germplasmfacets$.subscribe(facets => { this.germplasmfacets = facets; -- GitLab From fe275ed5f28578c4af98fcd359a12a146445b8e3 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Thu, 19 Dec 2019 17:52:12 +0100 Subject: [PATCH 09/35] fix: Fix the fronted test and implement new tests for germplasm-result-page and the facets management. GNP-4309 --- backend/src/main/main.iml | 22 + frontend/package-lock.json | 615 ++++++++++++++---- frontend/src/app/brapi.service.spec.ts | 3 +- .../src/app/facets/facets.component.spec.ts | 23 +- .../large-facets/large-facets.component.html | 2 +- .../large-facets.component.spec.ts | 98 ++- .../small-facets.component.spec.ts | 17 +- .../germplasm-card.component.spec.ts | 8 - .../germplasm-result-page.component.spec.ts | 229 ++++++- .../germplasm-result-page.component.ts | 2 +- frontend/src/app/gnpis.service.spec.ts | 31 +- frontend/src/app/gnpis.service.ts | 4 - 12 files changed, 885 insertions(+), 169 deletions(-) diff --git a/backend/src/main/main.iml b/backend/src/main/main.iml index 908ad4f5..f4e15773 100644 --- a/backend/src/main/main.iml +++ b/backend/src/main/main.iml @@ -7,5 +7,27 @@ </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="library" name="Gradle: io.swagger:swagger-annotations:1.5.21" level="project" /> + <orderEntry type="library" name="Gradle: org.springframework:spring-beans:5.1.4.RELEASE" level="project" /> + <orderEntry type="library" name="Gradle: org.springframework:spring-web:5.1.4.RELEASE" level="project" /> + <orderEntry type="library" name="Gradle: io.springfox:springfox-core:2.9.2" level="project" /> + <orderEntry type="library" name="Gradle: io.springfox:springfox-spring-web:2.9.2" level="project" /> + <orderEntry type="library" name="Gradle: javax.validation:validation-api:2.0.1.Final" level="project" /> + <orderEntry type="library" name="Gradle: com.google.guava:guava:27.0.1-jre" level="project" /> + <orderEntry type="library" name="Gradle: commons-collections:commons-collections:3.2.2" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.httpcomponents:httpcore:4.4.10" level="project" /> + <orderEntry type="library" name="Gradle: org.elasticsearch.client:elasticsearch-rest-client:6.4.3" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.httpcomponents:httpcore-nio:4.4.10" level="project" /> + <orderEntry type="library" name="Gradle: org.springframework.boot:spring-boot-autoconfigure:2.1.2.RELEASE" level="project" /> + <orderEntry type="library" name="Gradle: org.springframework:spring-context:5.1.4.RELEASE" level="project" /> + <orderEntry type="library" name="Gradle: com.fasterxml.jackson.core:jackson-databind:2.9.8" level="project" /> + <orderEntry type="library" name="Gradle: org.springframework.boot:spring-boot:2.1.2.RELEASE" level="project" /> + <orderEntry type="library" name="Gradle: org.springframework.security:spring-security-config:5.1.3.RELEASE" level="project" /> + <orderEntry type="library" name="Gradle: io.springfox:springfox-spi:2.9.2" level="project" /> + <orderEntry type="library" name="Gradle: io.springfox:springfox-swagger2:2.9.2" level="project" /> + <orderEntry type="library" name="Gradle: org.elasticsearch:elasticsearch:6.5.4" level="project" /> + <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> + <orderEntry type="library" name="Gradle: org.elasticsearch.client:elasticsearch-rest-high-level-client:6.5.4" level="project" /> + <orderEntry type="library" name="Gradle: org.elasticsearch:elasticsearch-core:6.5.4" level="project" /> </component> </module> \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index aa2aa4ef..89fdb615 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -78,6 +78,166 @@ "webpack-subresource-integrity": "1.1.0-rc.6" }, "dependencies": { + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "cacache": { + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.3.tgz", + "integrity": "sha512-p8WcneCytvzPxhDvYp31PD039vi77I12W+/KfR9S8AZbaiARFBCpsPJS+9uhWfeBfeAtW7o/4vt3MUqLkbY6nA==", + "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", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "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" + } + } + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "rxjs": { "version": "6.3.3", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", @@ -86,6 +246,43 @@ "requires": { "tslib": "^1.9.0" } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "terser-webpack-plugin": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.2.tgz", + "integrity": "sha512-1DMkTk286BzmfylAvLXwpJrI7dWa5BnFmscV/2dCr8+c56egFcbaeFAl7+sujAjdmpLam21XRdhA4oifLyiWWg==", + "dev": true, + "requires": { + "cacache": "^11.0.2", + "find-cache-dir": "^2.0.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "terser": "^3.16.1", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true } } }, @@ -715,9 +912,9 @@ } }, "@ng-bootstrap/ng-bootstrap": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-4.0.0.tgz", - "integrity": "sha512-jWVHRDUYppOSvzclP9d3lVWGUO+y0X9W6jdfyYehDkJCY0MFgtwefLYMtQ/NJ4niPzm2d/zZn+Bte48iIn0nKw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-4.2.2.tgz", + "integrity": "sha512-v8QmC17bv9he5Ep6zutaI9aQ2w/2NqySP0fejOKe7cacKpGUqsLIakpyd2FD7mfZu7pSCCtHYpRWR+h6yq+Ngg==", "requires": { "tslib": "^1.9.0" } @@ -1273,6 +1470,7 @@ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, + "optional": true, "requires": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" @@ -1802,9 +2000,9 @@ } }, "bootstrap": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.1.3.tgz", - "integrity": "sha512-rDFIzgXcof0jDyjNosjv4Sno77X4KuPeFxG2XZZv1/Kc8DRVGVADdoQyyOVDwPqL36DDmtCQbrpMCqvpPLJQ0w==" + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.3.1.tgz", + "integrity": "sha512-rXqOmH1VilAt2DyPzluTi2blhk17bO7ef+zLLPlWvG494pDxcM234pJ8wTc/6R40UWizAIIMgxjvxZg5kmsbag==" }, "brace-expansion": { "version": "1.1.11", @@ -2484,7 +2682,8 @@ "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 + "dev": true, + "optional": true }, "constants-browserify": { "version": "1.0.0", @@ -2886,7 +3085,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true + "dev": true, + "optional": true }, "depd": { "version": "1.1.2", @@ -3608,6 +3808,11 @@ "schema-utils": "^1.0.0" } }, + "file-saver": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.2.tgz", + "integrity": "sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw==" + }, "fileset": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", @@ -3853,7 +4058,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -3874,12 +4080,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3894,17 +4102,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -4021,7 +4232,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -4033,6 +4245,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4047,6 +4260,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4054,12 +4268,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -4078,6 +4294,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -4158,7 +4375,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -4170,6 +4388,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -4255,7 +4474,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -4291,6 +4511,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -4310,6 +4531,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -4353,20 +4575,23 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, "fstream": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", "dev": true, + "optional": true, "requires": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", @@ -4379,6 +4604,7 @@ "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, + "optional": true, "requires": { "aproba": "^1.0.3", "console-control-strings": "^1.0.0", @@ -4416,7 +4642,8 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true + "dev": true, + "optional": true }, "get-stream": { "version": "3.0.0", @@ -4528,12 +4755,12 @@ "dev": true }, "handlebars": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.0.tgz", - "integrity": "sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", + "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", "dev": true, "requires": { - "async": "^2.5.0", + "neo-async": "^2.6.0", "optimist": "^0.6.1", "source-map": "^0.6.1", "uglify-js": "^3.1.4" @@ -4605,7 +4832,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true + "dev": true, + "optional": true }, "has-value": { "version": "1.0.0", @@ -4786,15 +5014,24 @@ "dev": true }, "https-proxy-agent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", - "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", "dev": true, "requires": { - "agent-base": "^4.1.0", + "agent-base": "^4.3.0", "debug": "^3.1.0" }, "dependencies": { + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", @@ -4805,9 +5042,9 @@ } }, "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==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } @@ -4981,6 +5218,12 @@ "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", @@ -5325,7 +5568,8 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true + "dev": true, + "optional": true }, "is-windows": { "version": "1.0.2", @@ -5604,9 +5848,9 @@ "dev": true }, "jquery": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", - "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==" + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz", + "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==" }, "js-base64": { "version": "2.5.1", @@ -5622,9 +5866,9 @@ "dev": true }, "js-yaml": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz", - "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -5876,6 +6120,21 @@ "source-map-support": "^0.5.5" } }, + "katex": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.11.1.tgz", + "integrity": "sha512-5oANDICCTX0NqYIyAiFCCwjQ7ERu3DQG2JFHLbYOf+fXaMoH8eg/zOq5WSYJsKMi/QebW+Eh3gSM+oss1H/bww==", + "requires": { + "commander": "^2.19.0" + }, + "dependencies": { + "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==" + } + } + }, "killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", @@ -5968,6 +6227,7 @@ "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, + "optional": true, "requires": { "graceful-fs": "^4.1.2", "parse-json": "^2.2.0", @@ -5980,7 +6240,8 @@ "version": "2.3.0", "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "dev": true, + "optional": true } } }, @@ -6012,9 +6273,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true }, "lodash.assign": { @@ -6037,9 +6298,9 @@ "dev": true }, "lodash.mergewith": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", - "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", "dev": true, "optional": true }, @@ -6255,7 +6516,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true + "dev": true, + "optional": true }, "map-visit": { "version": "1.0.0", @@ -6267,9 +6529,9 @@ } }, "marked": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.6.2.tgz", - "integrity": "sha512-LqxwVH3P/rqKX4EKGz7+c2G9r98WeM/SW34ybhgNGhUQNKtf1GmmSkJ6cDGJ/t6tiyae49qRkpyTw2B9HOrgUA==" + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", + "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==" }, "md5.js": { "version": "1.3.5", @@ -6483,9 +6745,9 @@ } }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -6629,12 +6891,13 @@ "integrity": "sha512-Zorpd5I6KmvTtiYwcjymzCaortznMZr5CRB737XaNheITTUb2rVLUoEBk1dwQE3b/Cp5sByuS85fzwJRvjEXKQ==" }, "ngx-markdown": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/ngx-markdown/-/ngx-markdown-8.0.2.tgz", - "integrity": "sha512-dLF205/JrSI7pOIgsrikhf9KYF6yiP797FuHGjVEw51KhHfuhCoR+3pl7Ky2vxuQBJjmkNGbgH75+Qy2SAnFEg==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/ngx-markdown/-/ngx-markdown-8.2.1.tgz", + "integrity": "sha512-59LG8rEoOwDsZyyJckp+QDnW/c5wMaRpNkb6TWktlBVTfQKyAYHr6BuSskVbZ4y8nsj54UQg0CDFLBOfUiqOwA==", "requires": { - "@types/marked": "^0.6.0", - "marked": "^0.6.0", + "@types/marked": "^0.6.5", + "katex": "^0.11.1", + "marked": "^0.7.0", "prismjs": "^1.16.0", "tslib": "^1.9.0" } @@ -6919,6 +7182,7 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, + "optional": true, "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -7579,9 +7843,9 @@ "dev": true }, "prismjs": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.16.0.tgz", - "integrity": "sha512-OA4MKxjFZHSvZcisLGe14THYsug/nF6O1f0pAJc0KN0wTyAcLqmsbE+lTGKSpyh+9pEW57+k6pg2AfYR+coyHA==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.17.1.tgz", + "integrity": "sha512-PrEDJAFdUGbOP6xK/UsfkC5ghJsPJviKgnQOoxaDbBjwc8op68Quupwt1DeAFoG8GImPhiKXAvvsH7wDSLsu1Q==", "requires": { "clipboard": "^2.0.0" } @@ -7933,6 +8197,7 @@ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, + "optional": true, "requires": { "load-json-file": "^1.0.0", "normalize-package-data": "^2.3.2", @@ -7944,6 +8209,7 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, + "optional": true, "requires": { "graceful-fs": "^4.1.2", "pify": "^2.0.0", @@ -7954,7 +8220,8 @@ "version": "2.3.0", "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "dev": true, + "optional": true } } }, @@ -7963,6 +8230,7 @@ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, + "optional": true, "requires": { "find-up": "^1.0.0", "read-pkg": "^1.0.0" @@ -7973,6 +8241,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, + "optional": true, "requires": { "path-exists": "^2.0.0", "pinkie-promise": "^2.0.0" @@ -7983,6 +8252,7 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, + "optional": true, "requires": { "pinkie-promise": "^2.0.0" } @@ -8513,9 +8783,9 @@ "dev": true }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -9263,6 +9533,7 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, + "optional": true, "requires": { "is-utf8": "^0.2.0" } @@ -9365,14 +9636,14 @@ "dev": true }, "tar": { - "version": "2.2.1", - "resolved": "http://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", "dev": true, "optional": true, "requires": { "block-stream": "*", - "fstream": "^1.0.2", + "fstream": "^1.0.12", "inherits": "2" } }, @@ -9396,51 +9667,65 @@ } }, "terser-webpack-plugin": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.2.tgz", - "integrity": "sha512-1DMkTk286BzmfylAvLXwpJrI7dWa5BnFmscV/2dCr8+c56egFcbaeFAl7+sujAjdmpLam21XRdhA4oifLyiWWg==", + "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": "^11.0.2", - "find-cache-dir": "^2.0.0", + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", "schema-utils": "^1.0.0", - "serialize-javascript": "^1.4.0", + "serialize-javascript": "^2.1.2", "source-map": "^0.6.1", - "terser": "^3.16.1", - "webpack-sources": "^1.1.0", - "worker-farm": "^1.5.2" + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" }, "dependencies": { + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, "cacache": { - "version": "11.3.2", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", - "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", + "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", "dev": true, "requires": { - "bluebird": "^3.5.3", + "bluebird": "^3.5.5", "chownr": "^1.1.1", "figgy-pudding": "^3.5.1", - "glob": "^7.1.3", + "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.2", + "rimraf": "^2.6.3", "ssri": "^6.0.1", "unique-filename": "^1.1.1", "y18n": "^4.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 + }, "find-cache-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.0.0.tgz", - "integrity": "sha512-LDUY6V1Xs5eFskUVYtIwatojt6+9xC9Chnlk/jYOOvn3FAFfSaWddxahDGyNHh0b2dMXa6YW2m0tk8TdVaXHlA==", + "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": "^1.0.0", + "make-dir": "^2.0.0", "pkg-dir": "^3.0.0" } }, @@ -9453,6 +9738,20 @@ "locate-path": "^3.0.0" } }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "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" + } + }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -9472,6 +9771,16 @@ "yallist": "^3.0.2" } }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, "mississippi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", @@ -9491,9 +9800,9 @@ } }, "p-limit": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", - "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -9509,9 +9818,15 @@ } }, "p-try": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", - "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true }, "pkg-dir": { @@ -9533,12 +9848,28 @@ "once": "^1.3.1" } }, + "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 + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, + "source-map-support": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "ssri": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", @@ -9548,10 +9879,40 @@ "figgy-pudding": "^3.5.1" } }, + "terser": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.3.tgz", + "integrity": "sha512-0ikKraVtRDKGzHrzkCv5rUNDzqlhmhowOBqC0XqUHFpW+vJ45+20/IFBcebwKfiS2Z9fJin6Eo+F1zLZsxi8RA==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + } + }, + "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" + } + }, + "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" + } + }, "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true } } @@ -9686,6 +10047,13 @@ "requires": { "jquery": "3.3.1", "jstree": "3.3.5" + }, + "dependencies": { + "jquery": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", + "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==" + } } }, "tree-kill": { @@ -9839,16 +10207,23 @@ "dev": true }, "uglify-js": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", - "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.2.tgz", + "integrity": "sha512-uhRwZcANNWVLrxLfNFEdltoPNhECUR3lc+UdJoG9CBpMcSnKyWA94tc3eAujB1GcMY5Uwq8ZMp4qWpxWYDQmaA==", "dev": true, "optional": true, "requires": { - "commander": "~2.17.1", + "commander": "~2.20.3", "source-map": "~0.6.1" }, "dependencies": { + "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, + "optional": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -9865,38 +10240,15 @@ "dev": true }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "unique-filename": { @@ -10625,6 +10977,7 @@ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, + "optional": true, "requires": { "string-width": "^1.0.2 || 2" } diff --git a/frontend/src/app/brapi.service.spec.ts b/frontend/src/app/brapi.service.spec.ts index 5466b472..7f60d4fa 100644 --- a/frontend/src/app/brapi.service.spec.ts +++ b/frontend/src/app/brapi.service.spec.ts @@ -299,7 +299,8 @@ describe('BrapiService', () => { distributors: [origin], panel: [germplasmSet], collection: [germplasmSet], - population: [germplasmSet] + population: [germplasmSet], + 'schema:includedInDataCatalog': null }; let brapiService: BrapiService; diff --git a/frontend/src/app/facets/facets.component.spec.ts b/frontend/src/app/facets/facets.component.spec.ts index 5a1e3ba8..2819afd6 100644 --- a/frontend/src/app/facets/facets.component.spec.ts +++ b/frontend/src/app/facets/facets.component.spec.ts @@ -1,23 +1,36 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { FacetsComponent } from './facets.component'; +import { BehaviorSubject } from 'rxjs'; +import { + DataDiscoveryCriteria, + DataDiscoveryCriteriaUtils +} from '../models/data-discovery.model'; +import { GermplasmSearchCriteria } from '../models/gnpis.model'; +import { SmallFacetsComponent } from './small-facets/small-facets.component'; +import { LargeFacetsComponent } from './large-facets/large-facets.component'; +import { MockComponents } from 'ng-mocks'; -describe('LargeFacetsComponent', () => { +describe('FacetsComponent', () => { let component: FacetsComponent; let fixture: ComponentFixture<FacetsComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [FacetsComponent] + declarations: [FacetsComponent, MockComponents(SmallFacetsComponent), MockComponents(LargeFacetsComponent)] }) .compileComponents(); - })); - beforeEach(() => { + fixture = TestBed.createComponent(FacetsComponent); component = fixture.componentInstance; + + component.criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(DataDiscoveryCriteriaUtils.emptyCriteria()); + component.germplasmSearchCriteria$ = new BehaviorSubject<GermplasmSearchCriteria>(DataDiscoveryCriteriaUtils + .emptyGermplasmSearchCriteria()); + component.displayGermplasmResult$ = new BehaviorSubject<boolean>(false); fixture.detectChanges(); - }); + })); it('should create', () => { expect(component).toBeTruthy(); diff --git a/frontend/src/app/facets/large-facets/large-facets.component.html b/frontend/src/app/facets/large-facets/large-facets.component.html index 62d0207a..c082f409 100644 --- a/frontend/src/app/facets/large-facets/large-facets.component.html +++ b/frontend/src/app/facets/large-facets/large-facets.component.html @@ -11,7 +11,7 @@ </ng-template> </ng-template> -<ng-container *ngIf="facet.terms.length && facet.terms.length > 8"> +<ng-container> <div class="mb-2"> <span class="badge badge-pill badge-secondary mr-1" *ngFor="let term of selectedTerms[facet.field]" tabindex="0" diff --git a/frontend/src/app/facets/large-facets/large-facets.component.spec.ts b/frontend/src/app/facets/large-facets/large-facets.component.spec.ts index d1032589..daee20f7 100644 --- a/frontend/src/app/facets/large-facets/large-facets.component.spec.ts +++ b/frontend/src/app/facets/large-facets/large-facets.component.spec.ts @@ -1,25 +1,115 @@ +import { LargeFacetsComponent } from './large-facets.component'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { + DataDiscoveryCriteria, DataDiscoveryCriteriaUtils, + DataDiscoveryFacet +} from '../../models/data-discovery.model'; +import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap'; +import { ReactiveFormsModule } from '@angular/forms'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { BehaviorSubject } from 'rxjs'; +import { GermplasmSearchCriteria } from '../../models/gnpis.model'; +import { ComponentTester } from 'ngx-speculoos'; -import { LargeFacetsComponent } from './large-facets.component'; describe('LargeFacetsComponent', () => { let component: LargeFacetsComponent; let fixture: ComponentFixture<LargeFacetsComponent>; + class LargeFacetsComponentTester extends ComponentTester<LargeFacetsComponent> { + constructor() { + super(LargeFacetsComponent); + } + + get facetTitle() { + return this.element('h3') + } + + get facetInput() { + return this.element('input') + } + } + + const largeFacet: DataDiscoveryFacet = { + field: 'large Facet', + terms: [ + { + term: 'source 1', + label: 'SOURCE 1', + count: 10 + }, { + term: 'source 2', + label: 'SOURCE 2', + count: 20 + }, { + term: 'Germplasm', + label: 'GERMPLASM', + count: 10 + },{ + term: 'Traditional cultivar/landrace', + label: 'Traditional cultivar/landrace', + count: 74 + }, { + term: 'Wild', + label: 'Wild', + count: 7095 + }, { + term: 'Hybrid', + label: 'Hybrid', + count: 478 + },{ + term: 'INRA', + label: 'INRA', + count: 74 + }, { + term: 'INRA-ONF', + label: 'INRA-ONF', + count: 8282 + }, { + term: 'USDA', + label: 'USDA', + count: 3871 + }, { + term: 'IPK', + label: 'IPK', + count: 2606 + }, { + term: 'INRA, CNRS', + label: 'INRA, CNRS', + count: 2170 + } + ] + }; + beforeEach(async(() => { TestBed.configureTestingModule({ + imports: [NgbTypeaheadModule, ReactiveFormsModule, HttpClientTestingModule ], declarations: [LargeFacetsComponent] }) .compileComponents(); - })); - beforeEach(() => { fixture = TestBed.createComponent(LargeFacetsComponent); component = fixture.componentInstance; + + component.criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(DataDiscoveryCriteriaUtils.emptyCriteria()); + component.germplasmSearchCriteria$ = new BehaviorSubject<GermplasmSearchCriteria>(DataDiscoveryCriteriaUtils + .emptyGermplasmSearchCriteria()); + component.facet = largeFacet; fixture.detectChanges(); - }); + })); it('should create', () => { expect(component).toBeTruthy(); }); + + it ('should display search box', () => { + const tester = new LargeFacetsComponentTester(); + const component = tester.componentInstance; + component.facet = largeFacet; + tester.detectChanges(); + + expect(tester.facetTitle.textContent).toEqual('Large Facet'); + expect(tester.facetInput).toBeTruthy(); + + }); }); diff --git a/frontend/src/app/facets/small-facets/small-facets.component.spec.ts b/frontend/src/app/facets/small-facets/small-facets.component.spec.ts index 44f14c37..1560843a 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.spec.ts +++ b/frontend/src/app/facets/small-facets/small-facets.component.spec.ts @@ -30,8 +30,8 @@ describe('SmallFacetsComponent', () => { return this.elements('input'); } - get advanceSearchButton() { - return this.elements('button'); + get switchButton() { + return this.elements('label#switchTitle'); } } @@ -76,6 +76,7 @@ describe('SmallFacetsComponent', () => { const component = tester.componentInstance; component.facet = exampleFacet1; component.criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(DataDiscoveryCriteriaUtils.emptyCriteria()); + component.displayGermplasmResult$ = new BehaviorSubject<boolean>(false); tester.detectChanges(); expect(tester.title).toContainText('Sources'); @@ -100,6 +101,7 @@ describe('SmallFacetsComponent', () => { sources: ['source 2'] }; component.criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(criteria); + component.displayGermplasmResult$ = new BehaviorSubject<boolean>(false); tester.detectChanges(); @@ -114,6 +116,7 @@ describe('SmallFacetsComponent', () => { const component = tester.componentInstance; component.facet = exampleFacet1; component.criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(DataDiscoveryCriteriaUtils.emptyCriteria()); + component.displayGermplasmResult$ = new BehaviorSubject<boolean>(false); tester.detectChanges(); // No sources selected in criteria @@ -141,11 +144,12 @@ describe('SmallFacetsComponent', () => { types: ['Germplasm'] }; component.criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(criteria); + component.displayGermplasmResult$ = new BehaviorSubject<boolean>(true); component.facet = exampleFacet2; tester.detectChanges(); - expect(tester.advanceSearchButton.length).toEqual(1); - expect(tester.advanceSearchButton[0]).toContainText('Advance search'); + expect(tester.switchButton.length).toEqual(1); + expect(tester.switchButton[0]).toContainText('Focus'); }); @@ -157,11 +161,12 @@ describe('SmallFacetsComponent', () => { ...DataDiscoveryCriteriaUtils.emptyCriteria(), types: ['Germplasm', 'Phenotyping Study'] }; - component.criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(criteria); + component.criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(DataDiscoveryCriteriaUtils.emptyCriteria()); + component.displayGermplasmResult$ = new BehaviorSubject<boolean>(false); component.facet = exampleFacet2; tester.detectChanges(); - expect(tester.advanceSearchButton).toEqual([]); + expect(tester.switchButton).toEqual([]); }); }); diff --git a/frontend/src/app/germplasm-card/germplasm-card.component.spec.ts b/frontend/src/app/germplasm-card/germplasm-card.component.spec.ts index 04735dd5..b0fa68a8 100644 --- a/frontend/src/app/germplasm-card/germplasm-card.component.spec.ts +++ b/frontend/src/app/germplasm-card/germplasm-card.component.spec.ts @@ -40,14 +40,6 @@ describe('GermplasmCardComponent', () => { } } - const gnpisSite: Site = { - latitude: null, - longitude: null, - siteId: null, - siteName: null, - siteType: null - }; - const brapiSibling: BrapiSibling = { germplasmDbId: 'frere1', defaultDisplayName: 'frere1' diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts index dc6b03f9..65de0cce 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts @@ -1,25 +1,246 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { GermplasmResultPageComponent } from './germplasm-result-page.component'; +import { RouterTestingModule } from '@angular/router/testing'; +import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.component'; +import { GnpisService } from '../gnpis.service'; +import { BrapiGermplasm, GermplasmResults } from '../models/brapi.model'; +import { CardSectionComponent } from '../card-section/card-section.component'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { + DataDiscoveryCriteria, + DataDiscoveryCriteriaUtils, + DataDiscoveryFacet, + DEFAULT_PAGE_SIZE, + MAX_RESULTS +} from '../models/data-discovery.model'; +import { GermplasmSearchCriteria } from '../models/gnpis.model'; +import { BehaviorSubject, of } from 'rxjs'; + describe('GermplasmResultPageComponent', () => { let component: GermplasmResultPageComponent; let fixture: ComponentFixture<GermplasmResultPageComponent>; + const pagination = { + startResult: 1, + endResult: DEFAULT_PAGE_SIZE, + totalResult: 2, + currentPage: 0, + pageSize: DEFAULT_PAGE_SIZE, + totalPages: 1, + totalCount: 2, + maxResults: MAX_RESULTS + }; + + const facets: DataDiscoveryFacet[] = [ + { + field: 'holdingInstitute', + terms: [ + { + term: 'INRA', + label: 'INRA', + count: 74 + }, + { + term: 'INRA-ONF', + label: 'INRA-ONF', + count: 8282 + }, + { + term: 'USDA', + label: 'USDA', + count: 3871 + }, + { + term: 'IPK', + label: 'IPK', + count: 2606 + }, + { + term: 'INRA, CNRS', + label: 'INRA, CNRS', + count: 2170 + } + ] + }, + { + field: 'biologicalStatus', + terms: [ + { + term: 'Traditional cultivar/landrace', + label: 'Traditional cultivar/landrace', + count: 74 + }, + { + term: 'Wild', + label: 'Wild', + count: 7095 + }, + { + term: 'Hybrid', + label: 'Hybrid', + count: 478 + } + ] + } + ]; + + const germplasmSearchResult: GermplasmResults<BrapiGermplasm> = { + metadata: { + pagination: pagination + }, + facets: facets, + result: { + data: [{ + germplasmDbId: 'g1', + defaultDisplayName: 'germplam1', + accessionNumber: 'G_10', + germplasmName: 'germplam1', + germplasmPUI: 'urn_g1', + pedigree: null, + seedSource: 'Versaille Institute', + synonyms: null, + commonCropName: 'cheery', + instituteCode: '78', + instituteName: 'Versaille Institute', + biologicalStatusOfAccessionCode: null, + countryOfOriginCode: 'FR', + typeOfGermplasmStorageCode: null, + taxonIds: null, + genus: 'Populus', + species: 'x generosa', + speciesAuthority: 'Pop', + subtaxa: 'subsp', + subtaxaAuthority: '', + donors: null, + acquisitionDate: 'yesterday' + }, { + germplasmDbId: 'g2', + defaultDisplayName: 'germplam2', + accessionNumber: 'G_20', + germplasmName: 'germplam2', + germplasmPUI: 'urn_g2', + pedigree: null, + seedSource: 'Versaille Institute', + synonyms: null, + commonCropName: 'cheery', + instituteCode: '78', + instituteName: 'Versaille Institute', + biologicalStatusOfAccessionCode: null, + countryOfOriginCode: 'FR', + typeOfGermplasmStorageCode: null, + taxonIds: null, + genus: 'Triticum', + species: 'aestivum', + speciesAuthority: 'Trit', + subtaxa: 'subsp', + subtaxaAuthority: '', + donors: null, + acquisitionDate: 'today' + }], + } + }; + + const criteria: GermplasmSearchCriteria = { + accessionNumbers: ['G_20'], + germplasmDbIds: null, + germplasmGenus: null, + germplasmNames: ['germplam1'], + germplasmPUIs: null, + germplasmSpecies: null, + + synonyms: null, + panel: null, + collection: null, + population: null, + commonCropName: null, + species: null, + genusSpecies: null, + subtaxa: null, + genusSpeciesSubtaxa: null, + taxonSynonyms: null, + biologicalStatus: null, + geneticNature: null, + holdingInstitute: null, + sources: null, + + facetFields: null, + sortBy: 'germplasmName', + sortOrder: null, + page: 1, + pageSize: 10, + }; + + const dataDiscoveryCriteria: DataDiscoveryCriteria = { + accessions: ['G_10', 'incisa', 'G_20'], + crops: ['cheery', 'prunus'], + facetFields: null, + germplasmLists: null, + observationVariableIds: null, + sources: ['URGI'], + types: null, + topSelectedTraitOntologyIds: null, + + page: 1, + pageSize: 10 + }; + + + const gnpisService = jasmine.createSpyObj( + 'GnpisService', [ + 'germplasmSearch' + ] + ); + gnpisService.germplasmSearch.and.returnValue(of(germplasmSearchResult)); + + beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [GermplasmResultPageComponent] + imports: [RouterTestingModule], + declarations: [GermplasmResultPageComponent, CardSectionComponent, LoadingSpinnerComponent], + providers: [ + { provide: GnpisService, useValue: gnpisService } + ], + schemas: [NO_ERRORS_SCHEMA], }) .compileComponents(); - })); - beforeEach(() => { fixture = TestBed.createComponent(GermplasmResultPageComponent); component = fixture.componentInstance; + component.criteriaFromForm$ = new BehaviorSubject<DataDiscoveryCriteria>(DataDiscoveryCriteriaUtils + .emptyCriteria()); + component.germplasmSearchCriteria$ = new BehaviorSubject<GermplasmSearchCriteria>(DataDiscoveryCriteriaUtils + .emptyGermplasmSearchCriteria()); + + component.germplasmFacets$ = new BehaviorSubject<DataDiscoveryFacet[]>(facets); fixture.detectChanges(); - }); + })); it('should create', () => { expect(component).toBeTruthy(); }); + + it('should fecth the germplasm', () => { + component.germplasmSearchCriteria$.next(criteria); + expect(component.localCriteria).toEqual(criteria); + expect(gnpisService.germplasmSearch).toHaveBeenCalledWith(criteria); + expect(component.germplasm).toEqual(germplasmSearchResult.result.data); + + }); + + it('should get criteria information from data discovery criteria', () => { + component.criteriaFromForm$.next(dataDiscoveryCriteria); + fixture.detectChanges(); + + expect(component.localCriteria.commonCropName).toContain('cheery'); + expect(component.localCriteria.commonCropName).toContain('prunus'); + expect(component.localCriteria.commonCropName).toContain('cheery'); + expect(component.localCriteria.genusSpecies).toContain('prunus'); + expect(component.localCriteria.genusSpeciesSubtaxa).toContain('prunus'); + + expect(component.localCriteria.germplasmNames).toContain('G_10'); + expect(component.localCriteria.accessionNumbers).toContain('incisa'); + expect(component.localCriteria.synonyms).toContain('G_20'); + }); }); diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts index 42fd18e0..8f21912f 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts @@ -51,7 +51,7 @@ export class GermplasmResultPageComponent implements OnInit { maxResults: MAX_RESULTS }; - constructor(public service: GnpisService, private route: ActivatedRoute, private router: Router) { + constructor(public service: GnpisService, private route: ActivatedRoute) { } ngOnInit() { diff --git a/frontend/src/app/gnpis.service.spec.ts b/frontend/src/app/gnpis.service.spec.ts index 27e81fe0..cf3073d1 100644 --- a/frontend/src/app/gnpis.service.spec.ts +++ b/frontend/src/app/gnpis.service.spec.ts @@ -13,6 +13,7 @@ import { Donor, Germplasm, GermplasmInstitute, + GermplasmSearchCriteria, GermplasmSet, Institute, Site @@ -125,16 +126,38 @@ describe('GnpisService', () => { distributors: [germplasmInstitute], panel: [germplasmSet], collection: [germplasmSet], - population: [germplasmSet] + population: [germplasmSet], + 'schema:includedInDataCatalog': null }; - const germplasmExportCriteria: GermplasmExportCriteria = { + const germplasmExportCriteria: GermplasmSearchCriteria = { accessionNumbers: ['VCR010'], germplasmDbIds: [], germplasmGenus: [], germplasmNames: [], germplasmPUIs: [], - germplasmSpecies: [] + germplasmSpecies: [], + + synonyms: null, + panel: null, + collection: null, + population: null, + commonCropName: null, + species: null, + genusSpecies: null, + subtaxa: null, + genusSpeciesSubtaxa: null, + taxonSynonyms: null, + biologicalStatus: null, + geneticNature: null, + holdingInstitute: null, + sources: null, + + facetFields: null, + sortBy: null, + sortOrder: null, + page: 1, + pageSize: 10 }; const exportFile: string = '"DOI";"AccessionNumber";' + @@ -269,7 +292,7 @@ describe('GnpisService', () => { } ); const req = http.expectOne({ - url: `${BASE_URL}/germplasm/csv`, + url: `${BASE_URL}/germplasm/germplasm-list-csv`, method: 'POST' }); req.flush(exportFile); diff --git a/frontend/src/app/gnpis.service.ts b/frontend/src/app/gnpis.service.ts index 8561ef5c..bcd26c5e 100644 --- a/frontend/src/app/gnpis.service.ts +++ b/frontend/src/app/gnpis.service.ts @@ -111,10 +111,6 @@ export class GnpisService { germplasmSearch(criteria: GermplasmCriteria): Observable<GermplasmResults<BrapiGermplasm>> { - /*return this.http.post<GermplasmResults<Germplasm>>(`${BASE_URL}/germplasm/search`, - criteria, - { headers: { 'Accept': 'application/ld+json,application/json' } });*/ - return zip( // Get source by URI this.sourceByURI$, -- GitLab From 825c7191fb86d94200a9f81a6be792d071eb2b93 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Fri, 20 Dec 2019 10:01:47 +0100 Subject: [PATCH 10/35] fix: Fix the fronted test and the errors and warning of the linter. GNP-4309 --- frontend/src/app/error-interceptor.service.spec.ts | 8 ++++---- frontend/src/app/error-interceptor.service.ts | 10 ++++++++-- .../facets/large-facets/large-facets.component.spec.ts | 10 +++++----- .../facets/small-facets/small-facets.component.spec.ts | 7 +++++++ 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/frontend/src/app/error-interceptor.service.spec.ts b/frontend/src/app/error-interceptor.service.spec.ts index c6ce5cc7..5b321dff 100644 --- a/frontend/src/app/error-interceptor.service.spec.ts +++ b/frontend/src/app/error-interceptor.service.spec.ts @@ -34,7 +34,7 @@ describe('ErrorInterceptorService', () => { error = err; }); - httpClient.get('/test').subscribe(null, noop); + httpClient.get('/test').subscribe({ next: null, error: noop }); http.expectOne('/test').error(new ErrorEvent('unknown', { message: 'not good' })); expect(error.status).toBeNull(); @@ -47,7 +47,7 @@ describe('ErrorInterceptorService', () => { error = err; }); - httpClient.get('/test').subscribe(null, noop); + httpClient.get('/test').subscribe({ next: null, error: noop }); http.expectOne('/test').flush(null, { status: 500, statusText: 'Server Error' }); expect(error.status).toBe(500); @@ -68,7 +68,7 @@ describe('ErrorInterceptorService', () => { } }; - httpClient.get('/test').subscribe(null, noop); + httpClient.get('/test').subscribe({ next: null, error: noop }); http.expectOne('/test').flush(result, { status: 500, statusText: 'Server Error' }); expect(error.status).toBe(500); @@ -91,7 +91,7 @@ describe('ErrorInterceptorService', () => { } }; - httpClient.get('/test').subscribe(null, noop); + httpClient.get('/test').subscribe({ next: null, error: noop }); http.expectOne('/test').flush(result, { status: 500, statusText: 'Server Error' }); expect(error.status).toBe(500); diff --git a/frontend/src/app/error-interceptor.service.ts b/frontend/src/app/error-interceptor.service.ts index 87fa7ff6..f969531c 100644 --- a/frontend/src/app/error-interceptor.service.ts +++ b/frontend/src/app/error-interceptor.service.ts @@ -1,5 +1,11 @@ import { Injectable } from '@angular/core'; -import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; +import { + HttpErrorResponse, + HttpEvent, + HttpHandler, + HttpInterceptor, + HttpRequest +} from '@angular/common/http'; import { Observable, Subject } from 'rxjs'; import { tap } from 'rxjs/operators'; @@ -17,7 +23,7 @@ export class ErrorInterceptorService implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { return next.handle(req).pipe( - tap(null, err => this.errorSubject.next(this.toError(err))) + tap(() => null, err => this.errorSubject.next(this.toError(err))) ); } diff --git a/frontend/src/app/facets/large-facets/large-facets.component.spec.ts b/frontend/src/app/facets/large-facets/large-facets.component.spec.ts index daee20f7..f52b6855 100644 --- a/frontend/src/app/facets/large-facets/large-facets.component.spec.ts +++ b/frontend/src/app/facets/large-facets/large-facets.component.spec.ts @@ -22,11 +22,11 @@ describe('LargeFacetsComponent', () => { } get facetTitle() { - return this.element('h3') + return this.element('h3'); } get facetInput() { - return this.element('input') + return this.element('input'); } } @@ -45,7 +45,7 @@ describe('LargeFacetsComponent', () => { term: 'Germplasm', label: 'GERMPLASM', count: 10 - },{ + }, { term: 'Traditional cultivar/landrace', label: 'Traditional cultivar/landrace', count: 74 @@ -57,7 +57,7 @@ describe('LargeFacetsComponent', () => { term: 'Hybrid', label: 'Hybrid', count: 478 - },{ + }, { term: 'INRA', label: 'INRA', count: 74 @@ -104,7 +104,7 @@ describe('LargeFacetsComponent', () => { it ('should display search box', () => { const tester = new LargeFacetsComponentTester(); - const component = tester.componentInstance; + component = tester.componentInstance; component.facet = largeFacet; tester.detectChanges(); diff --git a/frontend/src/app/facets/small-facets/small-facets.component.spec.ts b/frontend/src/app/facets/small-facets/small-facets.component.spec.ts index 1560843a..06b7a72b 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.spec.ts +++ b/frontend/src/app/facets/small-facets/small-facets.component.spec.ts @@ -11,6 +11,7 @@ import { import { BehaviorSubject } from 'rxjs'; import { take } from 'rxjs/operators'; import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { GermplasmSearchCriteria } from '../../models/gnpis.model'; describe('SmallFacetsComponent', () => { class FacetsComponentTester extends ComponentTester<SmallFacetsComponent> { @@ -76,6 +77,8 @@ describe('SmallFacetsComponent', () => { const component = tester.componentInstance; component.facet = exampleFacet1; component.criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(DataDiscoveryCriteriaUtils.emptyCriteria()); + component.germplasmSearchCriteria$ = new BehaviorSubject<GermplasmSearchCriteria>(DataDiscoveryCriteriaUtils + .emptyGermplasmSearchCriteria()); component.displayGermplasmResult$ = new BehaviorSubject<boolean>(false); tester.detectChanges(); @@ -116,6 +119,8 @@ describe('SmallFacetsComponent', () => { const component = tester.componentInstance; component.facet = exampleFacet1; component.criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(DataDiscoveryCriteriaUtils.emptyCriteria()); + component.germplasmSearchCriteria$ = new BehaviorSubject<GermplasmSearchCriteria>(DataDiscoveryCriteriaUtils + .emptyGermplasmSearchCriteria()); component.displayGermplasmResult$ = new BehaviorSubject<boolean>(false); tester.detectChanges(); @@ -162,6 +167,8 @@ describe('SmallFacetsComponent', () => { types: ['Germplasm', 'Phenotyping Study'] }; component.criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(DataDiscoveryCriteriaUtils.emptyCriteria()); + component.germplasmSearchCriteria$ = new BehaviorSubject<GermplasmSearchCriteria>(DataDiscoveryCriteriaUtils + .emptyGermplasmSearchCriteria()); component.displayGermplasmResult$ = new BehaviorSubject<boolean>(false); component.facet = exampleFacet2; tester.detectChanges(); -- GitLab From e6b719e53e046d1f5d503699147a59bfb5a42ea4 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Wed, 8 Jan 2020 18:30:49 +0100 Subject: [PATCH 11/35] fix: Fix the display of large facets. GNP-4309 --- .../src/app/facets/large-facets/large-facets.component.html | 4 ++-- .../src/app/facets/small-facets/small-facets.component.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/facets/large-facets/large-facets.component.html b/frontend/src/app/facets/large-facets/large-facets.component.html index c082f409..29cfcd8d 100644 --- a/frontend/src/app/facets/large-facets/large-facets.component.html +++ b/frontend/src/app/facets/large-facets/large-facets.component.html @@ -11,7 +11,7 @@ </ng-template> </ng-template> -<ng-container> +<ng-container *ngIf="facet.terms.length && facet.terms.length > 8"> <div class="mb-2"> <span class="badge badge-pill badge-secondary mr-1" *ngFor="let term of selectedTerms[facet.field]" tabindex="0" @@ -21,7 +21,7 @@ (click)="removeKey(term)">×</button> </span> </div> - <div class="card mb-1" *ngIf="facet.terms.length"> + <div class="card mb-1"> <div class="card-body"> <h3 class="card-title">{{ facet.field | titlecase }}</h3> <input #typeahead class="form-control" [formControl]="criterion" diff --git a/frontend/src/app/facets/small-facets/small-facets.component.html b/frontend/src/app/facets/small-facets/small-facets.component.html index fcc3de2f..bac6c152 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.html +++ b/frontend/src/app/facets/small-facets/small-facets.component.html @@ -1,4 +1,4 @@ -<div class="card mb-1" *ngIf="facet.terms.length && facet.terms.length < 8"> +<div class="card mb-1" *ngIf="facet.terms.length && facet.terms.length <= 8"> <div class="card-body"> <h3 class="card-title">{{ facet.field | titlecase }}</h3> -- GitLab From aa2102a9244e6985d7583f9c2c884e51b89a01f5 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Tue, 28 Jan 2020 16:40:33 +0100 Subject: [PATCH 12/35] fix: Fix the backend test. GNP-4309 --- .../faidare/v1/GnpISGermplasmController.java | 7 +- .../repository/es/GermplasmRepository.java | 6 + .../es/GermplasmRepositoryImpl.java | 134 +++++++++++++- .../faidare/service/es/GermplasmService.java | 5 +- .../service/es/GermplasmServiceImpl.java | 167 +----------------- 5 files changed, 144 insertions(+), 175 deletions(-) diff --git a/backend/src/main/java/fr/inra/urgi/faidare/api/faidare/v1/GnpISGermplasmController.java b/backend/src/main/java/fr/inra/urgi/faidare/api/faidare/v1/GnpISGermplasmController.java index 5658be57..26d8f4e0 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/api/faidare/v1/GnpISGermplasmController.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/api/faidare/v1/GnpISGermplasmController.java @@ -10,7 +10,6 @@ import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO; import fr.inra.urgi.faidare.domain.datadiscovery.response.GermplasmSearchResponse; import fr.inra.urgi.faidare.domain.response.PaginatedList; import fr.inra.urgi.faidare.service.es.GermplasmService; -import fr.inra.urgi.faidare.utils.StringFunctions; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; @@ -20,9 +19,7 @@ import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; import java.io.File; -import java.io.UnsupportedEncodingException; import java.util.Collections; -import java.util.LinkedHashSet; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @@ -129,7 +126,7 @@ public class GnpISGermplasmController { } } - @ApiOperation("Suggest germplasm document field values") + /*@ApiOperation("Suggest germplasm document field values") @PostMapping(value = "/suggest", consumes = APPLICATION_JSON_VALUE) public LinkedHashSet<String> germplasmSuggest( @RequestParam String field, @@ -142,5 +139,5 @@ public class GnpISGermplasmController { } return germplasmService.suggest(field, StringFunctions.asUTF8(text), fetchSize, criteria); - } + }*/ } diff --git a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepository.java b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepository.java index db79c910..f52e18fa 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepository.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepository.java @@ -5,6 +5,7 @@ import fr.inra.urgi.faidare.domain.criteria.GermplasmSearchCriteria; import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO; import fr.inra.urgi.faidare.domain.data.germplasm.PedigreeVO; import fr.inra.urgi.faidare.domain.data.germplasm.ProgenyVO; +import fr.inra.urgi.faidare.domain.datadiscovery.response.GermplasmSearchResponse; import fr.inra.urgi.faidare.domain.response.PaginatedList; import fr.inra.urgi.faidare.elasticsearch.repository.ESFindRepository; @@ -19,6 +20,11 @@ public interface GermplasmRepository @Override PaginatedList<GermplasmVO> find(GermplasmSearchCriteria criteria); + /** + * Find germplasm by criteria with pagination with a should query. + */ + GermplasmSearchResponse esShouldFind(FaidareGermplasmPOSTShearchCriteria germSearCrit); + /** * Get germplasm by id. */ diff --git a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java index 53c1d18a..aa77ef59 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java @@ -6,10 +6,20 @@ import fr.inra.urgi.faidare.domain.criteria.GermplasmSearchCriteria; import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO; import fr.inra.urgi.faidare.domain.data.germplasm.PedigreeVO; import fr.inra.urgi.faidare.domain.data.germplasm.ProgenyVO; +import fr.inra.urgi.faidare.domain.datadiscovery.data.FacetImpl; +import fr.inra.urgi.faidare.domain.datadiscovery.data.FacetTermImpl; +import fr.inra.urgi.faidare.domain.datadiscovery.response.GermplasmSearchResponse; +import fr.inra.urgi.faidare.domain.response.ApiResponseFactory; import fr.inra.urgi.faidare.domain.response.PaginatedList; +import fr.inra.urgi.faidare.domain.response.Pagination; +import fr.inra.urgi.faidare.domain.response.PaginationImpl; import fr.inra.urgi.faidare.elasticsearch.ESRequestFactory; import fr.inra.urgi.faidare.elasticsearch.ESResponseParser; import fr.inra.urgi.faidare.elasticsearch.ESScrollIterator; +import fr.inra.urgi.faidare.elasticsearch.criteria.AnnotatedCriteriaMapper; +import fr.inra.urgi.faidare.elasticsearch.criteria.mapping.CriteriaMapping; +import fr.inra.urgi.faidare.elasticsearch.document.DocumentAnnotationUtil; +import fr.inra.urgi.faidare.elasticsearch.document.DocumentMetadata; import fr.inra.urgi.faidare.elasticsearch.query.impl.ESGenericQueryFactory; import fr.inra.urgi.faidare.elasticsearch.repository.ESFindRepository; import fr.inra.urgi.faidare.elasticsearch.repository.ESGetByIdRepository; @@ -23,19 +33,28 @@ import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; +import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import java.io.IOException; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; + +import static org.elasticsearch.search.aggregations.AggregationBuilders.filter; +import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; @Repository public class GermplasmRepositoryImpl implements GermplasmRepository { private static final Logger LOGGER = LoggerFactory.getLogger(GermplasmRepositoryImpl.class); + private final DocumentMetadata<GermplasmVO> documentMetadata; + private final CriteriaMapping criteriaMapping; + private final RestHighLevelClient client; private final ObjectMapper mapper; private final ESRequestFactory requestFactory; @@ -60,6 +79,10 @@ public class GermplasmRepositoryImpl implements GermplasmRepository { this.queryFactory = new ESGenericQueryFactory<>(); this.findRepository = new ESGenericFindRepository<>(client, requestFactory, voClass, this.parser); this.getByIdRepository = new ESGenericGetByIdRepository<>(client, requestFactory, voClass, this.parser); + Class<GermplasmVO> documentClass = GermplasmVO.class; + Class<FaidareGermplasmPOSTShearchCriteria> criteriaClass = FaidareGermplasmPOSTShearchCriteria.class; + this.documentMetadata = DocumentAnnotationUtil.getDocumentObjectMetadata(documentClass); + this.criteriaMapping = AnnotatedCriteriaMapper.getMapping(criteriaClass); } @Override @@ -86,11 +109,31 @@ public class GermplasmRepositoryImpl implements GermplasmRepository { return findRepository.find(criteria); } + @Override + public GermplasmSearchResponse esShouldFind(FaidareGermplasmPOSTShearchCriteria germplasmSearchCriteria) { + try { + // QueryBuilder query = queryFactory.createOrQuery(germplasmSearchCriteria); + + // Prepare search request + SearchRequest request = prepareSearchRequest(germplasmSearchCriteria); + + // Execute request + LOGGER.debug(request.toString()); + SearchResponse response = client.search(request, RequestOptions.DEFAULT); + + // Return paginated list + return parseResponse (germplasmSearchCriteria, response); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + @Override public PedigreeVO findPedigree(String germplasmDbId) { QueryBuilder termQuery = QueryBuilders.termQuery("germplasmDbId", germplasmDbId); SearchRequest request = requestFactory.prepareSearch("germplasmPedigree", termQuery); - SearchResponse response = null; + SearchResponse response; try { response = client.search(request, RequestOptions.DEFAULT); } catch (IOException e) { @@ -120,7 +163,7 @@ public class GermplasmRepositoryImpl implements GermplasmRepository { public ProgenyVO findProgeny(String germplasmDbId) { QueryBuilder termQuery = QueryBuilders.termQuery("germplasmDbId", germplasmDbId); SearchRequest request = requestFactory.prepareSearch("germplasmProgeny", termQuery); - SearchResponse response = null; + SearchResponse response; try { response = client.search(request, RequestOptions.DEFAULT); } catch (IOException e) { @@ -143,4 +186,91 @@ public class GermplasmRepositoryImpl implements GermplasmRepository { } return progenyVO; } + + + + + + private SearchRequest prepareSearchRequest(FaidareGermplasmPOSTShearchCriteria criteria) { + List<String> facetFields = criteria.getFacetFields(); + String[] documentFieldsInFacets = criteriaFieldsToDocumentFields(facetFields); + + // Build search query (excluding document fields used in facets) + QueryBuilder query = queryFactory.createEsShouldQueryExcludingFields(criteria, documentFieldsInFacets); + + // Prepare search request with query + SearchRequest request = ESGenericFindRepository.prepareSearchRequest( + query, criteria, documentMetadata, requestFactory); + + // Build facet aggregations + if (facetFields != null) { + for (String facetField : facetFields) { + String documentPath = criteriaMapping.getDocumentPath(facetField, true); + + String filterAggName = facetField + "Filter"; + + // Create facet term agg + TermsAggregationBuilder termAgg = terms(facetField) + .field(documentPath) + .size(ESRequestFactory.MAX_TERM_AGG_SIZE); + + // Create filter agg for this facet excluding it self + QueryBuilder facetFilter = queryFactory.createEsShouldQueryExcludingFields(criteria, documentPath); + if (facetFilter == null) { + facetFilter = QueryBuilders.matchAllQuery(); + } + + request.source().aggregation( + filter(filterAggName, facetFilter).subAggregation(termAgg) + ); + } + } + + // Build post filter (including document fields used in facets) + QueryBuilder postFilter = queryFactory.createQueryIncludingFields(criteria, documentFieldsInFacets); + request.source().postFilter(postFilter); + + return request; + } + + private String[] criteriaFieldsToDocumentFields(List<String> criteriaFields) { + List<String> fields = new ArrayList<>(); + if (criteriaFields != null) { + for (String facetField : criteriaFields) { + fields.add(criteriaMapping.getDocumentPath(facetField, true)); + } + } + return fields.toArray(new String[]{}); + } + + /** + * Parse Elasticsearch search response into data discovery response + */ + private GermplasmSearchResponse parseResponse( + FaidareGermplasmPOSTShearchCriteria criteria, SearchResponse response + ) throws IOException, ReflectiveOperationException { + // Parse pagination + Pagination pagination = PaginationImpl.create(criteria, parser.parseTotalHits(response)); + + // Parse result list + List<GermplasmVO> resultList = parser.parseHits(response, + documentMetadata.getDocumentClass()); + + // Parse facet terms + List<String> facetFields = criteria.getFacetFields(); + List<FacetImpl> facets = null; + if (facetFields != null) { + facets = new ArrayList<>(); + for (String facetField : facetFields) { + String filterAggName = facetField + "Filter"; + List<FacetTermImpl> terms = parser.parseFacetTerms( + response, filterAggName, facetField + ); + facets.add(new FacetImpl(facetField, terms)); + } + } + + // Return paginated list + return ApiResponseFactory.createGermplasmListResponseWithFacets(pagination, resultList, facets); + } } diff --git a/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmService.java b/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmService.java index db3d920f..f181e528 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmService.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmService.java @@ -9,7 +9,6 @@ import fr.inra.urgi.faidare.domain.datadiscovery.response.GermplasmSearchRespons import fr.inra.urgi.faidare.domain.response.PaginatedList; import java.io.File; -import java.util.LinkedHashSet; public interface GermplasmService { @@ -20,9 +19,9 @@ public interface GermplasmService { GermplasmSearchResponse esShouldFind(FaidareGermplasmPOSTShearchCriteria criteria); - LinkedHashSet<String> suggest( + /*LinkedHashSet<String> suggest( String criteriaField, String searchText, Integer fetchSize, FaidareGermplasmPOSTShearchCriteria criteria - ); + );*/ File exportCSV(GermplasmSearchCriteria criteria); diff --git a/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java b/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java index 3f8fba0d..3c32ac33 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java @@ -7,31 +7,9 @@ import fr.inra.urgi.faidare.domain.criteria.GermplasmSearchCriteria; import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO; import fr.inra.urgi.faidare.domain.data.germplasm.PedigreeVO; import fr.inra.urgi.faidare.domain.data.germplasm.ProgenyVO; -import fr.inra.urgi.faidare.domain.datadiscovery.data.FacetImpl; -import fr.inra.urgi.faidare.domain.datadiscovery.data.FacetTermImpl; import fr.inra.urgi.faidare.domain.datadiscovery.response.GermplasmSearchResponse; -import fr.inra.urgi.faidare.domain.response.ApiResponseFactory; import fr.inra.urgi.faidare.domain.response.PaginatedList; -import fr.inra.urgi.faidare.domain.response.Pagination; -import fr.inra.urgi.faidare.domain.response.PaginationImpl; -import fr.inra.urgi.faidare.elasticsearch.ESRequestFactory; -import fr.inra.urgi.faidare.elasticsearch.ESResponseParser; -import fr.inra.urgi.faidare.elasticsearch.criteria.AnnotatedCriteriaMapper; -import fr.inra.urgi.faidare.elasticsearch.criteria.mapping.CriteriaMapping; -import fr.inra.urgi.faidare.elasticsearch.document.DocumentAnnotationUtil; -import fr.inra.urgi.faidare.elasticsearch.document.DocumentMetadata; -import fr.inra.urgi.faidare.elasticsearch.query.impl.ESGenericQueryFactory; -import fr.inra.urgi.faidare.elasticsearch.repository.ESSuggestRepository; -import fr.inra.urgi.faidare.elasticsearch.repository.impl.ESGenericFindRepository; -import fr.inra.urgi.faidare.elasticsearch.repository.impl.ESGenericSuggestRepository; import fr.inra.urgi.faidare.repository.es.GermplasmRepository; -import org.elasticsearch.action.search.SearchRequest; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.client.RequestOptions; -import org.elasticsearch.client.RestHighLevelClient; -import org.elasticsearch.index.query.QueryBuilder; -import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @@ -41,13 +19,7 @@ import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; - -import static org.elasticsearch.search.aggregations.AggregationBuilders.filter; -import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; /** * @author cpommier, gcornut @@ -57,34 +29,6 @@ public class GermplasmServiceImpl implements GermplasmService { private final static Logger LOGGER = LoggerFactory.getLogger(GnpISGermplasmController.class); - private final DocumentMetadata<GermplasmVO> documentMetadata; - private final CriteriaMapping criteriaMapping; - - private final RestHighLevelClient client; - private final ESRequestFactory requestFactory; - private final ESSuggestRepository<FaidareGermplasmPOSTShearchCriteria> suggestRepository; - private final ESGenericQueryFactory<FaidareGermplasmPOSTShearchCriteria> queryFactory; - private final ESResponseParser parser; - - public GermplasmServiceImpl ( - RestHighLevelClient client, - ESRequestFactory requestFactory, - ESResponseParser parser - ){ - this.client = client; - this.parser = parser; - - Class<GermplasmVO> documentClass = GermplasmVO.class; - Class<FaidareGermplasmPOSTShearchCriteria> criteriaClass = FaidareGermplasmPOSTShearchCriteria.class; - - this.requestFactory = requestFactory; - this.documentMetadata = DocumentAnnotationUtil.getDocumentObjectMetadata(documentClass); - - this.criteriaMapping = AnnotatedCriteriaMapper.getMapping(criteriaClass); - this.queryFactory = new ESGenericQueryFactory<>(); - this.suggestRepository = new ESGenericSuggestRepository<>(client, requestFactory, documentClass, queryFactory, parser); - } - @Resource GermplasmRepository germplasmRepository; @@ -146,31 +90,8 @@ public class GermplasmServiceImpl implements GermplasmService { } @Override - public GermplasmSearchResponse esShouldFind(FaidareGermplasmPOSTShearchCriteria germSearCrit) { - try { - // QueryBuilder query = queryFactory.createOrQuery(germSearCrit); - - // Prepare search request - SearchRequest request = prepareSearchRequest(germSearCrit); - - // Execute request - LOGGER.debug(request.toString()); - SearchResponse response = client.search(request, RequestOptions.DEFAULT); - - // Return paginated list - return parseResponse (germSearCrit, response); - - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public LinkedHashSet<String> suggest( - String criteriaField, String searchText, Integer fetchSize, FaidareGermplasmPOSTShearchCriteria criteria - ) { - String documentFieldPath = criteriaMapping.getDocumentPath(criteriaField, true); - return suggestRepository.suggest(documentFieldPath, searchText, fetchSize, criteria); + public GermplasmSearchResponse esShouldFind(FaidareGermplasmPOSTShearchCriteria germplasmSearchCriteria) { + return germplasmRepository.esShouldFind(germplasmSearchCriteria); } @Override @@ -182,88 +103,4 @@ public class GermplasmServiceImpl implements GermplasmService { public ProgenyVO getProgeny(String germplasmDbId) { return germplasmRepository.findProgeny(germplasmDbId); } - - - private SearchRequest prepareSearchRequest(FaidareGermplasmPOSTShearchCriteria criteria) { - List<String> facetFields = criteria.getFacetFields(); - String[] documentFieldsInFacets = criteriaFieldsToDocumentFields(facetFields); - - // Build search query (excluding document fields used in facets) - QueryBuilder query = queryFactory.createEsShouldQueryExcludingFields(criteria, documentFieldsInFacets); - - // Prepare search request with query - SearchRequest request = ESGenericFindRepository.prepareSearchRequest( - query, criteria, documentMetadata, requestFactory); - - // Build facet aggregations - if (facetFields != null) { - for (String facetField : facetFields) { - String documentPath = criteriaMapping.getDocumentPath(facetField, true); - - String filterAggName = facetField + "Filter"; - - // Create facet term agg - TermsAggregationBuilder termAgg = terms(facetField) - .field(documentPath) - .size(ESRequestFactory.MAX_TERM_AGG_SIZE); - - // Create filter agg for this facet excluding it self - QueryBuilder facetFilter = queryFactory.createEsShouldQueryExcludingFields(criteria, documentPath); - if (facetFilter == null) { - facetFilter = QueryBuilders.matchAllQuery(); - } - - request.source().aggregation( - filter(filterAggName, facetFilter).subAggregation(termAgg) - ); - } - } - - // Build post filter (including document fields used in facets) - QueryBuilder postFilter = queryFactory.createQueryIncludingFields(criteria, documentFieldsInFacets); - request.source().postFilter(postFilter); - - return request; - } - - private String[] criteriaFieldsToDocumentFields(List<String> criteriaFields) { - List<String> fields = new ArrayList<>(); - if (criteriaFields != null) { - for (String facetField : criteriaFields) { - fields.add(criteriaMapping.getDocumentPath(facetField, true)); - } - } - return fields.toArray(new String[]{}); - } - - /** - * Parse Elasticsearch search response into data discovery response - */ - private GermplasmSearchResponse parseResponse( - FaidareGermplasmPOSTShearchCriteria criteria, SearchResponse response - ) throws IOException, ReflectiveOperationException { - // Parse pagination - Pagination pagination = PaginationImpl.create(criteria, parser.parseTotalHits(response)); - - // Parse result list - List<GermplasmVO> resultList = parser.parseHits(response, - documentMetadata.getDocumentClass()); - - // Parse facet terms - List<String> facetFields = criteria.getFacetFields(); - List<FacetImpl> facets = null; - if (facetFields != null) { - facets = new ArrayList<>(); - for (String facetField : facetFields) { - String filterAggName = facetField + "Filter"; - List<FacetTermImpl> terms = parser.parseFacetTerms( - response, filterAggName, facetField - ); - facets.add(new FacetImpl(facetField, terms)); - } - } - - // Return paginated list - return ApiResponseFactory.createGermplasmListResponseWithFacets(pagination, resultList, facets); - } } -- GitLab From c78a0b804b3eb0a2e318bc57f8700175b668cd31 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Wed, 29 Jan 2020 18:18:32 +0100 Subject: [PATCH 13/35] fix: Change the column that are display in the germplasm result table. Change the margin of the border to have better display, and fix the tests link to the modifications GNP-4309 --- frontend/package-lock.json | 77 +++++-------------- frontend/src/app/app.component.html | 2 +- .../large-facets/large-facets.component.html | 2 +- .../large-facets.component.spec.ts | 5 +- .../small-facets/small-facets.component.html | 2 +- .../small-facets.component.spec.ts | 2 +- .../germplasm-result-page.component.html | 3 +- .../germplasm-result-page.component.spec.ts | 65 ++++++++++++++-- .../germplasm-result-page.component.ts | 22 +++--- frontend/src/app/gnpis.service.spec.ts | 1 + frontend/src/app/gnpis.service.ts | 3 +- .../src/app/models/data-discovery.model.ts | 1 + frontend/src/app/models/gnpis.model.ts | 1 + 13 files changed, 104 insertions(+), 82 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 89fdb615..23ee3c4c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1470,7 +1470,6 @@ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, - "optional": true, "requires": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" @@ -2682,8 +2681,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true + "dev": true }, "constants-browserify": { "version": "1.0.0", @@ -3085,8 +3083,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true, - "optional": true + "dev": true }, "depd": { "version": "1.1.2", @@ -4058,8 +4055,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -4080,14 +4076,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4102,20 +4096,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -4232,8 +4223,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -4245,7 +4235,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4260,7 +4249,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4268,14 +4256,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -4294,7 +4280,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -4375,8 +4360,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -4388,7 +4372,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -4474,8 +4457,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -4511,7 +4493,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -4531,7 +4512,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -4575,14 +4555,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -4591,7 +4569,6 @@ "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", "dev": true, - "optional": true, "requires": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", @@ -4604,7 +4581,6 @@ "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, - "optional": true, "requires": { "aproba": "^1.0.3", "console-control-strings": "^1.0.0", @@ -4642,8 +4618,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true, - "optional": true + "dev": true }, "get-stream": { "version": "3.0.0", @@ -4832,8 +4807,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true, - "optional": true + "dev": true }, "has-value": { "version": "1.0.0", @@ -5568,8 +5542,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true, - "optional": true + "dev": true }, "is-windows": { "version": "1.0.2", @@ -6227,7 +6200,6 @@ "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, - "optional": true, "requires": { "graceful-fs": "^4.1.2", "parse-json": "^2.2.0", @@ -6240,8 +6212,7 @@ "version": "2.3.0", "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "optional": true + "dev": true } } }, @@ -6516,8 +6487,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true, - "optional": true + "dev": true }, "map-visit": { "version": "1.0.0", @@ -7182,7 +7152,6 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, - "optional": true, "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -8197,7 +8166,6 @@ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, - "optional": true, "requires": { "load-json-file": "^1.0.0", "normalize-package-data": "^2.3.2", @@ -8209,7 +8177,6 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, - "optional": true, "requires": { "graceful-fs": "^4.1.2", "pify": "^2.0.0", @@ -8220,8 +8187,7 @@ "version": "2.3.0", "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "optional": true + "dev": true } } }, @@ -8230,7 +8196,6 @@ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, - "optional": true, "requires": { "find-up": "^1.0.0", "read-pkg": "^1.0.0" @@ -8241,7 +8206,6 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, - "optional": true, "requires": { "path-exists": "^2.0.0", "pinkie-promise": "^2.0.0" @@ -8252,7 +8216,6 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, - "optional": true, "requires": { "pinkie-promise": "^2.0.0" } @@ -9533,7 +9496,6 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, - "optional": true, "requires": { "is-utf8": "^0.2.0" } @@ -10977,7 +10939,6 @@ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, - "optional": true, "requires": { "string-width": "^1.0.2 || 2" } diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index 19054abf..e56bebd4 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -1,5 +1,5 @@ <faidare-navbar></faidare-navbar> -<div class="container" style="margin-top: 100px"> +<div style="margin-top: 100px ; margin-right: 100px ; margin-left: 100px"> <faidare-error></faidare-error> <router-outlet></router-outlet> </div> diff --git a/frontend/src/app/facets/large-facets/large-facets.component.html b/frontend/src/app/facets/large-facets/large-facets.component.html index 29cfcd8d..96939295 100644 --- a/frontend/src/app/facets/large-facets/large-facets.component.html +++ b/frontend/src/app/facets/large-facets/large-facets.component.html @@ -23,7 +23,7 @@ </div> <div class="card mb-1"> <div class="card-body"> - <h3 class="card-title">{{ facet.field | titlecase }}</h3> + <h3 class="card-title">{{ facet.field }}</h3> <input #typeahead class="form-control" [formControl]="criterion" [ngbTypeahead]="search" (selectItem)="selectKey($event)" [resultTemplate]="resultTemplate" diff --git a/frontend/src/app/facets/large-facets/large-facets.component.spec.ts b/frontend/src/app/facets/large-facets/large-facets.component.spec.ts index f52b6855..0c65fbef 100644 --- a/frontend/src/app/facets/large-facets/large-facets.component.spec.ts +++ b/frontend/src/app/facets/large-facets/large-facets.component.spec.ts @@ -1,7 +1,8 @@ import { LargeFacetsComponent } from './large-facets.component'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { - DataDiscoveryCriteria, DataDiscoveryCriteriaUtils, + DataDiscoveryCriteria, + DataDiscoveryCriteriaUtils, DataDiscoveryFacet } from '../../models/data-discovery.model'; import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap'; @@ -108,7 +109,7 @@ describe('LargeFacetsComponent', () => { component.facet = largeFacet; tester.detectChanges(); - expect(tester.facetTitle.textContent).toEqual('Large Facet'); + expect(tester.facetTitle.textContent).toEqual('large Facet'); expect(tester.facetInput).toBeTruthy(); }); diff --git a/frontend/src/app/facets/small-facets/small-facets.component.html b/frontend/src/app/facets/small-facets/small-facets.component.html index bac6c152..b9fdb0e5 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.html +++ b/frontend/src/app/facets/small-facets/small-facets.component.html @@ -1,6 +1,6 @@ <div class="card mb-1" *ngIf="facet.terms.length && facet.terms.length <= 8"> <div class="card-body"> - <h3 class="card-title">{{ facet.field | titlecase }}</h3> + <h3 class="card-title">{{ facet.field }}</h3> <form [formGroup]="checkBoxes" class="card-text"> <div class="form-check" *ngFor="let term of facet.terms"> diff --git a/frontend/src/app/facets/small-facets/small-facets.component.spec.ts b/frontend/src/app/facets/small-facets/small-facets.component.spec.ts index 06b7a72b..63c8a854 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.spec.ts +++ b/frontend/src/app/facets/small-facets/small-facets.component.spec.ts @@ -82,7 +82,7 @@ describe('SmallFacetsComponent', () => { component.displayGermplasmResult$ = new BehaviorSubject<boolean>(false); tester.detectChanges(); - expect(tester.title).toContainText('Sources'); + expect(tester.title).toContainText('sources'); expect(tester.terms[0]).toContainText('SOURCE 1 (10)'); expect(tester.terms[0].attr('for')).toBe('source 1'); diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html index 4c6b3e2d..0c624745 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html @@ -61,8 +61,9 @@ <tr> <td>{{ accession.germplasmName }}</td> <td>{{ accession.accessionNumber }}</td> - <td>{{ accession.commonCropName }}</td> + <td>{{ accession.genusSpecies }}</td> <td>{{ accession.instituteName }}</td> + <td>{{ accession.biologicalStatusOfAccessionCode }}</td> </tr> </ng-container> </tbody> diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts index 65de0cce..b394b6ec 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts @@ -4,7 +4,7 @@ import { GermplasmResultPageComponent } from './germplasm-result-page.component' import { RouterTestingModule } from '@angular/router/testing'; import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.component'; import { GnpisService } from '../gnpis.service'; -import { BrapiGermplasm, GermplasmResults } from '../models/brapi.model'; +import { GermplasmResults } from '../models/brapi.model'; import { CardSectionComponent } from '../card-section/card-section.component'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { @@ -14,7 +14,7 @@ import { DEFAULT_PAGE_SIZE, MAX_RESULTS } from '../models/data-discovery.model'; -import { GermplasmSearchCriteria } from '../models/gnpis.model'; +import { Germplasm, GermplasmSearchCriteria } from '../models/gnpis.model'; import { BehaviorSubject, of } from 'rxjs'; @@ -86,7 +86,7 @@ describe('GermplasmResultPageComponent', () => { } ]; - const germplasmSearchResult: GermplasmResults<BrapiGermplasm> = { + const germplasmSearchResult: GermplasmResults<Germplasm> = { metadata: { pagination: pagination }, @@ -114,7 +114,31 @@ describe('GermplasmResultPageComponent', () => { subtaxa: 'subsp', subtaxaAuthority: '', donors: null, - acquisitionDate: 'yesterday' + acquisitionDate: 'yesterday', + + + genusSpecies: null, + genusSpeciesSubtaxa: null, + taxonSynonyms: null, + taxonCommonNames: null, + taxonComment: null, + geneticNature: null, + comment: null, + photo: null, + holdingInstitute: null, + holdingGenbank: null, + presenceStatus: null, + children: null, + originSite: null, + collectingSite: null, + evaluationSites: null, + collector: null, + breeder: null, + distributors: null, + panel: null, + collection: null, + population: null, + 'schema:includedInDataCatalog': null }, { germplasmDbId: 'g2', defaultDisplayName: 'germplam2', @@ -137,11 +161,41 @@ describe('GermplasmResultPageComponent', () => { subtaxa: 'subsp', subtaxaAuthority: '', donors: null, - acquisitionDate: 'today' + acquisitionDate: 'today', + + + genusSpecies: null, + genusSpeciesSubtaxa: null, + taxonSynonyms: null, + taxonCommonNames: null, + taxonComment: null, + geneticNature: null, + comment: null, + photo: null, + holdingInstitute: null, + holdingGenbank: null, + presenceStatus: null, + children: null, + originSite: null, + collectingSite: null, + evaluationSites: null, + collector: null, + breeder: null, + distributors: null, + panel: null, + collection: null, + population: null, + 'schema:includedInDataCatalog': null + }], } }; + + + + + const criteria: GermplasmSearchCriteria = { accessionNumbers: ['G_20'], germplasmDbIds: null, @@ -164,6 +218,7 @@ describe('GermplasmResultPageComponent', () => { geneticNature: null, holdingInstitute: null, sources: null, + types: null, facetFields: null, sortBy: 'germplasmName', diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts index 8f21912f..424fb355 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts @@ -1,11 +1,10 @@ import { Component, Input, OnInit } from '@angular/core'; -import { BrapiGermplasm } from '../models/brapi.model'; import { GnpisService } from '../gnpis.service'; -import { GermplasmSearchCriteria } from '../models/gnpis.model'; +import { Germplasm, GermplasmSearchCriteria } from '../models/gnpis.model'; import { saveAs } from 'file-saver'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; import { DataDiscoveryCriteria, DataDiscoveryCriteriaUtils, @@ -24,21 +23,22 @@ import { BehaviorSubject } from 'rxjs'; export class GermplasmResultPageComponent implements OnInit { - germplasm: BrapiGermplasm[]; + germplasm: Germplasm[]; localCriteria: GermplasmSearchCriteria = DataDiscoveryCriteriaUtils.emptyGermplasmSearchCriteria(); @Input() criteriaFromForm$: BehaviorSubject<DataDiscoveryCriteria>; @Input() germplasmSearchCriteria$: BehaviorSubject<GermplasmSearchCriteria>; @Input() germplasmFacets$: BehaviorSubject<DataDiscoveryFacet[]>; - headers: string[] = ['germplasmName', 'accessionNumber', 'commonCropName', 'instituteName']; + headers: string[] = ['germplasmName', 'accessionNumber', 'genusSpecies', 'instituteName', 'biologicalStatusOfAccessionCode']; elementPerPage: number[] = [15, 20, 25]; loading: boolean; fieldSortState: object = { germplasmName: null, accessionNumber: null, - commonCropName: null, - instituteName: null + genusSpecies: null, + instituteName: null, + biologicalStatusOfAccessionCode: null }; pagination = { @@ -102,7 +102,8 @@ export class GermplasmResultPageComponent implements OnInit { accessionNumbers: asArray(criteria.accessions), synonyms: asArray(criteria.accessions), - sources: asArray(criteria.sources) + // Do not use the source as a criterion because of the ES should request. + // sources: asArray(criteria.sources) }; this.germplasmSearchCriteria$.next(this.localCriteria); @@ -182,7 +183,8 @@ export class GermplasmResultPageComponent implements OnInit { this.germplasmSearchCriteria$.next(this.localCriteria); } - formatFacets(facets: DataDiscoveryFacet[]): DataDiscoveryFacet[] { + // Format facets by renaming and merging some facets in one facet. + /*formatFacets(facets: DataDiscoveryFacet[]): DataDiscoveryFacet[] { const bioStatusAndGeneticNature = []; let newFacets: DataDiscoveryFacet[] = []; for (const facet of facets) { @@ -206,5 +208,5 @@ export class GermplasmResultPageComponent implements OnInit { ...newFacets ]; return newFacets; - } + }*/ } diff --git a/frontend/src/app/gnpis.service.spec.ts b/frontend/src/app/gnpis.service.spec.ts index cf3073d1..351301fc 100644 --- a/frontend/src/app/gnpis.service.spec.ts +++ b/frontend/src/app/gnpis.service.spec.ts @@ -152,6 +152,7 @@ describe('GnpisService', () => { geneticNature: null, holdingInstitute: null, sources: null, + types: 'Germplasm', facetFields: null, sortBy: null, diff --git a/frontend/src/app/gnpis.service.ts b/frontend/src/app/gnpis.service.ts index bcd26c5e..f149bbca 100644 --- a/frontend/src/app/gnpis.service.ts +++ b/frontend/src/app/gnpis.service.ts @@ -8,7 +8,6 @@ import { DataDiscoverySource } from './models/data-discovery.model'; import { - BrapiGermplasm, BrapiResults, GermplasmCriteria, GermplasmResults @@ -109,7 +108,7 @@ export class GnpisService { ); } - germplasmSearch(criteria: GermplasmCriteria): Observable<GermplasmResults<BrapiGermplasm>> { + germplasmSearch(criteria: GermplasmCriteria): Observable<GermplasmResults<Germplasm>> { return zip( // Get source by URI diff --git a/frontend/src/app/models/data-discovery.model.ts b/frontend/src/app/models/data-discovery.model.ts index 77e6fcef..dc8d63d8 100644 --- a/frontend/src/app/models/data-discovery.model.ts +++ b/frontend/src/app/models/data-discovery.model.ts @@ -65,6 +65,7 @@ export class DataDiscoveryCriteriaUtils { biologicalStatus: null, geneticNature: null, sources: null, + types: null, facetFields: ['holdingInstitute', 'biologicalStatus', 'geneticNature', 'country'], diff --git a/frontend/src/app/models/gnpis.model.ts b/frontend/src/app/models/gnpis.model.ts index 0396e0ef..1fdd633e 100644 --- a/frontend/src/app/models/gnpis.model.ts +++ b/frontend/src/app/models/gnpis.model.ts @@ -23,6 +23,7 @@ export interface GermplasmSearchCriteria { geneticNature: string[]; holdingInstitute: string[]; sources: string[]; + types: string; facetFields: string[]; sortBy: string; -- GitLab From 5e11bf657ea190ac8e4fb204a7a86398e7db6904 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Thu, 30 Jan 2020 15:32:05 +0100 Subject: [PATCH 14/35] fix: Add collection and panel names in export. Fix bug with focus button. GNP-4309 --- .../service/es/GermplasmServiceImpl.java | 21 +++++++++++++++++++ .../small-facets/small-facets.component.ts | 12 ++++++----- .../src/app/models/data-discovery.model.ts | 2 +- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java b/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java index 3c32ac33..75fbcc4e 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java @@ -4,6 +4,7 @@ import com.opencsv.CSVWriter; import fr.inra.urgi.faidare.api.faidare.v1.GnpISGermplasmController; import fr.inra.urgi.faidare.domain.criteria.FaidareGermplasmPOSTShearchCriteria; import fr.inra.urgi.faidare.domain.criteria.GermplasmSearchCriteria; +import fr.inra.urgi.faidare.domain.data.germplasm.CollPopVO; import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO; import fr.inra.urgi.faidare.domain.data.germplasm.PedigreeVO; import fr.inra.urgi.faidare.domain.data.germplasm.ProgenyVO; @@ -19,7 +20,9 @@ import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; /** * @author cpommier, gcornut @@ -66,14 +69,32 @@ public class GermplasmServiceImpl implements GermplasmService { "LotName", "LotSynonym", "CollectionName", "CollectionType", "PanelName", "PanelSize"}; csvWriter.writeNext(header); + while (germplasms.hasNext()) { + List<String> collectionNames = new ArrayList<>(); + List<String> panelNames = new ArrayList<>(); GermplasmVO germplasmVO = germplasms.next(); + + if (germplasmVO.getCollection() != null) { + for (CollPopVO collection : germplasmVO.getCollection()) { + collectionNames.add(collection.getName()); + } + } + + if (germplasmVO.getPanel() != null) { + for (CollPopVO panel : germplasmVO.getPanel()) { + panelNames.add(panel.getName()); + } + } + String[] line = new String[header.length]; line[0] = germplasmVO.getGermplasmPUI(); line[1] = germplasmVO.getAccessionNumber(); line[2] = germplasmVO.getGermplasmName(); line[3] = germplasmVO.getCommonCropName(); line[4] = germplasmVO.getInstituteName(); + line[7] = (collectionNames != null) ? String.join(", ", collectionNames) : ""; + line[9] = (panelNames != null) ? String.join(", ", panelNames) : ""; csvWriter.writeNext(line); } csvWriter.close(); diff --git a/frontend/src/app/facets/small-facets/small-facets.component.ts b/frontend/src/app/facets/small-facets/small-facets.component.ts index 05de4b62..06229027 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.ts +++ b/frontend/src/app/facets/small-facets/small-facets.component.ts @@ -46,6 +46,10 @@ export class SmallFacetsComponent implements OnInit { this.checkBoxes.addControl('selectSwitchButton', switchControl); } + this.displayGermplasmResult$.subscribe(value => { + this.displayGermplasmCurrentState = value; + }); + if (this.criteria$) { this.criteria$.pipe(filter(c => c !== this.localCriteria)) .subscribe(criteria => { @@ -62,7 +66,9 @@ export class SmallFacetsComponent implements OnInit { this.germplasmSearchCriteria$.pipe(filter(c => c !== this.germplasmLocalCriteria)) .subscribe(germplasmCriteria => { this.germplasmLocalCriteria = germplasmCriteria; - this.getSelectedTerms(germplasmCriteria); + if (this.displayGermplasmCurrentState) { + this.getSelectedTerms(germplasmCriteria); + } }); } @@ -84,10 +90,6 @@ export class SmallFacetsComponent implements OnInit { } this.germplasmSearchCriteria$.next(this.germplasmLocalCriteria); }); - - this.displayGermplasmResult$.subscribe(value => { - this.displayGermplasmCurrentState = value; - }); } getSelectedTerms(criteria) { diff --git a/frontend/src/app/models/data-discovery.model.ts b/frontend/src/app/models/data-discovery.model.ts index dc8d63d8..977bc3b3 100644 --- a/frontend/src/app/models/data-discovery.model.ts +++ b/frontend/src/app/models/data-discovery.model.ts @@ -65,7 +65,7 @@ export class DataDiscoveryCriteriaUtils { biologicalStatus: null, geneticNature: null, sources: null, - types: null, + types: 'Germplasm', facetFields: ['holdingInstitute', 'biologicalStatus', 'geneticNature', 'country'], -- GitLab From 00e84e8aad60682bbc5f196cc3f1a9e54fe38d83 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Mon, 3 Feb 2020 12:14:33 +0100 Subject: [PATCH 15/35] fix: Rename focus button into Details. Rename export button into Export GnpIS Plant Material. Move the display of selected element above the search box in large facets. Fix the display of the germplasm result page when unselect Germplasm and when select other criterion in facet type. GNP-4309 --- .../large-facets/large-facets.component.html | 16 +++++++----- .../small-facets/small-facets.component.html | 24 +++++++++++++++++- .../small-facets/small-facets.component.scss | 3 +++ .../small-facets/small-facets.component.ts | 7 +++++ .../germplasm-result-page.component.html | 2 +- .../assets/faidare/images/advance_search.png | Bin 4181 -> 0 bytes .../assets/faidare/images/question-mark.png | Bin 0 -> 37311 bytes 7 files changed, 44 insertions(+), 8 deletions(-) delete mode 100644 frontend/src/assets/faidare/images/advance_search.png create mode 100644 frontend/src/assets/faidare/images/question-mark.png diff --git a/frontend/src/app/facets/large-facets/large-facets.component.html b/frontend/src/app/facets/large-facets/large-facets.component.html index 96939295..98904073 100644 --- a/frontend/src/app/facets/large-facets/large-facets.component.html +++ b/frontend/src/app/facets/large-facets/large-facets.component.html @@ -12,18 +12,22 @@ </ng-template> <ng-container *ngIf="facet.terms.length && facet.terms.length > 8"> - <div class="mb-2"> - <span class="badge badge-pill badge-secondary mr-1" + + <div class="card mb-1"> + <div class="card-body"> + <h3 class="card-title">{{ facet.field }}</h3> + + <div class="mb-2"> + <span class="badge badge-pill badge-secondary mr-1 selectedElem" + style="font-size: smaller" *ngFor="let term of selectedTerms[facet.field]" tabindex="0" (keydown.delete)="removeKey(term)" (keydown.backspace)="removeKey(term)"> {{ displaySourceName(term) }} <button tabindex="-1" type="button" class="btn btn-link" (click)="removeKey(term)">×</button> </span> - </div> - <div class="card mb-1"> - <div class="card-body"> - <h3 class="card-title">{{ facet.field }}</h3> + </div> + <input #typeahead class="form-control" [formControl]="criterion" [ngbTypeahead]="search" (selectItem)="selectKey($event)" [resultTemplate]="resultTemplate" diff --git a/frontend/src/app/facets/small-facets/small-facets.component.html b/frontend/src/app/facets/small-facets/small-facets.component.html index b9fdb0e5..f1521597 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.html +++ b/frontend/src/app/facets/small-facets/small-facets.component.html @@ -1,3 +1,17 @@ +<ng-template #germplasmDetailsPopup> + <div class="card ngb-popover-window "> + <br> + This is a button to view more information about the germplasm + (germplasm name, accession number, genus species, institute name * , biological status.) + that are return by your research. <br> + You can also download the data in GnpIS Plant Material standard.<br> + <br> + * The name of the institute which store the accession. + </div> +</ng-template> + + + <div class="card mb-1" *ngIf="facet.terms.length && facet.terms.length <= 8"> <div class="card-body"> <h3 class="card-title">{{ facet.field }}</h3> @@ -15,10 +29,11 @@ <div id="germplasmSearch" title="Focus on germplasm details with export data link" + style="margin-top: 5px" *ngIf="term.term == 'Germplasm' && !criteriaIsEmpty"> <label for="selectSwitchButton" style="margin-right: 5px" id="switchTitle"> - Focus + Details </label> <label class="switch" id="switchButton"> <input type="checkbox" id="selectSwitchButton" @@ -26,6 +41,13 @@ (change)="switchToGermplasmResult()"> <span class="slider round"></span> </label> + <a class="btn popovers" data-boundary="window" placement="auto" + [autoClose]="'outside'" + [ngbPopover]="germplasmDetailsPopup" + [popoverTitle]="'Details button help.'" container="body"> + <img src="assets/faidare/images/question-mark.png" alt="help" title="" + height="20px" style="margin-top: -10px"/> + </a> </div> </div> </form> diff --git a/frontend/src/app/facets/small-facets/small-facets.component.scss b/frontend/src/app/facets/small-facets/small-facets.component.scss index de4ff0c5..0fd05582 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.scss +++ b/frontend/src/app/facets/small-facets/small-facets.component.scss @@ -7,6 +7,9 @@ .card h3 { font-weight: bold; } +.popovers{ + cursor: pointer; +} #switchTitle { cursor: pointer; diff --git a/frontend/src/app/facets/small-facets/small-facets.component.ts b/frontend/src/app/facets/small-facets/small-facets.component.ts index 06229027..05c7033b 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.ts +++ b/frontend/src/app/facets/small-facets/small-facets.component.ts @@ -74,6 +74,13 @@ export class SmallFacetsComponent implements OnInit { this.checkBoxes.valueChanges.subscribe(values => { const selectedTerms = Object.keys(values).filter(key => values[key]); + const multiSelection = Object.keys(values).filter(key => values[key] && key != 'Germplasm'); + const unselectGermplasm = Object.keys(values).filter(key => key == 'Germplasm' && !values[key]); + + if (multiSelection.length > 0 || unselectGermplasm.length > 0) { + this.displayGermplasmResult$.next(false); + } + this.showAndHideAdvanceGermplasmSearch(selectedTerms); if (this.criteria$) { this.localCriteria = { diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html index 0c624745..13ca85b9 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html @@ -22,7 +22,7 @@ <button type="button" class="btn btn-outline-success mb-2" (click)="exportPlantMaterial(localCriteria)"> - Export + Export GnpIS Plant Material <img src="assets/faidare/images/csv-logo.png" alt="csv logo" title="" height="20px"/> </button> diff --git a/frontend/src/assets/faidare/images/advance_search.png b/frontend/src/assets/faidare/images/advance_search.png deleted file mode 100644 index 549f0a9c761da810782ac97751d627ddc6642fb0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4181 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 diff --git a/frontend/src/assets/faidare/images/question-mark.png b/frontend/src/assets/faidare/images/question-mark.png new file mode 100644 index 0000000000000000000000000000000000000000..87fbdf4294ddba32291fdcae056a7c0f9cef793c GIT binary patch literal 37311 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 A@&Et; literal 0 HcmV?d00001 -- GitLab From b4efa2dc3b92cc886abb8c7b85853ff1e191b68a Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Mon, 3 Feb 2020 12:19:48 +0100 Subject: [PATCH 16/35] fix: Fix the lint and the tests. GNP-4309 --- .../app/facets/small-facets/small-facets.component.spec.ts | 2 +- .../src/app/facets/small-facets/small-facets.component.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/facets/small-facets/small-facets.component.spec.ts b/frontend/src/app/facets/small-facets/small-facets.component.spec.ts index 63c8a854..b3c910f9 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.spec.ts +++ b/frontend/src/app/facets/small-facets/small-facets.component.spec.ts @@ -154,7 +154,7 @@ describe('SmallFacetsComponent', () => { tester.detectChanges(); expect(tester.switchButton.length).toEqual(1); - expect(tester.switchButton[0]).toContainText('Focus'); + expect(tester.switchButton[0]).toContainText('Details'); }); diff --git a/frontend/src/app/facets/small-facets/small-facets.component.ts b/frontend/src/app/facets/small-facets/small-facets.component.ts index 05c7033b..36e508a8 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.ts +++ b/frontend/src/app/facets/small-facets/small-facets.component.ts @@ -74,8 +74,8 @@ export class SmallFacetsComponent implements OnInit { this.checkBoxes.valueChanges.subscribe(values => { const selectedTerms = Object.keys(values).filter(key => values[key]); - const multiSelection = Object.keys(values).filter(key => values[key] && key != 'Germplasm'); - const unselectGermplasm = Object.keys(values).filter(key => key == 'Germplasm' && !values[key]); + const multiSelection = Object.keys(values).filter(key => values[key] && key !== 'Germplasm'); + const unselectGermplasm = Object.keys(values).filter(key => key === 'Germplasm' && !values[key]); if (multiSelection.length > 0 || unselectGermplasm.length > 0) { this.displayGermplasmResult$.next(false); -- GitLab From e8b3821043b2371a4b57a294686a4475b6f1026d Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Mon, 3 Feb 2020 18:13:27 +0100 Subject: [PATCH 17/35] fix: Create a objects mapping to have more readable name in the headers of the germplasm result page and on the facets title. GNP-4309 --- frontend/src/app/facets/facets.component.ts | 6 ++++++ .../app/facets/large-facets/large-facets.component.html | 2 +- .../facets/large-facets/large-facets.component.spec.ts | 2 +- .../src/app/facets/large-facets/large-facets.component.ts | 2 ++ .../app/facets/small-facets/small-facets.component.html | 4 ++-- .../facets/small-facets/small-facets.component.spec.ts | 2 +- .../src/app/facets/small-facets/small-facets.component.ts | 4 +++- .../germplasm-result-page.component.html | 2 +- .../germplasm-result-page.component.ts | 8 ++++++++ 9 files changed, 25 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/facets/facets.component.ts b/frontend/src/app/facets/facets.component.ts index 2f6f3264..c1f30b80 100644 --- a/frontend/src/app/facets/facets.component.ts +++ b/frontend/src/app/facets/facets.component.ts @@ -6,6 +6,12 @@ import { import { BehaviorSubject } from 'rxjs'; import { GermplasmSearchCriteria } from '../models/gnpis.model'; +export const formatFacets: {[key: string]: string} = { + 'holdingInstitute': 'holding institute', + 'biologicalStatus': 'biological status', + 'geneticNature': 'genetic nature' +}; + @Component({ selector: 'faidare-facets', templateUrl: './facets.component.html', diff --git a/frontend/src/app/facets/large-facets/large-facets.component.html b/frontend/src/app/facets/large-facets/large-facets.component.html index 98904073..3424c035 100644 --- a/frontend/src/app/facets/large-facets/large-facets.component.html +++ b/frontend/src/app/facets/large-facets/large-facets.component.html @@ -15,7 +15,7 @@ <div class="card mb-1"> <div class="card-body"> - <h3 class="card-title">{{ facet.field }}</h3> + <h3 class="card-title">{{ formatFacets[facet.field] ? (formatFacets[ facet.field] | titlecase) : facet.field | titlecase }}</h3> <div class="mb-2"> <span class="badge badge-pill badge-secondary mr-1 selectedElem" diff --git a/frontend/src/app/facets/large-facets/large-facets.component.spec.ts b/frontend/src/app/facets/large-facets/large-facets.component.spec.ts index 0c65fbef..032b1273 100644 --- a/frontend/src/app/facets/large-facets/large-facets.component.spec.ts +++ b/frontend/src/app/facets/large-facets/large-facets.component.spec.ts @@ -109,7 +109,7 @@ describe('LargeFacetsComponent', () => { component.facet = largeFacet; tester.detectChanges(); - expect(tester.facetTitle.textContent).toEqual('large Facet'); + expect(tester.facetTitle.textContent).toEqual('Large Facet'); expect(tester.facetInput).toBeTruthy(); }); diff --git a/frontend/src/app/facets/large-facets/large-facets.component.ts b/frontend/src/app/facets/large-facets/large-facets.component.ts index 196b5a89..be9693f0 100644 --- a/frontend/src/app/facets/large-facets/large-facets.component.ts +++ b/frontend/src/app/facets/large-facets/large-facets.component.ts @@ -10,6 +10,7 @@ import { FormControl } from '@angular/forms'; import { NgbTypeaheadSelectItemEvent } from '@ng-bootstrap/ng-bootstrap'; import { GermplasmSearchCriteria } from '../../models/gnpis.model'; import { GnpisService } from '../../gnpis.service'; +import { formatFacets } from '../facets.component'; export type FacetTermOrRefine = { term: string; @@ -30,6 +31,7 @@ export class LargeFacetsComponent implements OnInit { @Input() germplasmSearchCriteria$: BehaviorSubject<GermplasmSearchCriteria>; @ViewChild('typeahead') typeahead: ElementRef<HTMLInputElement>; + formatFacets = formatFacets; localCriteria: DataDiscoveryCriteria; germplasmLocalCriteria: GermplasmSearchCriteria; diff --git a/frontend/src/app/facets/small-facets/small-facets.component.html b/frontend/src/app/facets/small-facets/small-facets.component.html index f1521597..026b6190 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.html +++ b/frontend/src/app/facets/small-facets/small-facets.component.html @@ -14,7 +14,7 @@ <div class="card mb-1" *ngIf="facet.terms.length && facet.terms.length <= 8"> <div class="card-body"> - <h3 class="card-title">{{ facet.field }}</h3> + <h3 class="card-title">{{ formatFacets[facet.field] ? (formatFacets[ facet.field] | titlecase) : facet.field | titlecase }}</h3> <form [formGroup]="checkBoxes" class="card-text"> <div class="form-check" *ngFor="let term of facet.terms"> @@ -44,7 +44,7 @@ <a class="btn popovers" data-boundary="window" placement="auto" [autoClose]="'outside'" [ngbPopover]="germplasmDetailsPopup" - [popoverTitle]="'Details button help.'" container="body"> + [popoverTitle]="'Details button\'s help.'" container="body"> <img src="assets/faidare/images/question-mark.png" alt="help" title="" height="20px" style="margin-top: -10px"/> </a> diff --git a/frontend/src/app/facets/small-facets/small-facets.component.spec.ts b/frontend/src/app/facets/small-facets/small-facets.component.spec.ts index b3c910f9..6d8f8ce9 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.spec.ts +++ b/frontend/src/app/facets/small-facets/small-facets.component.spec.ts @@ -82,7 +82,7 @@ describe('SmallFacetsComponent', () => { component.displayGermplasmResult$ = new BehaviorSubject<boolean>(false); tester.detectChanges(); - expect(tester.title).toContainText('sources'); + expect(tester.title).toContainText('Sources'); expect(tester.terms[0]).toContainText('SOURCE 1 (10)'); expect(tester.terms[0].attr('for')).toBe('source 1'); diff --git a/frontend/src/app/facets/small-facets/small-facets.component.ts b/frontend/src/app/facets/small-facets/small-facets.component.ts index 36e508a8..305a77e7 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.ts +++ b/frontend/src/app/facets/small-facets/small-facets.component.ts @@ -9,6 +9,7 @@ import { BehaviorSubject } from 'rxjs'; import { filter } from 'rxjs/operators'; import { Params } from '@angular/router'; import { GermplasmSearchCriteria } from '../../models/gnpis.model'; +import { formatFacets } from '../facets.component'; @Component({ selector: 'faidare-small-facets', @@ -23,6 +24,7 @@ export class SmallFacetsComponent implements OnInit { @Input() displayGermplasmResult$: BehaviorSubject<boolean>; @Output() changed = new EventEmitter<boolean>(); + formatFacets = formatFacets; localCriteria: DataDiscoveryCriteria; @@ -77,7 +79,7 @@ export class SmallFacetsComponent implements OnInit { const multiSelection = Object.keys(values).filter(key => values[key] && key !== 'Germplasm'); const unselectGermplasm = Object.keys(values).filter(key => key === 'Germplasm' && !values[key]); - if (multiSelection.length > 0 || unselectGermplasm.length > 0) { + if ((multiSelection.length > 0 && this.facet.field === 'types') || unselectGermplasm.length > 0) { this.displayGermplasmResult$.next(false); } diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html index 13ca85b9..e98fac4f 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html @@ -43,7 +43,7 @@ <tr> <th id="germplasmResultThead" *ngFor="let header of headers" scope="col"> - <label id="tabHeader" (click)="getTabField(header)">{{ header }} + <label id="tabHeader" (click)="getTabField(header)">{{ formatHeaders[header] }} <i *ngIf="!fieldSortState[header]" class="fa fa-sort" aria-hidden="true"></i> <i *ngIf="fieldSortState[header] =='desc'" diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts index 424fb355..7f9d719f 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts @@ -41,6 +41,14 @@ export class GermplasmResultPageComponent implements OnInit { biologicalStatusOfAccessionCode: null }; + formatHeaders: { [key: string]: string } = { + 'germplasmName': 'Germplasm name', + 'accessionNumber': 'Accession number', + 'genusSpecies': 'Genus species', + 'instituteName': 'Institute name', + 'biologicalStatusOfAccessionCode': 'Biological status' + }; + pagination = { startResult: 1, endResult: DEFAULT_PAGE_SIZE, -- GitLab From 4b535c5ac4bf8e8a8c3718eae5df6f45c7de8bdf Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Wed, 5 Feb 2020 14:51:47 +0100 Subject: [PATCH 18/35] fix: Add the country of origin on the germplasm result page. Minor fixes. GNP-4309 --- .../repository/es/GermplasmRepositoryImpl.java | 1 - backend/src/main/main.iml | 6 ++++++ .../facets/small-facets/small-facets.component.html | 13 +++++-------- .../germplasm-result-page.component.html | 3 ++- .../germplasm-result-page.component.ts | 11 +++++++++-- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java index aa77ef59..6be901a2 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java @@ -112,7 +112,6 @@ public class GermplasmRepositoryImpl implements GermplasmRepository { @Override public GermplasmSearchResponse esShouldFind(FaidareGermplasmPOSTShearchCriteria germplasmSearchCriteria) { try { - // QueryBuilder query = queryFactory.createOrQuery(germplasmSearchCriteria); // Prepare search request SearchRequest request = prepareSearchRequest(germplasmSearchCriteria); diff --git a/backend/src/main/main.iml b/backend/src/main/main.iml index f4e15773..50f3d6bc 100644 --- a/backend/src/main/main.iml +++ b/backend/src/main/main.iml @@ -29,5 +29,11 @@ <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" /> <orderEntry type="library" name="Gradle: org.elasticsearch.client:elasticsearch-rest-high-level-client:6.5.4" level="project" /> <orderEntry type="library" name="Gradle: org.elasticsearch:elasticsearch-core:6.5.4" level="project" /> + <orderEntry type="library" name="Gradle: org.springframework:spring-core:5.1.4.RELEASE" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.tomcat.embed:tomcat-embed-core:9.0.14" level="project" /> + <orderEntry type="library" name="Gradle: com.opencsv:opencsv:4.4" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.8.1" level="project" /> + <orderEntry type="library" name="Gradle: org.apache.lucene:lucene-join:7.5.0" level="project" /> + <orderEntry type="library" name="Gradle: com.fasterxml.jackson.core:jackson-annotations:2.9.0" level="project" /> </component> </module> \ No newline at end of file diff --git a/frontend/src/app/facets/small-facets/small-facets.component.html b/frontend/src/app/facets/small-facets/small-facets.component.html index 026b6190..400fc8fa 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.html +++ b/frontend/src/app/facets/small-facets/small-facets.component.html @@ -1,12 +1,8 @@ <ng-template #germplasmDetailsPopup> <div class="card ngb-popover-window "> <br> - This is a button to view more information about the germplasm - (germplasm name, accession number, genus species, institute name * , biological status.) - that are return by your research. <br> - You can also download the data in GnpIS Plant Material standard.<br> - <br> - * The name of the institute which store the accession. + Activation of the button allows to view more information about the germplasm.<br> + You can also download the germplasm list. </div> </ng-template> @@ -28,7 +24,7 @@ </label> <div id="germplasmSearch" - title="Focus on germplasm details with export data link" + title="" style="margin-top: 5px" *ngIf="term.term == 'Germplasm' && !criteriaIsEmpty"> <label for="selectSwitchButton" style="margin-right: 5px" @@ -45,7 +41,8 @@ [autoClose]="'outside'" [ngbPopover]="germplasmDetailsPopup" [popoverTitle]="'Details button\'s help.'" container="body"> - <img src="assets/faidare/images/question-mark.png" alt="help" title="" + <img src="assets/faidare/images/question-mark.png" alt="help" + title="This is a button to view more information about the germplasm that are return by your search. You can also download the results in standard format." height="20px" style="margin-top: -10px"/> </a> </div> diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html index e98fac4f..c4fce582 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html @@ -22,7 +22,7 @@ <button type="button" class="btn btn-outline-success mb-2" (click)="exportPlantMaterial(localCriteria)"> - Export GnpIS Plant Material + Export Plant Material list <img src="assets/faidare/images/csv-logo.png" alt="csv logo" title="" height="20px"/> </button> @@ -64,6 +64,7 @@ <td>{{ accession.genusSpecies }}</td> <td>{{ accession.instituteName }}</td> <td>{{ accession.biologicalStatusOfAccessionCode }}</td> + <td>{{ accession.countryOfOriginCode }}</td> </tr> </ng-container> </tbody> diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts index 7f9d719f..5d5ee288 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts @@ -30,7 +30,13 @@ export class GermplasmResultPageComponent implements OnInit { @Input() germplasmSearchCriteria$: BehaviorSubject<GermplasmSearchCriteria>; @Input() germplasmFacets$: BehaviorSubject<DataDiscoveryFacet[]>; - headers: string[] = ['germplasmName', 'accessionNumber', 'genusSpecies', 'instituteName', 'biologicalStatusOfAccessionCode']; + headers: string[] = [ + 'germplasmName', + 'accessionNumber', + 'genusSpecies', + 'instituteName', + 'biologicalStatusOfAccessionCode', + 'countryOfOriginCode']; elementPerPage: number[] = [15, 20, 25]; loading: boolean; fieldSortState: object = { @@ -46,7 +52,8 @@ export class GermplasmResultPageComponent implements OnInit { 'accessionNumber': 'Accession number', 'genusSpecies': 'Genus species', 'instituteName': 'Institute name', - 'biologicalStatusOfAccessionCode': 'Biological status' + 'biologicalStatusOfAccessionCode': 'Biological status', + 'countryOfOriginCode': 'Country of origin' }; pagination = { -- GitLab From af153c2a7dbe158c5db54d7a74f3aa7869277ec4 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Fri, 7 Feb 2020 15:19:32 +0100 Subject: [PATCH 19/35] fix: Change the query use to get the germplasm list and use filter to have more accurate result. GNP-4309 --- .idea/compiler.xml | 9 +++++ .../query/impl/ESGenericQueryFactory.java | 37 +++++++++++++++---- .../germplasm-result-page.component.ts | 30 +-------------- 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 677323ac..8b06b981 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,15 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="CompilerConfiguration"> + <annotationProcessing> + <profile name="Gradle Imported" enabled="true"> + <outputRelativeToContentRoot value="true" /> + <processorPath useClasspath="false"> + <entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-configuration-processor/2.1.2.RELEASE/db9671c321defb942a6700fae8a7700a137a25e/spring-boot-configuration-processor-2.1.2.RELEASE.jar" /> + </processorPath> + <module name="faidare.backend.main" /> + </profile> + </annotationProcessing> <bytecodeTargetLevel> <module name="faidare.backend.main" target="1.8" /> <module name="faidare.backend.test" target="1.8" /> diff --git a/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactory.java b/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactory.java index f35b89df..31c7be0d 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactory.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactory.java @@ -64,7 +64,7 @@ public class ESGenericQueryFactory<C> implements ESQueryFactory<C> { List<QueryBuilder> queries = createQueryFromMapping(criteria, null, null, voMappingToCriteria, documentMetadata); if (!queries.isEmpty()) { - return orQueries(queries); + return germplasmFilterQueries(queries); } else { return matchAllQuery(); } @@ -103,7 +103,7 @@ public class ESGenericQueryFactory<C> implements ESQueryFactory<C> { List<QueryBuilder> queries = createQueryFromMapping(criteria, null, excludedDocumentFields, voMappingToCriteria, documentMetadata); if (!queries.isEmpty()) { - return orQueries(queries); + return germplasmFilterQueries(queries); } else { return matchAllQuery(); } @@ -142,7 +142,7 @@ public class ESGenericQueryFactory<C> implements ESQueryFactory<C> { List<QueryBuilder> queries = createQueryFromMapping(criteria, includedDocumentFields, null, voMappingToCriteria, documentMetadata); if (!queries.isEmpty()) { - return orQueries(queries); + return germplasmFilterQueries(queries); } else { return matchAllQuery(); } @@ -367,21 +367,44 @@ public class ESGenericQueryFactory<C> implements ESQueryFactory<C> { * * @return null if not queries given; the first query if only one query is given; a bool query otherwise */ - public static QueryBuilder orQueries(List<QueryBuilder> queries) { + public static QueryBuilder germplasmFilterQueries(List<QueryBuilder> queries) { if (queries == null || queries.isEmpty()) { return null; } else if (queries.size() == 1) { return queries.get(0); } else { + List<String> shouldCriterion = Arrays.asList("commonCropName", + "species", "germplasmGenus", "genusSpecies", "subtaxa", "genus", + "genusSpeciesSubtaxa", "taxonSynonyms", "panel", "collection", "population", + "germplasmName", "accessionNumber", "synonyms"); BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); + BoolQueryBuilder boolShouldQueryBuilder = QueryBuilders.boolQuery(); + List<QueryBuilder> filterQueries = new ArrayList<>(); for (QueryBuilder query : queries) { - boolQueryBuilder.should(query); + boolean isShouldCriterion = stringContainsItemFromList(query.toString(), shouldCriterion); + if (isShouldCriterion){ + boolShouldQueryBuilder.should(query); + } else { + filterQueries.add(query); + } + } + boolQueryBuilder.must(boolShouldQueryBuilder); + for (QueryBuilder query: filterQueries) { + boolQueryBuilder.filter(query); } return boolQueryBuilder; } } - public static QueryBuilder orQueries(QueryBuilder... queries) { - return andQueries(Arrays.asList(queries)); + public static boolean stringContainsItemFromList(String inputStr, List<String> items) + { + for(String item: items) + { + if(inputStr.contains(item)) + { + return true; + } + } + return false; } } diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts index 5d5ee288..1772c98e 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts @@ -117,8 +117,7 @@ export class GermplasmResultPageComponent implements OnInit { accessionNumbers: asArray(criteria.accessions), synonyms: asArray(criteria.accessions), - // Do not use the source as a criterion because of the ES should request. - // sources: asArray(criteria.sources) + sources: asArray(criteria.sources) }; this.germplasmSearchCriteria$.next(this.localCriteria); @@ -197,31 +196,4 @@ export class GermplasmResultPageComponent implements OnInit { this.localCriteria.pageSize = pageSize; this.germplasmSearchCriteria$.next(this.localCriteria); } - - // Format facets by renaming and merging some facets in one facet. - /*formatFacets(facets: DataDiscoveryFacet[]): DataDiscoveryFacet[] { - const bioStatusAndGeneticNature = []; - let newFacets: DataDiscoveryFacet[] = []; - for (const facet of facets) { - if (facet.field === 'biologicalStatus' || facet.field === 'geneticNature') { - for (const term of facet.terms) { - bioStatusAndGeneticNature - .push(term); - } - } else if (facet.field === 'holdingInstitute') { - facet.field = 'holding institute'; - newFacets.push(facet); - } else { - newFacets.push(facet); - } - } - newFacets = [ - { - field: 'Biological status / Genetic nature', - terms: bioStatusAndGeneticNature - }, - ...newFacets - ]; - return newFacets; - }*/ } -- GitLab From 5536429561d133c2a455472f32d6697cd0c87895 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Tue, 11 Feb 2020 11:32:13 +0100 Subject: [PATCH 20/35] fix: Remove popover on question mark. GNP-4309 --- .../src/app/facets/small-facets/small-facets.component.html | 5 ----- .../germplasm-result-page.component.html | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/facets/small-facets/small-facets.component.html b/frontend/src/app/facets/small-facets/small-facets.component.html index 400fc8fa..86ccf497 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.html +++ b/frontend/src/app/facets/small-facets/small-facets.component.html @@ -37,14 +37,9 @@ (change)="switchToGermplasmResult()"> <span class="slider round"></span> </label> - <a class="btn popovers" data-boundary="window" placement="auto" - [autoClose]="'outside'" - [ngbPopover]="germplasmDetailsPopup" - [popoverTitle]="'Details button\'s help.'" container="body"> <img src="assets/faidare/images/question-mark.png" alt="help" title="This is a button to view more information about the germplasm that are return by your search. You can also download the results in standard format." height="20px" style="margin-top: -10px"/> - </a> </div> </div> </form> diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html index c4fce582..0a38353a 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html @@ -5,7 +5,6 @@ <span class="col mt-2 bolder"> Results: </span> - <div class="row align-content-center"> <span *ngIf="pagination.totalResult" style="margin-right: 5px" class="col align-self-end small text-muted mt-3"> @@ -43,7 +42,8 @@ <tr> <th id="germplasmResultThead" *ngFor="let header of headers" scope="col"> - <label id="tabHeader" (click)="getTabField(header)">{{ formatHeaders[header] }} + <label id="tabHeader" + (click)="getTabField(header)">{{ formatHeaders[header] }} <i *ngIf="!fieldSortState[header]" class="fa fa-sort" aria-hidden="true"></i> <i *ngIf="fieldSortState[header] =='desc'" -- GitLab From 0a41fc74505adfef396bae3a047b1bf2d5419a34 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Tue, 11 Feb 2020 15:54:09 +0100 Subject: [PATCH 21/35] fix: Minor fixes. GNP-4309 --- .../src/app/facets/small-facets/small-facets.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/facets/small-facets/small-facets.component.html b/frontend/src/app/facets/small-facets/small-facets.component.html index 86ccf497..f3a4b568 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.html +++ b/frontend/src/app/facets/small-facets/small-facets.component.html @@ -39,7 +39,7 @@ </label> <img src="assets/faidare/images/question-mark.png" alt="help" title="This is a button to view more information about the germplasm that are return by your search. You can also download the results in standard format." - height="20px" style="margin-top: -10px"/> + height="20px" style="margin-top: -10px; margin-left: 10px"/> </div> </div> </form> -- GitLab From beea973d6930a8c4eb4cb50c89e78966be5373a1 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Wed, 12 Feb 2020 18:02:28 +0100 Subject: [PATCH 22/35] fix: Add schema:includedInDataCatalog in germplasm_mapping.json. --- .../faidare/repository/es/setup/index/germplasm_mapping.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/test/resources/fr/inra/urgi/faidare/repository/es/setup/index/germplasm_mapping.json b/backend/src/test/resources/fr/inra/urgi/faidare/repository/es/setup/index/germplasm_mapping.json index 359f0616..ab6abaf7 100644 --- a/backend/src/test/resources/fr/inra/urgi/faidare/repository/es/setup/index/germplasm_mapping.json +++ b/backend/src/test/resources/fr/inra/urgi/faidare/repository/es/setup/index/germplasm_mapping.json @@ -131,7 +131,7 @@ }, "instituteName": { "type": "keyword" - }, + }, "pedigree": { "type": "keyword" }, @@ -590,7 +590,7 @@ "source": { "type": "keyword" - }, + }, "groupId": { "type": "long" }, -- GitLab From ecd2710cab05cb8e002ed525ef77c1f3b177d296 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Thu, 13 Feb 2020 18:39:57 +0100 Subject: [PATCH 23/35] fix: Display a no result message when there no germplasm to display on the germplasm result page. Add test on the method which create the query to get the germplasm list. Rename the method createShouldQuery into createShouldFilterQuery and esShouldFind into germplasmFind. GNP-4309 --- .../faidare/v1/GnpISGermplasmController.java | 17 +--- .../elasticsearch/query/ESQueryFactory.java | 2 +- .../query/impl/ESGenericQueryFactory.java | 2 +- .../repository/es/GermplasmRepository.java | 2 +- .../es/GermplasmRepositoryImpl.java | 4 +- .../faidare/service/es/GermplasmService.java | 2 +- .../service/es/GermplasmServiceImpl.java | 4 +- .../query/impl/ESGenericQueryFactoryTest.java | 27 +++++- .../query/impl/expected/query8.json | 89 +++++++++++++++++++ backend/src/test/test.iml | 2 +- .../germplasm-result-page.component.html | 9 ++ .../germplasm-result-page.component.scss | 5 ++ 12 files changed, 136 insertions(+), 29 deletions(-) create mode 100644 backend/src/test/resources/fr/inra/urgi/faidare/elasticsearch/query/impl/expected/query8.json diff --git a/backend/src/main/java/fr/inra/urgi/faidare/api/faidare/v1/GnpISGermplasmController.java b/backend/src/main/java/fr/inra/urgi/faidare/api/faidare/v1/GnpISGermplasmController.java index 26d8f4e0..5a43701f 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/api/faidare/v1/GnpISGermplasmController.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/api/faidare/v1/GnpISGermplasmController.java @@ -119,25 +119,10 @@ public class GnpISGermplasmController { @PostMapping(value = "/search", consumes = APPLICATION_JSON_VALUE) public GermplasmSearchResponse germplasmSearch(@RequestBody @Valid FaidareGermplasmPOSTShearchCriteria criteria) { try { - return germplasmService.esShouldFind(criteria); + return germplasmService.germplasmFind(criteria); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(); } } - - /*@ApiOperation("Suggest germplasm document field values") - @PostMapping(value = "/suggest", consumes = APPLICATION_JSON_VALUE) - public LinkedHashSet<String> germplasmSuggest( - @RequestParam String field, - @RequestParam(required = false) String text, - @RequestParam(required = false) Integer fetchSize, - @RequestBody(required = false) @Valid FaidareGermplasmPOSTShearchCriteria criteria) - throws UnsupportedEncodingException { - if (fetchSize == null) { - fetchSize = Integer.MAX_VALUE; - } - return germplasmService.suggest(field, StringFunctions.asUTF8(text), fetchSize, criteria); - - }*/ } diff --git a/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/ESQueryFactory.java b/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/ESQueryFactory.java index d92d23ef..4fccfa7c 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/ESQueryFactory.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/ESQueryFactory.java @@ -12,6 +12,6 @@ public interface ESQueryFactory<C> { */ QueryBuilder createQuery(C criteria); - QueryBuilder createEsShouldQuery(C criteria); + QueryBuilder createShouldFilterQuery(C criteria); } diff --git a/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactory.java b/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactory.java index 31c7be0d..55b96079 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactory.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactory.java @@ -56,7 +56,7 @@ public class ESGenericQueryFactory<C> implements ESQueryFactory<C> { } @Override - public QueryBuilder createEsShouldQuery(C criteria) { + public QueryBuilder createShouldFilterQuery(C criteria) { try { CriteriaMapping voMappingToCriteria = AnnotatedCriteriaMapper.getMapping(criteria.getClass()); DocumentMetadata<?> documentMetadata = voMappingToCriteria.getDocumentMetadata(); diff --git a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepository.java b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepository.java index f52e18fa..0929cda8 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepository.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepository.java @@ -23,7 +23,7 @@ public interface GermplasmRepository /** * Find germplasm by criteria with pagination with a should query. */ - GermplasmSearchResponse esShouldFind(FaidareGermplasmPOSTShearchCriteria germSearCrit); + GermplasmSearchResponse germplasmFind(FaidareGermplasmPOSTShearchCriteria germSearCrit); /** * Get germplasm by id. diff --git a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java index 6be901a2..91db9f00 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java @@ -94,7 +94,7 @@ public class GermplasmRepositoryImpl implements GermplasmRepository { @Override public Iterator<GermplasmVO> scrollAllGermplasm(FaidareGermplasmPOSTShearchCriteria criteria) { - QueryBuilder query = queryFactory.createEsShouldQuery(criteria); + QueryBuilder query = queryFactory.createShouldFilterQuery(criteria); int fetchSize = criteria.getPageSize().intValue(); return new ESScrollIterator<>(client, requestFactory, parser, GermplasmVO.class, query, fetchSize); } @@ -110,7 +110,7 @@ public class GermplasmRepositoryImpl implements GermplasmRepository { } @Override - public GermplasmSearchResponse esShouldFind(FaidareGermplasmPOSTShearchCriteria germplasmSearchCriteria) { + public GermplasmSearchResponse germplasmFind(FaidareGermplasmPOSTShearchCriteria germplasmSearchCriteria) { try { // Prepare search request diff --git a/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmService.java b/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmService.java index f181e528..7082135d 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmService.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmService.java @@ -17,7 +17,7 @@ public interface GermplasmService { PaginatedList<GermplasmVO> find(GermplasmSearchCriteria criteria); - GermplasmSearchResponse esShouldFind(FaidareGermplasmPOSTShearchCriteria criteria); + GermplasmSearchResponse germplasmFind(FaidareGermplasmPOSTShearchCriteria criteria); /*LinkedHashSet<String> suggest( String criteriaField, String searchText, Integer fetchSize, FaidareGermplasmPOSTShearchCriteria criteria diff --git a/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java b/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java index 75fbcc4e..2fdbfbb7 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java @@ -111,8 +111,8 @@ public class GermplasmServiceImpl implements GermplasmService { } @Override - public GermplasmSearchResponse esShouldFind(FaidareGermplasmPOSTShearchCriteria germplasmSearchCriteria) { - return germplasmRepository.esShouldFind(germplasmSearchCriteria); + public GermplasmSearchResponse germplasmFind(FaidareGermplasmPOSTShearchCriteria germplasmSearchCriteria) { + return germplasmRepository.germplasmFind(germplasmSearchCriteria); } @Override diff --git a/backend/src/test/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactoryTest.java b/backend/src/test/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactoryTest.java index 304d2215..8b6bcd21 100644 --- a/backend/src/test/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactoryTest.java +++ b/backend/src/test/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactoryTest.java @@ -4,10 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Sets; import com.google.common.io.CharStreams; -import fr.inra.urgi.faidare.domain.criteria.LocationCriteria; -import fr.inra.urgi.faidare.domain.criteria.ObservationUnitCriteria; -import fr.inra.urgi.faidare.domain.criteria.ProgramCriteria; -import fr.inra.urgi.faidare.domain.criteria.StudySearchCriteria; +import fr.inra.urgi.faidare.domain.criteria.*; import fr.inra.urgi.faidare.domain.datadiscovery.criteria.DataDiscoveryCriteria; import fr.inra.urgi.faidare.domain.datadiscovery.criteria.DataDiscoveryCriteriaImpl; import fr.inra.urgi.faidare.elasticsearch.criteria.annotation.CriteriaForDocument; @@ -292,6 +289,28 @@ public class ESGenericQueryFactoryTest { assertJsonEquals(actualQuery, expectedQuery); } + + @Test + void should_Generate_Filter_Bool_Terms_Query() throws Exception { + FaidareGermplasmPOSTShearchCriteria criteria = new FaidareGermplasmPOSTShearchCriteria(); + + criteria.setSpecies(newArrayList("Triticum", "Populus")); + criteria.setGenus(newArrayList("Triticum", "Populus")); + criteria.setGenusSpecies(newArrayList("Triticum", "Populus")); + + criteria.setBiologicalStatus(newArrayList("Wild")); + + ESGenericQueryFactory<FaidareGermplasmPOSTShearchCriteria> queryFactory = + new ESGenericQueryFactory<>(); + + QueryBuilder query = queryFactory.createShouldFilterQuery(criteria); + + String actualQuery = query.toString(); + + String expectedQuery = readResource("expected/query8.json"); + assertJsonEquals(actualQuery, expectedQuery); + } + public static void assertJsonEquals(String json1, String json2) throws IOException { ObjectMapper mapper = new ObjectMapper(); diff --git a/backend/src/test/resources/fr/inra/urgi/faidare/elasticsearch/query/impl/expected/query8.json b/backend/src/test/resources/fr/inra/urgi/faidare/elasticsearch/query/impl/expected/query8.json new file mode 100644 index 00000000..46270871 --- /dev/null +++ b/backend/src/test/resources/fr/inra/urgi/faidare/elasticsearch/query/impl/expected/query8.json @@ -0,0 +1,89 @@ +{ + "bool" : { + "must" : [ + { + "bool" : { + "should" : [ + { + "terms" : { + "commonCropName" : [ + "Triticum", + "Populus" + ], + "boost" : 1.0 + } + }, + { + "terms" : { + "genusSpeciesSubtaxa" : [ + "Triticum", + "Populus" + ], + "boost" : 1.0 + } + }, + { + "terms" : { + "species" : [ + "Triticum", + "Populus" + ], + "boost" : 1.0 + } + }, + { + "terms" : { + "genus" : [ + "Triticum", + "Populus" + ], + "boost" : 1.0 + } + }, + { + "terms" : { + "subtaxa" : [ + "Triticum", + "Populus" + ], + "boost" : 1.0 + } + }, + { + "terms" : { + "taxonSynonyms" : [ + "Triticum", + "Populus" + ], + "boost" : 1.0 + } + }, + { + "terms" : { + "genusSpecies" : [ + "Triticum", + "Populus" + ], + "boost" : 1.0 + } + } + ], + "adjust_pure_negative" : true, + "boost" : 1.0 + } + } + ], + "filter" : [ + { + "terms" : { + "biologicalStatusOfAccessionCode" : [ + "Wild" + ], + "boost" : 1.0 + } + } + ], + "adjust_pure_negative" : true, + "boost" : 1.0 + } +} diff --git a/backend/src/test/test.iml b/backend/src/test/test.iml index 5ebc6f48..6e30bb1c 100644 --- a/backend/src/test/test.iml +++ b/backend/src/test/test.iml @@ -9,4 +9,4 @@ <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="module" module-name="main" /> </component> -</module> \ No newline at end of file +</module> diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html index 0a38353a..04de6c8f 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html @@ -107,3 +107,12 @@ </div> </ng-container> + + +<div *ngIf="germplasm.length == 0" + id="no-results" class="text-center"> + <div class="no-result-icon"> + <span class="fa fa-meh-o"></span> + </div> + No results. +</div> diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.scss b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.scss index bcaa4682..27258cfa 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.scss +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.scss @@ -18,6 +18,11 @@ border-bottom: 1px solid #c2c2c2; } +.no-result-icon { + font-size: 8rem; + color: $gray-300; +} + .bolder { font-size: 1.2rem; font-weight: bold; -- GitLab From 0c08d877bc441e86290fdbd58a890d141066d2be Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Fri, 14 Feb 2020 12:05:34 +0100 Subject: [PATCH 24/35] fix: Fix the two call sending to elasticSearch to get the result of the germplasm-result-page.component. Minor fixes GNP-4309 --- .../germplasm-result-page.component.html | 22 ++++++------- .../germplasm-result-page.component.spec.ts | 2 +- .../germplasm-result-page.component.ts | 6 ++-- .../app/result-page/result-page.component.ts | 31 ------------------- 4 files changed, 14 insertions(+), 47 deletions(-) diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html index 04de6c8f..aff65748 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html @@ -1,4 +1,4 @@ -<ng-container *ngIf="germplasm && germplasm.length>0"> +<ng-container *ngIf="germplasms && germplasms.length>0"> <div class="container mb-3"> <div class="row result align-content-center"> @@ -34,7 +34,7 @@ <faidare-card-section class="col-12 col-lg" header="Germplasm data: " - [test]="germplasm"> + [test]="germplasms"> <ng-template> <div class="table-responsive table-card-body"> <table class="table table-sm table-striped"> @@ -57,14 +57,14 @@ </tr> </thead> <tbody> - <ng-container *ngFor="let accession of germplasm"> + <ng-container *ngFor="let germplasm of germplasms"> <tr> - <td>{{ accession.germplasmName }}</td> - <td>{{ accession.accessionNumber }}</td> - <td>{{ accession.genusSpecies }}</td> - <td>{{ accession.instituteName }}</td> - <td>{{ accession.biologicalStatusOfAccessionCode }}</td> - <td>{{ accession.countryOfOriginCode }}</td> + <td>{{ germplasm.germplasmName }}</td> + <td>{{ germplasm.accessionNumber }}</td> + <td>{{ germplasm.genusSpecies }}</td> + <td>{{ germplasm.instituteName }}</td> + <td>{{ germplasm.biologicalStatusOfAccessionCode }}</td> + <td>{{ germplasm.countryOfOriginCode }}</td> </tr> </ng-container> </tbody> @@ -75,7 +75,7 @@ <div class="container text-right" style="margin-top: -30px" - *ngIf="germplasm"> + *ngIf="germplasms"> <div ngbDropdown class="dropdown-container"> <button class="btn btn-outline-secondary btn-sm" ngbDropdownToggle>Results per page : {{ pagination.pageSize }} @@ -109,7 +109,7 @@ </ng-container> -<div *ngIf="germplasm.length == 0" +<div *ngIf="germplasms.length == 0" id="no-results" class="text-center"> <div class="no-result-icon"> <span class="fa fa-meh-o"></span> diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts index b394b6ec..25f32971 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts @@ -280,7 +280,7 @@ describe('GermplasmResultPageComponent', () => { component.germplasmSearchCriteria$.next(criteria); expect(component.localCriteria).toEqual(criteria); expect(gnpisService.germplasmSearch).toHaveBeenCalledWith(criteria); - expect(component.germplasm).toEqual(germplasmSearchResult.result.data); + expect(component.germplasms).toEqual(germplasmSearchResult.result.data); }); diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts index 1772c98e..3afaf877 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts @@ -23,7 +23,7 @@ import { BehaviorSubject } from 'rxjs'; export class GermplasmResultPageComponent implements OnInit { - germplasm: Germplasm[]; + germplasms: Germplasm[]; localCriteria: GermplasmSearchCriteria = DataDiscoveryCriteriaUtils.emptyGermplasmSearchCriteria(); @Input() criteriaFromForm$: BehaviorSubject<DataDiscoveryCriteria>; @@ -90,7 +90,7 @@ export class GermplasmResultPageComponent implements OnInit { searchGermplasm(criteria: GermplasmSearchCriteria) { this.service.germplasmSearch(criteria) .subscribe(({ metadata, facets, result }) => { - this.germplasm = result.data; + this.germplasms = result.data; this.germplasmFacets$.next(facets); DataDiscoveryCriteriaUtils.updatePagination(this.pagination, metadata.pagination); }); @@ -120,8 +120,6 @@ export class GermplasmResultPageComponent implements OnInit { sources: asArray(criteria.sources) }; - this.germplasmSearchCriteria$.next(this.localCriteria); - } exportPlantMaterial(criteria: GermplasmSearchCriteria) { diff --git a/frontend/src/app/result-page/result-page.component.ts b/frontend/src/app/result-page/result-page.component.ts index bdda07dc..3d115942 100644 --- a/frontend/src/app/result-page/result-page.component.ts +++ b/frontend/src/app/result-page/result-page.component.ts @@ -65,16 +65,6 @@ export class ResultPageComponent implements OnInit { }); } - // TODO : delete because move to DataDiscoveryCriteriaUtils - /*private updatePagination({ currentPage, pageSize, totalCount, totalPages }) { - this.pagination.currentPage = currentPage; - this.pagination.pageSize = pageSize; - this.pagination.totalPages = totalPages; - this.pagination.startResult = pageSize * currentPage + 1; - this.pagination.endResult = this.pagination.startResult + pageSize - 1; - this.pagination.totalResult = totalCount; - }*/ - ngOnInit(): void { const queryParams = this.route.snapshot.queryParams; this.router.events.subscribe((event) => { @@ -90,22 +80,6 @@ export class ResultPageComponent implements OnInit { this.criteriaIsEmpty = DataDiscoveryCriteriaUtils.checkCriteriaIsEmpty(initialCriteria); - // TODO : delete because move to DataDiscoveryCriteriaUtils - /*this.criteria$.subscribe(criteria => { - this.criteriaIsEmpty = true; - for (const field of Object.keys(criteria)) { - if (field === 'facetFields') { - // Ignore facet fields criteria - continue; - } - - if (criteria[field] && criteria[field].length) { - this.criteriaIsEmpty = false; - break; - } - } - });*/ - this.form.traitWidgetInitialized.subscribe(() => { this.fetchDocumentsAndFacets(); }); @@ -130,11 +104,6 @@ export class ResultPageComponent implements OnInit { this.displayGermplasmResult$.next(this.displayGermplasmResult); }); - /*this.displayGermplasmResult$.subscribe(value => { - this.displayGermplasmResult = value; - }); - this.displayGermplasmResult$.next(this.displayGermplasmResult);*/ - this.germplasmfacets$.subscribe(facets => { this.germplasmfacets = facets; }); -- GitLab From c305c41f97be29283b1c9b306e54c2b260c19dc5 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Fri, 14 Feb 2020 16:06:56 +0100 Subject: [PATCH 25/35] fix: Factorize duplicate code that map the source name with there uri. Add the link to the germplasm card on the name of the germplasm in the germplasm-result-page table.GNP-4309 --- .../germplasm-result-page.component.html | 10 ++++-- frontend/src/app/gnpis.service.ts | 32 ++++++++----------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html index aff65748..b9a075fe 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html @@ -59,7 +59,13 @@ <tbody> <ng-container *ngFor="let germplasm of germplasms"> <tr> - <td>{{ germplasm.germplasmName }}</td> + <td> + <a [routerLink]="['/germplasm']" + [queryParams]="{id: germplasm.germplasmDbId}" + target="_blank"> + {{ germplasm.germplasmName }} + </a> + </td> <td>{{ germplasm.accessionNumber }}</td> <td>{{ germplasm.genusSpecies }}</td> <td>{{ germplasm.instituteName }}</td> @@ -109,7 +115,7 @@ </ng-container> -<div *ngIf="germplasms.length == 0" +<div *ngIf="germplasms && germplasms.length == 0" id="no-results" class="text-center"> <div class="no-result-icon"> <span class="fa fa-meh-o"></span> diff --git a/frontend/src/app/gnpis.service.ts b/frontend/src/app/gnpis.service.ts index f149bbca..aff0fac0 100644 --- a/frontend/src/app/gnpis.service.ts +++ b/frontend/src/app/gnpis.service.ts @@ -72,12 +72,17 @@ export class GnpisService { search( criteria: DataDiscoveryCriteria ): Observable<DataDiscoveryResults> { - return zip( + return this.mapSources( zip( // Get source by URI this.sourceByURI$, // Get documents by criteria this.http.post<any>(`${BASE_URL}/datadiscovery/search`, criteria) - ).pipe(map(([sourceByURI, response]) => { + )); + } + + + mapSources(httpResponse: Observable<any>) { + return httpResponse.pipe(map(([sourceByURI, response]) => { // Extract BrAPI documents from result const documents = response.result.data; @@ -92,8 +97,12 @@ export class GnpisService { } return response; })); + } + + + /** * Get germplasm by ID or PUI with data source (present in JSON-LD response) * @param params containing Id or PUI @@ -110,28 +119,13 @@ export class GnpisService { germplasmSearch(criteria: GermplasmCriteria): Observable<GermplasmResults<Germplasm>> { - return zip( + return this.mapSources(zip( // Get source by URI this.sourceByURI$, // Get documents by criteria this.http.post<GermplasmResults<Germplasm>>(`${BASE_URL}/germplasm/search`, criteria, - { headers: { 'Accept': 'application/ld+json,application/json' } })) - .pipe(map(([sourceByURI, response]) => { - // Extract BrAPI documents from result - const germplasm = response.result.data; - - // Transform document to have the source details in place of the source URI - response.result.data = germplasm.map(data => { - const sourceURI = data['schema:includedInDataCatalog']; - data['schema:includedInDataCatalog'] = sourceByURI[sourceURI]; - return data; - }); - if (response.facets) { - this.getSourcesName(sourceByURI, response); - } - return response; - })); + { headers: { 'Accept': 'application/ld+json,application/json' } }))); } /** -- GitLab From 326820b6a9196c3c6807f82eccd34e664344e8c9 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Mon, 24 Feb 2020 15:11:46 +0100 Subject: [PATCH 26/35] fix: Add taxonCommonNames in the germplasm search criteria.GNP-4309 --- .../FaidareGermplasmPOSTShearchCriteria.java | 14 ++++++++++++++ .../query/impl/ESGenericQueryFactory.java | 5 +++-- .../faidare/service/es/GermplasmServiceImpl.java | 2 +- .../germplasm-result-page.component.spec.ts | 1 + .../germplasm-result-page.component.ts | 1 + frontend/src/app/gnpis.service.spec.ts | 1 + frontend/src/app/models/data-discovery.model.ts | 1 + frontend/src/app/models/gnpis.model.ts | 1 + 8 files changed, 23 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/fr/inra/urgi/faidare/domain/criteria/FaidareGermplasmPOSTShearchCriteria.java b/backend/src/main/java/fr/inra/urgi/faidare/domain/criteria/FaidareGermplasmPOSTShearchCriteria.java index e697867a..5c46dc15 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/domain/criteria/FaidareGermplasmPOSTShearchCriteria.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/domain/criteria/FaidareGermplasmPOSTShearchCriteria.java @@ -8,6 +8,9 @@ import fr.inra.urgi.faidare.elasticsearch.criteria.annotation.NoDocumentMapping; import java.util.List; +/** + * @author jdestin + */ @CriteriaForDocument(GermplasmVO.class) public class FaidareGermplasmPOSTShearchCriteria extends GermplasmPOSTSearchCriteria implements SortCriteria { @@ -44,6 +47,9 @@ public class FaidareGermplasmPOSTShearchCriteria extends GermplasmPOSTSearchCrit @DocumentPath("taxonSynonyms") private List<String> taxonSynonyms; + @DocumentPath("taxonCommonNames") + private List<String> taxonCommonNames; + @DocumentPath(value = {"holdingInstitute", "organisation"}) private List<String> holdingInstitute; @@ -123,6 +129,14 @@ public class FaidareGermplasmPOSTShearchCriteria extends GermplasmPOSTSearchCrit public void setSynonyms(List<String> synonyms) { this.synonyms = synonyms; } + public List<String> getTaxonCommonNames() { + return taxonCommonNames; + } + + public void setTaxonCommonNames(List<String> taxonCommonNames) { + this.taxonCommonNames = taxonCommonNames; + } + public List<String> getHoldingInstitute() { return holdingInstitute; } diff --git a/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactory.java b/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactory.java index 55b96079..77ad147e 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactory.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/elasticsearch/query/impl/ESGenericQueryFactory.java @@ -22,7 +22,7 @@ import static org.elasticsearch.index.query.QueryBuilders.*; * Generic Elasticsearch query generator for criteria mapped on value object via * {@link DocumentPath} annotations * - * @author gcornut + * @author gcornut, jdestin */ public class ESGenericQueryFactory<C> implements ESQueryFactory<C> { @@ -373,9 +373,10 @@ public class ESGenericQueryFactory<C> implements ESQueryFactory<C> { } else if (queries.size() == 1) { return queries.get(0); } else { + // List of the criteria that will be use in should query part, the other criteria will be used as filter. List<String> shouldCriterion = Arrays.asList("commonCropName", "species", "germplasmGenus", "genusSpecies", "subtaxa", "genus", - "genusSpeciesSubtaxa", "taxonSynonyms", "panel", "collection", "population", + "genusSpeciesSubtaxa", "taxonSynonyms", "taxonCommonNames", "panel", "collection", "population", "germplasmName", "accessionNumber", "synonyms"); BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); BoolQueryBuilder boolShouldQueryBuilder = QueryBuilders.boolQuery(); diff --git a/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java b/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java index 2fdbfbb7..cc9c2e09 100644 --- a/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java +++ b/backend/src/main/java/fr/inra/urgi/faidare/service/es/GermplasmServiceImpl.java @@ -25,7 +25,7 @@ import java.util.Iterator; import java.util.List; /** - * @author cpommier, gcornut + * @author cpommier, gcornut, jdestin */ @Service("germplasmService") public class GermplasmServiceImpl implements GermplasmService { diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts index 25f32971..786c51b7 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.spec.ts @@ -214,6 +214,7 @@ describe('GermplasmResultPageComponent', () => { subtaxa: null, genusSpeciesSubtaxa: null, taxonSynonyms: null, + taxonCommonNames: null, biologicalStatus: null, geneticNature: null, holdingInstitute: null, diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts index 3afaf877..a3f56611 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts @@ -108,6 +108,7 @@ export class GermplasmResultPageComponent implements OnInit { subtaxa: asArray(criteria.crops), genusSpeciesSubtaxa: asArray(criteria.crops), taxonSynonyms: asArray(criteria.crops), + taxonCommonNames: asArray(criteria.crops), panel: asArray(criteria.germplasmLists), collection: asArray(criteria.germplasmLists), diff --git a/frontend/src/app/gnpis.service.spec.ts b/frontend/src/app/gnpis.service.spec.ts index 351301fc..1bf27758 100644 --- a/frontend/src/app/gnpis.service.spec.ts +++ b/frontend/src/app/gnpis.service.spec.ts @@ -148,6 +148,7 @@ describe('GnpisService', () => { subtaxa: null, genusSpeciesSubtaxa: null, taxonSynonyms: null, + taxonCommonNames: null, biologicalStatus: null, geneticNature: null, holdingInstitute: null, diff --git a/frontend/src/app/models/data-discovery.model.ts b/frontend/src/app/models/data-discovery.model.ts index 977bc3b3..2594e869 100644 --- a/frontend/src/app/models/data-discovery.model.ts +++ b/frontend/src/app/models/data-discovery.model.ts @@ -62,6 +62,7 @@ export class DataDiscoveryCriteriaUtils { subtaxa: null, genusSpeciesSubtaxa: null, taxonSynonyms: null, + taxonCommonNames: null, biologicalStatus: null, geneticNature: null, sources: null, diff --git a/frontend/src/app/models/gnpis.model.ts b/frontend/src/app/models/gnpis.model.ts index 1fdd633e..1aa84c9e 100644 --- a/frontend/src/app/models/gnpis.model.ts +++ b/frontend/src/app/models/gnpis.model.ts @@ -19,6 +19,7 @@ export interface GermplasmSearchCriteria { subtaxa: string[]; genusSpeciesSubtaxa: string[]; taxonSynonyms: string[]; + taxonCommonNames: string[]; biologicalStatus: string[]; geneticNature: string[]; holdingInstitute: string[]; -- GitLab From d010cb9dd2d3041a7b855b4b46747216ee554409 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Tue, 25 Feb 2020 15:59:41 +0100 Subject: [PATCH 27/35] fix: Make the question mark clickable, hide the Germplasm List search box and use the facet sources generate by the germplasm search web service when displaying the germplasm result page. Upgrade the tslib version GNP-4309 --- frontend/package-lock.json | 6 +++--- frontend/package.json | 2 +- .../small-facets/small-facets.component.html | 17 ++++++++++++----- .../small-facets/small-facets.component.ts | 18 ++++++++++++------ frontend/src/app/form/form.component.html | 3 ++- frontend/src/app/form/form.component.spec.ts | 3 +++ frontend/src/app/form/form.component.ts | 8 ++++++-- .../germplasm-result-page.component.ts | 2 +- .../src/app/models/data-discovery.model.ts | 4 ++-- .../app/result-page/result-page.component.html | 1 - 10 files changed, 42 insertions(+), 22 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 23ee3c4c..3f2c731b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10064,9 +10064,9 @@ } }, "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.0.tgz", + "integrity": "sha512-BmndXUtiTn/VDDrJzQE7Mm22Ix3PxgLltW9bSNLoeCY31gnG2OPx0QqJnuc9oMIKioYrz487i6K9o4Pdn0j+Kg==" }, "tslint": { "version": "5.11.0", diff --git a/frontend/package.json b/frontend/package.json index e7e0486b..4d1de75f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -39,7 +39,7 @@ "popper.js": "1.14.6", "rxjs": "6.4.0", "trait-ontology-widget": "git+https://github.com/gnpis/trait-ontology-widget.git#v2.2.1", - "tslib": "1.9.3", + "tslib": "1.11.0", "zone.js": "0.8.29" }, "devDependencies": { diff --git a/frontend/src/app/facets/small-facets/small-facets.component.html b/frontend/src/app/facets/small-facets/small-facets.component.html index f3a4b568..714cd699 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.html +++ b/frontend/src/app/facets/small-facets/small-facets.component.html @@ -1,16 +1,16 @@ <ng-template #germplasmDetailsPopup> <div class="card ngb-popover-window "> <br> - Activation of the button allows to view more information about the germplasm.<br> - You can also download the germplasm list. + This is a button to view more information about the germplasm that are return by your search.<br> + You can also download the results in standard format. </div> </ng-template> - <div class="card mb-1" *ngIf="facet.terms.length && facet.terms.length <= 8"> <div class="card-body"> - <h3 class="card-title">{{ formatFacets[facet.field] ? (formatFacets[ facet.field] | titlecase) : facet.field | titlecase }}</h3> + <h3 + class="card-title">{{ formatFacets[facet.field] ? (formatFacets[facet.field] | titlecase) : facet.field | titlecase }}</h3> <form [formGroup]="checkBoxes" class="card-text"> <div class="form-check" *ngFor="let term of facet.terms"> @@ -37,9 +37,16 @@ (change)="switchToGermplasmResult()"> <span class="slider round"></span> </label> + <a class="btn popovers" data-boundary="window" placement="auto" + [autoClose]="'outside'" + [ngbPopover]="germplasmDetailsPopup" + [popoverTitle]="'Details button\'s help.'" container="body"> + <img src="assets/faidare/images/question-mark.png" alt="help" - title="This is a button to view more information about the germplasm that are return by your search. You can also download the results in standard format." + title="This is a button to view more information about the germplasm that are return by your search. + You can also download the results in standard format." height="20px" style="margin-top: -10px; margin-left: 10px"/> + </a> </div> </div> </form> diff --git a/frontend/src/app/facets/small-facets/small-facets.component.ts b/frontend/src/app/facets/small-facets/small-facets.component.ts index 305a77e7..d3048945 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.ts +++ b/frontend/src/app/facets/small-facets/small-facets.component.ts @@ -121,12 +121,18 @@ export class SmallFacetsComponent implements OnInit { } switchToGermplasmResult() { - - /*for (const [key, control] of Object.entries(this.checkBoxes.controls)) { - if (key === 'selectSwitchButton') { - control.setValue(currentState, { emitEvent: false }); - } - }*/ + if (!this.displayGermplasmCurrentState) { + this.localCriteria = { + ... this.localCriteria, + facetFields: ['types'] + }; + } else { + this.localCriteria = { + ... this.localCriteria, + facetFields: ['types', 'sources'] + }; + } + this.criteria$.next(this.localCriteria); this.displayGermplasmResult$.next(!this.displayGermplasmCurrentState); } diff --git a/frontend/src/app/form/form.component.html b/frontend/src/app/form/form.component.html index 8f8de923..bb81b123 100644 --- a/frontend/src/app/form/form.component.html +++ b/frontend/src/app/form/form.component.html @@ -36,7 +36,8 @@ </div> </div> <!-- Input for the germplasmList field --> - <div class="form-group row"> + <div class="form-group row" + *ngIf="!displayGermplasmResult"> <label for="germplasmList" class="col-sm-4"> <span>Germplasm list</span> <small class="small text-muted"> diff --git a/frontend/src/app/form/form.component.spec.ts b/frontend/src/app/form/form.component.spec.ts index b6ef6e07..1a950eed 100644 --- a/frontend/src/app/form/form.component.spec.ts +++ b/frontend/src/app/form/form.component.spec.ts @@ -4,6 +4,7 @@ import { FormComponent } from './form.component'; import { MockComponents } from 'ng-mocks'; import { SuggestionFieldComponent } from './suggestion-field/suggestion-field.component'; import { TraitOntologyWidgetComponent } from './trait-ontology-widget/trait-ontology-widget.component'; +import { BehaviorSubject } from 'rxjs'; describe('FormComponent', () => { @@ -18,6 +19,8 @@ describe('FormComponent', () => { it('should switch tabs', async(() => { const fixture = TestBed.createComponent(FormComponent); + const component = fixture.componentInstance; + component.displayGermplasmResult$ = new BehaviorSubject(false); fixture.detectChanges(); const element: HTMLElement = fixture.nativeElement; diff --git a/frontend/src/app/form/form.component.ts b/frontend/src/app/form/form.component.ts index 9ec5f90a..36c0f5de 100644 --- a/frontend/src/app/form/form.component.ts +++ b/frontend/src/app/form/form.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { DataDiscoveryCriteria } from '../models/data-discovery.model'; import { BehaviorSubject } from 'rxjs'; @@ -12,7 +12,7 @@ enum Tabs { templateUrl: './form.component.html', styleUrls: ['./form.component.scss'] }) -export class FormComponent { +export class FormComponent implements OnInit { @Input() criteria$: BehaviorSubject<DataDiscoveryCriteria>; @Input() displayGermplasmResult$: BehaviorSubject<boolean>; @Output() traitWidgetInitialized = new EventEmitter(); @@ -22,6 +22,7 @@ export class FormComponent { // to give access in HTML template tabs = Tabs; + displayGermplasmResult: boolean; getNavClass(tab: Tabs) { return this.activeTab === tab ? 'active' : ''; @@ -30,4 +31,7 @@ export class FormComponent { getTabClass(tab: Tabs) { return this.activeTab === tab ? 'visible' : 'd-none'; } + ngOnInit(): void { + this.displayGermplasmResult$.subscribe(displayStatus => this.displayGermplasmResult = displayStatus); + } } diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts index a3f56611..5d5b017c 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts @@ -72,7 +72,7 @@ export class GermplasmResultPageComponent implements OnInit { ngOnInit() { const queryParams = this.route.snapshot.queryParams; - this.reassignCriteriaFieldFromDataDiscoveryFields(queryParams); + // this.reassignCriteriaFieldFromDataDiscoveryFields(queryParams); this.criteriaFromForm$.subscribe(criteria => { this.reassignCriteriaFieldFromDataDiscoveryFields(criteria); diff --git a/frontend/src/app/models/data-discovery.model.ts b/frontend/src/app/models/data-discovery.model.ts index 2594e869..509d54c3 100644 --- a/frontend/src/app/models/data-discovery.model.ts +++ b/frontend/src/app/models/data-discovery.model.ts @@ -31,7 +31,7 @@ export class DataDiscoveryCriteriaUtils { return { accessions: null, crops: null, - facetFields: ['sources', 'types'], + facetFields: ['types', 'sources'], germplasmLists: null, observationVariableIds: null, topSelectedTraitOntologyIds: null, @@ -68,7 +68,7 @@ export class DataDiscoveryCriteriaUtils { sources: null, types: 'Germplasm', - facetFields: ['holdingInstitute', + facetFields: ['sources', 'holdingInstitute', 'biologicalStatus', 'geneticNature', 'country'], sortBy: null, sortOrder: null, diff --git a/frontend/src/app/result-page/result-page.component.html b/frontend/src/app/result-page/result-page.component.html index 13f98721..ffa36b5c 100644 --- a/frontend/src/app/result-page/result-page.component.html +++ b/frontend/src/app/result-page/result-page.component.html @@ -13,7 +13,6 @@ <faidare-facets *ngIf="displayGermplasmResult && germplasmfacets.length" - [criteria$]="criteria$" [facets]="germplasmfacets" [displayGermplasmResult$]="displayGermplasmResult$" [germplasmSearchCriteria$]="germplasmSearchCriteria$"> -- GitLab From 65e576f7724bfb9429493c6ae49842ac5207fb52 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Mon, 2 Mar 2020 11:17:29 +0100 Subject: [PATCH 28/35] fix: Extract the germplasm switch button in a component and add it to large-facets-component. Minor fixes. GNP-4309 --- frontend/src/app/app.module.ts | 4 +- frontend/src/app/facets/facets.component.html | 3 +- .../large-facets/large-facets.component.html | 16 ++++- .../large-facets/large-facets.component.ts | 50 +++++++++++-- .../small-facets/small-facets.component.html | 33 +++------ .../small-facets/small-facets.component.ts | 10 ++- .../switch-button.component.html | 33 +++++++++ .../switch-button.component.scss | 71 +++++++++++++++++++ .../switch-button.component.spec.ts | 25 +++++++ .../switch-button/switch-button.component.ts | 51 +++++++++++++ .../src/app/models/data-discovery.model.ts | 2 +- frontend/src/app/models/gnpis.model.ts | 2 +- .../app/result-page/result-page.component.ts | 5 ++ 13 files changed, 260 insertions(+), 45 deletions(-) create mode 100644 frontend/src/app/facets/switch-button/switch-button.component.html create mode 100644 frontend/src/app/facets/switch-button/switch-button.component.scss create mode 100644 frontend/src/app/facets/switch-button/switch-button.component.spec.ts create mode 100644 frontend/src/app/facets/switch-button/switch-button.component.ts diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index f10c08e8..ba96c6c9 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -42,6 +42,7 @@ import { MarkdownPageComponent } from './markdown-page/markdown-page.component'; import { DecimalPipe } from '@angular/common'; import { LargeFacetsComponent } from './facets/large-facets/large-facets.component'; import { SmallFacetsComponent } from './facets/small-facets/small-facets.component'; +import { SwitchButtonComponent } from './facets/switch-button/switch-button.component'; @NgModule({ declarations: [ @@ -68,7 +69,8 @@ import { SmallFacetsComponent } from './facets/small-facets/small-facets.compone CardGenericDocumentComponent, MarkdownPageComponent, LargeFacetsComponent, - SmallFacetsComponent + SmallFacetsComponent, + SwitchButtonComponent ], imports: [ BrowserModule, diff --git a/frontend/src/app/facets/facets.component.html b/frontend/src/app/facets/facets.component.html index 4b7d99ff..4e039482 100644 --- a/frontend/src/app/facets/facets.component.html +++ b/frontend/src/app/facets/facets.component.html @@ -17,6 +17,7 @@ *ngFor="let facet of facets" [criteria$]="criteria$" [facet]="facet" - [germplasmSearchCriteria$]="germplasmSearchCriteria$"> + [germplasmSearchCriteria$]="germplasmSearchCriteria$" + [displayGermplasmResult$]="displayGermplasmResult$"> </faidare-large-facets> </div> diff --git a/frontend/src/app/facets/large-facets/large-facets.component.html b/frontend/src/app/facets/large-facets/large-facets.component.html index 3424c035..20eb8c83 100644 --- a/frontend/src/app/facets/large-facets/large-facets.component.html +++ b/frontend/src/app/facets/large-facets/large-facets.component.html @@ -15,14 +15,15 @@ <div class="card mb-1"> <div class="card-body"> - <h3 class="card-title">{{ formatFacets[facet.field] ? (formatFacets[ facet.field] | titlecase) : facet.field | titlecase }}</h3> + <h3 + class="card-title">{{ formatFacets[facet.field] ? (formatFacets[facet.field] | titlecase) : facet.field | titlecase }}</h3> <div class="mb-2"> <span class="badge badge-pill badge-secondary mr-1 selectedElem" style="font-size: smaller" - *ngFor="let term of selectedTerms[facet.field]" tabindex="0" + *ngFor="let term of selectedTerms[facet.field]" (keydown.delete)="removeKey(term)" - (keydown.backspace)="removeKey(term)"> {{ displaySourceName(term) }} + (keydown.backspace)="removeKey(term)"> {{ displaySourceName(term) }} <button tabindex="-1" type="button" class="btn btn-link" (click)="removeKey(term)">×</button> </span> @@ -35,6 +36,15 @@ placeholder="Filter on {{ facet.field.toLowerCase() }}..." (focus)="focus$.next($event.target.value)"/> </div> + + <faidare-switch-button + style="margin-top: -10px; margin-left: 25px" + [criteria$]=criteria$ + [displayGermplasmResult$]="displayGermplasmResult$" + [facetFiled]="facet.field" + [switchTitle]="'Germplasm details'"> + </faidare-switch-button> + </div> </ng-container> diff --git a/frontend/src/app/facets/large-facets/large-facets.component.ts b/frontend/src/app/facets/large-facets/large-facets.component.ts index be9693f0..9eea3946 100644 --- a/frontend/src/app/facets/large-facets/large-facets.component.ts +++ b/frontend/src/app/facets/large-facets/large-facets.component.ts @@ -1,6 +1,7 @@ import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; import { DataDiscoveryCriteria, + DataDiscoveryCriteriaUtils, DataDiscoveryFacet, DataDiscoverySource } from '../../models/data-discovery.model'; @@ -29,6 +30,7 @@ export class LargeFacetsComponent implements OnInit { @Input() facet: DataDiscoveryFacet; @Input() criteria$: BehaviorSubject<DataDiscoveryCriteria>; @Input() germplasmSearchCriteria$: BehaviorSubject<GermplasmSearchCriteria>; + @Input() displayGermplasmResult$: BehaviorSubject<boolean>; @ViewChild('typeahead') typeahead: ElementRef<HTMLInputElement>; formatFacets = formatFacets; @@ -39,6 +41,8 @@ export class LargeFacetsComponent implements OnInit { selectedTerms: { [key: string]: string[]; } = {}; criterion = new FormControl(''); sources: DataDiscoverySource[]; + criteriaIsEmpty = true; + displayGermplasmCurrentState = false; constructor(private gnpisService: GnpisService) { } @@ -49,19 +53,23 @@ export class LargeFacetsComponent implements OnInit { this.sources = sources; }); + this.displayGermplasmResult$.subscribe(status => this.displayGermplasmCurrentState = status); + if (this.criteria$) { this.criteria$.pipe(filter(c => c !== this.localCriteria)) .subscribe(criteria => { this.localCriteria = { ...criteria }; this.selectedTerms[this.facet.field] = criteria[this.facet.field] || []; + this.criteriaIsEmpty = DataDiscoveryCriteriaUtils.checkCriteriaIsEmpty(criteria); }); } - if (this.germplasmSearchCriteria$) { + if (this.germplasmSearchCriteria$ && this.displayGermplasmCurrentState) { this.germplasmSearchCriteria$.pipe(filter(c => c !== this.germplasmLocalCriteria)) .subscribe(germplasmCriteria => { this.germplasmLocalCriteria = germplasmCriteria; this.selectedTerms[this.facet.field] = germplasmCriteria[this.facet.field] || []; + this.criteriaIsEmpty = DataDiscoveryCriteriaUtils.checkCriteriaIsEmpty(germplasmCriteria); }); } } @@ -110,14 +118,24 @@ export class LargeFacetsComponent implements OnInit { if (selected !== 'REFINE') { // the item field of the event contains the facet term // we push the selected key to our collection of keys - if (this.criteria$) { + if (this.criteria$ && !this.displayGermplasmCurrentState) { if (this.localCriteria[this.facet.field]) { this.localCriteria[this.facet.field].push(event.item.term); } else { this.localCriteria[this.facet.field] = [event.item.term]; } } - if (this.germplasmSearchCriteria$) { + if (this.germplasmSearchCriteria$ && this.displayGermplasmCurrentState) { + + if (event.item.term !== 'Germplasm' && this.facet.field === 'types') { + if (this.localCriteria[this.facet.field]) { + console.log('here'); + this.localCriteria[this.facet.field].push(event.item.term); + } else { + this.localCriteria[this.facet.field] = [event.item.term]; + } + this.switchGermplasmResult(); + } if (!this.germplasmLocalCriteria[this.facet.field]) { this.germplasmLocalCriteria[this.facet.field] = [event.item.term]; } else { @@ -130,10 +148,10 @@ export class LargeFacetsComponent implements OnInit { } emitChanges() { - if (this.criteria$) { + if (this.criteria$ && !this.displayGermplasmCurrentState) { this.criteria$.next(this.localCriteria); } - if (this.germplasmSearchCriteria$) { + if (this.germplasmSearchCriteria$ && this.displayGermplasmCurrentState) { this.germplasmSearchCriteria$.next(this.germplasmLocalCriteria); } } @@ -141,11 +159,14 @@ export class LargeFacetsComponent implements OnInit { removeKey(key: string) { this.selectedTerms[this.facet.field] = this.removeFromList(this.selectedTerms[this.facet.field], key); - if (this.criteria$) { + if (this.criteria$ && !this.displayGermplasmCurrentState) { this.localCriteria[this.facet.field] = this.removeFromList(this.localCriteria[this.facet.field], key); } - if (this.germplasmSearchCriteria$) { + if (this.germplasmSearchCriteria$ && this.displayGermplasmCurrentState) { + if (key === 'Germplasm') { + this.switchGermplasmResult(); + } this.germplasmLocalCriteria[this.facet.field] = this.removeFromList(this.germplasmLocalCriteria[this.facet.field], key); } @@ -159,4 +180,19 @@ export class LargeFacetsComponent implements OnInit { ...list.slice(index + 1)]; } + switchGermplasmResult() { + if (!this.displayGermplasmCurrentState) { + this.localCriteria = { + ...this.localCriteria, + facetFields: ['types'] + }; + } else { + this.localCriteria = { + ...this.localCriteria, + facetFields: ['types', 'sources'] + }; + } + this.criteria$.next(this.localCriteria); + this.displayGermplasmResult$.next(!this.displayGermplasmCurrentState); + } } diff --git a/frontend/src/app/facets/small-facets/small-facets.component.html b/frontend/src/app/facets/small-facets/small-facets.component.html index 714cd699..62dc5c08 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.html +++ b/frontend/src/app/facets/small-facets/small-facets.component.html @@ -23,31 +23,14 @@ {{ term.label }} ({{ term.count | number }}) </label> - <div id="germplasmSearch" - title="" - style="margin-top: 5px" - *ngIf="term.term == 'Germplasm' && !criteriaIsEmpty"> - <label for="selectSwitchButton" style="margin-right: 5px" - id="switchTitle"> - Details - </label> - <label class="switch" id="switchButton"> - <input type="checkbox" id="selectSwitchButton" - [checked]="displayGermplasmCurrentState" - (change)="switchToGermplasmResult()"> - <span class="slider round"></span> - </label> - <a class="btn popovers" data-boundary="window" placement="auto" - [autoClose]="'outside'" - [ngbPopover]="germplasmDetailsPopup" - [popoverTitle]="'Details button\'s help.'" container="body"> - - <img src="assets/faidare/images/question-mark.png" alt="help" - title="This is a button to view more information about the germplasm that are return by your search. - You can also download the results in standard format." - height="20px" style="margin-top: -10px; margin-left: 10px"/> - </a> - </div> + <faidare-switch-button + style="margin-top: 5px" + *ngIf="term.term == 'Germplasm'" + [criteria$]=criteria$ + [displayGermplasmResult$]="displayGermplasmResult$" + [facetFiled]="facet.field" + [switchTitle]="'Details'"> + </faidare-switch-button> </div> </form> </div> diff --git a/frontend/src/app/facets/small-facets/small-facets.component.ts b/frontend/src/app/facets/small-facets/small-facets.component.ts index d3048945..44197c30 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.ts +++ b/frontend/src/app/facets/small-facets/small-facets.component.ts @@ -80,7 +80,7 @@ export class SmallFacetsComponent implements OnInit { const unselectGermplasm = Object.keys(values).filter(key => key === 'Germplasm' && !values[key]); if ((multiSelection.length > 0 && this.facet.field === 'types') || unselectGermplasm.length > 0) { - this.displayGermplasmResult$.next(false); + this.switchGermplasmResult(); } this.showAndHideAdvanceGermplasmSearch(selectedTerms); @@ -109,8 +109,6 @@ export class SmallFacetsComponent implements OnInit { control.setValue(isSelected, { emitEvent: false }); } - // this.queryParams = this.queryParamsForGermplasmPage(criteria); - this.criteriaIsEmpty = DataDiscoveryCriteriaUtils.checkCriteriaIsEmpty(criteria); } @@ -120,15 +118,15 @@ export class SmallFacetsComponent implements OnInit { this.displayAdvanceGermplasmSearchButton = facetIsTypes && GermplasmSelected; } - switchToGermplasmResult() { + switchGermplasmResult() { if (!this.displayGermplasmCurrentState) { this.localCriteria = { - ... this.localCriteria, + ...this.localCriteria, facetFields: ['types'] }; } else { this.localCriteria = { - ... this.localCriteria, + ...this.localCriteria, facetFields: ['types', 'sources'] }; } diff --git a/frontend/src/app/facets/switch-button/switch-button.component.html b/frontend/src/app/facets/switch-button/switch-button.component.html new file mode 100644 index 00000000..b9071217 --- /dev/null +++ b/frontend/src/app/facets/switch-button/switch-button.component.html @@ -0,0 +1,33 @@ +<div id="germplasmSearch" + title="" + *ngIf="facetFiled == 'types'"> + <label for="selectSwitchButton" style="margin-right: 5px" + id="switchTitle"> + {{ switchTitle }} + </label> + <label class="switch" id="switchButton"> + <input type="checkbox" id="selectSwitchButton" + [checked]="displayGermplasmCurrentState" + (change)="switchGermplasmResult()"> + <span class="slider round"></span> + </label> + <a class="btn popovers" data-boundary="window" placement="auto" + [autoClose]="'outside'" + [ngbPopover]="germplasmDetailsPopup" + [popoverTitle]="'Details button\'s help.'" container="body"> + + <img src="assets/faidare/images/question-mark.png" alt="help" + title="This is a button to view more information about the germplasm that are return by your search. + You can also download the results in standard format." + height="20px" style="margin-top: -10px; margin-left: 10px"/> + </a> +</div> + + +<ng-template #germplasmDetailsPopup> + <div class="card ngb-popover-window "> + <br> + This is a button to view more information about the germplasm that are return by your search.<br> + You can also download the results in standard format. + </div> +</ng-template> diff --git a/frontend/src/app/facets/switch-button/switch-button.component.scss b/frontend/src/app/facets/switch-button/switch-button.component.scss new file mode 100644 index 00000000..9e31a5a9 --- /dev/null +++ b/frontend/src/app/facets/switch-button/switch-button.component.scss @@ -0,0 +1,71 @@ + +.popovers{ + cursor: pointer; +} + +#switchTitle { + cursor: pointer; +} + +/* The switch - the box around the slider */ + +.switch { + position: relative; + display: inline-block; + width: 30px; + height: 17px; +} + +/* Hide default HTML checkbox */ +.switch input { + opacity: 0; + width: 0; + height: 0; +} +/* The slider */ +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: .4s; + transition: .4s; +} + +.slider:before { + position: absolute; + content: ""; + height: 13px; + width: 13px; + left: 2px; + bottom: 2px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; +} + +input:checked + .slider { + background-color: #2196F3; +} + +input:focus + .slider { + box-shadow: 0 0 1px #2196F3; +} + +input:checked + .slider:before { + -webkit-transform: translateX(15px); + -ms-transform: translateX(15px); + transform: translateX(15px); +} + +/* Rounded sliders */ +.slider.round { + border-radius: 17px; +} + +.slider.round:before { + border-radius: 50%; +} diff --git a/frontend/src/app/facets/switch-button/switch-button.component.spec.ts b/frontend/src/app/facets/switch-button/switch-button.component.spec.ts new file mode 100644 index 00000000..d751b94f --- /dev/null +++ b/frontend/src/app/facets/switch-button/switch-button.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SwitchButtonComponent } from './switch-button.component'; + +describe('SwitchButtonComponent', () => { + let component: SwitchButtonComponent; + let fixture: ComponentFixture<SwitchButtonComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SwitchButtonComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SwitchButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/facets/switch-button/switch-button.component.ts b/frontend/src/app/facets/switch-button/switch-button.component.ts new file mode 100644 index 00000000..1d628ced --- /dev/null +++ b/frontend/src/app/facets/switch-button/switch-button.component.ts @@ -0,0 +1,51 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { + DataDiscoveryCriteria, + DataDiscoverySource +} from '../../models/data-discovery.model'; +import { BehaviorSubject } from 'rxjs'; + +@Component({ + selector: 'faidare-switch-button', + templateUrl: './switch-button.component.html', + styleUrls: ['./switch-button.component.scss'] +}) +export class SwitchButtonComponent implements OnInit { + + + @Input() criteria$: BehaviorSubject<DataDiscoveryCriteria>; + @Input() displayGermplasmResult$: BehaviorSubject<boolean>; + @Input() facetFiled: string; + @Input() switchTitle: string; + + localCriteria: DataDiscoveryCriteria; + sources: DataDiscoverySource[]; + criteriaIsEmpty = true; + displayGermplasmCurrentState = false; + + constructor() { + } + + ngOnInit() { + + this.displayGermplasmResult$.subscribe(value => { + this.displayGermplasmCurrentState = value; + }); + } + + switchGermplasmResult() { + if (!this.displayGermplasmCurrentState) { + this.localCriteria = { + ...this.localCriteria, + facetFields: ['types'] + }; + } else { + this.localCriteria = { + ...this.localCriteria, + facetFields: ['types', 'sources'] + }; + } + this.criteria$.next(this.localCriteria); + this.displayGermplasmResult$.next(!this.displayGermplasmCurrentState); + } +} diff --git a/frontend/src/app/models/data-discovery.model.ts b/frontend/src/app/models/data-discovery.model.ts index 509d54c3..63958fcc 100644 --- a/frontend/src/app/models/data-discovery.model.ts +++ b/frontend/src/app/models/data-discovery.model.ts @@ -66,7 +66,7 @@ export class DataDiscoveryCriteriaUtils { biologicalStatus: null, geneticNature: null, sources: null, - types: 'Germplasm', + types: ['Germplasm'], facetFields: ['sources', 'holdingInstitute', 'biologicalStatus', 'geneticNature', 'country'], diff --git a/frontend/src/app/models/gnpis.model.ts b/frontend/src/app/models/gnpis.model.ts index 1aa84c9e..93760346 100644 --- a/frontend/src/app/models/gnpis.model.ts +++ b/frontend/src/app/models/gnpis.model.ts @@ -24,7 +24,7 @@ export interface GermplasmSearchCriteria { geneticNature: string[]; holdingInstitute: string[]; sources: string[]; - types: string; + types: string[]; facetFields: string[]; sortBy: string; diff --git a/frontend/src/app/result-page/result-page.component.ts b/frontend/src/app/result-page/result-page.component.ts index 3d115942..86624b43 100644 --- a/frontend/src/app/result-page/result-page.component.ts +++ b/frontend/src/app/result-page/result-page.component.ts @@ -84,6 +84,11 @@ export class ResultPageComponent implements OnInit { this.fetchDocumentsAndFacets(); }); + this.displayGermplasmResult$.subscribe(value => { + this.displayGermplasmResult = value; + }); + this.displayGermplasmResult$.next(this.displayGermplasmResult); + this.criteria$ .pipe(filter(c => c !== initialCriteria)) .subscribe(newCriteria => { -- GitLab From 1ae751b304f9976f0cbd4c126fb68edb60586eeb Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Mon, 2 Mar 2020 18:35:01 +0100 Subject: [PATCH 29/35] fix: Fix test and bugs. Minor fixes. GNP-4309 --- .../large-facets/large-facets.component.html | 5 ++- .../large-facets.component.spec.ts | 12 +++-- .../large-facets/large-facets.component.ts | 24 +++++----- .../small-facets/small-facets.component.html | 2 +- .../small-facets.component.spec.ts | 3 +- .../small-facets/small-facets.component.ts | 44 +++++++------------ .../switch-button.component.html | 2 +- .../switch-button.component.spec.ts | 40 ++++++++++------- .../switch-button/switch-button.component.ts | 16 +++++-- frontend/src/app/gnpis.service.spec.ts | 2 +- .../app/result-page/result-page.component.ts | 4 -- 11 files changed, 82 insertions(+), 72 deletions(-) diff --git a/frontend/src/app/facets/large-facets/large-facets.component.html b/frontend/src/app/facets/large-facets/large-facets.component.html index 20eb8c83..0156498b 100644 --- a/frontend/src/app/facets/large-facets/large-facets.component.html +++ b/frontend/src/app/facets/large-facets/large-facets.component.html @@ -11,7 +11,7 @@ </ng-template> </ng-template> -<ng-container *ngIf="facet.terms.length && facet.terms.length > 8"> +<ng-container *ngIf="facet.terms.length && facet.terms.length >8"> <div class="card mb-1"> <div class="card-body"> @@ -23,7 +23,7 @@ style="font-size: smaller" *ngFor="let term of selectedTerms[facet.field]" (keydown.delete)="removeKey(term)" - (keydown.backspace)="removeKey(term)"> {{ displaySourceName(term) }} + (keydown.backspace)="removeKey(term)"> {{ displaySourceName(term) }} <button tabindex="-1" type="button" class="btn btn-link" (click)="removeKey(term)">×</button> </span> @@ -38,6 +38,7 @@ </div> <faidare-switch-button + *ngIf="!criteriaIsEmpty" style="margin-top: -10px; margin-left: 25px" [criteria$]=criteria$ [displayGermplasmResult$]="displayGermplasmResult$" diff --git a/frontend/src/app/facets/large-facets/large-facets.component.spec.ts b/frontend/src/app/facets/large-facets/large-facets.component.spec.ts index 032b1273..8529e56c 100644 --- a/frontend/src/app/facets/large-facets/large-facets.component.spec.ts +++ b/frontend/src/app/facets/large-facets/large-facets.component.spec.ts @@ -5,12 +5,16 @@ import { DataDiscoveryCriteriaUtils, DataDiscoveryFacet } from '../../models/data-discovery.model'; -import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap'; +import { + NgbPopoverModule, + NgbTypeaheadModule +} from '@ng-bootstrap/ng-bootstrap'; import { ReactiveFormsModule } from '@angular/forms'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { BehaviorSubject } from 'rxjs'; import { GermplasmSearchCriteria } from '../../models/gnpis.model'; import { ComponentTester } from 'ngx-speculoos'; +import { SwitchButtonComponent } from '../switch-button/switch-button.component'; describe('LargeFacetsComponent', () => { @@ -84,8 +88,8 @@ describe('LargeFacetsComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [NgbTypeaheadModule, ReactiveFormsModule, HttpClientTestingModule ], - declarations: [LargeFacetsComponent] + imports: [NgbTypeaheadModule, ReactiveFormsModule, HttpClientTestingModule, NgbPopoverModule], + declarations: [LargeFacetsComponent, SwitchButtonComponent] }) .compileComponents(); @@ -95,6 +99,7 @@ describe('LargeFacetsComponent', () => { component.criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(DataDiscoveryCriteriaUtils.emptyCriteria()); component.germplasmSearchCriteria$ = new BehaviorSubject<GermplasmSearchCriteria>(DataDiscoveryCriteriaUtils .emptyGermplasmSearchCriteria()); + component.displayGermplasmResult$ = new BehaviorSubject<boolean>(false); component.facet = largeFacet; fixture.detectChanges(); })); @@ -106,6 +111,7 @@ describe('LargeFacetsComponent', () => { it ('should display search box', () => { const tester = new LargeFacetsComponentTester(); component = tester.componentInstance; + component.displayGermplasmResult$ = new BehaviorSubject<boolean>(false); component.facet = largeFacet; tester.detectChanges(); diff --git a/frontend/src/app/facets/large-facets/large-facets.component.ts b/frontend/src/app/facets/large-facets/large-facets.component.ts index 9eea3946..1cfbf0b0 100644 --- a/frontend/src/app/facets/large-facets/large-facets.component.ts +++ b/frontend/src/app/facets/large-facets/large-facets.component.ts @@ -42,7 +42,7 @@ export class LargeFacetsComponent implements OnInit { criterion = new FormControl(''); sources: DataDiscoverySource[]; criteriaIsEmpty = true; - displayGermplasmCurrentState = false; + germplasmDisplayCurrentState = false; constructor(private gnpisService: GnpisService) { } @@ -53,7 +53,9 @@ export class LargeFacetsComponent implements OnInit { this.sources = sources; }); - this.displayGermplasmResult$.subscribe(status => this.displayGermplasmCurrentState = status); + this.displayGermplasmResult$.subscribe(status => { + this.germplasmDisplayCurrentState = status; + }); if (this.criteria$) { this.criteria$.pipe(filter(c => c !== this.localCriteria)) @@ -64,7 +66,7 @@ export class LargeFacetsComponent implements OnInit { }); } - if (this.germplasmSearchCriteria$ && this.displayGermplasmCurrentState) { + if (this.germplasmSearchCriteria$ && this.germplasmDisplayCurrentState) { this.germplasmSearchCriteria$.pipe(filter(c => c !== this.germplasmLocalCriteria)) .subscribe(germplasmCriteria => { this.germplasmLocalCriteria = germplasmCriteria; @@ -118,14 +120,14 @@ export class LargeFacetsComponent implements OnInit { if (selected !== 'REFINE') { // the item field of the event contains the facet term // we push the selected key to our collection of keys - if (this.criteria$ && !this.displayGermplasmCurrentState) { + if (this.criteria$) { if (this.localCriteria[this.facet.field]) { this.localCriteria[this.facet.field].push(event.item.term); } else { this.localCriteria[this.facet.field] = [event.item.term]; } } - if (this.germplasmSearchCriteria$ && this.displayGermplasmCurrentState) { + if (this.germplasmSearchCriteria$ && this.germplasmDisplayCurrentState) { if (event.item.term !== 'Germplasm' && this.facet.field === 'types') { if (this.localCriteria[this.facet.field]) { @@ -148,10 +150,10 @@ export class LargeFacetsComponent implements OnInit { } emitChanges() { - if (this.criteria$ && !this.displayGermplasmCurrentState) { + if (this.criteria$) { this.criteria$.next(this.localCriteria); } - if (this.germplasmSearchCriteria$ && this.displayGermplasmCurrentState) { + if (this.germplasmSearchCriteria$ && this.germplasmDisplayCurrentState) { this.germplasmSearchCriteria$.next(this.germplasmLocalCriteria); } } @@ -159,11 +161,11 @@ export class LargeFacetsComponent implements OnInit { removeKey(key: string) { this.selectedTerms[this.facet.field] = this.removeFromList(this.selectedTerms[this.facet.field], key); - if (this.criteria$ && !this.displayGermplasmCurrentState) { + if (this.criteria$ && !this.germplasmDisplayCurrentState) { this.localCriteria[this.facet.field] = this.removeFromList(this.localCriteria[this.facet.field], key); } - if (this.germplasmSearchCriteria$ && this.displayGermplasmCurrentState) { + if (this.germplasmSearchCriteria$ && this.germplasmDisplayCurrentState) { if (key === 'Germplasm') { this.switchGermplasmResult(); } @@ -181,7 +183,7 @@ export class LargeFacetsComponent implements OnInit { } switchGermplasmResult() { - if (!this.displayGermplasmCurrentState) { + if (!this.germplasmDisplayCurrentState) { this.localCriteria = { ...this.localCriteria, facetFields: ['types'] @@ -193,6 +195,6 @@ export class LargeFacetsComponent implements OnInit { }; } this.criteria$.next(this.localCriteria); - this.displayGermplasmResult$.next(!this.displayGermplasmCurrentState); + this.displayGermplasmResult$.next(!this.germplasmDisplayCurrentState); } } diff --git a/frontend/src/app/facets/small-facets/small-facets.component.html b/frontend/src/app/facets/small-facets/small-facets.component.html index 62dc5c08..aff83755 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.html +++ b/frontend/src/app/facets/small-facets/small-facets.component.html @@ -25,7 +25,7 @@ <faidare-switch-button style="margin-top: 5px" - *ngIf="term.term == 'Germplasm'" + *ngIf="term.term == 'Germplasm' && !criteriaIsEmpty" [criteria$]=criteria$ [displayGermplasmResult$]="displayGermplasmResult$" [facetFiled]="facet.field" diff --git a/frontend/src/app/facets/small-facets/small-facets.component.spec.ts b/frontend/src/app/facets/small-facets/small-facets.component.spec.ts index 6d8f8ce9..49189947 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.spec.ts +++ b/frontend/src/app/facets/small-facets/small-facets.component.spec.ts @@ -12,6 +12,7 @@ import { BehaviorSubject } from 'rxjs'; import { take } from 'rxjs/operators'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { GermplasmSearchCriteria } from '../../models/gnpis.model'; +import { SwitchButtonComponent } from '../switch-button/switch-button.component'; describe('SmallFacetsComponent', () => { class FacetsComponentTester extends ComponentTester<SmallFacetsComponent> { @@ -65,7 +66,7 @@ describe('SmallFacetsComponent', () => { beforeEach(() => TestBed.configureTestingModule({ imports: [ReactiveFormsModule], - declarations: [SmallFacetsComponent], + declarations: [SmallFacetsComponent, SwitchButtonComponent], schemas: [NO_ERRORS_SCHEMA] })); diff --git a/frontend/src/app/facets/small-facets/small-facets.component.ts b/frontend/src/app/facets/small-facets/small-facets.component.ts index 44197c30..f4e893c8 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.ts +++ b/frontend/src/app/facets/small-facets/small-facets.component.ts @@ -33,7 +33,7 @@ export class SmallFacetsComponent implements OnInit { queryParams: Params; checkBoxes: FormGroup = new FormGroup({}); displayAdvanceGermplasmSearchButton: boolean; - displayGermplasmCurrentState = false; + germplasmDisplayCurrentState = false; constructor() { } @@ -49,7 +49,7 @@ export class SmallFacetsComponent implements OnInit { } this.displayGermplasmResult$.subscribe(value => { - this.displayGermplasmCurrentState = value; + this.germplasmDisplayCurrentState = value; }); if (this.criteria$) { @@ -58,29 +58,36 @@ export class SmallFacetsComponent implements OnInit { this.localCriteria = criteria; this.getSelectedTerms(criteria); + this.criteriaIsEmpty = DataDiscoveryCriteriaUtils.checkCriteriaIsEmpty(criteria); if (criteria.types) { this.showAndHideAdvanceGermplasmSearch(criteria.types); } }); } - if (this.germplasmSearchCriteria$) { + if (this.germplasmSearchCriteria$ && this.germplasmDisplayCurrentState) { this.germplasmSearchCriteria$.pipe(filter(c => c !== this.germplasmLocalCriteria)) .subscribe(germplasmCriteria => { this.germplasmLocalCriteria = germplasmCriteria; - if (this.displayGermplasmCurrentState) { + if (this.germplasmDisplayCurrentState) { this.getSelectedTerms(germplasmCriteria); } }); } - this.checkBoxes.valueChanges.subscribe(values => { - const selectedTerms = Object.keys(values).filter(key => values[key]); - const multiSelection = Object.keys(values).filter(key => values[key] && key !== 'Germplasm'); - const unselectGermplasm = Object.keys(values).filter(key => key === 'Germplasm' && !values[key]); + this.checkBoxes.valueChanges.subscribe(checkBoxesValue => { + const selectedTerms = Object.keys(checkBoxesValue).filter(key => checkBoxesValue[key]); + const multiSelection = Object.keys(checkBoxesValue).filter(key => checkBoxesValue[key] && key !== 'Germplasm'); + const unselectGermplasm = Object.keys(checkBoxesValue).filter(key => key === 'Germplasm' && !checkBoxesValue[key]); if ((multiSelection.length > 0 && this.facet.field === 'types') || unselectGermplasm.length > 0) { - this.switchGermplasmResult(); + this.localCriteria = { + ...this.localCriteria, + facetFields: ['types', 'sources'], + [this.facet.field]: selectedTerms + }; + this.criteria$.next(this.localCriteria); + this.displayGermplasmResult$.next(false); } this.showAndHideAdvanceGermplasmSearch(selectedTerms); @@ -91,7 +98,7 @@ export class SmallFacetsComponent implements OnInit { }; this.criteria$.next(this.localCriteria); } - if (this.germplasmSearchCriteria$) { + if (this.germplasmSearchCriteria$ && this.germplasmDisplayCurrentState) { this.germplasmLocalCriteria = { ...this.germplasmLocalCriteria, [this.facet.field]: selectedTerms @@ -117,21 +124,4 @@ export class SmallFacetsComponent implements OnInit { const GermplasmSelected = typeList.includes('Germplasm'); this.displayAdvanceGermplasmSearchButton = facetIsTypes && GermplasmSelected; } - - switchGermplasmResult() { - if (!this.displayGermplasmCurrentState) { - this.localCriteria = { - ...this.localCriteria, - facetFields: ['types'] - }; - } else { - this.localCriteria = { - ...this.localCriteria, - facetFields: ['types', 'sources'] - }; - } - this.criteria$.next(this.localCriteria); - this.displayGermplasmResult$.next(!this.displayGermplasmCurrentState); - } - } diff --git a/frontend/src/app/facets/switch-button/switch-button.component.html b/frontend/src/app/facets/switch-button/switch-button.component.html index b9071217..1b865ae5 100644 --- a/frontend/src/app/facets/switch-button/switch-button.component.html +++ b/frontend/src/app/facets/switch-button/switch-button.component.html @@ -7,7 +7,7 @@ </label> <label class="switch" id="switchButton"> <input type="checkbox" id="selectSwitchButton" - [checked]="displayGermplasmCurrentState" + [checked]="germplasmDisplayCurrentState" (change)="switchGermplasmResult()"> <span class="slider round"></span> </label> diff --git a/frontend/src/app/facets/switch-button/switch-button.component.spec.ts b/frontend/src/app/facets/switch-button/switch-button.component.spec.ts index d751b94f..927fd40c 100644 --- a/frontend/src/app/facets/switch-button/switch-button.component.spec.ts +++ b/frontend/src/app/facets/switch-button/switch-button.component.spec.ts @@ -1,25 +1,31 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { SwitchButtonComponent } from './switch-button.component'; +import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'; +import { + DataDiscoveryCriteria, + DataDiscoveryCriteriaUtils +} from '../../models/data-discovery.model'; +import { BehaviorSubject } from 'rxjs'; describe('SwitchButtonComponent', () => { - let component: SwitchButtonComponent; - let fixture: ComponentFixture<SwitchButtonComponent>; + let component: SwitchButtonComponent; + let fixture: ComponentFixture<SwitchButtonComponent>; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ SwitchButtonComponent ] - }) - .compileComponents(); - })); + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [NgbPopoverModule], + declarations: [SwitchButtonComponent] + }) + .compileComponents(); + })); - beforeEach(() => { - fixture = TestBed.createComponent(SwitchButtonComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + fixture = TestBed.createComponent(SwitchButtonComponent); + component = fixture.componentInstance; + component.displayGermplasmResult$ = new BehaviorSubject<boolean>(false); + component.criteria$ = new BehaviorSubject<DataDiscoveryCriteria>(DataDiscoveryCriteriaUtils.emptyCriteria()); + fixture.detectChanges(); + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/facets/switch-button/switch-button.component.ts b/frontend/src/app/facets/switch-button/switch-button.component.ts index 1d628ced..422cad9d 100644 --- a/frontend/src/app/facets/switch-button/switch-button.component.ts +++ b/frontend/src/app/facets/switch-button/switch-button.component.ts @@ -1,6 +1,7 @@ import { Component, Input, OnInit } from '@angular/core'; import { DataDiscoveryCriteria, + DataDiscoveryCriteriaUtils, DataDiscoverySource } from '../../models/data-discovery.model'; import { BehaviorSubject } from 'rxjs'; @@ -21,7 +22,7 @@ export class SwitchButtonComponent implements OnInit { localCriteria: DataDiscoveryCriteria; sources: DataDiscoverySource[]; criteriaIsEmpty = true; - displayGermplasmCurrentState = false; + germplasmDisplayCurrentState = false; constructor() { } @@ -29,12 +30,19 @@ export class SwitchButtonComponent implements OnInit { ngOnInit() { this.displayGermplasmResult$.subscribe(value => { - this.displayGermplasmCurrentState = value; + this.germplasmDisplayCurrentState = value; }); + + if (this.criteria$) { + this.criteria$.subscribe(criteria => { + this.localCriteria = criteria; + this.criteriaIsEmpty = DataDiscoveryCriteriaUtils.checkCriteriaIsEmpty(criteria); + }); + } } switchGermplasmResult() { - if (!this.displayGermplasmCurrentState) { + if (!this.germplasmDisplayCurrentState) { this.localCriteria = { ...this.localCriteria, facetFields: ['types'] @@ -46,6 +54,6 @@ export class SwitchButtonComponent implements OnInit { }; } this.criteria$.next(this.localCriteria); - this.displayGermplasmResult$.next(!this.displayGermplasmCurrentState); + this.displayGermplasmResult$.next(!this.germplasmDisplayCurrentState); } } diff --git a/frontend/src/app/gnpis.service.spec.ts b/frontend/src/app/gnpis.service.spec.ts index 1bf27758..6f82603c 100644 --- a/frontend/src/app/gnpis.service.spec.ts +++ b/frontend/src/app/gnpis.service.spec.ts @@ -153,7 +153,7 @@ describe('GnpisService', () => { geneticNature: null, holdingInstitute: null, sources: null, - types: 'Germplasm', + types: ['Germplasm'], facetFields: null, sortBy: null, diff --git a/frontend/src/app/result-page/result-page.component.ts b/frontend/src/app/result-page/result-page.component.ts index 86624b43..ce3bd67b 100644 --- a/frontend/src/app/result-page/result-page.component.ts +++ b/frontend/src/app/result-page/result-page.component.ts @@ -103,10 +103,6 @@ export class ResultPageComponent implements OnInit { relativeTo: this.route, queryParams: DataDiscoveryCriteriaUtils.toQueryParams(newCriteria) }); - this.displayGermplasmResult$.subscribe(value => { - this.displayGermplasmResult = value; - }); - this.displayGermplasmResult$.next(this.displayGermplasmResult); }); this.germplasmfacets$.subscribe(facets => { -- GitLab From ad57a69e40bd0bf89aa92aa363cff8bf2bfc2609 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Tue, 3 Mar 2020 11:02:34 +0100 Subject: [PATCH 30/35] fix: Fix the error tht appear when selected filter after back on data discovery results. GNP-4309 --- frontend/src/app/facets/facets.component.html | 4 ++-- .../src/app/facets/small-facets/small-facets.component.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/facets/facets.component.html b/frontend/src/app/facets/facets.component.html index 4e039482..dc1554b8 100644 --- a/frontend/src/app/facets/facets.component.html +++ b/frontend/src/app/facets/facets.component.html @@ -4,8 +4,8 @@ <faidare-small-facets class="col-12 col-lg-12 col-sm-6" *ngFor="let facet of facets" - [criteria$]="criteria$" [facet]="facet" + [criteria$]="criteria$" [germplasmSearchCriteria$]="germplasmSearchCriteria$" [displayGermplasmResult$]="displayGermplasmResult$"> </faidare-small-facets> @@ -15,8 +15,8 @@ <faidare-large-facets class="col-12 col-lg-12 col-sm-6" *ngFor="let facet of facets" - [criteria$]="criteria$" [facet]="facet" + [criteria$]="criteria$" [germplasmSearchCriteria$]="germplasmSearchCriteria$" [displayGermplasmResult$]="displayGermplasmResult$"> </faidare-large-facets> diff --git a/frontend/src/app/facets/small-facets/small-facets.component.ts b/frontend/src/app/facets/small-facets/small-facets.component.ts index f4e893c8..7b501a8d 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.ts +++ b/frontend/src/app/facets/small-facets/small-facets.component.ts @@ -103,8 +103,8 @@ export class SmallFacetsComponent implements OnInit { ...this.germplasmLocalCriteria, [this.facet.field]: selectedTerms }; + this.germplasmSearchCriteria$.next(this.germplasmLocalCriteria); } - this.germplasmSearchCriteria$.next(this.germplasmLocalCriteria); }); } -- GitLab From 9e30c1dbd7732b8d0be1089bdca5070d37c41a8d Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Tue, 3 Mar 2020 17:07:20 +0100 Subject: [PATCH 31/35] fix: Fix the error message which appear in the browser console when display the germplasm-result-page for the second time. GNP-4309 --- .../facets/large-facets/large-facets.component.html | 1 + .../app/facets/large-facets/large-facets.component.ts | 1 - .../facets/small-facets/small-facets.component.html | 1 + .../app/facets/small-facets/small-facets.component.ts | 11 ----------- .../facets/switch-button/switch-button.component.ts | 3 +++ .../germplasm-result-page.component.ts | 9 ++------- .../src/app/result-page/result-page.component.html | 1 - 7 files changed, 7 insertions(+), 20 deletions(-) diff --git a/frontend/src/app/facets/large-facets/large-facets.component.html b/frontend/src/app/facets/large-facets/large-facets.component.html index 0156498b..93e0fe1e 100644 --- a/frontend/src/app/facets/large-facets/large-facets.component.html +++ b/frontend/src/app/facets/large-facets/large-facets.component.html @@ -41,6 +41,7 @@ *ngIf="!criteriaIsEmpty" style="margin-top: -10px; margin-left: 25px" [criteria$]=criteria$ + [germplasmSearchCriteria$]="germplasmSearchCriteria$" [displayGermplasmResult$]="displayGermplasmResult$" [facetFiled]="facet.field" [switchTitle]="'Germplasm details'"> diff --git a/frontend/src/app/facets/large-facets/large-facets.component.ts b/frontend/src/app/facets/large-facets/large-facets.component.ts index 1cfbf0b0..8170e0b8 100644 --- a/frontend/src/app/facets/large-facets/large-facets.component.ts +++ b/frontend/src/app/facets/large-facets/large-facets.component.ts @@ -131,7 +131,6 @@ export class LargeFacetsComponent implements OnInit { if (event.item.term !== 'Germplasm' && this.facet.field === 'types') { if (this.localCriteria[this.facet.field]) { - console.log('here'); this.localCriteria[this.facet.field].push(event.item.term); } else { this.localCriteria[this.facet.field] = [event.item.term]; diff --git a/frontend/src/app/facets/small-facets/small-facets.component.html b/frontend/src/app/facets/small-facets/small-facets.component.html index aff83755..c14fa027 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.html +++ b/frontend/src/app/facets/small-facets/small-facets.component.html @@ -27,6 +27,7 @@ style="margin-top: 5px" *ngIf="term.term == 'Germplasm' && !criteriaIsEmpty" [criteria$]=criteria$ + [germplasmSearchCriteria$]="germplasmSearchCriteria$" [displayGermplasmResult$]="displayGermplasmResult$" [facetFiled]="facet.field" [switchTitle]="'Details'"> diff --git a/frontend/src/app/facets/small-facets/small-facets.component.ts b/frontend/src/app/facets/small-facets/small-facets.component.ts index 7b501a8d..4d978e46 100644 --- a/frontend/src/app/facets/small-facets/small-facets.component.ts +++ b/frontend/src/app/facets/small-facets/small-facets.component.ts @@ -32,7 +32,6 @@ export class SmallFacetsComponent implements OnInit { criteriaIsEmpty = true; queryParams: Params; checkBoxes: FormGroup = new FormGroup({}); - displayAdvanceGermplasmSearchButton: boolean; germplasmDisplayCurrentState = false; constructor() { @@ -59,9 +58,6 @@ export class SmallFacetsComponent implements OnInit { this.getSelectedTerms(criteria); this.criteriaIsEmpty = DataDiscoveryCriteriaUtils.checkCriteriaIsEmpty(criteria); - if (criteria.types) { - this.showAndHideAdvanceGermplasmSearch(criteria.types); - } }); } @@ -90,7 +86,6 @@ export class SmallFacetsComponent implements OnInit { this.displayGermplasmResult$.next(false); } - this.showAndHideAdvanceGermplasmSearch(selectedTerms); if (this.criteria$) { this.localCriteria = { ...this.localCriteria, @@ -118,10 +113,4 @@ export class SmallFacetsComponent implements OnInit { this.criteriaIsEmpty = DataDiscoveryCriteriaUtils.checkCriteriaIsEmpty(criteria); } - - showAndHideAdvanceGermplasmSearch(typeList: String[]) { - const facetIsTypes = this.facet.field === 'types'; - const GermplasmSelected = typeList.includes('Germplasm'); - this.displayAdvanceGermplasmSearchButton = facetIsTypes && GermplasmSelected; - } } diff --git a/frontend/src/app/facets/switch-button/switch-button.component.ts b/frontend/src/app/facets/switch-button/switch-button.component.ts index 422cad9d..eac2ecdd 100644 --- a/frontend/src/app/facets/switch-button/switch-button.component.ts +++ b/frontend/src/app/facets/switch-button/switch-button.component.ts @@ -5,6 +5,7 @@ import { DataDiscoverySource } from '../../models/data-discovery.model'; import { BehaviorSubject } from 'rxjs'; +import { GermplasmSearchCriteria } from '../../models/gnpis.model'; @Component({ selector: 'faidare-switch-button', @@ -15,6 +16,7 @@ export class SwitchButtonComponent implements OnInit { @Input() criteria$: BehaviorSubject<DataDiscoveryCriteria>; + @Input() germplasmSearchCriteria$: BehaviorSubject<GermplasmSearchCriteria>; @Input() displayGermplasmResult$: BehaviorSubject<boolean>; @Input() facetFiled: string; @Input() switchTitle: string; @@ -52,6 +54,7 @@ export class SwitchButtonComponent implements OnInit { ...this.localCriteria, facetFields: ['types', 'sources'] }; + this.germplasmSearchCriteria$.next(DataDiscoveryCriteriaUtils.emptyGermplasmSearchCriteria()); } this.criteria$.next(this.localCriteria); this.displayGermplasmResult$.next(!this.germplasmDisplayCurrentState); diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts index 5d5b017c..8118524d 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.ts @@ -72,7 +72,7 @@ export class GermplasmResultPageComponent implements OnInit { ngOnInit() { const queryParams = this.route.snapshot.queryParams; - // this.reassignCriteriaFieldFromDataDiscoveryFields(queryParams); + this.reassignCriteriaFieldFromDataDiscoveryFields(queryParams); this.criteriaFromForm$.subscribe(criteria => { this.reassignCriteriaFieldFromDataDiscoveryFields(criteria); @@ -82,7 +82,7 @@ export class GermplasmResultPageComponent implements OnInit { this.germplasmSearchCriteria$ .subscribe(criteria => { this.localCriteria = criteria; - this.searchGermplasm(criteria); + this.searchGermplasm(this.localCriteria); }); } @@ -180,11 +180,6 @@ export class GermplasmResultPageComponent implements OnInit { changePage(page: number) { this.localCriteria.page = page - 1; this.germplasmSearchCriteria$.next(this.localCriteria); - /*this.router.navigate(['.'], { - relativeTo: this.route, - queryParams: { page }, - queryParamsHandling: 'merge' - });*/ } changeNbElementPerPage(pageSize: number) { diff --git a/frontend/src/app/result-page/result-page.component.html b/frontend/src/app/result-page/result-page.component.html index ffa36b5c..ae0f77c4 100644 --- a/frontend/src/app/result-page/result-page.component.html +++ b/frontend/src/app/result-page/result-page.component.html @@ -107,7 +107,6 @@ </ng-container> <ng-container *ngIf="displayGermplasmResult"> - <faidare-germplasm-result-page [criteriaFromForm$]=criteria$ [germplasmSearchCriteria$]="germplasmSearchCriteria$" -- GitLab From fafc103bcef3f259007c68cf5e7e08dc279ae27a Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Tue, 10 Mar 2020 19:08:38 +0100 Subject: [PATCH 32/35] fix: Do not display tab trait when germplasm-result page is active and clear Germplasm-list field to have more generic suggestions. GNP-4309 --- frontend/package-lock.json | 11123 ---------------- .../switch-button/switch-button.component.ts | 7 +- frontend/src/app/form/form.component.html | 3 +- frontend/src/app/form/form.component.ts | 7 +- 4 files changed, 13 insertions(+), 11127 deletions(-) delete mode 100644 frontend/package-lock.json diff --git a/frontend/package-lock.json b/frontend/package-lock.json deleted file mode 100644 index 3f2c731b..00000000 --- a/frontend/package-lock.json +++ /dev/null @@ -1,11123 +0,0 @@ -{ - "name": "frontend", - "version": "0.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@angular-devkit/architect": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.13.2.tgz", - "integrity": "sha512-wcUdMzcpsxzscEa+wrhV1SE2PsHS6FnHJlRURFOtQmKvQAq3Y8gVw28l008SMt5d0bTrRV4xLL2lgvwJJoc7LA==", - "dev": true, - "requires": { - "@angular-devkit/core": "7.3.2", - "rxjs": "6.3.3" - }, - "dependencies": { - "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@angular-devkit/build-angular": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.13.2.tgz", - "integrity": "sha512-zRrV/dknx8891XSjXTh5JcTZnX4h+YsCHi6u8GABnIZW9JyiCl9QZpv0mRIyGTEaK2udmfMo2Yp5qZo1sd8jeQ==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.13.2", - "@angular-devkit/build-optimizer": "0.13.2", - "@angular-devkit/build-webpack": "0.13.2", - "@angular-devkit/core": "7.3.2", - "@ngtools/webpack": "7.3.2", - "ajv": "6.9.1", - "autoprefixer": "9.4.6", - "circular-dependency-plugin": "5.0.2", - "clean-css": "4.2.1", - "copy-webpack-plugin": "4.6.0", - "file-loader": "3.0.1", - "glob": "7.1.3", - "istanbul-instrumenter-loader": "3.0.1", - "karma-source-map-support": "1.3.0", - "less": "3.9.0", - "less-loader": "4.1.0", - "license-webpack-plugin": "2.1.0", - "loader-utils": "1.2.3", - "mini-css-extract-plugin": "0.5.0", - "minimatch": "3.0.4", - "node-sass": "4.11.0", - "opn": "5.4.0", - "parse5": "4.0.0", - "postcss": "7.0.14", - "postcss-import": "12.0.1", - "postcss-loader": "3.0.0", - "raw-loader": "1.0.0", - "rxjs": "6.3.3", - "sass-loader": "7.1.0", - "semver": "5.6.0", - "source-map-loader": "0.2.4", - "source-map-support": "0.5.10", - "speed-measure-webpack-plugin": "1.3.0", - "stats-webpack-plugin": "0.7.0", - "style-loader": "0.23.1", - "stylus": "0.54.5", - "stylus-loader": "3.0.2", - "terser-webpack-plugin": "1.2.2", - "tree-kill": "1.2.1", - "webpack": "4.29.0", - "webpack-dev-middleware": "3.5.1", - "webpack-dev-server": "3.1.14", - "webpack-merge": "4.2.1", - "webpack-sources": "1.3.0", - "webpack-subresource-integrity": "1.1.0-rc.6" - }, - "dependencies": { - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "cacache": { - "version": "11.3.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.3.tgz", - "integrity": "sha512-p8WcneCytvzPxhDvYp31PD039vi77I12W+/KfR9S8AZbaiARFBCpsPJS+9uhWfeBfeAtW7o/4vt3MUqLkbY6nA==", - "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", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "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" - } - } - } - }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "terser-webpack-plugin": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.2.tgz", - "integrity": "sha512-1DMkTk286BzmfylAvLXwpJrI7dWa5BnFmscV/2dCr8+c56egFcbaeFAl7+sujAjdmpLam21XRdhA4oifLyiWWg==", - "dev": true, - "requires": { - "cacache": "^11.0.2", - "find-cache-dir": "^2.0.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^1.4.0", - "source-map": "^0.6.1", - "terser": "^3.16.1", - "webpack-sources": "^1.1.0", - "worker-farm": "^1.5.2" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "@angular-devkit/build-optimizer": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.13.2.tgz", - "integrity": "sha512-pM3t+6VD+gdcesgwuThR41DFdsZ9ZVQ97Hhr0JXHLbLyRt4eXxWi2+B5VL0jjAaX0RIiUIe8wgScwE6m/dxemg==", - "dev": true, - "requires": { - "loader-utils": "1.2.3", - "source-map": "0.5.6", - "typescript": "3.2.4", - "webpack-sources": "1.3.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.6", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", - "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", - "dev": true - } - } - }, - "@angular-devkit/build-webpack": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.13.2.tgz", - "integrity": "sha512-Uemur2KhFu7VGU2QQmfRiMwmoSKprZrMZRZXwZdCQPN5srIcMAgGjm1PGbZuCUddhwd2XRP9dKY6zOZpMzm84Q==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.13.2", - "@angular-devkit/core": "7.3.2", - "rxjs": "6.3.3" - }, - "dependencies": { - "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@angular-devkit/core": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.2.tgz", - "integrity": "sha512-W5KjkHRNVBcZRUNJamAn52IAj9Gl1zUjPA2r75JJK7k199xOA8UZqcIukQOgM1N7rwKCWht08i4FsdcTDghMhQ==", - "dev": true, - "requires": { - "ajv": "6.9.1", - "chokidar": "2.0.4", - "fast-json-stable-stringify": "2.0.0", - "rxjs": "6.3.3", - "source-map": "0.7.3" - }, - "dependencies": { - "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@angular-devkit/schematics": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-7.3.4.tgz", - "integrity": "sha512-BLI4MDHmpzw+snu/2Dw1nMmfJ0VAARTbU6DrmzXyl2Se45+iE/tdRy4yNx3IfHhyoCrVZ15R0y9CXeEsLftlIg==", - "dev": true, - "requires": { - "@angular-devkit/core": "7.3.4", - "rxjs": "6.3.3" - }, - "dependencies": { - "@angular-devkit/core": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.4.tgz", - "integrity": "sha512-MBfen51iOBKfK4tlg5KwmPxePsF1QoFNUMGLuvUUwPkteonrGcupX1Q7NWTpf+HA+i08mOnZGuepeuQkD12IQw==", - "dev": true, - "requires": { - "ajv": "6.9.1", - "chokidar": "2.0.4", - "fast-json-stable-stringify": "2.0.0", - "rxjs": "6.3.3", - "source-map": "0.7.3" - } - }, - "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@angular/animations": { - "version": "7.2.7", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-7.2.7.tgz", - "integrity": "sha512-eU/wSkBmukZXCCe/epUl02xsKPauF+deMbncxBE+w/NmmWjJ77Q09iZAcgzM92RVXj2LsVYQXsNEBGT3X0hRZw==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@angular/cli": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-7.3.4.tgz", - "integrity": "sha512-uGL8xiQf+GvuJvqvMUu/XHcijbq9ocbX487LO2PgJ29etHfI7dC0toJbQ8ob+HnF9e1qwMe+uu45OU4C2p+a1A==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.13.4", - "@angular-devkit/core": "7.3.4", - "@angular-devkit/schematics": "7.3.4", - "@schematics/angular": "7.3.4", - "@schematics/update": "0.13.4", - "@yarnpkg/lockfile": "1.1.0", - "ini": "1.3.5", - "inquirer": "6.2.1", - "npm-package-arg": "6.1.0", - "opn": "5.4.0", - "pacote": "9.4.0", - "semver": "5.6.0", - "symbol-observable": "1.2.0" - }, - "dependencies": { - "@angular-devkit/architect": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.13.4.tgz", - "integrity": "sha512-wJF8oz8MurtpFi0ik42bkI2F5gEnuOe79KHPO1i3SYfdhEp5NY8igVKZ6chB/eq4Ml50aHxas8Hh9ke12K+Pxw==", - "dev": true, - "requires": { - "@angular-devkit/core": "7.3.4", - "rxjs": "6.3.3" - } - }, - "@angular-devkit/core": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.4.tgz", - "integrity": "sha512-MBfen51iOBKfK4tlg5KwmPxePsF1QoFNUMGLuvUUwPkteonrGcupX1Q7NWTpf+HA+i08mOnZGuepeuQkD12IQw==", - "dev": true, - "requires": { - "ajv": "6.9.1", - "chokidar": "2.0.4", - "fast-json-stable-stringify": "2.0.0", - "rxjs": "6.3.3", - "source-map": "0.7.3" - } - }, - "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@angular/common": { - "version": "7.2.7", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.2.7.tgz", - "integrity": "sha512-U1l2CIcmpTAJMWcyTXI9qt1E8CxwKNW1vr6XWZo4X5ziCIzf7RvClzK7Ci5KZKkoPJrJqBJu661Q75Yt22dJsg==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@angular/compiler": { - "version": "7.2.7", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.2.7.tgz", - "integrity": "sha512-e61YVxW5x4w+X4yjGaptYoJIja7HwH0+8FFEaH6VuPl/DrK8wP4HDMhLo4NzdgeZKLR2jBIQSqLmoM8W7UXcqw==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@angular/compiler-cli": { - "version": "7.2.7", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.2.7.tgz", - "integrity": "sha512-UPWROJzBLejgNf+aqgEUXYts8UiFOl2IavDhS/olA9irszv2lNFj9Yqr8OKdy0jK/lKaipZog3VZEx8g5dNeBA==", - "dev": true, - "requires": { - "canonical-path": "1.0.0", - "chokidar": "^2.1.1", - "convert-source-map": "^1.5.1", - "dependency-graph": "^0.7.2", - "magic-string": "^0.25.0", - "minimist": "^1.2.0", - "reflect-metadata": "^0.1.2", - "shelljs": "^0.8.1", - "source-map": "^0.6.1", - "tslib": "^1.9.0", - "yargs": "9.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "chokidar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.2.tgz", - "integrity": "sha512-IwXUx0FXc5ibYmPC2XeEj5mpXoV66sR+t3jqu2NS2GYwCktt3KF1/Qqjws/NkegajBA4RbZ5+DDwlOiJsxDHEg==", - "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.0" - } - }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, - "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "dev": true, - "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" - } - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yargs": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-9.0.1.tgz", - "integrity": "sha1-UqzCP+7Kw0BCB47njAwAf1CF20w=", - "dev": true, - "requires": { - "camelcase": "^4.1.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "read-pkg-up": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^7.0.0" - } - }, - "yargs-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", - "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", - "dev": true, - "requires": { - "camelcase": "^4.1.0" - } - } - } - }, - "@angular/core": { - "version": "7.2.7", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.2.7.tgz", - "integrity": "sha512-E7qjMQdS77SbRROKu13VsfL+MJN52eTlrU0SzEAFGUOgdvbmDYJOaEwjqrouKpYZ0pul8KOoalvlPB7oVflC7A==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@angular/forms": { - "version": "7.2.7", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.2.7.tgz", - "integrity": "sha512-2gBs+BG2cMPsHq9JVEzmu2Ev539zjfHmr6cna2W38KLXeGbNf42rbbMUXpYD8cndY0QTYcnwfMpRNIl9zKRZnw==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@angular/http": { - "version": "7.2.7", - "resolved": "https://registry.npmjs.org/@angular/http/-/http-7.2.7.tgz", - "integrity": "sha512-HTHYF3qR4S55A+9pyThSOy7++7Makp+klbZTNmpwwJj8yL3qgy9PyDXtf+xhZcUEd8xfXmnz8s4hZr0O9GUy1A==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@angular/language-service": { - "version": "7.2.7", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-7.2.7.tgz", - "integrity": "sha512-d3iCBpOfgLNSGMrtqZvN6NHZIEnKD2MV8Hz4WsRLU4WY0RbshZj5dqx2nO3YRT2tACpAvhWBQoYvtLpTCPzsMA==", - "dev": true - }, - "@angular/platform-browser": { - "version": "7.2.7", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.2.7.tgz", - "integrity": "sha512-9C3ffZs0ZUw+dYg1oJKiONf64UKTdAzIOaTQXTrVrCa3oN7Jb2tUfmpenmB+ATRxwhL2n7Yi725YWwxY2FwqvQ==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@angular/platform-browser-dynamic": { - "version": "7.2.7", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.2.7.tgz", - "integrity": "sha512-3nlcwCZOzlKw/4CMJ4zy1JEVy8Ky4KyLRRePLledOMdsGbuDIoq/kyAnBzg295Xe9ovBxv8cmuSkShci+s/x8g==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@angular/router": { - "version": "7.2.7", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-7.2.7.tgz", - "integrity": "sha512-59+M8+IH7V2NPPqWw2mwdg+kh/jfwQcXE0tB8iZ5V2ldACPucY/Td6qiT5H6t7EkELtvkKJwS6vKFV22qdRp3w==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/generator": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.3.3.tgz", - "integrity": "sha512-aEADYwRRZjJyMnKN7llGIlircxTCofm3dtV5pmY6ob18MSIuipHpA2yZWkPlycwu5HJcx/pADS3zssd8eY7/6A==", - "dev": true, - "requires": { - "@babel/types": "^7.3.3", - "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - }, - "dependencies": { - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/helper-function-name": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", - "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", - "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "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 - } - } - }, - "@babel/parser": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.3.3.tgz", - "integrity": "sha512-xsH1CJoln2r74hR+y7cg2B5JCPaTh+Hd+EbBRk9nWGSNspuo6krjhX0Om6RnRQuIvFq8wVXCLKH3kwKDYhanSg==", - "dev": true - }, - "@babel/template": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz", - "integrity": "sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.2.2", - "@babel/types": "^7.2.2" - } - }, - "@babel/traverse": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.3.tgz", - "integrity": "sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.2.2", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.0.0", - "@babel/parser": "^7.2.3", - "@babel/types": "^7.2.2", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.10" - }, - "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" - } - }, - "globals": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", - "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==", - "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 - } - } - }, - "@babel/types": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.3.tgz", - "integrity": "sha512-2tACZ80Wg09UnPg5uGAOUvvInaqLk3l/IAhQzlxLQOIXacr6bMsra5SH6AWw/hIDRCSbCdHP2KzSOD+cT7TzMQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "@ng-bootstrap/ng-bootstrap": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-4.2.2.tgz", - "integrity": "sha512-v8QmC17bv9he5Ep6zutaI9aQ2w/2NqySP0fejOKe7cacKpGUqsLIakpyd2FD7mfZu7pSCCtHYpRWR+h6yq+Ngg==", - "requires": { - "tslib": "^1.9.0" - } - }, - "@ngtools/webpack": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-7.3.2.tgz", - "integrity": "sha512-q98nt7HUTcdEtP+aJjsm5HUMDL+BXwLz80TthtFlu/f7JYdKxMSWZRHEv+q8Rs69pWMpwxj8RuHm8XiKD/8Cpg==", - "dev": true, - "requires": { - "@angular-devkit/core": "7.3.2", - "enhanced-resolve": "4.1.0", - "rxjs": "6.3.3", - "tree-kill": "1.2.1", - "webpack-sources": "1.3.0" - }, - "dependencies": { - "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@schematics/angular": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-7.3.4.tgz", - "integrity": "sha512-Bb5DZQ8MeP8yhxPe6nVqyQ7sGVNwUx6nXPlrQV45ZycD3nJlqsuxr2DE75HFpn5oU+vlkq9J/Sys4WLJ4E/OMw==", - "dev": true, - "requires": { - "@angular-devkit/core": "7.3.4", - "@angular-devkit/schematics": "7.3.4", - "typescript": "3.2.4" - }, - "dependencies": { - "@angular-devkit/core": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.4.tgz", - "integrity": "sha512-MBfen51iOBKfK4tlg5KwmPxePsF1QoFNUMGLuvUUwPkteonrGcupX1Q7NWTpf+HA+i08mOnZGuepeuQkD12IQw==", - "dev": true, - "requires": { - "ajv": "6.9.1", - "chokidar": "2.0.4", - "fast-json-stable-stringify": "2.0.0", - "rxjs": "6.3.3", - "source-map": "0.7.3" - } - }, - "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@schematics/update": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.13.4.tgz", - "integrity": "sha512-YarSCCBSVPVG/MyN5H/FliRwaIDoeercy5Nip+NWZJsDyvtsAekO9s6QwizSvAr3541MmSQFeQICsjyM2dl3Bg==", - "dev": true, - "requires": { - "@angular-devkit/core": "7.3.4", - "@angular-devkit/schematics": "7.3.4", - "@yarnpkg/lockfile": "1.1.0", - "ini": "1.3.5", - "pacote": "9.4.0", - "rxjs": "6.3.3", - "semver": "5.6.0", - "semver-intersect": "1.4.0" - }, - "dependencies": { - "@angular-devkit/core": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.4.tgz", - "integrity": "sha512-MBfen51iOBKfK4tlg5KwmPxePsF1QoFNUMGLuvUUwPkteonrGcupX1Q7NWTpf+HA+i08mOnZGuepeuQkD12IQw==", - "dev": true, - "requires": { - "ajv": "6.9.1", - "chokidar": "2.0.4", - "fast-json-stable-stringify": "2.0.0", - "rxjs": "6.3.3", - "source-map": "0.7.3" - } - }, - "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - } - } - }, - "@types/geojson": { - "version": "7946.0.6", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.6.tgz", - "integrity": "sha512-f6qai3iR62QuMPPdgyH+LyiXTL2n9Rf62UniJjV7KHrbiwzLTZUKsdq0mFSTxAHbO7JvwxwC4tH0m1UnweuLrA==" - }, - "@types/jasmine": { - "version": "2.8.12", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.12.tgz", - "integrity": "sha512-eE+xeiGBPgQsNcyg61JBqQS6NtxC+s2yfOikMCnc0Z4NqKujzmSahmtjLCKVQU/AyrTEQ76TOwQBnr8wGP2bmA==", - "dev": true - }, - "@types/jasminewd2": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.6.tgz", - "integrity": "sha512-2ZOKrxb8bKRmP/po5ObYnRDgFE4i+lQiEB27bAMmtMWLgJSqlIDqlLx6S0IRorpOmOPRQ6O80NujTmQAtBkeNw==", - "dev": true, - "requires": { - "@types/jasmine": "*" - } - }, - "@types/leaflet": { - "version": "1.2.14", - "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.2.14.tgz", - "integrity": "sha512-acP2w5DygY0V7bwmjFmaen5I2iBl8RkWx9kon1IJA7k9mNFgBb6702WApjZSrM4AG1ucJVxFcTlS6nr4HvahEw==", - "requires": { - "@types/geojson": "*" - } - }, - "@types/leaflet.markercluster": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/leaflet.markercluster/-/leaflet.markercluster-1.0.3.tgz", - "integrity": "sha512-rz4xQcsD3Ha9TcX4nMba9wpNe7HPQ03Hvo8Osi3SLpfaDCydHMoTquOG1IsjQ2aFm/LIHz4Uo4hYoeLv7q082w==", - "requires": { - "@types/leaflet": "*" - } - }, - "@types/marked": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/@types/marked/-/marked-0.6.5.tgz", - "integrity": "sha512-6kBKf64aVfx93UJrcyEZ+OBM5nGv4RLsI6sR1Ar34bpgvGVRoyTgpxn4ZmtxOM5aDTAaaznYuYUH8bUX3Nk3YA==" - }, - "@types/node": { - "version": "8.9.5", - "resolved": "http://registry.npmjs.org/@types/node/-/node-8.9.5.tgz", - "integrity": "sha512-jRHfWsvyMtXdbhnz5CVHxaBgnV6duZnPlQuRSo/dm/GnmikNcmZhxIES4E9OZjUmQ8C+HCl4KJux+cXN/ErGDQ==", - "dev": true - }, - "@types/q": { - "version": "0.0.32", - "resolved": "http://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", - "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", - "dev": true - }, - "@types/selenium-webdriver": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.15.tgz", - "integrity": "sha512-5nh8/K2u9p4bk95GGCJB7KBvewaB0TUziZ9DTr+mR2I6RoO4OJVqx7rxK83hs2J1tomwtCGkhiW+Dy8EUnfB+Q==", - "dev": true - }, - "@types/source-list-map": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", - "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", - "dev": true - }, - "@types/webpack-sources": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.5.tgz", - "integrity": "sha512-zfvjpp7jiafSmrzJ2/i3LqOyTYTuJ7u1KOXlKgDlvsj9Rr0x7ZiYu5lZbXwobL7lmsRNtPXlBfmaUD8eU2Hu8w==", - "dev": true, - "requires": { - "@types/node": "*", - "@types/source-list-map": "*", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "@webassemblyjs/ast": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz", - "integrity": "sha512-ZEzy4vjvTzScC+SH8RBssQUawpaInUdMTYwYYLh54/s8TuT0gBLuyUnppKsVyZEi876VmmStKsUs28UxPgdvrA==", - "dev": true, - "requires": { - "@webassemblyjs/helper-module-context": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/wast-parser": "1.7.11" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.11.tgz", - "integrity": "sha512-zY8dSNyYcgzNRNT666/zOoAyImshm3ycKdoLsyDw/Bwo6+/uktb7p4xyApuef1dwEBo/U/SYQzbGBvV+nru2Xg==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.11.tgz", - "integrity": "sha512-7r1qXLmiglC+wPNkGuXCvkmalyEstKVwcueZRP2GNC2PAvxbLYwLLPr14rcdJaE4UtHxQKfFkuDFuv91ipqvXg==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.11.tgz", - "integrity": "sha512-MynuervdylPPh3ix+mKZloTcL06P8tenNH3sx6s0qE8SLR6DdwnfgA7Hc9NSYeob2jrW5Vql6GVlsQzKQCa13w==", - "dev": true - }, - "@webassemblyjs/helper-code-frame": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.11.tgz", - "integrity": "sha512-T8ESC9KMXFTXA5urJcyor5cn6qWeZ4/zLPyWeEXZ03hj/x9weSokGNkVCdnhSabKGYWxElSdgJ+sFa9G/RdHNw==", - "dev": true, - "requires": { - "@webassemblyjs/wast-printer": "1.7.11" - } - }, - "@webassemblyjs/helper-fsm": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.11.tgz", - "integrity": "sha512-nsAQWNP1+8Z6tkzdYlXT0kxfa2Z1tRTARd8wYnc/e3Zv3VydVVnaeePgqUzFrpkGUyhUUxOl5ML7f1NuT+gC0A==", - "dev": true - }, - "@webassemblyjs/helper-module-context": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.11.tgz", - "integrity": "sha512-JxfD5DX8Ygq4PvXDucq0M+sbUFA7BJAv/GGl9ITovqE+idGX+J3QSzJYz+LwQmL7fC3Rs+utvWoJxDb6pmC0qg==", - "dev": true - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.11.tgz", - "integrity": "sha512-cMXeVS9rhoXsI9LLL4tJxBgVD/KMOKXuFqYb5oCJ/opScWpkCMEz9EJtkonaNcnLv2R3K5jIeS4TRj/drde1JQ==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.11.tgz", - "integrity": "sha512-8ZRY5iZbZdtNFE5UFunB8mmBEAbSI3guwbrsCl4fWdfRiAcvqQpeqd5KHhSWLL5wuxo53zcaGZDBU64qgn4I4Q==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-buffer": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/wasm-gen": "1.7.11" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.7.11.tgz", - "integrity": "sha512-Mmqx/cS68K1tSrvRLtaV/Lp3NZWzXtOHUW2IvDvl2sihAwJh4ACE0eL6A8FvMyDG9abes3saB6dMimLOs+HMoQ==", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.7.11.tgz", - "integrity": "sha512-vuGmgZjjp3zjcerQg+JA+tGOncOnJLWVkt8Aze5eWQLwTQGNgVLcyOTqgSCxWTR4J42ijHbBxnuRaL1Rv7XMdw==", - "dev": true, - "requires": { - "@xtuc/long": "4.2.1" - } - }, - "@webassemblyjs/utf8": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.7.11.tgz", - "integrity": "sha512-C6GFkc7aErQIAH+BMrIdVSmW+6HSe20wg57HEC1uqJP8E/xpMjXqQUxkQw07MhNDSDcGpxI9G5JSNOQCqJk4sA==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.11.tgz", - "integrity": "sha512-FUd97guNGsCZQgeTPKdgxJhBXkUbMTY6hFPf2Y4OedXd48H97J+sOY2Ltaq6WGVpIH8o/TGOVNiVz/SbpEMJGg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-buffer": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/helper-wasm-section": "1.7.11", - "@webassemblyjs/wasm-gen": "1.7.11", - "@webassemblyjs/wasm-opt": "1.7.11", - "@webassemblyjs/wasm-parser": "1.7.11", - "@webassemblyjs/wast-printer": "1.7.11" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.11.tgz", - "integrity": "sha512-U/KDYp7fgAZX5KPfq4NOupK/BmhDc5Kjy2GIqstMhvvdJRcER/kUsMThpWeRP8BMn4LXaKhSTggIJPOeYHwISA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/ieee754": "1.7.11", - "@webassemblyjs/leb128": "1.7.11", - "@webassemblyjs/utf8": "1.7.11" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.11.tgz", - "integrity": "sha512-XynkOwQyiRidh0GLua7SkeHvAPXQV/RxsUeERILmAInZegApOUAIJfRuPYe2F7RcjOC9tW3Cb9juPvAC/sCqvg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-buffer": "1.7.11", - "@webassemblyjs/wasm-gen": "1.7.11", - "@webassemblyjs/wasm-parser": "1.7.11" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.11.tgz", - "integrity": "sha512-6lmXRTrrZjYD8Ng8xRyvyXQJYUQKYSXhJqXOBLw24rdiXsHAOlvw5PhesjdcaMadU/pyPQOJ5dHreMjBxwnQKg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-api-error": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/ieee754": "1.7.11", - "@webassemblyjs/leb128": "1.7.11", - "@webassemblyjs/utf8": "1.7.11" - } - }, - "@webassemblyjs/wast-parser": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.7.11.tgz", - "integrity": "sha512-lEyVCg2np15tS+dm7+JJTNhNWq9yTZvi3qEhAIIOaofcYlUp0UR5/tVqOwa/gXYr3gjwSZqw+/lS9dscyLelbQ==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/floating-point-hex-parser": "1.7.11", - "@webassemblyjs/helper-api-error": "1.7.11", - "@webassemblyjs/helper-code-frame": "1.7.11", - "@webassemblyjs/helper-fsm": "1.7.11", - "@xtuc/long": "4.2.1" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.7.11.tgz", - "integrity": "sha512-m5vkAsuJ32QpkdkDOUPGSltrg8Cuk3KBx4YrmAGQwCZPRdUHXxG4phIOuuycLemHFr74sWL9Wthqss4fzdzSwg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/wast-parser": "1.7.11", - "@xtuc/long": "4.2.1" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "@xtuc/long": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.1.tgz", - "integrity": "sha512-FZdkNBDqBRHKQ2MEbSC17xnPFOhZxeJ2YGSfr2BKf3sujG49Qe3bB+rGCwQfIaA7WHnGeGkSijX4FuBCdrzW/g==", - "dev": true - }, - "@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", - "dev": true - }, - "JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true, - "optional": true - }, - "accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", - "dev": true, - "requires": { - "mime-types": "~2.1.18", - "negotiator": "0.6.1" - } - }, - "acorn": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.0.tgz", - "integrity": "sha512-MW/FjM+IvU9CgBzjO3UIPCE2pyEwUsoFl+VGdczOPEdxfGFjuKny/gN54mOuX7Qxmb9Rg9MCn2oKiSUeW+pjrw==", - "dev": true - }, - "acorn-dynamic-import": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz", - "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==", - "dev": true - }, - "adm-zip": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.13.tgz", - "integrity": "sha512-fERNJX8sOXfel6qCBCMPvZLzENBEhZTzKqg6vrOW5pvoEaQuJhRU4ndTAh6lHOxn1I6jnz2NHra56ZODM751uw==", - "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.2.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", - "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "agentkeepalive": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", - "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", - "dev": true, - "requires": { - "humanize-ms": "^1.2.1" - } - }, - "ajv": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", - "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "dev": true - }, - "ajv-keywords": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz", - "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==", - "dev": true - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true - }, - "angular-coordinates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/angular-coordinates/-/angular-coordinates-1.0.0.tgz", - "integrity": "sha1-IcuYpv+PTV6LWLjOjvzBP7elnug=" - }, - "angular-mocks": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/angular-mocks/-/angular-mocks-1.7.8.tgz", - "integrity": "sha512-LB13ESBT0eJrhQhfPXyLR9qm4LI9g44hyBFwUqZKEHEA4DpfxVTu0ONipiNoN0zWtmEAezA8u2gjcoaO2TStig==" - }, - "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", - "dev": true - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "app-root-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.1.0.tgz", - "integrity": "sha1-mL9lmTJ+zqGZMJhm6BQDaP0uZGo=", - "dev": true - }, - "append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", - "dev": true, - "requires": { - "default-require-extensions": "^2.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" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true, - "optional": true - }, - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "arraybuffer.slice": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", - "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "dev": true, - "optional": true - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", - "dev": true, - "requires": { - "util": "0.10.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - } - } - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", - "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", - "dev": true - }, - "async-foreach": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", - "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", - "dev": true, - "optional": true - }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "autoprefixer": { - "version": "9.4.6", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.6.tgz", - "integrity": "sha512-Yp51mevbOEdxDUy5WjiKtpQaecqYq9OqZSL04rSoCiry7Tc5I9FEyo3bfxiTJc1DfHeKwSFCUYbBAiOQ2VGfiw==", - "dev": true, - "requires": { - "browserslist": "^4.4.1", - "caniuse-lite": "^1.0.30000929", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^7.0.13", - "postcss-value-parser": "^3.3.1" - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "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": "http://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" - } - }, - "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": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "babel-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-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" - } - }, - "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" - } - }, - "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" - } - }, - "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=", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "base64-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.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", - "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==", - "dev": true - }, - "binary-extensions": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.0.tgz", - "integrity": "sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw==", - "dev": true - }, - "blob": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", - "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", - "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, - "optional": 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.5.3", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", - "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", - "dev": true - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - }, - "body-parser": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", - "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", - "dev": true, - "requires": { - "bytes": "3.0.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "~1.6.3", - "iconv-lite": "0.4.23", - "on-finished": "~2.3.0", - "qs": "6.5.2", - "raw-body": "2.3.3", - "type-is": "~1.6.16" - } - }, - "bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", - "dev": true, - "requires": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" - } - }, - "bootstrap": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.3.1.tgz", - "integrity": "sha512-rXqOmH1VilAt2DyPzluTi2blhk17bO7ef+zLLPlWvG494pDxcM234pJ8wTc/6R40UWizAIIMgxjvxZg5kmsbag==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "http://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": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" - } - }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "dev": true, - "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" - } - }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "requires": { - "pako": "~1.0.5" - } - }, - "browserslist": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.4.1.tgz", - "integrity": "sha512-pEBxEXg7JwaakBXjATYw/D1YZh4QUSCX/Mnd/wnqSRPPSi1U39iDhDoKGoBUcraKdxDlrYqJxSI5nNvD+dWP2A==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30000929", - "electron-to-chromium": "^1.3.103", - "node-releases": "^1.1.3" - } - }, - "browserstack": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.2.tgz", - "integrity": "sha512-+6AFt9HzhKykcPF79W6yjEUJcdvZOV0lIXdkORXMJftGrDl0OKWqRF4GHqpDNkxiceDT/uB7Fb/aDwktvXX7dg==", - "dev": true, - "requires": { - "https-proxy-agent": "^2.2.1" - } - }, - "buffer": { - "version": "4.9.1", - "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "buffer-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-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==", - "dev": true - }, - "buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, - "builtins": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", - "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", - "dev": true - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true - }, - "cacache": { - "version": "10.0.4", - "resolved": "http://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", - "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", - "dev": true, - "requires": { - "bluebird": "^3.5.1", - "chownr": "^1.0.1", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "lru-cache": "^4.1.1", - "mississippi": "^2.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^5.2.4", - "unique-filename": "^1.1.0", - "y18n": "^4.0.0" - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", - "dev": true - }, - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true, - "optional": true - }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true, - "optional": true, - "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" - } - }, - "caniuse-lite": { - "version": "1.0.30000938", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000938.tgz", - "integrity": "sha512-ekW8NQ3/FvokviDxhdKLZZAx7PptXNwxKgXtnR5y+PR3hckwuP3yJ1Ir+4/c97dsHNqtAyfKUGdw8P4EYzBNgw==", - "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==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "chokidar": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", - "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" - } - }, - "chownr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", - "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", - "dev": true - }, - "chrome-trace-event": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz", - "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "circular-dependency-plugin": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.0.2.tgz", - "integrity": "sha512-oC7/DVAyfcY3UWKm0sN/oVoDedQDQiw/vIiAnuTWTpE5s0zWf7l3WY417Xw/Fbi/QbAjctAkxgMiS9P0s3zkmA==", - "dev": true - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "clean-css": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", - "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", - "dev": true, - "requires": { - "source-map": "~0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "clipboard": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz", - "integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==", - "optional": true, - "requires": { - "good-listener": "^1.2.2", - "select": "^1.1.2", - "tiny-emitter": "^2.0.0" - } - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - } - }, - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true - }, - "clone-deep": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-2.0.2.tgz", - "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==", - "dev": true, - "requires": { - "for-own": "^1.0.0", - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.0", - "shallow-clone": "^1.0.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "codelyzer": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-4.5.0.tgz", - "integrity": "sha512-oO6vCkjqsVrEsmh58oNlnJkRXuA30hF8cdNAQV9DytEalDwyOFRvHMnlKFzmOStNerOmPGZU9GAHnBo4tGvtiQ==", - "dev": true, - "requires": { - "app-root-path": "^2.1.0", - "css-selector-tokenizer": "^0.7.0", - "cssauron": "^1.4.0", - "semver-dsl": "^1.0.1", - "source-map": "^0.5.7", - "sprintf-js": "^1.1.1" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", - "dev": true - } - } - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "colors": { - "version": "1.1.2", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", - "dev": true - }, - "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "compare-versions": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.4.0.tgz", - "integrity": "sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg==", - "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.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "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.16", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.16.tgz", - "integrity": "sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA==", - "dev": true, - "requires": { - "mime-db": ">= 1.38.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==", - "dev": true, - "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=", - "dev": true - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "connect": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", - "integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=", - "dev": true, - "requires": { - "debug": "2.6.9", - "finalhandler": "1.1.0", - "parseurl": "~1.3.2", - "utils-merge": "1.0.1" - }, - "dependencies": { - "finalhandler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", - "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.3.1", - "unpipe": "~1.0.0" - } - }, - "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", - "dev": true - } - } - }, - "connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", - "dev": true - }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, - "requires": { - "date-now": "^0.1.4" - } - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", - "dev": true - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true - }, - "convert-source-map": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", - "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true - }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "copy-webpack-plugin": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz", - "integrity": "sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==", - "dev": true, - "requires": { - "cacache": "^10.0.4", - "find-cache-dir": "^1.0.0", - "globby": "^7.1.1", - "is-glob": "^4.0.0", - "loader-utils": "^1.1.0", - "minimatch": "^3.0.4", - "p-limit": "^1.0.0", - "serialize-javascript": "^1.4.0" - } - }, - "core-js": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cosmiconfig": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-4.0.0.tgz", - "integrity": "sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ==", - "dev": true, - "requires": { - "is-directory": "^0.3.1", - "js-yaml": "^3.9.0", - "parse-json": "^4.0.0", - "require-from-string": "^2.0.1" - }, - "dependencies": { - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - } - } - }, - "create-ecdh": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" - } - }, - "create-hash": { - "version": "1.2.0", - "resolved": "http://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": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "cross-spawn": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", - "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", - "dev": true, - "optional": true, - "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - } - }, - "css-parse": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.7.0.tgz", - "integrity": "sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs=", - "dev": true - }, - "css-selector-tokenizer": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz", - "integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==", - "dev": true, - "requires": { - "cssesc": "^0.1.0", - "fastparse": "^1.1.1", - "regexpu-core": "^1.0.0" - } - }, - "cssauron": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", - "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", - "dev": true, - "requires": { - "through": "X.X.X" - } - }, - "cssesc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", - "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", - "dev": true - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "optional": true, - "requires": { - "array-find-index": "^1.0.1" - } - }, - "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": "0.2.2", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", - "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", - "dev": true - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "date-format": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.0.0.tgz", - "integrity": "sha512-M6UqVvZVgFYqZL1SfHsRGIQSz3ZL+qgbsV5Lp1Vj61LZVYuEwcMXYay7DRDtYs2HQQBK5hQtQ0fD9aEJ89V0LA==", - "dev": true - }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", - "dev": true - }, - "default-gateway": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-2.7.2.tgz", - "integrity": "sha512-lAc4i9QJR0YHSDFdzeBQKfZ1SRDG3hsJNEkrpcZa8QhBfidLAilT60BDEIVUUGqosFp425KOgB3uYqcnQrWafQ==", - "dev": true, - "requires": { - "execa": "^0.10.0", - "ip-regex": "^2.1.0" - } - }, - "default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", - "dev": true, - "requires": { - "strip-bom": "^3.0.0" - }, - "dependencies": { - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "del": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", - "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", - "dev": true, - "requires": { - "globby": "^6.1.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "p-map": "^1.1.1", - "pify": "^3.0.0", - "rimraf": "^2.2.8" - }, - "dependencies": { - "globby": { - "version": "6.1.0", - "resolved": "http://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": "http://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 - }, - "delegate": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", - "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==", - "optional": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "dependency-graph": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.7.2.tgz", - "integrity": "sha512-KqtH4/EZdtdfWX0p6MGP9jljvxSY6msy/pRUD4jgNwVpv3v1QmNLlsB3LDSSUg79BRVSn7jI1QPRtArGABovAQ==", - "dev": true - }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "dev": true - }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "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 - }, - "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": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - } - }, - "dir-glob": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", - "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", - "dev": true, - "requires": { - "path-type": "^3.0.0" - } - }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", - "dev": true - }, - "dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", - "dev": true, - "requires": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", - "dev": true, - "requires": { - "buffer-indexof": "^1.0.0" - } - }, - "doctrine": { - "version": "0.7.2", - "resolved": "http://registry.npmjs.org/doctrine/-/doctrine-0.7.2.tgz", - "integrity": "sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=", - "dev": true, - "requires": { - "esutils": "^1.1.6", - "isarray": "0.0.1" - }, - "dependencies": { - "esutils": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz", - "integrity": "sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U=", - "dev": true - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - } - } - }, - "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" - } - }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true - }, - "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dev": true, - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "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.113", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.113.tgz", - "integrity": "sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g==", - "dev": true - }, - "elliptic": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", - "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", - "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" - } - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true - }, - "encoding": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", - "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", - "dev": true, - "requires": { - "iconv-lite": "~0.4.13" - } - }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "engine.io": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", - "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "base64id": "1.0.0", - "cookie": "0.3.1", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.0", - "ws": "~3.3.1" - }, - "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" - } - } - } - }, - "engine.io-client": { - "version": "3.2.1", - "resolved": "http://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", - "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", - "dev": true, - "requires": { - "component-emitter": "1.2.1", - "component-inherit": "0.0.3", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.1", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "ws": "~3.3.1", - "xmlhttprequest-ssl": "~1.5.4", - "yeast": "0.1.2" - }, - "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" - } - } - } - }, - "engine.io-parser": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", - "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", - "dev": true, - "requires": { - "after": "0.8.2", - "arraybuffer.slice": "~0.0.7", - "base64-arraybuffer": "0.1.5", - "blob": "0.0.5", - "has-binary2": "~1.0.2" - } - }, - "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" - } - }, - "ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", - "dev": true - }, - "err-code": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", - "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", - "dev": true - }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dev": true, - "requires": { - "prr": "~1.0.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es6-promise": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz", - "integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==", - "dev": true - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "dev": true, - "requires": { - "es6-promise": "^4.0.3" - } - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint-scope": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", - "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "dev": true - }, - "eventemitter3": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", - "dev": true - }, - "events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", - "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", - "dev": true - }, - "eventsource": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", - "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", - "dev": true, - "requires": { - "original": "^1.0.0" - } - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "execa": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", - "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - } - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "express": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", - "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", - "dev": true, - "requires": { - "accepts": "~1.3.5", - "array-flatten": "1.1.1", - "body-parser": "1.18.3", - "content-disposition": "0.5.2", - "content-type": "~1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.1.1", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.4", - "qs": "6.5.2", - "range-parser": "~1.2.0", - "safe-buffer": "5.1.2", - "send": "0.16.2", - "serve-static": "1.13.2", - "setprototypeof": "1.1.0", - "statuses": "~1.4.0", - "type-is": "~1.6.16", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "external-editor": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", - "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", - "dev": true - }, - "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "figgy-pudding": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", - "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", - "dev": true - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-loader": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz", - "integrity": "sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==", - "dev": true, - "requires": { - "loader-utils": "^1.0.2", - "schema-utils": "^1.0.0" - } - }, - "file-saver": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.2.tgz", - "integrity": "sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw==" - }, - "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=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "finalhandler": { - "version": "1.1.1", - "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", - "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.4.0", - "unpipe": "~1.0.0" - } - }, - "find-cache-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", - "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^1.0.0", - "pkg-dir": "^2.0.0" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "flatted": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", - "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", - "dev": true - }, - "flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" - } - }, - "follow-redirects": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz", - "integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==", - "dev": true, - "requires": { - "debug": "^3.2.6" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "font-awesome": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", - "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=" - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "dev": true, - "requires": { - "for-in": "^1.0.1" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "dev": true - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "dev": true - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "fs-access": { - "version": "1.0.1", - "resolved": "http://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": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fs-minipass": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", - "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", - "dev": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", - "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", - "dev": true, - "optional": true, - "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true - }, - "minipass": { - "version": "2.3.5", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.2.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.10.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "5.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "dev": 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" - } - }, - "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" - } - }, - "gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", - "dev": true, - "optional": true, - "requires": { - "globule": "^1.0.0" - } - }, - "genfun": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", - "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==", - "dev": true - }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true - }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true - }, - "get-stream": { - "version": "3.0.0", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "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" - } - }, - "globule": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", - "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", - "dev": true, - "optional": true, - "requires": { - "glob": "~7.1.1", - "lodash": "~4.17.10", - "minimatch": "~3.0.2" - } - }, - "good-listener": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", - "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", - "optional": true, - "requires": { - "delegate": "^3.1.2" - } - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "handle-thing": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", - "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==", - "dev": true - }, - "handlebars": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", - "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", - "dev": true, - "requires": { - "neo-async": "^2.6.0", - "optimist": "^0.6.1", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "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-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-binary2": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", - "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", - "dev": true, - "requires": { - "isarray": "2.0.1" - }, - "dependencies": { - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "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=", - "dev": true - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "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" - } - }, - "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", - "dev": true - }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "html-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", - "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", - "dev": true - }, - "http-cache-semantics": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", - "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", - "dev": true - }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", - "dev": true - }, - "http-errors": { - "version": "1.6.3", - "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "http-parser-js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz", - "integrity": "sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==", - "dev": true - }, - "http-proxy": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", - "dev": true, - "requires": { - "eventemitter3": "^3.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "http-proxy-agent": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", - "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", - "dev": true, - "requires": { - "agent-base": "4", - "debug": "3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "http-proxy-middleware": { - "version": "0.18.0", - "resolved": "http://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", - "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", - "dev": true, - "requires": { - "http-proxy": "^1.16.2", - "is-glob": "^4.0.0", - "lodash": "^4.17.5", - "micromatch": "^3.1.9" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "dependencies": { - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", - "dev": true, - "requires": { - "ms": "^2.0.0" - } - }, - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", - "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", - "dev": true - }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", - "dev": true - }, - "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - }, - "ignore-walk": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", - "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", - "dev": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "image-size": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", - "dev": true, - "optional": true - }, - "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-from": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", - "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" - } - }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dev": true, - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", - "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", - "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - } - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "in-publish": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", - "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", - "dev": true, - "optional": true - }, - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, - "optional": true, - "requires": { - "repeating": "^2.0.0" - } - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "inquirer": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.1.tgz", - "integrity": "sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg==", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.0", - "figures": "^2.0.0", - "lodash": "^4.17.10", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.1.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "strip-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz", - "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==", - "dev": true, - "requires": { - "ansi-regex": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz", - "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==", - "dev": true - } - } - } - } - }, - "internal-ip": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-3.0.1.tgz", - "integrity": "sha512-NXXgESC2nNVtU+pqmC9e6R8B1GpKxzsAQhffvh5AL79qKnodd+L7tnEQmTiUAVngqLalPbSqRA7XGIEL5nCd0Q==", - "dev": true, - "requires": { - "default-gateway": "^2.6.0", - "ipaddr.js": "^1.5.2" - } - }, - "interpret": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", - "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", - "dev": true - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true - }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true - }, - "ipaddr.js": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", - "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", - "dev": true - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, - "requires": { - "is-path-inside": "^1.0.0" - } - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "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=", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul-api": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.1.tgz", - "integrity": "sha512-kVmYrehiwyeBAk/wE71tW6emzLiHGjYIiDrc8sfyty4F8M02/lrgXSm+R1kXysmF20zArvmZXjlE/mg24TVPJw==", - "dev": true, - "requires": { - "async": "^2.6.1", - "compare-versions": "^3.2.1", - "fileset": "^2.0.3", - "istanbul-lib-coverage": "^2.0.3", - "istanbul-lib-hook": "^2.0.3", - "istanbul-lib-instrument": "^3.1.0", - "istanbul-lib-report": "^2.0.4", - "istanbul-lib-source-maps": "^3.0.2", - "istanbul-reports": "^2.1.1", - "js-yaml": "^3.12.0", - "make-dir": "^1.3.0", - "minimatch": "^3.0.4", - "once": "^1.4.0" - }, - "dependencies": { - "istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz", - "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==", - "dev": true, - "requires": { - "@babel/generator": "^7.0.0", - "@babel/parser": "^7.0.0", - "@babel/template": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@babel/types": "^7.0.0", - "istanbul-lib-coverage": "^2.0.3", - "semver": "^5.5.0" - } - } - } - }, - "istanbul-instrumenter-loader": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.1.tgz", - "integrity": "sha512-a5SPObZgS0jB/ixaKSMdn6n/gXSrK2S6q/UfRJBT3e6gQmVjwZROTODQsYW5ZNwOu78hG62Y3fWlebaVOL0C+w==", - "dev": true, - "requires": { - "convert-source-map": "^1.5.0", - "istanbul-lib-instrument": "^1.7.3", - "loader-utils": "^1.1.0", - "schema-utils": "^0.3.0" - }, - "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, - "schema-utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", - "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", - "dev": true, - "requires": { - "ajv": "^5.0.0" - } - } - } - }, - "istanbul-lib-coverage": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz", - "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.3.tgz", - "integrity": "sha512-CLmEqwEhuCYtGcpNVJjLV1DQyVnIqavMLFHV/DP+np/g3qvdxu3gsPqYoJMXm15sN84xOlckFB3VNvRbf5yEgA==", - "dev": true, - "requires": { - "append-transform": "^1.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz", - "integrity": "sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==", - "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-report": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.4.tgz", - "integrity": "sha512-sOiLZLAWpA0+3b5w5/dq0cjm2rrNdAfHWaGhmn7XEFW6X++IV9Ohn+pnELAl9K3rfpaeBfbmH9JU5sejacdLeA==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^2.0.3", - "make-dir": "^1.3.0", - "supports-color": "^6.0.0" - }, - "dependencies": { - "istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", - "dev": true - } - } - }, - "istanbul-lib-source-maps": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.2.tgz", - "integrity": "sha512-JX4v0CiKTGp9fZPmoxpu9YEkPbEqCqBbO3403VabKjH+NRXo72HafD5UgnjTEqHL2SAjaZK1XDuDOkn6I5QVfQ==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.3", - "make-dir": "^1.3.0", - "rimraf": "^2.6.2", - "source-map": "^0.6.1" - }, - "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" - } - }, - "istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", - "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 - }, - "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 - } - } - }, - "istanbul-reports": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.1.1.tgz", - "integrity": "sha512-FzNahnidyEPBCI0HcufJoSEoKykesRlFcSzQqjH9x0+LC8tnnE/p/90PBLu8iZTxr8yYZNyTtiAujUqyN+CIxw==", - "dev": true, - "requires": { - "handlebars": "^4.1.0" - } - }, - "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": "3.3.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.3.0.tgz", - "integrity": "sha512-3/xSmG/d35hf80BEN66Y6g9Ca5l/Isdeg/j6zvbTYlTzeKinzmaTM4p9am5kYqOmE05D7s1t8FGjzdSnbUbceA==", - "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 - }, - "jquery": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz", - "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==" - }, - "js-base64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", - "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==", - "dev": true, - "optional": true - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "jsesc": { - "version": "1.3.0", - "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", - "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.2", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "jstree": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jstree/-/jstree-3.3.5.tgz", - "integrity": "sha1-nFeNsy0KZDd1zd2AIK1ZkvQRnBM=", - "requires": { - "jquery": ">=1.9.1" - } - }, - "jszip": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.5.tgz", - "integrity": "sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ==", - "dev": true, - "requires": { - "core-js": "~2.3.0", - "es6-promise": "~3.0.2", - "lie": "~3.1.0", - "pako": "~1.0.2", - "readable-stream": "~2.0.6" - }, - "dependencies": { - "core-js": { - "version": "2.3.0", - "resolved": "http://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", - "integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=", - "dev": true - }, - "es6-promise": { - "version": "3.0.2", - "resolved": "http://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", - "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=", - "dev": true - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, - "readable-stream": { - "version": "2.0.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" - } - }, - "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 - } - } - }, - "karma": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/karma/-/karma-4.0.1.tgz", - "integrity": "sha512-ind+4s03BqIXas7ZmraV3/kc5+mnqwCd+VDX1FndS6jxbt03kQKX2vXrWxNLuCjVYmhMwOZosAEKMM0a2q7w7A==", - "dev": true, - "requires": { - "bluebird": "^3.3.0", - "body-parser": "^1.16.1", - "braces": "^2.3.2", - "chokidar": "^2.0.3", - "colors": "^1.1.0", - "connect": "^3.6.0", - "core-js": "^2.2.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.0", - "flatted": "^2.0.0", - "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "http-proxy": "^1.13.0", - "isbinaryfile": "^3.0.0", - "lodash": "^4.17.11", - "log4js": "^4.0.0", - "mime": "^2.3.1", - "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": "2.1.1", - "source-map": "^0.6.1", - "tmp": "0.0.33", - "useragent": "2.3.0" - }, - "dependencies": { - "mime": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", - "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", - "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 - } - } - }, - "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.5", - "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.0.5.tgz", - "integrity": "sha512-yPvAlKtY3y+rKKWbOo0CzBMVTvJEeMOgbMXuVv3yWvS8YtYKC98AU9vFF0mVBZ2RP1E9SgS90+PT6Kf14P3S4w==", - "dev": true, - "requires": { - "istanbul-api": "^2.1.1", - "minimatch": "^3.0.4" - } - }, - "karma-firefox-launcher": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-1.1.0.tgz", - "integrity": "sha512-LbZ5/XlIXLeQ3cqnCbYLn+rOVhuMIK9aZwlP6eOLGzWdo1UVp7t6CN3DP4SafiRLjexKwHeKHDm0c38Mtd3VxA==", - "dev": true - }, - "karma-jasmine": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.2.tgz", - "integrity": "sha1-OU8rJf+0pkS5rabyLUQ+L9CIhsM=", - "dev": true - }, - "karma-jasmine-html-reporter": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.4.0.tgz", - "integrity": "sha512-0wxhwA8PLPpICZ4o2GRnPi67hf3JhfQm5WCB8nElh4qsE6wRNOTtrqooyBPNqI087Xr2SBhxLg5fU+BJ/qxRrw==", - "dev": true - }, - "karma-junit-reporter": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/karma-junit-reporter/-/karma-junit-reporter-1.2.0.tgz", - "integrity": "sha1-T5xAzt+xo5X4rvh2q/lhiZF8Y5Y=", - "dev": true, - "requires": { - "path-is-absolute": "^1.0.0", - "xmlbuilder": "8.2.2" - } - }, - "karma-source-map-support": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.3.0.tgz", - "integrity": "sha512-HcPqdAusNez/ywa+biN4EphGz62MmQyPggUsDfsHqa7tSe4jdsxgvTKuDfIazjL+IOxpVWyT7Pr4dhAV+sxX5Q==", - "dev": true, - "requires": { - "source-map-support": "^0.5.5" - } - }, - "katex": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.11.1.tgz", - "integrity": "sha512-5oANDICCTX0NqYIyAiFCCwjQ7ERu3DQG2JFHLbYOf+fXaMoH8eg/zOq5WSYJsKMi/QebW+Eh3gSM+oss1H/bww==", - "requires": { - "commander": "^2.19.0" - }, - "dependencies": { - "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==" - } - } - }, - "killable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", - "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, - "requires": { - "invert-kv": "^1.0.0" - } - }, - "leaflet": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.3.4.tgz", - "integrity": "sha512-FYL1LGFdj6v+2Ifpw+AcFIuIOqjNggfoLUwuwQv6+3sS21Za7Wvapq+LhbSE4NDXrEj6eYnW3y7LsaBICpyXtw==" - }, - "leaflet.markercluster": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/leaflet.markercluster/-/leaflet.markercluster-1.4.1.tgz", - "integrity": "sha512-ZSEpE/EFApR0bJ1w/dUGwTSUvWlpalKqIzkaYdYB7jaftQA/Y2Jav+eT4CMtEYFj+ZK4mswP13Q2acnPBnhGOw==" - }, - "less": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/less/-/less-3.9.0.tgz", - "integrity": "sha512-31CmtPEZraNUtuUREYjSqRkeETFdyEHSEPAGq4erDlUXtda7pzNmctdljdIagSb589d/qXGWiiP31R5JVf+v0w==", - "dev": true, - "requires": { - "clone": "^2.1.2", - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "mime": "^1.4.1", - "mkdirp": "^0.5.0", - "promise": "^7.1.1", - "request": "^2.83.0", - "source-map": "~0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } - } - }, - "less-loader": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-4.1.0.tgz", - "integrity": "sha512-KNTsgCE9tMOM70+ddxp9yyt9iHqgmSs0yTZc5XH5Wo+g80RWRIYNqE58QJKm/yMud5wZEvz50ugRDuzVIkyahg==", - "dev": true, - "requires": { - "clone": "^2.1.1", - "loader-utils": "^1.1.0", - "pify": "^3.0.0" - } - }, - "license-webpack-plugin": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.1.0.tgz", - "integrity": "sha512-vDiBeMWxjE9n6TabQ9J4FH8urFdsRK0Nvxn1cit9biCiR9aq1zBR0X2BlAkEiIG6qPamLeU0GzvIgLkrFc398A==", - "dev": true, - "requires": { - "@types/webpack-sources": "^0.1.5", - "webpack-sources": "^1.2.0" - } - }, - "lie": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", - "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", - "dev": true, - "requires": { - "immediate": "~3.0.5" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "http://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": { - "pify": { - "version": "2.3.0", - "resolved": "http://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.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, - "lodash.assign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", - "dev": true, - "optional": true - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "dev": true - }, - "lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", - "dev": true, - "optional": true - }, - "lodash.tail": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", - "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=", - "dev": true - }, - "log4js": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.0.2.tgz", - "integrity": "sha512-KE7HjiieVDPPdveA3bJZSuu0n8chMkFl8mIoisBFxwEJ9FmXe4YzNuiqSwYUiR1K8q8/5/8Yd6AClENY1RA9ww==", - "dev": true, - "requires": { - "date-format": "^2.0.0", - "debug": "^3.1.0", - "flatted": "^2.0.0", - "rfdc": "^1.1.2", - "streamroller": "^1.0.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.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "loglevel": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", - "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", - "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, - "optional": true, - "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - } - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "magic-string": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.2.tgz", - "integrity": "sha512-iLs9mPjh9IuTtRsqqhNGYcZXGei0Nh/A4xirrsqW7c+QhKVFL2vm7U09ru6cHRD22azaP/wMDgI+HCqbETMTtg==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.4" - } - }, - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "make-error": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", - "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", - "dev": true - }, - "make-fetch-happen": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-4.0.1.tgz", - "integrity": "sha512-7R5ivfy9ilRJ1EMKIOziwrns9fGeAD4bAha8EB7BIiBBLHm2KeTUGCrICFt2rbHfzheTLynv50GnNTK1zDTrcQ==", - "dev": true, - "requires": { - "agentkeepalive": "^3.4.1", - "cacache": "^11.0.1", - "http-cache-semantics": "^3.8.1", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", - "lru-cache": "^4.1.2", - "mississippi": "^3.0.0", - "node-fetch-npm": "^2.0.2", - "promise-retry": "^1.1.1", - "socks-proxy-agent": "^4.0.0", - "ssri": "^6.0.0" - }, - "dependencies": { - "cacache": { - "version": "11.3.2", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", - "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", - "dev": true, - "requires": { - "bluebird": "^3.5.3", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - } - } - }, - "mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true - } - } - }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "marked": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", - "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==" - }, - "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": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true - }, - "mem": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz", - "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^1.0.0", - "p-is-promise": "^2.0.0" - } - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "meow": { - "version": "3.7.0", - "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true, - "optional": true, - "requires": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", - "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", - "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "optional": true - }, - "mime-db": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", - "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==", - "dev": true - }, - "mime-types": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", - "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", - "dev": true, - "requires": { - "mime-db": "~1.38.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "mini-css-extract-plugin": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz", - "integrity": "sha512-IuaLjruM0vMKhUUT51fQdQzBYTX49dLj8w68ALEAe2A4iYNpIC4eMac67mt3NzycvjOlf07/kYxJDc0RTl1Wqw==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "schema-utils": "^1.0.0", - "webpack-sources": "^1.1.0" - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "minipass": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", - "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - }, - "dependencies": { - "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true - } - } - }, - "minizlib": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", - "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", - "dev": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mississippi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", - "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^2.0.1", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mixin-object": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", - "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", - "dev": true, - "requires": { - "for-in": "^0.1.3", - "is-extendable": "^0.1.1" - }, - "dependencies": { - "for-in": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", - "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=", - "dev": true - } - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "http://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.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" - }, - "move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", - "dev": true, - "requires": { - "dns-packet": "^1.3.1", - "thunky": "^1.0.2" - } - }, - "multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", - "dev": true - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, - "nan": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", - "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", - "dev": true, - "optional": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", - "dev": true - }, - "neo-async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", - "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", - "dev": true - }, - "ng-mocks": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/ng-mocks/-/ng-mocks-7.6.0.tgz", - "integrity": "sha512-Zorpd5I6KmvTtiYwcjymzCaortznMZr5CRB737XaNheITTUb2rVLUoEBk1dwQE3b/Cp5sByuS85fzwJRvjEXKQ==" - }, - "ngx-markdown": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/ngx-markdown/-/ngx-markdown-8.2.1.tgz", - "integrity": "sha512-59LG8rEoOwDsZyyJckp+QDnW/c5wMaRpNkb6TWktlBVTfQKyAYHr6BuSskVbZ4y8nsj54UQg0CDFLBOfUiqOwA==", - "requires": { - "@types/marked": "^0.6.5", - "katex": "^0.11.1", - "marked": "^0.7.0", - "prismjs": "^1.16.0", - "tslib": "^1.9.0" - } - }, - "ngx-moment": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ngx-moment/-/ngx-moment-3.3.0.tgz", - "integrity": "sha512-6fpllpJqLfjRWboOhphgeEYt+rzIA9O29rG5QWCebRt2X0uNk4P93sLEb0S8lbDF0dEp2NOC3UOD+xoCVlJQhA==", - "requires": { - "tslib": "^1.9.0" - } - }, - "ngx-speculoos": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ngx-speculoos/-/ngx-speculoos-1.1.0.tgz", - "integrity": "sha512-gtQ6t3KOgu6TCscnOOFl4PmV35R36vgafhofJwqIcIrWBCcPFwKYTAyf5TUwkrW7GEayxEqUREGO6g4rBfvNRw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node-fetch-npm": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz", - "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==", - "dev": true, - "requires": { - "encoding": "^0.1.11", - "json-parse-better-errors": "^1.0.0", - "safe-buffer": "^5.1.1" - } - }, - "node-forge": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", - "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==", - "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, - "optional": true, - "requires": { - "fstream": "^1.0.0", - "glob": "^7.0.3", - "graceful-fs": "^4.1.2", - "mkdirp": "^0.5.0", - "nopt": "2 || 3", - "npmlog": "0 || 1 || 2 || 3 || 4", - "osenv": "0", - "request": "^2.87.0", - "rimraf": "2", - "semver": "~5.3.0", - "tar": "^2.0.0", - "which": "1" - }, - "dependencies": { - "semver": { - "version": "5.3.0", - "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "dev": true, - "optional": true - } - } - }, - "node-libs-browser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz", - "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==", - "dev": true, - "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.0", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "0.0.4" - }, - "dependencies": { - "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.8", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.8.tgz", - "integrity": "sha512-gQm+K9mGCiT/NXHy+V/ZZS1N/LOaGGqRAAJJs3X9Ah1g+CIbRcBgNyoNYQ+SEtcyAtB9KqDruu+fF7nWjsqRaA==", - "dev": true, - "requires": { - "semver": "^5.3.0" - } - }, - "node-sass": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.11.0.tgz", - "integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==", - "dev": true, - "optional": true, - "requires": { - "async-foreach": "^0.1.3", - "chalk": "^1.1.1", - "cross-spawn": "^3.0.0", - "gaze": "^1.0.0", - "get-stdin": "^4.0.1", - "glob": "^7.0.3", - "in-publish": "^2.0.0", - "lodash.assign": "^4.2.0", - "lodash.clonedeep": "^4.3.2", - "lodash.mergewith": "^4.6.0", - "meow": "^3.7.0", - "mkdirp": "^0.5.1", - "nan": "^2.10.0", - "node-gyp": "^3.8.0", - "npmlog": "^4.0.0", - "request": "^2.88.0", - "sass-graph": "^2.2.4", - "stdout-stream": "^1.4.0", - "true-case-path": "^1.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true, - "optional": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "optional": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true, - "optional": true - } - } - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "optional": true, - "requires": { - "abbrev": "1" - } - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "npm-bundled": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", - "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", - "dev": true - }, - "npm-package-arg": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.0.tgz", - "integrity": "sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.6.0", - "osenv": "^0.1.5", - "semver": "^5.5.0", - "validate-npm-package-name": "^3.0.0" - } - }, - "npm-packlist": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz", - "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", - "dev": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npm-pick-manifest": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-2.2.3.tgz", - "integrity": "sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1", - "npm-package-arg": "^6.0.0", - "semver": "^5.4.1" - } - }, - "npm-registry-fetch": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-3.9.0.tgz", - "integrity": "sha512-srwmt8YhNajAoSAaDWndmZgx89lJwIZ1GWxOuckH4Coek4uHv5S+o/l9FLQe/awA+JwTnj4FJHldxhlXdZEBmw==", - "dev": true, - "requires": { - "JSONStream": "^1.3.4", - "bluebird": "^3.5.1", - "figgy-pudding": "^3.4.1", - "lru-cache": "^4.1.3", - "make-fetch-happen": "^4.0.1", - "npm-package-arg": "^6.1.0" - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.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=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "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.1", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", - "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "opn": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.4.0.tgz", - "integrity": "sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==", - "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 - } - } - }, - "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": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-locale": { - "version": "1.4.0", - "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, - "optional": true, - "requires": { - "lcid": "^1.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, - "p-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.0.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz", - "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==", - "dev": true - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", - "dev": true - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "pacote": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.4.0.tgz", - "integrity": "sha512-WQ1KL/phGMkedYEQx9ODsjj7xvwLSpdFJJdEXrLyw5SILMxcTNt5DTxT2Z93fXuLFYJBlZJdnwdalrQdB/rX5w==", - "dev": true, - "requires": { - "bluebird": "^3.5.3", - "cacache": "^11.3.2", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.1.0", - "glob": "^7.1.3", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^4.0.1", - "minimatch": "^3.0.4", - "minipass": "^2.3.5", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "normalize-package-data": "^2.4.0", - "npm-package-arg": "^6.1.0", - "npm-packlist": "^1.1.12", - "npm-pick-manifest": "^2.2.3", - "npm-registry-fetch": "^3.8.0", - "osenv": "^0.1.5", - "promise-inflight": "^1.0.1", - "promise-retry": "^1.1.1", - "protoduck": "^5.0.1", - "rimraf": "^2.6.2", - "safe-buffer": "^5.1.2", - "semver": "^5.6.0", - "ssri": "^6.0.1", - "tar": "^4.4.8", - "unique-filename": "^1.1.1", - "which": "^1.3.1" - }, - "dependencies": { - "cacache": { - "version": "11.3.2", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", - "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", - "dev": true, - "requires": { - "bluebird": "^3.5.3", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "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" - } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "tar": { - "version": "4.4.8", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", - "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", - "dev": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true - } - } - }, - "pako": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.8.tgz", - "integrity": "sha512-6i0HVbUfcKaTv+EG8ZTr75az7GFXcLYk9UyLEg7Notv/Ma+z/UG3TCoz6GiNeOrn1E/e63I0X/Hpw18jHOTUnA==", - "dev": true - }, - "parallel-transform": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", - "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", - "dev": true, - "requires": { - "cyclist": "~0.2.2", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - } - }, - "parse-asn1": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", - "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", - "dev": true, - "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true - }, - "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.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", - "dev": true - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pbkdf2": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", - "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", - "dev": true, - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - }, - "popper.js": { - "version": "1.14.6", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.6.tgz", - "integrity": "sha512-AGwHGQBKumlk/MDfrSOf0JHhJCImdDMcGNoqKmKkU+68GFazv3CQ6q9r7Ja1sKDZmYWTckY/uLyEznheTDycnA==" - }, - "portfinder": { - "version": "1.0.20", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz", - "integrity": "sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==", - "dev": true, - "requires": { - "async": "^1.5.2", - "debug": "^2.2.0", - "mkdirp": "0.5.x" - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - } - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "postcss": { - "version": "7.0.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz", - "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "postcss-import": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.1.tgz", - "integrity": "sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw==", - "dev": true, - "requires": { - "postcss": "^7.0.1", - "postcss-value-parser": "^3.2.3", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - } - }, - "postcss-load-config": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.0.0.tgz", - "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==", - "dev": true, - "requires": { - "cosmiconfig": "^4.0.0", - "import-cwd": "^2.0.0" - } - }, - "postcss-loader": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", - "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "postcss": "^7.0.0", - "postcss-load-config": "^2.0.0", - "schema-utils": "^1.0.0" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "prismjs": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.17.1.tgz", - "integrity": "sha512-PrEDJAFdUGbOP6xK/UsfkC5ghJsPJviKgnQOoxaDbBjwc8op68Quupwt1DeAFoG8GImPhiKXAvvsH7wDSLsu1Q==", - "requires": { - "clipboard": "^2.0.0" - } - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true - }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "dev": true, - "optional": true, - "requires": { - "asap": "~2.0.3" - } - }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true - }, - "promise-retry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", - "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", - "dev": true, - "requires": { - "err-code": "^1.0.0", - "retry": "^0.10.0" - } - }, - "protoduck": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz", - "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", - "dev": true, - "requires": { - "genfun": "^5.0.0" - } - }, - "protractor": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.1.tgz", - "integrity": "sha512-ORey5ewQMYiXQxcQohsqEiKYOg/r5yJoJbt0tuROmmgajdg/CA3gTOZNIFJncUVMAJIk5YFqBBLUjKVmQO6tfA==", - "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.1.0", - "webdriver-manager": "^12.0.6" - }, - "dependencies": { - "@types/node": { - "version": "6.14.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.3.tgz", - "integrity": "sha512-V2VrQBCKo4U0rni6tW4AASRDqIO5ZTLDN/Xzrm4mNBr9SGQYZ+7zZJn+hMs89Q8ZCIHzp4aWQPyCpK+rux1YGA==", - "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": "http://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": "http://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" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "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.1", - "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.1.tgz", - "integrity": "sha512-L9TEQmZs6JbMMRQI1w60mfps265/NCr0toYJl7p/R2OAk6oXAfwI6jqYP7EWae+d7Ad2S2Aj4+rzxoSjqk3ZuA==", - "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.4", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", - "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", - "dev": true, - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.8.0" - } - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "psl": { - "version": "1.1.31", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", - "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", - "dev": true - }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "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 - }, - "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.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.0.tgz", - "integrity": "sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg==", - "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.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", - "dev": true - }, - "raw-body": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", - "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", - "dev": true, - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.3", - "iconv-lite": "0.4.23", - "unpipe": "1.0.0" - } - }, - "raw-loader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-1.0.0.tgz", - "integrity": "sha512-Uqy5AqELpytJTRxYT4fhltcKPj0TyaEpzJDcGz7DFJi+pQOOi3GjR/DOdxTkTsF+NzhnldIoG6TORaBlInUuqA==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "schema-utils": "^1.0.0" - } - }, - "read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", - "dev": true, - "requires": { - "pify": "^2.3.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "http://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" - }, - "dependencies": { - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - }, - "dependencies": { - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "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" - } - } - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "requires": { - "resolve": "^1.1.6" - } - }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true, - "optional": true, - "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" - } - }, - "reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", - "dev": true - }, - "regenerate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", - "dev": true - }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "regexpu-core": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", - "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", - "dev": true, - "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "http://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "http://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==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "^1.0.0" - } - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "resolve": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", - "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" - } - }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "retry": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", - "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", - "dev": true - }, - "rfdc": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.2.tgz", - "integrity": "sha512-92ktAgvZhBzYTIK0Mja9uen5q5J3NRVMoDkJL2VMwq6SXjVCgqvQeVP2XAaUY6HT+XpQYeLSjb3UoitBryKmdA==", - "dev": true - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } - }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "dev": true, - "requires": { - "aproba": "^1.1.1" - } - }, - "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", - "requires": { - "tslib": "^1.9.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "sass-graph": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", - "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", - "dev": true, - "optional": true, - "requires": { - "glob": "^7.0.0", - "lodash": "^4.0.0", - "scss-tokenizer": "^0.2.3", - "yargs": "^7.0.0" - } - }, - "sass-loader": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.1.0.tgz", - "integrity": "sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==", - "dev": true, - "requires": { - "clone-deep": "^2.0.1", - "loader-utils": "^1.0.1", - "lodash.tail": "^4.1.1", - "neo-async": "^2.5.0", - "pify": "^3.0.0", - "semver": "^5.5.0" - } - }, - "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": "http://registry.npmjs.org/sax/-/sax-0.5.8.tgz", - "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=", - "dev": true - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, - "scss-tokenizer": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", - "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", - "dev": true, - "optional": true, - "requires": { - "js-base64": "^2.1.8", - "source-map": "^0.4.2" - }, - "dependencies": { - "source-map": { - "version": "0.4.4", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "optional": true, - "requires": { - "amdefine": ">=0.0.4" - } - } - } - }, - "select": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", - "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=", - "optional": true - }, - "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.4", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.4.tgz", - "integrity": "sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==", - "dev": true, - "requires": { - "node-forge": "0.7.5" - } - }, - "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", - "dev": true - }, - "semver-dsl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz", - "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", - "dev": true, - "requires": { - "semver": "^5.3.0" - } - }, - "semver-intersect": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz", - "integrity": "sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ==", - "dev": true, - "requires": { - "semver": "^5.0.0" - } - }, - "send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", - "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" - }, - "dependencies": { - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", - "dev": true - } - } - }, - "serialize-javascript": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.6.1.tgz", - "integrity": "sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw==", - "dev": true - }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - } - }, - "serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", - "dev": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.2", - "send": "0.16.2" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - }, - "sha.js": { - "version": "2.4.11", - "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "shallow-clone": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-1.0.0.tgz", - "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==", - "dev": true, - "requires": { - "is-extendable": "^0.1.1", - "kind-of": "^5.0.0", - "mixin-object": "^2.0.1" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "shelljs": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz", - "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==", - "dev": true, - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - } - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, - "smart-buffer": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.2.tgz", - "integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw==", - "dev": true - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "socket.io": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", - "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", - "dev": true, - "requires": { - "debug": "~3.1.0", - "engine.io": "~3.2.0", - "has-binary2": "~1.0.2", - "socket.io-adapter": "~1.1.0", - "socket.io-client": "2.1.1", - "socket.io-parser": "~3.2.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" - } - } - } - }, - "socket.io-adapter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", - "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=", - "dev": true - }, - "socket.io-client": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", - "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", - "dev": true, - "requires": { - "backo2": "1.0.2", - "base64-arraybuffer": "0.1.5", - "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "~3.1.0", - "engine.io-client": "~3.2.0", - "has-binary2": "~1.0.2", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "object-component": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "socket.io-parser": "~3.2.0", - "to-array": "0.1.4" - }, - "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" - } - } - } - }, - "socket.io-parser": { - "version": "3.2.0", - "resolved": "http://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", - "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", - "dev": true, - "requires": { - "component-emitter": "1.2.1", - "debug": "~3.1.0", - "isarray": "2.0.1" - }, - "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" - } - }, - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "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.3.0", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.3.0.tgz", - "integrity": "sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==", - "dev": true, - "requires": { - "debug": "^3.2.5", - "eventsource": "^1.0.7", - "faye-websocket": "~0.11.1", - "inherits": "^2.0.3", - "json3": "^3.3.2", - "url-parse": "^1.4.3" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "faye-websocket": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", - "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "socks": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.3.tgz", - "integrity": "sha512-+2r83WaRT3PXYoO/1z+RDEBE7Z2f9YcdQnJ0K/ncXXbV5gJ6wYfNAebYFYiiUjM6E4JyXnPY8cimwyvFYHVUUA==", - "dev": true, - "requires": { - "ip": "^1.1.5", - "smart-buffer": "4.0.2" - } - }, - "socks-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz", - "integrity": "sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw==", - "dev": true, - "requires": { - "agent-base": "~4.2.0", - "socks": "~2.2.0" - } - }, - "source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - }, - "source-map-loader": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.4.tgz", - "integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==", - "dev": true, - "requires": { - "async": "^2.5.0", - "loader-utils": "^1.1.0" - } - }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "dev": true, - "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz", - "integrity": "sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "sourcemap-codec": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.4.tgz", - "integrity": "sha512-CYAPYdBu34781kLHkaW3m6b/uUSyMOC2R61gcYMWooeuaGtjof86ZA/8T+qVPPt7np1085CR9hmMGrySwEc8Xg==", - "dev": true - }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", - "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==", - "dev": true - }, - "spdy": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.0.tgz", - "integrity": "sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "readable-stream": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz", - "integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "speed-measure-webpack-plugin": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.0.tgz", - "integrity": "sha512-b9Yd0TrzceMVYSbuamM1sFsGM1oVfyFTM22gOoyLhymNvBVApuYpkdFOgYkKJpN/KhTpcCYcTGHg7X+FJ33Vvw==", - "dev": true, - "requires": { - "chalk": "^2.0.1" - } - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "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": "5.3.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", - "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.1" - } - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "stats-webpack-plugin": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/stats-webpack-plugin/-/stats-webpack-plugin-0.7.0.tgz", - "integrity": "sha512-NT0YGhwuQ0EOX+uPhhUcI6/+1Sq/pMzNuSCBVT4GbFl/ac6I/JZefBcjlECNfAb1t3GOx5dEj1Z7x0cAxeeVLQ==", - "dev": true, - "requires": { - "lodash": "^4.17.4" - } - }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true - }, - "stdout-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", - "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", - "dev": true, - "optional": true, - "requires": { - "readable-stream": "^2.0.1" - } - }, - "stream-browserify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "stream-each": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", - "dev": true, - "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - } - }, - "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "dev": true - }, - "streamroller": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.3.tgz", - "integrity": "sha512-P7z9NwP51EltdZ81otaGAN3ob+/F88USJE546joNq7bqRNTe6jc74fTBDyynxP4qpIfKlt/CesEYicuMzI0yJg==", - "dev": true, - "requires": { - "async": "^2.6.1", - "date-format": "^2.0.0", - "debug": "^3.1.0", - "fs-extra": "^7.0.0", - "lodash": "^4.17.10" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "http://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": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true, - "optional": true, - "requires": { - "get-stdin": "^4.0.1" - } - }, - "style-loader": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz", - "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "schema-utils": "^1.0.0" - } - }, - "stylus": { - "version": "0.54.5", - "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.5.tgz", - "integrity": "sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk=", - "dev": true, - "requires": { - "css-parse": "1.7.x", - "debug": "*", - "glob": "7.0.x", - "mkdirp": "0.5.x", - "sax": "0.5.x", - "source-map": "0.1.x" - }, - "dependencies": { - "glob": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", - "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.2", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "source-map": { - "version": "0.1.43", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", - "dev": true, - "requires": { - "amdefine": ">=0.0.4" - } - } - } - }, - "stylus-loader": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.2.tgz", - "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==", - "dev": true, - "requires": { - "loader-utils": "^1.0.2", - "lodash.clonedeep": "^4.5.0", - "when": "~3.6.x" - } - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "symbol-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.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz", - "integrity": "sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==", - "dev": true - }, - "tar": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", - "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", - "dev": true, - "optional": true, - "requires": { - "block-stream": "*", - "fstream": "^1.0.12", - "inherits": "2" - } - }, - "terser": { - "version": "3.16.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-3.16.1.tgz", - "integrity": "sha512-JDJjgleBROeek2iBcSNzOHLKsB/MdDf+E/BOAJ0Tk9r7p9/fVobfv7LMJ/g/k3v9SXdmjZnIlFd5nfn/Rt0Xow==", - "dev": true, - "requires": { - "commander": "~2.17.1", - "source-map": "~0.6.1", - "source-map-support": "~0.5.9" - }, - "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": { - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "cacache": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", - "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "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 - }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "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" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "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 - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", - "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.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" - } - }, - "terser": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.3.tgz", - "integrity": "sha512-0ikKraVtRDKGzHrzkCv5rUNDzqlhmhowOBqC0XqUHFpW+vJ45+20/IFBcebwKfiS2Z9fJin6Eo+F1zLZsxi8RA==", - "dev": true, - "requires": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - } - }, - "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" - } - }, - "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" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "through": { - "version": "2.3.8", - "resolved": "http://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.0.3", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.3.tgz", - "integrity": "sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==", - "dev": true - }, - "timers-browserify": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", - "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", - "dev": true, - "requires": { - "setimmediate": "^1.0.4" - } - }, - "tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", - "optional": true - }, - "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": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } - } - }, - "trait-ontology-widget": { - "version": "git+https://github.com/gnpis/trait-ontology-widget.git#6db245aa78ab66628ecd1897e349f3d92e9a2006", - "from": "git+https://github.com/gnpis/trait-ontology-widget.git#v2.2.1", - "requires": { - "jquery": "3.3.1", - "jstree": "3.3.5" - }, - "dependencies": { - "jquery": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", - "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==" - } - } - }, - "tree-kill": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz", - "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==", - "dev": true - }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true, - "optional": true - }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "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, - "optional": true, - "requires": { - "glob": "^7.1.2" - } - }, - "ts-node": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", - "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", - "dev": true, - "requires": { - "arrify": "^1.0.0", - "buffer-from": "^1.1.0", - "diff": "^3.1.0", - "make-error": "^1.1.1", - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "source-map-support": "^0.5.6", - "yn": "^2.0.0" - } - }, - "tslib": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.0.tgz", - "integrity": "sha512-BmndXUtiTn/VDDrJzQE7Mm22Ix3PxgLltW9bSNLoeCY31gnG2OPx0QqJnuc9oMIKioYrz487i6K9o4Pdn0j+Kg==" - }, - "tslint": { - "version": "5.11.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.11.0.tgz", - "integrity": "sha1-mPMMAurjzecAYgHkwzywi0hYHu0=", - "dev": true, - "requires": { - "babel-code-frame": "^6.22.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^3.2.0", - "glob": "^7.1.1", - "js-yaml": "^3.7.0", - "minimatch": "^3.0.4", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.8.0", - "tsutils": "^2.27.2" - } - }, - "tslint-eslint-rules": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/tslint-eslint-rules/-/tslint-eslint-rules-5.4.0.tgz", - "integrity": "sha512-WlSXE+J2vY/VPgIcqQuijMQiel+UtmXS+4nvK4ZzlDiqBfXse8FAvkNnTcYhnQyOTW5KFM+uRRGXxYhFpuBc6w==", - "dev": true, - "requires": { - "doctrine": "0.7.2", - "tslib": "1.9.0", - "tsutils": "^3.0.0" - }, - "dependencies": { - "tslib": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", - "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==", - "dev": true - }, - "tsutils": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.8.0.tgz", - "integrity": "sha512-XQdPhgcoTbCD8baXC38PQ0vpTZ8T3YrE+vR66YIj/xvDt1//8iAhafpIT/4DmvzzC1QFapEImERu48Pa01dIUA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - } - } - }, - "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-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.18" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "typescript": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.4.tgz", - "integrity": "sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==", - "dev": true - }, - "uglify-js": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.2.tgz", - "integrity": "sha512-uhRwZcANNWVLrxLfNFEdltoPNhECUR3lc+UdJoG9CBpMcSnKyWA94tc3eAujB1GcMY5Uwq8ZMp4qWpxWYDQmaA==", - "dev": true, - "optional": true, - "requires": { - "commander": "~2.20.3", - "source-map": "~0.6.1" - }, - "dependencies": { - "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, - "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 - } - } - }, - "ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", - "dev": true - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "dev": true, - "requires": { - "unique-slug": "^2.0.0" - } - }, - "unique-slug": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz", - "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4" - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - } - } - }, - "upath": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", - "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", - "dev": true - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, - "url-parse": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz", - "integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==", - "dev": true, - "requires": { - "querystringify": "^2.0.0", - "requires-port": "^1.0.0" - } - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "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" - } - }, - "util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "validate-npm-package-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", - "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", - "dev": true, - "requires": { - "builtins": "^1.0.3" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } - }, - "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.6.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", - "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", - "dev": true, - "requires": { - "chokidar": "^2.0.2", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" - } - }, - "wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "requires": { - "minimalistic-assert": "^1.0.0" - } - }, - "webdriver-js-extender": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", - "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==", - "dev": true, - "requires": { - "@types/selenium-webdriver": "^3.0.0", - "selenium-webdriver": "^3.0.1" - } - }, - "webpack": { - "version": "4.29.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.29.0.tgz", - "integrity": "sha512-pxdGG0keDBtamE1mNvT5zyBdx+7wkh6mh7uzMOo/uRQ/fhsdj5FXkh/j5mapzs060forql1oXqXN9HJGju+y7w==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-module-context": "1.7.11", - "@webassemblyjs/wasm-edit": "1.7.11", - "@webassemblyjs/wasm-parser": "1.7.11", - "acorn": "^6.0.5", - "acorn-dynamic-import": "^4.0.0", - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0", - "chrome-trace-event": "^1.0.0", - "enhanced-resolve": "^4.1.0", - "eslint-scope": "^4.0.0", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.3.0", - "loader-utils": "^1.1.0", - "memory-fs": "~0.4.1", - "micromatch": "^3.1.8", - "mkdirp": "~0.5.0", - "neo-async": "^2.5.0", - "node-libs-browser": "^2.0.0", - "schema-utils": "^0.4.4", - "tapable": "^1.1.0", - "terser-webpack-plugin": "^1.1.0", - "watchpack": "^1.5.0", - "webpack-sources": "^1.3.0" - }, - "dependencies": { - "schema-utils": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", - "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" - } - } - } - }, - "webpack-core": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz", - "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=", - "dev": true, - "requires": { - "source-list-map": "~0.1.7", - "source-map": "~0.4.1" - }, - "dependencies": { - "source-list-map": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", - "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=", - "dev": true - }, - "source-map": { - "version": "0.4.4", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "requires": { - "amdefine": ">=0.0.4" - } - } - } - }, - "webpack-dev-middleware": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.5.1.tgz", - "integrity": "sha512-4dwCh/AyMOYAybggUr8fiCkRnjVDp+Cqlr9c+aaNB3GJYgRGYQWJ1YX/WAKUNA9dPNHZ6QSN2lYDKqjKSI8Vqw==", - "dev": true, - "requires": { - "memory-fs": "~0.4.1", - "mime": "^2.3.1", - "range-parser": "^1.0.3", - "webpack-log": "^2.0.0" - }, - "dependencies": { - "mime": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", - "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", - "dev": true - } - } - }, - "webpack-dev-server": { - "version": "3.1.14", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.14.tgz", - "integrity": "sha512-mGXDgz5SlTxcF3hUpfC8hrQ11yhAttuUQWf1Wmb+6zo3x6rb7b9mIfuQvAPLdfDRCGRGvakBWHdHOa0I9p/EVQ==", - "dev": true, - "requires": { - "ansi-html": "0.0.7", - "bonjour": "^3.5.0", - "chokidar": "^2.0.0", - "compression": "^1.5.2", - "connect-history-api-fallback": "^1.3.0", - "debug": "^3.1.0", - "del": "^3.0.0", - "express": "^4.16.2", - "html-entities": "^1.2.0", - "http-proxy-middleware": "~0.18.0", - "import-local": "^2.0.0", - "internal-ip": "^3.0.1", - "ip": "^1.1.5", - "killable": "^1.0.0", - "loglevel": "^1.4.1", - "opn": "^5.1.0", - "portfinder": "^1.0.9", - "schema-utils": "^1.0.0", - "selfsigned": "^1.9.1", - "semver": "^5.6.0", - "serve-index": "^1.7.2", - "sockjs": "0.3.19", - "sockjs-client": "1.3.0", - "spdy": "^4.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^5.1.0", - "url": "^0.11.0", - "webpack-dev-middleware": "3.4.0", - "webpack-log": "^2.0.0", - "yargs": "12.0.2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "decamelize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", - "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", - "dev": true, - "requires": { - "xregexp": "4.0.0" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "mime": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", - "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", - "dev": true - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "p-limit": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", - "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", - "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", - "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" - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "webpack-dev-middleware": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.4.0.tgz", - "integrity": "sha512-Q9Iyc0X9dP9bAsYskAVJ/hmIZZQwf/3Sy4xCAZgL5cUkjZmUZLt4l5HpbST/Pdgjn3u6pE7u5OdGd1apgzRujA==", - "dev": true, - "requires": { - "memory-fs": "~0.4.1", - "mime": "^2.3.1", - "range-parser": "^1.0.3", - "webpack-log": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "yargs": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz", - "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^2.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^10.1.0" - } - }, - "yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", - "dev": true, - "requires": { - "camelcase": "^4.1.0" - } - } - } - }, - "webpack-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", - "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", - "dev": true, - "requires": { - "ansi-colors": "^3.0.0", - "uuid": "^3.3.2" - } - }, - "webpack-merge": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.1.tgz", - "integrity": "sha512-4p8WQyS98bUJcCvFMbdGZyZmsKuWjWVnVHnAS3FFg0HDaRVrPbkivx2RYCre8UiemD67RsiFFLfn4JhLAin8Vw==", - "dev": true, - "requires": { - "lodash": "^4.17.5" - } - }, - "webpack-sources": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz", - "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "webpack-subresource-integrity": { - "version": "1.1.0-rc.6", - "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.1.0-rc.6.tgz", - "integrity": "sha512-Az7y8xTniNhaA0620AV1KPwWOqawurVVDzQSpPAeR5RwNbL91GoBSJAAo9cfd+GiFHwsS5bbHepBw1e6Hzxy4w==", - "dev": true, - "requires": { - "webpack-core": "^0.6.8" - } - }, - "websocket-driver": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", - "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", - "dev": true, - "requires": { - "http-parser-js": ">=0.4.0", - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", - "dev": true - }, - "when": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz", - "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=", - "dev": true - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "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.6.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", - "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", - "dev": true, - "requires": { - "errno": "~0.1.7" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } - }, - "xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", - "dev": true, - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~9.0.1" - }, - "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": "9.0.7", - "resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", - "dev": true - } - } - }, - "xmlbuilder": { - "version": "8.2.2", - "resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz", - "integrity": "sha1-aSSGc0ELS6QuGmE2VR0pIjNap3M=", - "dev": true - }, - "xmlhttprequest-ssl": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", - "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", - "dev": true - }, - "xregexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", - "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", - "dev": true - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "yargs": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", - "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", - "dev": true, - "optional": true, - "requires": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^5.0.0" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true, - "optional": true - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true, - "optional": true - } - } - }, - "yargs-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", - "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", - "dev": true, - "optional": true, - "requires": { - "camelcase": "^3.0.0" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true, - "optional": true - } - } - }, - "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.8.29", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.29.tgz", - "integrity": "sha512-mla2acNCMkWXBD+c+yeUrBUrzOxYMNFdQ6FGfigGGtEVBPJx07BQeJekjt9DmH1FtZek4E9rE1eRR9qQpxACOQ==" - } - } -} diff --git a/frontend/src/app/facets/switch-button/switch-button.component.ts b/frontend/src/app/facets/switch-button/switch-button.component.ts index eac2ecdd..501fa65a 100644 --- a/frontend/src/app/facets/switch-button/switch-button.component.ts +++ b/frontend/src/app/facets/switch-button/switch-button.component.ts @@ -44,15 +44,18 @@ export class SwitchButtonComponent implements OnInit { } switchGermplasmResult() { + if (!this.germplasmDisplayCurrentState) { this.localCriteria = { ...this.localCriteria, - facetFields: ['types'] + facetFields: ['types'], + germplasmLists: null, + types: this.localCriteria.types ? this.localCriteria.types : ['Germplasm'] }; } else { this.localCriteria = { ...this.localCriteria, - facetFields: ['types', 'sources'] + facetFields: ['types', 'sources'], }; this.germplasmSearchCriteria$.next(DataDiscoveryCriteriaUtils.emptyGermplasmSearchCriteria()); } diff --git a/frontend/src/app/form/form.component.html b/frontend/src/app/form/form.component.html index bb81b123..ca5638c7 100644 --- a/frontend/src/app/form/form.component.html +++ b/frontend/src/app/form/form.component.html @@ -6,7 +6,8 @@ {{ tabs.GERMPLASM }} </a> </li> - <li class="nav-item"> + <li class="nav-item" + *ngIf="!displayGermplasmResult"> <a tabindex="1" class="nav-link trait {{ getNavClass(tabs.TRAIT) }}" (click)="activeTab=tabs.TRAIT"> diff --git a/frontend/src/app/form/form.component.ts b/frontend/src/app/form/form.component.ts index 36c0f5de..836ed4b8 100644 --- a/frontend/src/app/form/form.component.ts +++ b/frontend/src/app/form/form.component.ts @@ -32,6 +32,11 @@ export class FormComponent implements OnInit { return this.activeTab === tab ? 'visible' : 'd-none'; } ngOnInit(): void { - this.displayGermplasmResult$.subscribe(displayStatus => this.displayGermplasmResult = displayStatus); + this.displayGermplasmResult$.subscribe(displayStatus => { + this.displayGermplasmResult = displayStatus; + if (this.displayGermplasmResult) { + this.activeTab = Tabs.GERMPLASM; + } + }); } } -- GitLab From c4f7be3b76e61cf554aff5fc95ca151e9fa3d85b Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Thu, 12 Mar 2020 19:36:46 +0100 Subject: [PATCH 33/35] fix: Fix the bug that appear when select the tab trait (The band that indicate the number of results and the page navigator were not display on the good part of the page). GNP-4309 --- .../result-page/result-page.component.html | 193 ++++++++++-------- .../result-page/result-page.component.scss | 4 - 2 files changed, 103 insertions(+), 94 deletions(-) diff --git a/frontend/src/app/result-page/result-page.component.html b/frontend/src/app/result-page/result-page.component.html index ae0f77c4..bbcbfb6c 100644 --- a/frontend/src/app/result-page/result-page.component.html +++ b/frontend/src/app/result-page/result-page.component.html @@ -1,9 +1,18 @@ <h3 align="center" class="mb-4">{{ appTitle }}</h3> -<div class="row justify-content-end"> - <!-- Column for facets --> - <div class="col-lg-3 order-lg-first"> +<!-- Reset all button --> +<div class="text-right"> + <button type="button" class="btn btn-sm btn-danger mt-1" + (click)="resetAll()"> + Reset all + </button> +</div> + +<div class="row"> + + <!-- Column for facets --> + <div class="col-3"> <faidare-facets [criteria$]="criteria$" [facets]="facets" @@ -19,109 +28,113 @@ </faidare-facets> </div> - <!-- Column for form and results--> - <div class="col-lg-9"> - <!-- Reset all button --> - <div class="text-right reset-all-div"> - <button type="button" class="btn btn-sm btn-danger mt-1" - (click)="resetAll()"> - Reset all - </button> - </div> - <!-- Form --> - <faidare-form - #form - [criteria$]="criteria$" - [displayGermplasmResult$]="displayGermplasmResult$"> - </faidare-form> + <!-- Column for form and results --> + <div class="col-9"> + <div class="row"> - <!-- Loading spinner--> - <div class="text-center"> - <faidare-loading-spinner [loading]="loading"></faidare-loading-spinner> - </div> + <div class="col-12"> + <!-- Form --> + <faidare-form + #form + [criteria$]="criteria$" + [displayGermplasmResult$]="displayGermplasmResult$"> + </faidare-form> + </div> - <!-- No criteria selected --> - <div *ngIf="criteriaIsEmpty && !loading" - class="text-center text-muted mt-5 bolder"> - Select criteria to get results. - </div> + <div class="col-12"> + <!-- Loading spinner--> + <div class="text-center"> + <faidare-loading-spinner + [loading]="loading"></faidare-loading-spinner> + </div> - <!-- Display results when possible --> - <ng-container - *ngIf="documents.length && !criteriaIsEmpty && !loading && !displayGermplasmResult"> - <!-- Pagination status --> - <div class="container"> - <div class="row result align-content-center"> + <!-- No criteria selected --> + <div *ngIf="criteriaIsEmpty && !loading" + class="text-center text-muted mt-5 bolder"> + Select criteria to get results. + </div> + + + <!-- Display results when possible --> + <ng-container + *ngIf="documents.length && !criteriaIsEmpty && !loading && !displayGermplasmResult"> + <!-- Pagination status --> + <div class="container"> + <div class="row result align-content-center"> <span class="col-4 mt-2 bolder"> Results: </span> - <span *ngIf="pagination.totalResult" - class="col-8 text-right small text-muted mt-3"> + <span *ngIf="pagination.totalResult" + class="col-8 text-right small text-muted mt-3"> From {{ pagination.startResult | number }} - to {{ pagination.endResult | number }} - over {{ pagination.totalResult | number }} documents + to {{ pagination.endResult | number }} + over {{ pagination.totalResult | number }} documents <span *ngIf="pagination.totalResult > pagination.maxResults"> (limited to {{ pagination.maxResults | number }}) </span> </span> - </div> - </div> + </div> + </div> - <!--Top page navigator--> - <div class="d-flex justify-content-center mt-3" - *ngIf="pagination.totalPages > 1"> - <!-- we add 1 to the page because ngb-pagination is 1 based --> - <ngb-pagination [page]="pagination.currentPage + 1" - (pageChange)="changePage($event)" - [collectionSize]="resultCount()" - [pageSize]="pagination.pageSize" - [maxSize]="5" - [boundaryLinks]="true" - [ellipses]="false" - size="sm"> - </ngb-pagination> - </div> + <!--Top page navigator--> + <div class="d-flex justify-content-center mt-3" + *ngIf="pagination.totalPages > 1"> + <!-- we add 1 to the page because ngb-pagination is 1 based --> + <ngb-pagination [page]="pagination.currentPage + 1" + (pageChange)="changePage($event)" + [collectionSize]="resultCount()" + [pageSize]="pagination.pageSize" + [maxSize]="5" + [boundaryLinks]="true" + [ellipses]="false" + size="sm"> + </ngb-pagination> + </div> - <!-- Result document --> - <faidare-document - *ngFor="let document of documents" - [document]="document"> - </faidare-document> - - <!-- Pagination --> - <!--Bottom page navigator--> - <div class="d-flex justify-content-center mt-4 mb-5" - *ngIf="pagination.totalPages > 1"> - <!-- we add 1 to the page because ngb-pagination is 1 based --> - <ngb-pagination [page]="pagination.currentPage + 1" - (pageChange)="changePage($event)" - [collectionSize]="resultCount()" - [pageSize]="pagination.pageSize" - [maxSize]="5" - [boundaryLinks]="true" - [ellipses]="false" - size="sm"> - </ngb-pagination> - </div> - </ng-container> - - <ng-container *ngIf="displayGermplasmResult"> - <faidare-germplasm-result-page - [criteriaFromForm$]=criteria$ - [germplasmSearchCriteria$]="germplasmSearchCriteria$" - [germplasmFacets$]="germplasmfacets$"> - </faidare-germplasm-result-page> - </ng-container> - - <!-- Else we display a simple message when no result found --> - <div *ngIf="pagination.totalResult == 0 && !loading + <!-- Result document --> + <faidare-document + *ngFor="let document of documents" + [document]="document"> + </faidare-document> + + <!-- Pagination --> + <!--Bottom page navigator--> + <div class="d-flex justify-content-center mt-4 mb-5" + *ngIf="pagination.totalPages > 1"> + <!-- we add 1 to the page because ngb-pagination is 1 based --> + <ngb-pagination [page]="pagination.currentPage + 1" + (pageChange)="changePage($event)" + [collectionSize]="resultCount()" + [pageSize]="pagination.pageSize" + [maxSize]="5" + [boundaryLinks]="true" + [ellipses]="false" + size="sm"> + </ngb-pagination> + </div> + </ng-container> + + <!-- Display the table of the germplasm-result-page --> + <ng-container *ngIf="displayGermplasmResult"> + <faidare-germplasm-result-page + [criteriaFromForm$]=criteria$ + [germplasmSearchCriteria$]="germplasmSearchCriteria$" + [germplasmFacets$]="germplasmfacets$"> + </faidare-germplasm-result-page> + </ng-container> + + <!-- Else we display a simple message when no result found --> + <div *ngIf="pagination.totalResult == 0 && !loading && (!criteriaIsEmpty && !displayGermplasmResult)" - id="no-results" class="text-center"> - <div class="no-result-icon"> - <span class="fa fa-meh-o"></span> + id="no-results" class="text-center"> + <div class="no-result-icon"> + <span class="fa fa-meh-o"></span> + </div> + No results. + </div> + </div> - No results. </div> </div> </div> diff --git a/frontend/src/app/result-page/result-page.component.scss b/frontend/src/app/result-page/result-page.component.scss index 334361d5..7e5e1037 100644 --- a/frontend/src/app/result-page/result-page.component.scss +++ b/frontend/src/app/result-page/result-page.component.scss @@ -15,10 +15,6 @@ font-weight: bold; } -.reset-all-div { - height: 0; -} - // Loading spinner .lds-spinner { $scale: 0.75; -- GitLab From 04fbae40c009f5e8354da4931b89ef576f2c7824 Mon Sep 17 00:00:00 2001 From: Jerem-info <domyj03@gmail.com> Date: Tue, 31 Mar 2020 18:28:03 +0200 Subject: [PATCH 34/35] fix: Fix the bug when select sources from the germplasm result page to have more accurate suggestions. GNP-4309 --- .../facets/switch-button/switch-button.component.ts | 4 ++-- .../src/app/result-page/result-page.component.html | 1 + .../src/app/result-page/result-page.component.ts | 12 +++++++----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/facets/switch-button/switch-button.component.ts b/frontend/src/app/facets/switch-button/switch-button.component.ts index 501fa65a..ecdb42fa 100644 --- a/frontend/src/app/facets/switch-button/switch-button.component.ts +++ b/frontend/src/app/facets/switch-button/switch-button.component.ts @@ -45,7 +45,8 @@ export class SwitchButtonComponent implements OnInit { switchGermplasmResult() { - if (!this.germplasmDisplayCurrentState) { + this.displayGermplasmResult$.next(!this.germplasmDisplayCurrentState); + if (this.germplasmDisplayCurrentState) { this.localCriteria = { ...this.localCriteria, facetFields: ['types'], @@ -60,6 +61,5 @@ export class SwitchButtonComponent implements OnInit { this.germplasmSearchCriteria$.next(DataDiscoveryCriteriaUtils.emptyGermplasmSearchCriteria()); } this.criteria$.next(this.localCriteria); - this.displayGermplasmResult$.next(!this.germplasmDisplayCurrentState); } } diff --git a/frontend/src/app/result-page/result-page.component.html b/frontend/src/app/result-page/result-page.component.html index bbcbfb6c..265a7e38 100644 --- a/frontend/src/app/result-page/result-page.component.html +++ b/frontend/src/app/result-page/result-page.component.html @@ -23,6 +23,7 @@ <faidare-facets *ngIf="displayGermplasmResult && germplasmfacets.length" [facets]="germplasmfacets" + [criteria$]="criteria$" [displayGermplasmResult$]="displayGermplasmResult$" [germplasmSearchCriteria$]="germplasmSearchCriteria$"> </faidare-facets> diff --git a/frontend/src/app/result-page/result-page.component.ts b/frontend/src/app/result-page/result-page.component.ts index ce3bd67b..1005b863 100644 --- a/frontend/src/app/result-page/result-page.component.ts +++ b/frontend/src/app/result-page/result-page.component.ts @@ -98,11 +98,13 @@ export class ResultPageComponent implements OnInit { this.fetchDocumentsAndFacets(); this.criteriaIsEmpty = DataDiscoveryCriteriaUtils.checkCriteriaIsEmpty(newCriteria); - // Update URL query params - this.router.navigate(['.'], { - relativeTo: this.route, - queryParams: DataDiscoveryCriteriaUtils.toQueryParams(newCriteria) - }); + if (!this.displayGermplasmResult) { + // Update URL query params + this.router.navigate(['.'], { + relativeTo: this.route, + queryParams: DataDiscoveryCriteriaUtils.toQueryParams(newCriteria) + }); + } }); this.germplasmfacets$.subscribe(facets => { -- GitLab From bc271570fbe8be233c50b6035931ded6bc6868e3 Mon Sep 17 00:00:00 2001 From: jdestin <jeremy.destin@inra.fr> Date: Thu, 23 Jul 2020 17:51:44 +0200 Subject: [PATCH 35/35] Add font awesome csv logo instead of png logo. --- .../germplasm-result-page.component.html | 4 ++-- .../germplasm-result-page.component.scss | 5 +++++ frontend/src/assets/faidare/images/csv-logo.png | Bin 32999 -> 0 bytes frontend/src/index.html | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) delete mode 100644 frontend/src/assets/faidare/images/csv-logo.png diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html index b9a075fe..01f2ff47 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.html @@ -21,9 +21,9 @@ <button type="button" class="btn btn-outline-success mb-2" (click)="exportPlantMaterial(localCriteria)"> + + <span class="iconify" data-icon="fa-solid:file-csv" data-inline="false"></span> Export Plant Material list - <img src="assets/faidare/images/csv-logo.png" alt="csv logo" title="" - height="20px"/> </button> <!-- Loading spinner--> diff --git a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.scss b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.scss index 27258cfa..7579016a 100644 --- a/frontend/src/app/germplasm-result-page/germplasm-result-page.component.scss +++ b/frontend/src/app/germplasm-result-page/germplasm-result-page.component.scss @@ -40,3 +40,8 @@ label { display: block; } } + + +.iconify { + width: 35px; + height: 35px; } diff --git a/frontend/src/assets/faidare/images/csv-logo.png b/frontend/src/assets/faidare/images/csv-logo.png deleted file mode 100644 index abf670cf659222944f7e717d563ddea72715d75e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32999 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 diff --git a/frontend/src/index.html b/frontend/src/index.html index de174438..4f9431d7 100644 --- a/frontend/src/index.html +++ b/frontend/src/index.html @@ -7,6 +7,7 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="assets/favicon.ico"> + <script src="https://code.iconify.design/1/1.0.7/iconify.min.js"></script> </head> <body> <faidare-root></faidare-root> -- GitLab