[open-ils-commits] [GIT] Evergreen ILS branch master updated. d58e26fe9262bdc3853030fd5575582bd2a24e04

Evergreen Git git at git.evergreen-ils.org
Wed May 29 15:31:35 EDT 2019


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Evergreen ILS".

The branch, master has been updated
       via  d58e26fe9262bdc3853030fd5575582bd2a24e04 (commit)
       via  c367c3feec0bc9258f6cf9ba9ca621038f0c8f82 (commit)
       via  d18bf8c5e2892fb642b0800ea7c321f5dc490174 (commit)
       via  a09bc5d9b61bc970c6e7d541937ed870c30ed572 (commit)
       via  f52728d7d8230fb9506ec591db255cd4561faf5e (commit)
       via  58814097786c080b46ce3cd5d54f8bc21d23297f (commit)
       via  1ec8ea366ec372b4464d7115021de463eb3f0fd1 (commit)
       via  bb9f8f3a4db4a5f08b2bcc6d7f10885443731502 (commit)
       via  28f955619a2d2d4665ff31c8efd2918a0e7532dd (commit)
       via  311d189c820696609dd70fc63d626eee98221d39 (commit)
       via  e1fc8b6423f59dd64f53dc9152b3cd01f412cc4e (commit)
       via  6534a4e20037fcfd69c89039c17396be00e4cc77 (commit)
       via  4b55b65c5fdd482315fe635e4d130b7613bf60f6 (commit)
       via  099eabb17082a3d3a0a0d411dd591ba1ed40fcd7 (commit)
       via  0b671ab7d7e4d42a478530d19815bb370291ff83 (commit)
       via  65702937fe98b5fdba102636e7f264d028385a7a (commit)
       via  5e8476c7f5cac8f89a9c80e4433dadd0023d22c7 (commit)
       via  ae30dbc241436121f1056bdfce85cc8bbc24ee68 (commit)
       via  3785ed053676b4e0c873b77c26b6f6e54eb67f76 (commit)
       via  aab9e8d66f5e46a0eaf99ee78b0b46f6099071c5 (commit)
       via  a7a8afb9fe6b4ef1b7a1dfa58c42dd691aba795c (commit)
       via  3c3d0678b67425f3f9dcdca760cdbb6b33752203 (commit)
       via  dfbc0644b41da7b3433a44f109b3b40cdb2d8ea9 (commit)
       via  6e14b5a7622d2b1ebe3541404a02f8cc2aa9a755 (commit)
       via  4bea10e45c6b1eeea1e42d08a1534a6f199458b8 (commit)
      from  df482b4a2db781b6cfad780b8d4661b1079a475b (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit d58e26fe9262bdc3853030fd5575582bd2a24e04
Author: Bill Erickson <berickxx at gmail.com>
Date:   Fri Apr 5 12:00:00 2019 -0400

    LP1823367 Angular lint repairs
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-action.component.ts b/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-action.component.ts
index c84867fcf5..2cab7f9a20 100644
--- a/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-action.component.ts
+++ b/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-action.component.ts
@@ -50,7 +50,7 @@ export class GridToolbarActionComponent implements OnInit {
         }
 
         if (this.action) {
-            console.debug('toolbar [action] is deprecated.  use (onClick) instead.')
+            console.debug('toolbar [action] is deprecated. use (onClick) instead.');
         }
 
         this.toolbarAction.label = this.label;
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/conjoined.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/record/conjoined.component.ts
index 560214f99b..b4d6274973 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/conjoined.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/conjoined.component.ts
@@ -73,8 +73,9 @@ export class ConjoinedComponent implements OnInit {
         if (this.idsToUnlink.length === 0) { return; }
 
         try { // rejects on dismiss, which results in an Error
+            // TODO this will change with LP #1823041
             await this.confirmUnlink.open({size: 'sm'});
-        } catch (dismissed) {return;}
+        } catch (dismissed) { return; }
 
         const maps = [];
         this.pcrud.search('bpbcm',
@@ -90,7 +91,7 @@ export class ConjoinedComponent implements OnInit {
                         this.idsToUnlink = [];
                         this.grid.reload();
                     }
-                )
+                );
             }
         );
     }
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
index 15fbb071a6..7ffa692be2 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
@@ -890,7 +890,7 @@ export class HoldingsMaintenanceComponent implements OnInit {
             this.makeBookableDialog.open({}).then(
                 modified => {}, // No refresh needed
                 dismissed => {}
-            )
+            );
         }
     }
 }
diff --git a/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.ts b/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.ts
index 955e95aa5f..106f3640b2 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.ts
@@ -151,7 +151,7 @@ export class AdminPageComponent implements OnInit {
             try {
                 this.gridFilters = JSON.parse(filters);
             } catch (E) {
-                console.error('Invalid grid filters provided: ', filters)
+                console.error('Invalid grid filters provided: ', filters);
             }
         }
 

commit c367c3feec0bc9258f6cf9ba9ca621038f0c8f82
Author: Bill Erickson <berickxx at gmail.com>
Date:   Thu Apr 4 14:00:01 2019 -0400

    LP1821382 Initials Serials menu; button styling
    
    Record detail Serials drop-down added with subscription and MFHD manage
    actions.
    
    Quick receive action pending.
    
    Record action buttons use outline styling to reduce the amount of page
    color.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/actions.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/record/actions.component.html
index 7634103fd7..452c3feb92 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/actions.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/actions.component.html
@@ -16,19 +16,32 @@
 <div class="row ml-0 mr-0">
 
   <a target="_blank" href="/eg/opac/record/{{recId}}">
-    <button class="btn btn-info ml-1" i18n>View in Catalog</button>
+    <button class="btn btn-outline-primary ml-1" i18n>View in Catalog</button>
   </a>
 
   <a routerLink="/staff/catalog/hold/T" [queryParams]="{target: recId}">
-    <button class="btn btn-info ml-1" i18n>Place Hold</button>
+    <button class="btn btn-outline-primary ml-1" i18n>Place Hold</button>
   </a>
 
-  <button class="btn btn-info ml-1" (click)="addVolumes()" i18n>
+  <button class="btn btn-outline-primary ml-1" (click)="addVolumes()" i18n>
     Add Holdings
   </button>
 
   <div ngbDropdown placement="bottom-right" class="ml-1">
-    <button class="btn btn-info" id="actionsForDd" 
+    <button class="btn btn-outline-primary" id="actionsForSerials"
+      ngbDropdownToggle i18n>Serials</button>
+    <div ngbDropdownMenu aria-labelledby="actionsForSerials">
+      <a class="dropdown-item" href="/eg/staff/serials/{{recId}}" i18n>
+        Manage Subscriptions
+      </a>
+      <a class="dropdown-item" href="/eg/staff/serials/{{recId}}/manage-mfhds" i18n>
+        Manage MFHDs
+      </a>
+    </div>
+  </div>
+
+  <div ngbDropdown placement="bottom-right" class="ml-1">
+    <button class="btn btn-outline-primary" id="actionsForDd"
       ngbDropdownToggle i18n>Mark For...</button>
     <div ngbDropdownMenu aria-labelledby="actionsForDd">
       <button class="dropdown-item" (click)="mark('conjoined')">
@@ -62,7 +75,7 @@
   </div>
 
   <div ngbDropdown placement="bottom-right" class="ml-1">
-    <button class="btn btn-info" id="otherActionsForDd" 
+    <button class="btn btn-outline-primary" id="otherActionsForDd"
       ngbDropdownToggle i18n>Other Actions</button>
     <div ngbDropdownMenu aria-labelledby="otherActionsForDd">
       <button class="dropdown-item" (click)="recordBucketDialog.open({size: 'lg'})">

commit d18bf8c5e2892fb642b0800ea7c321f5dc490174
Author: Bill Erickson <berickxx at gmail.com>
Date:   Fri Mar 15 12:13:44 2019 -0400

    LP1820304 Ang catalog copy/volume hold links
    
    Add support to the Angular staff catalog record detail page copy table
    for placing holds on copies and call numbers for holdable copies.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/copies.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/record/copies.component.html
index dd5718e5b7..428fe4475b 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/copies.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/copies.component.html
@@ -16,7 +16,22 @@
 </ng-template>
 
 <ng-template #holdableTemplate let-copy="row" let-context="userContext">
-  <span *ngIf="context.holdable(copy)" i18n>Yes</span>
+  <span *ngIf="context.holdable(copy)" i18n>
+    <div class="border-bottom">
+      <a routerLink="/staff/catalog/hold/C"
+        [queryParams]="{target: copy.id}"
+        queryParamsHandling="merge" i18n>
+        Copy Hold
+      </a>
+    </div>
+    <div>
+      <a routerLink="/staff/catalog/hold/V"
+        [queryParams]="{target: copy.call_number}"
+        queryParamsHandling="merge" i18n>
+        Volume Hold
+      </a>
+    </div>
+  </span>
   <span *ngIf="!context.holdable(copy)" i18n>No</span>
 </ng-template>
 

commit a09bc5d9b61bc970c6e7d541937ed870c30ed572
Author: Bill Erickson <berickxx at gmail.com>
Date:   Tue Mar 12 12:56:47 2019 -0400

    LP1819745 Ang staff result page link repairs
    
    Title, Title-by-Jacket-image, Author, and Facet links in the Angular
    staff catalog now behave like regular browser links, which means they
    can used to open new tabs via control-click, etc.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/share/catalog/catalog-url.service.ts b/Open-ILS/src/eg2/src/app/share/catalog/catalog-url.service.ts
index a59b7fc2db..91922d48d5 100644
--- a/Open-ILS/src/eg2/src/app/share/catalog/catalog-url.service.ts
+++ b/Open-ILS/src/eg2/src/app/share/catalog/catalog-url.service.ts
@@ -4,6 +4,7 @@ import {OrgService} from '@eg/core/org.service';
 import {CatalogSearchContext, CatalogBrowseContext, CatalogMarcContext,
    CatalogTermContext, FacetFilter} from './search-context';
 import {CATALOG_CCVM_FILTERS} from './search-context';
+import {HashParams} from '@eg/share/util/hash-params';
 
 @Injectable()
 export class CatalogUrlService {
@@ -130,6 +131,10 @@ export class CatalogUrlService {
         return params;
     }
 
+    fromUrlHash(params: any): CatalogSearchContext {
+        return this.fromUrlParams(new HashParams(params));
+    }
+
     /**
      * Creates a new search context from the active route params.
      */
diff --git a/Open-ILS/src/eg2/src/app/share/catalog/catalog.service.ts b/Open-ILS/src/eg2/src/app/share/catalog/catalog.service.ts
index 55fd18e188..9cff2c4e5d 100644
--- a/Open-ILS/src/eg2/src/app/share/catalog/catalog.service.ts
+++ b/Open-ILS/src/eg2/src/app/share/catalog/catalog.service.ts
@@ -1,6 +1,6 @@
 import {Injectable, EventEmitter} from '@angular/core';
 import {Observable} from 'rxjs';
-import {mergeMap, map, tap} from 'rxjs/operators';
+import {map, tap} from 'rxjs/operators';
 import {OrgService} from '@eg/core/org.service';
 import {UnapiService} from '@eg/share/catalog/unapi.service';
 import {IdlService, IdlObject} from '@eg/core/idl.service';
diff --git a/Open-ILS/src/eg2/src/app/share/catalog/search-context.ts b/Open-ILS/src/eg2/src/app/share/catalog/search-context.ts
index 222536f69e..ef0fd552ea 100644
--- a/Open-ILS/src/eg2/src/app/share/catalog/search-context.ts
+++ b/Open-ILS/src/eg2/src/app/share/catalog/search-context.ts
@@ -1,7 +1,6 @@
 import {OrgService} from '@eg/core/org.service';
 import {IdlObject} from '@eg/core/idl.service';
 import {Pager} from '@eg/share/util/pager';
-import {Params} from '@angular/router';
 
 // CCVM's we care about in a catalog context
 // Don't fetch them all because there are a lot.
diff --git a/Open-ILS/src/eg2/src/app/share/util/hash-params.ts b/Open-ILS/src/eg2/src/app/share/util/hash-params.ts
new file mode 100644
index 0000000000..86a7bb5e2a
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/share/util/hash-params.ts
@@ -0,0 +1,29 @@
+import {ParamMap} from '@angular/router';
+
+
+/**
+ * Class to map a generic hash to an Angular ParamMap.
+ */
+export class HashParams implements ParamMap {
+    private params: {[key: string]: any[]};
+
+    public get keys(): string[] {
+        return Object.keys(this.params);
+    }
+
+    constructor(params: {[key: string]: any}) {
+        this.params = params || {};
+    }
+
+    has(key: string): boolean {
+        return key in this.params;
+    }
+
+    get(key: string): string | null {
+        return this.has(key) ? [].concat(this.params[key])[0] : null;
+    }
+
+    getAll(key: string): string[] {
+        return this.has(key) ? [].concat(this.params[key]) : [];
+    }
+}
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts
index 3b4f6962b6..3c1ba9579a 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts
@@ -47,6 +47,11 @@ export class StaffCatalogService {
         this.applySearchDefaults();
     }
 
+    cloneContext(context: CatalogSearchContext): CatalogSearchContext {
+        const params: any = this.catUrl.toUrlParams(context);
+        return this.catUrl.fromUrlHash(params);
+    }
+
     applySearchDefaults(): void {
         if (!this.searchContext.searchOrg) {
             this.searchContext.searchOrg =
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/result/facets.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/result/facets.component.html
index 9681747043..2c2cb14cdb 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/result/facets.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/result/facets.component.html
@@ -26,8 +26,8 @@
                 <div class="row">
                   <div class="col-lg-9">
                     <a class="card-link"
-                      href='javascript:;'
-                      (click)="applyFacet(facetConf.facetClass, name, value.value)">
+                      routerLink="/staff/catalog/search"
+                      [queryParams]="getFacetUrlParams(facetConf.facetClass, name, value.value)">
                       {{value.value}}
                     </a>
                   </div>
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/result/facets.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/result/facets.component.ts
index f16215a65f..aacb27c3bc 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/result/facets.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/result/facets.component.ts
@@ -1,5 +1,6 @@
 import {Component, OnInit, Input} from '@angular/core';
 import {CatalogService} from '@eg/share/catalog/catalog.service';
+import {CatalogUrlService} from '@eg/share/catalog/catalog-url.service';
 import {CatalogSearchContext, FacetFilter} from '@eg/share/catalog/search-context';
 import {StaffCatalogService} from '../catalog.service';
 
@@ -25,6 +26,7 @@ export class ResultFacetsComponent implements OnInit {
 
     constructor(
         private cat: CatalogService,
+        private catUrl: CatalogUrlService,
         private staffCat: StaffCatalogService
     ) {
         this.facetConfig = FACET_CONFIG;
@@ -38,10 +40,11 @@ export class ResultFacetsComponent implements OnInit {
         return this.searchContext.termSearch.hasFacet(new FacetFilter(cls, name, value));
     }
 
-    applyFacet(cls: string, name: string, value: string): void {
-        this.searchContext.termSearch.toggleFacet(new FacetFilter(cls, name, value));
-        this.searchContext.pager.offset = 0;
-        this.staffCat.search();
+    getFacetUrlParams(cls: string, name: string, value: string): any {
+        const context = this.staffCat.cloneContext(this.searchContext);
+        context.termSearch.toggleFacet(new FacetFilter(cls, name, value));
+        context.pager.offset = 0;
+        return this.catUrl.toUrlParams(context);
     }
 }
 
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.html
index eb33fe8877..5ddf94e000 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.html
@@ -21,9 +21,18 @@
         <!-- XXX hard-coded width so columns align vertically regardless
              of the presence of a jacket image -->
         <div class="pl-2 record-jacket-div" >
-          <a href="javascript:void(0)" (click)="navigateToRecord(summary)">
-            <img src="/opac/extras/ac/jacket/small/r/{{summary.id}}"/>
-          </a>
+          <ng-container *ngIf="hasMrConstituentRecords(summary)">
+            <a routerLink="/staff/catalog/search"
+              [queryParams]="appendFromMrParam(summary)">
+              <img src="/opac/extras/ac/jacket/small/r/{{summary.id}}"/>
+            </a>
+          </ng-container>
+          <ng-container *ngIf="!hasMrConstituentRecords(summary)">
+              <a routerLink="/staff/catalog/record/{{summary.id}}"
+                [queryParams]="currentParams()">
+                <img src="/opac/extras/ac/jacket/small/r/{{summary.id}}"/>
+              </a>
+          </ng-container>
         </div>
         <!-- for call number browse display -->
         <ng-container *ngIf="callNumber">
@@ -38,17 +47,25 @@
           <div class="row">
             <div class="col-lg-12 font-weight-bold">
               <!-- nbsp allows the column to take shape when no value exists -->
-              <a href="javascript:void(0)"
-                (click)="navigateToRecord(summary)">
-                {{summary.display.title || ' '}}
-              </a>
+              <ng-container *ngIf="hasMrConstituentRecords(summary)">
+                  <a routerLink="/staff/catalog/search"
+                    [queryParams]="appendFromMrParam(summary)">
+                    {{summary.display.title || ' '}}
+                  </a>
+              </ng-container>
+              <ng-container *ngIf="!hasMrConstituentRecords(summary)">
+                <a routerLink="/staff/catalog/record/{{summary.id}}"
+                  [queryParams]="currentParams()">
+                  {{summary.display.title || ' '}}
+                </a>
+              </ng-container>
             </div>
           </div>
           <div class="row pt-2">
             <div class="col-lg-12">
               <!-- nbsp allows the column to take shape when no value exists -->
-              <a href="javascript:void(0)"
-                (click)="searchAuthor(summary)">
+              <a routerLink="/staff/catalog/search"
+                  [queryParams]="getAuthorSearchParams(summary)">
                 {{summary.display.author || ' '}}
               </a>
             </div>
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.ts
index 8505e768f3..b46e4ca0ba 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.ts
@@ -1,6 +1,6 @@
 import {Component, OnInit, OnDestroy, Input} from '@angular/core';
 import {Subscription} from 'rxjs';
-import {Router} from '@angular/router';
+import {Router, ParamMap} from '@angular/router';
 import {OrgService} from '@eg/core/org.service';
 import {NetService} from '@eg/core/net.service';
 import {IdlObject} from '@eg/core/idl.service';
@@ -82,29 +82,35 @@ export class ResultRecordComponent implements OnInit, OnDestroy {
         alert('Adding to list for bib ' + this.summary.id);
     }
 
-    searchAuthor(summary: any) {
-        this.searchContext.reset();
-        this.searchContext.termSearch.fieldClass = ['author'];
-        this.searchContext.termSearch.query = [summary.display.author];
-        this.staffCat.search();
+    // Params to genreate a new author search based on a reset
+    // clone of the current page params.
+    getAuthorSearchParams(summary: BibRecordSummary): any {
+        const tmpContext = this.staffCat.cloneContext(this.searchContext);
+        tmpContext.reset();
+        tmpContext.termSearch.fieldClass = ['author'];
+        tmpContext.termSearch.query = [summary.display.author];
+        return this.catUrl.toUrlParams(tmpContext);
     }
 
-    /**
-     * Propagate the search params along when navigating to each record.
-     */
-    navigateToRecord(summary: BibRecordSummary) {
-        const params = this.catUrl.toUrlParams(this.searchContext);
-
-        // Jump to metarecord constituent records page when a
-        // MR has more than 1 constituents.
-        if (summary.metabibId && summary.metabibRecords.length > 1) {
-            this.searchContext.termSearch.fromMetarecord = summary.metabibId;
-            this.staffCat.search();
-            return;
-        }
+    // Returns the URL parameters for the current page plus the
+    // "fromMetarecord" param used for linking title links to
+    // MR constituent result records list.
+    appendFromMrParam(summary: BibRecordSummary): any {
+        const tmpContext = this.staffCat.cloneContext(this.searchContext);
+        tmpContext.termSearch.fromMetarecord = summary.metabibId;
+        return this.catUrl.toUrlParams(tmpContext);
+    }
+
+    // Returns true if the selected record summary is a metarecord summary
+    // and it links to more than one constituent bib record.
+    hasMrConstituentRecords(summary: BibRecordSummary): boolean {
+        return (
+            summary.metabibId && summary.metabibRecords.length > 1
+        );
+    }
 
-        this.router.navigate(
-            ['/staff/catalog/record/' + summary.id], {queryParams: params});
+    currentParams(): any {
+        return this.catUrl.toUrlParams(this.searchContext);
     }
 
     toggleBasketEntry() {

commit f52728d7d8230fb9506ec591db255cd4561faf5e
Author: Bill Erickson <berickxx at gmail.com>
Date:   Mon Mar 11 14:01:50 2019 -0400

    LP1819498 Release Notes - Staff Cat CN Browse
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/docs/RELEASE_NOTES_NEXT/Client/ang-staff-cat-cnbrowse.adoc b/docs/RELEASE_NOTES_NEXT/Client/ang-staff-cat-cnbrowse.adoc
new file mode 100644
index 0000000000..2576a419fb
--- /dev/null
+++ b/docs/RELEASE_NOTES_NEXT/Client/ang-staff-cat-cnbrowse.adoc
@@ -0,0 +1,7 @@
+Experiment Staff Catalog Call Number Browse
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Adds support for call number browsing in the staff catalog.  The browse
+results display vertically for consistency with the regular search and
+browse result interfaces.
+

commit 58814097786c080b46ce3cd5d54f8bc21d23297f
Author: Bill Erickson <berickxx at gmail.com>
Date:   Mon Mar 11 11:37:30 2019 -0400

    LP1819498 Angular staff catalog call number browse
    
    Implements call number browse as a vertical paged set, similiar to the
    browse UI and search results.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/share/accesskey/accesskey.service.ts b/Open-ILS/src/eg2/src/app/share/accesskey/accesskey.service.ts
index 51dda57940..0b6302f81d 100644
--- a/Open-ILS/src/eg2/src/app/share/accesskey/accesskey.service.ts
+++ b/Open-ILS/src/eg2/src/app/share/accesskey/accesskey.service.ts
@@ -25,7 +25,9 @@ export class AccessKeyService {
      * string.  For example:  Control and 't' becomes 'ctrl+t'.
      */
     compressKeys(evt: KeyboardEvent): string {
-
+        if (!evt.key) {
+            return null;
+        }
         let s = '';
         if (evt.ctrlKey || evt.metaKey) { s += 'ctrl+'; }
         if (evt.altKey) { s += 'alt+'; }
diff --git a/Open-ILS/src/eg2/src/app/share/catalog/catalog-url.service.ts b/Open-ILS/src/eg2/src/app/share/catalog/catalog-url.service.ts
index 8f326adead..a59b7fc2db 100644
--- a/Open-ILS/src/eg2/src/app/share/catalog/catalog-url.service.ts
+++ b/Open-ILS/src/eg2/src/app/share/catalog/catalog-url.service.ts
@@ -122,6 +122,11 @@ export class CatalogUrlService {
             }
         }
 
+        if (context.cnBrowseSearch.isSearchable()) {
+            params.cnBrowseTerm = context.cnBrowseSearch.value;
+            params.cnBrowsePage = context.cnBrowseSearch.offset;
+        }
+
         return params;
     }
 
@@ -185,6 +190,11 @@ export class CatalogUrlService {
             }
         }
 
+        if (params.get('cnBrowseTerm')) {
+            context.cnBrowseSearch.value = params.get('cnBrowseTerm');
+            context.cnBrowseSearch.offset = Number(params.get('cnBrowsePage'));
+        }
+
         const ts = context.termSearch;
 
         // browseEntry and query searches may be facet-limited
diff --git a/Open-ILS/src/eg2/src/app/share/catalog/catalog.service.ts b/Open-ILS/src/eg2/src/app/share/catalog/catalog.service.ts
index 90a959975a..55fd18e188 100644
--- a/Open-ILS/src/eg2/src/app/share/catalog/catalog.service.ts
+++ b/Open-ILS/src/eg2/src/app/share/catalog/catalog.service.ts
@@ -362,4 +362,15 @@ export class CatalogService {
             ctx.searchState = CatalogSearchState.COMPLETE;
         }));
     }
+
+    cnBrowse(ctx: CatalogSearchContext): Observable<any> {
+        ctx.searchState = CatalogSearchState.SEARCHING;
+        const cbs = ctx.cnBrowseSearch;
+
+        return this.net.request(
+            'open-ils.supercat',
+            'open-ils.supercat.call_number.browse',
+            cbs.value, ctx.searchOrg.shortname(), ctx.pager.limit, cbs.offset
+        ).pipe(tap(result => ctx.searchState = CatalogSearchState.COMPLETE));
+    }
 }
diff --git a/Open-ILS/src/eg2/src/app/share/catalog/search-context.ts b/Open-ILS/src/eg2/src/app/share/catalog/search-context.ts
index f18a8cde2d..222536f69e 100644
--- a/Open-ILS/src/eg2/src/app/share/catalog/search-context.ts
+++ b/Open-ILS/src/eg2/src/app/share/catalog/search-context.ts
@@ -112,6 +112,22 @@ export class CatalogIdentContext {
 
 }
 
+export class CatalogCnBrowseContext {
+    value: string;
+    // offset in pages from base browse term
+    // e.g. -2 means 2 pages back (alphabetically) from the original search.
+    offset: number;
+
+    reset() {
+        this.value = '';
+        this.offset = 0;
+    }
+
+    isSearchable() {
+        return this.value !== '';
+    }
+}
+
 export class CatalogTermContext {
     fieldClass: string[];
     query: string[];
@@ -214,6 +230,7 @@ export class CatalogSearchContext {
     marcSearch: CatalogMarcContext;
     identSearch: CatalogIdentContext;
     browseSearch: CatalogBrowseContext;
+    cnBrowseSearch: CatalogCnBrowseContext;
 
     // Result from most recent search.
     result: CatalogSearchResults;
@@ -232,6 +249,7 @@ export class CatalogSearchContext {
         this.marcSearch = new CatalogMarcContext();
         this.identSearch = new CatalogIdentContext();
         this.browseSearch = new CatalogBrowseContext();
+        this.cnBrowseSearch = new CatalogCnBrowseContext();
         this.reset();
     }
 
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/browse.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/browse.component.html
index b50a415b4b..175104b81b 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/browse.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/browse.component.html
@@ -1,5 +1,5 @@
 
 <eg-catalog-search-form #searchForm></eg-catalog-search-form>
 
-<eg-catalog-browse-results><eg-catalog-browse-results>
+<eg-catalog-browse-results></eg-catalog-browse-results>
 
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/browse.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/browse.component.ts
index 67e5eed1f1..bf46a4e115 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/browse.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/browse.component.ts
@@ -1,6 +1,5 @@
 import {Component, OnInit, ViewChild} from '@angular/core';
 import {StaffCatalogService} from './catalog.service';
-import {BasketService} from '@eg/share/catalog/basket.service';
 import {SearchFormComponent} from './search-form.component';
 
 @Component({
@@ -11,17 +10,12 @@ export class BrowseComponent implements OnInit {
     @ViewChild('searchForm') searchForm: SearchFormComponent;
 
     constructor(
-        private staffCat: StaffCatalogService,
-        private basket: BasketService
+        private staffCat: StaffCatalogService
     ) {}
 
     ngOnInit() {
         // A SearchContext provides all the data needed for browse.
         this.staffCat.createContext();
-
-        // Cache the basket on page load.
-        this.basket.getRecordIds();
-
         this.searchForm.searchTab = 'browse';
     }
 }
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/browse/results.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/browse/results.component.ts
index 65d02e5342..e8b3499c66 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/browse/results.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/browse/results.component.ts
@@ -1,38 +1,37 @@
-import {Component, OnInit, Input} from '@angular/core';
-import {Observable, Subscription} from 'rxjs';
-import {map, switchMap, distinctUntilChanged} from 'rxjs/operators';
+import {Component, OnInit, OnDestroy} from '@angular/core';
 import {ActivatedRoute, ParamMap} from '@angular/router';
+import {Subscription} from 'rxjs';
 import {CatalogService} from '@eg/share/catalog/catalog.service';
-import {BibRecordService} from '@eg/share/catalog/bib-record.service';
 import {CatalogUrlService} from '@eg/share/catalog/catalog-url.service';
 import {CatalogSearchContext, CatalogSearchState} from '@eg/share/catalog/search-context';
-import {PcrudService} from '@eg/core/pcrud.service';
 import {StaffCatalogService} from '../catalog.service';
-import {IdlObject} from '@eg/core/idl.service';
 
 @Component({
   selector: 'eg-catalog-browse-results',
   templateUrl: 'results.component.html'
 })
-export class BrowseResultsComponent implements OnInit {
+export class BrowseResultsComponent implements OnInit, OnDestroy {
 
     searchContext: CatalogSearchContext;
     results: any[];
+    routeSub: Subscription;
 
     constructor(
         private route: ActivatedRoute,
-        private pcrud: PcrudService,
         private cat: CatalogService,
-        private bib: BibRecordService,
         private catUrl: CatalogUrlService,
         private staffCat: StaffCatalogService
     ) {}
 
     ngOnInit() {
         this.searchContext = this.staffCat.searchContext;
-        this.route.queryParamMap.subscribe((params: ParamMap) => {
-            this.browseByUrl(params);
-        });
+        this.routeSub = this.route.queryParamMap.subscribe(
+            (params: ParamMap) => this.browseByUrl(params)
+        );
+    }
+
+    ngOnDestroy() {
+        this.routeSub.unsubscribe();
     }
 
     browseByUrl(params: ParamMap): void {
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.component.ts
index 0e2fc98884..f9fcf6dc8f 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.component.ts
@@ -17,9 +17,6 @@ export class CatalogComponent implements OnInit {
         // child components.  After initial creation, the context is
         // reset and updated as needed to apply new search parameters.
         this.staffCat.createContext();
-
-        // Cache the basket on page load.
-        this.basket.getRecordIds();
     }
 }
 
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
index b07938a4c8..e78a951e62 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
@@ -24,6 +24,8 @@ import {BrowseComponent} from './browse.component';
 import {BrowseResultsComponent} from './browse/results.component';
 import {HoldingsMaintenanceComponent} from './record/holdings.component';
 import {ConjoinedComponent} from './record/conjoined.component';
+import {CnBrowseComponent} from './cnbrowse.component';
+import {CnBrowseResultsComponent} from './cnbrowse/results.component';
 
 @NgModule({
   declarations: [
@@ -44,7 +46,9 @@ import {ConjoinedComponent} from './record/conjoined.component';
     BrowseComponent,
     BrowseResultsComponent,
     ConjoinedComponent,
-    HoldingsMaintenanceComponent
+    HoldingsMaintenanceComponent,
+    CnBrowseComponent,
+    CnBrowseResultsComponent
   ],
   imports: [
     StaffCommonModule,
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts
index cf0a36c97f..3b4f6962b6 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts
@@ -89,7 +89,6 @@ export class StaffCatalogService {
      */
     browse(): void {
         if (!this.searchContext.browseSearch.isSearchable()) { return; }
-
         const params = this.catUrl.toUrlParams(this.searchContext);
 
         // Force a new browse every time this method is called, even if
@@ -102,7 +101,16 @@ export class StaffCatalogService {
         params.ridx = '' + this.routeIndex++;
 
         this.router.navigate(
-          ['/staff/catalog/browse'], {queryParams: params});
+            ['/staff/catalog/browse'], {queryParams: params});
+    }
+
+    // Call number browse.
+    // Redirect to cn browse page and let its component perform the search
+    cnBrowse(): void {
+        if (!this.searchContext.cnBrowseSearch.isSearchable()) { return; }
+        const params = this.catUrl.toUrlParams(this.searchContext);
+        params.ridx = '' + this.routeIndex++; // see comments above
+        this.router.navigate(['/staff/catalog/cnbrowse'], {queryParams: params});
     }
 }
 
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/browse.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/cnbrowse.component.html
similarity index 51%
copy from Open-ILS/src/eg2/src/app/staff/catalog/browse.component.html
copy to Open-ILS/src/eg2/src/app/staff/catalog/cnbrowse.component.html
index b50a415b4b..9be00ff8db 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/browse.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/cnbrowse.component.html
@@ -1,5 +1,5 @@
 
 <eg-catalog-search-form #searchForm></eg-catalog-search-form>
 
-<eg-catalog-browse-results><eg-catalog-browse-results>
+<eg-catalog-cn-browse-results></eg-catalog-cn-browse-results>
 
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/browse.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/cnbrowse.component.ts
similarity index 59%
copy from Open-ILS/src/eg2/src/app/staff/catalog/browse.component.ts
copy to Open-ILS/src/eg2/src/app/staff/catalog/cnbrowse.component.ts
index 67e5eed1f1..e0f7bf73fe 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/browse.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/cnbrowse.component.ts
@@ -1,28 +1,22 @@
 import {Component, OnInit, ViewChild} from '@angular/core';
 import {StaffCatalogService} from './catalog.service';
-import {BasketService} from '@eg/share/catalog/basket.service';
 import {SearchFormComponent} from './search-form.component';
 
 @Component({
-  templateUrl: 'browse.component.html'
+  templateUrl: 'cnbrowse.component.html'
 })
-export class BrowseComponent implements OnInit {
+export class CnBrowseComponent implements OnInit {
 
     @ViewChild('searchForm') searchForm: SearchFormComponent;
 
     constructor(
         private staffCat: StaffCatalogService,
-        private basket: BasketService
     ) {}
 
     ngOnInit() {
         // A SearchContext provides all the data needed for browse.
         this.staffCat.createContext();
-
-        // Cache the basket on page load.
-        this.basket.getRecordIds();
-
-        this.searchForm.searchTab = 'browse';
+        this.searchForm.searchTab = 'cnbrowse';
     }
 }
 
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/cnbrowse/results.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/cnbrowse/results.component.html
new file mode 100644
index 0000000000..09a1f4e870
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/cnbrowse/results.component.html
@@ -0,0 +1,52 @@
+<!-- search results progress bar -->
+<div class="row" *ngIf="browseIsActive()">
+  <div class="col-lg-6 offset-lg-3 pt-3">
+    <div class="progress">
+      <div class="progress-bar progress-bar-striped active w-100"
+        role="progressbar" aria-valuenow="100" 
+        aria-valuemin="0" aria-valuemax="100">
+        <span class="sr-only" i18n>Searching..</span>
+      </div>
+    </div>
+  </div>
+</div>
+
+<!-- no items found -->
+<div *ngIf="browseIsDone() && !browseHasResults()">
+  <div class="row pt-3">
+    <div class="col-lg-6 offset-lg-3">
+      <div class="alert alert-warning">
+        <span i18n>No Maching Items Were Found</span>
+      </div>
+    </div>
+  </div>
+</div>
+
+<!-- header, pager, and list of records -->
+<div id="staff-catalog-browse-results-container" *ngIf="browseHasResults()">
+
+  <div class="row mb-2">
+    <div class="col-lg-3">
+      <button class="btn btn-primary" (click)="prevPage()">Back</button>
+      <button class="btn btn-primary ml-3" (click)="nextPage()">Next</button>
+    </div>
+  </div>
+
+  <div class="row" *ngFor="let result of results; let idx = index">
+    <div class="col-lg-12" *ngIf="result._bibSummary">
+      <eg-catalog-result-record [summary]="result._bibSummary" 
+        [index]="idx" [callNumber]="result">
+      </eg-catalog-result-record>
+    </div>
+  </div>
+
+  <div class="row mb-2">
+    <div class="col-lg-3">
+      <button class="btn btn-primary" (click)="prevPage()">Back</button>
+      <button class="btn btn-primary ml-3" (click)="nextPage()">Next</button>
+    </div>
+  </div>
+
+</div>
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/cnbrowse/results.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/cnbrowse/results.component.ts
new file mode 100644
index 0000000000..037b9ea88f
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/cnbrowse/results.component.ts
@@ -0,0 +1,122 @@
+import {Component, OnInit, OnDestroy} from '@angular/core';
+import {ActivatedRoute, Router, ParamMap} from '@angular/router';
+import {Subscription} from 'rxjs';
+import {IdlObject} from '@eg/core/idl.service';
+import {CatalogService} from '@eg/share/catalog/catalog.service';
+import {BibRecordService} from '@eg/share/catalog/bib-record.service';
+import {CatalogUrlService} from '@eg/share/catalog/catalog-url.service';
+import {CatalogSearchContext, CatalogSearchState} from '@eg/share/catalog/search-context';
+import {StaffCatalogService} from '../catalog.service';
+import {BibRecordSummary} from '@eg/share/catalog/bib-record.service';
+
+ at Component({
+  selector: 'eg-catalog-cn-browse-results',
+  templateUrl: 'results.component.html'
+})
+export class CnBrowseResultsComponent implements OnInit, OnDestroy {
+
+    searchContext: CatalogSearchContext;
+    results: any[];
+    routeSub: Subscription;
+
+    constructor(
+        private router: Router,
+        private route: ActivatedRoute,
+        private cat: CatalogService,
+        private bib: BibRecordService,
+        private catUrl: CatalogUrlService,
+        private staffCat: StaffCatalogService
+    ) {}
+
+    ngOnInit() {
+        this.searchContext = this.staffCat.searchContext;
+        this.routeSub = this.route.queryParamMap.subscribe(
+            (params: ParamMap) => this.browseByUrl(params)
+        );
+    }
+
+    ngOnDestroy() {
+        this.routeSub.unsubscribe();
+    }
+
+    browseByUrl(params: ParamMap): void {
+        this.catUrl.applyUrlParams(this.searchContext, params);
+        const cbs = this.searchContext.cnBrowseSearch;
+
+        if (cbs.isSearchable()) {
+            this.results = [];
+            this.cat.cnBrowse(this.searchContext)
+                .subscribe(results => this.processResults(results));
+        }
+    }
+
+    processResults(results: any[]) {
+        this.results = results;
+
+        const depth = this.searchContext.global ?
+            this.searchContext.org.root().ou_type().depth() :
+            this.searchContext.searchOrg.ou_type().depth();
+
+        const bibIds = this.results.map(r => r.record().id());
+        const distinct = (value: any, index: number, self: Array<number>) => {
+            return self.indexOf(value) === index;
+        };
+
+        const bres: IdlObject[] = [];
+        this.bib.getBibSummary(
+            bibIds.filter(distinct),
+            this.searchContext.searchOrg.id(), depth
+        ).subscribe(
+            summary => {
+                // Response order not guaranteed.  Match the summary
+                // object up with its response object.  A bib may be
+                // linked to multiple call numbers
+                const bibResults = this.results.filter(
+                    r => Number(r.record().id()) === summary.id);
+
+                bres.push(summary.record);
+
+                // Use _ since result is an 'acn' object.
+                bibResults.forEach(r => r._bibSummary = summary);
+            },
+            err => {},
+            ()  => {
+                this.bib.fleshBibUsers(bres);
+            }
+        );
+    }
+
+    browseIsDone(): boolean {
+        return this.searchContext.searchState === CatalogSearchState.COMPLETE;
+    }
+
+    browseIsActive(): boolean {
+        return this.searchContext.searchState === CatalogSearchState.SEARCHING;
+    }
+
+    browseHasResults(): boolean {
+        return this.browseIsDone() && this.results.length > 0;
+    }
+
+    prevPage() {
+        this.searchContext.cnBrowseSearch.offset--;
+        this.staffCat.cnBrowse();
+    }
+
+    nextPage() {
+        this.searchContext.cnBrowseSearch.offset++;
+        this.staffCat.cnBrowse();
+    }
+
+    /**
+     * Propagate the search params along when navigating to each record.
+     */
+    navigateToRecord(summary: BibRecordSummary) {
+        const params = this.catUrl.toUrlParams(this.searchContext);
+
+        this.router.navigate(
+            ['/staff/catalog/record/' + summary.id], {queryParams: params});
+    }
+}
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/resolver.service.ts b/Open-ILS/src/eg2/src/app/staff/catalog/resolver.service.ts
index 0a09cbdca7..1dac53644e 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/resolver.service.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/resolver.service.ts
@@ -1,14 +1,14 @@
 import {Injectable} from '@angular/core';
-import {Observable, Observer} from 'rxjs';
 import {Router, Resolve, RouterStateSnapshot,
         ActivatedRouteSnapshot} from '@angular/router';
 import {ServerStoreService} from '@eg/core/server-store.service';
 import {NetService} from '@eg/core/net.service';
 import {OrgService} from '@eg/core/org.service';
 import {AuthService} from '@eg/core/auth.service';
-import {PcrudService} from '@eg/core/pcrud.service';
 import {CatalogService} from '@eg/share/catalog/catalog.service';
 import {StaffCatalogService} from './catalog.service';
+import {BasketService} from '@eg/share/catalog/basket.service';
+
 
 @Injectable()
 export class CatalogResolver implements Resolve<Promise<any[]>> {
@@ -20,7 +20,8 @@ export class CatalogResolver implements Resolve<Promise<any[]>> {
         private net: NetService,
         private auth: AuthService,
         private cat: CatalogService,
-        private staffCat: StaffCatalogService
+        private staffCat: StaffCatalogService,
+        private basket: BasketService
     ) {}
 
     resolve(
@@ -32,7 +33,8 @@ export class CatalogResolver implements Resolve<Promise<any[]>> {
         return Promise.all([
             this.cat.fetchCcvms(),
             this.cat.fetchCmfs(),
-            this.fetchSettings()
+            this.fetchSettings(),
+            this.basket.getRecordIds()
         ]);
     }
 
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.html
index 9a65dafc60..eb33fe8877 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.html
@@ -25,6 +25,15 @@
             <img src="/opac/extras/ac/jacket/small/r/{{summary.id}}"/>
           </a>
         </div>
+        <!-- for call number browse display -->
+        <ng-container *ngIf="callNumber">
+          <div class="pl-2 font-weight-bold">
+            {{callNumber.prefix().label()}}
+            {{callNumber.label()}}
+            {{callNumber.suffix().label()}}
+            @ {{orgName(callNumber.owning_lib())}}
+          </div>
+        </ng-container>
         <div class="flex-1 pl-2">
           <div class="row">
             <div class="col-lg-12 font-weight-bold">
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.ts
index dd13b9d8f6..8505e768f3 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.ts
@@ -3,6 +3,7 @@ import {Subscription} from 'rxjs';
 import {Router} from '@angular/router';
 import {OrgService} from '@eg/core/org.service';
 import {NetService} from '@eg/core/net.service';
+import {IdlObject} from '@eg/core/idl.service';
 import {CatalogService} from '@eg/share/catalog/catalog.service';
 import {BibRecordService, BibRecordSummary} from '@eg/share/catalog/bib-record.service';
 import {CatalogSearchContext} from '@eg/share/catalog/search-context';
@@ -19,6 +20,12 @@ export class ResultRecordComponent implements OnInit, OnDestroy {
 
     @Input() index: number;  // 0-index display row
     @Input() summary: BibRecordSummary;
+
+    // Optional call number (acn) object to highlight
+    // Assumed prefix/suffix are fleshed
+    // Used by call number browse.
+    @Input() callNumber: IdlObject;
+
     searchContext: CatalogSearchContext;
     isRecordSelected: boolean;
     basketSub: Subscription;
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/catalog/routing.module.ts
index 8bcef4f30c..e0da65f9d1 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/routing.module.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/routing.module.ts
@@ -6,6 +6,7 @@ import {RecordComponent} from './record/record.component';
 import {CatalogResolver} from './resolver.service';
 import {HoldComponent} from './hold/hold.component';
 import {BrowseComponent} from './browse.component';
+import {CnBrowseComponent} from './cnbrowse.component';
 
 const routes: Routes = [{
   path: '',
@@ -24,11 +25,16 @@ const routes: Routes = [{
     path: 'record/:id/:tab',
     component: RecordComponent
   }]}, {
-  // Browse is a top-level UI
-  path: 'browse',
-  component: BrowseComponent,
-  resolve: {catResolver : CatalogResolver},
-}];
+    // Browse is a top-level UI
+    path: 'browse',
+    component: BrowseComponent,
+    resolve: {catResolver : CatalogResolver}
+  }, {
+    path: 'cnbrowse',
+    component: CnBrowseComponent,
+    resolve: {catResolver : CatalogResolver}
+  }
+];
 
 @NgModule({
   imports: [RouterModule.forChild(routes)],
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/search-form.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/search-form.component.html
index ee4abc522c..18af372284 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/search-form.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/search-form.component.html
@@ -296,6 +296,22 @@ TODO focus search input
           </div>
         </ng-template>
       </ngb-tab>
+      <ngb-tab title="Shelf Browse" i18n-title id="cnbrowse">
+        <ng-template ngbTabContent>
+          <div class="row mt-4">
+            <div class="col-lg-12 form-inline">
+              <label for="cnbrowse-term-input" i18n>
+                Browse Call Numbers starting with 
+              </label>
+              <input type="text" class="form-control ml-2" 
+                id='cnbrowse-term-input' name="query"
+                [(ngModel)]="context.cnBrowseSearch.value"
+                (keyup.enter)="searchByForm()"
+                placeholder="Browse Call Numbers..."/>
+            </div>
+          </div>
+        </ng-template>
+      </ngb-tab>      
     </ngb-tabset>
   </div>
   <div class="col-lg-4">
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/search-form.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/search-form.component.ts
index 785e69e682..c8cee02105 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/search-form.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/search-form.component.ts
@@ -81,7 +81,7 @@ export class SearchFormComponent implements OnInit, AfterViewInit {
 
     focusTabInput() {
         // Select a DOM node to focus when the tab changes.
-        let selector;
+        let selector: string;
         switch (this.searchTab) {
             case 'ident':
                 selector = '#ident-query-input';
@@ -92,6 +92,9 @@ export class SearchFormComponent implements OnInit, AfterViewInit {
             case 'browse':
                 selector = '#browse-term-input';
                 break;
+            case 'cnbrowse':
+                selector = '#cnbrowse-term-input';
+                break;
             default:
                 this.refreshCopyLocations();
                 selector = '#first-query-input';
@@ -197,6 +200,7 @@ export class SearchFormComponent implements OnInit, AfterViewInit {
                 this.context.marcSearch.reset();
                 this.context.browseSearch.reset();
                 this.context.identSearch.reset();
+                this.context.cnBrowseSearch.reset();
                 this.context.termSearch.hasBrowseEntry = '';
                 this.context.termSearch.browseEntry = null;
                 this.context.termSearch.fromMetarecord = null;
@@ -208,6 +212,7 @@ export class SearchFormComponent implements OnInit, AfterViewInit {
                 this.context.marcSearch.reset();
                 this.context.browseSearch.reset();
                 this.context.termSearch.reset();
+                this.context.cnBrowseSearch.reset();
                 this.staffCat.search();
                 break;
 
@@ -215,6 +220,7 @@ export class SearchFormComponent implements OnInit, AfterViewInit {
                 this.context.browseSearch.reset();
                 this.context.termSearch.reset();
                 this.context.identSearch.reset();
+                this.context.cnBrowseSearch.reset();
                 this.staffCat.search();
                 break;
 
@@ -222,9 +228,19 @@ export class SearchFormComponent implements OnInit, AfterViewInit {
                 this.context.marcSearch.reset();
                 this.context.termSearch.reset();
                 this.context.identSearch.reset();
+                this.context.cnBrowseSearch.reset();
                 this.context.browseSearch.pivot = null;
                 this.staffCat.browse();
                 break;
+
+            case 'cnbrowse':
+                this.context.marcSearch.reset();
+                this.context.termSearch.reset();
+                this.context.identSearch.reset();
+                this.context.browseSearch.reset();
+                this.context.cnBrowseSearch.offset = 0;
+                this.staffCat.cnBrowse();
+                break;
         }
     }
 
@@ -236,10 +252,6 @@ export class SearchFormComponent implements OnInit, AfterViewInit {
     searchIsActive(): boolean {
         return this.context.searchState === CatalogSearchState.SEARCHING;
     }
-
-    goToBrowse() {
-        this.router.navigate(['/staff/catalog/browse']);
-    }
 }
 
 

commit 1ec8ea366ec372b4464d7115021de463eb3f0fd1
Author: Bill Erickson <berickxx at gmail.com>
Date:   Thu Mar 7 14:26:01 2019 -0500

    LP1819053 Release notes - basket export
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/docs/RELEASE_NOTES_NEXT/Cataloging/catalog-basket-export.adoc b/docs/RELEASE_NOTES_NEXT/Cataloging/catalog-basket-export.adoc
new file mode 100644
index 0000000000..d1f85e24ee
--- /dev/null
+++ b/docs/RELEASE_NOTES_NEXT/Cataloging/catalog-basket-export.adoc
@@ -0,0 +1,10 @@
+Staff Catalog Basket Export Option
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Adds a new "Export Records" option to the staff catalog basket menu.
+When selected, the user is directed to the Vandelay record export
+interface, which will be set to "basket export" mode.  Staff can then
+apply export preferences (usmarc, marxml, etc.) and export the basket
+records.  In "basket export" mode, Vandley provides a link to return to
+the catalog (preserving search params).
+

commit bb9f8f3a4db4a5f08b2bcc6d7f10885443731502
Author: Bill Erickson <berickxx at gmail.com>
Date:   Thu Mar 7 13:55:04 2019 -0500

    LP1819053 Angular staff catalog basket export
    
    Adds a new "Export Records" option to the staff catalog basket menu.
    When selected, the user is directed to the Vandelay record export
    interface, which will be set to "basket export" mode.  Staff can then
    apply export preferences (usmarc, marxml, etc.) and export the basket
    records.  In "basket export" mode, Vandley provides a link to return to
    the catalog (preserving search params).
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/staff/cat/vandelay/export.component.html b/Open-ILS/src/eg2/src/app/staff/cat/vandelay/export.component.html
index 53f36e6813..b6fb361799 100644
--- a/Open-ILS/src/eg2/src/app/staff/cat/vandelay/export.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/cat/vandelay/export.component.html
@@ -3,60 +3,73 @@
 <div class="common-form striped-even form-validated">
   <div class="row">
     <div class="col-lg-6">
-      <div class="row"><label>Select a Record Source</label></div>
-      <ngb-accordion [closeOthers]="true" activeIds="csv" 
-        (panelChange)="sourceChange($event)">
-        <ngb-panel id="csv" title="CSV File">
-          <ng-template ngbPanelContent>
-            <div class="row">
-              <div class="col-lg-6">
-                <label for="csv-input" i18n>Use Field Number</label>
-              </div>
-              <div class="col-lg-6">
-                <input id='csv-input' type="number" class="form-control" 
-                  [(ngModel)]="fieldNumber"
-                  i18n-placeholder placeholder="Starts at 0..."/>
-              </div>
-            </div>
-            <div class="row">
-              <div class="col-lg-6">
-                <label for="use-csv-file" i18n>From CSV file</label>
-              </div>
-              <div class="col-lg-6">
-                <input #fileSelector (change)="fileSelected($event)" 
-                  id="use-csv-file" class="form-control" type="file"/>
-              </div>
-            </div>
-          </ng-template>
-        </ngb-panel>
-        <ngb-panel id="record-id" title="Record ID">
-          <ng-template ngbPanelContent>
-            <div class="row">
-              <div class="col-lg-6">
-                <label for="record-id-input" i18n>Record ID</label>
+     <ng-container *ngIf="exportingBasket">
+        <div class="alert alert-info" i18n>
+          Exporting {{basketRecords.length}} Records from Catalog Basket.
+        </div>
+        <div>
+          <a routerLink="/staff/catalog/search" queryParamsHandling="merge">
+            <button class="btn btn-info" i18n>Return to Catalog</button>
+          </a>
+        </div>
+      </ng-container>
+      <ng-container *ngIf="!exportingBasket">
+        <div class="row"><label>Select a Record Source</label></div>
+        <ngb-accordion [closeOthers]="true" activeIds="csv" 
+          (panelChange)="sourceChange($event)">
+          <ngb-panel id="csv" title="CSV File">
+            <ng-template ngbPanelContent>
+              <div class="row">
+                <div class="col-lg-6">
+                  <label for="csv-input" i18n>Use Field Number</label>
+                </div>
+                <div class="col-lg-6">
+                  <input id='csv-input' type="number" class="form-control" 
+                    [(ngModel)]="fieldNumber"
+                    i18n-placeholder placeholder="Starts at 0..."/>
+                </div>
               </div>
-              <div class="col-lg-6">
-                <input id='record-id-input' type="number" 
-                  class="form-control" [(ngModel)]="recordId"/>
+              <div class="row">
+                <div class="col-lg-6">
+                  <label for="use-csv-file" i18n>From CSV file</label>
+                </div>
+                <div class="col-lg-6">
+                  <input #fileSelector (change)="fileSelected($event)" 
+                    id="use-csv-file" class="form-control" type="file"/>
+                </div>
               </div>
-            </div>
-          </ng-template>
-        </ngb-panel>
-        <ngb-panel id="bucket-id" title="Bucket">
-          <ng-template ngbPanelContent>
-            <div class="row">
-              <div class="col-lg-6">
-                <label for="bucket-id-input" i18n>Bucket ID</label>
+            </ng-template>
+          </ngb-panel>
+          <ngb-panel id="record-id" title="Record ID">
+            <ng-template ngbPanelContent>
+              <div class="row">
+                <div class="col-lg-6">
+                  <label for="record-id-input" i18n>Record ID</label>
+                </div>
+                <div class="col-lg-6">
+                  <input id='record-id-input' type="number" 
+                    class="form-control" [(ngModel)]="recordId"/>
+                </div>
               </div>
-              <div class="col-lg-6">
-                <input id='bucket-id-input' type="number" 
-                  class="form-control" [(ngModel)]="bucketId"/>
+            </ng-template>
+          </ngb-panel>
+          <ngb-panel id="bucket-id" title="Bucket">
+            <ng-template ngbPanelContent>
+              <div class="row">
+                <div class="col-lg-6">
+                  <label for="bucket-id-input" i18n>Bucket ID</label>
+                </div>
+                <div class="col-lg-6">
+                  <input id='bucket-id-input' type="number" 
+                    class="form-control" [(ngModel)]="bucketId"/>
+                </div>
               </div>
-            </div>
-          </ng-template>
-        </ngb-panel>
-      </ngb-accordion>
+            </ng-template>
+          </ngb-panel>
+        </ngb-accordion>
+      </ng-container>
     </div><!-- col -->
+
     <div class="col-lg-6">
       <div class="row">
         <div class="col-lg-6">
@@ -64,6 +77,7 @@
         </div>
         <div class="col-lg-6">
           <select class="form-control" 
+            [disabled]="exportingBasket"
             [(ngModel)]="recordType" id="record-type">
             <option i18n value="biblio">Bibliographic Records</option>
             <option i18n value="authority">Authority Records</option>
@@ -121,5 +135,6 @@
       </div>
     </div><!-- left col -->
   </div><!-- row -->
-</div>
+</div><!-- form -->
+
 
diff --git a/Open-ILS/src/eg2/src/app/staff/cat/vandelay/export.component.ts b/Open-ILS/src/eg2/src/app/staff/cat/vandelay/export.component.ts
index d980e02bd2..648f4d58b6 100644
--- a/Open-ILS/src/eg2/src/app/staff/cat/vandelay/export.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/cat/vandelay/export.component.ts
@@ -1,4 +1,5 @@
-import {Component, AfterViewInit, ViewChild, Renderer2} from '@angular/core';
+import {Component, AfterViewInit, ViewChild, Renderer2, OnInit} from '@angular/core';
+import {ActivatedRoute} from '@angular/router';
 import {NgbPanelChangeEvent} from '@ng-bootstrap/ng-bootstrap';
 import {HttpClient, HttpRequest, HttpEventType} from '@angular/common/http';
 import {HttpResponse, HttpErrorResponse} from '@angular/common/http';
@@ -6,13 +7,14 @@ import {saveAs} from 'file-saver';
 import {AuthService} from '@eg/core/auth.service';
 import {ToastService} from '@eg/share/toast/toast.service';
 import {ProgressInlineComponent} from '@eg/share/dialog/progress-inline.component';
-import {VandelayService, VANDELAY_EXPORT_PATH} from './vandelay.service';
+import {VANDELAY_EXPORT_PATH} from './vandelay.service';
+import {BasketService} from '@eg/share/catalog/basket.service';
 
 
 @Component({
   templateUrl: 'export.component.html'
 })
-export class ExportComponent implements AfterViewInit {
+export class ExportComponent implements AfterViewInit, OnInit {
 
     recordSource: string;
     fieldNumber: number;
@@ -24,6 +26,8 @@ export class ExportComponent implements AfterViewInit {
     recordEncoding: string;
     includeHoldings: boolean;
     isExporting: boolean;
+    exportingBasket: boolean;
+    basketRecords: number[];
 
     @ViewChild('fileSelector') private fileSelector;
     @ViewChild('exportProgress')
@@ -31,23 +35,44 @@ export class ExportComponent implements AfterViewInit {
 
     constructor(
         private renderer: Renderer2,
+        private route: ActivatedRoute,
         private http: HttpClient,
         private toast: ToastService,
-        private auth: AuthService
+        private auth: AuthService,
+        private basket: BasketService
     ) {
         this.recordType = 'biblio';
         this.recordFormat = 'USMARC';
         this.recordEncoding = 'UTF-8';
         this.includeHoldings = false;
+        this.basketRecords = [];
+    }
+
+    ngOnInit() {
+        const segments = this.route.snapshot.url.length;
+        if (segments > 0 &&
+            this.route.snapshot.url[segments - 1].path === 'basket') {
+                this.exportingBasket = true;
+                this.basket.getRecordIds().then(
+                    ids => this.basketRecords = ids
+                );
+        }
     }
 
     ngAfterViewInit() {
+        if (this.exportingBasket) {
+            return; // no source to focus
+        }
         this.renderer.selectRootElement('#csv-input').focus();
     }
 
     sourceChange($event: NgbPanelChangeEvent) {
         this.recordSource = $event.panelId;
 
+        if (this.exportingBasket) {
+            return; // no source to focus
+        }
+
         if ($event.nextState) { // panel opened
 
             // give the panel a chance to render before focusing input
@@ -64,7 +89,10 @@ export class ExportComponent implements AfterViewInit {
 
     hasNeededData(): boolean {
         return Boolean(
-            this.selectedFile || this.recordId || this.bucketId
+            this.selectedFile ||
+            this.recordId     ||
+            this.bucketId     ||
+            (this.exportingBasket && this.basketRecords.length > 0)
         );
     }
 
@@ -83,21 +111,27 @@ export class ExportComponent implements AfterViewInit {
             formData.append('holdings', '1');
         }
 
-        switch (this.recordSource) {
+        if (this.exportingBasket) {
+            this.basketRecords.forEach(id => formData.append('id', '' + id));
+
+        } else {
 
-            case 'csv':
-                formData.append('idcolumn', '' + this.fieldNumber);
-                formData.append('idfile',
-                    this.selectedFile, this.selectedFile.name);
-                break;
+            switch (this.recordSource) {
 
-            case 'record-id':
-                formData.append('id', '' + this.recordId);
-                break;
+                case 'csv':
+                    formData.append('idcolumn', '' + this.fieldNumber);
+                    formData.append('idfile',
+                        this.selectedFile, this.selectedFile.name);
+                    break;
 
-            case 'bucket-id':
-                formData.append('containerid', '' + this.bucketId);
-                break;
+                case 'record-id':
+                    formData.append('id', '' + this.recordId);
+                    break;
+
+                case 'bucket-id':
+                    formData.append('containerid', '' + this.bucketId);
+                    break;
+            }
         }
 
         this.sendExportRequest(formData);
diff --git a/Open-ILS/src/eg2/src/app/staff/cat/vandelay/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/cat/vandelay/routing.module.ts
index 707b92b9ca..f4095a4825 100644
--- a/Open-ILS/src/eg2/src/app/staff/cat/vandelay/routing.module.ts
+++ b/Open-ILS/src/eg2/src/app/staff/cat/vandelay/routing.module.ts
@@ -28,6 +28,9 @@ const routes: Routes = [{
     path: 'export',
     component: ExportComponent
   }, {
+    path: 'export/basket',
+    component: ExportComponent
+  }, {
     path: 'queue',
     component: QueueListComponent
   }, {
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.html
index 4857db8cc1..9fcd873cee 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.html
@@ -22,6 +22,7 @@
       <option value="print"  i18n>Print Title Details</option>
       <option value="email"  i18n>Email Title Details</option>
       <option value="bucket" i18n>Add Basket to Bucket</option>
+      <option value="export_marc" i18n>Export Records</option>
       <option value="clear"  i18n>Clear Basket</option>
     </select>
   </div>
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.ts
index f96df6fd75..da3f793625 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.ts
@@ -1,6 +1,5 @@
 import {Component, OnInit, ViewChild} from '@angular/core';
 import {BasketService} from '@eg/share/catalog/basket.service';
-import {Subscription} from 'rxjs';
 import {Router} from '@angular/router';
 import {NetService} from '@eg/core/net.service';
 import {AuthService} from '@eg/core/auth.service';
@@ -87,6 +86,13 @@ export class BasketActionsComponent implements OnInit {
                 });
                 break;
 
+            case 'export_marc':
+                this.router.navigate(
+                    ['/staff/cat/vandelay/export/basket'],
+                    {queryParamsHandling: 'merge'}
+                );
+                break;
+
             case 'bucket':
                 this.basket.getRecordIds().then(ids => {
                     this.addToBucketDialog.bucketClass = 'biblio';

commit 28f955619a2d2d4665ff31c8efd2918a0e7532dd
Author: Bill Erickson <berickxx at gmail.com>
Date:   Fri Mar 29 16:47:27 2019 +0000

    LP1821382 Grid showDeclaredFieldsOnly option; sort repair.
    
    Adds a @Input() showDeclaredFieldsOnly option which tells the grid to
    avoid showing auto-generated columns by default and only show those
    declared in the markup.
    
    Also repairs a grid column sorting/insert bug where declared columns
    would be displayed in the wrong order when mixed with auto columns.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts b/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts
index e1f988ff9f..2b28d2db17 100644
--- a/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts
+++ b/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts
@@ -76,6 +76,10 @@ export class GridComponent implements OnInit, AfterViewInit, OnDestroy {
     // the selected fields will be hidden.
     @Input() hideFields: string;
 
+    // When true, only display columns that are declared in the markup
+    // and leave all auto-generated fields hidden.
+    @Input() showDeclaredFieldsOnly: boolean;
+
     // Allow the caller to jump directly to a specific page of
     // grid data.
     @Input() pageOffset: number;
@@ -133,6 +137,7 @@ export class GridComponent implements OnInit, AfterViewInit, OnDestroy {
         this.context.showLinkSelectors = this.showLinkSelectors === true;
         this.context.disableMultiSelect = this.disableMultiSelect === true;
         this.context.rowFlairIsEnabled = this.rowFlairIsEnabled  === true;
+        this.context.showDeclaredFieldsOnly = this.showDeclaredFieldsOnly;
         this.context.rowFlairCallback = this.rowFlairCallback;
         this.context.disablePaging = this.disablePaging === true;
         if (this.showFields) {
diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.ts b/Open-ILS/src/eg2/src/app/share/grid/grid.ts
index fc50f59332..b133d1ad13 100644
--- a/Open-ILS/src/eg2/src/app/share/grid/grid.ts
+++ b/Open-ILS/src/eg2/src/app/share/grid/grid.ts
@@ -130,7 +130,7 @@ export class GridColumnSet {
                 if (idx === 0) {
                     this.columns.unshift(col);
                 } else {
-                    this.columns.splice(idx - 1, 0, col);
+                    this.columns.splice(idx, 0, col);
                 }
                 return true;
             }
@@ -444,6 +444,7 @@ export class GridContext {
     overflowCells: boolean;
     showLinkSelectors: boolean;
     disablePaging: boolean;
+    showDeclaredFieldsOnly: boolean;
 
     // Allow calling code to know when the select-all-rows-in-page
     // action has occurred.
@@ -990,6 +991,10 @@ export class GridContext {
                 }
             }
 
+            if (this.showDeclaredFieldsOnly) {
+                col.hidden = true;
+            }
+
             this.columnSet.add(col);
         });
     }

commit 311d189c820696609dd70fc63d626eee98221d39
Author: Bill Erickson <berickxx at gmail.com>
Date:   Wed Mar 27 10:50:36 2019 -0400

    LP1821382 Make items bookable (part 2)
    
    Including support for passing filters and default context org values
    to admin page grids.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.ts b/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.ts
index 125d3a0fd2..955e95aa5f 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.ts
@@ -1,4 +1,5 @@
 import {Component, Input, OnInit, TemplateRef, ViewChild} from '@angular/core';
+import {ActivatedRoute} from '@angular/router';
 import {IdlService, IdlObject} from '@eg/core/idl.service';
 import {GridDataSource} from '@eg/share/grid/grid';
 import {GridComponent} from '@eg/share/grid/grid.component';
@@ -90,7 +91,12 @@ export class AdminPageComponent implements OnInit {
     viewPerms: string;
     canCreate: boolean;
 
+    // Filters may be passed via URL query param.
+    // They are used to augment the grid data search query.
+    gridFilters: {[key: string]: string | number};
+
     constructor(
+        private route: ActivatedRoute,
         private idl: IdlService,
         private org: OrgService,
         private auth: AuthService,
@@ -101,7 +107,7 @@ export class AdminPageComponent implements OnInit {
         this.translatableFields = [];
     }
 
-    applyOrgValues() {
+    applyOrgValues(orgId?: number) {
 
         if (this.disableOrgFilter) {
             this.orgField = null;
@@ -121,7 +127,7 @@ export class AdminPageComponent implements OnInit {
 
         if (this.orgField) {
             this.orgFieldLabel = this.idlClassDef.field_map[this.orgField].label;
-            this.contextOrg = this.org.root();
+            this.contextOrg = this.org.get(orgId) || this.org.root();
         }
     }
 
@@ -139,6 +145,16 @@ export class AdminPageComponent implements OnInit {
                 this.idlClassDef.table;
         }
 
+        // gridFilters are a JSON encoded string
+        const filters = this.route.snapshot.queryParamMap.get('gridFilters');
+        if (filters) {
+            try {
+                this.gridFilters = JSON.parse(filters);
+            } catch (E) {
+                console.error('Invalid grid filters provided: ', filters)
+            }
+        }
+
         // Limit the view org selector to orgs where the user has
         // permacrud-encoded view permissions.
         const pc = this.idlClassDef.permacrud;
@@ -146,8 +162,9 @@ export class AdminPageComponent implements OnInit {
             this.viewPerms = pc.retrieve.perms;
         }
 
+        const contextOrg = this.route.snapshot.queryParamMap.get('contextOrg');
         this.checkCreatePerms();
-        this.applyOrgValues();
+        this.applyOrgValues(Number(contextOrg));
 
         // If the caller provides not data source, create a generic one.
         if (!this.dataSource) {
@@ -288,6 +305,14 @@ export class AdminPageComponent implements OnInit {
                 order_by: orderBy
             };
 
+            if (!this.contextOrg && !this.gridFilters) {
+                // No org filter -- fetch all rows
+                return this.pcrud.retrieveAll(
+                    this.idlClass, searchOps, {fleshSelectors: true});
+            }
+
+            const search: any = {};
+
             if (this.contextOrg) {
                 // Filter rows by those linking to the context org and
                 // optionally ancestor and descendant org units.
@@ -304,15 +329,18 @@ export class AdminPageComponent implements OnInit {
                         this.org.descendants(this.contextOrg, true));
                 }
 
-                const search = {};
                 search[this.orgField] = orgs;
-                return this.pcrud.search(
-                    this.idlClass, search, searchOps, {fleshSelectors: true});
             }
 
-            // No org filter -- fetch all rows
-            return this.pcrud.retrieveAll(
-                this.idlClass, searchOps, {fleshSelectors: true});
+            if (this.gridFilters) {
+                // Lay the URL grid filters over our search object.
+                Object.keys(this.gridFilters).forEach(key => {
+                    search[key] = this.gridFilters[key];
+                });
+            }
+
+            return this.pcrud.search(
+                this.idlClass, search, searchOps, {fleshSelectors: true});
         };
     }
 
diff --git a/Open-ILS/src/eg2/src/app/staff/share/booking/make-bookable-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/share/booking/make-bookable-dialog.component.html
index cc03fc66dc..25ad596575 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/booking/make-bookable-dialog.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/share/booking/make-bookable-dialog.component.html
@@ -16,11 +16,22 @@
       </button>
     </div>
     <div class="modal-body">
-      <div class="row">
-        <div class="col-lg-12 d-flex justify-content-center">
+      <div class="row" *ngIf="!updateComplete">
+        <div class="col-lg-12">
           <span i18n>Make {{copyIds.length}} Item(s) Bookable?</span>
         </div>
       </div>
+      <div class="row" *ngIf="updateComplete">
+        <div class="col-lg-12 d-flex flex-column">
+          <div i18n>{{numSucceeded}} Item(s) Made Bookable.</div>
+          <div class="mt-2">
+            <a target="_blank" routerLink="/staff/admin/booking/resource"
+              [queryParams]="manageUrlParams()" i18n>
+              Manage Bookable Resources
+            </a>
+          </div>
+        </div>
+      </div>
     </div>
     <div class="modal-footer">
       <ng-container>
diff --git a/Open-ILS/src/eg2/src/app/staff/share/booking/make-bookable-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/share/booking/make-bookable-dialog.component.ts
index 84d7941467..9df3f9f668 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/booking/make-bookable-dialog.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/share/booking/make-bookable-dialog.component.ts
@@ -1,5 +1,4 @@
-import {Component, OnInit, OnDestroy, Input, ViewChild,
-        Renderer2} from '@angular/core';
+import {Component, OnInit, OnDestroy, Input, ViewChild} from '@angular/core';
 import {Subscription} from 'rxjs';
 import {IdlObject} from '@eg/core/idl.service';
 import {NetService} from '@eg/core/net.service';
@@ -7,7 +6,7 @@ import {EventService} from '@eg/core/event.service';
 import {PcrudService} from '@eg/core/pcrud.service';
 import {ToastService} from '@eg/share/toast/toast.service';
 import {AuthService} from '@eg/core/auth.service';
-import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
+import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
 import {DialogComponent} from '@eg/share/dialog/dialog.component';
 import {StringComponent} from '@eg/share/string/string.component';
 
@@ -30,6 +29,8 @@ export class MakeBookableDialogComponent
     numSucceeded: number;
     numFailed: number;
     updateComplete: boolean;
+    newResourceType: number;
+    newResourceOrg: number;
 
     onOpenSub: Subscription;
 
@@ -42,7 +43,6 @@ export class MakeBookableDialogComponent
         private net: NetService,
         private pcrud: PcrudService,
         private evt: EventService,
-        private renderer: Renderer2,
         private auth: AuthService) {
         super(modal); // required for subclassing
     }
@@ -58,6 +58,54 @@ export class MakeBookableDialogComponent
     ngOnDestroy() {
         this.onOpenSub.unsubscribe();
     }
+
+    manageUrlParams(): any {
+        if (this.newResourceOrg) {
+            return {
+                gridFilters: JSON.stringify({type: this.newResourceType}),
+                contextOrg: this.newResourceOrg
+            };
+        }
+    }
+
+    makeBookable() {
+        this.newResourceType = null;
+
+        this.net.request(
+            'open-ils.booking',
+            'open-ils.booking.resources.create_from_copies',
+            this.auth.token(), this.copyIds
+        ).toPromise().then(
+            resp => {
+                // resp.brsrc = [[brsrc.id, acp.id, existed], ...]
+                // resp.brt = [[brt.id, brt.peer_record, existed], ...]
+                const evt = this.evt.parse(resp);
+                if (evt) { return Promise.reject(evt); }
+                this.numSucceeded = resp.brsrc.length;
+                this.newResourceType = resp.brt[0][0]; // new resource ID
+                this.updateComplete = true;
+                this.successMsg.current().then(msg => this.toast.success(msg));
+            },
+            err => Promise.reject(err)
+        ).then(
+            ok => {
+                // Once resource creation is complete, grab the call number
+                // for the first copy to get the owning library
+                this.pcrud.retrieve('acp', this.copyIds[0],
+                    {flesh: 1, flesh_fields: {acp: ['call_number']}})
+                .toPromise().then(copy => {
+                    this.newResourceOrg = copy.call_number().owning_lib();
+                    this.updateComplete = true;
+                });
+            },
+            err => {
+                console.error(err);
+                this.numFailed++;
+                this.errorMsg.current().then(msg => this.toast.danger(msg));
+                this.updateComplete = true;
+            }
+        );
+    }
 }
 
 

commit e1fc8b6423f59dd64f53dc9152b3cd01f412cc4e
Author: Bill Erickson <berickxx at gmail.com>
Date:   Tue Mar 26 18:08:44 2019 -0400

    LP1821382 Make items bookable (part 1)
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
index aab101d743..b07938a4c8 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
@@ -4,6 +4,7 @@ import {CatalogCommonModule} from '@eg/share/catalog/catalog-common.module';
 import {CatalogRoutingModule} from './routing.module';
 import {HoldsModule} from '@eg/staff/share/holds/holds.module';
 import {HoldingsModule} from '@eg/staff/share/holdings/holdings.module';
+import {BookingModule} from '@eg/staff/share/booking/booking.module';
 import {CatalogComponent} from './catalog.component';
 import {SearchFormComponent} from './search-form.component';
 import {ResultsComponent} from './result/results.component';
@@ -50,7 +51,8 @@ import {ConjoinedComponent} from './record/conjoined.component';
     CatalogCommonModule,
     CatalogRoutingModule,
     HoldsModule,
-    HoldingsModule
+    HoldingsModule,
+    BookingModule
   ],
   providers: [
     StaffCatalogService
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
index bf5ee84928..830538fb9e 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
@@ -51,6 +51,7 @@
 <eg-delete-volcopy-dialog #deleteVolcopy></eg-delete-volcopy-dialog>
 <eg-bucket-dialog #bucketDialog></eg-bucket-dialog>
 <eg-conjoined-items-dialog #conjoinedDialog></eg-conjoined-items-dialog>
+<eg-make-bookable-dialog #makeBookableDialog></eg-make-bookable-dialog>
 
 <!-- holdings grid -->
 <div class='eg-copies w-100 mt-3'>
@@ -228,7 +229,8 @@
     </eg-grid-column>
     <eg-grid-column path="copyCount" datatype="number" label="Copies" i18n-label>
     </eg-grid-column>
-    <eg-grid-column path="volume._label" name="call_number.label" label="Call Number" i18n-label>
+    <eg-grid-column path="volume._label" name="call_number.label" 
+      label="Call Number" i18n-label>
     </eg-grid-column>
     <eg-grid-column path="copy.barcode" name="barcode" label="Barcode" i18n-label>
     </eg-grid-column>
@@ -238,15 +240,18 @@
       datatype="org_unit"></eg-grid-column>
     <eg-grid-column i18n-label label="Due Date" path="circ.due_date" 
       datatype="timestamp"></eg-grid-column>
-    <eg-grid-column i18n-label label="Shelving Location" path="copy.location.name" name="location.name">
+    <eg-grid-column i18n-label label="Shelving Location" 
+      path="copy.location.name" name="location.name">
     </eg-grid-column>
     <eg-grid-column i18n-label label="Circulation Modifier" 
       path="copy.circ_modifier" name="circ_modifier">
     </eg-grid-column>
-    <eg-grid-column i18n-label label="Copy Number" path="copy.copy_number" name="copy_number" [hidden]="true">
+    <eg-grid-column i18n-label label="Copy Number" path="copy.copy_number" 
+      name="copy_number" [hidden]="true">
     </eg-grid-column>
 
-    <eg-grid-column i18n-label label="Status" path="copy.status.name" name="status_name">
+    <eg-grid-column i18n-label label="Status" 
+      path="copy.status.name" name="status_name">
     </eg-grid-column>
     <eg-grid-column i18n-label label="Call Number Prefix" 
       path="volume.prefix.label" name="call_number.prefix.label" [hidden]="true">
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
index 555ac83ac7..15fbb071a6 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
@@ -30,6 +30,8 @@ import {BucketDialogComponent
     } from '@eg/staff/share/buckets/bucket-dialog.component';
 import {ConjoinedItemsDialogComponent
     } from '@eg/staff/share/holdings/conjoined-items-dialog.component';
+import {MakeBookableDialogComponent
+    } from '@eg/staff/share/booking/make-bookable-dialog.component';
 
 // The holdings grid models a single HoldingsTree, composed of HoldingsTreeNodes
 // flattened on-demand into a list of HoldingEntry objects.
@@ -103,6 +105,8 @@ export class HoldingsMaintenanceComponent implements OnInit {
         private bucketDialog: BucketDialogComponent;
     @ViewChild('conjoinedDialog')
         private conjoinedDialog: ConjoinedItemsDialogComponent;
+    @ViewChild('makeBookableDialog')
+        private makeBookableDialog: MakeBookableDialogComponent;
 
     holdingsTree: HoldingsTree;
 
@@ -882,7 +886,11 @@ export class HoldingsMaintenanceComponent implements OnInit {
     makeBookable(rows: HoldingsEntry[]) {
         const copyIds = this.selectedCopyIds(rows);
         if (copyIds.length > 0) {
-            alert('TODO');
+            this.makeBookableDialog.copyIds = copyIds;
+            this.makeBookableDialog.open({}).then(
+                modified => {}, // No refresh needed
+                dismissed => {}
+            )
         }
     }
 }
diff --git a/Open-ILS/src/eg2/src/app/staff/share/booking/booking.module.ts b/Open-ILS/src/eg2/src/app/staff/share/booking/booking.module.ts
new file mode 100644
index 0000000000..f1f11c7b72
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/booking/booking.module.ts
@@ -0,0 +1,20 @@
+import {NgModule} from '@angular/core';
+import {StaffCommonModule} from '@eg/staff/common.module';
+import {MakeBookableDialogComponent} from './make-bookable-dialog.component';
+
+ at NgModule({
+    declarations: [
+        MakeBookableDialogComponent
+    ],
+    imports: [
+        StaffCommonModule
+    ],
+    exports: [
+        MakeBookableDialogComponent
+    ],
+    providers: [
+    ]
+})
+
+export class BookingModule {}
+
diff --git a/Open-ILS/src/eg2/src/app/staff/share/booking/make-bookable-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/share/booking/make-bookable-dialog.component.html
new file mode 100644
index 0000000000..cc03fc66dc
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/booking/make-bookable-dialog.component.html
@@ -0,0 +1,40 @@
+
+
+<eg-string #successMsg
+    text="Successfully Made Item(s) Bookable" i18n-text></eg-string>
+<eg-string #errorMsg 
+    text="Failed To Make Item(s) Bookable" i18n-text></eg-string>
+
+<ng-template #dialogContent>
+    <div class="modal-header bg-info">
+      <h4 class="modal-title">
+        <span i18n>Make Items Bookable</span>
+      </h4>
+      <button type="button" class="close" 
+        i18n-aria-label aria-label="Close" (click)="dismiss('cross_click')">
+        <span aria-hidden="true">×</span>
+      </button>
+    </div>
+    <div class="modal-body">
+      <div class="row">
+        <div class="col-lg-12 d-flex justify-content-center">
+          <span i18n>Make {{copyIds.length}} Item(s) Bookable?</span>
+        </div>
+      </div>
+    </div>
+    <div class="modal-footer">
+      <ng-container>
+        <button type="button" class="btn btn-warning" 
+          (click)="dismiss('canceled')" i18n>Cancel</button>
+        <ng-container *ngIf="!updateComplete">
+          <button type="button" class="btn btn-success" 
+            (click)="makeBookable()" i18n>Make Bookable</button>
+        </ng-container>
+        <ng-container *ngIf="updateComplete">
+          <button type="button" class="btn btn-success" 
+            (click)="close(numSucceeded > 0)" i18n>Done</button>
+        </ng-container>
+      </ng-container>
+    </div>
+  </ng-template>
+  
diff --git a/Open-ILS/src/eg2/src/app/staff/share/booking/make-bookable-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/share/booking/make-bookable-dialog.component.ts
new file mode 100644
index 0000000000..84d7941467
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/booking/make-bookable-dialog.component.ts
@@ -0,0 +1,64 @@
+import {Component, OnInit, OnDestroy, Input, ViewChild,
+        Renderer2} from '@angular/core';
+import {Subscription} from 'rxjs';
+import {IdlObject} from '@eg/core/idl.service';
+import {NetService} from '@eg/core/net.service';
+import {EventService} from '@eg/core/event.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {AuthService} from '@eg/core/auth.service';
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+import {StringComponent} from '@eg/share/string/string.component';
+
+/**
+ * Dialog for making items bookable
+ */
+
+ at Component({
+  selector: 'eg-make-bookable-dialog',
+  templateUrl: 'make-bookable-dialog.component.html'
+})
+export class MakeBookableDialogComponent
+    extends DialogComponent implements OnInit, OnDestroy {
+
+    // Note copyIds must refer to copies that belong to a single
+    // bib record.
+    @Input() copyIds: number[];
+    copies: IdlObject[];
+
+    numSucceeded: number;
+    numFailed: number;
+    updateComplete: boolean;
+
+    onOpenSub: Subscription;
+
+    @ViewChild('successMsg') private successMsg: StringComponent;
+    @ViewChild('errorMsg') private errorMsg: StringComponent;
+
+    constructor(
+        private modal: NgbModal, // required for passing to parent
+        private toast: ToastService,
+        private net: NetService,
+        private pcrud: PcrudService,
+        private evt: EventService,
+        private renderer: Renderer2,
+        private auth: AuthService) {
+        super(modal); // required for subclassing
+    }
+
+    ngOnInit() {
+        this.onOpenSub = this.onOpen$.subscribe(async () => {
+            this.numSucceeded = 0;
+            this.numFailed = 0;
+            this.updateComplete = false;
+        });
+    }
+
+    ngOnDestroy() {
+        this.onOpenSub.unsubscribe();
+    }
+}
+
+
+

commit 6534a4e20037fcfd69c89039c17396be00e4cc77
Author: Bill Erickson <berickxx at gmail.com>
Date:   Tue Mar 26 16:51:54 2019 -0400

    LP1821382 Conjoined items grid
    
    Record detail conjoined items grid, with actions for batch-changing the
    peer type and anctions for unlinking selected rows.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index 1f510735fb..7270ee3be6 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -7476,7 +7476,7 @@ SELECT  usr,
 			<field reporter:label="Last Editing User" name="editor" reporter:datatype="link"/>
 			<field reporter:label="Fine Level" name="fine_level" reporter:datatype="int"/>
 			<field reporter:label="Is Holdable" name="holdable" reporter:datatype="bool" />
-			<field reporter:label="Copy ID" name="id" reporter:datatype="id"/>
+			<field reporter:label="Copy ID" name="id" reporter:selector="barcode" reporter:datatype="id"/>
 			<field reporter:label="Loan Duration" name="loan_duration" reporter:datatype="int"/>
 			<field reporter:label="Shelving Location" name="location" reporter:datatype="link"/>
 			<field reporter:label="OPAC Visible" name="opac_visible" reporter:datatype="bool" />
diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-action.component.ts b/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-action.component.ts
index 4f8555404f..c84867fcf5 100644
--- a/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-action.component.ts
+++ b/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-action.component.ts
@@ -9,17 +9,26 @@ import {GridComponent} from './grid.component';
 
 export class GridToolbarActionComponent implements OnInit {
 
+    toolbarAction: GridToolbarAction;
+
     // Note most input fields should match class fields for GridColumn
     @Input() label: string;
 
     // Register to click events
     @Output() onClick: EventEmitter<any []>;
 
+    // When present, actions will be grouped by the provided label.
+    @Input() group: string;
+
     // DEPRECATED: Pass a reference to a function that is called on click.
     @Input() action: (rows: any[]) => any;
 
-    // When present, actions will be grouped by the provided label.
-    @Input() group: string;
+    @Input() set disabled(d: boolean) {
+        this.toolbarAction.disabled = d;
+    }
+    get disabled(): boolean {
+        return this.toolbarAction.disabled;
+    }
 
     // Optional: add a function that returns true or false.
     // If true, this action will be disabled; if false
@@ -30,6 +39,7 @@ export class GridToolbarActionComponent implements OnInit {
     // get a reference to our container grid.
     constructor(@Host() private grid: GridComponent) {
         this.onClick = new EventEmitter<any []>();
+        this.toolbarAction = new GridToolbarAction();
     }
 
     ngOnInit() {
@@ -39,12 +49,16 @@ export class GridToolbarActionComponent implements OnInit {
             return;
         }
 
-        const action = new GridToolbarAction();
-        action.label = this.label;
-        action.action = this.action;
-        action.onClick = this.onClick;
-        action.group = this.group;
-        action.disableOnRows = this.disableOnRows;
-        this.grid.context.toolbarActions.push(action);
+        if (this.action) {
+            console.debug('toolbar [action] is deprecated.  use (onClick) instead.')
+        }
+
+        this.toolbarAction.label = this.label;
+        this.toolbarAction.onClick = this.onClick;
+        this.toolbarAction.group = this.group;
+        this.toolbarAction.action = this.action;
+        this.toolbarAction.disabled = this.disabled;
+        this.toolbarAction.disableOnRows = this.disableOnRows;
+        this.grid.context.toolbarActions.push(this.toolbarAction);
     }
 }
diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.ts b/Open-ILS/src/eg2/src/app/share/grid/grid.ts
index 7cf5f53487..fc50f59332 100644
--- a/Open-ILS/src/eg2/src/app/share/grid/grid.ts
+++ b/Open-ILS/src/eg2/src/app/share/grid/grid.ts
@@ -1021,6 +1021,7 @@ export class GridToolbarAction {
     onClick: EventEmitter<any []>;
     action: (rows: any[]) => any; // DEPRECATED
     group: string;
+    disabled: boolean;
     isGroup: boolean; // used for group placeholder entries
     disableOnRows: (rows: any[]) => boolean;
 }
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
index 46d25d7d1c..aab101d743 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
@@ -22,6 +22,7 @@ import {PartMergeDialogComponent} from './record/part-merge-dialog.component';
 import {BrowseComponent} from './browse.component';
 import {BrowseResultsComponent} from './browse/results.component';
 import {HoldingsMaintenanceComponent} from './record/holdings.component';
+import {ConjoinedComponent} from './record/conjoined.component';
 
 @NgModule({
   declarations: [
@@ -41,6 +42,7 @@ import {HoldingsMaintenanceComponent} from './record/holdings.component';
     PartMergeDialogComponent,
     BrowseComponent,
     BrowseResultsComponent,
+    ConjoinedComponent,
     HoldingsMaintenanceComponent
   ],
   imports: [
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/conjoined.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/record/conjoined.component.html
new file mode 100644
index 0000000000..30b8aa5d61
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/conjoined.component.html
@@ -0,0 +1,27 @@
+
+
+<eg-conjoined-items-dialog #conjoinedDialog 
+  [peerRecord]="recordId" [modifyAll]="true">
+</eg-conjoined-items-dialog>
+
+<eg-confirm-dialog #confirmUnlink 
+  i18n-dialogTitle dialogTitle="Confirm Unlink"
+  i18n-dialogBody dialogBody="Unlink {{idsToUnlink.length}} Items?">
+</eg-confirm-dialog>
+
+<div class="mt-3">
+
+  <eg-grid #conjoinedGrid idlClass="bpbcm" [dataSource]="gridDataSource"
+      [sortable]="true" persistKey="catalog.record.conjoined" class="mt-3">
+    
+    <eg-grid-toolbar-button [disabled]="!hasPerm" i18n-label 
+      label="Change Type" (onClick)="openConjoinedDialog()">
+    </eg-grid-toolbar-button>
+    
+    <eg-grid-toolbar-action 
+      [disabled]="!hasPerm"
+      label="Unlink" i18n-label (onClick)="unlink($event)">
+    </eg-grid-toolbar-action>
+
+  </eg-grid>
+</div>
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/conjoined.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/record/conjoined.component.ts
new file mode 100644
index 0000000000..560214f99b
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/conjoined.component.ts
@@ -0,0 +1,109 @@
+import {Component, OnInit, Input, ViewChild} from '@angular/core';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {Pager} from '@eg/share/util/pager';
+import {OrgService} from '@eg/core/org.service';
+import {PermService} from '@eg/core/perm.service';
+import {GridDataSource} from '@eg/share/grid/grid';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
+import {ConjoinedItemsDialogComponent
+    } from '@eg/staff/share/holdings/conjoined-items-dialog.component';
+
+/** Conjoined items per record grid */
+
+ at Component({
+  selector: 'eg-catalog-record-conjoined',
+  templateUrl: 'conjoined.component.html'
+})
+export class ConjoinedComponent implements OnInit {
+
+    @Input() recordId: number;
+
+    hasPerm: boolean;
+    gridDataSource: GridDataSource;
+    idsToUnlink: number[];
+
+    @ViewChild('conjoinedGrid') private grid: GridComponent;
+
+    @ViewChild('conjoinedDialog')
+        private conjoinedDialog: ConjoinedItemsDialogComponent;
+
+    @ViewChild('confirmUnlink')
+        private confirmUnlink: ConfirmDialogComponent;
+
+    constructor(
+        private idl: IdlService,
+        private org: OrgService,
+        private pcrud: PcrudService,
+        private perm: PermService
+    ) {
+        this.gridDataSource = new GridDataSource();
+        this.idsToUnlink = [];
+    }
+
+    ngOnInit() {
+        // Load edit perms
+        this.perm.hasWorkPermHere(['UPDATE_COPY'])
+            .then(perms => this.hasPerm = perms.UPDATE_COPY);
+
+        this.gridDataSource.getRows = (pager: Pager, sort: any[]) => {
+            const orderBy: any = {};
+
+            if (sort.length) { // Sort provided by grid.
+                orderBy.bmp = sort[0].name + ' ' + sort[0].dir;
+            } else {
+                orderBy.bmp = 'id';
+            }
+
+            const searchOps = {
+                offset: pager.offset,
+                limit: pager.limit,
+                order_by: orderBy
+            };
+
+            return this.pcrud.search('bpbcm',
+                {peer_record: this.recordId}, searchOps, {fleshSelectors: true});
+        };
+    }
+
+    async unlink(rows: any) {
+
+        this.idsToUnlink = rows.map(r => r.target_copy().id());
+        if (this.idsToUnlink.length === 0) { return; }
+
+        try { // rejects on dismiss, which results in an Error
+            await this.confirmUnlink.open({size: 'sm'});
+        } catch (dismissed) {return;}
+
+        const maps = [];
+        this.pcrud.search('bpbcm',
+            {target_copy: this.idsToUnlink, peer_record: this.recordId})
+        .subscribe(
+            map => maps.push(map),
+            err => {},
+            () => {
+                this.pcrud.remove(maps).subscribe(
+                    ok => console.debug('deleted map ', ok),
+                    err => console.error(err),
+                    ()  => {
+                        this.idsToUnlink = [];
+                        this.grid.reload();
+                    }
+                )
+            }
+        );
+    }
+
+    openConjoinedDialog() {
+        this.conjoinedDialog.open({size: 'sm'}).then(
+            modified => {
+                if (modified) {
+                    this.grid.reload();
+                }
+            },
+            notOk => {}
+        );
+    }
+}
+
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html
index 450887034d..8be4524985 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html
@@ -72,13 +72,8 @@
       </ngb-tab>
       <ngb-tab title="Conjoined Items" i18n-title id="conjoined">
         <ng-template ngbTabContent>
-          <div class="alert alert-info mt-3" i18n>
-            Conjoined Items not yet implemented.  See the
-            <a target="_blank"
-              href="/eg/staff/cat/catalog/record/{{recordId}}/conjoined">
-              AngularJS Conjoined Items Tab.
-            </a>
-          </div>
+          <eg-catalog-record-conjoined [recordId]="recordId">
+          </eg-catalog-record-conjoined>
         </ng-template>
       </ngb-tab>
     </ngb-tabset>
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.html
index 906ce24765..9801c3ee77 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.html
@@ -1,14 +1,14 @@
 
 
 <eg-string #successMsg
-    text="Successfully Attached Conjoined Item(s)" i18n-text></eg-string>
+    text="Successfully Attached/Modified Conjoined Item(s)" i18n-text></eg-string>
 <eg-string #errorMsg 
-    text="Failed To Attach Conjoined Item(s)" i18n-text></eg-string>
+    text="Failed To Attach/Modify Conjoined Item(s)" i18n-text></eg-string>
 
 <ng-template #dialogContent>
     <div class="modal-header bg-info">
       <h4 class="modal-title">
-        <span i18n>Attach {{copyIds.length}} Conjoined Item(s)</span>
+        <span i18n>Attach/Modify {{copyIds.length}} Conjoined Item(s)</span>
       </h4>
       <button type="button" class="close" 
         i18n-aria-label aria-label="Close" (click)="dismiss('cross_click')">
@@ -34,7 +34,7 @@
           (click)="dismiss('canceled')" i18n>Cancel</button>
         <button type="button" class="btn btn-success" 
           (click)="linkCopies()" [disabled]="!peerType" i18n>
-          Attach
+          Attach/Modify
         </button>
       </ng-container>
     </div>
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.ts
index 69ff7e79bb..98bd462bf1 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.ts
@@ -1,6 +1,6 @@
 import {Component, OnInit, OnDestroy, Input, ViewChild, Renderer2} from '@angular/core';
 import {Subscription} from 'rxjs';
-import {IdlService} from '@eg/core/idl.service';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
 import {PcrudService} from '@eg/core/pcrud.service';
 import {ToastService} from '@eg/share/toast/toast.service';
 import {StoreService} from '@eg/core/store.service';
@@ -23,22 +23,23 @@ export class ConjoinedItemsDialogComponent
     extends DialogComponent implements OnInit, OnDestroy {
 
     @Input() copyIds: number[];
-    ids: number[]; // copy of list so we can pop()
+
+    // If true, ignore the provided copyIds array and fetch all of
+    // the linked copies to work on.
+    @Input() modifyAll: boolean;
+
+    // If peerRecord is not set, the localStorage value will be used.
+    @Input() peerRecord: number;
 
     peerType: number;
     numSucceeded: number;
     numFailed: number;
     peerTypes: ComboboxEntry[];
-    peerRecord: number;
     existingMaps: any;
-
     onOpenSub: Subscription;
 
-    @ViewChild('successMsg')
-        private successMsg: StringComponent;
-
-    @ViewChild('errorMsg')
-        private errorMsg: StringComponent;
+    @ViewChild('successMsg') private successMsg: StringComponent;
+    @ViewChild('errorMsg') private errorMsg: StringComponent;
 
     constructor(
         private modal: NgbModal, // required for passing to parent
@@ -48,25 +49,33 @@ export class ConjoinedItemsDialogComponent
         private localStore: StoreService) {
         super(modal); // required for subclassing
         this.peerTypes = [];
+        this.copyIds = [];
     }
 
     ngOnInit() {
         this.onOpenSub = this.onOpen$.subscribe(() => {
-            this.ids = [].concat(this.copyIds);
+            if (this.modifyAll) {
+                // This will be set once the list of copies to
+                // modify has been fetched.
+                this.copyIds = [];
+            }
             this.numSucceeded = 0;
             this.numFailed = 0;
-            this.peerRecord =
-                this.localStore.getLocalItem('eg.cat.marked_conjoined_record');
 
             if (!this.peerRecord) {
-                this.close(false);
+                this.peerRecord =
+                    this.localStore.getLocalItem('eg.cat.marked_conjoined_record');
+
+                    if (!this.peerRecord) {
+                    this.close(false);
+                }
             }
 
             if (this.peerTypes.length === 0) {
                 this.getPeerTypes();
             }
 
-            this.fetchExisting();
+            this.fetchExistingMaps();
         });
     }
 
@@ -74,17 +83,29 @@ export class ConjoinedItemsDialogComponent
         this.onOpenSub.unsubscribe();
     }
 
-    fetchExisting() {
+    fetchExistingMaps() {
         this.existingMaps = {};
-        this.pcrud.search('bpbcm',
-            {target_copy: this.copyIds, peer_record: this.peerRecord})
-        .subscribe(map => this.existingMaps[map.target_copy()] = map);
+        const search: any = {
+            peer_record: this.peerRecord
+        };
+
+        if (!this.modifyAll) {
+            search.target_copy = this.copyIds;
+        }
+
+        this.pcrud.search('bpbcm', search)
+        .subscribe(map => {
+            this.existingMaps[map.target_copy()] = map;
+            if (this.modifyAll) {
+                this.copyIds.push(map.target_copy());
+            }
+        });
     }
 
+    // Fetch and map peer types to combobox entries
     getPeerTypes(): Promise<any> {
         return this.pcrud.retrieveAll('bpt', {}, {atomic: true}).toPromise()
         .then(types =>
-            // Map types to ComboboxEntry's
             this.peerTypes = types.map(t => ({id: t.id(), label: t.name()}))
         );
     }
@@ -97,37 +118,36 @@ export class ConjoinedItemsDialogComponent
         }
     }
 
-    linkCopies(): Promise<any> {
-
-        if (this.ids.length === 0) {
-            this.close(this.numSucceeded > 0);
-            return Promise.resolve();
-        }
-
-        const id = this.ids.pop();
-        const map = this.existingMaps[id] || this.idl.create('bpbcm');
-        map.peer_record(this.peerRecord);
-        map.target_copy(id);
-        map.peer_type(this.peerType);
+    // Create or update peer copy links.
+    linkCopies() {
+
+        const maps: IdlObject[] = [];
+        this.copyIds.forEach(id => {
+            let map: IdlObject;
+            if (this.existingMaps[id]) {
+                map = this.existingMaps[id];
+                map.ischanged(true);
+            } else {
+                map = this.idl.create('bpbcm');
+                map.isnew(true);
+            }
 
-        let promise: Promise<any>;
-        if (this.existingMaps[id]) {
-            promise = this.pcrud.update(map).toPromise();
-        } else {
-            promise = this.pcrud.create(map).toPromise();
-        }
+            map.peer_record(this.peerRecord);
+            map.target_copy(id);
+            map.peer_type(this.peerType);
+            maps.push(map);
+        });
 
-        return promise.then(
-            ok => {
-                this.successMsg.current().then(msg => this.toast.success(msg));
-                this.numSucceeded++;
-                return this.linkCopies();
-            },
+        return this.pcrud.autoApply(maps).subscribe(
+            ok => this.numSucceeded++,
             err => {
                 this.numFailed++;
                 console.error(err);
                 this.errorMsg.current().then(msg => this.toast.warning(msg));
-                return this.linkCopies();
+            },
+            () => {
+                this.successMsg.current().then(msg => this.toast.success(msg));
+                this.close(this.numSucceeded > 0);
             }
         );
     }

commit 4b55b65c5fdd482315fe635e4d130b7613bf60f6
Author: Bill Erickson <berickxx at gmail.com>
Date:   Tue Mar 26 14:15:12 2019 -0400

    LP1821382 Booking menu entry placeholders
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
index 895734512f..bf5ee84928 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
@@ -116,8 +116,20 @@
     <eg-grid-toolbar-action
       i18n-group group="Add" i18n-label label="Add Items To Bucket"
       (onClick)="openBucketDialog($event)">
-    Z</eg-grid-toolbar-action>
+    </eg-grid-toolbar-action>
+
+    <!-- row actions: Booking -->
+
+    <eg-grid-toolbar-action
+      i18n-group group="Booking" i18n-label label="Book Item Now"
+      (onClick)="bookItems($event)">
+    </eg-grid-toolbar-action>
 
+    <eg-grid-toolbar-action
+      i18n-group group="Booking" i18n-label label="Make Items Bookable"
+      (onClick)="makeBookable($event)">
+    </eg-grid-toolbar-action>
+    
     <!-- row actions: Edit -->
 
     <eg-grid-toolbar-action
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
index 6405b0d8ea..555ac83ac7 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
@@ -871,4 +871,18 @@ export class HoldingsMaintenanceComponent implements OnInit {
             );
         }
     }
+
+    bookItems(rows: HoldingsEntry[]) {
+        const copyIds = this.selectedCopyIds(rows);
+        if (copyIds.length > 0) {
+            alert('TODO');
+        }
+    }
+
+    makeBookable(rows: HoldingsEntry[]) {
+        const copyIds = this.selectedCopyIds(rows);
+        if (copyIds.length > 0) {
+            alert('TODO');
+        }
+    }
 }

commit 099eabb17082a3d3a0a0d411dd591ba1ed40fcd7
Author: Bill Erickson <berickxx at gmail.com>
Date:   Tue Mar 26 13:58:46 2019 -0400

    LP1821382 Conjoined linking repairs
    
    Modify existing copy->record conjoined links where necessary instead of
    create duplicates.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.ts
index 51000a77cd..69ff7e79bb 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.ts
@@ -30,6 +30,7 @@ export class ConjoinedItemsDialogComponent
     numFailed: number;
     peerTypes: ComboboxEntry[];
     peerRecord: number;
+    existingMaps: any;
 
     onOpenSub: Subscription;
 
@@ -64,6 +65,8 @@ export class ConjoinedItemsDialogComponent
             if (this.peerTypes.length === 0) {
                 this.getPeerTypes();
             }
+
+            this.fetchExisting();
         });
     }
 
@@ -71,6 +74,13 @@ export class ConjoinedItemsDialogComponent
         this.onOpenSub.unsubscribe();
     }
 
+    fetchExisting() {
+        this.existingMaps = {};
+        this.pcrud.search('bpbcm',
+            {target_copy: this.copyIds, peer_record: this.peerRecord})
+        .subscribe(map => this.existingMaps[map.target_copy()] = map);
+    }
+
     getPeerTypes(): Promise<any> {
         return this.pcrud.retrieveAll('bpt', {}, {atomic: true}).toPromise()
         .then(types =>
@@ -95,12 +105,19 @@ export class ConjoinedItemsDialogComponent
         }
 
         const id = this.ids.pop();
-        const map = this.idl.create('bpbcm');
+        const map = this.existingMaps[id] || this.idl.create('bpbcm');
         map.peer_record(this.peerRecord);
         map.target_copy(id);
         map.peer_type(this.peerType);
 
-        return this.pcrud.create(map).toPromise().then(
+        let promise: Promise<any>;
+        if (this.existingMaps[id]) {
+            promise = this.pcrud.update(map).toPromise();
+        } else {
+            promise = this.pcrud.create(map).toPromise();
+        }
+
+        return promise.then(
             ok => {
                 this.successMsg.current().then(msg => this.toast.success(msg));
                 this.numSucceeded++;

commit 0b671ab7d7e4d42a478530d19815bb370291ff83
Author: Bill Erickson <berickxx at gmail.com>
Date:   Tue Mar 26 13:30:54 2019 -0400

    LP1821382 Link as conjoined items menu action
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
index dddba6ede2..895734512f 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
@@ -50,6 +50,7 @@
 <eg-replace-barcode-dialog #replaceBarcode></eg-replace-barcode-dialog>
 <eg-delete-volcopy-dialog #deleteVolcopy></eg-delete-volcopy-dialog>
 <eg-bucket-dialog #bucketDialog></eg-bucket-dialog>
+<eg-conjoined-items-dialog #conjoinedDialog></eg-conjoined-items-dialog>
 
 <!-- holdings grid -->
 <div class='eg-copies w-100 mt-3'>
@@ -85,6 +86,11 @@
       i18n-label label="Request Items" (onClick)="requestItems($event)">
     </eg-grid-toolbar-action>
 
+    <eg-grid-toolbar-action
+      i18n-label label="Link as Conjoined to Marked Bib Record"
+      (onClick)="openConjoinedDialog($event)">
+    </eg-grid-toolbar-action>
+
     <!-- row actions : Add -->
 
     <eg-grid-toolbar-action
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
index 75efc7a461..6405b0d8ea 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
@@ -28,6 +28,8 @@ import {DeleteVolcopyDialogComponent
     } from '@eg/staff/share/holdings/delete-volcopy-dialog.component';
 import {BucketDialogComponent
     } from '@eg/staff/share/buckets/bucket-dialog.component';
+import {ConjoinedItemsDialogComponent
+    } from '@eg/staff/share/holdings/conjoined-items-dialog.component';
 
 // The holdings grid models a single HoldingsTree, composed of HoldingsTreeNodes
 // flattened on-demand into a list of HoldingEntry objects.
@@ -99,6 +101,8 @@ export class HoldingsMaintenanceComponent implements OnInit {
         private deleteVolcopy: DeleteVolcopyDialogComponent;
     @ViewChild('bucketDialog')
         private bucketDialog: BucketDialogComponent;
+    @ViewChild('conjoinedDialog')
+        private conjoinedDialog: ConjoinedItemsDialogComponent;
 
     holdingsTree: HoldingsTree;
 
@@ -851,7 +855,18 @@ export class HoldingsMaintenanceComponent implements OnInit {
             this.bucketDialog.itemIds = copyIds;
             this.bucketDialog.open({size: 'lg'}).then(
                 // No need to reload the grid after adding items to buckets.
-                () => {},
+                ok => {},
+                dismissed => {}
+            );
+        }
+    }
+
+    openConjoinedDialog(rows: HoldingsEntry[]) {
+        const copyIds = this.selectedCopyIds(rows);
+        if (copyIds.length > 0) {
+            this.conjoinedDialog.copyIds = copyIds;
+            this.conjoinedDialog.open({size: 'sm'}).then(
+                ok => {}, // No grid reload required
                 dismissed => {}
             );
         }
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.html
new file mode 100644
index 0000000000..906ce24765
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.html
@@ -0,0 +1,42 @@
+
+
+<eg-string #successMsg
+    text="Successfully Attached Conjoined Item(s)" i18n-text></eg-string>
+<eg-string #errorMsg 
+    text="Failed To Attach Conjoined Item(s)" i18n-text></eg-string>
+
+<ng-template #dialogContent>
+    <div class="modal-header bg-info">
+      <h4 class="modal-title">
+        <span i18n>Attach {{copyIds.length}} Conjoined Item(s)</span>
+      </h4>
+      <button type="button" class="close" 
+        i18n-aria-label aria-label="Close" (click)="dismiss('cross_click')">
+        <span aria-hidden="true">×</span>
+      </button>
+    </div>
+    <div class="modal-body">
+      <div class="row form-validated">
+        <div class="col-lg-4" i18n>
+          <label for="cbox-peer-types">Peer Type:</label>
+        </div>
+        <div class="col-lg-8">
+          <eg-combobox [entries]="peerTypes" [required]="true"
+            i18n-placeholder placeholder="Peer Type..."
+            id="cbox-peer-types" (onChange)="peerTypeChanged($event)">
+          </eg-combobox>
+        </div>
+      </div>
+    </div>
+    <div class="modal-footer">
+      <ng-container>
+        <button type="button" class="btn btn-warning" 
+          (click)="dismiss('canceled')" i18n>Cancel</button>
+        <button type="button" class="btn btn-success" 
+          (click)="linkCopies()" [disabled]="!peerType" i18n>
+          Attach
+        </button>
+      </ng-container>
+    </div>
+  </ng-template>
+  
\ No newline at end of file
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.ts
new file mode 100644
index 0000000000..51000a77cd
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.ts
@@ -0,0 +1,120 @@
+import {Component, OnInit, OnDestroy, Input, ViewChild, Renderer2} from '@angular/core';
+import {Subscription} from 'rxjs';
+import {IdlService} from '@eg/core/idl.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {StoreService} from '@eg/core/store.service';
+import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+import {StringComponent} from '@eg/share/string/string.component';
+import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
+
+
+/**
+ * Dialog for linking conjoined items.
+ */
+
+ at Component({
+  selector: 'eg-conjoined-items-dialog',
+  templateUrl: 'conjoined-items-dialog.component.html'
+})
+
+export class ConjoinedItemsDialogComponent
+    extends DialogComponent implements OnInit, OnDestroy {
+
+    @Input() copyIds: number[];
+    ids: number[]; // copy of list so we can pop()
+
+    peerType: number;
+    numSucceeded: number;
+    numFailed: number;
+    peerTypes: ComboboxEntry[];
+    peerRecord: number;
+
+    onOpenSub: Subscription;
+
+    @ViewChild('successMsg')
+        private successMsg: StringComponent;
+
+    @ViewChild('errorMsg')
+        private errorMsg: StringComponent;
+
+    constructor(
+        private modal: NgbModal, // required for passing to parent
+        private toast: ToastService,
+        private idl: IdlService,
+        private pcrud: PcrudService,
+        private localStore: StoreService) {
+        super(modal); // required for subclassing
+        this.peerTypes = [];
+    }
+
+    ngOnInit() {
+        this.onOpenSub = this.onOpen$.subscribe(() => {
+            this.ids = [].concat(this.copyIds);
+            this.numSucceeded = 0;
+            this.numFailed = 0;
+            this.peerRecord =
+                this.localStore.getLocalItem('eg.cat.marked_conjoined_record');
+
+            if (!this.peerRecord) {
+                this.close(false);
+            }
+
+            if (this.peerTypes.length === 0) {
+                this.getPeerTypes();
+            }
+        });
+    }
+
+    ngOnDestroy() {
+        this.onOpenSub.unsubscribe();
+    }
+
+    getPeerTypes(): Promise<any> {
+        return this.pcrud.retrieveAll('bpt', {}, {atomic: true}).toPromise()
+        .then(types =>
+            // Map types to ComboboxEntry's
+            this.peerTypes = types.map(t => ({id: t.id(), label: t.name()}))
+        );
+    }
+
+    peerTypeChanged(entry: ComboboxEntry) {
+        if (entry) {
+            this.peerType = entry.id;
+        } else {
+            this.peerType = null;
+        }
+    }
+
+    linkCopies(): Promise<any> {
+
+        if (this.ids.length === 0) {
+            this.close(this.numSucceeded > 0);
+            return Promise.resolve();
+        }
+
+        const id = this.ids.pop();
+        const map = this.idl.create('bpbcm');
+        map.peer_record(this.peerRecord);
+        map.target_copy(id);
+        map.peer_type(this.peerType);
+
+        return this.pcrud.create(map).toPromise().then(
+            ok => {
+                this.successMsg.current().then(msg => this.toast.success(msg));
+                this.numSucceeded++;
+                return this.linkCopies();
+            },
+            err => {
+                this.numFailed++;
+                console.error(err);
+                this.errorMsg.current().then(msg => this.toast.warning(msg));
+                return this.linkCopies();
+            }
+        );
+    }
+}
+
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.module.ts b/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.module.ts
index 97a65ce0e2..d9ae4fb7e3 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.module.ts
+++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.module.ts
@@ -6,6 +6,7 @@ import {MarkMissingDialogComponent} from './mark-missing-dialog.component';
 import {CopyAlertsDialogComponent} from './copy-alerts-dialog.component';
 import {ReplaceBarcodeDialogComponent} from './replace-barcode-dialog.component';
 import {DeleteVolcopyDialogComponent} from './delete-volcopy-dialog.component';
+import {ConjoinedItemsDialogComponent} from './conjoined-items-dialog.component';
 
 @NgModule({
     declarations: [
@@ -13,7 +14,8 @@ import {DeleteVolcopyDialogComponent} from './delete-volcopy-dialog.component';
       MarkMissingDialogComponent,
       CopyAlertsDialogComponent,
       ReplaceBarcodeDialogComponent,
-      DeleteVolcopyDialogComponent
+      DeleteVolcopyDialogComponent,
+      ConjoinedItemsDialogComponent
     ],
     imports: [
         StaffCommonModule
@@ -23,7 +25,8 @@ import {DeleteVolcopyDialogComponent} from './delete-volcopy-dialog.component';
       MarkMissingDialogComponent,
       CopyAlertsDialogComponent,
       ReplaceBarcodeDialogComponent,
-      DeleteVolcopyDialogComponent
+      DeleteVolcopyDialogComponent,
+      ConjoinedItemsDialogComponent
     ],
     providers: [
         HoldingsService

commit 65702937fe98b5fdba102636e7f264d028385a7a
Author: Bill Erickson <berickxx at gmail.com>
Date:   Tue Mar 26 11:23:22 2019 -0400

    LP1821382 Grid scroll menu repairs
    
    Make scrollable menus require addition of a class so the CSS does not
    apply to all drop-down menus.  Specifically, it makes less sense to use
    scrollable menus for the main menu bar.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.html b/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.html
index 68f7fade5f..0de7ede362 100644
--- a/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.html
+++ b/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.html
@@ -38,7 +38,7 @@
       <span title="Actions For Selected Rows" i18n-title
         class="material-icons mat-icon-in-button">playlist_add_check</span>
     </button>
-    <div class="dropdown-menu" ngbDropdownMenu>
+    <div class="dropdown-menu scrollable-menu" ngbDropdownMenu>
       <button class="dropdown-item" (click)="performAction(action)"
         *ngFor="let action of gridContext.toolbarActions"
         [disabled]="shouldDisableAction(action)">
@@ -109,7 +109,7 @@
       <span title="Show Grid Options" i18n-title
         class="material-icons mat-icon-in-button">settings</span>
     </button>
-    <div class="dropdown-menu" ngbDropdownMenu>
+    <div class="dropdown-menu scrollable-menu" ngbDropdownMenu>
       <a class="dropdown-item label-with-material-icon"
         (click)="columnConfDialog.open({size:'lg'})">
         <span class="material-icons">build</span>
diff --git a/Open-ILS/src/eg2/src/styles.css b/Open-ILS/src/eg2/src/styles.css
index a959e8cf69..10424f2722 100644
--- a/Open-ILS/src/eg2/src/styles.css
+++ b/Open-ILS/src/eg2/src/styles.css
@@ -125,11 +125,11 @@ h5 {font-size: .95rem}
 }
 
 /* Limit size of dropdown menus and allow for scrolling */
-.dropdown-menu {
+.scrollable-menu {
   height: auto;
   max-height: 300px;
   overflow-y: auto;
-  font-size: 98%;
+  font-size: 99%;
 }
 
 /* --------------------------------------------------------------------------

commit 5e8476c7f5cac8f89a9c80e4433dadd0023d22c7
Author: Bill Erickson <berickxx at gmail.com>
Date:   Tue Mar 26 11:17:35 2019 -0400

    LP1821382 Add Items to Bucket menu action
    
    Includes changes to the existing record bucket dialog to support all
    bucket types.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/staff/cat/vandelay/queue.component.html b/Open-ILS/src/eg2/src/app/staff/cat/vandelay/queue.component.html
index e4ef84565b..2c1b3c3215 100644
--- a/Open-ILS/src/eg2/src/app/staff/cat/vandelay/queue.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/cat/vandelay/queue.component.html
@@ -75,8 +75,8 @@
           </li>
           <li class="list-group-item">
             <div class="d-flex">
-              <eg-record-bucket-dialog #bucketDialog [queueId]="queueId">
-              </eg-record-bucket-dialog>
+              <eg-bucket-dialog #bucketDialog bucketClass="biblio" [fromBibQueue]="queueId">
+              </eg-bucket-dialog>
               <div class="flex-1">
                 <a [routerLink]="" (click)="bucketDialog.open({size:'lg'})" i18n>
                   Copy Queue To Bucket
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.html
index 5837acee84..4857db8cc1 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.html
@@ -1,5 +1,5 @@
-<eg-record-bucket-dialog #addBasketToBucketDialog>
-</eg-record-bucket-dialog>
+<eg-bucket-dialog #addBasketToBucketDialog>
+</eg-bucket-dialog>
 
 <div class="row">
   <div class="col-lg-4 pr-1">
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.ts
index e00a396d17..f96df6fd75 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.ts
@@ -5,8 +5,8 @@ import {Router} from '@angular/router';
 import {NetService} from '@eg/core/net.service';
 import {AuthService} from '@eg/core/auth.service';
 import {PrintService} from '@eg/share/print/print.service';
-import {RecordBucketDialogComponent
-    } from '@eg/staff/share/buckets/record-bucket-dialog.component';
+import {BucketDialogComponent
+    } from '@eg/staff/share/buckets/bucket-dialog.component';
 
 @Component({
   selector: 'eg-catalog-basket-actions',
@@ -17,7 +17,7 @@ export class BasketActionsComponent implements OnInit {
     basketAction: string;
 
     @ViewChild('addBasketToBucketDialog')
-        addToBucketDialog: RecordBucketDialogComponent;
+        addToBucketDialog: BucketDialogComponent;
 
     constructor(
         private router: Router,
@@ -89,8 +89,12 @@ export class BasketActionsComponent implements OnInit {
 
             case 'bucket':
                 this.basket.getRecordIds().then(ids => {
-                    this.addToBucketDialog.recordId = ids;
-                    this.addToBucketDialog.open({size: 'lg'});
+                    this.addToBucketDialog.bucketClass = 'biblio';
+                    this.addToBucketDialog.itemIds = ids;
+                    this.addToBucketDialog.open({size: 'lg'}).then(
+                        ok => {},
+                        dismissed => {}
+                    );
                 });
                 break;
 
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/actions.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/record/actions.component.html
index c52609925e..7634103fd7 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/actions.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/actions.component.html
@@ -10,8 +10,8 @@
 <eg-string key="catalog.record.toast.cleared" 
   text="Record Marks Cleared"></eg-string>
 
-<eg-record-bucket-dialog #recordBucketDialog [recordId]="recId">
-</eg-record-bucket-dialog>
+<eg-bucket-dialog #recordBucketDialog bucketClass="biblio" [itemIds]="[recId]">
+</eg-bucket-dialog>
 
 <div class="row ml-0 mr-0">
 
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
index 42366eded1..dddba6ede2 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
@@ -49,6 +49,7 @@
 <eg-copy-alerts-dialog #copyAlertsDialog></eg-copy-alerts-dialog>
 <eg-replace-barcode-dialog #replaceBarcode></eg-replace-barcode-dialog>
 <eg-delete-volcopy-dialog #deleteVolcopy></eg-delete-volcopy-dialog>
+<eg-bucket-dialog #bucketDialog></eg-bucket-dialog>
 
 <!-- holdings grid -->
 <div class='eg-copies w-100 mt-3'>
@@ -106,6 +107,11 @@
       (onClick)="openItemNotes($event, 'create')">
     </eg-grid-toolbar-action>
 
+    <eg-grid-toolbar-action
+      i18n-group group="Add" i18n-label label="Add Items To Bucket"
+      (onClick)="openBucketDialog($event)">
+    Z</eg-grid-toolbar-action>
+
     <!-- row actions: Edit -->
 
     <eg-grid-toolbar-action
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
index fea6385403..75efc7a461 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
@@ -26,6 +26,8 @@ import {ReplaceBarcodeDialogComponent
     } from '@eg/staff/share/holdings/replace-barcode-dialog.component';
 import {DeleteVolcopyDialogComponent
     } from '@eg/staff/share/holdings/delete-volcopy-dialog.component';
+import {BucketDialogComponent
+    } from '@eg/staff/share/buckets/bucket-dialog.component';
 
 // The holdings grid models a single HoldingsTree, composed of HoldingsTreeNodes
 // flattened on-demand into a list of HoldingEntry objects.
@@ -95,6 +97,8 @@ export class HoldingsMaintenanceComponent implements OnInit {
         private replaceBarcode: ReplaceBarcodeDialogComponent;
     @ViewChild('deleteVolcopy')
         private deleteVolcopy: DeleteVolcopyDialogComponent;
+    @ViewChild('bucketDialog')
+        private bucketDialog: BucketDialogComponent;
 
     holdingsTree: HoldingsTree;
 
@@ -839,4 +843,17 @@ export class HoldingsMaintenanceComponent implements OnInit {
         const params = {target: copyIds, holdFor: 'staff'};
         this.router.navigate(['/staff/catalog/hold/C'], {queryParams: params});
     }
+
+    openBucketDialog(rows: HoldingsEntry[]) {
+        const copyIds = this.selectedCopyIds(rows);
+        if (copyIds.length > 0) {
+            this.bucketDialog.bucketClass = 'copy';
+            this.bucketDialog.itemIds = copyIds;
+            this.bucketDialog.open({size: 'lg'}).then(
+                // No need to reload the grid after adding items to buckets.
+                () => {},
+                dismissed => {}
+            );
+        }
+    }
 }
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.html
index 90f066b1e9..9a65dafc60 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.html
@@ -3,8 +3,6 @@
   routerLink's
   egDateFilter's
 -->
-<eg-record-bucket-dialog #addToListDialog>
-</eg-record-bucket-dialog>
 
 <div class="col-lg-12 card tight-card mb-2 bg-light">
   <div class="card-body">
@@ -127,16 +125,6 @@
                   <span i18n>Place Hold</span>
                 </button>
               </span>
-              <!--
-              <span class="pl-1">
-                <button 
-                  (click)="addToListDialog.recordId=summary.record.id(); addToListDialog.open({size: 'lg'})"
-                  class="btn btn-sm btn-info label-with-material-icon small-text-1">
-                  <span class="material-icons">playlist_add_check</span>
-                  <span i18n>Add to List</span>
-                </button>
-              </span>
-              -->
             </div>
           </div>
         </div>
diff --git a/Open-ILS/src/eg2/src/app/staff/common.module.ts b/Open-ILS/src/eg2/src/app/staff/common.module.ts
index 5a83f8ac29..9c822a2c6c 100644
--- a/Open-ILS/src/eg2/src/app/staff/common.module.ts
+++ b/Open-ILS/src/eg2/src/app/staff/common.module.ts
@@ -17,7 +17,7 @@ import {StringService} from '@eg/share/string/string.service';
 import {TitleComponent} from '@eg/share/title/title.component';
 import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
 import {DateSelectComponent} from '@eg/share/date-select/date-select.component';
-import {RecordBucketDialogComponent} from '@eg/staff/share/buckets/record-bucket-dialog.component';
+import {BucketDialogComponent} from '@eg/staff/share/buckets/bucket-dialog.component';
 import {BibSummaryComponent} from '@eg/staff/share/bib-summary/bib-summary.component';
 import {TranslateComponent} from '@eg/staff/share/translate/translate.component';
 import {AdminPageComponent} from '@eg/staff/share/admin-page/admin-page.component';
@@ -40,7 +40,7 @@ import {AdminPageComponent} from '@eg/staff/share/admin-page/admin-page.componen
     OpChangeComponent,
     FmRecordEditorComponent,
     DateSelectComponent,
-    RecordBucketDialogComponent,
+    BucketDialogComponent,
     BibSummaryComponent,
     TranslateComponent,
     AdminPageComponent
@@ -64,7 +64,7 @@ import {AdminPageComponent} from '@eg/staff/share/admin-page/admin-page.componen
     OpChangeComponent,
     FmRecordEditorComponent,
     DateSelectComponent,
-    RecordBucketDialogComponent,
+    BucketDialogComponent,
     BibSummaryComponent,
     TranslateComponent,
     AdminPageComponent
diff --git a/Open-ILS/src/eg2/src/app/staff/share/buckets/record-bucket-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/share/buckets/bucket-dialog.component.html
similarity index 72%
rename from Open-ILS/src/eg2/src/app/staff/share/buckets/record-bucket-dialog.component.html
rename to Open-ILS/src/eg2/src/app/staff/share/buckets/bucket-dialog.component.html
index a2c88b8e34..32b6e2ec74 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/buckets/record-bucket-dialog.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/share/buckets/bucket-dialog.component.html
@@ -1,13 +1,13 @@
 <ng-template #dialogContent>
   <div class="modal-header bg-info">
     <h4 class="modal-title">
-      <ng-container *ngIf="recIds.length > 0">
-        <span *ngIf="recIds.length == 1" i18n>
-          Add Record #{{recIds[0]}} to Bucket</span>
-        <span *ngIf="recIds.length > 1" i18n>
-          Add {{recIds.length}} Record(s) to Bucket</span>
+      <ng-container *ngIf="itemIds.length > 0">
+        <span *ngIf="itemIds.length == 1" i18n>
+          Add Item #{{itemIds[0]}} to Bucket</span>
+        <span *ngIf="itemIds.length > 1" i18n>
+          Add {{itemIds.length}} Items to Bucket</span>
       </ng-container>
-      <span *ngIf="qId" i18n>Add Records from queue #{{qId}} to Bucket</span>
+      <span *ngIf="fromBibQueue" i18n>Add Records from queue #{{fromBibQueue}} to Bucket</span>
     </h4>
     <button type="button" class="close" 
       i18n-aria-label aria-label="Close" 
@@ -19,14 +19,10 @@
     <div class="row">
       <div class="col-lg-3 font-weight-bold" i18n>Name of existing bucket</div>
       <div class="col-lg-5">
-         <select 
-          class="form-control"
-          placeholder="Existing Bucket..."
-          i18n-placeholder
-          [(ngModel)]="selectedBucket">
-          <option *ngFor="let bkt of buckets" 
-            value="{{bkt.id()}}">{{bkt.name()}}</option>
-        </select>
+        <eg-combobox [entries]="formatBucketEntries()" 
+          (onChange)="bucketChanged($event)"
+          placeholder="Existing Bucket..." i18n-placeholder>
+        </eg-combobox>
       </div>
       <div class="col-lg-4">
         <button class="btn btn-info" (click)="addToSelected()" i18n 
diff --git a/Open-ILS/src/eg2/src/app/staff/share/buckets/record-bucket-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/share/buckets/bucket-dialog.component.ts
similarity index 56%
rename from Open-ILS/src/eg2/src/app/staff/share/buckets/record-bucket-dialog.component.ts
rename to Open-ILS/src/eg2/src/app/staff/share/buckets/bucket-dialog.component.ts
index f1f6f19cfa..1300f0f3e3 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/buckets/record-bucket-dialog.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/share/buckets/bucket-dialog.component.ts
@@ -6,37 +6,36 @@ import {ToastService} from '@eg/share/toast/toast.service';
 import {AuthService} from '@eg/core/auth.service';
 import {DialogComponent} from '@eg/share/dialog/dialog.component';
 import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
+import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
 
 /**
  * Dialog for adding bib records to new and existing record buckets.
  */
 
 @Component({
-  selector: 'eg-record-bucket-dialog',
-  templateUrl: 'record-bucket-dialog.component.html'
+  selector: 'eg-bucket-dialog',
+  templateUrl: 'bucket-dialog.component.html'
 })
 
-export class RecordBucketDialogComponent
-    extends DialogComponent implements OnInit {
+export class BucketDialogComponent extends DialogComponent implements OnInit {
 
     selectedBucket: number;
     newBucketName: string;
     newBucketDesc: string;
     buckets: any[];
 
-    @Input() bucketType: string;
+    @Input() bucketClass: 'biblio' | 'user' | 'callnumber' | 'copy';
+    @Input() bucketType: string; // e.g. staff_client
 
-    // Add one or more bib records to bucket by ID.
-    recIds: number[];
-    @Input() set recordId(id: number | number[]) {
-        this.recIds = [].concat(id);
-    }
+    // ID's of items to add to the bucket
+    @Input() itemIds: number[];
 
-    // Add items from a (vandelay) bib queue to a bucket
-    qId: number;
-    @Input() set queueId(id: number) {
-        this.qId = id;
-    }
+    // If set, itemIds will be derived from the records in a bib queue
+    @Input() fromBibQueue: number;
+
+    // bucket item classes are these plus a following 'i'.
+    bucketFmClass: 'ccb' | 'ccnb' | 'cbreb' | 'cub';
+    targetField: string;
 
     constructor(
         private modal: NgbModal, // required for passing to parent
@@ -47,40 +46,77 @@ export class RecordBucketDialogComponent
         private evt: EventService,
         private auth: AuthService) {
         super(modal); // required for subclassing
-        this.recIds = [];
+        this.buckets = [];
+        this.itemIds = [];
+        this.fromBibQueue = null;
     }
 
     ngOnInit() {
-
-        if (this.qId) {
-            this.bucketType = 'vandelay_queue';
-        } else {
-            this.bucketType = 'staff_client';
-        }
-
         this.onOpen$.subscribe(ok => {
-            // Reset data on dialog open
-
-            this.selectedBucket = null;
-            this.newBucketName = '';
-            this.newBucketDesc = '';
-
+            this.reset(); // Reset data on dialog open
             this.net.request(
                 'open-ils.actor',
                 'open-ils.actor.container.retrieve_by_class.authoritative',
                 this.auth.token(), this.auth.user().id(),
-                'biblio', this.bucketType
+                this.bucketClass, this.bucketType
             ).subscribe(buckets => this.buckets = buckets);
         });
     }
 
+    reset() {
+        this.selectedBucket = null;
+        this.newBucketName = '';
+        this.newBucketDesc = '';
+
+        if (!this.bucketClass) {
+            this.bucketClass = 'biblio';
+        }
+
+        switch (this.bucketClass) {
+            case 'biblio':
+                if (this.fromBibQueue) {
+                    this.bucketType = 'vandelay_queue';
+                }
+                this.bucketFmClass = 'cbreb';
+                this.targetField = 'target_biblio_record_entry';
+                break;
+            case 'copy':
+                this.bucketFmClass = 'ccb';
+                this.targetField = 'target_copy';
+                break;
+            case 'callnumber':
+                this.bucketFmClass = 'ccnb';
+                this.targetField = 'target_call_number';
+                break;
+            case 'user':
+                this.bucketFmClass = 'cub';
+                this.targetField = 'target_user';
+        }
+
+        if (!this.bucketType) {
+            this.bucketType = 'staff_client';
+        }
+    }
+
     addToSelected() {
         this.addToBucket(this.selectedBucket);
     }
 
+    bucketChanged(entry: ComboboxEntry) {
+        if (entry) {
+            this.selectedBucket = entry.id;
+        } else {
+            this.selectedBucket = null;
+        }
+    }
+
+    formatBucketEntries(): ComboboxEntry[] {
+        return this.buckets.map(b => ({id: b.id(), label: b.name()}));
+    }
+
     // Create a new bucket then add the record
     addToNew() {
-        const bucket = this.idl.create('cbreb');
+        const bucket = this.idl.create(this.bucketFmClass);
 
         bucket.owner(this.auth.user().id());
         bucket.name(this.newBucketName);
@@ -90,7 +126,7 @@ export class RecordBucketDialogComponent
         this.net.request(
             'open-ils.actor',
             'open-ils.actor.container.create',
-            this.auth.token(), 'biblio', bucket
+            this.auth.token(), this.bucketClass, bucket
         ).subscribe(bktId => {
             const evt = this.evt.parse(bktId);
             if (evt) {
@@ -106,27 +142,27 @@ export class RecordBucketDialogComponent
     }
 
     addToBucket(id: number) {
-        if (this.recIds.length > 0) {
+        if (this.itemIds.length > 0) {
             this.addRecordToBucket(id);
-        } else if (this.qId) {
-            this.addQueueToBucket(id);
+        } else if (this.fromBibQueue) {
+            this.addBibQueueToBucket(id);
         }
     }
 
     // Add the record(s) to the bucket with provided ID.
     addRecordToBucket(bucketId: number) {
         const items = [];
-        this.recIds.forEach(recId => {
-            const item = this.idl.create('cbrebi');
+        this.itemIds.forEach(itemId => {
+            const item = this.idl.create(this.bucketFmClass + 'i');
             item.bucket(bucketId);
-            item.target_biblio_record_entry(recId);
+            item[this.targetField](itemId);
             items.push(item);
         });
 
         this.net.request(
             'open-ils.actor',
             'open-ils.actor.container.item.create',
-            this.auth.token(), 'biblio', items
+            this.auth.token(), this.bucketClass, items
         ).subscribe(resp => {
             const evt = this.evt.parse(resp);
             if (evt) {
@@ -137,14 +173,14 @@ export class RecordBucketDialogComponent
         });
     }
 
-    addQueueToBucket(bucketId: number) {
+    addBibQueueToBucket(bucketId: number) {
         const bucket = this.buckets.filter(b => b.id() === bucketId)[0];
         if (!bucket) { return; }
 
         this.net.request(
             'open-ils.vandelay',
             'open-ils.vandelay.bib_queue.to_bucket',
-            this.auth.token(), this.qId, bucket.name()
+            this.auth.token(), this.fromBibQueue, bucket.name()
         ).toPromise().then(resp => {
             const evt = this.evt.parse(resp);
             if (evt) {

commit ae30dbc241436121f1056bdfce85cc8bbc24ee68
Author: Bill Erickson <berickxx at gmail.com>
Date:   Mon Mar 25 20:57:57 2019 +0000

    LP1821382 Angular lint repairs
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/share/util/bool.component.ts b/Open-ILS/src/eg2/src/app/share/util/bool.component.ts
index 37d2e8d7d7..2c7ec971f0 100644
--- a/Open-ILS/src/eg2/src/app/share/util/bool.component.ts
+++ b/Open-ILS/src/eg2/src/app/share/util/bool.component.ts
@@ -36,4 +36,5 @@ export class BoolDisplayComponent {
     constructor() {
         this.value = null;
     }
-}
\ No newline at end of file
+}
+
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
index 623b547542..fea6385403 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
@@ -166,7 +166,7 @@ export class HoldingsMaintenanceComponent implements OnInit {
                 return 'holdings-org-row holdings-org-row-' +
                     row.treeNode.target.ou_type().depth();
             }
-        }
+        };
 
         this.gridTemplateContext = {
             toggleExpandRow: (row: HoldingsEntry) => {
@@ -178,7 +178,7 @@ export class HoldingsMaintenanceComponent implements OnInit {
                     const traverse = (node: HoldingsTreeNode) => {
                         node.expanded = false;
                         node.children.forEach(traverse);
-                    }
+                    };
                     traverse(row.treeNode);
                 }
 
@@ -190,7 +190,7 @@ export class HoldingsMaintenanceComponent implements OnInit {
                     && copy.location().holdable() === 't'
                     && copy.status().holdable() === 't';
             }
-        }
+        };
     }
 
     ngOnInit() {
@@ -282,7 +282,7 @@ export class HoldingsMaintenanceComponent implements OnInit {
         // The initial tree simply matches the org unit tree
         const traverseOrg = (node: HoldingsTreeNode) => {
             node.target.children().forEach((org: IdlObject) => {
-                if (visibleOrgs.indexOf(org.id()) == -1) {
+                if (visibleOrgs.indexOf(org.id()) === -1) {
                     return; // Org is outside of scope
                 }
                 const nodeChild = new HoldingsTreeNode();
@@ -293,7 +293,7 @@ export class HoldingsMaintenanceComponent implements OnInit {
                 this.treeNodeCache.org[org.id()] = nodeChild;
                 traverseOrg(nodeChild);
             });
-        }
+        };
 
         this.treeNodeCache = {
             org: {},
@@ -336,7 +336,7 @@ export class HoldingsMaintenanceComponent implements OnInit {
         if (node.nodeType === 'org') {
             node.copyCount = 0;
             node.volumeCount = 0;
-        } else if(node.nodeType === 'volume') {
+        } else if (node.nodeType === 'volume') {
             node.copyCount = 0;
         }
 
@@ -383,7 +383,7 @@ export class HoldingsMaintenanceComponent implements OnInit {
         entry.treeNode = node;
         entry.index = this.gridIndex++;
 
-        switch(node.nodeType) {
+        switch (node.nodeType) {
             case 'org':
                 if (node.volumeCount === 0
                     && !this.emptyLibsCheckbox.checked()) {
@@ -416,7 +416,7 @@ export class HoldingsMaintenanceComponent implements OnInit {
             case 'copy':
                 entry.locationLabel = node.target.barcode();
                 entry.locationDepth = node.parentNode.parentNode.target.ou_type().depth() + 2;
-                entry.callNumberLabel = node.parentNode.target.label() // TODO
+                entry.callNumberLabel = node.parentNode.target.label(); // TODO
                 entry.volume = node.parentNode.target;
                 entry.copy = node.target;
                 entry.circ = node.target._circ;
@@ -514,7 +514,7 @@ export class HoldingsMaintenanceComponent implements OnInit {
         this.setVolumeLabel(volume);
 
         if (volNode) {
-            const pNode = this.treeNodeCache.org[volume.owning_lib()]
+            const pNode = this.treeNodeCache.org[volume.owning_lib()];
             if (volNode.parentNode.target.id() !== pNode.target.id()) {
                 // Volume owning library changed.  Un-link it from the previous
                 // org unit collection before adding to the new one.
@@ -525,7 +525,7 @@ export class HoldingsMaintenanceComponent implements OnInit {
         } else {
             volNode = new HoldingsTreeNode();
             volNode.nodeType = 'volume';
-            volNode.parentNode = this.treeNodeCache.org[volume.owning_lib()]
+            volNode.parentNode = this.treeNodeCache.org[volume.owning_lib()];
             volNode.parentNode.children.push(volNode);
             this.treeNodeCache.volume[volume.id()] = volNode;
         }
@@ -657,7 +657,7 @@ export class HoldingsMaintenanceComponent implements OnInit {
 
             // Add volume target when performed on a volume row.
             this.localStore.setLocalItem(
-                'eg.cat.transfer_target_vol', node.target.id())
+                'eg.cat.transfer_target_vol', node.target.id());
         }
 
         this.localStore.setLocalItem('eg.cat.transfer_target_record', this.recordId);
@@ -736,7 +736,7 @@ export class HoldingsMaintenanceComponent implements OnInit {
                 } else {
 
                 // Otherwise create new volumes from scratch.
-                entries.push({owner: this.auth.user().ws_ou()})
+                entries.push({owner: this.auth.user().ws_ou()});
             }
 
             this.holdings.spawnAddHoldingsUi(
@@ -757,7 +757,7 @@ export class HoldingsMaintenanceComponent implements OnInit {
                 }
             },
             dismissed => {}
-        )
+        );
     }
 
     openReplaceBarcodeDialog(rows: HoldingsEntry[]) {
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/copy-alerts-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/share/holdings/copy-alerts-dialog.component.ts
index e0ce7637b0..630126b238 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/holdings/copy-alerts-dialog.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/copy-alerts-dialog.component.ts
@@ -168,18 +168,18 @@ export class CopyAlertsDialogComponent
                 }
             },
             err => {
-                this.errorMsg.current().then(msg => this.toast.danger(msg))
+                this.errorMsg.current().then(msg => this.toast.danger(msg));
             }
         );
     }
 
     applyChanges() {
         const alerts = this.copy.copy_alerts().filter(a => a.ischanged());
-        if (alerts.length === 0) { return ;}
+        if (alerts.length === 0) { return; }
         this.pcrud.update(alerts).toPromise().then(
             ok => this.successMsg.current().then(msg => this.toast.success(msg)),
             err => this.errorMsg.current().then(msg => this.toast.danger(msg))
-        )
+        );
     }
 }
 
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/delete-volcopy-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/share/holdings/delete-volcopy-dialog.component.ts
index 82bb82791e..57c613db06 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/holdings/delete-volcopy-dialog.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/delete-volcopy-dialog.component.ts
@@ -73,10 +73,10 @@ export class DeleteVolcopyDialogComponent
                         // Marking copies deleted in forceDeleteCopies mode
                         // is not required, but we do it here so we can
                         // report the number of copies to be deleted.
-                        c.isdeleted(true)
+                        c.isdeleted(true);
                         this.numCopies++;
                     }
-                })
+                });
             }
         });
 
@@ -103,7 +103,7 @@ export class DeleteVolcopyDialogComponent
                 const evt = this.evt.parse(result);
                 if (evt) {
                     console.warn(evt);
-                    this.errorMsg.current().then(msg =>this.toast.warning(msg));
+                    this.errorMsg.current().then(msg => this.toast.warning(msg));
                     this.numFailed++;
                 } else {
                     this.numSucceeded++;
@@ -112,7 +112,7 @@ export class DeleteVolcopyDialogComponent
             },
             err => {
                 console.warn(err);
-                this.errorMsg.current().then(msg =>this.toast.warning(msg));
+                this.errorMsg.current().then(msg => this.toast.warning(msg));
                 this.numFailed++;
             }
         );
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/replace-barcode-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/share/holdings/replace-barcode-dialog.component.ts
index 552922e9a0..2cd67de8d1 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/holdings/replace-barcode-dialog.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/replace-barcode-dialog.component.ts
@@ -102,8 +102,8 @@ export class ReplaceBarcodeDialogComponent
                     console.error('Replace barcode failed: ', err);
                     this.toast.warning(await this.errorMsg.current());
                 }
-            )
-        })
+            );
+        });
     }
 }
 

commit 3785ed053676b4e0c873b77c26b6f6e54eb67f76
Author: Bill Erickson <berickxx at gmail.com>
Date:   Mon Mar 25 20:18:55 2019 +0000

    LP1821382 Request items menu action
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/hold/hold.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/hold/hold.component.ts
index 8322b7a145..539c434e4a 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/hold/hold.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/hold/hold.component.ts
@@ -1,7 +1,5 @@
 import {Component, OnInit, Input, ViewChild, Renderer2} from '@angular/core';
 import {Router, ActivatedRoute, ParamMap} from '@angular/router';
-import {Observable} from 'rxjs';
-import {tap} from 'rxjs/operators';
 import {EventService} from '@eg/core/event.service';
 import {NetService} from '@eg/core/net.service';
 import {AuthService} from '@eg/core/auth.service';
@@ -10,7 +8,6 @@ import {PermService} from '@eg/core/perm.service';
 import {IdlObject} from '@eg/core/idl.service';
 import {OrgService} from '@eg/core/org.service';
 import {BibRecordService, BibRecordSummary} from '@eg/share/catalog/bib-record.service';
-import {CatalogSearchContext, CatalogSearchState} from '@eg/share/catalog/search-context';
 import {CatalogService} from '@eg/share/catalog/catalog.service';
 import {StaffCatalogService} from '../catalog.service';
 import {HoldsService, HoldRequest,
@@ -89,13 +86,14 @@ export class HoldComponent implements OnInit {
 
         this.holdType = this.route.snapshot.params['type'];
         this.holdTargets = this.route.snapshot.queryParams['target'];
+        this.holdFor = this.route.snapshot.queryParams['holdFor'] || 'patron';
 
         if (!Array.isArray(this.holdTargets)) {
             this.holdTargets = [this.holdTargets];
         }
 
         this.holdTargets = this.holdTargets.map(t => Number(t));
-        this.holdFor = 'patron';
+
         this.requestor = this.auth.user();
         this.pickupLib = this.auth.user().ws_ou();
 
@@ -104,6 +102,10 @@ export class HoldComponent implements OnInit {
             return ctx;
         });
 
+        if (this.holdFor === 'staff') {
+            this.holdForChanged();
+        }
+
         this.getTargetMeta();
 
         this.org.settings('sms.enable').then(sets => {
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
index 31b6e0c58e..42366eded1 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
@@ -80,6 +80,10 @@
       i18n-label label="Print Labels" (onClick)="openItemPrintLabels($event)">
     </eg-grid-toolbar-action>
 
+    <eg-grid-toolbar-action
+      i18n-label label="Request Items" (onClick)="requestItems($event)">
+    </eg-grid-toolbar-action>
+
     <!-- row actions : Add -->
 
     <eg-grid-toolbar-action
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
index bf375bc4c8..623b547542 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
@@ -1,4 +1,5 @@
 import {Component, OnInit, Input, ViewChild} from '@angular/core';
+import {Router} from '@angular/router';
 import {Observable, Observer, of} from 'rxjs';
 import {map} from 'rxjs/operators';
 import {Pager} from '@eg/share/util/pager';
@@ -133,6 +134,7 @@ export class HoldingsMaintenanceComponent implements OnInit {
     contextOrg: IdlObject;
 
     constructor(
+        private router: Router,
         private org: OrgService,
         private idl: IdlService,
         private pcrud: PcrudService,
@@ -830,4 +832,11 @@ export class HoldingsMaintenanceComponent implements OnInit {
             dismissed => {}
         );
     }
+
+    requestItems(rows: HoldingsEntry[]) {
+        const copyIds = this.selectedCopyIds(rows);
+        if (copyIds.length === 0) { return; }
+        const params = {target: copyIds, holdFor: 'staff'};
+        this.router.navigate(['/staff/catalog/hold/C'], {queryParams: params});
+    }
 }

commit aab9e8d66f5e46a0eaf99ee78b0b46f6099071c5
Author: Bill Erickson <berickxx at gmail.com>
Date:   Mon Mar 25 16:51:54 2019 +0000

    LP1821382 Delete volcopy menu actions
    
    Holdings grid menu actions for delete copies and call numbers.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/package-lock.json b/Open-ILS/src/eg2/package-lock.json
index fe0147bdc3..31f4512320 100644
--- a/Open-ILS/src/eg2/package-lock.json
+++ b/Open-ILS/src/eg2/package-lock.json
@@ -20,7 +20,7 @@
           "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
           "dev": true,
           "requires": {
-            "tslib": "^1.9.0"
+            "tslib": "1.9.3"
           }
         }
       }
@@ -65,7 +65,7 @@
         "semver": "5.5.1",
         "source-map-loader": "0.2.4",
         "source-map-support": "0.5.9",
-        "speed-measure-webpack-plugin": "^1.2.3",
+        "speed-measure-webpack-plugin": "1.3.0",
         "stats-webpack-plugin": "0.7.0",
         "style-loader": "0.23.0",
         "stylus": "0.54.5",
@@ -86,7 +86,7 @@
           "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
           "dev": true,
           "requires": {
-            "tslib": "^1.9.0"
+            "tslib": "1.9.3"
           }
         }
       }
@@ -128,7 +128,7 @@
           "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
           "dev": true,
           "requires": {
-            "tslib": "^1.9.0"
+            "tslib": "1.9.3"
           }
         }
       }
@@ -152,7 +152,7 @@
           "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
           "dev": true,
           "requires": {
-            "tslib": "^1.9.0"
+            "tslib": "1.9.3"
           }
         },
         "source-map": {
@@ -179,17 +179,17 @@
           "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
           "dev": true,
           "requires": {
-            "tslib": "^1.9.0"
+            "tslib": "1.9.3"
           }
         }
       }
     },
     "@angular/animations": {
-      "version": "7.2.2",
-      "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-7.2.2.tgz",
-      "integrity": "sha512-St03YR3N4Rv0vLr0+3V0kmf/QNi9q0tOenAlHP+jG/YySPkkv8P3xRcGVU38ID4JQzRiShUD+k2r+oZGCQMNjw==",
+      "version": "7.2.10",
+      "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-7.2.10.tgz",
+      "integrity": "sha512-WgklBWYKy8LVlugMJ8XgBB4whNiMng8vY6mpaYhza064nsrQXK+ua1anSOGMGlmCI/5i4EgaycTS05XD/C+Kkw==",
       "requires": {
-        "tslib": "^1.9.0"
+        "tslib": "1.9.3"
       }
     },
     "@angular/cli": {
@@ -216,25 +216,25 @@
           "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
           "dev": true,
           "requires": {
-            "tslib": "^1.9.0"
+            "tslib": "1.9.3"
           }
         }
       }
     },
     "@angular/common": {
-      "version": "7.2.2",
-      "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.2.2.tgz",
-      "integrity": "sha512-43EcR3mbM+dKH4VE1EYS1HxSuEToxxv5XPktKqdzY95g8PBOxe11ifcXoYHgImd7YOWzcKoy0k6yQbX3o0cZ8g==",
+      "version": "7.2.10",
+      "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.2.10.tgz",
+      "integrity": "sha512-FyiVUhV1MaRogXmVKsw6CMXqWZHsiYMdK651absXtGqWZn6and0jiUwEM9LbV4HRpLbB7F6gwqCvT5Ft8xpzeg==",
       "requires": {
-        "tslib": "^1.9.0"
+        "tslib": "1.9.3"
       }
     },
     "@angular/compiler": {
-      "version": "7.2.2",
-      "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.2.2.tgz",
-      "integrity": "sha512-vjPreOVPca6HSuDmj7N1w5u8hwXdm98gEPo2wqQMVuJd6qvGEyLYE9FsHc0XCchyQEKSybAYl1dwsjZq2nNSvQ==",
+      "version": "7.2.10",
+      "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.2.10.tgz",
+      "integrity": "sha512-sRuH+UDogzAHpagorHfj2rbF8HWGEJGsFUn5TGq1vyWA37ALuu/zkemufIcgET16lTiBEvVn7/Oz9Am+JTlGrg==",
       "requires": {
-        "tslib": "^1.9.0"
+        "tslib": "1.9.3"
       }
     },
     "@angular/compiler-cli": {
@@ -244,15 +244,15 @@
       "dev": true,
       "requires": {
         "canonical-path": "1.0.0",
-        "chokidar": "^1.4.2",
-        "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",
+        "chokidar": "1.7.0",
+        "convert-source-map": "1.6.0",
+        "dependency-graph": "0.7.2",
+        "magic-string": "0.25.1",
+        "minimist": "1.2.0",
+        "reflect-metadata": "0.1.13",
+        "shelljs": "0.8.3",
+        "source-map": "0.6.1",
+        "tslib": "1.9.3",
         "yargs": "9.0.1"
       },
       "dependencies": {
@@ -268,8 +268,8 @@
           "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==",
           "dev": true,
           "requires": {
-            "micromatch": "^2.1.5",
-            "normalize-path": "^2.0.0"
+            "micromatch": "2.3.11",
+            "normalize-path": "2.1.1"
           }
         },
         "arr-diff": {
@@ -278,7 +278,7 @@
           "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
           "dev": true,
           "requires": {
-            "arr-flatten": "^1.0.1"
+            "arr-flatten": "1.1.0"
           }
         },
         "array-unique": {
@@ -293,9 +293,9 @@
           "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
           "dev": true,
           "requires": {
-            "expand-range": "^1.8.1",
-            "preserve": "^0.2.0",
-            "repeat-element": "^1.1.2"
+            "expand-range": "1.8.2",
+            "preserve": "0.2.0",
+            "repeat-element": "1.1.3"
           }
         },
         "camelcase": {
@@ -310,15 +310,15 @@
           "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=",
           "dev": true,
           "requires": {
-            "anymatch": "^1.3.0",
-            "async-each": "^1.0.0",
-            "fsevents": "^1.0.0",
-            "glob-parent": "^2.0.0",
-            "inherits": "^2.0.1",
-            "is-binary-path": "^1.0.0",
-            "is-glob": "^2.0.0",
-            "path-is-absolute": "^1.0.0",
-            "readdirp": "^2.0.0"
+            "anymatch": "1.3.2",
+            "async-each": "1.0.1",
+            "fsevents": "1.2.4",
+            "glob-parent": "2.0.0",
+            "inherits": "2.0.3",
+            "is-binary-path": "1.0.1",
+            "is-glob": "2.0.1",
+            "path-is-absolute": "1.0.1",
+            "readdirp": "2.1.0"
           }
         },
         "cross-spawn": {
@@ -327,9 +327,9 @@
           "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
           "dev": true,
           "requires": {
-            "lru-cache": "^4.0.1",
-            "shebang-command": "^1.2.0",
-            "which": "^1.2.9"
+            "lru-cache": "4.1.3",
+            "shebang-command": "1.2.0",
+            "which": "1.3.1"
           }
         },
         "execa": {
@@ -338,13 +338,13 @@
           "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"
+            "cross-spawn": "5.1.0",
+            "get-stream": "3.0.0",
+            "is-stream": "1.1.0",
+            "npm-run-path": "2.0.2",
+            "p-finally": "1.0.0",
+            "signal-exit": "3.0.2",
+            "strip-eof": "1.0.0"
           }
         },
         "expand-brackets": {
@@ -353,7 +353,7 @@
           "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
           "dev": true,
           "requires": {
-            "is-posix-bracket": "^0.1.0"
+            "is-posix-bracket": "0.1.1"
           }
         },
         "extglob": {
@@ -362,7 +362,7 @@
           "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
           "dev": true,
           "requires": {
-            "is-extglob": "^1.0.0"
+            "is-extglob": "1.0.0"
           }
         },
         "glob-parent": {
@@ -371,7 +371,7 @@
           "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
           "dev": true,
           "requires": {
-            "is-glob": "^2.0.0"
+            "is-glob": "2.0.1"
           }
         },
         "is-extglob": {
@@ -392,7 +392,7 @@
           "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
           "dev": true,
           "requires": {
-            "is-extglob": "^1.0.0"
+            "is-extglob": "1.0.0"
           }
         },
         "kind-of": {
@@ -401,7 +401,7 @@
           "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
           "dev": true,
           "requires": {
-            "is-buffer": "^1.1.5"
+            "is-buffer": "1.1.6"
           }
         },
         "load-json-file": {
@@ -410,10 +410,10 @@
           "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"
+            "graceful-fs": "4.1.11",
+            "parse-json": "2.2.0",
+            "pify": "2.3.0",
+            "strip-bom": "3.0.0"
           }
         },
         "mem": {
@@ -422,7 +422,7 @@
           "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=",
           "dev": true,
           "requires": {
-            "mimic-fn": "^1.0.0"
+            "mimic-fn": "1.2.0"
           }
         },
         "micromatch": {
@@ -431,19 +431,19 @@
           "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
           "dev": true,
           "requires": {
-            "arr-diff": "^2.0.0",
-            "array-unique": "^0.2.1",
-            "braces": "^1.8.2",
-            "expand-brackets": "^0.1.4",
-            "extglob": "^0.3.1",
-            "filename-regex": "^2.0.0",
-            "is-extglob": "^1.0.0",
-            "is-glob": "^2.0.1",
-            "kind-of": "^3.0.2",
-            "normalize-path": "^2.0.1",
-            "object.omit": "^2.0.0",
-            "parse-glob": "^3.0.4",
-            "regex-cache": "^0.4.2"
+            "arr-diff": "2.0.0",
+            "array-unique": "0.2.1",
+            "braces": "1.8.5",
+            "expand-brackets": "0.1.5",
+            "extglob": "0.3.2",
+            "filename-regex": "2.0.1",
+            "is-extglob": "1.0.0",
+            "is-glob": "2.0.1",
+            "kind-of": "3.2.2",
+            "normalize-path": "2.1.1",
+            "object.omit": "2.0.1",
+            "parse-glob": "3.0.4",
+            "regex-cache": "0.4.4"
           }
         },
         "minimist": {
@@ -458,9 +458,9 @@
           "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==",
           "dev": true,
           "requires": {
-            "execa": "^0.7.0",
-            "lcid": "^1.0.0",
-            "mem": "^1.1.0"
+            "execa": "0.7.0",
+            "lcid": "1.0.0",
+            "mem": "1.1.0"
           }
         },
         "path-type": {
@@ -469,7 +469,7 @@
           "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
           "dev": true,
           "requires": {
-            "pify": "^2.0.0"
+            "pify": "2.3.0"
           }
         },
         "pify": {
@@ -484,9 +484,9 @@
           "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
           "dev": true,
           "requires": {
-            "load-json-file": "^2.0.0",
-            "normalize-package-data": "^2.3.2",
-            "path-type": "^2.0.0"
+            "load-json-file": "2.0.0",
+            "normalize-package-data": "2.4.1",
+            "path-type": "2.0.0"
           }
         },
         "read-pkg-up": {
@@ -495,8 +495,8 @@
           "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
           "dev": true,
           "requires": {
-            "find-up": "^2.0.0",
-            "read-pkg": "^2.0.0"
+            "find-up": "2.1.0",
+            "read-pkg": "2.0.0"
           }
         },
         "source-map": {
@@ -511,8 +511,8 @@
           "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
           "dev": true,
           "requires": {
-            "is-fullwidth-code-point": "^2.0.0",
-            "strip-ansi": "^4.0.0"
+            "is-fullwidth-code-point": "2.0.0",
+            "strip-ansi": "4.0.0"
           }
         },
         "strip-ansi": {
@@ -521,7 +521,7 @@
           "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
           "dev": true,
           "requires": {
-            "ansi-regex": "^3.0.0"
+            "ansi-regex": "3.0.0"
           }
         },
         "strip-bom": {
@@ -548,19 +548,19 @@
           "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"
+            "camelcase": "4.1.0",
+            "cliui": "3.2.0",
+            "decamelize": "1.2.0",
+            "get-caller-file": "1.0.3",
+            "os-locale": "2.1.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.1.1",
+            "which-module": "2.0.0",
+            "y18n": "3.2.1",
+            "yargs-parser": "7.0.0"
           }
         },
         "yargs-parser": {
@@ -569,33 +569,33 @@
           "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=",
           "dev": true,
           "requires": {
-            "camelcase": "^4.1.0"
+            "camelcase": "4.1.0"
           }
         }
       }
     },
     "@angular/core": {
-      "version": "7.2.2",
-      "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.2.2.tgz",
-      "integrity": "sha512-kQ0HxUYAPvly8b3aibTGbiodFnBBgo3asXAQuPgFjYYEqcKR1zZII7PQdaEF9kb9sfm/IKLKj4nd9fZ0gcgqZg==",
+      "version": "7.2.10",
+      "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.2.10.tgz",
+      "integrity": "sha512-u2IKaq4G1wpq5w1AI0Q7jnsKuSfR4WDsBLnwtjTIjde1YDqA0n0dYwqrvWzLK6SbzSWjlrAXx0hrz5SlDchmqA==",
       "requires": {
-        "tslib": "^1.9.0"
+        "tslib": "1.9.3"
       }
     },
     "@angular/forms": {
-      "version": "7.2.2",
-      "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.2.2.tgz",
-      "integrity": "sha512-IsvVuUnzIA2ryRmh7l42AANPZFSyNcwqZNtxbmRq2wm3Lfed64U0rsRWWNqipjz7QTxZ2SRdAlP+XDgzg8hvMQ==",
+      "version": "7.2.10",
+      "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.2.10.tgz",
+      "integrity": "sha512-fQccon0Yuni13QJt16npSRlkitPZBLXfWXDFwCEybo/QqtSar3BOJAQFW2yqokrfW5lbO5VDFJ7Pj2dDyBXEAA==",
       "requires": {
-        "tslib": "^1.9.0"
+        "tslib": "1.9.3"
       }
     },
     "@angular/http": {
-      "version": "7.2.2",
-      "resolved": "https://registry.npmjs.org/@angular/http/-/http-7.2.2.tgz",
-      "integrity": "sha512-+9dZgiVQStGUO3iJGxEWBkjDCARuVLIPk7QPl4Qntbz8Sd/kR7IBqXMM+74W7T5rlG6bunDxq6LuisvsjWCppw==",
+      "version": "7.2.10",
+      "resolved": "https://registry.npmjs.org/@angular/http/-/http-7.2.10.tgz",
+      "integrity": "sha512-Uaq9Ep21ZZIG+yzYHhyH+RA/AEyKGTtWT5y12UXXFsCdXSwJ2p+czw2EdP42Oj1g1QptkP6lAl2W/RUlxwXQcw==",
       "requires": {
-        "tslib": "^1.9.0"
+        "tslib": "1.9.3"
       }
     },
     "@angular/language-service": {
@@ -605,27 +605,27 @@
       "dev": true
     },
     "@angular/platform-browser": {
-      "version": "7.2.2",
-      "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.2.2.tgz",
-      "integrity": "sha512-eiaqHq26PVASx1kTngBDkFkXhaJzEjoGtc5I+wQUef8CUjq6ZViWz8tUgiiDPOWdqUKUacRZG4q6VR/6uwQj0A==",
+      "version": "7.2.10",
+      "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.2.10.tgz",
+      "integrity": "sha512-78r527d0Nw0svkDw2SBr58H3VEH5VAvn+r+q7NLqIqD8jsOMf8CMztUCDs+h02HhEHveCXWI5EtFXxWepB6wFg==",
       "requires": {
-        "tslib": "^1.9.0"
+        "tslib": "1.9.3"
       }
     },
     "@angular/platform-browser-dynamic": {
-      "version": "7.2.2",
-      "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.2.2.tgz",
-      "integrity": "sha512-bw5PuzMzjKMecB4slG/btmvxgn4qFWhNmJVpf2pbtZW7NtZz3HlrqipYzMk9XrCUDGjtwy7O2Z71C3ujI748iw==",
+      "version": "7.2.10",
+      "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.2.10.tgz",
+      "integrity": "sha512-ZTJL7iIEL3yDgJLx5/a2wfKsxe0ZHEUSibtbRQNpn35ZI0G9QN7ezbTOqj2+/QGvaY8Y1JeoYCeJrMzaUxDxGg==",
       "requires": {
-        "tslib": "^1.9.0"
+        "tslib": "1.9.3"
       }
     },
     "@angular/router": {
-      "version": "7.2.2",
-      "resolved": "https://registry.npmjs.org/@angular/router/-/router-7.2.2.tgz",
-      "integrity": "sha512-+cBC+JxbPdjk+Nyqq27PKkjfIdnc+H+xjMGrkO6dlAKhVMGxyNaYt5NUNugb8XJPsQ1XNXyzwTfZK6jcAGLw6w==",
+      "version": "7.2.10",
+      "resolved": "https://registry.npmjs.org/@angular/router/-/router-7.2.10.tgz",
+      "integrity": "sha512-7A0n6O5sW2xbQTG6pHfCwSkx6UvUQLg6Z/sDOghAZUakf4kd5BIWet1Q9eyiYndkpq6esQ+gMt9+CeJidf5l1A==",
       "requires": {
-        "tslib": "^1.9.0"
+        "tslib": "1.9.3"
       }
     },
     "@babel/code-frame": {
@@ -634,7 +634,7 @@
       "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==",
       "dev": true,
       "requires": {
-        "@babel/highlight": "^7.0.0"
+        "@babel/highlight": "7.0.0"
       }
     },
     "@babel/generator": {
@@ -643,11 +643,11 @@
       "integrity": "sha512-dZTwMvTgWfhmibq4V9X+LMf6Bgl7zAodRn9PvcPdhlzFMbvUutx74dbEv7Atz3ToeEpevYEJtAwfxq/bDCzHWg==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.3.0",
-        "jsesc": "^2.5.1",
-        "lodash": "^4.17.10",
-        "source-map": "^0.5.0",
-        "trim-right": "^1.0.1"
+        "@babel/types": "7.3.0",
+        "jsesc": "2.5.2",
+        "lodash": "4.17.10",
+        "source-map": "0.5.7",
+        "trim-right": "1.0.1"
       },
       "dependencies": {
         "jsesc": {
@@ -664,9 +664,9 @@
       "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": "7.0.0",
+        "@babel/template": "7.2.2",
+        "@babel/types": "7.3.0"
       }
     },
     "@babel/helper-get-function-arity": {
@@ -675,7 +675,7 @@
       "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.0.0"
+        "@babel/types": "7.3.0"
       }
     },
     "@babel/helper-split-export-declaration": {
@@ -684,7 +684,7 @@
       "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.0.0"
+        "@babel/types": "7.3.0"
       }
     },
     "@babel/highlight": {
@@ -693,9 +693,9 @@
       "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==",
       "dev": true,
       "requires": {
-        "chalk": "^2.0.0",
-        "esutils": "^2.0.2",
-        "js-tokens": "^4.0.0"
+        "chalk": "2.4.1",
+        "esutils": "2.0.2",
+        "js-tokens": "4.0.0"
       },
       "dependencies": {
         "js-tokens": {
@@ -718,9 +718,9 @@
       "integrity": "sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==",
       "dev": true,
       "requires": {
-        "@babel/code-frame": "^7.0.0",
-        "@babel/parser": "^7.2.2",
-        "@babel/types": "^7.2.2"
+        "@babel/code-frame": "7.0.0",
+        "@babel/parser": "7.3.1",
+        "@babel/types": "7.3.0"
       }
     },
     "@babel/traverse": {
@@ -729,15 +729,15 @@
       "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"
+        "@babel/code-frame": "7.0.0",
+        "@babel/generator": "7.3.0",
+        "@babel/helper-function-name": "7.1.0",
+        "@babel/helper-split-export-declaration": "7.0.0",
+        "@babel/parser": "7.3.1",
+        "@babel/types": "7.3.0",
+        "debug": "4.1.1",
+        "globals": "11.10.0",
+        "lodash": "4.17.10"
       },
       "dependencies": {
         "debug": {
@@ -746,7 +746,7 @@
           "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
           "dev": true,
           "requires": {
-            "ms": "^2.1.1"
+            "ms": "2.1.1"
           }
         },
         "globals": {
@@ -769,9 +769,9 @@
       "integrity": "sha512-QkFPw68QqWU1/RVPyBe8SO7lXbPfjtqAxRYQKpFpaB8yMq7X2qAqfwK5LKoQufEkSmO5NQ70O6Kc3Afk03RwXw==",
       "dev": true,
       "requires": {
-        "esutils": "^2.0.2",
-        "lodash": "^4.17.10",
-        "to-fast-properties": "^2.0.0"
+        "esutils": "2.0.2",
+        "lodash": "4.17.10",
+        "to-fast-properties": "2.0.0"
       },
       "dependencies": {
         "to-fast-properties": {
@@ -787,7 +787,7 @@
       "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-3.3.1.tgz",
       "integrity": "sha512-awty+5Kil0i/xIV7SSmKa5YozU83EdIx2EenF2AUDTczSKhHNhRByo82rjtwIhshN25/ZEss4aSDhgILLI88fw==",
       "requires": {
-        "tslib": "^1.9.0"
+        "tslib": "1.9.3"
       }
     },
     "@ngtools/webpack": {
@@ -809,15 +809,15 @@
           "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
           "dev": true,
           "requires": {
-            "tslib": "^1.9.0"
+            "tslib": "1.9.3"
           }
         }
       }
     },
     "@nguniversal/express-engine": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/@nguniversal/express-engine/-/express-engine-7.1.0.tgz",
-      "integrity": "sha512-otOA3WTjb+XnEDiyhwkvP0hE1gC7PiJrDoDFs5Q77SQ0ZAjuGzAIIgpQrPE8B+v0zmVj2oucDNCSZlmYWb1P/Q=="
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/@nguniversal/express-engine/-/express-engine-7.1.1.tgz",
+      "integrity": "sha512-RJ2VATA6s48bYNrAfjnkjUCohpR7ehiOySwGA2vuUIWCWXKDIIPxgmET5ffVHy1a2XdMsOgrQ9Whth7+CxnUgw=="
     },
     "@schematics/angular": {
       "version": "7.0.7",
@@ -850,7 +850,7 @@
           "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
           "dev": true,
           "requires": {
-            "tslib": "^1.9.0"
+            "tslib": "1.9.3"
           }
         }
       }
@@ -867,7 +867,7 @@
       "integrity": "sha512-2ZOKrxb8bKRmP/po5ObYnRDgFE4i+lQiEB27bAMmtMWLgJSqlIDqlLx6S0IRorpOmOPRQ6O80NujTmQAtBkeNw==",
       "dev": true,
       "requires": {
-        "@types/jasmine": "*"
+        "@types/jasmine": "2.8.16"
       }
     },
     "@types/node": {
@@ -903,7 +903,7 @@
         "@webassemblyjs/helper-module-context": "1.7.6",
         "@webassemblyjs/helper-wasm-bytecode": "1.7.6",
         "@webassemblyjs/wast-parser": "1.7.6",
-        "mamacro": "^0.0.3"
+        "mamacro": "0.0.3"
       }
     },
     "@webassemblyjs/floating-point-hex-parser": {
@@ -945,7 +945,7 @@
       "integrity": "sha512-e8/6GbY7OjLM+6OsN7f2krC2qYVNaSr0B0oe4lWdmq5sL++8dYDD1TFbD1TdAdWMRTYNr/Qq7ovXWzia2EbSjw==",
       "dev": true,
       "requires": {
-        "mamacro": "^0.0.3"
+        "mamacro": "0.0.3"
       }
     },
     "@webassemblyjs/helper-wasm-bytecode": {
@@ -972,7 +972,7 @@
       "integrity": "sha512-V4cIp0ruyw+hawUHwQLn6o2mFEw4t50tk530oKsYXQhEzKR+xNGDxs/SFFuyTO7X3NzEu4usA3w5jzhl2RYyzQ==",
       "dev": true,
       "requires": {
-        "@xtuc/ieee754": "^1.2.0"
+        "@xtuc/ieee754": "1.2.0"
       }
     },
     "@webassemblyjs/leb128": {
@@ -1057,7 +1057,7 @@
         "@webassemblyjs/helper-code-frame": "1.7.6",
         "@webassemblyjs/helper-fsm": "1.7.6",
         "@xtuc/long": "4.2.1",
-        "mamacro": "^0.0.3"
+        "mamacro": "0.0.3"
       }
     },
     "@webassemblyjs/wast-printer": {
@@ -1095,7 +1095,7 @@
       "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
       "dev": true,
       "requires": {
-        "mime-types": "~2.1.18",
+        "mime-types": "2.1.20",
         "negotiator": "0.6.1"
       }
     },
@@ -1111,7 +1111,7 @@
       "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==",
       "dev": true,
       "requires": {
-        "acorn": "^5.0.0"
+        "acorn": "5.7.3"
       }
     },
     "adm-zip": {
@@ -1132,7 +1132,7 @@
       "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
       "dev": true,
       "requires": {
-        "es6-promisify": "^5.0.0"
+        "es6-promisify": "5.0.0"
       }
     },
     "ajv": {
@@ -1141,10 +1141,10 @@
       "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==",
       "dev": true,
       "requires": {
-        "fast-deep-equal": "^2.0.1",
-        "fast-json-stable-stringify": "^2.0.0",
-        "json-schema-traverse": "^0.4.1",
-        "uri-js": "^4.2.2"
+        "fast-deep-equal": "2.0.1",
+        "fast-json-stable-stringify": "2.0.0",
+        "json-schema-traverse": "0.4.1",
+        "uri-js": "4.2.2"
       },
       "dependencies": {
         "fast-deep-equal": {
@@ -1209,7 +1209,7 @@
       "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
       "dev": true,
       "requires": {
-        "color-convert": "^1.9.0"
+        "color-convert": "1.9.3"
       }
     },
     "anymatch": {
@@ -1218,8 +1218,8 @@
       "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
       "dev": true,
       "requires": {
-        "micromatch": "^3.1.4",
-        "normalize-path": "^2.1.1"
+        "micromatch": "3.1.10",
+        "normalize-path": "2.1.1"
       }
     },
     "app-root-path": {
@@ -1234,7 +1234,7 @@
       "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==",
       "dev": true,
       "requires": {
-        "default-require-extensions": "^2.0.0"
+        "default-require-extensions": "2.0.0"
       }
     },
     "aproba": {
@@ -1248,10 +1248,9 @@
       "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"
+        "delegates": "1.0.0",
+        "readable-stream": "2.3.6"
       }
     },
     "argparse": {
@@ -1260,7 +1259,7 @@
       "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
       "dev": true,
       "requires": {
-        "sprintf-js": "~1.0.2"
+        "sprintf-js": "1.0.3"
       }
     },
     "arr-diff": {
@@ -1305,7 +1304,7 @@
       "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
       "dev": true,
       "requires": {
-        "array-uniq": "^1.0.1"
+        "array-uniq": "1.0.3"
       }
     },
     "array-uniq": {
@@ -1345,7 +1344,7 @@
       "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
       "dev": true,
       "requires": {
-        "safer-buffer": "~2.1.0"
+        "safer-buffer": "2.1.2"
       }
     },
     "asn1.js": {
@@ -1354,9 +1353,9 @@
       "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
       "dev": true,
       "requires": {
-        "bn.js": "^4.0.0",
-        "inherits": "^2.0.1",
-        "minimalistic-assert": "^1.0.0"
+        "bn.js": "4.11.8",
+        "inherits": "2.0.3",
+        "minimalistic-assert": "1.0.1"
       }
     },
     "assert": {
@@ -1434,12 +1433,12 @@
       "integrity": "sha512-kk4Zb6RUc58ld7gdosERHMF3DzIYJc2fp5sX46qEsGXQQy5bXsu8qyLjoxuY1NuQ/cJuCYnx99BfjwnRggrYIw==",
       "dev": true,
       "requires": {
-        "browserslist": "^4.1.0",
-        "caniuse-lite": "^1.0.30000884",
-        "normalize-range": "^0.1.2",
-        "num2fraction": "^1.2.2",
-        "postcss": "^7.0.2",
-        "postcss-value-parser": "^3.2.3"
+        "browserslist": "4.4.1",
+        "caniuse-lite": "1.0.30000932",
+        "normalize-range": "0.1.2",
+        "num2fraction": "1.2.2",
+        "postcss": "7.0.5",
+        "postcss-value-parser": "3.3.1"
       }
     },
     "aws-sign2": {
@@ -1460,9 +1459,9 @@
       "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
       "dev": true,
       "requires": {
-        "chalk": "^1.1.3",
-        "esutils": "^2.0.2",
-        "js-tokens": "^3.0.2"
+        "chalk": "1.1.3",
+        "esutils": "2.0.2",
+        "js-tokens": "3.0.2"
       },
       "dependencies": {
         "ansi-styles": {
@@ -1477,11 +1476,11 @@
           "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"
+            "ansi-styles": "2.2.1",
+            "escape-string-regexp": "1.0.5",
+            "has-ansi": "2.0.0",
+            "strip-ansi": "3.0.1",
+            "supports-color": "2.0.0"
           }
         },
         "supports-color": {
@@ -1498,14 +1497,14 @@
       "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"
+        "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.10",
+        "source-map": "0.5.7",
+        "trim-right": "1.0.1"
       }
     },
     "babel-messages": {
@@ -1514,7 +1513,7 @@
       "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
       "dev": true,
       "requires": {
-        "babel-runtime": "^6.22.0"
+        "babel-runtime": "6.26.0"
       }
     },
     "babel-runtime": {
@@ -1523,8 +1522,8 @@
       "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
       "dev": true,
       "requires": {
-        "core-js": "^2.4.0",
-        "regenerator-runtime": "^0.11.0"
+        "core-js": "2.6.5",
+        "regenerator-runtime": "0.11.1"
       }
     },
     "babel-template": {
@@ -1533,11 +1532,11 @@
       "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-runtime": "6.26.0",
+        "babel-traverse": "6.26.0",
+        "babel-types": "6.26.0",
+        "babylon": "6.18.0",
+        "lodash": "4.17.10"
       }
     },
     "babel-traverse": {
@@ -1546,15 +1545,15 @@
       "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-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.9",
+        "globals": "9.18.0",
+        "invariant": "2.2.4",
+        "lodash": "4.17.10"
       }
     },
     "babel-types": {
@@ -1563,10 +1562,10 @@
       "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"
+        "babel-runtime": "6.26.0",
+        "esutils": "2.0.2",
+        "lodash": "4.17.10",
+        "to-fast-properties": "1.0.3"
       }
     },
     "babylon": {
@@ -1593,13 +1592,13 @@
       "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"
+        "cache-base": "1.0.1",
+        "class-utils": "0.3.6",
+        "component-emitter": "1.2.1",
+        "define-property": "1.0.0",
+        "isobject": "3.0.1",
+        "mixin-deep": "1.3.1",
+        "pascalcase": "0.1.1"
       },
       "dependencies": {
         "define-property": {
@@ -1608,7 +1607,7 @@
           "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
           "dev": true,
           "requires": {
-            "is-descriptor": "^1.0.0"
+            "is-descriptor": "1.0.2"
           }
         },
         "is-accessor-descriptor": {
@@ -1617,7 +1616,7 @@
           "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
           "dev": true,
           "requires": {
-            "kind-of": "^6.0.0"
+            "kind-of": "6.0.2"
           }
         },
         "is-data-descriptor": {
@@ -1626,7 +1625,7 @@
           "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
           "dev": true,
           "requires": {
-            "kind-of": "^6.0.0"
+            "kind-of": "6.0.2"
           }
         },
         "is-descriptor": {
@@ -1635,9 +1634,9 @@
           "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
           "dev": true,
           "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
+            "is-accessor-descriptor": "1.0.0",
+            "is-data-descriptor": "1.0.0",
+            "kind-of": "6.0.2"
           }
         }
       }
@@ -1673,7 +1672,7 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "tweetnacl": "^0.14.3"
+        "tweetnacl": "0.14.5"
       }
     },
     "better-assert": {
@@ -1710,7 +1709,7 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "inherits": "~2.0.0"
+        "inherits": "2.0.3"
       }
     },
     "blocking-proxy": {
@@ -1719,7 +1718,7 @@
       "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==",
       "dev": true,
       "requires": {
-        "minimist": "^1.2.0"
+        "minimist": "1.2.0"
       },
       "dependencies": {
         "minimist": {
@@ -1749,15 +1748,15 @@
       "dev": true,
       "requires": {
         "bytes": "3.0.0",
-        "content-type": "~1.0.4",
+        "content-type": "1.0.4",
         "debug": "2.6.9",
-        "depd": "~1.1.1",
-        "http-errors": "~1.6.2",
+        "depd": "1.1.2",
+        "http-errors": "1.6.3",
         "iconv-lite": "0.4.19",
-        "on-finished": "~2.3.0",
+        "on-finished": "2.3.0",
         "qs": "6.5.1",
         "raw-body": "2.3.2",
-        "type-is": "~1.6.15"
+        "type-is": "1.6.16"
       },
       "dependencies": {
         "qs": {
@@ -1774,18 +1773,18 @@
       "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"
+        "array-flatten": "2.1.2",
+        "deep-equal": "1.0.1",
+        "dns-equal": "1.0.0",
+        "dns-txt": "2.0.2",
+        "multicast-dns": "6.2.3",
+        "multicast-dns-service-types": "1.1.0"
       }
     },
     "bootstrap-css-only": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/bootstrap-css-only/-/bootstrap-css-only-4.2.1.tgz",
-      "integrity": "sha512-3SkuhCfiAS8nMlJXoQjIUTQqlWVNFQP71VF9IzNc+6YxMtlHz6gfhLQkuADcyWQu9+EE/l2SqJbUJfsB3fB1MA=="
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/bootstrap-css-only/-/bootstrap-css-only-4.3.1.tgz",
+      "integrity": "sha512-xPQNmTR6skX7boM3Q/K2vWDL8RFhfHm5PbTcn/vd7nZtkzg9tc6ScNreIIsMaP9QLUxeqvUx+OGnDaiK4KBRiQ=="
     },
     "brace-expansion": {
       "version": "1.1.11",
@@ -1793,7 +1792,7 @@
       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
       "dev": true,
       "requires": {
-        "balanced-match": "^1.0.0",
+        "balanced-match": "1.0.0",
         "concat-map": "0.0.1"
       }
     },
@@ -1803,16 +1802,16 @@
       "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"
+        "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.3",
+        "snapdragon": "0.8.2",
+        "snapdragon-node": "2.1.1",
+        "split-string": "3.1.0",
+        "to-regex": "3.0.2"
       },
       "dependencies": {
         "extend-shallow": {
@@ -1821,7 +1820,7 @@
           "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
           "dev": true,
           "requires": {
-            "is-extendable": "^0.1.0"
+            "is-extendable": "0.1.1"
           }
         }
       }
@@ -1838,12 +1837,12 @@
       "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"
+        "buffer-xor": "1.0.3",
+        "cipher-base": "1.0.4",
+        "create-hash": "1.2.0",
+        "evp_bytestokey": "1.0.3",
+        "inherits": "2.0.3",
+        "safe-buffer": "5.1.2"
       }
     },
     "browserify-cipher": {
@@ -1852,9 +1851,9 @@
       "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-aes": "1.2.0",
+        "browserify-des": "1.0.2",
+        "evp_bytestokey": "1.0.3"
       }
     },
     "browserify-des": {
@@ -1863,10 +1862,10 @@
       "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"
+        "cipher-base": "1.0.4",
+        "des.js": "1.0.0",
+        "inherits": "2.0.3",
+        "safe-buffer": "5.1.2"
       }
     },
     "browserify-rsa": {
@@ -1875,8 +1874,8 @@
       "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
       "dev": true,
       "requires": {
-        "bn.js": "^4.1.0",
-        "randombytes": "^2.0.1"
+        "bn.js": "4.11.8",
+        "randombytes": "2.0.6"
       }
     },
     "browserify-sign": {
@@ -1885,13 +1884,13 @@
       "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"
+        "bn.js": "4.11.8",
+        "browserify-rsa": "4.0.1",
+        "create-hash": "1.2.0",
+        "create-hmac": "1.1.7",
+        "elliptic": "6.4.1",
+        "inherits": "2.0.3",
+        "parse-asn1": "5.1.3"
       }
     },
     "browserify-zlib": {
@@ -1900,7 +1899,7 @@
       "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
       "dev": true,
       "requires": {
-        "pako": "~1.0.5"
+        "pako": "1.0.8"
       }
     },
     "browserslist": {
@@ -1909,9 +1908,9 @@
       "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"
+        "caniuse-lite": "1.0.30000932",
+        "electron-to-chromium": "1.3.109",
+        "node-releases": "1.1.6"
       }
     },
     "browserstack": {
@@ -1920,7 +1919,7 @@
       "integrity": "sha512-+6AFt9HzhKykcPF79W6yjEUJcdvZOV0lIXdkORXMJftGrDl0OKWqRF4GHqpDNkxiceDT/uB7Fb/aDwktvXX7dg==",
       "dev": true,
       "requires": {
-        "https-proxy-agent": "^2.2.1"
+        "https-proxy-agent": "2.2.1"
       }
     },
     "buffer": {
@@ -1929,9 +1928,9 @@
       "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
       "dev": true,
       "requires": {
-        "base64-js": "^1.0.2",
-        "ieee754": "^1.1.4",
-        "isarray": "^1.0.0"
+        "base64-js": "1.3.0",
+        "ieee754": "1.1.12",
+        "isarray": "1.0.0"
       }
     },
     "buffer-alloc": {
@@ -1940,8 +1939,8 @@
       "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
       "dev": true,
       "requires": {
-        "buffer-alloc-unsafe": "^1.1.0",
-        "buffer-fill": "^1.0.0"
+        "buffer-alloc-unsafe": "1.1.0",
+        "buffer-fill": "1.0.0"
       }
     },
     "buffer-alloc-unsafe": {
@@ -2004,19 +2003,19 @@
       "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"
+        "bluebird": "3.5.2",
+        "chownr": "1.1.1",
+        "glob": "7.1.3",
+        "graceful-fs": "4.1.11",
+        "lru-cache": "4.1.3",
+        "mississippi": "2.0.0",
+        "mkdirp": "0.5.1",
+        "move-concurrently": "1.0.1",
+        "promise-inflight": "1.0.1",
+        "rimraf": "2.6.2",
+        "ssri": "5.3.0",
+        "unique-filename": "1.1.1",
+        "y18n": "4.0.0"
       }
     },
     "cache-base": {
@@ -2025,15 +2024,15 @@
       "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"
+        "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": {
@@ -2056,8 +2055,8 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "camelcase": "^2.0.0",
-        "map-obj": "^1.0.0"
+        "camelcase": "2.1.1",
+        "map-obj": "1.0.1"
       }
     },
     "caniuse-lite": {
@@ -2084,9 +2083,9 @@
       "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
       "dev": true,
       "requires": {
-        "ansi-styles": "^3.2.1",
-        "escape-string-regexp": "^1.0.5",
-        "supports-color": "^5.3.0"
+        "ansi-styles": "3.2.1",
+        "escape-string-regexp": "1.0.5",
+        "supports-color": "5.5.0"
       }
     },
     "chardet": {
@@ -2101,19 +2100,19 @@
       "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"
+        "anymatch": "2.0.0",
+        "async-each": "1.0.1",
+        "braces": "2.3.2",
+        "fsevents": "1.2.4",
+        "glob-parent": "3.1.0",
+        "inherits": "2.0.3",
+        "is-binary-path": "1.0.1",
+        "is-glob": "4.0.0",
+        "lodash.debounce": "4.0.8",
+        "normalize-path": "2.1.1",
+        "path-is-absolute": "1.0.1",
+        "readdirp": "2.1.0",
+        "upath": "1.1.0"
       }
     },
     "chownr": {
@@ -2128,7 +2127,7 @@
       "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==",
       "dev": true,
       "requires": {
-        "tslib": "^1.9.0"
+        "tslib": "1.9.3"
       }
     },
     "cipher-base": {
@@ -2137,8 +2136,8 @@
       "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
       "dev": true,
       "requires": {
-        "inherits": "^2.0.1",
-        "safe-buffer": "^5.0.1"
+        "inherits": "2.0.3",
+        "safe-buffer": "5.1.2"
       }
     },
     "circular-dependency-plugin": {
@@ -2153,10 +2152,10 @@
       "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"
+        "arr-union": "3.1.0",
+        "define-property": "0.2.5",
+        "isobject": "3.0.1",
+        "static-extend": "0.1.2"
       },
       "dependencies": {
         "define-property": {
@@ -2165,7 +2164,7 @@
           "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
           "dev": true,
           "requires": {
-            "is-descriptor": "^0.1.0"
+            "is-descriptor": "0.1.6"
           }
         }
       }
@@ -2176,7 +2175,7 @@
       "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==",
       "dev": true,
       "requires": {
-        "source-map": "~0.6.0"
+        "source-map": "0.6.1"
       },
       "dependencies": {
         "source-map": {
@@ -2193,7 +2192,7 @@
       "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
       "dev": true,
       "requires": {
-        "restore-cursor": "^2.0.0"
+        "restore-cursor": "2.0.0"
       }
     },
     "cli-width": {
@@ -2208,9 +2207,9 @@
       "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
       "dev": true,
       "requires": {
-        "string-width": "^1.0.1",
-        "strip-ansi": "^3.0.1",
-        "wrap-ansi": "^2.0.0"
+        "string-width": "1.0.2",
+        "strip-ansi": "3.0.1",
+        "wrap-ansi": "2.1.0"
       }
     },
     "clone": {
@@ -2225,10 +2224,10 @@
       "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"
+        "for-own": "1.0.0",
+        "is-plain-object": "2.0.4",
+        "kind-of": "6.0.2",
+        "shallow-clone": "1.0.0"
       }
     },
     "co": {
@@ -2249,12 +2248,12 @@
       "integrity": "sha512-CKwfgpfkqi9dyzy4s6ELaxJ54QgJ6A8iTSsM4bzHbLuTpbKncvNc3DUlCvpnkHBhK47gEf4qFsWoYqLrJPhy6g==",
       "dev": true,
       "requires": {
-        "app-root-path": "^2.0.1",
-        "css-selector-tokenizer": "^0.7.0",
-        "cssauron": "^1.4.0",
-        "semver-dsl": "^1.0.1",
-        "source-map": "^0.5.6",
-        "sprintf-js": "^1.0.3"
+        "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.0.3"
       }
     },
     "collection-visit": {
@@ -2263,8 +2262,8 @@
       "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
       "dev": true,
       "requires": {
-        "map-visit": "^1.0.0",
-        "object-visit": "^1.0.0"
+        "map-visit": "1.0.0",
+        "object-visit": "1.0.1"
       }
     },
     "color-convert": {
@@ -2294,7 +2293,7 @@
       "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=",
       "dev": true,
       "requires": {
-        "lodash": "^4.5.0"
+        "lodash": "4.17.10"
       }
     },
     "combined-stream": {
@@ -2303,7 +2302,7 @@
       "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
       "dev": true,
       "requires": {
-        "delayed-stream": "~1.0.0"
+        "delayed-stream": "1.0.0"
       }
     },
     "commander": {
@@ -2348,7 +2347,7 @@
       "integrity": "sha512-4aE67DL33dSW9gw4CI2H/yTxqHLNcxp0yS6jB+4h+wr3e43+1z7vm0HU9qXOH8j+qjKuL8+UtkOxYQSMq60Ylw==",
       "dev": true,
       "requires": {
-        "mime-db": ">= 1.36.0 < 2"
+        "mime-db": "1.36.0"
       }
     },
     "compression": {
@@ -2357,13 +2356,13 @@
       "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==",
       "dev": true,
       "requires": {
-        "accepts": "~1.3.5",
+        "accepts": "1.3.5",
         "bytes": "3.0.0",
-        "compressible": "~2.0.14",
+        "compressible": "2.0.15",
         "debug": "2.6.9",
-        "on-headers": "~1.0.1",
+        "on-headers": "1.0.1",
         "safe-buffer": "5.1.2",
-        "vary": "~1.1.2"
+        "vary": "1.1.2"
       }
     },
     "concat-map": {
@@ -2378,10 +2377,10 @@
       "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"
+        "buffer-from": "1.1.1",
+        "inherits": "2.0.3",
+        "readable-stream": "2.3.6",
+        "typedarray": "0.0.6"
       }
     },
     "connect": {
@@ -2392,7 +2391,7 @@
       "requires": {
         "debug": "2.6.9",
         "finalhandler": "1.1.0",
-        "parseurl": "~1.3.2",
+        "parseurl": "1.3.2",
         "utils-merge": "1.0.1"
       },
       "dependencies": {
@@ -2403,12 +2402,12 @@
           "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"
+            "encodeurl": "1.0.2",
+            "escape-html": "1.0.3",
+            "on-finished": "2.3.0",
+            "parseurl": "1.3.2",
+            "statuses": "1.3.1",
+            "unpipe": "1.0.0"
           }
         },
         "statuses": {
@@ -2431,15 +2430,14 @@
       "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
       "dev": true,
       "requires": {
-        "date-now": "^0.1.4"
+        "date-now": "0.1.4"
       }
     },
     "console-control-strings": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
       "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
-      "dev": true,
-      "optional": true
+      "dev": true
     },
     "constants-browserify": {
       "version": "1.0.0",
@@ -2465,7 +2463,7 @@
       "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==",
       "dev": true,
       "requires": {
-        "safe-buffer": "~5.1.1"
+        "safe-buffer": "5.1.2"
       }
     },
     "cookie": {
@@ -2486,12 +2484,12 @@
       "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"
+        "aproba": "1.2.0",
+        "fs-write-stream-atomic": "1.0.10",
+        "iferr": "0.1.5",
+        "mkdirp": "0.5.1",
+        "rimraf": "2.6.2",
+        "run-queue": "1.0.3"
       }
     },
     "copy-descriptor": {
@@ -2506,20 +2504,20 @@
       "integrity": "sha512-0lstlEyj74OAtYMrDxlNZsU7cwFijAI3Ofz2fD6Mpo9r4xCv4yegfa3uHIKvZY1NSuOtE9nvG6TAhJ+uz9gDaQ==",
       "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"
+        "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.3.0",
+        "serialize-javascript": "1.6.1"
       }
     },
     "core-js": {
-      "version": "2.6.3",
-      "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.3.tgz",
-      "integrity": "sha512-l00tmFFZOBHtYhN4Cz7k32VM7vTn3rE2ANjQDxdEN6zmXZ/xq1jQuutnmHvMG1ZJ7xd72+TA5YpUK8wz3rWsfQ=="
+      "version": "2.6.5",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz",
+      "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A=="
     },
     "core-util-is": {
       "version": "1.0.2",
@@ -2533,10 +2531,10 @@
       "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"
+        "is-directory": "0.3.1",
+        "js-yaml": "3.12.0",
+        "parse-json": "4.0.0",
+        "require-from-string": "2.0.2"
       },
       "dependencies": {
         "parse-json": {
@@ -2545,8 +2543,8 @@
           "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
           "dev": true,
           "requires": {
-            "error-ex": "^1.3.1",
-            "json-parse-better-errors": "^1.0.1"
+            "error-ex": "1.3.2",
+            "json-parse-better-errors": "1.0.2"
           }
         }
       }
@@ -2557,8 +2555,8 @@
       "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==",
       "dev": true,
       "requires": {
-        "bn.js": "^4.1.0",
-        "elliptic": "^6.0.0"
+        "bn.js": "4.11.8",
+        "elliptic": "6.4.1"
       }
     },
     "create-hash": {
@@ -2567,11 +2565,11 @@
       "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"
+        "cipher-base": "1.0.4",
+        "inherits": "2.0.3",
+        "md5.js": "1.3.5",
+        "ripemd160": "2.0.2",
+        "sha.js": "2.4.11"
       }
     },
     "create-hmac": {
@@ -2580,12 +2578,12 @@
       "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"
+        "cipher-base": "1.0.4",
+        "create-hash": "1.2.0",
+        "inherits": "2.0.3",
+        "ripemd160": "2.0.2",
+        "safe-buffer": "5.1.2",
+        "sha.js": "2.4.11"
       }
     },
     "cross-spawn": {
@@ -2595,8 +2593,8 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "lru-cache": "^4.0.1",
-        "which": "^1.2.9"
+        "lru-cache": "4.1.3",
+        "which": "1.3.1"
       }
     },
     "crypto-browserify": {
@@ -2605,17 +2603,17 @@
       "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"
+        "browserify-cipher": "1.0.1",
+        "browserify-sign": "4.0.4",
+        "create-ecdh": "4.0.3",
+        "create-hash": "1.2.0",
+        "create-hmac": "1.1.7",
+        "diffie-hellman": "5.0.3",
+        "inherits": "2.0.3",
+        "pbkdf2": "3.0.17",
+        "public-encrypt": "4.0.3",
+        "randombytes": "2.0.6",
+        "randomfill": "1.0.4"
       }
     },
     "css-parse": {
@@ -2630,9 +2628,9 @@
       "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=",
       "dev": true,
       "requires": {
-        "cssesc": "^0.1.0",
-        "fastparse": "^1.1.1",
-        "regexpu-core": "^1.0.0"
+        "cssesc": "0.1.0",
+        "fastparse": "1.1.1",
+        "regexpu-core": "1.0.0"
       }
     },
     "cssauron": {
@@ -2641,7 +2639,7 @@
       "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=",
       "dev": true,
       "requires": {
-        "through": "X.X.X"
+        "through": "2.3.8"
       }
     },
     "cssesc": {
@@ -2656,7 +2654,7 @@
       "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
       "dev": true,
       "requires": {
-        "array-find-index": "^1.0.1"
+        "array-find-index": "1.0.2"
       }
     },
     "custom-event": {
@@ -2677,7 +2675,7 @@
       "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
       "dev": true,
       "requires": {
-        "assert-plus": "^1.0.0"
+        "assert-plus": "1.0.0"
       }
     },
     "date-now": {
@@ -2725,8 +2723,8 @@
       "integrity": "sha512-lAc4i9QJR0YHSDFdzeBQKfZ1SRDG3hsJNEkrpcZa8QhBfidLAilT60BDEIVUUGqosFp425KOgB3uYqcnQrWafQ==",
       "dev": true,
       "requires": {
-        "execa": "^0.10.0",
-        "ip-regex": "^2.1.0"
+        "execa": "0.10.0",
+        "ip-regex": "2.1.0"
       }
     },
     "default-require-extensions": {
@@ -2735,7 +2733,7 @@
       "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=",
       "dev": true,
       "requires": {
-        "strip-bom": "^3.0.0"
+        "strip-bom": "3.0.0"
       },
       "dependencies": {
         "strip-bom": {
@@ -2752,8 +2750,8 @@
       "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
       "dev": true,
       "requires": {
-        "is-descriptor": "^1.0.2",
-        "isobject": "^3.0.1"
+        "is-descriptor": "1.0.2",
+        "isobject": "3.0.1"
       },
       "dependencies": {
         "is-accessor-descriptor": {
@@ -2762,7 +2760,7 @@
           "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
           "dev": true,
           "requires": {
-            "kind-of": "^6.0.0"
+            "kind-of": "6.0.2"
           }
         },
         "is-data-descriptor": {
@@ -2771,7 +2769,7 @@
           "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
           "dev": true,
           "requires": {
-            "kind-of": "^6.0.0"
+            "kind-of": "6.0.2"
           }
         },
         "is-descriptor": {
@@ -2780,9 +2778,9 @@
           "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
           "dev": true,
           "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
+            "is-accessor-descriptor": "1.0.0",
+            "is-data-descriptor": "1.0.0",
+            "kind-of": "6.0.2"
           }
         }
       }
@@ -2793,12 +2791,12 @@
       "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"
+        "globby": "6.1.0",
+        "is-path-cwd": "1.0.0",
+        "is-path-in-cwd": "1.0.1",
+        "p-map": "1.2.0",
+        "pify": "3.0.0",
+        "rimraf": "2.6.2"
       },
       "dependencies": {
         "globby": {
@@ -2807,11 +2805,11 @@
           "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"
+            "array-union": "1.0.2",
+            "glob": "7.1.3",
+            "object-assign": "4.1.1",
+            "pify": "2.3.0",
+            "pinkie-promise": "2.0.1"
           },
           "dependencies": {
             "pify": {
@@ -2834,8 +2832,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",
@@ -2855,8 +2852,8 @@
       "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
       "dev": true,
       "requires": {
-        "inherits": "^2.0.1",
-        "minimalistic-assert": "^1.0.0"
+        "inherits": "2.0.3",
+        "minimalistic-assert": "1.0.1"
       }
     },
     "destroy": {
@@ -2871,7 +2868,7 @@
       "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=",
       "dev": true,
       "requires": {
-        "repeating": "^2.0.0"
+        "repeating": "2.0.1"
       }
     },
     "detect-node": {
@@ -2898,9 +2895,9 @@
       "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
       "dev": true,
       "requires": {
-        "bn.js": "^4.1.0",
-        "miller-rabin": "^4.0.0",
-        "randombytes": "^2.0.0"
+        "bn.js": "4.11.8",
+        "miller-rabin": "4.0.1",
+        "randombytes": "2.0.6"
       }
     },
     "dir-glob": {
@@ -2909,7 +2906,7 @@
       "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==",
       "dev": true,
       "requires": {
-        "path-type": "^3.0.0"
+        "path-type": "3.0.0"
       }
     },
     "dns-equal": {
@@ -2924,8 +2921,8 @@
       "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==",
       "dev": true,
       "requires": {
-        "ip": "^1.1.0",
-        "safe-buffer": "^5.0.1"
+        "ip": "1.1.5",
+        "safe-buffer": "5.1.2"
       }
     },
     "dns-txt": {
@@ -2934,7 +2931,7 @@
       "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=",
       "dev": true,
       "requires": {
-        "buffer-indexof": "^1.0.0"
+        "buffer-indexof": "1.1.1"
       }
     },
     "dom-serialize": {
@@ -2943,10 +2940,10 @@
       "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=",
       "dev": true,
       "requires": {
-        "custom-event": "~1.0.0",
-        "ent": "~2.2.0",
-        "extend": "^3.0.0",
-        "void-elements": "^2.0.0"
+        "custom-event": "1.0.1",
+        "ent": "2.2.0",
+        "extend": "3.0.2",
+        "void-elements": "2.0.1"
       }
     },
     "domain-browser": {
@@ -2961,10 +2958,10 @@
       "integrity": "sha512-vM58DwdnKmty+FSPzT14K9JXb90H+j5emaR4KYbr2KTIz00WHGbWOe5ghQTx233ZCLZtrGDALzKwcjEtSt35mA==",
       "dev": true,
       "requires": {
-        "end-of-stream": "^1.0.0",
-        "inherits": "^2.0.1",
-        "readable-stream": "^2.0.0",
-        "stream-shift": "^1.0.0"
+        "end-of-stream": "1.4.1",
+        "inherits": "2.0.3",
+        "readable-stream": "2.3.6",
+        "stream-shift": "1.0.0"
       }
     },
     "ecc-jsbn": {
@@ -2974,8 +2971,8 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "jsbn": "~0.1.0",
-        "safer-buffer": "^2.1.0"
+        "jsbn": "0.1.1",
+        "safer-buffer": "2.1.2"
       }
     },
     "ee-first": {
@@ -2996,13 +2993,13 @@
       "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"
+        "bn.js": "4.11.8",
+        "brorand": "1.1.0",
+        "hash.js": "1.1.7",
+        "hmac-drbg": "1.0.1",
+        "inherits": "2.0.3",
+        "minimalistic-assert": "1.0.1",
+        "minimalistic-crypto-utils": "1.0.1"
       }
     },
     "emojis-list": {
@@ -3023,7 +3020,7 @@
       "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
       "dev": true,
       "requires": {
-        "once": "^1.4.0"
+        "once": "1.4.0"
       }
     },
     "engine.io": {
@@ -3046,7 +3043,7 @@
           "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=",
           "dev": true,
           "requires": {
-            "mime-types": "~2.1.11",
+            "mime-types": "2.1.20",
             "negotiator": "0.6.1"
           }
         },
@@ -3124,9 +3121,9 @@
       "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==",
       "dev": true,
       "requires": {
-        "graceful-fs": "^4.1.2",
-        "memory-fs": "^0.4.0",
-        "tapable": "^1.0.0"
+        "graceful-fs": "4.1.11",
+        "memory-fs": "0.4.1",
+        "tapable": "1.1.1"
       }
     },
     "ent": {
@@ -3141,7 +3138,7 @@
       "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
       "dev": true,
       "requires": {
-        "prr": "~1.0.1"
+        "prr": "1.0.1"
       }
     },
     "error-ex": {
@@ -3150,7 +3147,7 @@
       "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
       "dev": true,
       "requires": {
-        "is-arrayish": "^0.2.1"
+        "is-arrayish": "0.2.1"
       }
     },
     "es6-promise": {
@@ -3165,7 +3162,7 @@
       "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
       "dev": true,
       "requires": {
-        "es6-promise": "^4.0.3"
+        "es6-promise": "4.2.4"
       }
     },
     "escape-html": {
@@ -3186,11 +3183,11 @@
       "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=",
       "dev": true,
       "requires": {
-        "esprima": "^2.7.1",
-        "estraverse": "^1.9.1",
-        "esutils": "^2.0.2",
-        "optionator": "^0.8.1",
-        "source-map": "~0.2.0"
+        "esprima": "2.7.3",
+        "estraverse": "1.9.3",
+        "esutils": "2.0.2",
+        "optionator": "0.8.2",
+        "source-map": "0.2.0"
       },
       "dependencies": {
         "source-map": {
@@ -3200,7 +3197,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "amdefine": ">=0.0.4"
+            "amdefine": "1.0.1"
           }
         }
       }
@@ -3211,8 +3208,8 @@
       "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==",
       "dev": true,
       "requires": {
-        "esrecurse": "^4.1.0",
-        "estraverse": "^4.1.1"
+        "esrecurse": "4.2.1",
+        "estraverse": "4.2.0"
       },
       "dependencies": {
         "estraverse": {
@@ -3235,7 +3232,7 @@
       "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
       "dev": true,
       "requires": {
-        "estraverse": "^4.1.0"
+        "estraverse": "4.2.0"
       },
       "dependencies": {
         "estraverse": {
@@ -3282,7 +3279,7 @@
       "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=",
       "dev": true,
       "requires": {
-        "original": ">=0.0.5"
+        "original": "1.0.2"
       }
     },
     "evp_bytestokey": {
@@ -3291,8 +3288,8 @@
       "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
       "dev": true,
       "requires": {
-        "md5.js": "^1.3.4",
-        "safe-buffer": "^5.1.1"
+        "md5.js": "1.3.5",
+        "safe-buffer": "5.1.2"
       }
     },
     "execa": {
@@ -3301,13 +3298,13 @@
       "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"
+        "cross-spawn": "6.0.5",
+        "get-stream": "3.0.0",
+        "is-stream": "1.1.0",
+        "npm-run-path": "2.0.2",
+        "p-finally": "1.0.0",
+        "signal-exit": "3.0.2",
+        "strip-eof": "1.0.0"
       },
       "dependencies": {
         "cross-spawn": {
@@ -3316,11 +3313,11 @@
           "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"
+            "nice-try": "1.0.5",
+            "path-key": "2.0.1",
+            "semver": "5.5.1",
+            "shebang-command": "1.2.0",
+            "which": "1.3.1"
           }
         }
       }
@@ -3337,9 +3334,9 @@
       "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=",
       "dev": true,
       "requires": {
-        "array-slice": "^0.2.3",
-        "array-unique": "^0.2.1",
-        "braces": "^0.1.2"
+        "array-slice": "0.2.3",
+        "array-unique": "0.2.1",
+        "braces": "0.1.5"
       },
       "dependencies": {
         "array-unique": {
@@ -3354,7 +3351,7 @@
           "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=",
           "dev": true,
           "requires": {
-            "expand-range": "^0.1.0"
+            "expand-range": "0.1.1"
           }
         },
         "expand-range": {
@@ -3363,8 +3360,8 @@
           "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=",
           "dev": true,
           "requires": {
-            "is-number": "^0.1.1",
-            "repeat-string": "^0.2.2"
+            "is-number": "0.1.1",
+            "repeat-string": "0.2.2"
           }
         },
         "is-number": {
@@ -3387,13 +3384,13 @@
       "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"
+        "debug": "2.6.9",
+        "define-property": "0.2.5",
+        "extend-shallow": "2.0.1",
+        "posix-character-classes": "0.1.1",
+        "regex-not": "1.0.2",
+        "snapdragon": "0.8.2",
+        "to-regex": "3.0.2"
       },
       "dependencies": {
         "define-property": {
@@ -3402,7 +3399,7 @@
           "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
           "dev": true,
           "requires": {
-            "is-descriptor": "^0.1.0"
+            "is-descriptor": "0.1.6"
           }
         },
         "extend-shallow": {
@@ -3411,7 +3408,7 @@
           "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
           "dev": true,
           "requires": {
-            "is-extendable": "^0.1.0"
+            "is-extendable": "0.1.1"
           }
         }
       }
@@ -3422,7 +3419,7 @@
       "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=",
       "dev": true,
       "requires": {
-        "fill-range": "^2.1.0"
+        "fill-range": "2.2.4"
       },
       "dependencies": {
         "fill-range": {
@@ -3431,11 +3428,11 @@
           "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==",
           "dev": true,
           "requires": {
-            "is-number": "^2.1.0",
-            "isobject": "^2.0.0",
-            "randomatic": "^3.0.0",
-            "repeat-element": "^1.1.2",
-            "repeat-string": "^1.5.2"
+            "is-number": "2.1.0",
+            "isobject": "2.1.0",
+            "randomatic": "3.1.0",
+            "repeat-element": "1.1.3",
+            "repeat-string": "1.6.1"
           }
         },
         "is-number": {
@@ -3444,7 +3441,7 @@
           "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=",
           "dev": true,
           "requires": {
-            "kind-of": "^3.0.2"
+            "kind-of": "3.2.2"
           }
         },
         "isobject": {
@@ -3462,7 +3459,7 @@
           "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
           "dev": true,
           "requires": {
-            "is-buffer": "^1.1.5"
+            "is-buffer": "1.1.6"
           }
         }
       }
@@ -3473,36 +3470,36 @@
       "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
       "dev": true,
       "requires": {
-        "accepts": "~1.3.5",
+        "accepts": "1.3.5",
         "array-flatten": "1.1.1",
         "body-parser": "1.18.3",
         "content-disposition": "0.5.2",
-        "content-type": "~1.0.4",
+        "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",
+        "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",
+        "methods": "1.1.2",
+        "on-finished": "2.3.0",
+        "parseurl": "1.3.2",
         "path-to-regexp": "0.1.7",
-        "proxy-addr": "~2.0.4",
+        "proxy-addr": "2.0.4",
         "qs": "6.5.2",
-        "range-parser": "~1.2.0",
+        "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",
+        "statuses": "1.4.0",
+        "type-is": "1.6.16",
         "utils-merge": "1.0.1",
-        "vary": "~1.1.2"
+        "vary": "1.1.2"
       },
       "dependencies": {
         "array-flatten": {
@@ -3518,15 +3515,15 @@
           "dev": true,
           "requires": {
             "bytes": "3.0.0",
-            "content-type": "~1.0.4",
+            "content-type": "1.0.4",
             "debug": "2.6.9",
-            "depd": "~1.1.2",
-            "http-errors": "~1.6.3",
+            "depd": "1.1.2",
+            "http-errors": "1.6.3",
             "iconv-lite": "0.4.23",
-            "on-finished": "~2.3.0",
+            "on-finished": "2.3.0",
             "qs": "6.5.2",
             "raw-body": "2.3.3",
-            "type-is": "~1.6.16"
+            "type-is": "1.6.16"
           }
         },
         "iconv-lite": {
@@ -3535,7 +3532,7 @@
           "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
           "dev": true,
           "requires": {
-            "safer-buffer": ">= 2.1.2 < 3"
+            "safer-buffer": "2.1.2"
           }
         },
         "raw-body": {
@@ -3564,8 +3561,8 @@
       "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
       "dev": true,
       "requires": {
-        "assign-symbols": "^1.0.0",
-        "is-extendable": "^1.0.1"
+        "assign-symbols": "1.0.0",
+        "is-extendable": "1.0.1"
       },
       "dependencies": {
         "is-extendable": {
@@ -3574,7 +3571,7 @@
           "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
           "dev": true,
           "requires": {
-            "is-plain-object": "^2.0.4"
+            "is-plain-object": "2.0.4"
           }
         }
       }
@@ -3585,9 +3582,9 @@
       "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==",
       "dev": true,
       "requires": {
-        "chardet": "^0.7.0",
-        "iconv-lite": "^0.4.24",
-        "tmp": "^0.0.33"
+        "chardet": "0.7.0",
+        "iconv-lite": "0.4.24",
+        "tmp": "0.0.33"
       },
       "dependencies": {
         "iconv-lite": {
@@ -3596,7 +3593,7 @@
           "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
           "dev": true,
           "requires": {
-            "safer-buffer": ">= 2.1.2 < 3"
+            "safer-buffer": "2.1.2"
           }
         },
         "tmp": {
@@ -3605,7 +3602,7 @@
           "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
           "dev": true,
           "requires": {
-            "os-tmpdir": "~1.0.2"
+            "os-tmpdir": "1.0.2"
           }
         }
       }
@@ -3616,14 +3613,14 @@
       "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"
+        "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.2",
+        "snapdragon": "0.8.2",
+        "to-regex": "3.0.2"
       },
       "dependencies": {
         "define-property": {
@@ -3632,7 +3629,7 @@
           "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
           "dev": true,
           "requires": {
-            "is-descriptor": "^1.0.0"
+            "is-descriptor": "1.0.2"
           }
         },
         "extend-shallow": {
@@ -3641,7 +3638,7 @@
           "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
           "dev": true,
           "requires": {
-            "is-extendable": "^0.1.0"
+            "is-extendable": "0.1.1"
           }
         },
         "is-accessor-descriptor": {
@@ -3650,7 +3647,7 @@
           "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
           "dev": true,
           "requires": {
-            "kind-of": "^6.0.0"
+            "kind-of": "6.0.2"
           }
         },
         "is-data-descriptor": {
@@ -3659,7 +3656,7 @@
           "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
           "dev": true,
           "requires": {
-            "kind-of": "^6.0.0"
+            "kind-of": "6.0.2"
           }
         },
         "is-descriptor": {
@@ -3668,9 +3665,9 @@
           "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
           "dev": true,
           "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
+            "is-accessor-descriptor": "1.0.0",
+            "is-data-descriptor": "1.0.0",
+            "kind-of": "6.0.2"
           }
         }
       }
@@ -3723,7 +3720,7 @@
       "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=",
       "dev": true,
       "requires": {
-        "websocket-driver": ">=0.5.1"
+        "websocket-driver": "0.7.0"
       }
     },
     "fd-slicer": {
@@ -3732,7 +3729,7 @@
       "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=",
       "dev": true,
       "requires": {
-        "pend": "~1.2.0"
+        "pend": "1.2.0"
       }
     },
     "figgy-pudding": {
@@ -3747,7 +3744,7 @@
       "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
       "dev": true,
       "requires": {
-        "escape-string-regexp": "^1.0.5"
+        "escape-string-regexp": "1.0.5"
       }
     },
     "file-loader": {
@@ -3756,14 +3753,14 @@
       "integrity": "sha512-YCsBfd1ZGCyonOKLxPiKPdu+8ld9HAaMEvJewzz+b2eTF7uL5Zm/HdBF6FjCrpCMRq25Mi0U1gl4pwn2TlH7hQ==",
       "dev": true,
       "requires": {
-        "loader-utils": "^1.0.2",
-        "schema-utils": "^1.0.0"
+        "loader-utils": "1.1.0",
+        "schema-utils": "1.0.0"
       }
     },
     "file-saver": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.0.tgz",
-      "integrity": "sha512-cYM1ic5DAkg25pHKgi5f10ziAM7RJU37gaH1XQlyNDrtUnzhC/dfoV9zf2OmF0RMKi42jG5B0JWBnPQqyj/G6g=="
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.1.tgz",
+      "integrity": "sha512-dCB3K7/BvAcUmtmh1DzFdv0eXSVJ9IAFt1mw3XZfAexodNRoE29l3xB2EX4wH2q8m/UTzwzEPq/ArYk98kUkBQ=="
     },
     "filename-regex": {
       "version": "2.0.1",
@@ -3777,8 +3774,8 @@
       "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=",
       "dev": true,
       "requires": {
-        "glob": "^7.0.3",
-        "minimatch": "^3.0.3"
+        "glob": "7.1.3",
+        "minimatch": "3.0.4"
       }
     },
     "fill-range": {
@@ -3787,10 +3784,10 @@
       "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"
+        "extend-shallow": "2.0.1",
+        "is-number": "3.0.0",
+        "repeat-string": "1.6.1",
+        "to-regex-range": "2.1.1"
       },
       "dependencies": {
         "extend-shallow": {
@@ -3799,7 +3796,7 @@
           "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
           "dev": true,
           "requires": {
-            "is-extendable": "^0.1.0"
+            "is-extendable": "0.1.1"
           }
         }
       }
@@ -3811,12 +3808,12 @@
       "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"
+        "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": {
@@ -3825,9 +3822,9 @@
       "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=",
       "dev": true,
       "requires": {
-        "commondir": "^1.0.1",
-        "make-dir": "^1.0.0",
-        "pkg-dir": "^2.0.0"
+        "commondir": "1.0.1",
+        "make-dir": "1.3.0",
+        "pkg-dir": "2.0.0"
       }
     },
     "find-up": {
@@ -3836,7 +3833,7 @@
       "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
       "dev": true,
       "requires": {
-        "locate-path": "^2.0.0"
+        "locate-path": "2.0.0"
       }
     },
     "flush-write-stream": {
@@ -3845,8 +3842,8 @@
       "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==",
       "dev": true,
       "requires": {
-        "inherits": "^2.0.1",
-        "readable-stream": "^2.0.4"
+        "inherits": "2.0.3",
+        "readable-stream": "2.3.6"
       }
     },
     "follow-redirects": {
@@ -3855,7 +3852,7 @@
       "integrity": "sha512-NONJVIFiX7Z8k2WxfqBjtwqMifx7X42ORLFrOZ2LTKGj71G3C0kfdyTqGqr8fx5zSX6Foo/D95dgGWbPUiwnew==",
       "dev": true,
       "requires": {
-        "debug": "^3.1.0"
+        "debug": "3.1.0"
       },
       "dependencies": {
         "debug": {
@@ -3881,7 +3878,7 @@
       "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
       "dev": true,
       "requires": {
-        "for-in": "^1.0.1"
+        "for-in": "1.0.2"
       }
     },
     "forever-agent": {
@@ -3896,9 +3893,9 @@
       "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
       "dev": true,
       "requires": {
-        "asynckit": "^0.4.0",
+        "asynckit": "0.4.0",
         "combined-stream": "1.0.6",
-        "mime-types": "^2.1.12"
+        "mime-types": "2.1.20"
       }
     },
     "forwarded": {
@@ -3913,7 +3910,7 @@
       "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
       "dev": true,
       "requires": {
-        "map-cache": "^0.2.2"
+        "map-cache": "0.2.2"
       }
     },
     "fresh": {
@@ -3928,8 +3925,8 @@
       "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
       "dev": true,
       "requires": {
-        "inherits": "^2.0.1",
-        "readable-stream": "^2.0.0"
+        "inherits": "2.0.3",
+        "readable-stream": "2.3.6"
       }
     },
     "fs-access": {
@@ -3938,7 +3935,7 @@
       "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=",
       "dev": true,
       "requires": {
-        "null-check": "^1.0.0"
+        "null-check": "1.0.0"
       }
     },
     "fs-extra": {
@@ -3947,9 +3944,9 @@
       "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=",
       "dev": true,
       "requires": {
-        "graceful-fs": "^4.1.2",
-        "jsonfile": "^2.1.0",
-        "klaw": "^1.0.0"
+        "graceful-fs": "4.1.11",
+        "jsonfile": "2.4.0",
+        "klaw": "1.3.1"
       }
     },
     "fs-write-stream-atomic": {
@@ -3958,10 +3955,10 @@
       "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
       "dev": true,
       "requires": {
-        "graceful-fs": "^4.1.2",
-        "iferr": "^0.1.5",
-        "imurmurhash": "^0.1.4",
-        "readable-stream": "1 || 2"
+        "graceful-fs": "4.1.11",
+        "iferr": "0.1.5",
+        "imurmurhash": "0.1.4",
+        "readable-stream": "2.3.6"
       }
     },
     "fs.realpath": {
@@ -3977,8 +3974,8 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "nan": "^2.9.2",
-        "node-pre-gyp": "^0.10.0"
+        "nan": "2.11.1",
+        "node-pre-gyp": "0.10.0"
       },
       "dependencies": {
         "abbrev": {
@@ -4523,12 +4520,11 @@
       "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
       "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
       "dev": true,
-      "optional": true,
       "requires": {
-        "graceful-fs": "^4.1.2",
-        "inherits": "~2.0.0",
-        "mkdirp": ">=0.5 0",
-        "rimraf": "2"
+        "graceful-fs": "4.1.11",
+        "inherits": "2.0.3",
+        "mkdirp": "0.5.1",
+        "rimraf": "2.6.2"
       }
     },
     "gauge": {
@@ -4536,16 +4532,15 @@
       "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
       "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
       "dev": true,
-      "optional": true,
       "requires": {
-        "aproba": "^1.0.3",
-        "console-control-strings": "^1.0.0",
-        "has-unicode": "^2.0.0",
-        "object-assign": "^4.1.0",
-        "signal-exit": "^3.0.0",
-        "string-width": "^1.0.1",
-        "strip-ansi": "^3.0.1",
-        "wide-align": "^1.1.0"
+        "aproba": "1.2.0",
+        "console-control-strings": "1.1.0",
+        "has-unicode": "2.0.1",
+        "object-assign": "4.1.1",
+        "signal-exit": "3.0.2",
+        "string-width": "1.0.2",
+        "strip-ansi": "3.0.1",
+        "wide-align": "1.1.3"
       }
     },
     "gaze": {
@@ -4555,7 +4550,7 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "globule": "^1.0.0"
+        "globule": "1.2.1"
       }
     },
     "get-caller-file": {
@@ -4568,8 +4563,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",
@@ -4589,7 +4583,7 @@
       "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
       "dev": true,
       "requires": {
-        "assert-plus": "^1.0.0"
+        "assert-plus": "1.0.0"
       }
     },
     "glob": {
@@ -4598,12 +4592,12 @@
       "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"
+        "fs.realpath": "1.0.0",
+        "inflight": "1.0.6",
+        "inherits": "2.0.3",
+        "minimatch": "3.0.4",
+        "once": "1.4.0",
+        "path-is-absolute": "1.0.1"
       }
     },
     "glob-base": {
@@ -4612,8 +4606,8 @@
       "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=",
       "dev": true,
       "requires": {
-        "glob-parent": "^2.0.0",
-        "is-glob": "^2.0.0"
+        "glob-parent": "2.0.0",
+        "is-glob": "2.0.1"
       },
       "dependencies": {
         "glob-parent": {
@@ -4622,7 +4616,7 @@
           "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
           "dev": true,
           "requires": {
-            "is-glob": "^2.0.0"
+            "is-glob": "2.0.1"
           }
         },
         "is-extglob": {
@@ -4637,7 +4631,7 @@
           "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
           "dev": true,
           "requires": {
-            "is-extglob": "^1.0.0"
+            "is-extglob": "1.0.0"
           }
         }
       }
@@ -4648,8 +4642,8 @@
       "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
       "dev": true,
       "requires": {
-        "is-glob": "^3.1.0",
-        "path-dirname": "^1.0.0"
+        "is-glob": "3.1.0",
+        "path-dirname": "1.0.2"
       },
       "dependencies": {
         "is-glob": {
@@ -4658,7 +4652,7 @@
           "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
           "dev": true,
           "requires": {
-            "is-extglob": "^2.1.0"
+            "is-extglob": "2.1.1"
           }
         }
       }
@@ -4675,12 +4669,12 @@
       "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"
+        "array-union": "1.0.2",
+        "dir-glob": "2.2.2",
+        "glob": "7.1.3",
+        "ignore": "3.3.10",
+        "pify": "3.0.0",
+        "slash": "1.0.0"
       }
     },
     "globule": {
@@ -4690,9 +4684,9 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "glob": "~7.1.1",
-        "lodash": "~4.17.10",
-        "minimatch": "~3.0.2"
+        "glob": "7.1.3",
+        "lodash": "4.17.10",
+        "minimatch": "3.0.4"
       }
     },
     "graceful-fs": {
@@ -4713,10 +4707,10 @@
       "integrity": "sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA==",
       "dev": true,
       "requires": {
-        "async": "^2.5.0",
-        "optimist": "^0.6.1",
-        "source-map": "^0.6.1",
-        "uglify-js": "^3.1.4"
+        "async": "2.6.1",
+        "optimist": "0.6.1",
+        "source-map": "0.6.1",
+        "uglify-js": "3.4.9"
       },
       "dependencies": {
         "async": {
@@ -4725,7 +4719,7 @@
           "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
           "dev": true,
           "requires": {
-            "lodash": "^4.17.10"
+            "lodash": "4.17.10"
           }
         },
         "source-map": {
@@ -4748,8 +4742,8 @@
       "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==",
       "dev": true,
       "requires": {
-        "ajv": "^5.3.0",
-        "har-schema": "^2.0.0"
+        "ajv": "5.5.2",
+        "har-schema": "2.0.0"
       },
       "dependencies": {
         "ajv": {
@@ -4758,10 +4752,10 @@
           "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"
+            "co": "4.6.0",
+            "fast-deep-equal": "1.1.0",
+            "fast-json-stable-stringify": "2.0.0",
+            "json-schema-traverse": "0.3.1"
           }
         }
       }
@@ -4772,7 +4766,7 @@
       "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
       "dev": true,
       "requires": {
-        "ansi-regex": "^2.0.0"
+        "ansi-regex": "2.1.1"
       }
     },
     "has-binary": {
@@ -4808,8 +4802,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",
@@ -4817,9 +4810,9 @@
       "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
       "dev": true,
       "requires": {
-        "get-value": "^2.0.6",
-        "has-values": "^1.0.0",
-        "isobject": "^3.0.0"
+        "get-value": "2.0.6",
+        "has-values": "1.0.0",
+        "isobject": "3.0.1"
       }
     },
     "has-values": {
@@ -4828,8 +4821,8 @@
       "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
       "dev": true,
       "requires": {
-        "is-number": "^3.0.0",
-        "kind-of": "^4.0.0"
+        "is-number": "3.0.0",
+        "kind-of": "4.0.0"
       },
       "dependencies": {
         "kind-of": {
@@ -4838,7 +4831,7 @@
           "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
           "dev": true,
           "requires": {
-            "is-buffer": "^1.1.5"
+            "is-buffer": "1.1.6"
           }
         }
       }
@@ -4849,8 +4842,8 @@
       "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
       "dev": true,
       "requires": {
-        "inherits": "^2.0.1",
-        "safe-buffer": "^5.0.1"
+        "inherits": "2.0.3",
+        "safe-buffer": "5.1.2"
       }
     },
     "hash.js": {
@@ -4859,8 +4852,8 @@
       "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
       "dev": true,
       "requires": {
-        "inherits": "^2.0.3",
-        "minimalistic-assert": "^1.0.1"
+        "inherits": "2.0.3",
+        "minimalistic-assert": "1.0.1"
       }
     },
     "hasha": {
@@ -4869,8 +4862,8 @@
       "integrity": "sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=",
       "dev": true,
       "requires": {
-        "is-stream": "^1.0.1",
-        "pinkie-promise": "^2.0.0"
+        "is-stream": "1.1.0",
+        "pinkie-promise": "2.0.1"
       }
     },
     "he": {
@@ -4885,9 +4878,9 @@
       "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
       "dev": true,
       "requires": {
-        "hash.js": "^1.0.3",
-        "minimalistic-assert": "^1.0.0",
-        "minimalistic-crypto-utils": "^1.0.1"
+        "hash.js": "1.1.7",
+        "minimalistic-assert": "1.0.1",
+        "minimalistic-crypto-utils": "1.0.1"
       }
     },
     "hosted-git-info": {
@@ -4902,10 +4895,10 @@
       "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=",
       "dev": true,
       "requires": {
-        "inherits": "^2.0.1",
-        "obuf": "^1.0.0",
-        "readable-stream": "^2.0.1",
-        "wbuf": "^1.1.0"
+        "inherits": "2.0.3",
+        "obuf": "1.1.2",
+        "readable-stream": "2.3.6",
+        "wbuf": "1.7.3"
       }
     },
     "html-entities": {
@@ -4926,10 +4919,10 @@
       "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
       "dev": true,
       "requires": {
-        "depd": "~1.1.2",
+        "depd": "1.1.2",
         "inherits": "2.0.3",
         "setprototypeof": "1.1.0",
-        "statuses": ">= 1.4.0 < 2"
+        "statuses": "1.4.0"
       }
     },
     "http-parser-js": {
@@ -4944,9 +4937,9 @@
       "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==",
       "dev": true,
       "requires": {
-        "eventemitter3": "^3.0.0",
-        "follow-redirects": "^1.0.0",
-        "requires-port": "^1.0.0"
+        "eventemitter3": "3.1.0",
+        "follow-redirects": "1.5.7",
+        "requires-port": "1.0.0"
       }
     },
     "http-proxy-middleware": {
@@ -4955,10 +4948,10 @@
       "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-proxy": "1.17.0",
+        "is-glob": "4.0.0",
+        "lodash": "4.17.10",
+        "micromatch": "3.1.10"
       }
     },
     "http-signature": {
@@ -4967,9 +4960,9 @@
       "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
       "dev": true,
       "requires": {
-        "assert-plus": "^1.0.0",
-        "jsprim": "^1.2.2",
-        "sshpk": "^1.7.0"
+        "assert-plus": "1.0.0",
+        "jsprim": "1.4.1",
+        "sshpk": "1.14.2"
       }
     },
     "https-browserify": {
@@ -4984,8 +4977,8 @@
       "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==",
       "dev": true,
       "requires": {
-        "agent-base": "^4.1.0",
-        "debug": "^3.1.0"
+        "agent-base": "4.2.1",
+        "debug": "3.2.6"
       },
       "dependencies": {
         "debug": {
@@ -4994,7 +4987,7 @@
           "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
           "dev": true,
           "requires": {
-            "ms": "^2.1.1"
+            "ms": "2.1.1"
           }
         },
         "ms": {
@@ -5048,7 +5041,7 @@
       "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=",
       "dev": true,
       "requires": {
-        "import-from": "^2.1.0"
+        "import-from": "2.1.0"
       }
     },
     "import-from": {
@@ -5057,7 +5050,7 @@
       "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=",
       "dev": true,
       "requires": {
-        "resolve-from": "^3.0.0"
+        "resolve-from": "3.0.0"
       }
     },
     "import-local": {
@@ -5066,8 +5059,8 @@
       "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==",
       "dev": true,
       "requires": {
-        "pkg-dir": "^3.0.0",
-        "resolve-cwd": "^2.0.0"
+        "pkg-dir": "3.0.0",
+        "resolve-cwd": "2.0.0"
       },
       "dependencies": {
         "find-up": {
@@ -5076,7 +5069,7 @@
           "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
           "dev": true,
           "requires": {
-            "locate-path": "^3.0.0"
+            "locate-path": "3.0.0"
           }
         },
         "locate-path": {
@@ -5085,8 +5078,8 @@
           "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
           "dev": true,
           "requires": {
-            "p-locate": "^3.0.0",
-            "path-exists": "^3.0.0"
+            "p-locate": "3.0.0",
+            "path-exists": "3.0.0"
           }
         },
         "p-limit": {
@@ -5095,7 +5088,7 @@
           "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==",
           "dev": true,
           "requires": {
-            "p-try": "^2.0.0"
+            "p-try": "2.0.0"
           }
         },
         "p-locate": {
@@ -5104,7 +5097,7 @@
           "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
           "dev": true,
           "requires": {
-            "p-limit": "^2.0.0"
+            "p-limit": "2.1.0"
           }
         },
         "p-try": {
@@ -5119,7 +5112,7 @@
           "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
           "dev": true,
           "requires": {
-            "find-up": "^3.0.0"
+            "find-up": "3.0.0"
           }
         }
       }
@@ -5144,7 +5137,7 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "repeating": "^2.0.0"
+        "repeating": "2.0.1"
       }
     },
     "indexof": {
@@ -5159,8 +5152,8 @@
       "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
       "dev": true,
       "requires": {
-        "once": "^1.3.0",
-        "wrappy": "1"
+        "once": "1.4.0",
+        "wrappy": "1.0.2"
       }
     },
     "inherits": {
@@ -5181,19 +5174,19 @@
       "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==",
       "dev": true,
       "requires": {
-        "ansi-escapes": "^3.0.0",
-        "chalk": "^2.0.0",
-        "cli-cursor": "^2.1.0",
-        "cli-width": "^2.0.0",
-        "external-editor": "^3.0.0",
-        "figures": "^2.0.0",
-        "lodash": "^4.17.10",
+        "ansi-escapes": "3.2.0",
+        "chalk": "2.4.1",
+        "cli-cursor": "2.1.0",
+        "cli-width": "2.2.0",
+        "external-editor": "3.0.3",
+        "figures": "2.0.0",
+        "lodash": "4.17.10",
         "mute-stream": "0.0.7",
-        "run-async": "^2.2.0",
-        "rxjs": "^6.1.0",
-        "string-width": "^2.1.0",
-        "strip-ansi": "^4.0.0",
-        "through": "^2.3.6"
+        "run-async": "2.3.0",
+        "rxjs": "6.4.0",
+        "string-width": "2.1.1",
+        "strip-ansi": "4.0.0",
+        "through": "2.3.8"
       },
       "dependencies": {
         "ansi-regex": {
@@ -5214,8 +5207,8 @@
           "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
           "dev": true,
           "requires": {
-            "is-fullwidth-code-point": "^2.0.0",
-            "strip-ansi": "^4.0.0"
+            "is-fullwidth-code-point": "2.0.0",
+            "strip-ansi": "4.0.0"
           }
         },
         "strip-ansi": {
@@ -5224,7 +5217,7 @@
           "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
           "dev": true,
           "requires": {
-            "ansi-regex": "^3.0.0"
+            "ansi-regex": "3.0.0"
           }
         }
       }
@@ -5235,8 +5228,8 @@
       "integrity": "sha512-NXXgESC2nNVtU+pqmC9e6R8B1GpKxzsAQhffvh5AL79qKnodd+L7tnEQmTiUAVngqLalPbSqRA7XGIEL5nCd0Q==",
       "dev": true,
       "requires": {
-        "default-gateway": "^2.6.0",
-        "ipaddr.js": "^1.5.2"
+        "default-gateway": "2.7.2",
+        "ipaddr.js": "1.8.0"
       }
     },
     "interpret": {
@@ -5251,7 +5244,7 @@
       "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
       "dev": true,
       "requires": {
-        "loose-envify": "^1.0.0"
+        "loose-envify": "1.4.0"
       }
     },
     "invert-kv": {
@@ -5284,7 +5277,7 @@
       "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
       "dev": true,
       "requires": {
-        "kind-of": "^3.0.2"
+        "kind-of": "3.2.2"
       },
       "dependencies": {
         "kind-of": {
@@ -5293,7 +5286,7 @@
           "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
           "dev": true,
           "requires": {
-            "is-buffer": "^1.1.5"
+            "is-buffer": "1.1.6"
           }
         }
       }
@@ -5310,7 +5303,7 @@
       "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
       "dev": true,
       "requires": {
-        "binary-extensions": "^1.0.0"
+        "binary-extensions": "1.11.0"
       }
     },
     "is-buffer": {
@@ -5325,7 +5318,7 @@
       "integrity": "sha512-/93sDihsAD652hrMEbJGbMAVBf1qc96kyThHQ0CAOONHaE3aROLpTjDe4WQ5aoC5ITHFxEq1z8XqSU7km+8amw==",
       "dev": true,
       "requires": {
-        "builtin-modules": "^3.0.0"
+        "builtin-modules": "3.0.0"
       },
       "dependencies": {
         "builtin-modules": {
@@ -5342,7 +5335,7 @@
       "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
       "dev": true,
       "requires": {
-        "kind-of": "^3.0.2"
+        "kind-of": "3.2.2"
       },
       "dependencies": {
         "kind-of": {
@@ -5351,7 +5344,7 @@
           "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
           "dev": true,
           "requires": {
-            "is-buffer": "^1.1.5"
+            "is-buffer": "1.1.6"
           }
         }
       }
@@ -5362,9 +5355,9 @@
       "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
       "dev": true,
       "requires": {
-        "is-accessor-descriptor": "^0.1.6",
-        "is-data-descriptor": "^0.1.4",
-        "kind-of": "^5.0.0"
+        "is-accessor-descriptor": "0.1.6",
+        "is-data-descriptor": "0.1.4",
+        "kind-of": "5.1.0"
       },
       "dependencies": {
         "kind-of": {
@@ -5393,7 +5386,7 @@
       "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=",
       "dev": true,
       "requires": {
-        "is-primitive": "^2.0.0"
+        "is-primitive": "2.0.0"
       }
     },
     "is-extendable": {
@@ -5414,7 +5407,7 @@
       "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
       "dev": true,
       "requires": {
-        "number-is-nan": "^1.0.0"
+        "number-is-nan": "1.0.1"
       }
     },
     "is-fullwidth-code-point": {
@@ -5423,7 +5416,7 @@
       "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
       "dev": true,
       "requires": {
-        "number-is-nan": "^1.0.0"
+        "number-is-nan": "1.0.1"
       }
     },
     "is-glob": {
@@ -5432,7 +5425,7 @@
       "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=",
       "dev": true,
       "requires": {
-        "is-extglob": "^2.1.1"
+        "is-extglob": "2.1.1"
       }
     },
     "is-number": {
@@ -5441,7 +5434,7 @@
       "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
       "dev": true,
       "requires": {
-        "kind-of": "^3.0.2"
+        "kind-of": "3.2.2"
       },
       "dependencies": {
         "kind-of": {
@@ -5450,7 +5443,7 @@
           "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
           "dev": true,
           "requires": {
-            "is-buffer": "^1.1.5"
+            "is-buffer": "1.1.6"
           }
         }
       }
@@ -5467,7 +5460,7 @@
       "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==",
       "dev": true,
       "requires": {
-        "is-path-inside": "^1.0.0"
+        "is-path-inside": "1.0.1"
       }
     },
     "is-path-inside": {
@@ -5476,7 +5469,7 @@
       "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
       "dev": true,
       "requires": {
-        "path-is-inside": "^1.0.1"
+        "path-is-inside": "1.0.2"
       }
     },
     "is-plain-object": {
@@ -5485,7 +5478,7 @@
       "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
       "dev": true,
       "requires": {
-        "isobject": "^3.0.1"
+        "isobject": "3.0.1"
       }
     },
     "is-posix-bracket": {
@@ -5522,8 +5515,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",
@@ -5549,7 +5541,7 @@
       "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==",
       "dev": true,
       "requires": {
-        "buffer-alloc": "^1.2.0"
+        "buffer-alloc": "1.2.0"
       }
     },
     "isexe": {
@@ -5576,20 +5568,20 @@
       "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=",
       "dev": true,
       "requires": {
-        "abbrev": "1.0.x",
-        "async": "1.x",
-        "escodegen": "1.8.x",
-        "esprima": "2.7.x",
-        "glob": "^5.0.15",
-        "handlebars": "^4.0.1",
-        "js-yaml": "3.x",
-        "mkdirp": "0.5.x",
-        "nopt": "3.x",
-        "once": "1.x",
-        "resolve": "1.1.x",
-        "supports-color": "^3.1.0",
-        "which": "^1.1.1",
-        "wordwrap": "^1.0.0"
+        "abbrev": "1.0.9",
+        "async": "1.5.2",
+        "escodegen": "1.8.1",
+        "esprima": "2.7.3",
+        "glob": "5.0.15",
+        "handlebars": "4.0.12",
+        "js-yaml": "3.12.0",
+        "mkdirp": "0.5.1",
+        "nopt": "3.0.6",
+        "once": "1.4.0",
+        "resolve": "1.1.7",
+        "supports-color": "3.2.3",
+        "which": "1.3.1",
+        "wordwrap": "1.0.0"
       },
       "dependencies": {
         "glob": {
@@ -5598,11 +5590,11 @@
           "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
           "dev": true,
           "requires": {
-            "inflight": "^1.0.4",
-            "inherits": "2",
-            "minimatch": "2 || 3",
-            "once": "^1.3.0",
-            "path-is-absolute": "^1.0.0"
+            "inflight": "1.0.6",
+            "inherits": "2.0.3",
+            "minimatch": "3.0.4",
+            "once": "1.4.0",
+            "path-is-absolute": "1.0.1"
           }
         },
         "has-flag": {
@@ -5617,7 +5609,7 @@
           "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
           "dev": true,
           "requires": {
-            "has-flag": "^1.0.0"
+            "has-flag": "1.0.0"
           }
         }
       }
@@ -5628,18 +5620,18 @@
       "integrity": "sha512-8W5oeAGWXhtTJjAyVfvavOLVyZCTNCKsyF6GON/INKlBdO7uJ/bv3qnPj5M6ERKzmMCJS1kntnjjGuJ86fn3rQ==",
       "dev": true,
       "requires": {
-        "async": "^2.6.1",
-        "compare-versions": "^3.2.1",
-        "fileset": "^2.0.3",
-        "istanbul-lib-coverage": "^2.0.1",
-        "istanbul-lib-hook": "^2.0.1",
-        "istanbul-lib-instrument": "^3.0.0",
-        "istanbul-lib-report": "^2.0.2",
-        "istanbul-lib-source-maps": "^2.0.1",
-        "istanbul-reports": "^2.0.1",
-        "js-yaml": "^3.12.0",
-        "make-dir": "^1.3.0",
-        "once": "^1.4.0"
+        "async": "2.6.1",
+        "compare-versions": "3.4.0",
+        "fileset": "2.0.3",
+        "istanbul-lib-coverage": "2.0.1",
+        "istanbul-lib-hook": "2.0.1",
+        "istanbul-lib-instrument": "3.0.0",
+        "istanbul-lib-report": "2.0.2",
+        "istanbul-lib-source-maps": "2.0.1",
+        "istanbul-reports": "2.0.1",
+        "js-yaml": "3.12.0",
+        "make-dir": "1.3.0",
+        "once": "1.4.0"
       },
       "dependencies": {
         "async": {
@@ -5648,7 +5640,7 @@
           "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
           "dev": true,
           "requires": {
-            "lodash": "^4.17.10"
+            "lodash": "4.17.10"
           }
         },
         "istanbul-lib-coverage": {
@@ -5663,13 +5655,13 @@
           "integrity": "sha512-eQY9vN9elYjdgN9Iv6NS/00bptm02EBBk70lRMaVjeA6QYocQgenVrSgC28TJurdnZa80AGO3ASdFN+w/njGiQ==",
           "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.1",
-            "semver": "^5.5.0"
+            "@babel/generator": "7.3.0",
+            "@babel/parser": "7.3.1",
+            "@babel/template": "7.2.2",
+            "@babel/traverse": "7.2.3",
+            "@babel/types": "7.3.0",
+            "istanbul-lib-coverage": "2.0.1",
+            "semver": "5.5.1"
           }
         }
       }
@@ -5680,10 +5672,10 @@
       "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"
+        "convert-source-map": "1.6.0",
+        "istanbul-lib-instrument": "1.10.2",
+        "loader-utils": "1.1.0",
+        "schema-utils": "0.3.0"
       },
       "dependencies": {
         "ajv": {
@@ -5692,10 +5684,10 @@
           "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"
+            "co": "4.6.0",
+            "fast-deep-equal": "1.1.0",
+            "fast-json-stable-stringify": "2.0.0",
+            "json-schema-traverse": "0.3.1"
           }
         },
         "schema-utils": {
@@ -5704,7 +5696,7 @@
           "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=",
           "dev": true,
           "requires": {
-            "ajv": "^5.0.0"
+            "ajv": "5.5.2"
           }
         }
       }
@@ -5721,7 +5713,7 @@
       "integrity": "sha512-ufiZoiJ8CxY577JJWEeFuxXZoMqiKpq/RqZtOAYuQLvlkbJWscq9n3gc4xrCGH9n4pW0qnTxOz1oyMmVtk8E1w==",
       "dev": true,
       "requires": {
-        "append-transform": "^1.0.0"
+        "append-transform": "1.0.0"
       }
     },
     "istanbul-lib-instrument": {
@@ -5730,13 +5722,13 @@
       "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"
+        "babel-generator": "6.26.1",
+        "babel-template": "6.26.0",
+        "babel-traverse": "6.26.0",
+        "babel-types": "6.26.0",
+        "babylon": "6.18.0",
+        "istanbul-lib-coverage": "1.2.1",
+        "semver": "5.5.1"
       }
     },
     "istanbul-lib-report": {
@@ -5745,9 +5737,9 @@
       "integrity": "sha512-rJ8uR3peeIrwAxoDEbK4dJ7cqqtxBisZKCuwkMtMv0xYzaAnsAi3AHrHPAAtNXzG/bcCgZZ3OJVqm1DTi9ap2Q==",
       "dev": true,
       "requires": {
-        "istanbul-lib-coverage": "^2.0.1",
-        "make-dir": "^1.3.0",
-        "supports-color": "^5.4.0"
+        "istanbul-lib-coverage": "2.0.1",
+        "make-dir": "1.3.0",
+        "supports-color": "5.5.0"
       },
       "dependencies": {
         "istanbul-lib-coverage": {
@@ -5764,11 +5756,11 @@
       "integrity": "sha512-30l40ySg+gvBLcxTrLzR4Z2XTRj3HgRCA/p2rnbs/3OiTaoj054gAbuP5DcLOtwqmy4XW8qXBHzrmP2/bQ9i3A==",
       "dev": true,
       "requires": {
-        "debug": "^3.1.0",
-        "istanbul-lib-coverage": "^2.0.1",
-        "make-dir": "^1.3.0",
-        "rimraf": "^2.6.2",
-        "source-map": "^0.6.1"
+        "debug": "3.2.6",
+        "istanbul-lib-coverage": "2.0.1",
+        "make-dir": "1.3.0",
+        "rimraf": "2.6.2",
+        "source-map": "0.6.1"
       },
       "dependencies": {
         "debug": {
@@ -5777,7 +5769,7 @@
           "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
           "dev": true,
           "requires": {
-            "ms": "^2.1.1"
+            "ms": "2.1.1"
           }
         },
         "istanbul-lib-coverage": {
@@ -5806,7 +5798,7 @@
       "integrity": "sha512-CT0QgMBJqs6NJLF678ZHcquUAZIoBIUNzdJrRJfpkI9OnzG6MkUfHxbJC3ln981dMswC7/B1mfX3LNkhgJxsuw==",
       "dev": true,
       "requires": {
-        "handlebars": "^4.0.11"
+        "handlebars": "4.0.12"
       }
     },
     "jasmine": {
@@ -5815,9 +5807,9 @@
       "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=",
       "dev": true,
       "requires": {
-        "exit": "^0.1.2",
-        "glob": "^7.0.6",
-        "jasmine-core": "~2.8.0"
+        "exit": "0.1.2",
+        "glob": "7.1.3",
+        "jasmine-core": "2.8.0"
       },
       "dependencies": {
         "jasmine-core": {
@@ -5868,8 +5860,8 @@
       "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
       "dev": true,
       "requires": {
-        "argparse": "^1.0.7",
-        "esprima": "^4.0.0"
+        "argparse": "1.0.10",
+        "esprima": "4.0.1"
       },
       "dependencies": {
         "esprima": {
@@ -5935,7 +5927,7 @@
       "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
       "dev": true,
       "requires": {
-        "graceful-fs": "^4.1.6"
+        "graceful-fs": "4.1.11"
       }
     },
     "jsprim": {
@@ -5956,11 +5948,11 @@
       "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"
+        "core-js": "2.3.0",
+        "es6-promise": "3.0.2",
+        "lie": "3.1.1",
+        "pako": "1.0.8",
+        "readable-stream": "2.0.6"
       },
       "dependencies": {
         "core-js": {
@@ -5987,12 +5979,12 @@
           "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"
+            "core-util-is": "1.0.2",
+            "inherits": "2.0.3",
+            "isarray": "1.0.0",
+            "process-nextick-args": "1.0.7",
+            "string_decoder": "0.10.31",
+            "util-deprecate": "1.0.2"
           }
         },
         "string_decoder": {
@@ -6009,33 +6001,33 @@
       "integrity": "sha512-k5pBjHDhmkdaUccnC7gE3mBzZjcxyxYsYVaqiL2G5AqlfLyBO5nw2VdNK+O16cveEPd/gIOWULH7gkiYYwVNHg==",
       "dev": true,
       "requires": {
-        "bluebird": "^3.3.0",
-        "body-parser": "^1.16.1",
-        "chokidar": "^1.4.1",
-        "colors": "^1.1.0",
-        "combine-lists": "^1.0.0",
-        "connect": "^3.6.0",
-        "core-js": "^2.2.0",
-        "di": "^0.0.1",
-        "dom-serialize": "^2.2.0",
-        "expand-braces": "^0.1.1",
-        "glob": "^7.1.1",
-        "graceful-fs": "^4.1.2",
-        "http-proxy": "^1.13.0",
-        "isbinaryfile": "^3.0.0",
-        "lodash": "^3.8.0",
-        "log4js": "^0.6.31",
-        "mime": "^1.3.4",
-        "minimatch": "^3.0.2",
-        "optimist": "^0.6.1",
-        "qjobs": "^1.1.4",
-        "range-parser": "^1.2.0",
-        "rimraf": "^2.6.0",
-        "safe-buffer": "^5.0.1",
+        "bluebird": "3.5.2",
+        "body-parser": "1.18.2",
+        "chokidar": "1.7.0",
+        "colors": "1.1.2",
+        "combine-lists": "1.0.1",
+        "connect": "3.6.6",
+        "core-js": "2.6.5",
+        "di": "0.0.1",
+        "dom-serialize": "2.2.1",
+        "expand-braces": "0.1.2",
+        "glob": "7.1.3",
+        "graceful-fs": "4.1.11",
+        "http-proxy": "1.17.0",
+        "isbinaryfile": "3.0.3",
+        "lodash": "3.10.1",
+        "log4js": "0.6.38",
+        "mime": "1.6.0",
+        "minimatch": "3.0.4",
+        "optimist": "0.6.1",
+        "qjobs": "1.2.0",
+        "range-parser": "1.2.0",
+        "rimraf": "2.6.2",
+        "safe-buffer": "5.1.2",
         "socket.io": "1.7.3",
-        "source-map": "^0.5.3",
+        "source-map": "0.5.7",
         "tmp": "0.0.31",
-        "useragent": "^2.1.12"
+        "useragent": "2.3.0"
       },
       "dependencies": {
         "anymatch": {
@@ -6044,8 +6036,8 @@
           "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==",
           "dev": true,
           "requires": {
-            "micromatch": "^2.1.5",
-            "normalize-path": "^2.0.0"
+            "micromatch": "2.3.11",
+            "normalize-path": "2.1.1"
           }
         },
         "arr-diff": {
@@ -6054,7 +6046,7 @@
           "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
           "dev": true,
           "requires": {
-            "arr-flatten": "^1.0.1"
+            "arr-flatten": "1.1.0"
           }
         },
         "array-unique": {
@@ -6069,9 +6061,9 @@
           "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
           "dev": true,
           "requires": {
-            "expand-range": "^1.8.1",
-            "preserve": "^0.2.0",
-            "repeat-element": "^1.1.2"
+            "expand-range": "1.8.2",
+            "preserve": "0.2.0",
+            "repeat-element": "1.1.3"
           }
         },
         "chokidar": {
@@ -6080,15 +6072,15 @@
           "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=",
           "dev": true,
           "requires": {
-            "anymatch": "^1.3.0",
-            "async-each": "^1.0.0",
-            "fsevents": "^1.0.0",
-            "glob-parent": "^2.0.0",
-            "inherits": "^2.0.1",
-            "is-binary-path": "^1.0.0",
-            "is-glob": "^2.0.0",
-            "path-is-absolute": "^1.0.0",
-            "readdirp": "^2.0.0"
+            "anymatch": "1.3.2",
+            "async-each": "1.0.1",
+            "fsevents": "1.2.4",
+            "glob-parent": "2.0.0",
+            "inherits": "2.0.3",
+            "is-binary-path": "1.0.1",
+            "is-glob": "2.0.1",
+            "path-is-absolute": "1.0.1",
+            "readdirp": "2.1.0"
           }
         },
         "expand-brackets": {
@@ -6097,7 +6089,7 @@
           "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
           "dev": true,
           "requires": {
-            "is-posix-bracket": "^0.1.0"
+            "is-posix-bracket": "0.1.1"
           }
         },
         "extglob": {
@@ -6106,7 +6098,7 @@
           "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
           "dev": true,
           "requires": {
-            "is-extglob": "^1.0.0"
+            "is-extglob": "1.0.0"
           }
         },
         "glob-parent": {
@@ -6115,7 +6107,7 @@
           "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
           "dev": true,
           "requires": {
-            "is-glob": "^2.0.0"
+            "is-glob": "2.0.1"
           }
         },
         "is-extglob": {
@@ -6130,7 +6122,7 @@
           "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
           "dev": true,
           "requires": {
-            "is-extglob": "^1.0.0"
+            "is-extglob": "1.0.0"
           }
         },
         "kind-of": {
@@ -6139,7 +6131,7 @@
           "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
           "dev": true,
           "requires": {
-            "is-buffer": "^1.1.5"
+            "is-buffer": "1.1.6"
           }
         },
         "lodash": {
@@ -6154,19 +6146,19 @@
           "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
           "dev": true,
           "requires": {
-            "arr-diff": "^2.0.0",
-            "array-unique": "^0.2.1",
-            "braces": "^1.8.2",
-            "expand-brackets": "^0.1.4",
-            "extglob": "^0.3.1",
-            "filename-regex": "^2.0.0",
-            "is-extglob": "^1.0.0",
-            "is-glob": "^2.0.1",
-            "kind-of": "^3.0.2",
-            "normalize-path": "^2.0.1",
-            "object.omit": "^2.0.0",
-            "parse-glob": "^3.0.4",
-            "regex-cache": "^0.4.2"
+            "arr-diff": "2.0.0",
+            "array-unique": "0.2.1",
+            "braces": "1.8.5",
+            "expand-brackets": "0.1.5",
+            "extglob": "0.3.2",
+            "filename-regex": "2.0.1",
+            "is-extglob": "1.0.0",
+            "is-glob": "2.0.1",
+            "kind-of": "3.2.2",
+            "normalize-path": "2.1.1",
+            "object.omit": "2.0.1",
+            "parse-glob": "3.0.4",
+            "regex-cache": "0.4.4"
           }
         }
       }
@@ -6177,8 +6169,8 @@
       "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==",
       "dev": true,
       "requires": {
-        "fs-access": "^1.0.0",
-        "which": "^1.2.1"
+        "fs-access": "1.0.1",
+        "which": "1.3.1"
       }
     },
     "karma-coverage-istanbul-reporter": {
@@ -6187,8 +6179,8 @@
       "integrity": "sha512-xJS7QSQIVU6VK9HuJ/ieE5yynxKhjCCkd96NLY/BX/HXsx0CskU9JJiMQbd4cHALiddMwI4OWh1IIzeWrsavJw==",
       "dev": true,
       "requires": {
-        "istanbul-api": "^2.0.5",
-        "minimatch": "^3.0.4"
+        "istanbul-api": "2.0.6",
+        "minimatch": "3.0.4"
       }
     },
     "karma-jasmine": {
@@ -6203,7 +6195,7 @@
       "integrity": "sha1-SKjl7xiAdhfuK14zwRlMNbQ5Ukw=",
       "dev": true,
       "requires": {
-        "karma-jasmine": "^1.0.2"
+        "karma-jasmine": "1.1.2"
       }
     },
     "karma-phantomjs-launcher": {
@@ -6212,8 +6204,8 @@
       "integrity": "sha1-0jyjSAG9qYY60xjju0vUBisTrNI=",
       "dev": true,
       "requires": {
-        "lodash": "^4.0.1",
-        "phantomjs-prebuilt": "^2.1.7"
+        "lodash": "4.17.10",
+        "phantomjs-prebuilt": "2.1.16"
       }
     },
     "karma-source-map-support": {
@@ -6222,7 +6214,7 @@
       "integrity": "sha512-HcPqdAusNez/ywa+biN4EphGz62MmQyPggUsDfsHqa7tSe4jdsxgvTKuDfIazjL+IOxpVWyT7Pr4dhAV+sxX5Q==",
       "dev": true,
       "requires": {
-        "source-map-support": "^0.5.5"
+        "source-map-support": "0.5.9"
       }
     },
     "kew": {
@@ -6249,7 +6241,7 @@
       "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=",
       "dev": true,
       "requires": {
-        "graceful-fs": "^4.1.9"
+        "graceful-fs": "4.1.11"
       }
     },
     "lcid": {
@@ -6258,7 +6250,7 @@
       "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
       "dev": true,
       "requires": {
-        "invert-kv": "^1.0.0"
+        "invert-kv": "1.0.0"
       }
     },
     "less": {
@@ -6267,15 +6259,15 @@
       "integrity": "sha512-8HFGuWmL3FhQR0aH89escFNBQH/nEiYPP2ltDFdQw2chE28Yx2E3lhAIq9Y2saYwLSwa699s4dBVEfCY8Drf7Q==",
       "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"
+        "clone": "2.1.2",
+        "errno": "0.1.7",
+        "graceful-fs": "4.1.11",
+        "image-size": "0.5.5",
+        "mime": "1.6.0",
+        "mkdirp": "0.5.1",
+        "promise": "7.3.1",
+        "request": "2.88.0",
+        "source-map": "0.6.1"
       },
       "dependencies": {
         "source-map": {
@@ -6293,9 +6285,9 @@
       "integrity": "sha512-KNTsgCE9tMOM70+ddxp9yyt9iHqgmSs0yTZc5XH5Wo+g80RWRIYNqE58QJKm/yMud5wZEvz50ugRDuzVIkyahg==",
       "dev": true,
       "requires": {
-        "clone": "^2.1.1",
-        "loader-utils": "^1.1.0",
-        "pify": "^3.0.0"
+        "clone": "2.1.2",
+        "loader-utils": "1.1.0",
+        "pify": "3.0.0"
       }
     },
     "levn": {
@@ -6304,8 +6296,8 @@
       "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
       "dev": true,
       "requires": {
-        "prelude-ls": "~1.1.2",
-        "type-check": "~0.3.2"
+        "prelude-ls": "1.1.2",
+        "type-check": "0.3.2"
       }
     },
     "license-webpack-plugin": {
@@ -6314,7 +6306,7 @@
       "integrity": "sha512-GsomZw5VoT20ST8qH2tOjBgbyhn6Pgs9M94g0mbvfBIV1VXufm1iKY+4dbgfTObj1Mp6nSRE3Zf74deOZr0KwA==",
       "dev": true,
       "requires": {
-        "webpack-sources": "^1.2.0"
+        "webpack-sources": "1.2.0"
       }
     },
     "lie": {
@@ -6323,7 +6315,7 @@
       "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=",
       "dev": true,
       "requires": {
-        "immediate": "~3.0.5"
+        "immediate": "3.0.6"
       }
     },
     "load-json-file": {
@@ -6331,21 +6323,19 @@
       "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",
-        "pify": "^2.0.0",
-        "pinkie-promise": "^2.0.0",
-        "strip-bom": "^2.0.0"
+        "graceful-fs": "4.1.11",
+        "parse-json": "2.2.0",
+        "pify": "2.3.0",
+        "pinkie-promise": "2.0.1",
+        "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,
-          "optional": true
+          "dev": true
         }
       }
     },
@@ -6361,9 +6351,9 @@
       "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=",
       "dev": true,
       "requires": {
-        "big.js": "^3.1.3",
-        "emojis-list": "^2.0.0",
-        "json5": "^0.5.0"
+        "big.js": "3.2.0",
+        "emojis-list": "2.1.0",
+        "json5": "0.5.1"
       }
     },
     "locate-path": {
@@ -6372,8 +6362,8 @@
       "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
       "dev": true,
       "requires": {
-        "p-locate": "^2.0.0",
-        "path-exists": "^3.0.0"
+        "p-locate": "2.0.0",
+        "path-exists": "3.0.0"
       }
     },
     "lodash": {
@@ -6420,8 +6410,8 @@
       "integrity": "sha1-LElBFmldb7JUgJQ9P8hy5mKlIv0=",
       "dev": true,
       "requires": {
-        "readable-stream": "~1.0.2",
-        "semver": "~4.3.3"
+        "readable-stream": "1.0.34",
+        "semver": "4.3.6"
       },
       "dependencies": {
         "isarray": {
@@ -6436,10 +6426,10 @@
           "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
           "dev": true,
           "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.1",
+            "core-util-is": "1.0.2",
+            "inherits": "2.0.3",
             "isarray": "0.0.1",
-            "string_decoder": "~0.10.x"
+            "string_decoder": "0.10.31"
           }
         },
         "semver": {
@@ -6468,7 +6458,7 @@
       "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
       "dev": true,
       "requires": {
-        "js-tokens": "^3.0.0 || ^4.0.0"
+        "js-tokens": "3.0.2"
       }
     },
     "loud-rejection": {
@@ -6477,8 +6467,8 @@
       "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
       "dev": true,
       "requires": {
-        "currently-unhandled": "^0.4.1",
-        "signal-exit": "^3.0.0"
+        "currently-unhandled": "0.4.1",
+        "signal-exit": "3.0.2"
       }
     },
     "lru-cache": {
@@ -6487,8 +6477,8 @@
       "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==",
       "dev": true,
       "requires": {
-        "pseudomap": "^1.0.2",
-        "yallist": "^2.1.2"
+        "pseudomap": "1.0.2",
+        "yallist": "2.1.2"
       }
     },
     "magic-string": {
@@ -6497,7 +6487,7 @@
       "integrity": "sha512-sCuTz6pYom8Rlt4ISPFn6wuFodbKMIHUMv4Qko9P17dpxb7s52KJTmRuZZqHdGmLCK9AOcDare039nRIcfdkEg==",
       "dev": true,
       "requires": {
-        "sourcemap-codec": "^1.4.1"
+        "sourcemap-codec": "1.4.4"
       }
     },
     "make-dir": {
@@ -6506,7 +6496,7 @@
       "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
       "dev": true,
       "requires": {
-        "pify": "^3.0.0"
+        "pify": "3.0.0"
       }
     },
     "make-error": {
@@ -6527,7 +6517,7 @@
       "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
       "dev": true,
       "requires": {
-        "p-defer": "^1.0.0"
+        "p-defer": "1.0.0"
       }
     },
     "map-cache": {
@@ -6540,8 +6530,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",
@@ -6549,7 +6538,7 @@
       "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
       "dev": true,
       "requires": {
-        "object-visit": "^1.0.0"
+        "object-visit": "1.0.1"
       }
     },
     "math-random": {
@@ -6564,9 +6553,9 @@
       "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
       "dev": true,
       "requires": {
-        "hash-base": "^3.0.0",
-        "inherits": "^2.0.1",
-        "safe-buffer": "^5.1.2"
+        "hash-base": "3.0.4",
+        "inherits": "2.0.3",
+        "safe-buffer": "5.1.2"
       }
     },
     "media-typer": {
@@ -6581,9 +6570,9 @@
       "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==",
       "dev": true,
       "requires": {
-        "map-age-cleaner": "^0.1.1",
-        "mimic-fn": "^1.0.0",
-        "p-is-promise": "^2.0.0"
+        "map-age-cleaner": "0.1.3",
+        "mimic-fn": "1.2.0",
+        "p-is-promise": "2.0.0"
       }
     },
     "memory-fs": {
@@ -6592,8 +6581,8 @@
       "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
       "dev": true,
       "requires": {
-        "errno": "^0.1.3",
-        "readable-stream": "^2.0.1"
+        "errno": "0.1.7",
+        "readable-stream": "2.3.6"
       }
     },
     "meow": {
@@ -6603,16 +6592,16 @@
       "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"
+        "camelcase-keys": "2.1.0",
+        "decamelize": "1.2.0",
+        "loud-rejection": "1.6.0",
+        "map-obj": "1.0.1",
+        "minimist": "1.2.0",
+        "normalize-package-data": "2.4.1",
+        "object-assign": "4.1.1",
+        "read-pkg-up": "1.0.1",
+        "redent": "1.0.0",
+        "trim-newlines": "1.0.0"
       },
       "dependencies": {
         "minimist": {
@@ -6642,19 +6631,19 @@
       "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"
+        "arr-diff": "4.0.0",
+        "array-unique": "0.3.2",
+        "braces": "2.3.2",
+        "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.13",
+        "object.pick": "1.3.0",
+        "regex-not": "1.0.2",
+        "snapdragon": "0.8.2",
+        "to-regex": "3.0.2"
       }
     },
     "miller-rabin": {
@@ -6663,8 +6652,8 @@
       "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
       "dev": true,
       "requires": {
-        "bn.js": "^4.0.0",
-        "brorand": "^1.0.1"
+        "bn.js": "4.11.8",
+        "brorand": "1.1.0"
       }
     },
     "mime": {
@@ -6685,7 +6674,7 @@
       "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==",
       "dev": true,
       "requires": {
-        "mime-db": "~1.36.0"
+        "mime-db": "1.36.0"
       }
     },
     "mimic-fn": {
@@ -6700,9 +6689,9 @@
       "integrity": "sha512-Mxs0nxzF1kxPv4TRi2NimewgXlJqh0rGE30vviCU2WHrpbta6wklnUV9dr9FUtoAHmB3p3LeXEC+ZjgHvB0Dzg==",
       "dev": true,
       "requires": {
-        "loader-utils": "^1.1.0",
-        "schema-utils": "^1.0.0",
-        "webpack-sources": "^1.1.0"
+        "loader-utils": "1.1.0",
+        "schema-utils": "1.0.0",
+        "webpack-sources": "1.2.0"
       }
     },
     "minimalistic-assert": {
@@ -6723,7 +6712,7 @@
       "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
       "dev": true,
       "requires": {
-        "brace-expansion": "^1.1.7"
+        "brace-expansion": "1.1.11"
       }
     },
     "minimist": {
@@ -6738,16 +6727,16 @@
       "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"
+        "concat-stream": "1.6.2",
+        "duplexify": "3.6.1",
+        "end-of-stream": "1.4.1",
+        "flush-write-stream": "1.0.3",
+        "from2": "2.3.0",
+        "parallel-transform": "1.1.0",
+        "pump": "2.0.1",
+        "pumpify": "1.5.1",
+        "stream-each": "1.2.3",
+        "through2": "2.0.5"
       }
     },
     "mixin-deep": {
@@ -6756,8 +6745,8 @@
       "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==",
       "dev": true,
       "requires": {
-        "for-in": "^1.0.2",
-        "is-extendable": "^1.0.1"
+        "for-in": "1.0.2",
+        "is-extendable": "1.0.1"
       },
       "dependencies": {
         "is-extendable": {
@@ -6766,7 +6755,7 @@
           "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
           "dev": true,
           "requires": {
-            "is-plain-object": "^2.0.4"
+            "is-plain-object": "2.0.4"
           }
         }
       }
@@ -6777,8 +6766,8 @@
       "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=",
       "dev": true,
       "requires": {
-        "for-in": "^0.1.3",
-        "is-extendable": "^0.1.1"
+        "for-in": "0.1.8",
+        "is-extendable": "0.1.1"
       },
       "dependencies": {
         "for-in": {
@@ -6804,12 +6793,12 @@
       "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"
+        "aproba": "1.2.0",
+        "copy-concurrently": "1.0.5",
+        "fs-write-stream-atomic": "1.0.10",
+        "mkdirp": "0.5.1",
+        "rimraf": "2.6.2",
+        "run-queue": "1.0.3"
       }
     },
     "ms": {
@@ -6824,8 +6813,8 @@
       "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==",
       "dev": true,
       "requires": {
-        "dns-packet": "^1.3.1",
-        "thunky": "^1.0.2"
+        "dns-packet": "1.3.1",
+        "thunky": "1.0.3"
       }
     },
     "multicast-dns-service-types": {
@@ -6853,17 +6842,17 @@
       "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"
+        "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.2",
+        "snapdragon": "0.8.2",
+        "to-regex": "3.0.2"
       }
     },
     "negotiator": {
@@ -6883,7 +6872,7 @@
       "resolved": "https://registry.npmjs.org/ngx-cookie/-/ngx-cookie-4.1.2.tgz",
       "integrity": "sha512-BU3q+116mSQZvf8WsnKDxyWFy10LtxSvZz1YIjD7pmaSFpiKdWmHTHn0qLgm3OoIL9TfInQ7Ij46rKJWPD+4Kw==",
       "requires": {
-        "tslib": "^1.9.0"
+        "tslib": "1.9.3"
       }
     },
     "ngx-i18nsupport": {
@@ -6892,12 +6881,12 @@
       "integrity": "sha512-d8OCQs/XYBEI9qvztQyEkd8gEPFEBmyRg8UcriGQV8Ew1ujvrIieHxmX8YpDpFZKQ4ePextQGUSvjpGd2NauEQ==",
       "dev": true,
       "requires": {
-        "chalk": "^2.4.1",
-        "commander": "^2.15.1",
-        "he": "^1.1.1",
-        "ngx-i18nsupport-lib": "^1.10.2",
-        "request": "^2.85.0",
-        "rxjs": "^6.0.0"
+        "chalk": "2.4.1",
+        "commander": "2.17.1",
+        "he": "1.2.0",
+        "ngx-i18nsupport-lib": "1.10.2",
+        "request": "2.88.0",
+        "rxjs": "6.4.0"
       }
     },
     "ngx-i18nsupport-lib": {
@@ -6906,9 +6895,9 @@
       "integrity": "sha512-Z81I2/HUtZ/7X7C3sioJj/Zr/M0iQs0aR5EhYsrWTzdEy7fZWFVYabzzZs+8h6lhQ/4yIl+3sVOCBkI9BiUUEQ==",
       "dev": true,
       "requires": {
-        "@types/xmldom": "^0.1.29",
-        "tokenizr": "^1.3.4",
-        "xmldom": "^0.1.27"
+        "@types/xmldom": "0.1.29",
+        "tokenizr": "1.5.2",
+        "xmldom": "0.1.27"
       }
     },
     "nice-try": {
@@ -6930,18 +6919,18 @@
       "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"
+        "fstream": "1.0.11",
+        "glob": "7.1.3",
+        "graceful-fs": "4.1.11",
+        "mkdirp": "0.5.1",
+        "nopt": "3.0.6",
+        "npmlog": "4.1.2",
+        "osenv": "0.1.5",
+        "request": "2.88.0",
+        "rimraf": "2.6.2",
+        "semver": "5.3.0",
+        "tar": "2.2.1",
+        "which": "1.3.1"
       },
       "dependencies": {
         "semver": {
@@ -6959,28 +6948,28 @@
       "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",
+        "assert": "1.4.1",
+        "browserify-zlib": "0.2.0",
+        "buffer": "4.9.1",
+        "console-browserify": "1.1.0",
+        "constants-browserify": "1.0.0",
+        "crypto-browserify": "3.12.0",
+        "domain-browser": "1.2.0",
+        "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",
+        "process": "0.11.10",
+        "punycode": "1.4.1",
+        "querystring-es3": "0.2.1",
+        "readable-stream": "2.3.6",
+        "stream-browserify": "2.0.2",
+        "stream-http": "2.8.3",
+        "string_decoder": "1.1.1",
+        "timers-browserify": "2.0.10",
         "tty-browserify": "0.0.0",
-        "url": "^0.11.0",
-        "util": "^0.11.0",
+        "url": "0.11.0",
+        "util": "0.11.1",
         "vm-browserify": "0.0.4"
       },
       "dependencies": {
@@ -6998,7 +6987,7 @@
       "integrity": "sha512-lODUVHEIZutZx+TDdOk47qLik8FJMXzJ+WnyUGci1MTvTOyzZrz5eVPIIpc5Hb3NfHZGeGHeuwrRYVI1PEITWg==",
       "dev": true,
       "requires": {
-        "semver": "^5.3.0"
+        "semver": "5.5.1"
       }
     },
     "node-sass": {
@@ -7008,25 +6997,25 @@
       "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",
+        "async-foreach": "0.1.3",
+        "chalk": "1.1.3",
+        "cross-spawn": "3.0.1",
+        "gaze": "1.1.3",
+        "get-stdin": "4.0.1",
+        "glob": "7.1.3",
+        "in-publish": "2.0.0",
+        "lodash.assign": "4.2.0",
+        "lodash.clonedeep": "4.5.0",
+        "lodash.mergewith": "4.6.1",
+        "meow": "3.7.0",
+        "mkdirp": "0.5.1",
+        "nan": "2.11.1",
+        "node-gyp": "3.8.0",
+        "npmlog": "4.1.2",
         "request": "2.87.0",
-        "sass-graph": "^2.2.4",
-        "stdout-stream": "^1.4.0",
-        "true-case-path": "^1.0.2"
+        "sass-graph": "2.2.4",
+        "stdout-stream": "1.4.1",
+        "true-case-path": "1.0.3"
       },
       "dependencies": {
         "ajv": {
@@ -7036,10 +7025,10 @@
           "dev": true,
           "optional": 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"
+            "co": "4.6.0",
+            "fast-deep-equal": "1.1.0",
+            "fast-json-stable-stringify": "2.0.0",
+            "json-schema-traverse": "0.3.1"
           }
         },
         "ansi-styles": {
@@ -7056,11 +7045,11 @@
           "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"
+            "ansi-styles": "2.2.1",
+            "escape-string-regexp": "1.0.5",
+            "has-ansi": "2.0.0",
+            "strip-ansi": "3.0.1",
+            "supports-color": "2.0.0"
           }
         },
         "har-validator": {
@@ -7070,8 +7059,8 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "ajv": "^5.1.0",
-            "har-schema": "^2.0.0"
+            "ajv": "5.5.2",
+            "har-schema": "2.0.0"
           }
         },
         "oauth-sign": {
@@ -7095,26 +7084,26 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "aws-sign2": "~0.7.0",
-            "aws4": "^1.6.0",
-            "caseless": "~0.12.0",
-            "combined-stream": "~1.0.5",
-            "extend": "~3.0.1",
-            "forever-agent": "~0.6.1",
-            "form-data": "~2.3.1",
-            "har-validator": "~5.0.3",
-            "http-signature": "~1.2.0",
-            "is-typedarray": "~1.0.0",
-            "isstream": "~0.1.2",
-            "json-stringify-safe": "~5.0.1",
-            "mime-types": "~2.1.17",
-            "oauth-sign": "~0.8.2",
-            "performance-now": "^2.1.0",
-            "qs": "~6.5.1",
-            "safe-buffer": "^5.1.1",
-            "tough-cookie": "~2.3.3",
-            "tunnel-agent": "^0.6.0",
-            "uuid": "^3.1.0"
+            "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.0.3",
+            "http-signature": "1.2.0",
+            "is-typedarray": "1.0.0",
+            "isstream": "0.1.2",
+            "json-stringify-safe": "5.0.1",
+            "mime-types": "2.1.20",
+            "oauth-sign": "0.8.2",
+            "performance-now": "2.1.0",
+            "qs": "6.5.2",
+            "safe-buffer": "5.1.2",
+            "tough-cookie": "2.3.4",
+            "tunnel-agent": "0.6.0",
+            "uuid": "3.3.2"
           }
         },
         "supports-color": {
@@ -7131,7 +7120,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "punycode": "^1.4.1"
+            "punycode": "1.4.1"
           }
         }
       }
@@ -7142,7 +7131,7 @@
       "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
       "dev": true,
       "requires": {
-        "abbrev": "1"
+        "abbrev": "1.0.9"
       }
     },
     "normalize-package-data": {
@@ -7151,10 +7140,10 @@
       "integrity": "sha512-ZVuHxWJv1bopjv/SD5uPhgwUhLqxdJ+SsdUQbGR9HWlXrvnd/C08Cn9Bq48PbvX3y5V97GIpAHpL5Bk9BwChGg==",
       "dev": true,
       "requires": {
-        "hosted-git-info": "^2.1.4",
-        "is-builtin-module": "^3.0.0",
-        "semver": "2 || 3 || 4 || 5",
-        "validate-npm-package-license": "^3.0.1"
+        "hosted-git-info": "2.7.1",
+        "is-builtin-module": "3.0.0",
+        "semver": "5.5.1",
+        "validate-npm-package-license": "3.0.4"
       }
     },
     "normalize-path": {
@@ -7163,7 +7152,7 @@
       "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
       "dev": true,
       "requires": {
-        "remove-trailing-separator": "^1.0.1"
+        "remove-trailing-separator": "1.1.0"
       }
     },
     "normalize-range": {
@@ -7178,10 +7167,10 @@
       "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"
+        "hosted-git-info": "2.7.1",
+        "osenv": "0.1.5",
+        "semver": "5.5.1",
+        "validate-npm-package-name": "3.0.0"
       }
     },
     "npm-registry-client": {
@@ -7190,18 +7179,18 @@
       "integrity": "sha512-Qs6P6nnopig+Y8gbzpeN/dkt+n7IyVd8f45NTMotGk6Qo7GfBmzwYx6jRLoOOgKiMnaQfYxsuyQlD8Mc3guBhg==",
       "dev": true,
       "requires": {
-        "concat-stream": "^1.5.2",
-        "graceful-fs": "^4.1.6",
-        "normalize-package-data": "~1.0.1 || ^2.0.0",
-        "npm-package-arg": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0",
-        "npmlog": "2 || ^3.1.0 || ^4.0.0",
-        "once": "^1.3.3",
-        "request": "^2.74.0",
-        "retry": "^0.10.0",
-        "safe-buffer": "^5.1.1",
-        "semver": "2 >=2.2.1 || 3.x || 4 || 5",
-        "slide": "^1.1.3",
-        "ssri": "^5.2.4"
+        "concat-stream": "1.6.2",
+        "graceful-fs": "4.1.11",
+        "normalize-package-data": "2.4.1",
+        "npm-package-arg": "6.1.0",
+        "npmlog": "4.1.2",
+        "once": "1.4.0",
+        "request": "2.88.0",
+        "retry": "0.10.1",
+        "safe-buffer": "5.1.2",
+        "semver": "5.5.1",
+        "slide": "1.1.6",
+        "ssri": "5.3.0"
       }
     },
     "npm-run-path": {
@@ -7210,7 +7199,7 @@
       "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
       "dev": true,
       "requires": {
-        "path-key": "^2.0.0"
+        "path-key": "2.0.1"
       }
     },
     "npmlog": {
@@ -7218,12 +7207,11 @@
       "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
       "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
       "dev": true,
-      "optional": true,
       "requires": {
-        "are-we-there-yet": "~1.1.2",
-        "console-control-strings": "~1.1.0",
-        "gauge": "~2.7.3",
-        "set-blocking": "~2.0.0"
+        "are-we-there-yet": "1.1.5",
+        "console-control-strings": "1.1.0",
+        "gauge": "2.7.4",
+        "set-blocking": "2.0.0"
       }
     },
     "null-check": {
@@ -7268,9 +7256,9 @@
       "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
       "dev": true,
       "requires": {
-        "copy-descriptor": "^0.1.0",
-        "define-property": "^0.2.5",
-        "kind-of": "^3.0.3"
+        "copy-descriptor": "0.1.1",
+        "define-property": "0.2.5",
+        "kind-of": "3.2.2"
       },
       "dependencies": {
         "define-property": {
@@ -7279,7 +7267,7 @@
           "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
           "dev": true,
           "requires": {
-            "is-descriptor": "^0.1.0"
+            "is-descriptor": "0.1.6"
           }
         },
         "kind-of": {
@@ -7288,7 +7276,7 @@
           "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
           "dev": true,
           "requires": {
-            "is-buffer": "^1.1.5"
+            "is-buffer": "1.1.6"
           }
         }
       }
@@ -7299,7 +7287,7 @@
       "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
       "dev": true,
       "requires": {
-        "isobject": "^3.0.0"
+        "isobject": "3.0.1"
       }
     },
     "object.omit": {
@@ -7308,8 +7296,8 @@
       "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=",
       "dev": true,
       "requires": {
-        "for-own": "^0.1.4",
-        "is-extendable": "^0.1.1"
+        "for-own": "0.1.5",
+        "is-extendable": "0.1.1"
       },
       "dependencies": {
         "for-own": {
@@ -7318,7 +7306,7 @@
           "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=",
           "dev": true,
           "requires": {
-            "for-in": "^1.0.1"
+            "for-in": "1.0.2"
           }
         }
       }
@@ -7329,7 +7317,7 @@
       "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
       "dev": true,
       "requires": {
-        "isobject": "^3.0.1"
+        "isobject": "3.0.1"
       }
     },
     "obuf": {
@@ -7359,7 +7347,7 @@
       "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
       "dev": true,
       "requires": {
-        "wrappy": "1"
+        "wrappy": "1.0.2"
       }
     },
     "onetime": {
@@ -7368,7 +7356,7 @@
       "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
       "dev": true,
       "requires": {
-        "mimic-fn": "^1.0.0"
+        "mimic-fn": "1.2.0"
       }
     },
     "opn": {
@@ -7377,7 +7365,7 @@
       "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==",
       "dev": true,
       "requires": {
-        "is-wsl": "^1.1.0"
+        "is-wsl": "1.1.0"
       }
     },
     "optimist": {
@@ -7386,8 +7374,8 @@
       "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
       "dev": true,
       "requires": {
-        "minimist": "~0.0.1",
-        "wordwrap": "~0.0.2"
+        "minimist": "0.0.8",
+        "wordwrap": "0.0.3"
       },
       "dependencies": {
         "wordwrap": {
@@ -7404,12 +7392,12 @@
       "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
       "dev": true,
       "requires": {
-        "deep-is": "~0.1.3",
-        "fast-levenshtein": "~2.0.4",
-        "levn": "~0.3.0",
-        "prelude-ls": "~1.1.2",
-        "type-check": "~0.3.2",
-        "wordwrap": "~1.0.0"
+        "deep-is": "0.1.3",
+        "fast-levenshtein": "2.0.6",
+        "levn": "0.3.0",
+        "prelude-ls": "1.1.2",
+        "type-check": "0.3.2",
+        "wordwrap": "1.0.0"
       }
     },
     "options": {
@@ -7424,7 +7412,7 @@
       "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
       "dev": true,
       "requires": {
-        "url-parse": "^1.4.3"
+        "url-parse": "1.4.4"
       }
     },
     "os-browserify": {
@@ -7446,7 +7434,7 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "lcid": "^1.0.0"
+        "lcid": "1.0.0"
       }
     },
     "os-tmpdir": {
@@ -7461,8 +7449,8 @@
       "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
       "dev": true,
       "requires": {
-        "os-homedir": "^1.0.0",
-        "os-tmpdir": "^1.0.0"
+        "os-homedir": "1.0.2",
+        "os-tmpdir": "1.0.2"
       }
     },
     "p-defer": {
@@ -7489,7 +7477,7 @@
       "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
       "dev": true,
       "requires": {
-        "p-try": "^1.0.0"
+        "p-try": "1.0.0"
       }
     },
     "p-locate": {
@@ -7498,7 +7486,7 @@
       "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
       "dev": true,
       "requires": {
-        "p-limit": "^1.1.0"
+        "p-limit": "1.3.0"
       }
     },
     "p-map": {
@@ -7525,9 +7513,9 @@
       "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=",
       "dev": true,
       "requires": {
-        "cyclist": "~0.2.2",
-        "inherits": "^2.0.3",
-        "readable-stream": "^2.1.5"
+        "cyclist": "0.2.2",
+        "inherits": "2.0.3",
+        "readable-stream": "2.3.6"
       }
     },
     "parse-asn1": {
@@ -7536,12 +7524,12 @@
       "integrity": "sha512-VrPoetlz7B/FqjBLD2f5wBVZvsZVLnRUrxVLfRYhGXCODa/NWE4p3Wp+6+aV3ZPL3KM7/OZmxDIwwijD7yuucg==",
       "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"
+        "asn1.js": "4.10.1",
+        "browserify-aes": "1.2.0",
+        "create-hash": "1.2.0",
+        "evp_bytestokey": "1.0.3",
+        "pbkdf2": "3.0.17",
+        "safe-buffer": "5.1.2"
       }
     },
     "parse-glob": {
@@ -7550,10 +7538,10 @@
       "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=",
       "dev": true,
       "requires": {
-        "glob-base": "^0.3.0",
-        "is-dotfile": "^1.0.0",
-        "is-extglob": "^1.0.0",
-        "is-glob": "^2.0.0"
+        "glob-base": "0.3.0",
+        "is-dotfile": "1.0.3",
+        "is-extglob": "1.0.0",
+        "is-glob": "2.0.1"
       },
       "dependencies": {
         "is-extglob": {
@@ -7568,7 +7556,7 @@
           "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
           "dev": true,
           "requires": {
-            "is-extglob": "^1.0.0"
+            "is-extglob": "1.0.0"
           }
         }
       }
@@ -7579,7 +7567,7 @@
       "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
       "dev": true,
       "requires": {
-        "error-ex": "^1.2.0"
+        "error-ex": "1.3.2"
       }
     },
     "parse5": {
@@ -7594,7 +7582,7 @@
       "integrity": "sha1-q343WfIJ7OmUN5c/fQ8fZK4OZKs=",
       "dev": true,
       "requires": {
-        "better-assert": "~1.0.0"
+        "better-assert": "1.0.2"
       }
     },
     "parseqs": {
@@ -7603,7 +7591,7 @@
       "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
       "dev": true,
       "requires": {
-        "better-assert": "~1.0.0"
+        "better-assert": "1.0.2"
       }
     },
     "parseuri": {
@@ -7612,7 +7600,7 @@
       "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
       "dev": true,
       "requires": {
-        "better-assert": "~1.0.0"
+        "better-assert": "1.0.2"
       }
     },
     "parseurl": {
@@ -7681,7 +7669,7 @@
       "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
       "dev": true,
       "requires": {
-        "pify": "^3.0.0"
+        "pify": "3.0.0"
       }
     },
     "pbkdf2": {
@@ -7690,11 +7678,11 @@
       "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"
+        "create-hash": "1.2.0",
+        "create-hmac": "1.1.7",
+        "ripemd160": "2.0.2",
+        "safe-buffer": "5.1.2",
+        "sha.js": "2.4.11"
       }
     },
     "pend": {
@@ -7715,15 +7703,15 @@
       "integrity": "sha1-79ISpKOWbTZHaE6ouniFSb4q7+8=",
       "dev": true,
       "requires": {
-        "es6-promise": "^4.0.3",
-        "extract-zip": "^1.6.5",
-        "fs-extra": "^1.0.0",
-        "hasha": "^2.2.0",
-        "kew": "^0.7.0",
-        "progress": "^1.1.8",
-        "request": "^2.81.0",
-        "request-progress": "^2.0.1",
-        "which": "^1.2.10"
+        "es6-promise": "4.2.4",
+        "extract-zip": "1.6.7",
+        "fs-extra": "1.0.0",
+        "hasha": "2.2.0",
+        "kew": "0.7.0",
+        "progress": "1.1.8",
+        "request": "2.88.0",
+        "request-progress": "2.0.1",
+        "which": "1.3.1"
       }
     },
     "pify": {
@@ -7744,7 +7732,7 @@
       "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
       "dev": true,
       "requires": {
-        "pinkie": "^2.0.0"
+        "pinkie": "2.0.4"
       }
     },
     "pkg-dir": {
@@ -7753,7 +7741,7 @@
       "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
       "dev": true,
       "requires": {
-        "find-up": "^2.1.0"
+        "find-up": "2.1.0"
       }
     },
     "portfinder": {
@@ -7762,9 +7750,9 @@
       "integrity": "sha512-syFcRIRzVI1BoEFOCaAiizwDolh1S1YXSodsVhncbhjzjZQulhczNRbqnUl9N31Q4dKGOXsNDqxC2BWBgSMqeQ==",
       "dev": true,
       "requires": {
-        "async": "^1.5.2",
-        "debug": "^2.2.0",
-        "mkdirp": "0.5.x"
+        "async": "1.5.2",
+        "debug": "2.6.9",
+        "mkdirp": "0.5.1"
       }
     },
     "posix-character-classes": {
@@ -7779,9 +7767,9 @@
       "integrity": "sha512-HBNpviAUFCKvEh7NZhw1e8MBPivRszIiUnhrJ+sBFVSYSqubrzwX3KG51mYgcRHX8j/cAgZJedONZcm5jTBdgQ==",
       "dev": true,
       "requires": {
-        "chalk": "^2.4.1",
-        "source-map": "^0.6.1",
-        "supports-color": "^5.5.0"
+        "chalk": "2.4.1",
+        "source-map": "0.6.1",
+        "supports-color": "5.5.0"
       },
       "dependencies": {
         "source-map": {
@@ -7798,10 +7786,10 @@
       "integrity": "sha512-3KqKRZcaZAvxbY8DVLdd81tG5uKzbUQuiWIvy0o0fzEC42bKacqPYFWbfCQyw6L4LWUaqPz/idvIdbhpgQ32eQ==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.1",
-        "postcss-value-parser": "^3.2.3",
-        "read-cache": "^1.0.0",
-        "resolve": "^1.1.7"
+        "postcss": "7.0.5",
+        "postcss-value-parser": "3.3.1",
+        "read-cache": "1.0.0",
+        "resolve": "1.1.7"
       }
     },
     "postcss-load-config": {
@@ -7810,8 +7798,8 @@
       "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==",
       "dev": true,
       "requires": {
-        "cosmiconfig": "^4.0.0",
-        "import-cwd": "^2.0.0"
+        "cosmiconfig": "4.0.0",
+        "import-cwd": "2.1.0"
       }
     },
     "postcss-loader": {
@@ -7820,10 +7808,10 @@
       "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"
+        "loader-utils": "1.1.0",
+        "postcss": "7.0.5",
+        "postcss-load-config": "2.0.0",
+        "schema-utils": "1.0.0"
       }
     },
     "postcss-value-parser": {
@@ -7869,7 +7857,7 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "asap": "~2.0.3"
+        "asap": "2.0.6"
       }
     },
     "promise-inflight": {
@@ -7884,21 +7872,21 @@
       "integrity": "sha512-zlIj64Cr6IOWP7RwxVeD8O4UskLYPoyIcg0HboWJL9T79F1F0VWtKkGTr/9GN6BKL+/Q/GmM7C9kFVCfDbP5sA==",
       "dev": true,
       "requires": {
-        "@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",
+        "@types/q": "0.0.32",
+        "@types/selenium-webdriver": "3.0.14",
+        "blocking-proxy": "1.0.1",
+        "browserstack": "1.5.2",
+        "chalk": "1.1.3",
+        "glob": "7.1.3",
         "jasmine": "2.8.0",
-        "jasminewd2": "^2.1.0",
-        "optimist": "~0.6.0",
+        "jasminewd2": "2.2.0",
+        "optimist": "0.6.1",
         "q": "1.4.1",
-        "saucelabs": "^1.5.0",
+        "saucelabs": "1.5.0",
         "selenium-webdriver": "3.6.0",
-        "source-map-support": "~0.4.0",
+        "source-map-support": "0.4.18",
         "webdriver-js-extender": "2.1.0",
-        "webdriver-manager": "^12.0.6"
+        "webdriver-manager": "12.1.1"
       },
       "dependencies": {
         "ansi-styles": {
@@ -7913,11 +7901,11 @@
           "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"
+            "ansi-styles": "2.2.1",
+            "escape-string-regexp": "1.0.5",
+            "has-ansi": "2.0.0",
+            "strip-ansi": "3.0.1",
+            "supports-color": "2.0.0"
           }
         },
         "del": {
@@ -7926,13 +7914,13 @@
           "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": "5.0.0",
+            "is-path-cwd": "1.0.0",
+            "is-path-in-cwd": "1.0.1",
+            "object-assign": "4.1.1",
+            "pify": "2.3.0",
+            "pinkie-promise": "2.0.1",
+            "rimraf": "2.6.2"
           }
         },
         "globby": {
@@ -7941,12 +7929,12 @@
           "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"
+            "array-union": "1.0.2",
+            "arrify": "1.0.1",
+            "glob": "7.1.3",
+            "object-assign": "4.1.1",
+            "pify": "2.3.0",
+            "pinkie-promise": "2.0.1"
           }
         },
         "minimist": {
@@ -7967,7 +7955,7 @@
           "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
           "dev": true,
           "requires": {
-            "source-map": "^0.5.6"
+            "source-map": "0.5.7"
           }
         },
         "supports-color": {
@@ -7982,17 +7970,17 @@
           "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"
+            "adm-zip": "0.4.13",
+            "chalk": "1.1.3",
+            "del": "2.2.2",
+            "glob": "7.1.3",
+            "ini": "1.3.5",
+            "minimist": "1.2.0",
+            "q": "1.4.1",
+            "request": "2.88.0",
+            "rimraf": "2.6.2",
+            "semver": "5.5.1",
+            "xml2js": "0.4.19"
           }
         }
       }
@@ -8003,7 +7991,7 @@
       "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==",
       "dev": true,
       "requires": {
-        "forwarded": "~0.1.2",
+        "forwarded": "0.1.2",
         "ipaddr.js": "1.8.0"
       }
     },
@@ -8031,12 +8019,12 @@
       "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"
+        "bn.js": "4.11.8",
+        "browserify-rsa": "4.0.1",
+        "create-hash": "1.2.0",
+        "parse-asn1": "5.1.3",
+        "randombytes": "2.0.6",
+        "safe-buffer": "5.1.2"
       }
     },
     "pump": {
@@ -8045,8 +8033,8 @@
       "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
       "dev": true,
       "requires": {
-        "end-of-stream": "^1.1.0",
-        "once": "^1.3.1"
+        "end-of-stream": "1.4.1",
+        "once": "1.4.0"
       }
     },
     "pumpify": {
@@ -8055,9 +8043,9 @@
       "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
       "dev": true,
       "requires": {
-        "duplexify": "^3.6.0",
-        "inherits": "^2.0.3",
-        "pump": "^2.0.0"
+        "duplexify": "3.6.1",
+        "inherits": "2.0.3",
+        "pump": "2.0.1"
       }
     },
     "punycode": {
@@ -8108,9 +8096,9 @@
       "integrity": "sha512-KnGPVE0lo2WoXxIZ7cPR8YBpiol4gsSuOwDSg410oHh80ZMp5EiypNqL2K4Z77vJn6lB5rap7IkAmcUlalcnBQ==",
       "dev": true,
       "requires": {
-        "is-number": "^4.0.0",
-        "kind-of": "^6.0.0",
-        "math-random": "^1.0.1"
+        "is-number": "4.0.0",
+        "kind-of": "6.0.2",
+        "math-random": "1.0.1"
       },
       "dependencies": {
         "is-number": {
@@ -8127,7 +8115,7 @@
       "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==",
       "dev": true,
       "requires": {
-        "safe-buffer": "^5.1.0"
+        "safe-buffer": "5.1.2"
       }
     },
     "randomfill": {
@@ -8136,8 +8124,8 @@
       "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
       "dev": true,
       "requires": {
-        "randombytes": "^2.0.5",
-        "safe-buffer": "^5.1.0"
+        "randombytes": "2.0.6",
+        "safe-buffer": "5.1.2"
       }
     },
     "range-parser": {
@@ -8173,7 +8161,7 @@
             "depd": "1.1.1",
             "inherits": "2.0.3",
             "setprototypeof": "1.0.3",
-            "statuses": ">= 1.3.1 < 2"
+            "statuses": "1.4.0"
           }
         },
         "setprototypeof": {
@@ -8196,7 +8184,7 @@
       "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=",
       "dev": true,
       "requires": {
-        "pify": "^2.3.0"
+        "pify": "2.3.0"
       },
       "dependencies": {
         "pify": {
@@ -8212,11 +8200,10 @@
       "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
       "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
       "dev": true,
-      "optional": true,
       "requires": {
-        "load-json-file": "^1.0.0",
-        "normalize-package-data": "^2.3.2",
-        "path-type": "^1.0.0"
+        "load-json-file": "1.1.0",
+        "normalize-package-data": "2.4.1",
+        "path-type": "1.1.0"
       },
       "dependencies": {
         "path-type": {
@@ -8224,19 +8211,17 @@
           "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
           "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
           "dev": true,
-          "optional": true,
           "requires": {
-            "graceful-fs": "^4.1.2",
-            "pify": "^2.0.0",
-            "pinkie-promise": "^2.0.0"
+            "graceful-fs": "4.1.11",
+            "pify": "2.3.0",
+            "pinkie-promise": "2.0.1"
           }
         },
         "pify": {
           "version": "2.3.0",
           "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
           "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
-          "dev": true,
-          "optional": true
+          "dev": true
         }
       }
     },
@@ -8245,10 +8230,9 @@
       "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"
+        "find-up": "1.1.2",
+        "read-pkg": "1.1.0"
       },
       "dependencies": {
         "find-up": {
@@ -8256,10 +8240,9 @@
           "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
           "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
           "dev": true,
-          "optional": true,
           "requires": {
-            "path-exists": "^2.0.0",
-            "pinkie-promise": "^2.0.0"
+            "path-exists": "2.1.0",
+            "pinkie-promise": "2.0.1"
           }
         },
         "path-exists": {
@@ -8267,9 +8250,8 @@
           "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"
+            "pinkie-promise": "2.0.1"
           }
         }
       }
@@ -8280,13 +8262,13 @@
       "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"
+        "core-util-is": "1.0.2",
+        "inherits": "2.0.3",
+        "isarray": "1.0.0",
+        "process-nextick-args": "2.0.0",
+        "safe-buffer": "5.1.2",
+        "string_decoder": "1.1.1",
+        "util-deprecate": "1.0.2"
       }
     },
     "readdirp": {
@@ -8295,10 +8277,10 @@
       "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=",
       "dev": true,
       "requires": {
-        "graceful-fs": "^4.1.2",
-        "minimatch": "^3.0.2",
-        "readable-stream": "^2.0.2",
-        "set-immediate-shim": "^1.0.1"
+        "graceful-fs": "4.1.11",
+        "minimatch": "3.0.4",
+        "readable-stream": "2.3.6",
+        "set-immediate-shim": "1.0.1"
       }
     },
     "rechoir": {
@@ -8307,7 +8289,7 @@
       "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
       "dev": true,
       "requires": {
-        "resolve": "^1.1.6"
+        "resolve": "1.1.7"
       }
     },
     "redent": {
@@ -8317,8 +8299,8 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "indent-string": "^2.1.0",
-        "strip-indent": "^1.0.1"
+        "indent-string": "2.1.0",
+        "strip-indent": "1.0.1"
       }
     },
     "reflect-metadata": {
@@ -8345,7 +8327,7 @@
       "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==",
       "dev": true,
       "requires": {
-        "is-equal-shallow": "^0.1.3"
+        "is-equal-shallow": "0.1.3"
       }
     },
     "regex-not": {
@@ -8354,8 +8336,8 @@
       "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
       "dev": true,
       "requires": {
-        "extend-shallow": "^3.0.2",
-        "safe-regex": "^1.1.0"
+        "extend-shallow": "3.0.2",
+        "safe-regex": "1.1.0"
       }
     },
     "regexpu-core": {
@@ -8364,9 +8346,9 @@
       "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=",
       "dev": true,
       "requires": {
-        "regenerate": "^1.2.1",
-        "regjsgen": "^0.2.0",
-        "regjsparser": "^0.1.4"
+        "regenerate": "1.4.0",
+        "regjsgen": "0.2.0",
+        "regjsparser": "0.1.5"
       }
     },
     "regjsgen": {
@@ -8381,7 +8363,7 @@
       "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=",
       "dev": true,
       "requires": {
-        "jsesc": "~0.5.0"
+        "jsesc": "0.5.0"
       },
       "dependencies": {
         "jsesc": {
@@ -8416,7 +8398,7 @@
       "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
       "dev": true,
       "requires": {
-        "is-finite": "^1.0.0"
+        "is-finite": "1.0.2"
       }
     },
     "request": {
@@ -8425,26 +8407,26 @@
       "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"
+        "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.20",
+        "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"
       }
     },
     "request-progress": {
@@ -8453,7 +8435,7 @@
       "integrity": "sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=",
       "dev": true,
       "requires": {
-        "throttleit": "^1.0.0"
+        "throttleit": "1.0.0"
       }
     },
     "require-directory": {
@@ -8492,7 +8474,7 @@
       "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
       "dev": true,
       "requires": {
-        "resolve-from": "^3.0.0"
+        "resolve-from": "3.0.0"
       }
     },
     "resolve-from": {
@@ -8513,8 +8495,8 @@
       "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
       "dev": true,
       "requires": {
-        "onetime": "^2.0.0",
-        "signal-exit": "^3.0.2"
+        "onetime": "2.0.1",
+        "signal-exit": "3.0.2"
       }
     },
     "ret": {
@@ -8535,7 +8517,7 @@
       "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
       "dev": true,
       "requires": {
-        "glob": "^7.0.5"
+        "glob": "7.1.3"
       }
     },
     "ripemd160": {
@@ -8544,8 +8526,8 @@
       "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
       "dev": true,
       "requires": {
-        "hash-base": "^3.0.0",
-        "inherits": "^2.0.1"
+        "hash-base": "3.0.4",
+        "inherits": "2.0.3"
       }
     },
     "run-async": {
@@ -8554,7 +8536,7 @@
       "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
       "dev": true,
       "requires": {
-        "is-promise": "^2.1.0"
+        "is-promise": "2.1.0"
       }
     },
     "run-queue": {
@@ -8563,7 +8545,7 @@
       "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
       "dev": true,
       "requires": {
-        "aproba": "^1.1.1"
+        "aproba": "1.2.0"
       }
     },
     "rxjs": {
@@ -8571,7 +8553,7 @@
       "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz",
       "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==",
       "requires": {
-        "tslib": "^1.9.0"
+        "tslib": "1.9.3"
       }
     },
     "safe-buffer": {
@@ -8586,7 +8568,7 @@
       "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
       "dev": true,
       "requires": {
-        "ret": "~0.1.10"
+        "ret": "0.1.15"
       }
     },
     "safer-buffer": {
@@ -8602,10 +8584,10 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "glob": "^7.0.0",
-        "lodash": "^4.0.0",
-        "scss-tokenizer": "^0.2.3",
-        "yargs": "^7.0.0"
+        "glob": "7.1.3",
+        "lodash": "4.17.10",
+        "scss-tokenizer": "0.2.3",
+        "yargs": "7.1.0"
       }
     },
     "sass-loader": {
@@ -8614,12 +8596,12 @@
       "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"
+        "clone-deep": "2.0.2",
+        "loader-utils": "1.1.0",
+        "lodash.tail": "4.1.1",
+        "neo-async": "2.6.0",
+        "pify": "3.0.0",
+        "semver": "5.5.1"
       }
     },
     "saucelabs": {
@@ -8628,7 +8610,7 @@
       "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==",
       "dev": true,
       "requires": {
-        "https-proxy-agent": "^2.2.1"
+        "https-proxy-agent": "2.2.1"
       }
     },
     "sax": {
@@ -8643,9 +8625,9 @@
       "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
       "dev": true,
       "requires": {
-        "ajv": "^6.1.0",
-        "ajv-errors": "^1.0.0",
-        "ajv-keywords": "^3.1.0"
+        "ajv": "6.5.3",
+        "ajv-errors": "1.0.1",
+        "ajv-keywords": "3.3.0"
       }
     },
     "scss-tokenizer": {
@@ -8655,8 +8637,8 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "js-base64": "^2.1.8",
-        "source-map": "^0.4.2"
+        "js-base64": "2.5.1",
+        "source-map": "0.4.4"
       },
       "dependencies": {
         "source-map": {
@@ -8666,7 +8648,7 @@
           "dev": true,
           "optional": true,
           "requires": {
-            "amdefine": ">=0.0.4"
+            "amdefine": "1.0.1"
           }
         }
       }
@@ -8683,10 +8665,10 @@
       "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==",
       "dev": true,
       "requires": {
-        "jszip": "^3.1.3",
-        "rimraf": "^2.5.4",
+        "jszip": "3.1.5",
+        "rimraf": "2.6.2",
         "tmp": "0.0.30",
-        "xml2js": "^0.4.17"
+        "xml2js": "0.4.19"
       },
       "dependencies": {
         "tmp": {
@@ -8695,7 +8677,7 @@
           "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=",
           "dev": true,
           "requires": {
-            "os-tmpdir": "~1.0.1"
+            "os-tmpdir": "1.0.2"
           }
         }
       }
@@ -8721,7 +8703,7 @@
       "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=",
       "dev": true,
       "requires": {
-        "semver": "^5.3.0"
+        "semver": "5.5.1"
       }
     },
     "semver-intersect": {
@@ -8730,7 +8712,7 @@
       "integrity": "sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ==",
       "dev": true,
       "requires": {
-        "semver": "^5.0.0"
+        "semver": "5.5.1"
       }
     },
     "send": {
@@ -8740,18 +8722,18 @@
       "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",
+        "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",
+        "http-errors": "1.6.3",
         "mime": "1.4.1",
         "ms": "2.0.0",
-        "on-finished": "~2.3.0",
-        "range-parser": "~1.2.0",
-        "statuses": "~1.4.0"
+        "on-finished": "2.3.0",
+        "range-parser": "1.2.0",
+        "statuses": "1.4.0"
       },
       "dependencies": {
         "mime": {
@@ -8774,13 +8756,13 @@
       "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
       "dev": true,
       "requires": {
-        "accepts": "~1.3.4",
+        "accepts": "1.3.5",
         "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"
+        "escape-html": "1.0.3",
+        "http-errors": "1.6.3",
+        "mime-types": "2.1.20",
+        "parseurl": "1.3.2"
       }
     },
     "serve-static": {
@@ -8789,9 +8771,9 @@
       "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
       "dev": true,
       "requires": {
-        "encodeurl": "~1.0.2",
-        "escape-html": "~1.0.3",
-        "parseurl": "~1.3.2",
+        "encodeurl": "1.0.2",
+        "escape-html": "1.0.3",
+        "parseurl": "1.3.2",
         "send": "0.16.2"
       }
     },
@@ -8813,10 +8795,10 @@
       "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==",
       "dev": true,
       "requires": {
-        "extend-shallow": "^2.0.1",
-        "is-extendable": "^0.1.1",
-        "is-plain-object": "^2.0.3",
-        "split-string": "^3.0.1"
+        "extend-shallow": "2.0.1",
+        "is-extendable": "0.1.1",
+        "is-plain-object": "2.0.4",
+        "split-string": "3.1.0"
       },
       "dependencies": {
         "extend-shallow": {
@@ -8825,7 +8807,7 @@
           "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
           "dev": true,
           "requires": {
-            "is-extendable": "^0.1.0"
+            "is-extendable": "0.1.1"
           }
         }
       }
@@ -8848,8 +8830,8 @@
       "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
       "dev": true,
       "requires": {
-        "inherits": "^2.0.1",
-        "safe-buffer": "^5.0.1"
+        "inherits": "2.0.3",
+        "safe-buffer": "5.1.2"
       }
     },
     "shallow-clone": {
@@ -8858,9 +8840,9 @@
       "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"
+        "is-extendable": "0.1.1",
+        "kind-of": "5.1.0",
+        "mixin-object": "2.0.1"
       },
       "dependencies": {
         "kind-of": {
@@ -8877,7 +8859,7 @@
       "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
       "dev": true,
       "requires": {
-        "shebang-regex": "^1.0.0"
+        "shebang-regex": "1.0.0"
       }
     },
     "shebang-regex": {
@@ -8892,9 +8874,9 @@
       "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==",
       "dev": true,
       "requires": {
-        "glob": "^7.0.0",
-        "interpret": "^1.0.0",
-        "rechoir": "^0.6.2"
+        "glob": "7.1.3",
+        "interpret": "1.2.0",
+        "rechoir": "0.6.2"
       }
     },
     "signal-exit": {
@@ -8921,14 +8903,14 @@
       "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"
+        "base": "0.11.2",
+        "debug": "2.6.9",
+        "define-property": "0.2.5",
+        "extend-shallow": "2.0.1",
+        "map-cache": "0.2.2",
+        "source-map": "0.5.7",
+        "source-map-resolve": "0.5.2",
+        "use": "3.1.1"
       },
       "dependencies": {
         "define-property": {
@@ -8937,7 +8919,7 @@
           "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
           "dev": true,
           "requires": {
-            "is-descriptor": "^0.1.0"
+            "is-descriptor": "0.1.6"
           }
         },
         "extend-shallow": {
@@ -8946,7 +8928,7 @@
           "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
           "dev": true,
           "requires": {
-            "is-extendable": "^0.1.0"
+            "is-extendable": "0.1.1"
           }
         }
       }
@@ -8957,9 +8939,9 @@
       "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
       "dev": true,
       "requires": {
-        "define-property": "^1.0.0",
-        "isobject": "^3.0.0",
-        "snapdragon-util": "^3.0.1"
+        "define-property": "1.0.0",
+        "isobject": "3.0.1",
+        "snapdragon-util": "3.0.1"
       },
       "dependencies": {
         "define-property": {
@@ -8968,7 +8950,7 @@
           "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
           "dev": true,
           "requires": {
-            "is-descriptor": "^1.0.0"
+            "is-descriptor": "1.0.2"
           }
         },
         "is-accessor-descriptor": {
@@ -8977,7 +8959,7 @@
           "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
           "dev": true,
           "requires": {
-            "kind-of": "^6.0.0"
+            "kind-of": "6.0.2"
           }
         },
         "is-data-descriptor": {
@@ -8986,7 +8968,7 @@
           "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
           "dev": true,
           "requires": {
-            "kind-of": "^6.0.0"
+            "kind-of": "6.0.2"
           }
         },
         "is-descriptor": {
@@ -8995,9 +8977,9 @@
           "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
           "dev": true,
           "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
+            "is-accessor-descriptor": "1.0.0",
+            "is-data-descriptor": "1.0.0",
+            "kind-of": "6.0.2"
           }
         }
       }
@@ -9008,7 +8990,7 @@
       "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
       "dev": true,
       "requires": {
-        "kind-of": "^3.2.0"
+        "kind-of": "3.2.2"
       },
       "dependencies": {
         "kind-of": {
@@ -9017,7 +8999,7 @@
           "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
           "dev": true,
           "requires": {
-            "is-buffer": "^1.1.5"
+            "is-buffer": "1.1.6"
           }
         }
       }
@@ -9170,8 +9152,8 @@
       "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==",
       "dev": true,
       "requires": {
-        "faye-websocket": "^0.10.0",
-        "uuid": "^3.0.1"
+        "faye-websocket": "0.10.0",
+        "uuid": "3.3.2"
       }
     },
     "sockjs-client": {
@@ -9180,12 +9162,12 @@
       "integrity": "sha1-G7fA9yIsQPQq3xT0RCy9Eml3GoM=",
       "dev": true,
       "requires": {
-        "debug": "^2.6.6",
+        "debug": "2.6.9",
         "eventsource": "0.1.6",
-        "faye-websocket": "~0.11.0",
-        "inherits": "^2.0.1",
-        "json3": "^3.3.2",
-        "url-parse": "^1.1.8"
+        "faye-websocket": "0.11.1",
+        "inherits": "2.0.3",
+        "json3": "3.3.2",
+        "url-parse": "1.4.4"
       },
       "dependencies": {
         "faye-websocket": {
@@ -9194,7 +9176,7 @@
           "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=",
           "dev": true,
           "requires": {
-            "websocket-driver": ">=0.5.1"
+            "websocket-driver": "0.7.0"
           }
         }
       }
@@ -9217,8 +9199,8 @@
       "integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==",
       "dev": true,
       "requires": {
-        "async": "^2.5.0",
-        "loader-utils": "^1.1.0"
+        "async": "2.6.1",
+        "loader-utils": "1.1.0"
       },
       "dependencies": {
         "async": {
@@ -9227,7 +9209,7 @@
           "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
           "dev": true,
           "requires": {
-            "lodash": "^4.17.10"
+            "lodash": "4.17.10"
           }
         }
       }
@@ -9238,11 +9220,11 @@
       "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"
+        "atob": "2.1.2",
+        "decode-uri-component": "0.2.0",
+        "resolve-url": "0.2.1",
+        "source-map-url": "0.4.0",
+        "urix": "0.1.0"
       }
     },
     "source-map-support": {
@@ -9251,8 +9233,8 @@
       "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==",
       "dev": true,
       "requires": {
-        "buffer-from": "^1.0.0",
-        "source-map": "^0.6.0"
+        "buffer-from": "1.1.1",
+        "source-map": "0.6.1"
       },
       "dependencies": {
         "source-map": {
@@ -9281,8 +9263,8 @@
       "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
       "dev": true,
       "requires": {
-        "spdx-expression-parse": "^3.0.0",
-        "spdx-license-ids": "^3.0.0"
+        "spdx-expression-parse": "3.0.0",
+        "spdx-license-ids": "3.0.3"
       }
     },
     "spdx-exceptions": {
@@ -9297,8 +9279,8 @@
       "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
       "dev": true,
       "requires": {
-        "spdx-exceptions": "^2.1.0",
-        "spdx-license-ids": "^3.0.0"
+        "spdx-exceptions": "2.2.0",
+        "spdx-license-ids": "3.0.3"
       }
     },
     "spdx-license-ids": {
@@ -9313,12 +9295,12 @@
       "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=",
       "dev": true,
       "requires": {
-        "debug": "^2.6.8",
-        "handle-thing": "^1.2.5",
-        "http-deceiver": "^1.2.7",
-        "safe-buffer": "^5.0.1",
-        "select-hose": "^2.0.0",
-        "spdy-transport": "^2.0.18"
+        "debug": "2.6.9",
+        "handle-thing": "1.2.5",
+        "http-deceiver": "1.2.7",
+        "safe-buffer": "5.1.2",
+        "select-hose": "2.0.0",
+        "spdy-transport": "2.1.1"
       }
     },
     "spdy-transport": {
@@ -9327,13 +9309,13 @@
       "integrity": "sha512-q7D8c148escoB3Z7ySCASadkegMmUZW8Wb/Q1u0/XBgDKMO880rLQDj8Twiew/tYi7ghemKUi/whSYOwE17f5Q==",
       "dev": true,
       "requires": {
-        "debug": "^2.6.8",
-        "detect-node": "^2.0.3",
-        "hpack.js": "^2.1.6",
-        "obuf": "^1.1.1",
-        "readable-stream": "^2.2.9",
-        "safe-buffer": "^5.0.1",
-        "wbuf": "^1.7.2"
+        "debug": "2.6.9",
+        "detect-node": "2.0.4",
+        "hpack.js": "2.1.6",
+        "obuf": "1.1.2",
+        "readable-stream": "2.3.6",
+        "safe-buffer": "5.1.2",
+        "wbuf": "1.7.3"
       }
     },
     "speed-measure-webpack-plugin": {
@@ -9342,7 +9324,7 @@
       "integrity": "sha512-b9Yd0TrzceMVYSbuamM1sFsGM1oVfyFTM22gOoyLhymNvBVApuYpkdFOgYkKJpN/KhTpcCYcTGHg7X+FJ33Vvw==",
       "dev": true,
       "requires": {
-        "chalk": "^2.0.1"
+        "chalk": "2.4.1"
       }
     },
     "split-string": {
@@ -9351,7 +9333,7 @@
       "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
       "dev": true,
       "requires": {
-        "extend-shallow": "^3.0.0"
+        "extend-shallow": "3.0.2"
       }
     },
     "sprintf-js": {
@@ -9366,15 +9348,15 @@
       "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=",
       "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"
+        "asn1": "0.2.4",
+        "assert-plus": "1.0.0",
+        "bcrypt-pbkdf": "1.0.2",
+        "dashdash": "1.14.1",
+        "ecc-jsbn": "0.1.2",
+        "getpass": "0.1.7",
+        "jsbn": "0.1.1",
+        "safer-buffer": "2.1.2",
+        "tweetnacl": "0.14.5"
       }
     },
     "ssri": {
@@ -9383,7 +9365,7 @@
       "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==",
       "dev": true,
       "requires": {
-        "safe-buffer": "^5.1.1"
+        "safe-buffer": "5.1.2"
       }
     },
     "static-extend": {
@@ -9392,8 +9374,8 @@
       "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
       "dev": true,
       "requires": {
-        "define-property": "^0.2.5",
-        "object-copy": "^0.1.0"
+        "define-property": "0.2.5",
+        "object-copy": "0.1.0"
       },
       "dependencies": {
         "define-property": {
@@ -9402,7 +9384,7 @@
           "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
           "dev": true,
           "requires": {
-            "is-descriptor": "^0.1.0"
+            "is-descriptor": "0.1.6"
           }
         }
       }
@@ -9413,7 +9395,7 @@
       "integrity": "sha512-NT0YGhwuQ0EOX+uPhhUcI6/+1Sq/pMzNuSCBVT4GbFl/ac6I/JZefBcjlECNfAb1t3GOx5dEj1Z7x0cAxeeVLQ==",
       "dev": true,
       "requires": {
-        "lodash": "^4.17.4"
+        "lodash": "4.17.10"
       }
     },
     "statuses": {
@@ -9429,7 +9411,7 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "readable-stream": "^2.0.1"
+        "readable-stream": "2.3.6"
       }
     },
     "stream-browserify": {
@@ -9438,8 +9420,8 @@
       "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==",
       "dev": true,
       "requires": {
-        "inherits": "~2.0.1",
-        "readable-stream": "^2.0.2"
+        "inherits": "2.0.3",
+        "readable-stream": "2.3.6"
       }
     },
     "stream-each": {
@@ -9448,8 +9430,8 @@
       "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==",
       "dev": true,
       "requires": {
-        "end-of-stream": "^1.1.0",
-        "stream-shift": "^1.0.0"
+        "end-of-stream": "1.4.1",
+        "stream-shift": "1.0.0"
       }
     },
     "stream-http": {
@@ -9458,11 +9440,11 @@
       "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"
+        "builtin-status-codes": "3.0.0",
+        "inherits": "2.0.3",
+        "readable-stream": "2.3.6",
+        "to-arraybuffer": "1.0.1",
+        "xtend": "4.0.1"
       }
     },
     "stream-shift": {
@@ -9477,9 +9459,9 @@
       "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
       "dev": true,
       "requires": {
-        "code-point-at": "^1.0.0",
-        "is-fullwidth-code-point": "^1.0.0",
-        "strip-ansi": "^3.0.0"
+        "code-point-at": "1.1.0",
+        "is-fullwidth-code-point": "1.0.0",
+        "strip-ansi": "3.0.1"
       }
     },
     "string_decoder": {
@@ -9488,7 +9470,7 @@
       "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
       "dev": true,
       "requires": {
-        "safe-buffer": "~5.1.0"
+        "safe-buffer": "5.1.2"
       }
     },
     "strip-ansi": {
@@ -9497,7 +9479,7 @@
       "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
       "dev": true,
       "requires": {
-        "ansi-regex": "^2.0.0"
+        "ansi-regex": "2.1.1"
       }
     },
     "strip-bom": {
@@ -9505,9 +9487,8 @@
       "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"
+        "is-utf8": "0.2.1"
       }
     },
     "strip-eof": {
@@ -9523,7 +9504,7 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "get-stdin": "^4.0.1"
+        "get-stdin": "4.0.1"
       }
     },
     "style-loader": {
@@ -9532,8 +9513,8 @@
       "integrity": "sha512-uCcN7XWHkqwGVt7skpInW6IGO1tG6ReyFQ1Cseh0VcN6VdcFQi62aG/2F3Y9ueA8x4IVlfaSUxpmQXQD9QrEuQ==",
       "dev": true,
       "requires": {
-        "loader-utils": "^1.1.0",
-        "schema-utils": "^0.4.5"
+        "loader-utils": "1.1.0",
+        "schema-utils": "0.4.7"
       },
       "dependencies": {
         "schema-utils": {
@@ -9542,8 +9523,8 @@
           "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==",
           "dev": true,
           "requires": {
-            "ajv": "^6.1.0",
-            "ajv-keywords": "^3.1.0"
+            "ajv": "6.5.3",
+            "ajv-keywords": "3.3.0"
           }
         }
       }
@@ -9554,12 +9535,12 @@
       "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"
+        "css-parse": "1.7.0",
+        "debug": "2.6.9",
+        "glob": "7.0.6",
+        "mkdirp": "0.5.1",
+        "sax": "0.5.8",
+        "source-map": "0.1.43"
       },
       "dependencies": {
         "glob": {
@@ -9568,12 +9549,12 @@
           "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"
+            "fs.realpath": "1.0.0",
+            "inflight": "1.0.6",
+            "inherits": "2.0.3",
+            "minimatch": "3.0.4",
+            "once": "1.4.0",
+            "path-is-absolute": "1.0.1"
           }
         },
         "source-map": {
@@ -9582,7 +9563,7 @@
           "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=",
           "dev": true,
           "requires": {
-            "amdefine": ">=0.0.4"
+            "amdefine": "1.0.1"
           }
         }
       }
@@ -9593,9 +9574,9 @@
       "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==",
       "dev": true,
       "requires": {
-        "loader-utils": "^1.0.2",
-        "lodash.clonedeep": "^4.5.0",
-        "when": "~3.6.x"
+        "loader-utils": "1.1.0",
+        "lodash.clonedeep": "4.5.0",
+        "when": "3.6.4"
       }
     },
     "supports-color": {
@@ -9604,7 +9585,7 @@
       "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
       "dev": true,
       "requires": {
-        "has-flag": "^3.0.0"
+        "has-flag": "3.0.0"
       }
     },
     "symbol-observable": {
@@ -9626,9 +9607,9 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "block-stream": "*",
-        "fstream": "^1.0.2",
-        "inherits": "2"
+        "block-stream": "0.0.9",
+        "fstream": "1.0.11",
+        "inherits": "2.0.3"
       }
     },
     "terser": {
@@ -9637,9 +9618,9 @@
       "integrity": "sha512-NSo3E99QDbYSMeJaEk9YW2lTg3qS9V0aKGlb+PlOrei1X02r1wSBHCNX/O+yeTRFSWPKPIGj6MqvvdqV4rnVGw==",
       "dev": true,
       "requires": {
-        "commander": "~2.17.1",
-        "source-map": "~0.6.1",
-        "source-map-support": "~0.5.6"
+        "commander": "2.17.1",
+        "source-map": "0.6.1",
+        "source-map-support": "0.5.9"
       },
       "dependencies": {
         "source-map": {
@@ -9656,14 +9637,14 @@
       "integrity": "sha512-61lV0DSxMAZ8AyZG7/A4a3UPlrbOBo8NIQ4tJzLPAdGOQ+yoNC7l5ijEow27lBAL2humer01KLS6bGIMYQxKoA==",
       "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.8.1",
-        "webpack-sources": "^1.1.0",
-        "worker-farm": "^1.5.2"
+        "cacache": "11.3.2",
+        "find-cache-dir": "2.0.0",
+        "schema-utils": "1.0.0",
+        "serialize-javascript": "1.6.1",
+        "source-map": "0.6.1",
+        "terser": "3.14.1",
+        "webpack-sources": "1.2.0",
+        "worker-farm": "1.6.0"
       },
       "dependencies": {
         "bluebird": {
@@ -9678,20 +9659,20 @@
           "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"
+            "bluebird": "3.5.3",
+            "chownr": "1.1.1",
+            "figgy-pudding": "3.5.1",
+            "glob": "7.1.3",
+            "graceful-fs": "4.1.15",
+            "lru-cache": "5.1.1",
+            "mississippi": "3.0.0",
+            "mkdirp": "0.5.1",
+            "move-concurrently": "1.0.1",
+            "promise-inflight": "1.0.1",
+            "rimraf": "2.6.2",
+            "ssri": "6.0.1",
+            "unique-filename": "1.1.1",
+            "y18n": "4.0.0"
           }
         },
         "find-cache-dir": {
@@ -9700,9 +9681,9 @@
           "integrity": "sha512-LDUY6V1Xs5eFskUVYtIwatojt6+9xC9Chnlk/jYOOvn3FAFfSaWddxahDGyNHh0b2dMXa6YW2m0tk8TdVaXHlA==",
           "dev": true,
           "requires": {
-            "commondir": "^1.0.1",
-            "make-dir": "^1.0.0",
-            "pkg-dir": "^3.0.0"
+            "commondir": "1.0.1",
+            "make-dir": "1.3.0",
+            "pkg-dir": "3.0.0"
           }
         },
         "find-up": {
@@ -9711,7 +9692,7 @@
           "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
           "dev": true,
           "requires": {
-            "locate-path": "^3.0.0"
+            "locate-path": "3.0.0"
           }
         },
         "graceful-fs": {
@@ -9726,8 +9707,8 @@
           "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
           "dev": true,
           "requires": {
-            "p-locate": "^3.0.0",
-            "path-exists": "^3.0.0"
+            "p-locate": "3.0.0",
+            "path-exists": "3.0.0"
           }
         },
         "lru-cache": {
@@ -9736,7 +9717,7 @@
           "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
           "dev": true,
           "requires": {
-            "yallist": "^3.0.2"
+            "yallist": "3.0.3"
           }
         },
         "mississippi": {
@@ -9745,16 +9726,16 @@
           "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"
+            "concat-stream": "1.6.2",
+            "duplexify": "3.6.1",
+            "end-of-stream": "1.4.1",
+            "flush-write-stream": "1.0.3",
+            "from2": "2.3.0",
+            "parallel-transform": "1.1.0",
+            "pump": "3.0.0",
+            "pumpify": "1.5.1",
+            "stream-each": "1.2.3",
+            "through2": "2.0.5"
           }
         },
         "p-limit": {
@@ -9763,7 +9744,7 @@
           "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==",
           "dev": true,
           "requires": {
-            "p-try": "^2.0.0"
+            "p-try": "2.0.0"
           }
         },
         "p-locate": {
@@ -9772,7 +9753,7 @@
           "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
           "dev": true,
           "requires": {
-            "p-limit": "^2.0.0"
+            "p-limit": "2.1.0"
           }
         },
         "p-try": {
@@ -9787,7 +9768,7 @@
           "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
           "dev": true,
           "requires": {
-            "find-up": "^3.0.0"
+            "find-up": "3.0.0"
           }
         },
         "pump": {
@@ -9796,8 +9777,8 @@
           "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
           "dev": true,
           "requires": {
-            "end-of-stream": "^1.1.0",
-            "once": "^1.3.1"
+            "end-of-stream": "1.4.1",
+            "once": "1.4.0"
           }
         },
         "source-map": {
@@ -9812,7 +9793,7 @@
           "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
           "dev": true,
           "requires": {
-            "figgy-pudding": "^3.5.1"
+            "figgy-pudding": "3.5.1"
           }
         },
         "yallist": {
@@ -9841,8 +9822,8 @@
       "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
       "dev": true,
       "requires": {
-        "readable-stream": "~2.3.6",
-        "xtend": "~4.0.1"
+        "readable-stream": "2.3.6",
+        "xtend": "4.0.1"
       }
     },
     "thunky": {
@@ -9857,7 +9838,7 @@
       "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==",
       "dev": true,
       "requires": {
-        "setimmediate": "^1.0.4"
+        "setimmediate": "1.0.5"
       }
     },
     "tmp": {
@@ -9866,7 +9847,7 @@
       "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=",
       "dev": true,
       "requires": {
-        "os-tmpdir": "~1.0.1"
+        "os-tmpdir": "1.0.2"
       }
     },
     "to-array": {
@@ -9893,7 +9874,7 @@
       "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
       "dev": true,
       "requires": {
-        "kind-of": "^3.0.2"
+        "kind-of": "3.2.2"
       },
       "dependencies": {
         "kind-of": {
@@ -9902,7 +9883,7 @@
           "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
           "dev": true,
           "requires": {
-            "is-buffer": "^1.1.5"
+            "is-buffer": "1.1.6"
           }
         }
       }
@@ -9913,10 +9894,10 @@
       "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"
+        "define-property": "2.0.2",
+        "extend-shallow": "3.0.2",
+        "regex-not": "1.0.2",
+        "safe-regex": "1.1.0"
       }
     },
     "to-regex-range": {
@@ -9925,8 +9906,8 @@
       "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
       "dev": true,
       "requires": {
-        "is-number": "^3.0.0",
-        "repeat-string": "^1.6.1"
+        "is-number": "3.0.0",
+        "repeat-string": "1.6.1"
       }
     },
     "tokenizr": {
@@ -9941,8 +9922,8 @@
       "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
       "dev": true,
       "requires": {
-        "psl": "^1.1.24",
-        "punycode": "^1.4.1"
+        "psl": "1.1.29",
+        "punycode": "1.4.1"
       },
       "dependencies": {
         "punycode": {
@@ -9979,7 +9960,7 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "glob": "^7.1.2"
+        "glob": "7.1.3"
       }
     },
     "ts-node": {
@@ -9988,14 +9969,14 @@
       "integrity": "sha512-XK7QmDcNHVmZkVtkiwNDWiERRHPyU8nBqZB1+iv2UhOG0q3RQ9HsZ2CMqISlFbxjrYFGfG2mX7bW4dAyxBVzUw==",
       "dev": true,
       "requires": {
-        "arrify": "^1.0.0",
-        "chalk": "^2.3.0",
-        "diff": "^3.1.0",
-        "make-error": "^1.1.1",
-        "minimist": "^1.2.0",
-        "mkdirp": "^0.5.1",
-        "source-map-support": "^0.5.3",
-        "yn": "^2.0.0"
+        "arrify": "1.0.1",
+        "chalk": "2.4.1",
+        "diff": "3.5.0",
+        "make-error": "1.3.5",
+        "minimist": "1.2.0",
+        "mkdirp": "0.5.1",
+        "source-map-support": "0.5.9",
+        "yn": "2.0.0"
       },
       "dependencies": {
         "minimist": {
@@ -10017,18 +9998,18 @@
       "integrity": "sha1-ElX4ej/1frCw4fDmEKi0dIBGya4=",
       "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.12.1"
+        "babel-code-frame": "6.26.0",
+        "builtin-modules": "1.1.1",
+        "chalk": "2.4.1",
+        "commander": "2.17.1",
+        "diff": "3.5.0",
+        "glob": "7.1.3",
+        "js-yaml": "3.12.0",
+        "minimatch": "3.0.4",
+        "resolve": "1.8.1",
+        "semver": "5.5.1",
+        "tslib": "1.9.3",
+        "tsutils": "2.29.0"
       },
       "dependencies": {
         "resolve": {
@@ -10037,7 +10018,7 @@
           "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==",
           "dev": true,
           "requires": {
-            "path-parse": "^1.0.5"
+            "path-parse": "1.0.6"
           }
         }
       }
@@ -10048,7 +10029,7 @@
       "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
       "dev": true,
       "requires": {
-        "tslib": "^1.8.1"
+        "tslib": "1.9.3"
       }
     },
     "tty-browserify": {
@@ -10063,7 +10044,7 @@
       "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
       "dev": true,
       "requires": {
-        "safe-buffer": "^5.0.1"
+        "safe-buffer": "5.1.2"
       }
     },
     "tweetnacl": {
@@ -10079,7 +10060,7 @@
       "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
       "dev": true,
       "requires": {
-        "prelude-ls": "~1.1.2"
+        "prelude-ls": "1.1.2"
       }
     },
     "type-is": {
@@ -10089,7 +10070,7 @@
       "dev": true,
       "requires": {
         "media-typer": "0.3.0",
-        "mime-types": "~2.1.18"
+        "mime-types": "2.1.20"
       }
     },
     "typedarray": {
@@ -10111,8 +10092,8 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "commander": "~2.17.1",
-        "source-map": "~0.6.1"
+        "commander": "2.17.1",
+        "source-map": "0.6.1"
       },
       "dependencies": {
         "source-map": {
@@ -10130,14 +10111,14 @@
       "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==",
       "dev": true,
       "requires": {
-        "cacache": "^10.0.4",
-        "find-cache-dir": "^1.0.0",
-        "schema-utils": "^0.4.5",
-        "serialize-javascript": "^1.4.0",
-        "source-map": "^0.6.1",
-        "uglify-es": "^3.3.4",
-        "webpack-sources": "^1.1.0",
-        "worker-farm": "^1.5.2"
+        "cacache": "10.0.4",
+        "find-cache-dir": "1.0.0",
+        "schema-utils": "0.4.7",
+        "serialize-javascript": "1.6.1",
+        "source-map": "0.6.1",
+        "uglify-es": "3.3.9",
+        "webpack-sources": "1.2.0",
+        "worker-farm": "1.6.0"
       },
       "dependencies": {
         "commander": {
@@ -10152,8 +10133,8 @@
           "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==",
           "dev": true,
           "requires": {
-            "ajv": "^6.1.0",
-            "ajv-keywords": "^3.1.0"
+            "ajv": "6.5.3",
+            "ajv-keywords": "3.3.0"
           }
         },
         "source-map": {
@@ -10168,8 +10149,8 @@
           "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==",
           "dev": true,
           "requires": {
-            "commander": "~2.13.0",
-            "source-map": "~0.6.1"
+            "commander": "2.13.0",
+            "source-map": "0.6.1"
           }
         }
       }
@@ -10186,10 +10167,10 @@
       "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=",
       "dev": true,
       "requires": {
-        "arr-union": "^3.1.0",
-        "get-value": "^2.0.6",
-        "is-extendable": "^0.1.1",
-        "set-value": "^0.4.3"
+        "arr-union": "3.1.0",
+        "get-value": "2.0.6",
+        "is-extendable": "0.1.1",
+        "set-value": "0.4.3"
       },
       "dependencies": {
         "extend-shallow": {
@@ -10198,7 +10179,7 @@
           "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
           "dev": true,
           "requires": {
-            "is-extendable": "^0.1.0"
+            "is-extendable": "0.1.1"
           }
         },
         "set-value": {
@@ -10207,10 +10188,10 @@
           "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"
+            "extend-shallow": "2.0.1",
+            "is-extendable": "0.1.1",
+            "is-plain-object": "2.0.4",
+            "to-object-path": "0.3.0"
           }
         }
       }
@@ -10221,7 +10202,7 @@
       "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
       "dev": true,
       "requires": {
-        "unique-slug": "^2.0.0"
+        "unique-slug": "2.0.1"
       }
     },
     "unique-slug": {
@@ -10230,7 +10211,7 @@
       "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==",
       "dev": true,
       "requires": {
-        "imurmurhash": "^0.1.4"
+        "imurmurhash": "0.1.4"
       }
     },
     "unpipe": {
@@ -10245,8 +10226,8 @@
       "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
       "dev": true,
       "requires": {
-        "has-value": "^0.3.1",
-        "isobject": "^3.0.0"
+        "has-value": "0.3.1",
+        "isobject": "3.0.1"
       },
       "dependencies": {
         "has-value": {
@@ -10255,9 +10236,9 @@
           "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
           "dev": true,
           "requires": {
-            "get-value": "^2.0.3",
-            "has-values": "^0.1.4",
-            "isobject": "^2.0.0"
+            "get-value": "2.0.6",
+            "has-values": "0.1.4",
+            "isobject": "2.1.0"
           },
           "dependencies": {
             "isobject": {
@@ -10291,7 +10272,7 @@
       "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
       "dev": true,
       "requires": {
-        "punycode": "^2.1.0"
+        "punycode": "2.1.1"
       }
     },
     "urix": {
@@ -10330,8 +10311,8 @@
       "integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==",
       "dev": true,
       "requires": {
-        "querystringify": "^2.0.0",
-        "requires-port": "^1.0.0"
+        "querystringify": "2.1.0",
+        "requires-port": "1.0.0"
       }
     },
     "use": {
@@ -10346,8 +10327,8 @@
       "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==",
       "dev": true,
       "requires": {
-        "lru-cache": "4.1.x",
-        "tmp": "0.0.x"
+        "lru-cache": "4.1.3",
+        "tmp": "0.0.31"
       }
     },
     "util": {
@@ -10383,8 +10364,8 @@
       "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
       "dev": true,
       "requires": {
-        "spdx-correct": "^3.0.0",
-        "spdx-expression-parse": "^3.0.0"
+        "spdx-correct": "3.1.0",
+        "spdx-expression-parse": "3.0.0"
       }
     },
     "validate-npm-package-name": {
@@ -10393,7 +10374,7 @@
       "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=",
       "dev": true,
       "requires": {
-        "builtins": "^1.0.3"
+        "builtins": "1.0.3"
       }
     },
     "vary": {
@@ -10408,9 +10389,9 @@
       "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
       "dev": true,
       "requires": {
-        "assert-plus": "^1.0.0",
+        "assert-plus": "1.0.0",
         "core-util-is": "1.0.2",
-        "extsprintf": "^1.2.0"
+        "extsprintf": "1.3.0"
       }
     },
     "vm-browserify": {
@@ -10434,9 +10415,9 @@
       "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==",
       "dev": true,
       "requires": {
-        "chokidar": "^2.0.2",
-        "graceful-fs": "^4.1.2",
-        "neo-async": "^2.5.0"
+        "chokidar": "2.0.4",
+        "graceful-fs": "4.1.11",
+        "neo-async": "2.6.0"
       }
     },
     "wbuf": {
@@ -10445,7 +10426,7 @@
       "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==",
       "dev": true,
       "requires": {
-        "minimalistic-assert": "^1.0.0"
+        "minimalistic-assert": "1.0.1"
       }
     },
     "webdriver-js-extender": {
@@ -10454,8 +10435,8 @@
       "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==",
       "dev": true,
       "requires": {
-        "@types/selenium-webdriver": "^3.0.0",
-        "selenium-webdriver": "^3.0.1"
+        "@types/selenium-webdriver": "3.0.14",
+        "selenium-webdriver": "3.6.0"
       }
     },
     "webpack": {
@@ -10468,26 +10449,26 @@
         "@webassemblyjs/helper-module-context": "1.7.6",
         "@webassemblyjs/wasm-edit": "1.7.6",
         "@webassemblyjs/wasm-parser": "1.7.6",
-        "acorn": "^5.6.2",
-        "acorn-dynamic-import": "^3.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",
-        "uglifyjs-webpack-plugin": "^1.2.4",
-        "watchpack": "^1.5.0",
-        "webpack-sources": "^1.2.0"
+        "acorn": "5.7.3",
+        "acorn-dynamic-import": "3.0.0",
+        "ajv": "6.5.3",
+        "ajv-keywords": "3.3.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.4.0",
+        "loader-utils": "1.1.0",
+        "memory-fs": "0.4.1",
+        "micromatch": "3.1.10",
+        "mkdirp": "0.5.1",
+        "neo-async": "2.6.0",
+        "node-libs-browser": "2.2.0",
+        "schema-utils": "0.4.7",
+        "tapable": "1.1.1",
+        "uglifyjs-webpack-plugin": "1.3.0",
+        "watchpack": "1.6.0",
+        "webpack-sources": "1.2.0"
       },
       "dependencies": {
         "schema-utils": {
@@ -10496,8 +10477,8 @@
           "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==",
           "dev": true,
           "requires": {
-            "ajv": "^6.1.0",
-            "ajv-keywords": "^3.1.0"
+            "ajv": "6.5.3",
+            "ajv-keywords": "3.3.0"
           }
         }
       }
@@ -10508,8 +10489,8 @@
       "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=",
       "dev": true,
       "requires": {
-        "source-list-map": "~0.1.7",
-        "source-map": "~0.4.1"
+        "source-list-map": "0.1.8",
+        "source-map": "0.4.4"
       },
       "dependencies": {
         "source-list-map": {
@@ -10524,7 +10505,7 @@
           "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
           "dev": true,
           "requires": {
-            "amdefine": ">=0.0.4"
+            "amdefine": "1.0.1"
           }
         }
       }
@@ -10535,12 +10516,12 @@
       "integrity": "sha512-5C5gXtOo1I6+0AEg4UPglYEtu3Rai6l5IiO6aUu65scHXz29dc3oIWMiRwvcNLXgL0HwRkRxa9N02ZjFt4hY8w==",
       "dev": true,
       "requires": {
-        "loud-rejection": "^1.6.0",
-        "memory-fs": "~0.4.1",
-        "mime": "^2.3.1",
-        "range-parser": "^1.0.3",
-        "url-join": "^4.0.0",
-        "webpack-log": "^2.0.0"
+        "loud-rejection": "1.6.0",
+        "memory-fs": "0.4.1",
+        "mime": "2.4.0",
+        "range-parser": "1.2.0",
+        "url-join": "4.0.0",
+        "webpack-log": "2.0.0"
       },
       "dependencies": {
         "mime": {
@@ -10558,32 +10539,32 @@
       "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",
-        "serve-index": "^1.7.2",
+        "bonjour": "3.5.0",
+        "chokidar": "2.0.4",
+        "compression": "1.7.3",
+        "connect-history-api-fallback": "1.6.0",
+        "debug": "3.2.6",
+        "del": "3.0.0",
+        "express": "4.16.4",
+        "html-entities": "1.2.1",
+        "http-proxy-middleware": "0.18.0",
+        "import-local": "2.0.0",
+        "internal-ip": "3.0.1",
+        "ip": "1.1.5",
+        "killable": "1.0.1",
+        "loglevel": "1.6.1",
+        "opn": "5.3.0",
+        "portfinder": "1.0.17",
+        "schema-utils": "1.0.0",
+        "selfsigned": "1.10.4",
+        "serve-index": "1.9.1",
         "sockjs": "0.3.19",
         "sockjs-client": "1.1.5",
-        "spdy": "^3.4.1",
-        "strip-ansi": "^3.0.0",
-        "supports-color": "^5.1.0",
+        "spdy": "3.4.7",
+        "strip-ansi": "3.0.1",
+        "supports-color": "5.5.0",
         "webpack-dev-middleware": "3.2.0",
-        "webpack-log": "^2.0.0",
+        "webpack-log": "2.0.0",
         "yargs": "12.0.2"
       },
       "dependencies": {
@@ -10605,9 +10586,9 @@
           "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"
+            "string-width": "2.1.1",
+            "strip-ansi": "4.0.0",
+            "wrap-ansi": "2.1.0"
           },
           "dependencies": {
             "strip-ansi": {
@@ -10616,7 +10597,7 @@
               "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
               "dev": true,
               "requires": {
-                "ansi-regex": "^3.0.0"
+                "ansi-regex": "3.0.0"
               }
             }
           }
@@ -10627,11 +10608,11 @@
           "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"
+            "nice-try": "1.0.5",
+            "path-key": "2.0.1",
+            "semver": "5.5.1",
+            "shebang-command": "1.2.0",
+            "which": "1.3.1"
           }
         },
         "debug": {
@@ -10640,7 +10621,7 @@
           "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
           "dev": true,
           "requires": {
-            "ms": "^2.1.1"
+            "ms": "2.1.1"
           }
         },
         "decamelize": {
@@ -10658,13 +10639,13 @@
           "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"
+            "cross-spawn": "6.0.5",
+            "get-stream": "4.1.0",
+            "is-stream": "1.1.0",
+            "npm-run-path": "2.0.2",
+            "p-finally": "1.0.0",
+            "signal-exit": "3.0.2",
+            "strip-eof": "1.0.0"
           }
         },
         "find-up": {
@@ -10673,7 +10654,7 @@
           "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
           "dev": true,
           "requires": {
-            "locate-path": "^3.0.0"
+            "locate-path": "3.0.0"
           }
         },
         "get-stream": {
@@ -10682,7 +10663,7 @@
           "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
           "dev": true,
           "requires": {
-            "pump": "^3.0.0"
+            "pump": "3.0.0"
           }
         },
         "invert-kv": {
@@ -10703,7 +10684,7 @@
           "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
           "dev": true,
           "requires": {
-            "invert-kv": "^2.0.0"
+            "invert-kv": "2.0.0"
           }
         },
         "locate-path": {
@@ -10712,8 +10693,8 @@
           "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
           "dev": true,
           "requires": {
-            "p-locate": "^3.0.0",
-            "path-exists": "^3.0.0"
+            "p-locate": "3.0.0",
+            "path-exists": "3.0.0"
           }
         },
         "mime": {
@@ -10734,9 +10715,9 @@
           "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
           "dev": true,
           "requires": {
-            "execa": "^1.0.0",
-            "lcid": "^2.0.0",
-            "mem": "^4.0.0"
+            "execa": "1.0.0",
+            "lcid": "2.0.0",
+            "mem": "4.1.0"
           }
         },
         "p-limit": {
@@ -10745,7 +10726,7 @@
           "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==",
           "dev": true,
           "requires": {
-            "p-try": "^2.0.0"
+            "p-try": "2.0.0"
           }
         },
         "p-locate": {
@@ -10754,7 +10735,7 @@
           "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
           "dev": true,
           "requires": {
-            "p-limit": "^2.0.0"
+            "p-limit": "2.1.0"
           }
         },
         "p-try": {
@@ -10769,8 +10750,8 @@
           "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
           "dev": true,
           "requires": {
-            "end-of-stream": "^1.1.0",
-            "once": "^1.3.1"
+            "end-of-stream": "1.4.1",
+            "once": "1.4.0"
           }
         },
         "string-width": {
@@ -10779,8 +10760,8 @@
           "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
           "dev": true,
           "requires": {
-            "is-fullwidth-code-point": "^2.0.0",
-            "strip-ansi": "^4.0.0"
+            "is-fullwidth-code-point": "2.0.0",
+            "strip-ansi": "4.0.0"
           },
           "dependencies": {
             "strip-ansi": {
@@ -10789,7 +10770,7 @@
               "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
               "dev": true,
               "requires": {
-                "ansi-regex": "^3.0.0"
+                "ansi-regex": "3.0.0"
               }
             }
           }
@@ -10800,13 +10781,13 @@
           "integrity": "sha512-YJLMF/96TpKXaEQwaLEo+Z4NDK8aV133ROF6xp9pe3gQoS7sxfpXh4Rv9eC+8vCvWfmDjRQaMSlRPbO+9G6jgA==",
           "dev": true,
           "requires": {
-            "loud-rejection": "^1.6.0",
-            "memory-fs": "~0.4.1",
-            "mime": "^2.3.1",
-            "path-is-absolute": "^1.0.0",
-            "range-parser": "^1.0.3",
-            "url-join": "^4.0.0",
-            "webpack-log": "^2.0.0"
+            "loud-rejection": "1.6.0",
+            "memory-fs": "0.4.1",
+            "mime": "2.4.0",
+            "path-is-absolute": "1.0.1",
+            "range-parser": "1.2.0",
+            "url-join": "4.0.0",
+            "webpack-log": "2.0.0"
           }
         },
         "which-module": {
@@ -10821,18 +10802,18 @@
           "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"
+            "cliui": "4.1.0",
+            "decamelize": "2.0.0",
+            "find-up": "3.0.0",
+            "get-caller-file": "1.0.3",
+            "os-locale": "3.1.0",
+            "require-directory": "2.1.1",
+            "require-main-filename": "1.0.1",
+            "set-blocking": "2.0.0",
+            "string-width": "2.1.1",
+            "which-module": "2.0.0",
+            "y18n": "4.0.0",
+            "yargs-parser": "10.1.0"
           }
         },
         "yargs-parser": {
@@ -10841,7 +10822,7 @@
           "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==",
           "dev": true,
           "requires": {
-            "camelcase": "^4.1.0"
+            "camelcase": "4.1.0"
           }
         }
       }
@@ -10852,8 +10833,8 @@
       "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==",
       "dev": true,
       "requires": {
-        "ansi-colors": "^3.0.0",
-        "uuid": "^3.3.2"
+        "ansi-colors": "3.2.3",
+        "uuid": "3.3.2"
       }
     },
     "webpack-merge": {
@@ -10862,7 +10843,7 @@
       "integrity": "sha512-TmSe1HZKeOPey3oy1Ov2iS3guIZjWvMT2BBJDzzT5jScHTjVC3mpjJofgueEzaEd6ibhxRDD6MIblDr8tzh8iQ==",
       "dev": true,
       "requires": {
-        "lodash": "^4.17.5"
+        "lodash": "4.17.10"
       }
     },
     "webpack-sources": {
@@ -10871,8 +10852,8 @@
       "integrity": "sha512-9BZwxR85dNsjWz3blyxdOhTgtnQvv3OEs5xofI0wPYTwu5kaWxS08UuD1oI7WLBLpRO+ylf0ofnXLXWmGb2WMw==",
       "dev": true,
       "requires": {
-        "source-list-map": "^2.0.0",
-        "source-map": "~0.6.1"
+        "source-list-map": "2.0.1",
+        "source-map": "0.6.1"
       },
       "dependencies": {
         "source-map": {
@@ -10889,7 +10870,7 @@
       "integrity": "sha512-Az7y8xTniNhaA0620AV1KPwWOqawurVVDzQSpPAeR5RwNbL91GoBSJAAo9cfd+GiFHwsS5bbHepBw1e6Hzxy4w==",
       "dev": true,
       "requires": {
-        "webpack-core": "^0.6.8"
+        "webpack-core": "0.6.9"
       }
     },
     "websocket-driver": {
@@ -10898,8 +10879,8 @@
       "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=",
       "dev": true,
       "requires": {
-        "http-parser-js": ">=0.4.0",
-        "websocket-extensions": ">=0.1.1"
+        "http-parser-js": "0.5.0",
+        "websocket-extensions": "0.1.3"
       }
     },
     "websocket-extensions": {
@@ -10920,7 +10901,7 @@
       "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
       "dev": true,
       "requires": {
-        "isexe": "^2.0.0"
+        "isexe": "2.0.0"
       }
     },
     "which-module": {
@@ -10935,9 +10916,8 @@
       "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"
+        "string-width": "1.0.2"
       }
     },
     "wordwrap": {
@@ -10952,7 +10932,7 @@
       "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==",
       "dev": true,
       "requires": {
-        "errno": "~0.1.7"
+        "errno": "0.1.7"
       }
     },
     "wrap-ansi": {
@@ -10961,8 +10941,8 @@
       "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
       "dev": true,
       "requires": {
-        "string-width": "^1.0.1",
-        "strip-ansi": "^3.0.1"
+        "string-width": "1.0.2",
+        "strip-ansi": "3.0.1"
       }
     },
     "wrappy": {
@@ -10977,8 +10957,8 @@
       "integrity": "sha1-iiRPoFJAHgjJiGz0SoUYnh/UBn8=",
       "dev": true,
       "requires": {
-        "options": ">=0.0.5",
-        "ultron": "1.0.x"
+        "options": "0.0.6",
+        "ultron": "1.0.2"
       }
     },
     "wtf-8": {
@@ -10993,8 +10973,8 @@
       "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
       "dev": true,
       "requires": {
-        "sax": ">=0.6.0",
-        "xmlbuilder": "~9.0.1"
+        "sax": "1.2.4",
+        "xmlbuilder": "9.0.7"
       },
       "dependencies": {
         "sax": {
@@ -11054,19 +11034,19 @@
       "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"
+        "camelcase": "3.0.0",
+        "cliui": "3.2.0",
+        "decamelize": "1.2.0",
+        "get-caller-file": "1.0.3",
+        "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": {
@@ -11092,7 +11072,7 @@
       "dev": true,
       "optional": true,
       "requires": {
-        "camelcase": "^3.0.0"
+        "camelcase": "3.0.0"
       },
       "dependencies": {
         "camelcase": {
@@ -11110,7 +11090,7 @@
       "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=",
       "dev": true,
       "requires": {
-        "fd-slicer": "~1.0.1"
+        "fd-slicer": "1.0.1"
       }
     },
     "yeast": {
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
index 4b46e62120..31b6e0c58e 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
@@ -48,6 +48,7 @@
 <eg-mark-missing-dialog #markMissingDialog></eg-mark-missing-dialog>
 <eg-copy-alerts-dialog #copyAlertsDialog></eg-copy-alerts-dialog>
 <eg-replace-barcode-dialog #replaceBarcode></eg-replace-barcode-dialog>
+<eg-delete-volcopy-dialog #deleteVolcopy></eg-delete-volcopy-dialog>
 
 <!-- holdings grid -->
 <div class='eg-copies w-100 mt-3'>
@@ -127,6 +128,23 @@
       i18n-group group="Edit" i18n-label label="Replace Barcodes"
       (onClick)="openReplaceBarcodeDialog($event)">
     </eg-grid-toolbar-action>
+
+    <!-- row actions: Delete -->
+
+    <eg-grid-toolbar-action
+      i18n-group group="Delete" i18n-label label="Delete Empty Call Numbers"
+      (onClick)="deleteHoldings($event, 'vols')">
+    </eg-grid-toolbar-action>
+
+    <eg-grid-toolbar-action
+     i18n-group group="Delete" i18n-label label="Delete Items"
+     (onClick)="deleteHoldings($event, 'copies')">
+    </eg-grid-toolbar-action>
+
+    <eg-grid-toolbar-action
+      i18n-group group="Delete" i18n-label label="Delete Call Numbers and Items"
+      (onClick)="deleteHoldings($event, 'both')">
+    </eg-grid-toolbar-action>
     
     <!-- row actions : Show -->
 
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
index 3e69e90b96..bf375bc4c8 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
@@ -2,7 +2,7 @@ import {Component, OnInit, Input, ViewChild} from '@angular/core';
 import {Observable, Observer, of} from 'rxjs';
 import {map} from 'rxjs/operators';
 import {Pager} from '@eg/share/util/pager';
-import {IdlObject} from '@eg/core/idl.service';
+import {IdlObject, IdlService} from '@eg/core/idl.service';
 import {StaffCatalogService} from '../catalog.service';
 import {OrgService} from '@eg/core/org.service';
 import {PcrudService} from '@eg/core/pcrud.service';
@@ -23,6 +23,8 @@ import {CopyAlertsDialogComponent
     } from '@eg/staff/share/holdings/copy-alerts-dialog.component';
 import {ReplaceBarcodeDialogComponent
     } from '@eg/staff/share/holdings/replace-barcode-dialog.component';
+import {DeleteVolcopyDialogComponent
+    } from '@eg/staff/share/holdings/delete-volcopy-dialog.component';
 
 // The holdings grid models a single HoldingsTree, composed of HoldingsTreeNodes
 // flattened on-demand into a list of HoldingEntry objects.
@@ -90,6 +92,8 @@ export class HoldingsMaintenanceComponent implements OnInit {
         private copyAlertsDialog: CopyAlertsDialogComponent;
     @ViewChild('replaceBarcode')
         private replaceBarcode: ReplaceBarcodeDialogComponent;
+    @ViewChild('deleteVolcopy')
+        private deleteVolcopy: DeleteVolcopyDialogComponent;
 
     holdingsTree: HoldingsTree;
 
@@ -130,6 +134,7 @@ export class HoldingsMaintenanceComponent implements OnInit {
 
     constructor(
         private org: OrgService,
+        private idl: IdlService,
         private pcrud: PcrudService,
         private auth: AuthService,
         private staffCat: StaffCatalogService,
@@ -526,6 +531,7 @@ export class HoldingsMaintenanceComponent implements OnInit {
         volNode.target = volume;
 
         volume.copies()
+            .filter((copy: IdlObject) => (copy.deleted() !== 't'))
             .sort((a: IdlObject, b: IdlObject) => a.barcode() < b.barcode() ? -1 : 1)
             .forEach((copy: IdlObject) => this.appendCopy(volNode, copy));
     }
@@ -765,4 +771,63 @@ export class HoldingsMaintenanceComponent implements OnInit {
             dismissed => {}
         );
     }
+
+    // mode 'vols' -- only delete empty volumes
+    // mode 'copies' -- only delete selected copies
+    // mode 'both' -- delete selected copies and selected volumes, plus all
+    // copies linked to selected volumes, regardless of whether they are selected.
+    deleteHoldings(rows: HoldingsEntry[], mode: 'vols' | 'copies' | 'both') {
+        const volHash: any = {};
+
+        if (mode === 'vols' || mode === 'both') {
+            // Collect the volumes to be deleted.
+            rows.filter(r => r.treeNode.nodeType === 'volume').forEach(r => {
+                const vol = this.idl.clone(r.volume);
+                if (mode === 'vols') {
+                    if (vol.copies().length > 0) {
+                        // cannot delete non-empty volume in this mode.
+                        return;
+                    }
+                } else {
+                    vol.copies().forEach(c => c.isdeleted(true));
+                }
+                vol.isdeleted(true);
+                volHash[vol.id()] = vol;
+            });
+        }
+
+        if (mode === 'copies' || mode === 'both') {
+            // Collect the copies to be deleted, including their volumes
+            // since the API expects fleshed volume objects.
+            rows.filter(r => r.treeNode.nodeType === 'copy').forEach(r => {
+                const vol = r.treeNode.parentNode.target;
+                if (!volHash[vol.id()]) {
+                    volHash[vol.id()] = this.idl.clone(vol);
+                    volHash[vol.id()].copies([]);
+                }
+                const copy = this.idl.clone(r.copy);
+                copy.isdeleted(true);
+                volHash[vol.id()].copies().push(copy);
+            });
+        }
+
+        if (Object.keys(volHash).length === 0) {
+            // No data to process.
+            return;
+        }
+
+        // Note forceDeleteCopies should not be necessary here, since we
+        // manually marked all copies as deleted on deleted volumes in
+        // "both" mode.
+        this.deleteVolcopy.forceDeleteCopies = mode === 'both';
+        this.deleteVolcopy.volumes = Object.values(volHash);
+        this.deleteVolcopy.open({size: 'sm'}).then(
+            modified => {
+                if (modified) {
+                    this.hardRefresh();
+                }
+            },
+            dismissed => {}
+        );
+    }
 }
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/delete-volcopy-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/share/holdings/delete-volcopy-dialog.component.html
new file mode 100644
index 0000000000..9a07de77ff
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/delete-volcopy-dialog.component.html
@@ -0,0 +1,33 @@
+
+
+<eg-string #successMsg
+    text="Successfully Holdings" i18n-text></eg-string>
+<eg-string #errorMsg 
+    text="Failed To Delete Holdings" i18n-text></eg-string>
+
+
+<ng-template #dialogContent>
+    <div class="modal-header bg-info">
+      <h4 class="modal-title">
+        <span i18n>Delete Holdings</span>
+      </h4>
+      <button type="button" class="close" 
+        i18n-aria-label aria-label="Close" (click)="dismiss('cross_click')">
+        <span aria-hidden="true">×</span>
+      </button>
+    </div>
+    <div class="modal-body">
+      <p i18n>Delete {{numVols}} call numbers and {{numCopies}} copies?</p>
+    </div>
+    <div class="modal-footer">
+      <ng-container>
+        <button type="button" class="btn btn-warning" 
+          (click)="dismiss('canceled')" i18n>Cancel</button>
+        <button type="button" class="btn btn-success" 
+          (click)="deleteHoldings()" i18n>
+          Delete Holdings
+        </button>
+      </ng-container>
+    </div>
+  </ng-template>
+  
\ No newline at end of file
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/delete-volcopy-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/share/holdings/delete-volcopy-dialog.component.ts
new file mode 100644
index 0000000000..82bb82791e
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/delete-volcopy-dialog.component.ts
@@ -0,0 +1,123 @@
+import {Component, OnInit, Input, ViewChild, Renderer2} from '@angular/core';
+import {IdlObject} from '@eg/core/idl.service';
+import {NetService} from '@eg/core/net.service';
+import {EventService} from '@eg/core/event.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {AuthService} from '@eg/core/auth.service';
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+import {StringComponent} from '@eg/share/string/string.component';
+
+
+/**
+ * Dialog for marking items missing.
+ */
+
+ at Component({
+  selector: 'eg-delete-volcopy-dialog',
+  templateUrl: 'delete-volcopy-dialog.component.html'
+})
+
+export class DeleteVolcopyDialogComponent
+    extends DialogComponent implements OnInit {
+
+    // List of "acn" objects which may contain copies.
+    // Objects of either type marked "isdeleted" will be deleted.
+    @Input() volumes: IdlObject[];
+
+    // If true, just ask the server to delete all attached copies
+    // for any deleted call numbers.
+    // Note if this is true and a volume is provided that does not contain
+    // of its fleshed copies, the number of copies to delete will not be
+    // reported correctly.
+    @Input() forceDeleteCopies: boolean;
+
+    numVols: number;
+    numCopies: number;
+    numSucceeded: number;
+    numFailed: number;
+
+    @ViewChild('successMsg')
+        private successMsg: StringComponent;
+
+    @ViewChild('errorMsg')
+        private errorMsg: StringComponent;
+
+    constructor(
+        private modal: NgbModal, // required for passing to parent
+        private toast: ToastService,
+        private net: NetService,
+        private pcrud: PcrudService,
+        private evt: EventService,
+        private renderer: Renderer2,
+        private auth: AuthService) {
+        super(modal); // required for subclassing
+    }
+
+    ngOnInit() {}
+
+    async open(args: NgbModalOptions): Promise<boolean> {
+        this.numVols = 0;
+        this.numCopies = 0;
+        this.numSucceeded = 0;
+        this.numFailed = 0;
+
+        this.volumes.forEach(vol => {
+            if (vol.isdeleted()) {
+                this.numVols++;
+            }
+            if (Array.isArray(vol.copies())) {
+                vol.copies().forEach(c => {
+                    if (c.isdeleted() || this.forceDeleteCopies) {
+                        // Marking copies deleted in forceDeleteCopies mode
+                        // is not required, but we do it here so we can
+                        // report the number of copies to be deleted.
+                        c.isdeleted(true)
+                        this.numCopies++;
+                    }
+                })
+            }
+        });
+
+        if (this.numVols === 0 && this.numCopies === 0) {
+            console.debug('Volcopy delete called with no usable data');
+            return Promise.resolve(false);
+        }
+
+        return super.open(args);
+    }
+
+    deleteHoldings() {
+
+        const flags = {
+            force_delete_copies: this.forceDeleteCopies
+        };
+
+        this.net.request(
+            'open-ils.cat',
+            'open-ils.cat.asset.volume.fleshed.batch.update.override',
+            this.auth.token(), this.volumes, 1, flags
+        ).toPromise().then(
+            result => {
+                const evt = this.evt.parse(result);
+                if (evt) {
+                    console.warn(evt);
+                    this.errorMsg.current().then(msg =>this.toast.warning(msg));
+                    this.numFailed++;
+                } else {
+                    this.numSucceeded++;
+                    this.close(this.numSucceeded > 0);
+                }
+            },
+            err => {
+                console.warn(err);
+                this.errorMsg.current().then(msg =>this.toast.warning(msg));
+                this.numFailed++;
+            }
+        );
+    }
+}
+
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.module.ts b/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.module.ts
index c931a2fc27..97a65ce0e2 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.module.ts
+++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.module.ts
@@ -5,13 +5,15 @@ import {MarkDamagedDialogComponent} from './mark-damaged-dialog.component';
 import {MarkMissingDialogComponent} from './mark-missing-dialog.component';
 import {CopyAlertsDialogComponent} from './copy-alerts-dialog.component';
 import {ReplaceBarcodeDialogComponent} from './replace-barcode-dialog.component';
+import {DeleteVolcopyDialogComponent} from './delete-volcopy-dialog.component';
 
 @NgModule({
     declarations: [
       MarkDamagedDialogComponent,
       MarkMissingDialogComponent,
       CopyAlertsDialogComponent,
-      ReplaceBarcodeDialogComponent
+      ReplaceBarcodeDialogComponent,
+      DeleteVolcopyDialogComponent
     ],
     imports: [
         StaffCommonModule
@@ -20,7 +22,8 @@ import {ReplaceBarcodeDialogComponent} from './replace-barcode-dialog.component'
       MarkDamagedDialogComponent,
       MarkMissingDialogComponent,
       CopyAlertsDialogComponent,
-      ReplaceBarcodeDialogComponent
+      ReplaceBarcodeDialogComponent,
+      DeleteVolcopyDialogComponent
     ],
     providers: [
         HoldingsService
diff --git a/Open-ILS/src/eg2/src/styles.css b/Open-ILS/src/eg2/src/styles.css
index 4ca3abab7d..a959e8cf69 100644
--- a/Open-ILS/src/eg2/src/styles.css
+++ b/Open-ILS/src/eg2/src/styles.css
@@ -11,13 +11,13 @@
  * icons.  More research needed.
  * /
 /*
- at import '~material-design-icons/iconfont/material-icons.css'; 
+ at import '~material-design-icons/iconfont/material-icons.css';
 */
 
 /** BS default fonts are huge */
 body, .form-control, .btn, .input-group-text {
   /* This more or less matches the font size of the angularjs client.
-   * The default BS4 font of 1rem is comically large. 
+   * The default BS4 font of 1rem is comically large.
    */
   font-size: .88rem;
 }
@@ -115,15 +115,23 @@ h5 {font-size: .95rem}
   padding: .5rem;
 }
 
- at media all and (min-width: 800px) {                                            
-    /* scrollable typeahead menus for full-size screens */                               
+ at media all and (min-width: 800px) {
+    /* scrollable typeahead menus for full-size screens */
     ngb-typeahead-window {
-        height: auto;                                                          
-        max-height: 200px;                                                     
-        overflow-x: hidden;                                                    
+        height: auto;
+        max-height: 200px;
+        overflow-x: hidden;
     }
 }
 
+/* Limit size of dropdown menus and allow for scrolling */
+.dropdown-menu {
+  height: auto;
+  max-height: 300px;
+  overflow-y: auto;
+  font-size: 98%;
+}
+
 /* --------------------------------------------------------------------------
 /* Form Validation CSS - https://angular.io/guide/form-validation
  * TODO: these colors don't fit the EG color scheme
@@ -169,7 +177,7 @@ h5 {font-size: .95rem}
 
 
 /**
- * Only display the print container when printing 
+ * Only display the print container when printing
  */
 #eg-print-container {
     display: none;

commit a7a8afb9fe6b4ef1b7a1dfa58c42dd691aba795c
Author: Bill Erickson <berickxx at gmail.com>
Date:   Mon Mar 18 17:46:42 2019 -0400

    LP1821382 Angular holdings maintenance continued.
    
    Support for various context menu actions.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.ts b/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.ts
index 8b018d5e38..63f964ef81 100644
--- a/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.ts
+++ b/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.ts
@@ -89,7 +89,7 @@ export class ComboboxComponent implements OnInit {
 
     // Useful for massaging the match string prior to comparison
     // and display.  Default version trims leading/trailing spaces.
-    formatDisplayString: (ComboboxEntry) => string;
+    formatDisplayString: (e: ComboboxEntry) => string;
 
     constructor(
       private elm: ElementRef,
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
index 1855000f60..46d25d7d1c 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
@@ -47,7 +47,8 @@ import {HoldingsMaintenanceComponent} from './record/holdings.component';
     StaffCommonModule,
     CatalogCommonModule,
     CatalogRoutingModule,
-    HoldsModule
+    HoldsModule,
+    HoldingsModule
   ],
   providers: [
     StaffCatalogService
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.css b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.css
new file mode 100644
index 0000000000..61b04cd3e2
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.css
@@ -0,0 +1,41 @@
+/*
+:host /deep/ allows us to share style with child components.
+In this case, the holdings components wants its grid to see
+the CSS we have defined for different row types
+
+See https://v2.angular.io/docs/ts/latest/guide/component-styles.html
+*/
+
+/* grid row colors are bootstrap class="bg-info" with opacity */
+
+/*
+:host /deep/ .holdings-copy-row {
+}
+*/
+
+:host /deep/ .holdings-volume-row {
+    color: #004085;
+    background-color: rgb(23,162,184,0.2);
+}
+
+:host /deep/ .holdings-org-row-0 {
+    color: #004085;
+    background-color: rgb(23,162,184);
+}
+
+:host /deep/ .holdings-org-row-1 {
+    color: #004085;
+    background-color: rgb(23,162,184,0.8);
+}
+
+:host /deep/ .holdings-org-row-2 {
+    color: #004085;
+    background-color: rgb(23,162,184,0.6);
+}
+
+:host /deep/ .holdings-org-row-3 {
+    color: #004085;
+    background-color: rgb(23,162,184,0.4);
+}
+
+/* Add additional classes for more deeply nested org unit hierarchies */
\ No newline at end of file
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
index 3cfcb273a5..4b46e62120 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
@@ -1,4 +1,21 @@
 
+<!-- org unit selector -->
+
+<div class="row mt-3">
+  <div class="col-lg-4">
+    <div class="input-group">
+      <div class="input-group-prepend">
+        <div class="input-group-text" i18n>Holdings Maintenance</div>
+      </div>
+      <eg-org-select [initialOrg]="contextOrg" 
+        (onChange)="contextOrgChanged($event)">
+      </eg-org-select>
+    </div>
+  </div>
+</div>
+
+<!-- Location / Barcode cell template -->
+
 <ng-template #locationTemplate let-row="row" let-userContext="userContext">
   <!-- pl-* is doubled for added impact -->
   <div class="pl-{{row.locationDepth}}">
@@ -18,23 +35,28 @@
   </div>
 </ng-template>
 
+<!-- Holdable true/false display -->
+
 <ng-template #holdableTemplate let-row="row" let-userContext="userContext">
   <ng-container *ngIf="row.copy">
-    <ng-container *ngIf="userContext.copyIsHoldable(row.copy); else notHoldable">
-      <span i18n>Yes</span>
-    </ng-container>
-    <ng-template #notHoldable><span i18n>No</span></ng-template>
+    <eg-bool [value]="userContext.copyIsHoldable(row.copy)">
+    </eg-bool>
   </ng-container>
 </ng-template>
 
+<eg-mark-damaged-dialog #markDamagedDialog></eg-mark-damaged-dialog>
+<eg-mark-missing-dialog #markMissingDialog></eg-mark-missing-dialog>
+<eg-copy-alerts-dialog #copyAlertsDialog></eg-copy-alerts-dialog>
+<eg-replace-barcode-dialog #replaceBarcode></eg-replace-barcode-dialog>
 
+<!-- holdings grid -->
 <div class='eg-copies w-100 mt-3'>
   <eg-grid #holdingsGrid [dataSource]="gridDataSource"
-    (onRowActivate)="onRowActivate($event)"
-    [pageSize]="50" [rowClassCallback]="rowClassCallback"
+    (onRowActivate)="onRowActivate($event)" [disablePaging]="true"
+    [rowClassCallback]="rowClassCallback"
     [sortable]="false" persistKey="cat.holdings">
 
-    <!-- checkboxes -->
+    <!-- checkboxes / filters -->
 
     <eg-grid-toolbar-checkbox i18n-label label="Show Volumes" 
       #volsCheckbox (onChange)="toggleShowVolumes($event)">
@@ -49,14 +71,110 @@
       #emptyLibsCheckbox (onChange)="toggleShowEmptyLibs($event)">
     </eg-grid-toolbar-checkbox> 
 
+    <!-- row actions -->
+
+    <!-- row actions : Ungrouped -->
+
+    <eg-grid-toolbar-action
+      i18n-label label="Print Labels" (onClick)="openItemPrintLabels($event)">
+    </eg-grid-toolbar-action>
+
+    <!-- row actions : Add -->
+
+    <eg-grid-toolbar-action
+      i18n-group group="Add" i18n-label label="Add Call Numbers"
+      (onClick)="openVolCopyEdit($event, true, false)">
+    </eg-grid-toolbar-action>
+
+    <eg-grid-toolbar-action
+      i18n-group group="Add" i18n-label label="Add Items"
+      (onClick)="openVolCopyEdit($event, false, true)">
+    </eg-grid-toolbar-action>
+
+    <eg-grid-toolbar-action
+      i18n-group group="Add" i18n-label label="Add Call Numbers and Items"
+      (onClick)="openVolCopyEdit($event, true, true)">
+    </eg-grid-toolbar-action>
+
+    <eg-grid-toolbar-action
+      i18n-group group="Add" i18n-label label="Add Item Alerts"
+      (onClick)="openItemNotes($event, 'create')">
+    </eg-grid-toolbar-action>
+
+    <!-- row actions: Edit -->
+
+    <eg-grid-toolbar-action
+      i18n-group group="Edit" i18n-label label="Edit Call Numbers"
+      (onClick)="openVolCopyEdit($event, true, false)">
+    </eg-grid-toolbar-action>
+
+    <eg-grid-toolbar-action
+      i18n-group group="Edit" i18n-label label="Edit Call Numbers And Items"
+      (onClick)="openVolCopyEdit($event, true, true)">
+    </eg-grid-toolbar-action>
+
+    <eg-grid-toolbar-action
+      i18n-group group="Edit" i18n-label label="Edit Items"
+      (onClick)="openVolCopyEdit($event, false, true)">
+    </eg-grid-toolbar-action>
+    
+    <eg-grid-toolbar-action
+      i18n-group group="Edit" i18n-label label="Edit Item Alerts"
+      (onClick)="openItemNotes($event, 'manage')">
+    </eg-grid-toolbar-action>
+
+    <eg-grid-toolbar-action
+      i18n-group group="Edit" i18n-label label="Replace Barcodes"
+      (onClick)="openReplaceBarcodeDialog($event)">
+    </eg-grid-toolbar-action>
+    
+    <!-- row actions : Show -->
+
+    <eg-grid-toolbar-action
+      i18n-group group="Show" i18n-label label="Show Item Status (list)"
+      (onClick)="openItemStatusList($event)"></eg-grid-toolbar-action>
+
+    <eg-grid-toolbar-action
+      i18n-group group="Show" i18n-label label="Show Item Status (detail)"
+      (onClick)="openItemStatus($event)"></eg-grid-toolbar-action>
+
+    <eg-grid-toolbar-action
+      i18n-group group="Show" i18n-label label="Show Item Holds"
+      (onClick)="openItemHolds($event)"></eg-grid-toolbar-action>
+
+    <eg-grid-toolbar-action
+      i18n-group group="Show" i18n-label label="Show Triggered Events"
+      (onClick)="openItemTriggeredEvents($event)"></eg-grid-toolbar-action>
+
+    <!-- row actions : Mark -->
+
+    <eg-grid-toolbar-action
+      group="Mark" i18n-group i18n-label label="Mark Item Damaged"
+      (onClick)="showMarkDamagedDialog($event)"></eg-grid-toolbar-action>
+
+    <eg-grid-toolbar-action
+      i18n-group group="Mark" i18n-label label="Mark Item Missing"
+      (onClick)="showMarkMissingDialog($event)">
+    </eg-grid-toolbar-action>
+
+    <eg-grid-toolbar-action
+      i18n-group group="Mark" 
+      i18n-label label="Mark Library/Call Number as Transfer Destination"
+      (onClick)="markLibCnForTransfer($event)">
+    </eg-grid-toolbar-action>
+      
+
     <!-- fields -->
+    <!-- NOTE column names were added to match the names from the AngJS grid
+        so grid settings would propagate -->
+
     <eg-grid-column path="index" [hidden]="true" [index]="true">
     </eg-grid-column>
-    <eg-grid-column path="copy.id" [hidden]="true" label="Copy ID" i18n-label>
+    <eg-grid-column name="id" path="copy.id" [hidden]="true" label="Copy ID" i18n-label>
     </eg-grid-column>
     <eg-grid-column path="volume.id" [hidden]="true" label="Volume ID" i18n-label>
     </eg-grid-column>
-    <eg-grid-column name="location_barcode" [flex]="4"
+    <eg-grid-column name="owner_label" [flex]="4"
       [cellTemplate]="locationTemplate" [cellContext]="gridTemplateContext" 
       label="Location/Barcode" [disableTooltip]="true" i18n-label>
     </eg-grid-column>
@@ -64,31 +182,59 @@
     </eg-grid-column>
     <eg-grid-column path="copyCount" datatype="number" label="Copies" i18n-label>
     </eg-grid-column>
-    <eg-grid-column path="callNumberLabel" label="Call Number" i18n-label>
+    <eg-grid-column path="volume._label" name="call_number.label" label="Call Number" i18n-label>
+    </eg-grid-column>
+    <eg-grid-column path="copy.barcode" name="barcode" label="Barcode" i18n-label>
     </eg-grid-column>
     <eg-grid-column i18n-label label="Circ Library" path="copy.circ_lib" 
-      datatype="org_unit"></eg-grid-column>
+      name="circ_lib.name" datatype="org_unit"></eg-grid-column>
     <eg-grid-column i18n-label label="Owning Library" path="volume.owning_lib" 
       datatype="org_unit"></eg-grid-column>
     <eg-grid-column i18n-label label="Due Date" path="circ.due_date" 
       datatype="timestamp"></eg-grid-column>
-    <eg-grid-column i18n-label label="Shelving Location" path="copy.location.name">
+    <eg-grid-column i18n-label label="Shelving Location" path="copy.location.name" name="location.name">
     </eg-grid-column>
-    <eg-grid-column i18n-label label="Circulation Modifier" path="copy.circ_modifier">
+    <eg-grid-column i18n-label label="Circulation Modifier" 
+      path="copy.circ_modifier" name="circ_modifier">
+    </eg-grid-column>
+    <eg-grid-column i18n-label label="Copy Number" path="copy.copy_number" name="copy_number" [hidden]="true">
     </eg-grid-column>
 
-    <eg-grid-column i18n-label label="Status" path="copy.status.name">
+    <eg-grid-column i18n-label label="Status" path="copy.status.name" name="status_name">
+    </eg-grid-column>
+    <eg-grid-column i18n-label label="Call Number Prefix" 
+      path="volume.prefix.label" name="call_number.prefix.label" [hidden]="true">
+    </eg-grid-column>
+    <eg-grid-column i18n-label label="Call Number Suffix" 
+      path="volume.suffix.label" name="call_number.suffix.label" [hidden]="true">
     </eg-grid-column>
     <eg-grid-column i18n-label label="Active/Create Date" 
       path="copy.active_date" datatype="timestamp">
     </eg-grid-column>
     <eg-grid-column i18n-label label="Age Hold Protection" 
-      path="copy.age_protect.name"></eg-grid-column>
+      path="copy.age_protect.name" name="age_protect.name"></eg-grid-column>
+    <eg-grid-column i18n-label label="Copy Price" 
+      path="copy.price" name="price" [hidden]="true"></eg-grid-column>
 
+    <eg-grid-column i18n-label label="Circulate" path="copy.circulate" 
+      name="circulate" datatype="bool" [hidden]="true"></eg-grid-column>
+    <eg-grid-column i18n-label label="Deposit" path="copy.deposit" 
+      name="deposit" datatype="bool" [hidden]="true"></eg-grid-column>
+    <eg-grid-column i18n-label label="Deposit Amount" path="copy.deposit_amount" 
+      name="deposit_amount" datatype="money" [hidden]="true"></eg-grid-column>
     <eg-grid-column i18n-label label="Holdable?" name="holdable" 
       [cellTemplate]="holdableTemplate" [cellContext]="gridTemplateContext">
     </eg-grid-column>
-
+    <eg-grid-column i18n-label label="Reference?" path="copy.ref" 
+      name="ref" datatype="bool" [hidden]="true"></eg-grid-column>
+    <eg-grid-column i18n-label label="Last Inventory Date" 
+      path="copy.latest_inventory.inventory_date" 
+      name="latest_inventory.inventory_date" datatype="timestamp" [hidden]="true">
+    </eg-grid-column>
+    <eg-grid-column i18n-label label="Last Inventory Workstation" 
+      path="copy.latest_inventory.inventory_workstation.name" 
+      name="latest_inventory.inventory_workstation.name" [hidden]="true">
+    </eg-grid-column>
   </eg-grid>
 </div>
 
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
index 0f9e4ad990..3e69e90b96 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
@@ -3,16 +3,26 @@ import {Observable, Observer, of} from 'rxjs';
 import {map} from 'rxjs/operators';
 import {Pager} from '@eg/share/util/pager';
 import {IdlObject} from '@eg/core/idl.service';
-import {NetService} from '@eg/core/net.service';
 import {StaffCatalogService} from '../catalog.service';
 import {OrgService} from '@eg/core/org.service';
-import {AuthService} from '@eg/core/auth.service';
 import {PcrudService} from '@eg/core/pcrud.service';
+import {AuthService} from '@eg/core/auth.service';
 import {GridDataSource} from '@eg/share/grid/grid';
 import {GridComponent} from '@eg/share/grid/grid.component';
-import {GridToolbarCheckboxComponent} from '@eg/share/grid/grid-toolbar-checkbox.component';
+import {GridToolbarCheckboxComponent
+    } from '@eg/share/grid/grid-toolbar-checkbox.component';
+import {StoreService} from '@eg/core/store.service';
 import {ServerStoreService} from '@eg/core/server-store.service';
-
+import {MarkDamagedDialogComponent
+    } from '@eg/staff/share/holdings/mark-damaged-dialog.component';
+import {MarkMissingDialogComponent
+    } from '@eg/staff/share/holdings/mark-missing-dialog.component';
+import {AnonCacheService} from '@eg/share/util/anon-cache.service';
+import {HoldingsService} from '@eg/staff/share/holdings/holdings.service';
+import {CopyAlertsDialogComponent
+    } from '@eg/staff/share/holdings/copy-alerts-dialog.component';
+import {ReplaceBarcodeDialogComponent
+    } from '@eg/staff/share/holdings/replace-barcode-dialog.component';
 
 // The holdings grid models a single HoldingsTree, composed of HoldingsTreeNodes
 // flattened on-demand into a list of HoldingEntry objects.
@@ -53,26 +63,44 @@ class HoldingsEntry {
 
 @Component({
   selector: 'eg-holdings-maintenance',
-  templateUrl: 'holdings.component.html'
+  templateUrl: 'holdings.component.html',
+  styleUrls: ['holdings.component.css']
 })
 export class HoldingsMaintenanceComponent implements OnInit {
 
-    recId: number;
     initDone = false;
     gridDataSource: GridDataSource;
     gridTemplateContext: any;
     @ViewChild('holdingsGrid') holdingsGrid: GridComponent;
 
     // Manage visibility of various sub-sections
-    @ViewChild('volsCheckbox') volsCheckbox: GridToolbarCheckboxComponent;
-    @ViewChild('copiesCheckbox') copiesCheckbox: GridToolbarCheckboxComponent;
-    @ViewChild('emptyVolsCheckbox') emptyVolsCheckbox: GridToolbarCheckboxComponent;
-    @ViewChild('emptyLibsCheckbox') emptyLibsCheckbox: GridToolbarCheckboxComponent;
+    @ViewChild('volsCheckbox')
+        private volsCheckbox: GridToolbarCheckboxComponent;
+    @ViewChild('copiesCheckbox')
+        private copiesCheckbox: GridToolbarCheckboxComponent;
+    @ViewChild('emptyVolsCheckbox')
+        private emptyVolsCheckbox: GridToolbarCheckboxComponent;
+    @ViewChild('emptyLibsCheckbox')
+        private emptyLibsCheckbox: GridToolbarCheckboxComponent;
+    @ViewChild('markDamagedDialog')
+        private markDamagedDialog: MarkDamagedDialogComponent;
+    @ViewChild('markMissingDialog')
+        private markMissingDialog: MarkMissingDialogComponent;
+    @ViewChild('copyAlertsDialog')
+        private copyAlertsDialog: CopyAlertsDialogComponent;
+    @ViewChild('replaceBarcode')
+        private replaceBarcode: ReplaceBarcodeDialogComponent;
 
-    contextOrg: IdlObject;
     holdingsTree: HoldingsTree;
-    holdingsTreeOrgCache: {[id: number]: HoldingsTreeNode};
+
+    // nodeType => id => tree node cache
+    treeNodeCache: {[nodeType: string]: {[id: number]: HoldingsTreeNode}};
+
+    // When true and a grid reload is called, the holdings data will be
+    // re-fetched from the server.
     refreshHoldings: boolean;
+
+    // Used as a row identifier in th grid, since we're mixing object types.
     gridIndex: number;
 
     // List of copies whose due date we need to retrieve.
@@ -82,35 +110,54 @@ export class HoldingsMaintenanceComponent implements OnInit {
     // When not true, render based on the current "expanded" state of each node.
     // Rendering from prefs happens on initial load and when any prefs change.
     renderFromPrefs: boolean;
+
     rowClassCallback: (row: any) => string;
 
+    private _recId: number;
     @Input() set recordId(id: number) {
-        this.recId = id;
+        this._recId = id;
         // Only force new data collection when recordId()
         // is invoked after ngInit() has already run.
         if (this.initDone) {
-            this.refreshHoldings = true;
-            this.holdingsGrid.reload();
+            this.hardRefresh();
         }
     }
+    get recordId(): number {
+        return this._recId;
+    }
+
+    contextOrg: IdlObject;
 
     constructor(
-        private net: NetService,
         private org: OrgService,
-        private auth: AuthService,
         private pcrud: PcrudService,
+        private auth: AuthService,
         private staffCat: StaffCatalogService,
-        private store: ServerStoreService
+        private store: ServerStoreService,
+        private localStore: StoreService,
+        private holdings: HoldingsService,
+        private anonCache: AnonCacheService
     ) {
         // Set some sane defaults before settings are loaded.
-        this.contextOrg = this.org.get(this.auth.user().ws_ou());
         this.gridDataSource = new GridDataSource();
         this.refreshHoldings = true;
         this.renderFromPrefs = true;
 
+        // TODO: need a separate setting for this?
+        this.contextOrg = this.staffCat.searchContext.searchOrg;
+
         this.rowClassCallback = (row: any): string => {
-             if (row.volume && !row.copy) {
-                return 'bg-info';
+            if (row.volume) {
+                if (row.copy) {
+                    return 'holdings-copy-row';
+                } else {
+                    return 'holdings-volume-row';
+                }
+            } else {
+                // Add a generic org unit class and a depth-specific
+                // class for styling different levels of the org tree.
+                return 'holdings-org-row holdings-org-row-' +
+                    row.treeNode.target.ou_type().depth();
             }
         }
 
@@ -142,7 +189,7 @@ export class HoldingsMaintenanceComponent implements OnInit {
     ngOnInit() {
         this.initDone = true;
 
-        // These are pre-cached via the resolver.
+        // These are pre-cached via the catalog resolver.
         const settings = this.store.getItemBatchCached([
             'cat.holdings_show_empty_org',
             'cat.holdings_show_empty',
@@ -150,18 +197,31 @@ export class HoldingsMaintenanceComponent implements OnInit {
             'cat.holdings_show_vols'
         ]);
 
-        this.volsCheckbox.checked(settings['cat.holdings_show_vols']);
+        // Show volumes by default when no preference is set.
+        let showVols = settings['cat.holdings_show_vols'];
+        if (showVols === null) { showVols = true; }
+
+        this.volsCheckbox.checked(showVols);
         this.copiesCheckbox.checked(settings['cat.holdings_show_copies']);
         this.emptyVolsCheckbox.checked(settings['cat.holdings_show_empty']);
         this.emptyLibsCheckbox.checked(settings['cat.holdings_show_empty_org']);
 
+        this.initHoldingsTree();
         this.gridDataSource.getRows = (pager: Pager, sort: any[]) => {
             return this.fetchHoldings(pager);
         };
     }
 
-    ngAfterViewInit() {
+    contextOrgChanged(org: IdlObject) {
+        this.contextOrg = org;
+        this.hardRefresh();
+    }
 
+    hardRefresh() {
+        this.renderFromPrefs = true;
+        this.refreshHoldings = true;
+        this.initHoldingsTree();
+        this.holdingsGrid.reload();
     }
 
     toggleShowCopies(value: boolean) {
@@ -210,33 +270,40 @@ export class HoldingsMaintenanceComponent implements OnInit {
 
     initHoldingsTree() {
 
+        const visibleOrgs = this.org.fullPath(this.contextOrg, true);
+
         // The initial tree simply matches the org unit tree
         const traverseOrg = (node: HoldingsTreeNode) => {
-            node.expanded = true;
             node.target.children().forEach((org: IdlObject) => {
+                if (visibleOrgs.indexOf(org.id()) == -1) {
+                    return; // Org is outside of scope
+                }
                 const nodeChild = new HoldingsTreeNode();
                 nodeChild.nodeType = 'org';
                 nodeChild.target = org;
                 nodeChild.parentNode = node;
                 node.children.push(nodeChild);
-                this.holdingsTreeOrgCache[org.id()] = nodeChild;
+                this.treeNodeCache.org[org.id()] = nodeChild;
                 traverseOrg(nodeChild);
             });
         }
 
+        this.treeNodeCache = {
+            org: {},
+            volume: {},
+            copy: {}
+        };
+
         this.holdingsTree = new HoldingsTree();
         this.holdingsTree.root.nodeType = 'org';
         this.holdingsTree.root.target = this.org.root();
-
-        this.holdingsTreeOrgCache = {};
-        this.holdingsTreeOrgCache[this.org.root().id()] = this.holdingsTree.root;
+        this.treeNodeCache.org[this.org.root().id()] = this.holdingsTree.root;
 
         traverseOrg(this.holdingsTree.root);
     }
 
     // Org node children are sorted with any child org nodes pushed to the
     // front, followed by the call number nodes sorted alphabetcially by label.
-    // TODO: prefix/suffix
     sortOrgNodeChildren(node: HoldingsTreeNode) {
         node.children = node.children.sort((a, b) => {
             if (a.nodeType === 'org') {
@@ -248,7 +315,9 @@ export class HoldingsMaintenanceComponent implements OnInit {
             } else if (b.nodeType === 'org') {
                 return 1;
             } else {
-                return a.target.label() < b.target.label() ? -1 : 1;
+                // TODO: should this use label sortkey instead of
+                // the compiled volume label?
+                return a.target._label < b.target._label ? -1 : 1;
             }
         });
     }
@@ -309,7 +378,7 @@ export class HoldingsMaintenanceComponent implements OnInit {
 
         switch(node.nodeType) {
             case 'org':
-                if (this.renderFromPrefs && node.volumeCount === 0
+                if (node.volumeCount === 0
                     && !this.emptyLibsCheckbox.checked()) {
                     return;
                 }
@@ -321,7 +390,16 @@ export class HoldingsMaintenanceComponent implements OnInit {
                 break;
 
             case 'volume':
-                entry.locationLabel = node.target.label(); // TODO prefix/suffix
+                if (this.renderFromPrefs) {
+                    if (!this.volsCheckbox.checked()) {
+                        return;
+                    }
+                    if (node.copyCount === 0
+                        && !this.emptyVolsCheckbox.checked()) {
+                        return;
+                    }
+                }
+                entry.locationLabel = node.target._label;
                 entry.locationDepth = node.parentNode.target.ou_type().depth() + 1;
                 entry.callNumberLabel = entry.locationLabel;
                 entry.volume = node.target;
@@ -357,9 +435,9 @@ export class HoldingsMaintenanceComponent implements OnInit {
         this.renderFromPrefs = false;
     }
 
-
+    // Grab volumes, copies, and related data.
     fetchHoldings(pager: Pager): Observable<any> {
-        if (!this.recId) { return of([]); }
+        if (!this.recordId) { return of([]); }
 
         return new Observable<any>(observer => {
 
@@ -368,12 +446,11 @@ export class HoldingsMaintenanceComponent implements OnInit {
                 return;
             }
 
-            this.initHoldingsTree();
             this.itemCircsNeeded = [];
 
             this.pcrud.search('acn',
-                {   record: this.recId,
-                    owning_lib: this.org.ancestors(this.contextOrg, true),
+                {   record: this.recordId,
+                    owning_lib: this.org.fullPath(this.contextOrg, true),
                     deleted: 'f',
                     label: {'!=' : '##URI##'}
                 }, {
@@ -384,7 +461,8 @@ export class HoldingsMaintenanceComponent implements OnInit {
                         acn: ['prefix', 'suffix', 'copies'],
                         acli: ['inventory_workstation']
                     }
-                }
+                },
+                {authoritative: true}
             ).subscribe(
                 vol => this.appendVolume(vol),
                 err => {},
@@ -413,28 +491,278 @@ export class HoldingsMaintenanceComponent implements OnInit {
         })).toPromise();
     }
 
+    // Compile prefix + label + suffix into field volume._label;
+    setVolumeLabel(volume: IdlObject) {
+        const pfx = volume.prefix() ? volume.prefix().label() : '';
+        const sfx = volume.suffix() ? volume.suffix().label() : '';
+        volume._label = pfx ? pfx + ' ' : '';
+        volume._label += volume.label();
+        volume._label += sfx ? ' ' + sfx : '';
+    }
+
+    // Create the tree node for the volume if it doesn't already exist.
+    // Do the same for its linked copies.
     appendVolume(volume: IdlObject) {
+        let volNode = this.treeNodeCache.volume[volume.id()];
+        this.setVolumeLabel(volume);
+
+        if (volNode) {
+            const pNode = this.treeNodeCache.org[volume.owning_lib()]
+            if (volNode.parentNode.target.id() !== pNode.target.id()) {
+                // Volume owning library changed.  Un-link it from the previous
+                // org unit collection before adding to the new one.
+                // XXX TODO: ^--
+                volNode.parentNode = pNode;
+                volNode.parentNode.children.push(volNode);
+            }
+        } else {
+            volNode = new HoldingsTreeNode();
+            volNode.nodeType = 'volume';
+            volNode.parentNode = this.treeNodeCache.org[volume.owning_lib()]
+            volNode.parentNode.children.push(volNode);
+            this.treeNodeCache.volume[volume.id()] = volNode;
+        }
 
-        const volNode = new HoldingsTreeNode();
-        volNode.parentNode = this.holdingsTreeOrgCache[volume.owning_lib()];
-        volNode.parentNode.children.push(volNode);
-        volNode.nodeType = 'volume';
         volNode.target = volume;
 
         volume.copies()
             .sort((a: IdlObject, b: IdlObject) => a.barcode() < b.barcode() ? -1 : 1)
-            .forEach((copy: IdlObject) => {
-                const copyNode = new HoldingsTreeNode();
+            .forEach((copy: IdlObject) => this.appendCopy(volNode, copy));
+    }
+
+    // Find or create a copy node.
+    appendCopy(volNode: HoldingsTreeNode, copy: IdlObject) {
+        let copyNode = this.treeNodeCache.copy[copy.id()];
+
+        if (copyNode) {
+            const oldParent = copyNode.parentNode;
+            if (oldParent.target.id() !== volNode.target.id()) {
+                // TODO: copy changed owning volume.  Remove it from
+                // the previous volume before adding to the new volume.
                 copyNode.parentNode = volNode;
                 volNode.children.push(copyNode);
-                copyNode.nodeType = 'copy';
-                copyNode.target = copy;
-                const stat = Number(copy.status().id());
-                if (stat === 1 /* checked out */ || stat === 16 /* long overdue */) {
-                    this.itemCircsNeeded.push(copy);
-                }
-            });
+            }
+        } else {
+            // New node required
+            copyNode = new HoldingsTreeNode();
+            copyNode.nodeType = 'copy';
+            volNode.children.push(copyNode);
+            copyNode.parentNode = volNode;
+            this.treeNodeCache.copy[copy.id()] = copyNode;
+        }
+
+        copyNode.target = copy;
+        const stat = Number(copy.status().id());
+
+        if (stat === 1 /* checked out */ || stat === 16 /* long overdue */) {
+            // Avoid looking up circs on items that are not checked out.
+            this.itemCircsNeeded.push(copy);
+        }
     }
-}
 
+    // Which copies in the grid are selected.
+    selectedCopyIds(rows: HoldingsEntry[], skipStatus?: number): number[] {
+        let copyRows = rows.filter(r => Boolean(r.copy)).map(r => r.copy);
+        if (skipStatus) {
+            copyRows = copyRows.filter(
+                c => Number(c.status().id()) !== Number(skipStatus));
+        }
+        return copyRows.map(c => Number(c.id()));
+    }
+
+    selectedVolumeIds(rows: HoldingsEntry[]): number[] {
+        return rows
+            .filter(r => r.treeNode.nodeType === 'volume')
+            .map(r => Number(r.volume.id()));
+    }
+
+    async showMarkDamagedDialog(rows: HoldingsEntry[]) {
+        const copyIds = this.selectedCopyIds(rows, 14 /* ignore damaged */);
+
+        if (copyIds.length === 0) { return; }
+
+        let rowsModified = false;
+
+        const markNext = async(ids: number[]) => {
+            if (ids.length === 0) {
+                return Promise.resolve();
+            }
+
+            this.markDamagedDialog.copyId = ids.pop();
+            return this.markDamagedDialog.open({size: 'lg'}).then(
+                ok => {
+                    if (ok) { rowsModified = true; }
+                    return markNext(ids);
+                },
+                dismiss => markNext(ids)
+            );
+        };
+
+        await markNext(copyIds);
+        if (rowsModified) {
+            this.refreshHoldings = true;
+            this.holdingsGrid.reload();
+        }
+    }
+
+    showMarkMissingDialog(rows: any[]) {
+        const copyIds = this.selectedCopyIds(rows, 4 /* ignore missing */);
+        if (copyIds.length > 0) {
+            this.markMissingDialog.copyIds = copyIds;
+            this.markMissingDialog.open({}).then(
+                rowsModified => {
+                    if (rowsModified) {
+                        this.refreshHoldings = true;
+                        this.holdingsGrid.reload();
+                    }
+                },
+                dismissed => {} // avoid console errors
+            );
+        }
+    }
+
+    // Mark record, library, and potentially the selected call number
+    // as the current transfer target.
+    markLibCnForTransfer(rows: HoldingsEntry[]) {
+        if (rows.length === 0) {
+            return;
+        }
+
+        // Action may only apply to a single org or volume row.
+        const node = rows[0].treeNode;
+        if (node.nodeType === 'copy') {
+            return;
+        }
+
+        let orgId: number;
+
+        if (node.nodeType === 'org') {
+            orgId = node.target.id();
+
+            // Clear volume target when performed on an org unit row
+            this.localStore.removeLocalItem('eg.cat.transfer_target_vol');
 
+        } else if (node.nodeType === 'volume') {
+
+            // All volume nodes are children of org nodes.
+            orgId = node.parentNode.target.id();
+
+            // Add volume target when performed on a volume row.
+            this.localStore.setLocalItem(
+                'eg.cat.transfer_target_vol', node.target.id())
+        }
+
+        this.localStore.setLocalItem('eg.cat.transfer_target_record', this.recordId);
+        this.localStore.setLocalItem('eg.cat.transfer_target_lib', orgId);
+    }
+
+    openAngJsWindow(path: string) {
+        const url = `/eg/staff/${path}`;
+        window.open(url, '_blank');
+    }
+
+    openItemHolds(rows: HoldingsEntry[]) {
+        if (rows.length > 0 && rows[0].copy) {
+            this.openAngJsWindow(`cat/item/${rows[0].copy.id()}/holds`);
+        }
+    }
+
+    openItemStatusList(rows: HoldingsEntry[]) {
+        const ids = this.selectedCopyIds(rows);
+        if (ids.length > 0) {
+            return this.openAngJsWindow(`cat/item/search/${ids.join(',')}`);
+        }
+    }
+
+    openItemStatus(rows: HoldingsEntry[]) {
+        if (rows.length > 0 && rows[0].copy) {
+           return this.openAngJsWindow(`cat/item/${rows[0].copy.id()}`);
+        }
+    }
+
+    openItemTriggeredEvents(rows: HoldingsEntry[]) {
+        if (rows.length > 0 && rows[0].copy) {
+           return this.openAngJsWindow(
+               `cat/item/${rows[0].copy.id()}/triggered_events`);
+        }
+    }
+
+    openItemPrintLabels(rows: HoldingsEntry[]) {
+        const ids = this.selectedCopyIds(rows);
+        if (ids.length === 0) { return; }
+
+        this.anonCache.setItem(null, 'print-labels-these-copies', {copies: ids})
+        .then(key => this.openAngJsWindow(`cat/printlabels/${key}`));
+    }
+
+    openVolCopyEdit(rows: HoldingsEntry[], addVols: boolean, addCopies: boolean) {
+
+        // The user may select a set of volumes by selecting volume and/or
+        // copy rows.
+        const volumes = [];
+        rows.forEach(r => {
+            if (r.treeNode.nodeType === 'volume') {
+                volumes.push(r.volume);
+            } else if (r.treeNode.nodeType === 'copy') {
+                volumes.push(r.treeNode.parentNode.target);
+            }
+        });
+
+        if (addCopies && !addVols) {
+            // Adding copies to an existing set of volumes.
+            if (volumes.length > 0) {
+                const volIds = volumes.map(v => Number(v.id()));
+                this.holdings.spawnAddHoldingsUi(this.recordId, volIds);
+            }
+
+        } else if (addVols) {
+            const entries = [];
+
+            if (volumes.length > 0) {
+
+                // When adding volumes, if any are selected in the grid,
+                // create volumes that have the same label and owner.
+                volumes.forEach(v =>
+                    entries.push({label: v.label(), owner: v.owning_lib()}));
+
+                } else {
+
+                // Otherwise create new volumes from scratch.
+                entries.push({owner: this.auth.user().ws_ou()})
+            }
+
+            this.holdings.spawnAddHoldingsUi(
+                this.recordId, null, entries, !addCopies);
+        }
+    }
+
+    openItemNotes(rows: HoldingsEntry[], mode: string) {
+        const copyIds = this.selectedCopyIds(rows);
+        if (copyIds.length === 0) { return; }
+
+        this.copyAlertsDialog.copyIds = copyIds;
+        this.copyAlertsDialog.mode = mode;
+        this.copyAlertsDialog.open({size: 'lg'}).then(
+            modified => {
+                if (modified) {
+                    this.hardRefresh();
+                }
+            },
+            dismissed => {}
+        )
+    }
+
+    openReplaceBarcodeDialog(rows: HoldingsEntry[]) {
+        const ids = this.selectedCopyIds(rows);
+        if (ids.length === 0) { return; }
+        this.replaceBarcode.copyIds = ids;
+        this.replaceBarcode.open({}).then(
+            modified => {
+                if (modified) {
+                    this.hardRefresh();
+                }
+            },
+            dismissed => {}
+        );
+    }
+}
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/pagination.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/record/pagination.component.ts
index 793767b38f..b3e9a9c53e 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/pagination.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/pagination.component.ts
@@ -18,6 +18,14 @@ export class RecordPaginationComponent implements OnInit {
     initDone = false;
     searchContext: CatalogSearchContext;
 
+    _recordTab: string;
+    @Input() set recordTab(tab: string) {
+        this._recordTab = tab;
+    }
+    get recordTab(): string {
+        return this._recordTab;
+    }
+
     @Input() set recordId(id: number) {
         this.id = id;
         // Only apply new record data after the initial load
@@ -38,41 +46,33 @@ export class RecordPaginationComponent implements OnInit {
         this.setIndex();
     }
 
+    routeToRecord(id: number) {
+        let url = '/staff/catalog/record/' + id;
+        if (this.recordTab) { url += '/' + this.recordTab; }
+        const params = this.catUrl.toUrlParams(this.searchContext);
+        this.router.navigate([url], {queryParams: params});
+    }
+
     firstRecord(): void {
-        this.findRecordAtIndex(0).then(id => {
-            const params = this.catUrl.toUrlParams(this.searchContext);
-            this.router.navigate(
-                ['/staff/catalog/record/' + id], {queryParams: params});
-        });
+        this.findRecordAtIndex(0)
+        .then(id => this.routeToRecord(id));
     }
 
     lastRecord(): void {
-        this.findRecordAtIndex(
-            this.searchContext.result.count - 1
-        ).then(id => {
-            const params = this.catUrl.toUrlParams(this.searchContext);
-            this.router.navigate(
-                ['/staff/catalog/record/' + id], {queryParams: params});
-        });
+        this.findRecordAtIndex(this.searchContext.result.count - 1)
+        .then(id => this.routeToRecord(id));
     }
 
     nextRecord(): void {
-        this.findRecordAtIndex(this.index + 1).then(id => {
-            const params = this.catUrl.toUrlParams(this.searchContext);
-            this.router.navigate(
-                ['/staff/catalog/record/' + id], {queryParams: params});
-        });
+        this.findRecordAtIndex(this.index + 1)
+        .then(id => this.routeToRecord(id));
     }
 
     prevRecord(): void {
-        this.findRecordAtIndex(this.index - 1).then(id => {
-            const params = this.catUrl.toUrlParams(this.searchContext);
-            this.router.navigate(
-                ['/staff/catalog/record/' + id], {queryParams: params});
-        });
+        this.findRecordAtIndex(this.index - 1)
+        .then(id => this.routeToRecord(id));
     }
 
-
     // Returns the offset of the record within the search results as a whole.
     searchIndex(idx: number): number {
         return idx + this.searchContext.pager.offset;
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html
index b583cf7e71..450887034d 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html
@@ -3,7 +3,7 @@
   <div class="row ml-0 mr-0">
     <div id='staff-catalog-bib-navigation'>
       <div *ngIf="searchContext.isSearchable()">
-        <eg-catalog-record-pagination [recordId]="recordId">
+        <eg-catalog-record-pagination [recordId]="recordId" [recordTab]="recordTab">
         </eg-catalog-record-pagination>
       </div>
     </div>
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/resolver.service.ts b/Open-ILS/src/eg2/src/app/staff/catalog/resolver.service.ts
index f4f5d9719b..0a09cbdca7 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/resolver.service.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/resolver.service.ts
@@ -45,7 +45,6 @@ export class CatalogResolver implements Resolve<Promise<any[]>> {
             'cat.holdings_show_empty',
             'cat.marcedit.stack_subfields',
             'cat.marcedit.flateditor',
-            'eg.cat.record.summary.collapse',
             'cat.holdings_show_copies',
             'cat.holdings_show_vols'
         ]).then(settings => {
diff --git a/Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.html b/Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.html
index d49de1bd7d..530e1080e2 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.html
@@ -8,12 +8,12 @@
     <div>
       <a class="with-material-icon no-href text-primary" 
         title="Show More" i18n-title
-        *ngIf="!expandDisplay" (click)="expandDisplay=true">
+        *ngIf="!expand" (click)="expand=true">
         <span class="material-icons">expand_more</span>
       </a>
       <a class="with-material-icon no-href text-primary" 
         title="Show Less" i18n-title
-        *ngIf="expandDisplay" (click)="expandDisplay=false">
+        *ngIf="expand" (click)="expand=false">
         <span class="material-icons">expand_less</span>
       </a>
     </div>
@@ -36,7 +36,7 @@
           </div>
         </div>
       </li>
-      <li class="list-group-item" *ngIf="expandDisplay">
+      <li class="list-group-item" *ngIf="expand">
         <div class="d-flex">
           <div class="flex-1 font-weight-bold" i18n>Author:</div>
           <div class="flex-3">{{summary.display.author}}</div>
@@ -52,7 +52,7 @@
           </div>
         </div>
       </li>
-      <li class="list-group-item" *ngIf="expandDisplay">
+      <li class="list-group-item" *ngIf="expand">
         <div class="d-flex">
           <div class="flex-1 font-weight-bold" i18n>Bib Call #:</div>
           <div class="flex-3">{{summary.bibCallNumber}}</div>
diff --git a/Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.ts b/Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.ts
index 645b56cd78..954cb8bfe3 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.ts
@@ -1,9 +1,8 @@
 import {Component, OnInit, Input} from '@angular/core';
-import {NetService} from '@eg/core/net.service';
 import {OrgService} from '@eg/core/org.service';
-import {PcrudService} from '@eg/core/pcrud.service';
-import {CatalogService} from '@eg/share/catalog/catalog.service';
-import {BibRecordService, BibRecordSummary} from '@eg/share/catalog/bib-record.service';
+import {BibRecordService, BibRecordSummary
+    } from '@eg/share/catalog/bib-record.service';
+import {ServerStoreService} from '@eg/core/server-store.service';
 
 @Component({
   selector: 'eg-bib-summary',
@@ -13,10 +12,16 @@ import {BibRecordService, BibRecordSummary} from '@eg/share/catalog/bib-record.s
 export class BibSummaryComponent implements OnInit {
 
     initDone = false;
-    expandDisplay = true;
-    @Input() set expand(e: boolean) {
-        this.expandDisplay = e;
+
+    // True / false if the display is vertically expanded
+    private _exp: boolean;
+    set expand(e: boolean) {
+        this._exp = e;
+        if (this.initDone) {
+            this.saveExpandState();
+        }
     }
+    get expand(): boolean { return this._exp; }
 
     // If provided, the record will be fetched by the component.
     @Input() recordId: number;
@@ -32,14 +37,12 @@ export class BibSummaryComponent implements OnInit {
 
     constructor(
         private bib: BibRecordService,
-        private cat: CatalogService,
-        private net: NetService,
         private org: OrgService,
-        private pcrud: PcrudService
+        private store: ServerStoreService
     ) {}
 
     ngOnInit() {
-        this.initDone = true;
+
         if (this.summary) {
             this.summary.getBibCallNumber();
         } else {
@@ -47,6 +50,14 @@ export class BibSummaryComponent implements OnInit {
                 this.loadSummary();
             }
         }
+
+        this.store.getItem('eg.cat.record.summary.collapse')
+        .then(value => this.expand = !value)
+        .then(() => this.initDone = true);
+    }
+
+    saveExpandState() {
+        this.store.setItem('eg.cat.record.summary.collapse', !this.expand);
     }
 
     loadSummary(): void {
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/copy-alerts-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/share/holdings/copy-alerts-dialog.component.html
new file mode 100644
index 0000000000..4b3c1caab1
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/copy-alerts-dialog.component.html
@@ -0,0 +1,109 @@
+<eg-string #successMsg text="Successfully Modified Copy Alerts" i18n-text></eg-string>
+<eg-string #errorMsg text="Failed To Modify Copy Alerts" i18n-text></eg-string>
+
+<ng-template #dialogContent>
+  <div class="modal-header">
+    <h4 class="modal-title">
+      <ng-container *ngIf="mode == 'create'">
+        <span i18n>Adding alerts for {{copies.length}} item(s).</span>
+      </ng-container>
+      <ng-container *ngIf="mode == 'manage'">
+        <span i18n>Managing alerts for item {{copies[0].barcode()}}</span>
+      </ng-container>
+      <span i18n></span>
+    </h4>
+    <button type="button" class="close" 
+      i18n-aria-label aria-label="Close" (click)="dismiss('cross_click')">
+      <span aria-hidden="true">×</span>
+    </button>
+  </div>
+  <div class="modal-body p-4 form-validated">
+    <div class="row mt-2 p-2 rounded border border-success">
+      <div class="col-lg-4">
+        <eg-combobox [entries]="alertTypes" 
+          i18n-placeholder placeholder="New Alert Type..."
+          [required]="true"
+          (onChange)="newAlert.alert_type($event ? $event.id : null)">
+        </eg-combobox>
+      </div>
+      <div class="col-lg-5">
+        <textarea class="form-control" rows="2" 
+          i18n-placeholder placeholder="New Alert Note..."
+          (ngModelChange)="newAlert.note($event)" [ngModel]="newAlert.note()">
+        </textarea>
+      </div>
+      <div class="col-lg-3">
+        <div class="d-flex flex-column">
+          <div class="form-check">
+            <input class="form-check-input" type="checkbox" 
+              [ngModel]="newAlert.temp() == 't'" 
+              (ngModelChange)="newAlert.temp($event ? 't' : 'f')"
+              id="new-alert-temporary">
+            <label class="form-check-label" for="new-alert-temporary" i18n>
+              Temporary?
+            </label>
+          </div>
+          <div class="pt-2">
+            <button class="btn btn-success" (click)="addNew()" i18n>
+              Add New
+            </button>
+          </div>  
+        </div>  
+      </div>
+    </div>
+    <ng-container *ngIf="mode == 'manage'">
+      <!-- in manage mode list all of the alerts linked to the copy -->
+      <div class="row mt-2" 
+        *ngFor="let alert of copy.copy_alerts()">
+        <div class="col-lg-12 pb-2"><hr/></div>
+        <div class="col-lg-4">
+          <eg-combobox [entries]="alertTypes" [startId]="alert.alert_type()"
+            i18n-placeholder placeholder="Alert Type..."
+            [required]="true"
+            (onChange)="alert.alert_type($event ? $event.id : null); alert.ischanged(true)">
+          </eg-combobox>
+          <div class="pl-2 pt-2" i18n>
+            Added: {{alert.create_time() | date:'shortDate'}}
+          </div>
+        </div>
+        <div class="col-lg-5">
+          <textarea class="form-control" rows="2" 
+            i18n-placeholder placeholder="Alert Note..."
+            (ngModelChange)="alert.note($event); alert.ischanged(true)"
+            [ngModel]="alert.note()">
+          </textarea>
+        </div>
+        <div class="col-lg-3">
+          <div class="d-flex flex-column">
+            <div class="form-check">
+              <input class="form-check-input" type="checkbox" 
+                [ngModel]="alert.temp() == 't'" 
+                (ngModelChange)="alert.temp($event ? 't' : 'f'); alert.ischanged(true)"
+                id="alert-temporary-{{alert.id()}}">
+              <label class="form-check-label" for="alert-temporary-{{alert.id()}}" i18n>
+                Temporary?
+              </label>
+            </div>
+            <div class="form-check pt-2">
+              <input class="form-check-input" type="checkbox" 
+                [ngModel]="alert.ack_time() != null" 
+                (ngModelChange)="alert.ack_time($event ? 'now' : null); alert.ischanged(true)"
+                id="alert-temporary-{{alert.id()}}">
+              <label class="form-check-label" for="alert-temporary-{{alert.id()}}" i18n>
+                Clear?
+              </label>
+            </div>
+          </div>
+        </div>
+      </div>
+    </ng-container>
+  </div>
+  <div class="modal-footer">
+    <button type="button" class="btn btn-secondary" 
+      (click)="dismiss('canceled')" i18n>Close</button>
+    <ng-container *ngIf="mode == 'manage'">
+      <button class="btn btn-success mr-2" 
+        (click)="applyChanges()" i18n>Apply Changes</button>
+    </ng-container>
+  </div>
+</ng-template>
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/copy-alerts-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/share/holdings/copy-alerts-dialog.component.ts
new file mode 100644
index 0000000000..e0ce7637b0
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/copy-alerts-dialog.component.ts
@@ -0,0 +1,185 @@
+import {Component, OnInit, Input, ViewChild} from '@angular/core';
+import {NetService} from '@eg/core/net.service';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {EventService} from '@eg/core/event.service';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {AuthService} from '@eg/core/auth.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {OrgService} from '@eg/core/org.service';
+import {StringComponent} from '@eg/share/string/string.component';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
+import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
+import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
+
+/**
+ * Dialog for managing copy alerts.
+ */
+
+ at Component({
+  selector: 'eg-copy-alerts-dialog',
+  templateUrl: 'copy-alerts-dialog.component.html'
+})
+
+export class CopyAlertsDialogComponent
+    extends DialogComponent implements OnInit {
+
+    _copyIds: number[];
+    @Input() set copyIds(ids: number[]) {
+        this._copyIds = [].concat(ids);
+    }
+    get copyIds(): number[] {
+        return this._copyIds;
+    }
+
+    _mode: string; // create | manage
+    @Input() set mode(m: string) {
+        this._mode = m;
+    }
+    get mode(): string {
+        return this._mode;
+    }
+
+    // In 'create' mode, we may be adding notes to multiple copies.
+    copies: IdlObject[];
+    // In 'manage' mode we only handle a single copy.
+    copy: IdlObject;
+    alertTypes: ComboboxEntry[];
+    newAlert: IdlObject;
+    changesMade: boolean;
+
+    @ViewChild('successMsg') private successMsg: StringComponent;
+    @ViewChild('errorMsg') private errorMsg: StringComponent;
+    @ViewChild('confirmDeleteDialog')
+        private confirmDeleteDialog: ConfirmDialogComponent;
+
+    constructor(
+        private modal: NgbModal, // required for passing to parent
+        private toast: ToastService,
+        private net: NetService,
+        private idl: IdlService,
+        private pcrud: PcrudService,
+        private org: OrgService,
+        private auth: AuthService) {
+        super(modal); // required for subclassing
+        this.copyIds = [];
+        this.copies = [];
+    }
+
+    ngOnInit() {}
+
+    /**
+     * Fetch the item/record, then open the dialog.
+     * Dialog promise resolves with true/false indicating whether
+     * the mark-damanged action occured or was dismissed.
+     */
+    async open(args: NgbModalOptions): Promise<boolean> {
+        this.copy = null;
+        this.copies = [];
+        this.newAlert = this.idl.create('aca');
+        this.newAlert.create_staff(this.auth.user().id());
+
+        if (this.copyIds.length === 0) {
+            return Promise.reject('copy ID required');
+        }
+
+        // In manage mode, we can only manage a single copy.
+        // But in create mode, we can add alerts to multiple copies.
+
+        if (this.mode === 'manage') {
+            if (this.copyIds.length > 1) {
+                console.warn('Attempt to manage alerts for multiple copies.');
+                this.copyIds = [this.copyIds[0]];
+            }
+        }
+
+        await this.getAlertTypes();
+        await this.getCopies();
+        if (this.mode === 'manage') {
+            await this.getCopyAlerts();
+        }
+        return super.open(args);
+    }
+
+    async getAlertTypes(): Promise<any> {
+        if (this.alertTypes) {
+            return Promise.resolve();
+        }
+        return this.pcrud.retrieveAll('ccat',
+        {   active: true,
+            scope_org: this.org.ancestors(this.auth.user().ws_ou(), true)
+        }, {atomic: true}
+        ).toPromise().then(alerts => {
+            this.alertTypes = alerts.map(a => ({id: a.id(), label: a.name()}));
+        });
+    }
+
+    async getCopies(): Promise<any> {
+        return this.pcrud.search('acp', {id: this.copyIds}, {}, {atomic: true})
+        .toPromise().then(copies => {
+            this.copies = copies;
+            copies.forEach(c => c.copy_alerts([]));
+            if (this.mode === 'manage') {
+                this.copy = copies[0];
+            }
+        });
+    }
+
+    // Copy alerts for the selected copies which have not been
+    // acknowledged by staff and are within org unit range of
+    // the alert type.
+    async getCopyAlerts(): Promise<any> {
+        const copyIds = this.copies.map(c => c.id());
+        const typeIds = this.alertTypes.map(a => a.id);
+
+        return this.pcrud.search('aca',
+            {copy: copyIds, ack_time: null, alert_type: typeIds},
+            {}, {atomic: true})
+        .toPromise().then(alerts => {
+            alerts.forEach(a => {
+                const copy = this.copies.filter(c => c.id() === a.copy())[0];
+                copy.copy_alerts().push(a);
+            });
+        });
+    }
+
+    // Add the in-progress new note to all copies.
+    addNew() {
+        if (!this.newAlert.alert_type()) { return; }
+
+        const alerts: IdlObject[] = [];
+        this.copies.forEach(c => {
+            const a = this.idl.clone(this.newAlert);
+            a.copy(c.id());
+            alerts.push(a);
+        });
+
+        this.pcrud.create(alerts).toPromise().then(
+            newAlert => {
+                this.successMsg.current().then(msg => this.toast.success(msg));
+                this.changesMade = true;
+                if (this.mode === 'create') {
+                    // In create mode, we assume the user wants to create
+                    // a single alert and be done with it.
+                    this.close(this.changesMade);
+                } else {
+                    // Otherwise, add the alert to the copy
+                    this.copy.copy_alerts().push(newAlert);
+                }
+            },
+            err => {
+                this.errorMsg.current().then(msg => this.toast.danger(msg))
+            }
+        );
+    }
+
+    applyChanges() {
+        const alerts = this.copy.copy_alerts().filter(a => a.ischanged());
+        if (alerts.length === 0) { return ;}
+        this.pcrud.update(alerts).toPromise().then(
+            ok => this.successMsg.current().then(msg => this.toast.success(msg)),
+            err => this.errorMsg.current().then(msg => this.toast.danger(msg))
+        )
+    }
+}
+
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.module.ts b/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.module.ts
index 382e9060d7..c931a2fc27 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.module.ts
+++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.module.ts
@@ -3,18 +3,24 @@ import {StaffCommonModule} from '@eg/staff/common.module';
 import {HoldingsService} from './holdings.service';
 import {MarkDamagedDialogComponent} from './mark-damaged-dialog.component';
 import {MarkMissingDialogComponent} from './mark-missing-dialog.component';
+import {CopyAlertsDialogComponent} from './copy-alerts-dialog.component';
+import {ReplaceBarcodeDialogComponent} from './replace-barcode-dialog.component';
 
 @NgModule({
     declarations: [
       MarkDamagedDialogComponent,
-      MarkMissingDialogComponent
+      MarkMissingDialogComponent,
+      CopyAlertsDialogComponent,
+      ReplaceBarcodeDialogComponent
     ],
     imports: [
         StaffCommonModule
     ],
     exports: [
       MarkDamagedDialogComponent,
-      MarkMissingDialogComponent
+      MarkMissingDialogComponent,
+      CopyAlertsDialogComponent,
+      ReplaceBarcodeDialogComponent
     ],
     providers: [
         HoldingsService
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.service.ts b/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.service.ts
index 4b28f70369..87b0ff1a9a 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.service.ts
+++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.service.ts
@@ -24,9 +24,10 @@ export class HoldingsService {
 
     // Open the holdings editor UI in a new browser window/tab.
     spawnAddHoldingsUi(
-        recordId: number,                   // Bib record ID
-        addToVols: number[] = [],           // Add copies to existing volumes
-        volumeData: NewVolumeData[] = []) { // Creating new volumes
+        recordId: number,               // Bib record ID
+        addToVols?: number[],           // Add copies to / modify existing vols
+        volumeData?: NewVolumeData[],   // Creating new volumes
+        hideCopies?: boolean) {         // Hide the copy edit pane
 
         const raw: any[] = [];
 
@@ -42,7 +43,7 @@ export class HoldingsService {
             record_id: recordId,
             raw: raw,
             hide_vols : false,
-            hide_copies : false
+            hide_copies : hideCopies ? true : false
         }).then(key => {
             if (!key) {
                 console.error('Could not create holds cache key!');
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/replace-barcode-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/share/holdings/replace-barcode-dialog.component.html
new file mode 100644
index 0000000000..562681bcd9
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/replace-barcode-dialog.component.html
@@ -0,0 +1,50 @@
+
+
+<eg-string #successMsg
+    text="Successfully Replaced Barcode" i18n-text></eg-string>
+<eg-string #errorMsg 
+    text="Failed To Replace Barcode" i18n-text></eg-string>
+
+<ng-template #dialogContent>
+    <div class="modal-header bg-info">
+      <h4 class="modal-title">
+        <span i18n>Replace Item Barcode</span>
+      </h4>
+      <button type="button" class="close" 
+        i18n-aria-label aria-label="Close" (click)="dismiss('cross_click')">
+        <span aria-hidden="true">×</span>
+      </button>
+    </div>
+    <div class="modal-body">
+      <div class="row">
+        <div class="col-lg-4" i18n>Replacing barcode</div>
+        <div class="col-lg-8 font-weight-bold">{{copy.barcode()}}</div>
+      </div>
+      <div class="row pt-2 form-validated">
+        <div class="col-lg-4" i18n>
+          <label for="new-barcode-intput">New Barcode:</label>
+        </div>
+        <div class="col-log-8">
+          <input type="text" class="form-control" [required]="true"
+            [(ngModel)]="newBarcode" (keyup)="barcodeExists=false" 
+            id="new-barcode-input"/>
+        </div>
+      </div>
+      <div class="row d-flex pt-2 justify-content-center" *ngIf="barcodeExists">
+        <div class="alert alert-danger" i18n>
+          Barcode <span class="font-weight-bold">{{newBarcode}}</span> is already in use.
+        </div>
+      </div>
+    </div>
+    <div class="modal-footer">
+      <ng-container>
+        <button type="button" class="btn btn-warning" 
+          (click)="dismiss('canceled')" i18n>Cancel</button>
+        <button type="button" class="btn btn-success" 
+          (click)="replaceOneBarcode()" [disabled]="!newBarcode" i18n>
+          Replace Barcode
+        </button>
+      </ng-container>
+    </div>
+  </ng-template>
+  
\ No newline at end of file
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/replace-barcode-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/share/holdings/replace-barcode-dialog.component.ts
new file mode 100644
index 0000000000..552922e9a0
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/replace-barcode-dialog.component.ts
@@ -0,0 +1,111 @@
+import {Component, OnInit, Input, ViewChild, Renderer2} from '@angular/core';
+import {IdlObject} from '@eg/core/idl.service';
+import {NetService} from '@eg/core/net.service';
+import {EventService} from '@eg/core/event.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {AuthService} from '@eg/core/auth.service';
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+import {StringComponent} from '@eg/share/string/string.component';
+
+
+/**
+ * Dialog for marking items missing.
+ */
+
+ at Component({
+  selector: 'eg-replace-barcode-dialog',
+  templateUrl: 'replace-barcode-dialog.component.html'
+})
+
+export class ReplaceBarcodeDialogComponent
+    extends DialogComponent implements OnInit {
+
+    @Input() copyIds: number[];
+    ids: number[]; // copy of list so we can pop()
+
+    copy: IdlObject;
+    newBarcode: string;
+    barcodeExists: boolean;
+
+    numSucceeded: number;
+    numFailed: number;
+
+    @ViewChild('successMsg')
+        private successMsg: StringComponent;
+
+    @ViewChild('errorMsg')
+        private errorMsg: StringComponent;
+
+    constructor(
+        private modal: NgbModal, // required for passing to parent
+        private toast: ToastService,
+        private net: NetService,
+        private pcrud: PcrudService,
+        private evt: EventService,
+        private renderer: Renderer2,
+        private auth: AuthService) {
+        super(modal); // required for subclassing
+    }
+
+    ngOnInit() {}
+
+    async open(args: NgbModalOptions): Promise<boolean> {
+        this.ids = [].concat(this.copyIds);
+        this.numSucceeded = 0;
+        this.numFailed = 0;
+
+        await this.getNextCopy();
+        setTimeout(() =>
+            // Give the dialog a chance to render
+            this.renderer.selectRootElement('#new-barcode-input').focus()
+        );
+        return super.open(args);
+    }
+
+    async getNextCopy(): Promise<any> {
+
+        if (this.ids.length === 0) {
+            this.close(this.numSucceeded > 0);
+            return Promise.resolve();
+        }
+
+        this.newBarcode = '';
+
+        const id = this.ids.pop();
+
+        return this.pcrud.retrieve('acp', id)
+        .toPromise().then(c => this.copy = c);
+    }
+
+    async replaceOneBarcode(): Promise<any> {
+        this.barcodeExists = false;
+
+        // First see if the barcode is in use
+        return this.pcrud.search('acp', {deleted: 'f', barcode: this.newBarcode})
+        .toPromise().then(async (existing) => {
+            if (existing) {
+                this.barcodeExists = true;
+                return;
+            }
+
+            this.copy.barcode(this.newBarcode);
+            this.pcrud.update(this.copy).toPromise().then(
+                async (ok) => {
+                    this.numSucceeded++;
+                    this.toast.success(await this.successMsg.current());
+                    this.getNextCopy();
+                },
+                async (err) => {
+                    this.numFailed++;
+                    console.error('Replace barcode failed: ', err);
+                    this.toast.warning(await this.errorMsg.current());
+                }
+            )
+        })
+    }
+}
+
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holds/grid.component.ts b/Open-ILS/src/eg2/src/app/staff/share/holds/grid.component.ts
index af27574528..a95ed8572f 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/holds/grid.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/share/holds/grid.component.ts
@@ -335,7 +335,7 @@ export class HoldsGridComponent implements OnInit {
             }
 
             this.markDamagedDialog.copyId = ids.pop();
-            this.markDamagedDialog.open({size: 'lg'}).then(
+            return this.markDamagedDialog.open({size: 'lg'}).then(
                 ok => {
                     if (ok) { rowsModified = true; }
                     return markNext(ids);
@@ -397,3 +397,5 @@ export class HoldsGridComponent implements OnInit {
 }
 
 
+
+

commit 3c3d0678b67425f3f9dcdca760cdbb6b33752203
Author: Bill Erickson <berickxx at gmail.com>
Date:   Wed Mar 20 12:06:20 2019 -0400

    LP1821382 Angular boolean yes/no display component
    
    Simple component that accepts a boolean value and displays a yes/no
    badge.  Added as the default handler for boolean columsn in the grid.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/common.module.ts b/Open-ILS/src/eg2/src/app/common.module.ts
index f1d4467830..a4e402680d 100644
--- a/Open-ILS/src/eg2/src/app/common.module.ts
+++ b/Open-ILS/src/eg2/src/app/common.module.ts
@@ -25,6 +25,7 @@ import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
 import {PromptDialogComponent} from '@eg/share/dialog/prompt.component';
 import {ProgressInlineComponent} from '@eg/share/dialog/progress-inline.component';
 import {ProgressDialogComponent} from '@eg/share/dialog/progress.component';
+import {BoolDisplayComponent} from '@eg/share/util/bool.component';
 
 @NgModule({
   declarations: [
@@ -35,6 +36,7 @@ import {ProgressDialogComponent} from '@eg/share/dialog/progress.component';
     PromptDialogComponent,
     ProgressInlineComponent,
     ProgressDialogComponent,
+    BoolDisplayComponent,
     FormatValuePipe
   ],
   imports: [
@@ -55,6 +57,7 @@ import {ProgressDialogComponent} from '@eg/share/dialog/progress.component';
     PromptDialogComponent,
     ProgressInlineComponent,
     ProgressDialogComponent,
+    BoolDisplayComponent,
     FormatValuePipe
   ]
 })
diff --git a/Open-ILS/src/eg2/src/app/core/format.service.ts b/Open-ILS/src/eg2/src/app/core/format.service.ts
index 8108eec91b..d2b2ce51e2 100644
--- a/Open-ILS/src/eg2/src/app/core/format.service.ts
+++ b/Open-ILS/src/eg2/src/app/core/format.service.ts
@@ -119,7 +119,8 @@ export class FormatService {
 
             case 'bool':
                 // Slightly better than a bare 't' or 'f'.
-                // Should probably add a global true/false string.
+                // Note the caller is better off using an <eg-bool/> for
+                // boolean display.
                 return Boolean(
                     value === 't' || value === 1 ||
                     value === '1' || value === true
diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid-body-cell.component.html b/Open-ILS/src/eg2/src/app/share/grid/grid-body-cell.component.html
index 6dc4a99b56..921fafc459 100644
--- a/Open-ILS/src/eg2/src/app/share/grid/grid-body-cell.component.html
+++ b/Open-ILS/src/eg2/src/app/share/grid/grid-body-cell.component.html
@@ -4,7 +4,14 @@
   placement="top-left"
   class="{{context.cellClassCallback(row, column)}}"
   triggers="mouseenter:mouseleave">
-  {{context.getRowColumnValue(row, column)}}
+  <ng-container *ngIf="column.datatype == 'bool'">
+    <eg-bool [value]="context.getRowColumnValue(row, column)"
+      [ternary]="column.ternaryBool">
+    </eg-bool>
+  </ng-container>
+  <ng-container *ngIf="column.datatype != 'bool'">
+    {{context.getRowColumnValue(row, column)}}
+  </ng-container>
 </span>
 <span *ngIf="column.cellTemplate" 
   class="{{context.cellClassCallback(row, column)}}"
diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid-column.component.ts b/Open-ILS/src/eg2/src/app/share/grid/grid-column.component.ts
index 76a89f61bf..fc18fc7258 100644
--- a/Open-ILS/src/eg2/src/app/share/grid/grid-column.component.ts
+++ b/Open-ILS/src/eg2/src/app/share/grid/grid-column.component.ts
@@ -24,6 +24,9 @@ export class GridColumnComponent implements OnInit {
     @Input() datatype: string;
     @Input() multiSortable: boolean;
 
+    // If true, boolean fields support 3 values: true, false, null (unset)
+    @Input() ternaryBool: boolean;
+
     // Display date and time when datatype = timestamp
     @Input() datePlusTime: boolean;
 
@@ -57,6 +60,7 @@ export class GridColumnComponent implements OnInit {
         col.isMultiSortable = this.multiSortable;
         col.datatype = this.datatype;
         col.datePlusTime = this.datePlusTime;
+        col.ternaryBool = this.ternaryBool;
         col.isAuto = false;
         this.grid.context.columnSet.add(col);
     }
diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.ts b/Open-ILS/src/eg2/src/app/share/grid/grid.ts
index 64f3321fa7..7cf5f53487 100644
--- a/Open-ILS/src/eg2/src/app/share/grid/grid.ts
+++ b/Open-ILS/src/eg2/src/app/share/grid/grid.ts
@@ -26,6 +26,7 @@ export class GridColumn {
     idlFieldDef: any;
     datatype: string;
     datePlusTime: boolean;
+    ternaryBool: boolean;
     cellTemplate: TemplateRef<any>;
     cellContext: any;
     isIndex: boolean;
@@ -658,6 +659,12 @@ export class GridContext {
             val = this.getObjectFieldValue(row, col.name);
         }
 
+        if (col.datatype === 'bool') {
+            // Avoid string-ifying bools so we can use an <eg-bool/>
+            // in the grid template.
+            return val;
+        }
+
         return this.format.transform({
             value: val,
             idlClass: col.idlClass,
diff --git a/Open-ILS/src/eg2/src/app/share/util/bool.component.ts b/Open-ILS/src/eg2/src/app/share/util/bool.component.ts
new file mode 100644
index 0000000000..37d2e8d7d7
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/share/util/bool.component.ts
@@ -0,0 +1,39 @@
+import {Component, Input} from '@angular/core';
+
+/* Simple component to render a boolean value as human-friendly text */
+
+ at Component({
+    selector: 'eg-bool',
+    template: `
+      <ng-container>
+        <span *ngIf="value" class="badge badge-success" i18n>Yes</span>
+        <span *ngIf="value == false" class="badge badge-secondary" i18n>No</span>
+        <ng-container *ngIf="value === null">
+          <span *ngIf="ternary" class="badge badge-light" i18n>Unset</span>
+          <span *ngIf="!ternary"> </span>
+      </ng-container>`
+})
+export class BoolDisplayComponent {
+
+    _value: boolean;
+    @Input() set value(v: boolean) {
+        this._value = v;
+    }
+    get value(): boolean {
+        return this._value;
+    }
+
+    // If true, a null value displays as unset.
+    // If false, a null value displays as an empty string.
+    _ternary: boolean;
+    @Input() set ternary(t: boolean) {
+        this._ternary = t;
+    }
+    get ternary(): boolean {
+        return this._ternary;
+    }
+
+    constructor() {
+        this.value = null;
+    }
+}
\ No newline at end of file

commit dfbc0644b41da7b3433a44f109b3b40cdb2d8ea9
Author: Bill Erickson <berickxx at gmail.com>
Date:   Tue Mar 19 11:47:52 2019 -0400

    LP1821382 Angular grid inline load progress indicator.
    
    Display an indeterminate progress indicator while the grid is waiting
    for data to arrive.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.component.html b/Open-ILS/src/eg2/src/app/share/grid/grid.component.html
index dd462465b0..4a6ae6ec9b 100644
--- a/Open-ILS/src/eg2/src/app/share/grid/grid.component.html
+++ b/Open-ILS/src/eg2/src/app/share/grid/grid.component.html
@@ -16,15 +16,15 @@
   </eg-grid-print>
 
   <ng-container *ngIf="dataSource.data.length == 0">
-    <div class="row">>
+    <div class="row">
       <ng-container *ngIf="dataSource.requestingData">
         <div class="col-lg-6 offset-lg-3 text-center mt-3">
-         <eg-progress-inline></eg-progress-inline>
+          <eg-progress-inline></eg-progress-inline>
         </div>
       </ng-container>
       <ng-container *ngIf="!dataSource.requestingData">
-        <div class="col-lg-12 text-center alert alert-light font-italic" i18n>
-          Nothing to Display
+        <div class="col-lg-12 text-center alert alert-light font-italic">
+          <span i18n>Nothing to Display</span>
         </div>
       </ng-container>
     </div>

commit 6e14b5a7622d2b1ebe3541404a02f8cc2aa9a755
Author: Bill Erickson <berickxx at gmail.com>
Date:   Mon Mar 18 17:46:34 2019 -0400

    LP1821382 Angular grid optoin to disable paging
    
    Hides the paging controls in the toolbar and puts the grid into "fetch
    all" mode.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.html b/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.html
index af223fefdf..68f7fade5f 100644
--- a/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.html
+++ b/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.html
@@ -56,6 +56,8 @@
     </div>
   </div>
 
+  <ng-container *ngIf="!gridContext.disablePaging">
+
   <button [disabled]="gridContext.pager.isFirstPage()" type="button"
     class="btn btn-outline-dark mr-1" (click)="gridContext.pager.toFirst()">
     <span title="First Page" i18n-title
@@ -71,17 +73,6 @@
     <span title="Next Page" i18n-title
         class="material-icons mat-icon-in-button">keyboard_arrow_right</span>
   </button>
-
-  <!--
-  Hiding jump-to-last since there's no analog in the angularjs grid and
-  it has limited value since the size of the data set is often unknown.
-  <button [disabled]="!gridContext.pager.resultCount || gridContext.pager.isLastPage()"
-    type="button" class="btn btn-outline-dark mr-1" (click)="gridContext.pager.toLast()">
-    <span title="First Page" i18n-title
-        class="material-icons mat-icon-in-button">last_page</span>
-  </button>
-  -->
-
   <div ngbDropdown class="mr-1" placement="bottom-right">
     <button ngbDropdownToggle class="btn btn-outline-dark text-button">
       <span title="Select Row Count" i18n-title i18n>
@@ -96,6 +87,9 @@
       </a>
     </div>
   </div>
+  
+  </ng-container><!-- if disablePaging-->
+
 
   <button type="button"
     class="btn btn-outline-dark mr-1"
diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts b/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts
index 25b8d0d2cb..e1f988ff9f 100644
--- a/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts
+++ b/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts
@@ -96,6 +96,8 @@ export class GridComponent implements OnInit, AfterViewInit, OnDestroy {
     // field on the "aout" class.
     @Input() showLinkSelectors: boolean;
 
+    @Input() disablePaging: boolean;
+
     context: GridContext;
 
     // These events are emitted from our grid-body component.
@@ -132,6 +134,7 @@ export class GridComponent implements OnInit, AfterViewInit, OnDestroy {
         this.context.disableMultiSelect = this.disableMultiSelect === true;
         this.context.rowFlairIsEnabled = this.rowFlairIsEnabled  === true;
         this.context.rowFlairCallback = this.rowFlairCallback;
+        this.context.disablePaging = this.disablePaging === true;
         if (this.showFields) {
             this.context.defaultVisibleFields = this.showFields.split(',');
         }
diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.ts b/Open-ILS/src/eg2/src/app/share/grid/grid.ts
index 8017938ff6..64f3321fa7 100644
--- a/Open-ILS/src/eg2/src/app/share/grid/grid.ts
+++ b/Open-ILS/src/eg2/src/app/share/grid/grid.ts
@@ -442,6 +442,7 @@ export class GridContext {
     defaultHiddenFields: string[];
     overflowCells: boolean;
     showLinkSelectors: boolean;
+    disablePaging: boolean;
 
     // Allow calling code to know when the select-all-rows-in-page
     // action has occurred.
@@ -464,7 +465,6 @@ export class GridContext {
         this.store = store;
         this.format = format;
         this.pager = new Pager();
-        this.pager.limit = 10;
         this.rowSelector = new GridRowSelector();
         this.toolbarButtons = [];
         this.toolbarCheckboxes = [];
@@ -478,6 +478,9 @@ export class GridContext {
         this.columnSet.isMultiSortable = this.isMultiSortable === true;
         this.columnSet.defaultHiddenFields = this.defaultHiddenFields;
         this.columnSet.defaultVisibleFields = this.defaultVisibleFields;
+        if (!this.pager.limit) {
+            this.pager.limit = this.disablePaging ? MAX_ALL_ROW_COUNT : 10;
+        }
         this.generateColumns();
     }
 

commit 4bea10e45c6b1eeea1e42d08a1534a6f199458b8
Author: Bill Erickson <berickxx at gmail.com>
Date:   Fri Mar 15 17:01:13 2019 -0400

    LP1821382 Angular staff catalog Holdings Maintenance
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/eg2/src/app/core/server-store.service.ts b/Open-ILS/src/eg2/src/app/core/server-store.service.ts
index ea2d93da36..b54a4c9a87 100644
--- a/Open-ILS/src/eg2/src/app/core/server-store.service.ts
+++ b/Open-ILS/src/eg2/src/app/core/server-store.service.ts
@@ -60,6 +60,22 @@ export class ServerStoreService {
         );
     }
 
+    // Sync call for items known to be cached locally.
+    getItemCached(key: string): any {
+        return this.cache[key];
+    }
+
+    // Sync batch call for items known to be cached locally
+    getItemBatchCached(keys: string[]): {[key: string]: any} {
+        const values: any = {};
+        keys.forEach(key => {
+            if (key in this.cache) {
+                values[key] = this.cache[key];
+            }
+        });
+        return values;
+    }
+
     // Returns a set of key/value pairs for the requested settings
     getItemBatch(keys: string[]): Promise<any> {
 
diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid-body-cell.component.html b/Open-ILS/src/eg2/src/app/share/grid/grid-body-cell.component.html
index 578bef5e9c..6dc4a99b56 100644
--- a/Open-ILS/src/eg2/src/app/share/grid/grid-body-cell.component.html
+++ b/Open-ILS/src/eg2/src/app/share/grid/grid-body-cell.component.html
@@ -1,6 +1,6 @@
 
 <span *ngIf="!column.cellTemplate"
-  [ngbTooltip]="context.getRowColumnValue(row, column)"
+  [ngbTooltip]="column.disableTooltip ? null : context.getRowColumnValue(row, column)"
   placement="top-left"
   class="{{context.cellClassCallback(row, column)}}"
   triggers="mouseenter:mouseleave">
@@ -8,7 +8,7 @@
 </span>
 <span *ngIf="column.cellTemplate" 
   class="{{context.cellClassCallback(row, column)}}"
-  [ngbTooltip]="column.cellTemplate"
+  [ngbTooltip]="column.disableTooltip ? null : column.cellTemplate"
   placement="top-left"
   #tooltip="ngbTooltip" 
   (mouseenter)="tooltip.open(column.getCellContext(row))"
diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid-column.component.ts b/Open-ILS/src/eg2/src/app/share/grid/grid-column.component.ts
index 4cebd48211..76a89f61bf 100644
--- a/Open-ILS/src/eg2/src/app/share/grid/grid-column.component.ts
+++ b/Open-ILS/src/eg2/src/app/share/grid/grid-column.component.ts
@@ -31,6 +31,8 @@ export class GridColumnComponent implements OnInit {
     @Input() cellContext: any;
     @Input() cellTemplate: TemplateRef<any>;
 
+    @Input() disableTooltip: boolean;
+
     // get a reference to our container grid.
     constructor(@Host() private grid: GridComponent) {}
 
@@ -50,6 +52,7 @@ export class GridColumnComponent implements OnInit {
         col.isIndex = this.index === true;
         col.cellTemplate = this.cellTemplate;
         col.cellContext = this.cellContext;
+        col.disableTooltip = this.disableTooltip;
         col.isSortable = this.sortable;
         col.isMultiSortable = this.multiSortable;
         col.datatype = this.datatype;
diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-checkbox.component.ts b/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-checkbox.component.ts
index 7ee3019477..24b7616e86 100644
--- a/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-checkbox.component.ts
+++ b/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-checkbox.component.ts
@@ -16,29 +16,48 @@ export class GridToolbarCheckboxComponent implements OnInit {
     // This does NOT fire the onChange handler.
     @Input() initialValue: boolean;
 
-    // This is an input instead of an Output because the handler is
-    // passed off to the grid context for maintenance -- events
-    // are not fired directly from this component.
     @Output() onChange: EventEmitter<boolean>;
 
+    private cb: GridToolbarCheckbox;
+
     // get a reference to our container grid.
     constructor(@Host() private grid: GridComponent) {
         this.onChange = new EventEmitter<boolean>();
+
+        // Create in constructor so we can accept values before the
+        // grid is fully rendered.
+        this.cb = new GridToolbarCheckbox();
+        this.cb.isChecked = null;
+        this.initialValue = null;
     }
 
     ngOnInit() {
-
         if (!this.grid) {
             console.warn('GridToolbarCheckboxComponent needs a [grid]');
             return;
         }
 
-        const cb = new GridToolbarCheckbox();
-        cb.label = this.label;
-        cb.onChange = this.onChange;
-        cb.isChecked = this.initialValue;
+        this.cb.label = this.label;
+        this.cb.onChange = this.onChange;
+
+        if (this.cb.isChecked === null && this.initialValue !== null) {
+            this.cb.isChecked = this.initialValue;
+        }
+
+        this.grid.context.toolbarCheckboxes.push(this.cb);
+    }
 
-        this.grid.context.toolbarCheckboxes.push(cb);
+    // Toggle the value.  onChange is not fired.
+    toggle() {
+        this.cb.isChecked = !this.cb.isChecked;
+    }
+
+    // Set/get the value.  onChange is not fired.
+    checked(value?: boolean): boolean {
+        if (value === true || value === false) {
+            this.cb.isChecked = value;
+        }
+        return this.cb.isChecked;
     }
 }
 
diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.component.html b/Open-ILS/src/eg2/src/app/share/grid/grid.component.html
index 77ea0e6fb1..dd462465b0 100644
--- a/Open-ILS/src/eg2/src/app/share/grid/grid.component.html
+++ b/Open-ILS/src/eg2/src/app/share/grid/grid.component.html
@@ -15,12 +15,20 @@
   <eg-grid-print #gridPrinter [gridContext]="context">
   </eg-grid-print>
 
-  <!-- move me too -->
-  <div class="row" *ngIf="dataSource.data.length == 0">
-    <div class="col-lg-12 text-center alert alert-light font-italic" i18n>
-      Nothing to Display
+  <ng-container *ngIf="dataSource.data.length == 0">
+    <div class="row">>
+      <ng-container *ngIf="dataSource.requestingData">
+        <div class="col-lg-6 offset-lg-3 text-center mt-3">
+         <eg-progress-inline></eg-progress-inline>
+        </div>
+      </ng-container>
+      <ng-container *ngIf="!dataSource.requestingData">
+        <div class="col-lg-12 text-center alert alert-light font-italic" i18n>
+          Nothing to Display
+        </div>
+      </ng-container>
     </div>
-  </div>
+  </ng-container>
 
   <eg-grid-body [context]="context"></eg-grid-body>
 </div>
diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts b/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts
index 66686ef2e8..25b8d0d2cb 100644
--- a/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts
+++ b/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts
@@ -79,6 +79,8 @@ export class GridComponent implements OnInit, AfterViewInit, OnDestroy {
     // Allow the caller to jump directly to a specific page of
     // grid data.
     @Input() pageOffset: number;
+    // Pass in a default page size.  May be overridden by settings.
+    @Input() pageSize: number;
 
     // If true and an idlClass is specificed, the grid assumes
     // datatype=link fields that link to classes which define a selector
@@ -141,6 +143,10 @@ export class GridComponent implements OnInit, AfterViewInit, OnDestroy {
             this.context.pager.offset = this.pageOffset;
         }
 
+        if (this.pageSize) {
+            this.context.pager.limit = this.pageSize;
+        }
+
         // TS doesn't seem to like: let foo = bar || () => '';
         this.context.rowClassCallback =
             this.rowClassCallback || function () { return ''; };
diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.ts b/Open-ILS/src/eg2/src/app/share/grid/grid.ts
index fbc020895d..8017938ff6 100644
--- a/Open-ILS/src/eg2/src/app/share/grid/grid.ts
+++ b/Open-ILS/src/eg2/src/app/share/grid/grid.ts
@@ -32,6 +32,7 @@ export class GridColumn {
     isDragTarget: boolean;
     isSortable: boolean;
     isMultiSortable: boolean;
+    disableTooltip: boolean;
     comparator: (valueA: any, valueB: any) => number;
 
     // True if the column was automatically generated.
@@ -1033,6 +1034,7 @@ export class GridDataSource {
     data: any[];
     sort: any[];
     allRowsRetrieved: boolean;
+    requestingData: boolean;
     getRows: (pager: Pager, sort: any[]) => Observable<any>;
 
     constructor() {
@@ -1068,16 +1070,23 @@ export class GridDataSource {
             return Promise.resolve();
         }
 
+        // If we have to call out for data, set inFetch
+        this.requestingData = true;
+
         return new Promise((resolve, reject) => {
             let idx = pager.offset;
             return this.getRows(pager, this.sort).subscribe(
-                row => this.data[idx++] = row,
+                row => {
+                    this.data[idx++] = row;
+                    this.requestingData = false;
+                },
                 err => {
                     console.error(`grid getRows() error ${err}`);
                     reject(err);
                 },
                 ()  => {
                     this.checkAllRetrieved(pager, idx);
+                    this.requestingData = false;
                     resolve();
                 }
             );
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
index b158ac1442..1855000f60 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
@@ -21,6 +21,7 @@ import {PartsComponent} from './record/parts.component';
 import {PartMergeDialogComponent} from './record/part-merge-dialog.component';
 import {BrowseComponent} from './browse.component';
 import {BrowseResultsComponent} from './browse/results.component';
+import {HoldingsMaintenanceComponent} from './record/holdings.component';
 
 @NgModule({
   declarations: [
@@ -40,6 +41,7 @@ import {BrowseResultsComponent} from './browse/results.component';
     PartMergeDialogComponent,
     BrowseComponent,
     BrowseResultsComponent,
+    HoldingsMaintenanceComponent
   ],
   imports: [
     StaffCommonModule,
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
new file mode 100644
index 0000000000..3cfcb273a5
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
@@ -0,0 +1,94 @@
+
+<ng-template #locationTemplate let-row="row" let-userContext="userContext">
+  <!-- pl-* is doubled for added impact -->
+  <div class="pl-{{row.locationDepth}}">
+    <span class="pl-{{row.locationDepth}}">
+      <a class="label-with-material-icon" (click)="userContext.toggleExpandRow(row)">
+        <!--  leave the icons in place for all node types, but make them
+              invisible when they are not needed. -->
+        <span *ngIf="row.treeNode.expanded"
+          [ngClass]="{invisible: row.copy || row.treeNode.children.length == 0}"
+          class="material-icons p-0 m-0">arrow_drop_down</span>
+        <span *ngIf="!row.treeNode.expanded"
+          [ngClass]="{invisible: row.copy || row.treeNode.children.length == 0}"
+          class="material-icons p-0 m-0">arrow_right</span>
+        <span>{{row.locationLabel}}</span>
+      </a>
+    </span>
+  </div>
+</ng-template>
+
+<ng-template #holdableTemplate let-row="row" let-userContext="userContext">
+  <ng-container *ngIf="row.copy">
+    <ng-container *ngIf="userContext.copyIsHoldable(row.copy); else notHoldable">
+      <span i18n>Yes</span>
+    </ng-container>
+    <ng-template #notHoldable><span i18n>No</span></ng-template>
+  </ng-container>
+</ng-template>
+
+
+<div class='eg-copies w-100 mt-3'>
+  <eg-grid #holdingsGrid [dataSource]="gridDataSource"
+    (onRowActivate)="onRowActivate($event)"
+    [pageSize]="50" [rowClassCallback]="rowClassCallback"
+    [sortable]="false" persistKey="cat.holdings">
+
+    <!-- checkboxes -->
+
+    <eg-grid-toolbar-checkbox i18n-label label="Show Volumes" 
+      #volsCheckbox (onChange)="toggleShowVolumes($event)">
+    </eg-grid-toolbar-checkbox> 
+    <eg-grid-toolbar-checkbox i18n-label label="Show Copies" 
+      #copiesCheckbox (onChange)="toggleShowCopies($event)">
+    </eg-grid-toolbar-checkbox> 
+    <eg-grid-toolbar-checkbox i18n-label label="Show Empty Volumes"            
+      #emptyVolsCheckbox (onChange)="toggleShowEmptyVolumes($event)">
+    </eg-grid-toolbar-checkbox> 
+    <eg-grid-toolbar-checkbox i18n-label label="Show Empty Libs"            
+      #emptyLibsCheckbox (onChange)="toggleShowEmptyLibs($event)">
+    </eg-grid-toolbar-checkbox> 
+
+    <!-- fields -->
+    <eg-grid-column path="index" [hidden]="true" [index]="true">
+    </eg-grid-column>
+    <eg-grid-column path="copy.id" [hidden]="true" label="Copy ID" i18n-label>
+    </eg-grid-column>
+    <eg-grid-column path="volume.id" [hidden]="true" label="Volume ID" i18n-label>
+    </eg-grid-column>
+    <eg-grid-column name="location_barcode" [flex]="4"
+      [cellTemplate]="locationTemplate" [cellContext]="gridTemplateContext" 
+      label="Location/Barcode" [disableTooltip]="true" i18n-label>
+    </eg-grid-column>
+    <eg-grid-column path="volumeCount" datatype="number" label="Volumes" i18n-label>
+    </eg-grid-column>
+    <eg-grid-column path="copyCount" datatype="number" label="Copies" i18n-label>
+    </eg-grid-column>
+    <eg-grid-column path="callNumberLabel" label="Call Number" i18n-label>
+    </eg-grid-column>
+    <eg-grid-column i18n-label label="Circ Library" path="copy.circ_lib" 
+      datatype="org_unit"></eg-grid-column>
+    <eg-grid-column i18n-label label="Owning Library" path="volume.owning_lib" 
+      datatype="org_unit"></eg-grid-column>
+    <eg-grid-column i18n-label label="Due Date" path="circ.due_date" 
+      datatype="timestamp"></eg-grid-column>
+    <eg-grid-column i18n-label label="Shelving Location" path="copy.location.name">
+    </eg-grid-column>
+    <eg-grid-column i18n-label label="Circulation Modifier" path="copy.circ_modifier">
+    </eg-grid-column>
+
+    <eg-grid-column i18n-label label="Status" path="copy.status.name">
+    </eg-grid-column>
+    <eg-grid-column i18n-label label="Active/Create Date" 
+      path="copy.active_date" datatype="timestamp">
+    </eg-grid-column>
+    <eg-grid-column i18n-label label="Age Hold Protection" 
+      path="copy.age_protect.name"></eg-grid-column>
+
+    <eg-grid-column i18n-label label="Holdable?" name="holdable" 
+      [cellTemplate]="holdableTemplate" [cellContext]="gridTemplateContext">
+    </eg-grid-column>
+
+  </eg-grid>
+</div>
+
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
new file mode 100644
index 0000000000..0f9e4ad990
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
@@ -0,0 +1,440 @@
+import {Component, OnInit, Input, ViewChild} from '@angular/core';
+import {Observable, Observer, of} from 'rxjs';
+import {map} from 'rxjs/operators';
+import {Pager} from '@eg/share/util/pager';
+import {IdlObject} from '@eg/core/idl.service';
+import {NetService} from '@eg/core/net.service';
+import {StaffCatalogService} from '../catalog.service';
+import {OrgService} from '@eg/core/org.service';
+import {AuthService} from '@eg/core/auth.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {GridDataSource} from '@eg/share/grid/grid';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {GridToolbarCheckboxComponent} from '@eg/share/grid/grid-toolbar-checkbox.component';
+import {ServerStoreService} from '@eg/core/server-store.service';
+
+
+// The holdings grid models a single HoldingsTree, composed of HoldingsTreeNodes
+// flattened on-demand into a list of HoldingEntry objects.
+class HoldingsTreeNode {
+    children: HoldingsTreeNode[];
+    nodeType: 'org' | 'volume' | 'copy';
+    target: any;
+    parentNode: HoldingsTreeNode;
+    expanded: boolean;
+    copyCount: number;
+    volumeCount: number;
+    constructor() {
+        this.children = [];
+    }
+}
+
+class HoldingsTree {
+    root: HoldingsTreeNode;
+    constructor() {
+        this.root = new HoldingsTreeNode();
+    }
+}
+
+class HoldingsEntry {
+    index: number;
+    // org unit shortname, call number label, or copy barcode
+    locationLabel: string;
+    // location label indentation depth
+    locationDepth: number | null;
+    volumeCount: number | null;
+    copyCount: number | null;
+    callNumberLabel: string;
+    copy: IdlObject;
+    volume: IdlObject;
+    circ: IdlObject;
+    treeNode: HoldingsTreeNode;
+}
+
+ at Component({
+  selector: 'eg-holdings-maintenance',
+  templateUrl: 'holdings.component.html'
+})
+export class HoldingsMaintenanceComponent implements OnInit {
+
+    recId: number;
+    initDone = false;
+    gridDataSource: GridDataSource;
+    gridTemplateContext: any;
+    @ViewChild('holdingsGrid') holdingsGrid: GridComponent;
+
+    // Manage visibility of various sub-sections
+    @ViewChild('volsCheckbox') volsCheckbox: GridToolbarCheckboxComponent;
+    @ViewChild('copiesCheckbox') copiesCheckbox: GridToolbarCheckboxComponent;
+    @ViewChild('emptyVolsCheckbox') emptyVolsCheckbox: GridToolbarCheckboxComponent;
+    @ViewChild('emptyLibsCheckbox') emptyLibsCheckbox: GridToolbarCheckboxComponent;
+
+    contextOrg: IdlObject;
+    holdingsTree: HoldingsTree;
+    holdingsTreeOrgCache: {[id: number]: HoldingsTreeNode};
+    refreshHoldings: boolean;
+    gridIndex: number;
+
+    // List of copies whose due date we need to retrieve.
+    itemCircsNeeded: IdlObject[];
+
+    // When true draw the grid based on the stored preferences.
+    // When not true, render based on the current "expanded" state of each node.
+    // Rendering from prefs happens on initial load and when any prefs change.
+    renderFromPrefs: boolean;
+    rowClassCallback: (row: any) => string;
+
+    @Input() set recordId(id: number) {
+        this.recId = id;
+        // Only force new data collection when recordId()
+        // is invoked after ngInit() has already run.
+        if (this.initDone) {
+            this.refreshHoldings = true;
+            this.holdingsGrid.reload();
+        }
+    }
+
+    constructor(
+        private net: NetService,
+        private org: OrgService,
+        private auth: AuthService,
+        private pcrud: PcrudService,
+        private staffCat: StaffCatalogService,
+        private store: ServerStoreService
+    ) {
+        // Set some sane defaults before settings are loaded.
+        this.contextOrg = this.org.get(this.auth.user().ws_ou());
+        this.gridDataSource = new GridDataSource();
+        this.refreshHoldings = true;
+        this.renderFromPrefs = true;
+
+        this.rowClassCallback = (row: any): string => {
+             if (row.volume && !row.copy) {
+                return 'bg-info';
+            }
+        }
+
+        this.gridTemplateContext = {
+            toggleExpandRow: (row: HoldingsEntry) => {
+                row.treeNode.expanded = !row.treeNode.expanded;
+
+                if (!row.treeNode.expanded) {
+                    // When collapsing a node, all child nodes should be
+                    // collapsed as well.
+                    const traverse = (node: HoldingsTreeNode) => {
+                        node.expanded = false;
+                        node.children.forEach(traverse);
+                    }
+                    traverse(row.treeNode);
+                }
+
+                this.holdingsGrid.reload();
+            },
+
+            copyIsHoldable: (copy: IdlObject): boolean => {
+                return copy.holdable() === 't'
+                    && copy.location().holdable() === 't'
+                    && copy.status().holdable() === 't';
+            }
+        }
+    }
+
+    ngOnInit() {
+        this.initDone = true;
+
+        // These are pre-cached via the resolver.
+        const settings = this.store.getItemBatchCached([
+            'cat.holdings_show_empty_org',
+            'cat.holdings_show_empty',
+            'cat.holdings_show_copies',
+            'cat.holdings_show_vols'
+        ]);
+
+        this.volsCheckbox.checked(settings['cat.holdings_show_vols']);
+        this.copiesCheckbox.checked(settings['cat.holdings_show_copies']);
+        this.emptyVolsCheckbox.checked(settings['cat.holdings_show_empty']);
+        this.emptyLibsCheckbox.checked(settings['cat.holdings_show_empty_org']);
+
+        this.gridDataSource.getRows = (pager: Pager, sort: any[]) => {
+            return this.fetchHoldings(pager);
+        };
+    }
+
+    ngAfterViewInit() {
+
+    }
+
+    toggleShowCopies(value: boolean) {
+        this.store.setItem('cat.holdings_show_copies', value);
+        if (value) {
+            // Showing copies implies showing volumes
+            this.volsCheckbox.checked(true);
+        }
+        this.renderFromPrefs = true;
+        this.holdingsGrid.reload();
+    }
+
+    toggleShowVolumes(value: boolean) {
+        this.store.setItem('cat.holdings_show_vols', value);
+        if (!value) {
+            // Hiding volumes implies hiding empty vols and copies.
+            this.copiesCheckbox.checked(false);
+            this.emptyVolsCheckbox.checked(false);
+        }
+        this.renderFromPrefs = true;
+        this.holdingsGrid.reload();
+    }
+
+    toggleShowEmptyVolumes(value: boolean) {
+        this.store.setItem('cat.holdings_show_empty', value);
+        if (value) {
+            this.volsCheckbox.checked(true);
+        }
+        this.renderFromPrefs = true;
+        this.holdingsGrid.reload();
+    }
+
+    toggleShowEmptyLibs(value: boolean) {
+        this.store.setItem('cat.holdings_show_empty_org', value);
+        this.renderFromPrefs = true;
+        this.holdingsGrid.reload();
+    }
+
+    onRowActivate(row: any) {
+        if (row.copy) {
+            // Launch copy editor?
+        } else {
+            this.gridTemplateContext.toggleExpandRow(row);
+        }
+    }
+
+    initHoldingsTree() {
+
+        // The initial tree simply matches the org unit tree
+        const traverseOrg = (node: HoldingsTreeNode) => {
+            node.expanded = true;
+            node.target.children().forEach((org: IdlObject) => {
+                const nodeChild = new HoldingsTreeNode();
+                nodeChild.nodeType = 'org';
+                nodeChild.target = org;
+                nodeChild.parentNode = node;
+                node.children.push(nodeChild);
+                this.holdingsTreeOrgCache[org.id()] = nodeChild;
+                traverseOrg(nodeChild);
+            });
+        }
+
+        this.holdingsTree = new HoldingsTree();
+        this.holdingsTree.root.nodeType = 'org';
+        this.holdingsTree.root.target = this.org.root();
+
+        this.holdingsTreeOrgCache = {};
+        this.holdingsTreeOrgCache[this.org.root().id()] = this.holdingsTree.root;
+
+        traverseOrg(this.holdingsTree.root);
+    }
+
+    // Org node children are sorted with any child org nodes pushed to the
+    // front, followed by the call number nodes sorted alphabetcially by label.
+    // TODO: prefix/suffix
+    sortOrgNodeChildren(node: HoldingsTreeNode) {
+        node.children = node.children.sort((a, b) => {
+            if (a.nodeType === 'org') {
+                if (b.nodeType === 'org') {
+                    return a.target.shortname() < b.target.shortname() ? -1 : 1;
+                } else {
+                    return -1;
+                }
+            } else if (b.nodeType === 'org') {
+                return 1;
+            } else {
+                return a.target.label() < b.target.label() ? -1 : 1;
+            }
+        });
+    }
+
+    // Sets call number and copy count sums to nodes that need it.
+    // Applies the initial expansed state of each container node.
+    setTreeCounts(node: HoldingsTreeNode) {
+
+        if (node.nodeType === 'org') {
+            node.copyCount = 0;
+            node.volumeCount = 0;
+        } else if(node.nodeType === 'volume') {
+            node.copyCount = 0;
+        }
+
+        let hasChildOrgWithData = false;
+        let hasChildOrgSansData = false;
+        node.children.forEach(child => {
+            this.setTreeCounts(child);
+            if (node.nodeType === 'org') {
+                node.copyCount += child.copyCount;
+                if (child.nodeType === 'volume') {
+                    node.volumeCount++;
+                } else {
+                    hasChildOrgWithData = child.volumeCount > 0;
+                    hasChildOrgSansData = child.volumeCount === 0;
+                    node.volumeCount += child.volumeCount;
+                }
+            } else if (node.nodeType === 'volume') {
+                node.copyCount = node.children.length;
+                if (this.renderFromPrefs) {
+                    node.expanded = this.copiesCheckbox.checked();
+                }
+            }
+        });
+
+        if (this.renderFromPrefs && node.nodeType === 'org') {
+            if (node.copyCount > 0 && this.volsCheckbox.checked()) {
+                node.expanded = true;
+            } else if (node.volumeCount > 0 && this.emptyVolsCheckbox.checked()) {
+                node.expanded = true;
+            } else if (hasChildOrgWithData) {
+                node.expanded = true;
+            } else if (hasChildOrgSansData && this.emptyLibsCheckbox.checked()) {
+                node.expanded = true;
+            } else {
+                node.expanded = false;
+            }
+        }
+    }
+
+    // Create HoldingsEntry objects for tree nodes that should be displayed
+    // and relays them to the grid via the observer.
+    propagateTreeEntries(observer: Observer<HoldingsEntry>, node: HoldingsTreeNode) {
+        const entry = new HoldingsEntry();
+        entry.treeNode = node;
+        entry.index = this.gridIndex++;
+
+        switch(node.nodeType) {
+            case 'org':
+                if (this.renderFromPrefs && node.volumeCount === 0
+                    && !this.emptyLibsCheckbox.checked()) {
+                    return;
+                }
+                entry.locationLabel = node.target.shortname();
+                entry.locationDepth = node.target.ou_type().depth();
+                entry.copyCount = node.copyCount;
+                entry.volumeCount = node.volumeCount;
+                this.sortOrgNodeChildren(node);
+                break;
+
+            case 'volume':
+                entry.locationLabel = node.target.label(); // TODO prefix/suffix
+                entry.locationDepth = node.parentNode.target.ou_type().depth() + 1;
+                entry.callNumberLabel = entry.locationLabel;
+                entry.volume = node.target;
+                entry.copyCount = node.copyCount;
+                break;
+
+            case 'copy':
+                entry.locationLabel = node.target.barcode();
+                entry.locationDepth = node.parentNode.parentNode.target.ou_type().depth() + 2;
+                entry.callNumberLabel = node.parentNode.target.label() // TODO
+                entry.volume = node.parentNode.target;
+                entry.copy = node.target;
+                entry.circ = node.target._circ;
+                break;
+        }
+
+        // Tell the grid about the node entry
+        observer.next(entry);
+
+        if (node.expanded) {
+            // Process the child nodes.
+            node.children.forEach(child =>
+                this.propagateTreeEntries(observer, child));
+        }
+    }
+
+    // Turns the tree into a list of entries for grid display
+    flattenHoldingsTree(observer: Observer<HoldingsEntry>) {
+        this.gridIndex = 0;
+        this.setTreeCounts(this.holdingsTree.root);
+        this.propagateTreeEntries(observer, this.holdingsTree.root);
+        observer.complete();
+        this.renderFromPrefs = false;
+    }
+
+
+    fetchHoldings(pager: Pager): Observable<any> {
+        if (!this.recId) { return of([]); }
+
+        return new Observable<any>(observer => {
+
+            if (!this.refreshHoldings) {
+                this.flattenHoldingsTree(observer);
+                return;
+            }
+
+            this.initHoldingsTree();
+            this.itemCircsNeeded = [];
+
+            this.pcrud.search('acn',
+                {   record: this.recId,
+                    owning_lib: this.org.ancestors(this.contextOrg, true),
+                    deleted: 'f',
+                    label: {'!=' : '##URI##'}
+                }, {
+                    flesh: 3,
+                    flesh_fields: {
+                        acp: ['status', 'location', 'circ_lib', 'parts',
+                            'age_protect', 'copy_alerts', 'latest_inventory'],
+                        acn: ['prefix', 'suffix', 'copies'],
+                        acli: ['inventory_workstation']
+                    }
+                }
+            ).subscribe(
+                vol => this.appendVolume(vol),
+                err => {},
+                ()  => {
+                    this.refreshHoldings = false;
+                    this.fetchCircs().then(
+                        ok => this.flattenHoldingsTree(observer)
+                    );
+                }
+            );
+        });
+    }
+
+    // Retrieve circulation objects for checked out items.
+    fetchCircs(): Promise<any> {
+        const copyIds = this.itemCircsNeeded.map(copy => copy.id());
+        if (copyIds.length === 0) { return Promise.resolve(); }
+
+        return this.pcrud.search('circ', {
+            target_copy: copyIds,
+            checkin_time: null
+        }).pipe(map(circ => {
+            const copy = this.itemCircsNeeded.filter(
+                c => Number(c.id()) === Number(circ.target_copy()))[0];
+            copy._circ = circ;
+        })).toPromise();
+    }
+
+    appendVolume(volume: IdlObject) {
+
+        const volNode = new HoldingsTreeNode();
+        volNode.parentNode = this.holdingsTreeOrgCache[volume.owning_lib()];
+        volNode.parentNode.children.push(volNode);
+        volNode.nodeType = 'volume';
+        volNode.target = volume;
+
+        volume.copies()
+            .sort((a: IdlObject, b: IdlObject) => a.barcode() < b.barcode() ? -1 : 1)
+            .forEach((copy: IdlObject) => {
+                const copyNode = new HoldingsTreeNode();
+                copyNode.parentNode = volNode;
+                volNode.children.push(copyNode);
+                copyNode.nodeType = 'copy';
+                copyNode.target = copy;
+                const stat = Number(copy.status().id());
+                if (stat === 1 /* checked out */ || stat === 16 /* long overdue */) {
+                    this.itemCircsNeeded.push(copy);
+                }
+            });
+    }
+}
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html
index ad75118182..b583cf7e71 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html
@@ -66,13 +66,8 @@
       </ngb-tab>
       <ngb-tab title="Holdings View" i18n-title id="holdings">
         <ng-template ngbTabContent>
-          <div class="alert alert-info mt-3" i18n>
-            Holdings not yet implemented.  See the
-            <a target="_blank"
-              href="/eg/staff/cat/catalog/record/{{recordId}}/holdings">
-              AngularJS Holdings Tab.
-            </a>
-          </div>
+          <eg-holdings-maintenance [recordId]="recordId">
+          </eg-holdings-maintenance>
         </ng-template>
       </ngb-tab>
       <ngb-tab title="Conjoined Items" i18n-title id="conjoined">
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/resolver.service.ts b/Open-ILS/src/eg2/src/app/staff/catalog/resolver.service.ts
index 7dde4b4635..f4f5d9719b 100644
--- a/Open-ILS/src/eg2/src/app/staff/catalog/resolver.service.ts
+++ b/Open-ILS/src/eg2/src/app/staff/catalog/resolver.service.ts
@@ -40,7 +40,14 @@ export class CatalogResolver implements Resolve<Promise<any[]>> {
 
         return this.store.getItemBatch([
             'eg.search.search_lib',
-            'eg.search.pref_lib'
+            'eg.search.pref_lib',
+            'cat.holdings_show_empty_org',
+            'cat.holdings_show_empty',
+            'cat.marcedit.stack_subfields',
+            'cat.marcedit.flateditor',
+            'eg.cat.record.summary.collapse',
+            'cat.holdings_show_copies',
+            'cat.holdings_show_vols'
         ]).then(settings => {
             this.staffCat.defaultSearchOrg =
                 this.org.get(settings['eg.search.search_lib']);

-----------------------------------------------------------------------

Summary of changes:
 Open-ILS/examples/fm_IDL.xml                       |    2 +-
 Open-ILS/src/eg2/package-lock.json                 | 3620 ++++++++++----------
 Open-ILS/src/eg2/src/app/common.module.ts          |    3 +
 Open-ILS/src/eg2/src/app/core/format.service.ts    |    3 +-
 .../src/eg2/src/app/core/server-store.service.ts   |   16 +
 .../src/app/share/accesskey/accesskey.service.ts   |    4 +-
 .../src/app/share/catalog/catalog-url.service.ts   |   15 +
 .../eg2/src/app/share/catalog/catalog.service.ts   |   13 +-
 .../eg2/src/app/share/catalog/search-context.ts    |   19 +-
 .../src/app/share/combobox/combobox.component.ts   |    2 +-
 .../app/share/grid/grid-body-cell.component.html   |   13 +-
 .../src/app/share/grid/grid-column.component.ts    |    7 +
 .../share/grid/grid-toolbar-action.component.ts    |   32 +-
 .../share/grid/grid-toolbar-checkbox.component.ts  |   37 +-
 .../src/app/share/grid/grid-toolbar.component.html |   20 +-
 .../src/eg2/src/app/share/grid/grid.component.html |   18 +-
 .../src/eg2/src/app/share/grid/grid.component.ts   |   14 +
 Open-ILS/src/eg2/src/app/share/grid/grid.ts        |   31 +-
 .../src/eg2/src/app/share/util/bool.component.ts   |   40 +
 Open-ILS/src/eg2/src/app/share/util/hash-params.ts |   29 +
 .../app/staff/cat/vandelay/export.component.html   |  115 +-
 .../src/app/staff/cat/vandelay/export.component.ts |   68 +-
 .../app/staff/cat/vandelay/queue.component.html    |    4 +-
 .../src/app/staff/cat/vandelay/routing.module.ts   |    3 +
 .../staff/catalog/basket-actions.component.html    |    5 +-
 .../app/staff/catalog/basket-actions.component.ts  |   22 +-
 .../src/app/staff/catalog/browse.component.html    |    2 +-
 .../eg2/src/app/staff/catalog/browse.component.ts  |    8 +-
 .../app/staff/catalog/browse/results.component.ts  |   23 +-
 .../eg2/src/app/staff/catalog/catalog.component.ts |    3 -
 .../eg2/src/app/staff/catalog/catalog.module.ts    |   13 +-
 .../eg2/src/app/staff/catalog/catalog.service.ts   |   17 +-
 ...owse.component.html => cnbrowse.component.html} |    2 +-
 .../{browse.component.ts => cnbrowse.component.ts} |   12 +-
 .../staff/catalog/cnbrowse/results.component.html  |   52 +
 .../staff/catalog/cnbrowse/results.component.ts    |  122 +
 .../src/app/staff/catalog/hold/hold.component.ts   |   10 +-
 .../staff/catalog/record/actions.component.html    |   27 +-
 .../staff/catalog/record/conjoined.component.html  |   27 +
 .../staff/catalog/record/conjoined.component.ts    |  110 +
 .../app/staff/catalog/record/copies.component.html |   17 +-
 .../staff/catalog/record/holdings.component.css    |   41 +
 .../staff/catalog/record/holdings.component.html   |  291 ++
 .../app/staff/catalog/record/holdings.component.ts |  896 +++++
 .../staff/catalog/record/pagination.component.ts   |   46 +-
 .../app/staff/catalog/record/record.component.html |   20 +-
 .../eg2/src/app/staff/catalog/resolver.service.ts  |   18 +-
 .../app/staff/catalog/result/facets.component.html |    4 +-
 .../app/staff/catalog/result/facets.component.ts   |   11 +-
 .../app/staff/catalog/result/record.component.html |   56 +-
 .../app/staff/catalog/result/record.component.ts   |   55 +-
 .../eg2/src/app/staff/catalog/routing.module.ts    |   16 +-
 .../app/staff/catalog/search-form.component.html   |   16 +
 .../src/app/staff/catalog/search-form.component.ts |   22 +-
 Open-ILS/src/eg2/src/app/staff/common.module.ts    |    6 +-
 .../staff/share/admin-page/admin-page.component.ts |   46 +-
 .../share/bib-summary/bib-summary.component.html   |    8 +-
 .../share/bib-summary/bib-summary.component.ts     |   33 +-
 .../src/app/staff/share/booking/booking.module.ts  |   20 +
 .../booking/make-bookable-dialog.component.html    |   51 +
 .../booking/make-bookable-dialog.component.ts      |  112 +
 ...component.html => bucket-dialog.component.html} |   24 +-
 ...log.component.ts => bucket-dialog.component.ts} |  118 +-
 .../holdings/conjoined-items-dialog.component.html |   42 +
 .../holdings/conjoined-items-dialog.component.ts   |  157 +
 .../holdings/copy-alerts-dialog.component.html     |  109 +
 .../share/holdings/copy-alerts-dialog.component.ts |  185 +
 .../holdings/delete-volcopy-dialog.component.html  |   33 +
 .../holdings/delete-volcopy-dialog.component.ts    |  123 +
 .../app/staff/share/holdings/holdings.module.ts    |   16 +-
 .../app/staff/share/holdings/holdings.service.ts   |    9 +-
 .../holdings/replace-barcode-dialog.component.html |   50 +
 .../holdings/replace-barcode-dialog.component.ts   |  111 +
 .../src/app/staff/share/holds/grid.component.ts    |    4 +-
 Open-ILS/src/eg2/src/styles.css                    |   24 +-
 .../Cataloging/catalog-basket-export.adoc          |   10 +
 .../Client/ang-staff-cat-cnbrowse.adoc             |    7 +
 77 files changed, 5199 insertions(+), 2191 deletions(-)
 create mode 100644 Open-ILS/src/eg2/src/app/share/util/bool.component.ts
 create mode 100644 Open-ILS/src/eg2/src/app/share/util/hash-params.ts
 copy Open-ILS/src/eg2/src/app/staff/catalog/{browse.component.html => cnbrowse.component.html} (51%)
 copy Open-ILS/src/eg2/src/app/staff/catalog/{browse.component.ts => cnbrowse.component.ts} (59%)
 create mode 100644 Open-ILS/src/eg2/src/app/staff/catalog/cnbrowse/results.component.html
 create mode 100644 Open-ILS/src/eg2/src/app/staff/catalog/cnbrowse/results.component.ts
 create mode 100644 Open-ILS/src/eg2/src/app/staff/catalog/record/conjoined.component.html
 create mode 100644 Open-ILS/src/eg2/src/app/staff/catalog/record/conjoined.component.ts
 create mode 100644 Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.css
 create mode 100644 Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
 create mode 100644 Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/booking/booking.module.ts
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/booking/make-bookable-dialog.component.html
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/booking/make-bookable-dialog.component.ts
 rename Open-ILS/src/eg2/src/app/staff/share/buckets/{record-bucket-dialog.component.html => bucket-dialog.component.html} (72%)
 rename Open-ILS/src/eg2/src/app/staff/share/buckets/{record-bucket-dialog.component.ts => bucket-dialog.component.ts} (56%)
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.html
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.ts
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/holdings/copy-alerts-dialog.component.html
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/holdings/copy-alerts-dialog.component.ts
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/holdings/delete-volcopy-dialog.component.html
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/holdings/delete-volcopy-dialog.component.ts
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/holdings/replace-barcode-dialog.component.html
 create mode 100644 Open-ILS/src/eg2/src/app/staff/share/holdings/replace-barcode-dialog.component.ts
 create mode 100644 docs/RELEASE_NOTES_NEXT/Cataloging/catalog-basket-export.adoc
 create mode 100644 docs/RELEASE_NOTES_NEXT/Client/ang-staff-cat-cnbrowse.adoc


hooks/post-receive
-- 
Evergreen ILS


More information about the open-ils-commits mailing list