diff --git a/README.md b/README.md
index c0d1f11d4e488ec5a3ec94b96832c5c49948d634..107ccc105144a204a307d18f88eb3a482a040022 100644
--- a/README.md
+++ b/README.md
@@ -47,7 +47,7 @@ and then :
 `npm run lint`
 
 
-# Procédure d'ajout d'une calculette
+# Procédure d'ajout d'un module de calcul
 
 ## JaLHyd
 
@@ -138,7 +138,7 @@ and then :
 * Créer les tests unitaires correspondants
 
 
-* Ajouter une valeur à l'enum _CalculatorType_ pour identifier le type de calculette (par ex _MaCalculette_).
+* Ajouter une valeur à l'enum _CalculatorType_ pour identifier le type de module de calcul (par ex _MaCalculette_).
 
 
 * Compléter la méthode _Session.createNub()_.
@@ -146,7 +146,7 @@ and then :
 
 ## ngHyd
 
-* Créer les fichiers de configuration de la calculette
+* Créer les fichiers de configuration du module de calcul
 	- dans _src/app/calculators_ : créer un répertoire (par ex _ma-calculette_)
 
 	- dans _src/app/calculators/ma-calculette_ :
@@ -155,7 +155,7 @@ and then :
 		Les ids utilisés doivent correspondre au symbole fourni à classe _ParamDefinition_ (1er paramètre du constructeur)
 
 		Ne pas oublier de spécifier :
-		- éventuellement le type de noeud par défaut de la calculette dans les options avec le champ "_defaultNodeType_". Si ce champ est absent, sa valeur est "_ComputeNodeType.None_". Ce champ sert par ex pour les sections paramétrées à déterminer le type de section à afficher lors de la création de la calculette.
+		- éventuellement le type de noeud par défaut du module de calcul dans les options avec le champ "_defaultNodeType_". Si ce champ est absent, sa valeur est "_ComputeNodeType.None_". Ce champ sert par ex pour les sections paramétrées à déterminer le type de section à afficher lors de la création du module de calcul.
 
 		- éventuellement le type de noeud de paramètres particuliers (objets comportant _"type":"input"_) avec le champ _"nodeType": "MaCalculetteBleue"_ (par défaut, "_ComputeNodeType.None_")
 
@@ -178,7 +178,7 @@ and then :
 		On peut soit composer la classe concrète directement avec ces classes, soient dériver ces dernières et composer avec.
 
 * _src/locale/error_messages.<langue>.json_ :
-	Ajouter un champ pour le titre de la calculette. Par exemple :
+	Ajouter un champ pour le titre du module de calcul. Par exemple :
 		 _"INFO_MACALC_TITRE": "Ma calculette"_
 
 * Dans la méthode _FormulaireService.getConfigPathPrefix()_, compléter le _switch_ pour fournir le préfixe des fichiers de configuration/internationalisation.
diff --git a/angular.json b/angular.json
index 4f42d530dfc095135978945761b7bf9326194387..faadd182a9c765816a2267a04aae58dd292abce5 100644
--- a/angular.json
+++ b/angular.json
@@ -25,12 +25,11 @@
               { "glob": "**/*.png", "input": "src/", "output": "/" }
             ],
             "styles": [
-              "node_modules/font-awesome/scss/font-awesome.scss",
-              "node_modules/angular-bootstrap-md/scss/bootstrap/bootstrap.scss",
-              "node_modules/angular-bootstrap-md/scss/mdb-free.scss",
-              "src/styles.scss"
+              "src/styles.scss",
+              "src/theme.scss"
             ],
-            "scripts": []
+            "scripts": [],
+            "showCircularDependencies": false
           },
           "configurations": {
             "production": {
@@ -42,7 +41,7 @@
               "aot": true,
               "extractLicenses": true,
               "vendorChunk": false,
-              "buildOptimizer": true,
+              "buildOptimizer": false,
               "fileReplacements": [
                 {
                   "replace": "src/environments/environment.ts",
@@ -55,7 +54,8 @@
         "serve": {
           "builder": "@angular-devkit/build-angular:dev-server",
           "options": {
-            "browserTarget": "ngHyd:build"
+            "browserTarget": "ngHyd:build",
+            "aot": true
           },
           "configurations": {
             "production": {
@@ -78,10 +78,8 @@
             "tsConfig": "src/tsconfig.spec.json",
             "scripts": [],
             "styles": [
-              "node_modules/font-awesome/scss/font-awesome.scss",
-              "node_modules/angular-bootstrap-md/scss/bootstrap/bootstrap.scss",
-              "node_modules/angular-bootstrap-md/scss/mdb-free.scss",
-              "src/styles.scss"
+              "src/styles.scss",
+              "src/theme.scss"
             ],
             "assets": [
               "src/assets",
diff --git a/docs-fr/calculators/hsl/regime_uniforme.md b/docs-fr/calculators/hsl/regime_uniforme.md
index 09dc8c21024e562f994ad308c1183c5b30d8e793..039e0fca76c539bd3224429479254f4a439b95ac 100644
--- a/docs-fr/calculators/hsl/regime_uniforme.md
+++ b/docs-fr/calculators/hsl/regime_uniforme.md
@@ -34,4 +34,4 @@ $$h_{k+1} = h_k - \frac{f(h_k)}{f'(h_k)}$$
 - \(f(h_k) = Q-KR^{2/3}S\sqrt{I_f}\)
 - \(f'(h_k) = -K \sqrt{I_f}(\frac{2}{3}R'R^{-1/3}S+R^{2/3}S')\)
 
-Pour calculer les paramètres géométriques de la section, la calculette utilise l'équation de calcul du débit et résout le problème par dichotomie.
+Pour calculer les paramètres géométriques de la section, le module de calcul utilise l'équation de calcul du débit et résout le problème par dichotomie.
diff --git a/docs-fr/calculators/pam/macrorugo.md b/docs-fr/calculators/pam/macrorugo.md
index 6b4014b03e016a98e79878f67356ba4a731d7d77..0ad40bbb53e9ba02cc37a99108d5f57e0bb77a7a 100644
--- a/docs-fr/calculators/pam/macrorugo.md
+++ b/docs-fr/calculators/pam/macrorugo.md
@@ -1,6 +1,6 @@
-# Passe à macro-rugosité
+# Passe à macro-rugosités
 
-Le module de calcul passe à macro rugosité permet de calculer les caractéristiques d'une passe à macro-rugosité constituée de blocs uniformément répartis avec des espacements transversaux \(ay\) et longitudinaux \(ay\) égaux.
+Le module de calcul passe à macro-rugosités permet de calculer les caractéristiques d'une passe à macro-rugosités constituée de blocs uniformément répartis avec des espacements transversaux \(ay\) et longitudinaux \(ay\) égaux.
 
 ![Schéma d'une disposition régulière des enrochements et notations](pam_schema_enrochement_regulier.png)
 
diff --git a/docs-fr/calculators/structures/kivi.md b/docs-fr/calculators/structures/kivi.md
index fd2f16ef91c16e1abf74951946d735f4468e9756..3d28e01f8cbb9e9e0c24c38f8b1eac8150ab2842 100644
--- a/docs-fr/calculators/structures/kivi.md
+++ b/docs-fr/calculators/structures/kivi.md
@@ -1,7 +1,7 @@
 # Formule de Kindsvater-Carter et Villemonte
 
 
-La calculette permet d'effectuer des calculs hydrauliques pour plusieurs ouvrages en parallèle.
+Le module de calcul permet d'effectuer des calculs hydrauliques pour plusieurs ouvrages en parallèle.
 
 ## Formule de Kindsvater-Carter (1957)
 
diff --git a/docs-fr/calculators/structures/orifice_noye.md b/docs-fr/calculators/structures/orifice_noye.md
index e8c11452e057a1a45e577d7144d385ce2afa6329..f33fa101f621634a4f885441241f612e62d289f9 100644
--- a/docs-fr/calculators/structures/orifice_noye.md
+++ b/docs-fr/calculators/structures/orifice_noye.md
@@ -4,7 +4,7 @@
 
 *Extrait de Larinier, M., Travade, F., Porcher, J.-P., Gosset, C., 1992. Passes à poissons : expertise et conception des ouvrages de franchissement. CSP. (page 94)*
 
-L'équation correspond à peu de chose près à celle de la calculette de la vanne rectangulaire noyée à la différence près que la surface de l'orifice est donnée directement plutôt que par le rapport de la largeur avec la hauteur :
+L'équation correspond à peu de chose près à celle du module de calcul de la vanne rectangulaire noyée à la différence près que la surface de l'orifice est donnée directement plutôt que par le rapport de la largeur avec la hauteur :
 
 $$Q = \mu S \sqrt{2g \Delta H}$$
 
diff --git a/e2e/calculator.e2e-spec.ts b/e2e/calculator.e2e-spec.ts
index 58beabb11e1a15b082f9554744d028d9d8ce9a77..f7e986344c299fcb0117e168b4feeb81f0eea51a 100644
--- a/e2e/calculator.e2e-spec.ts
+++ b/e2e/calculator.e2e-spec.ts
@@ -17,7 +17,7 @@ describe("ngHyd − calculator page", () => {
     expect(text.length).toBeGreaterThan(0);
   });
 
-  it("when a calculator is open, no label should be empty", async () => {
+  it("when a calculator is open, no active label should be empty", async () => {
     const labels = page.getInputLabels();
     await labels.each(async (l) => {
       const label = await l.getText();
diff --git a/e2e/calculator.po.ts b/e2e/calculator.po.ts
index 31a21411827e0f847484859bb5163d455ea97185..d4b7a51d5adc0649cc0a73b1fdb2a04ce2ff1a7b 100644
--- a/e2e/calculator.po.ts
+++ b/e2e/calculator.po.ts
@@ -1,13 +1,9 @@
 import { browser, by, element } from "protractor";
 
 export class CalculatorPage {
-  navigateTo() {
-    // @TODO won't work
-    return browser.get("/#/calculator/0");
-  }
 
   getInputLabels() {
-    return element.all(by.css("ngparam-input label"));
+    return element.all(by.css("ngparam-input input:not([disabled]) label"));
   }
 
   getHeader1() {
diff --git a/e2e/list.e2e-spec.ts b/e2e/list.e2e-spec.ts
index 027322c634866a0ba3e80eaac8f57bc16cba143e..775a2735ff0c673b32a7f677ed115e7548116097 100644
--- a/e2e/list.e2e-spec.ts
+++ b/e2e/list.e2e-spec.ts
@@ -9,6 +9,7 @@ describe("ngHyd − list page", () => {
 
   it("when list is open, user should see the list of available compute nodes", async () => {
     await page.navigateTo();
+    expect(page.getThemesCardsLength()).toBeGreaterThan(4);
     expect(page.getCalculatorsMenuLength()).toBeGreaterThan(8);
   });
 });
diff --git a/e2e/list.po.ts b/e2e/list.po.ts
index 8b21d46d14e7c398dda682b1b061711f774e07b8..2f06248a15933ff12b610e0870991d8d4cfe39dc 100644
--- a/e2e/list.po.ts
+++ b/e2e/list.po.ts
@@ -5,12 +5,20 @@ export class ListPage {
     return browser.get("/#/list");
   }
 
+  getThemesCards() {
+    return element.all(by.css("mat-card.compute-nodes-theme"));
+  }
+
+  async getThemesCardsLength() {
+    return await this.getThemesCards().count();
+  }
+
   getCalculatorsMenuEntries() {
-    return element.all(by.css("ul.list-group"));
+    return element.all(by.css("mat-card.compute-nodes-theme button.theme-calculator"));
   }
 
-  getCalculatorsMenuLength() {
-    return this.getCalculatorsMenuEntries().count();
+  async getCalculatorsMenuLength() {
+    return await this.getCalculatorsMenuEntries().count();
   }
 
   async clickRandomCalculatorMenuEntry() {
diff --git a/e2e/load-sesssion.e2e-spec.ts b/e2e/load-sesssion.e2e-spec.ts
index 6b11ef387f0c8ce4f20d0439b38781ecc00a1d54..39f919a41a020378ddc0b05f33fc0d179d3c6cbd 100644
--- a/e2e/load-sesssion.e2e-spec.ts
+++ b/e2e/load-sesssion.e2e-spec.ts
@@ -1,6 +1,7 @@
 import { AppPage } from "./app.po";
 import { Navbar } from "./navbar.po";
 import { SideNav } from "./sidenav.po";
+import { browser } from "protractor";
 
 describe("ngHyd − start page", () => {
   let page: AppPage;
@@ -15,9 +16,16 @@ describe("ngHyd − start page", () => {
 
   it("when loading session-6-calc.test.json file from home page, 6 calculators should be loaded", async () => {
     await page.navigateTo();
+
     await navbar.clickMenuButton();
+    await browser.sleep(200);
+
     await sidenav.clickLoadSessionButton();
+    await browser.sleep(200);
+
     await sidenav.loadSessionFile("./session-6-calc.test.json");
+    await browser.sleep(200);
+
     expect(await navbar.getAllCalculatorTabs().count()).toBe(6);
   });
 
diff --git a/e2e/navbar.po.ts b/e2e/navbar.po.ts
index 4b7bc3715a7c9a5514178489f7323e1e7efeae06..2112ceecf8ae3666311cee6fe7627b09dc433b9a 100644
--- a/e2e/navbar.po.ts
+++ b/e2e/navbar.po.ts
@@ -2,7 +2,7 @@ import { by, element } from "protractor";
 
 export class Navbar {
   getAllCalculatorTabs() {
-    return element.all(by.css("ul#navbar > li.calculator-tab"));
+    return element.all(by.css("#tabs-container > button.calculator-button"));
   }
 
   getNewCalculatorButton() {
diff --git a/e2e/navigate-through-calculators.e2e-spec.ts b/e2e/navigate-through-calculators.e2e-spec.ts
index e33480fa8d4d439069ee173f1e5bf9d0cd664f50..f2f91340e01c80d132312f9d96bfab31a9268eb6 100644
--- a/e2e/navigate-through-calculators.e2e-spec.ts
+++ b/e2e/navigate-through-calculators.e2e-spec.ts
@@ -1,6 +1,7 @@
 import { ListPage } from "./list.po";
 import { Navbar } from "./navbar.po";
 import { CalculatorPage } from "./calculator.po";
+import { browser } from "protractor";
 
 describe("ngHyd − create calculators and navigate among them", () => {
   let listPage: ListPage;
@@ -13,14 +14,16 @@ describe("ngHyd − create calculators and navigate among them", () => {
     navbar = new Navbar();
   });
 
-  it("when many calculators are open, navigating among them should not fail (all labels should be visible)", async () => {
+  it("when many calculators are open, navigating among them should not fail (all active labels should be visible)", async () => {
+    await listPage.navigateTo();
     // open 8 calculators
-    for (let i = 0; i < 8; i++) {
+    for (let i = 0; i < 6; i++) {
       await navbar.clickNewCalculatorButton();
+      await browser.sleep(200);
       await listPage.clickRandomCalculatorMenuEntry();
     }
     // navigate among them
-    for (let i = 0; i < 16; i++) {
+    for (let i = 0; i < 10; i++) {
       await navbar.clickRandomCalculatorTab(i);
       // test all form labels
       const labels = calculatorPage.getInputLabels();
diff --git a/e2e/preferences.e2e-spec.ts b/e2e/preferences.e2e-spec.ts
index 5516cb68387f36a175f4e4ff547f2a52572a3ca3..42d4dc61172797b981bdf909383289bc347dbc18 100644
--- a/e2e/preferences.e2e-spec.ts
+++ b/e2e/preferences.e2e-spec.ts
@@ -1,4 +1,5 @@
 import { PreferencesPage } from "./preferences.po";
+import { browser } from "protractor";
 
 describe("ngHyd − preferences page", () => {
   let page: PreferencesPage;
@@ -14,11 +15,53 @@ describe("ngHyd − preferences page", () => {
   });
 
   it("when preferences are open, no label should be empty", async () => {
-    // page.navigateTo();
     const labels = page.getInputLabels();
     await labels.each(async (l) => {
       const label = await l.getText();
       expect(label.length).toBeGreaterThan(0);
     });
   });
+
+  it("when preferences are open, no input field should be empty", async () => {
+    const numericFields = page.getNumericFormFields();
+    await numericFields.each(async (nf) => {
+      const input = page.getInputForField(nf);
+      const val = await input.getAttribute("value");
+      expect(val).toBeTruthy();
+    });
+  });
+
+  it("when erroneous values are input, errors should appear", async () => {
+    const numericFields = page.getNumericFormFields();
+    await numericFields.each(async (nf) => {
+      // add a letter after the numerical value
+      await page.getInputForField(nf).sendKeys("d");
+      expect(page.getErrorsForField(nf).isPresent()).toBe(true);
+      // empty input
+      await page.getInputForField(nf).clear();
+      expect(page.getErrorsForField(nf).isPresent()).toBe(true);
+      // send bad value
+      await page.getInputForField(nf).sendKeys("0");
+      expect(page.getErrorsForField(nf).isPresent()).toBe(true);
+    });
+  });
+
+  it("when correct values are input, errors should disappear", async () => {
+    const numericFields = page.getNumericFormFields();
+    await numericFields.each(async (nf) => {
+      // send correct value
+      await page.getInputForField(nf).sendKeys("123");
+      expect(page.getErrorsForField(nf).isPresent()).toBe(false);
+    });
+  });
+
+  it("when language is changed, language should change", async () => {
+    await page.changeLanguage(0);
+    await browser.sleep(200);
+    const val1 = await page.getHeader1().getText();
+    await page.changeLanguage(1);
+    await browser.sleep(200);
+    const val2 = await page.getHeader1().getText();
+    expect(val1).not.toEqual(val2);
+  });
 });
diff --git a/e2e/preferences.po.ts b/e2e/preferences.po.ts
index 96ea80f42cde862b53c26b64b7b7b3f9728fcf3f..dc5aceb2bbec1595d77df593e16f1ea5b32ce405 100644
--- a/e2e/preferences.po.ts
+++ b/e2e/preferences.po.ts
@@ -1,4 +1,4 @@
-import { browser, by, element } from "protractor";
+import { browser, by, element, ElementFinder } from "protractor";
 
 export class PreferencesPage {
   navigateTo() {
@@ -9,7 +9,34 @@ export class PreferencesPage {
     return element(by.css("h1"));
   }
 
+  getLanguageSelect() {
+    // tslint:disable-next-line:quotemark
+    return element(by.css('mat-select[data-testid="language-select"]'));
+  }
+
   getInputLabels() {
-    return element.all(by.css("base-param-input label"));
+    return element.all(by.css("label.mat-form-field-label"));
+  }
+
+  getNumericFormFields() {
+    // tslint:disable-next-line:quotemark
+    return element.all(by.css('mat-form-field[data-testclass="numeric-input"]'));
+  }
+
+  getInputForField(ff: ElementFinder) {
+    return ff.element(by.css("input"));
+  }
+
+  getErrorsForField(ff: ElementFinder) {
+    return ff.element(by.css("mat-error"));
+  }
+
+  async changeLanguage(index: number) {
+    const select = this.getLanguageSelect();
+    await select.click();
+    const options = (await select.getAttribute("aria-owns")).split(" ");
+    const optId = options[index];
+    const option = element(by.id(optId));
+    await option.click();
   }
 }
diff --git a/e2e/sidenav.po.ts b/e2e/sidenav.po.ts
index 07dd1cea1fffe9fe8d3171cb108883fe764e6efb..2b3f469d4954b72021585ae86f22df0f33f0e57f 100644
--- a/e2e/sidenav.po.ts
+++ b/e2e/sidenav.po.ts
@@ -8,11 +8,12 @@ export class SideNav {
 
   getFileInput() {
     // tslint:disable-next-line:quotemark
-    return element(by.css('load-calc input[type="file"]'));
+    return element(by.css('dialog-load-session input[type="file"]'));
   }
 
   getFileLoadButton() {
-    return element(by.css("load-calc .modal-footer button.btn-success"));
+    // tslint:disable-next-line:quotemark
+    return element(by.css('dialog-load-session button[type="submit"]'));
   }
 
   async clickLoadSessionButton() {
diff --git a/jalhyd_branch b/jalhyd_branch
index bd388944e1091e13962974ce434774f09d31464e..1f7391f92b6a3792204e07e99f71f643cc35e7e1 100644
--- a/jalhyd_branch
+++ b/jalhyd_branch
@@ -1 +1 @@
-58-nettoyage-et-simplification-du-code
+master
diff --git a/package-lock.json b/package-lock.json
index 83568879fd764796c9823a20cf4064aecda2f88e..9f12af07c5303924deb581b2cf12855cc20ee42a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5,26 +5,37 @@
   "requires": true,
   "dependencies": {
     "@angular-devkit/architect": {
-      "version": "0.12.1",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.12.1.tgz",
-      "integrity": "sha512-1ozBP0ZAApkSfuPpZ7b9vShU8smNxb98jW+65S12cPOxv1bVVxCj5sTmC3sSfXapgq/pMzblbaVSKOG7Ajz0vQ==",
+      "version": "0.12.4",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.12.4.tgz",
+      "integrity": "sha512-19f3jbGyP+GzTSBgrHR4SWGK81SUgtTxhkAcyhmsIEDuXrMJM8kPu0HB9WivJ5p08+jzwz6xdF9mpNYSeD9uqw==",
       "dev": true,
       "requires": {
-        "@angular-devkit/core": "7.2.1",
+        "@angular-devkit/core": "7.2.4",
         "rxjs": "6.3.3"
+      },
+      "dependencies": {
+        "rxjs": {
+          "version": "6.3.3",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
+          "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
+          "dev": true,
+          "requires": {
+            "tslib": "^1.9.0"
+          }
+        }
       }
     },
     "@angular-devkit/build-angular": {
-      "version": "0.12.1",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.12.1.tgz",
-      "integrity": "sha512-TpaMgKECEm1Tta4jkvZVzWdbq2OakIwVyYSzZ/7ARVe0FXhEjVLgWB1pYAdhRx+Hv4/E2ZSPJW1J3N3DTE4W4Q==",
+      "version": "0.12.4",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.12.4.tgz",
+      "integrity": "sha512-zzjpM9GpCGEUtTPv/T04CALzFXkTgAAiNt1whY1Vmtu2YYUksXOm1ysA2RBLEhb81RodTEwVV2zFUj5v2xHYEw==",
       "dev": true,
       "requires": {
-        "@angular-devkit/architect": "0.12.1",
-        "@angular-devkit/build-optimizer": "0.12.1",
-        "@angular-devkit/build-webpack": "0.12.1",
-        "@angular-devkit/core": "7.2.1",
-        "@ngtools/webpack": "7.2.1",
+        "@angular-devkit/architect": "0.12.4",
+        "@angular-devkit/build-optimizer": "0.12.4",
+        "@angular-devkit/build-webpack": "0.12.4",
+        "@angular-devkit/core": "7.2.4",
+        "@ngtools/webpack": "7.2.4",
         "ajv": "6.6.2",
         "autoprefixer": "9.4.3",
         "circular-dependency-plugin": "5.0.2",
@@ -42,11 +53,11 @@
         "mini-css-extract-plugin": "0.4.4",
         "minimatch": "3.0.4",
         "node-sass": "4.10.0",
-        "opn": "5.3.0",
+        "opn": "5.4.0",
         "parse5": "4.0.0",
         "portfinder": "1.0.17",
-        "postcss": "7.0.5",
-        "postcss-import": "12.0.0",
+        "postcss": "7.0.13",
+        "postcss-import": "12.0.1",
         "postcss-loader": "3.0.0",
         "raw-loader": "0.5.1",
         "rxjs": "6.3.3",
@@ -54,14 +65,14 @@
         "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.2.5",
         "stats-webpack-plugin": "0.7.0",
         "style-loader": "0.23.1",
         "stylus": "0.54.5",
         "stylus-loader": "3.0.2",
-        "terser-webpack-plugin": "1.1.0",
+        "terser-webpack-plugin": "1.2.1",
         "tree-kill": "1.2.0",
-        "webpack": "4.23.1",
+        "webpack": "4.28.4",
         "webpack-dev-middleware": "3.4.0",
         "webpack-dev-server": "3.1.14",
         "webpack-merge": "4.1.4",
@@ -69,78 +80,45 @@
         "webpack-subresource-integrity": "1.1.0-rc.6"
       },
       "dependencies": {
-        "ajv": {
-          "version": "6.6.2",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz",
-          "integrity": "sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==",
-          "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"
-          }
-        },
-        "glob": {
-          "version": "7.1.3",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
-          "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
-          "dev": true,
-          "requires": {
-            "fs.realpath": "^1.0.0",
-            "inflight": "^1.0.4",
-            "inherits": "2",
-            "minimatch": "^3.0.4",
-            "once": "^1.3.0",
-            "path-is-absolute": "^1.0.0"
-          }
-        },
-        "semver": {
-          "version": "5.5.1",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
-          "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==",
-          "dev": true
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+        "parse5": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
+          "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==",
           "dev": true
         },
-        "source-map-support": {
-          "version": "0.5.9",
-          "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz",
-          "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==",
+        "rxjs": {
+          "version": "6.3.3",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
+          "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
           "dev": true,
           "requires": {
-            "buffer-from": "^1.0.0",
-            "source-map": "^0.6.0"
+            "tslib": "^1.9.0"
           }
         }
       }
     },
     "@angular-devkit/build-optimizer": {
-      "version": "0.12.1",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.12.1.tgz",
-      "integrity": "sha512-zYea22pJ5kvMud8UBrdzIcR9F1FDYWJ3vwj5WRUFM0sF7mbbrmTC+OsIvNI7qDJuXWNZGySwNlHw0e+rhv30gg==",
+      "version": "0.12.4",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.12.4.tgz",
+      "integrity": "sha512-KraU+ZARX7JMtttPjOku9wVF+dnjMsIbiIVsQrNXhpFiGT1fSJhQTPxc98ONgEmUiGROFXXq2mHLilvMr2WdwQ==",
       "dev": true,
       "requires": {
         "loader-utils": "1.1.0",
         "source-map": "0.5.6",
-        "typescript": "3.2.2",
+        "typescript": "3.2.4",
         "webpack-sources": "1.2.0"
       },
       "dependencies": {
         "source-map": {
           "version": "0.5.6",
-          "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
           "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=",
           "dev": true
         },
         "typescript": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz",
-          "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==",
+          "version": "3.2.4",
+          "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.4.tgz",
+          "integrity": "sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==",
           "dev": true
         },
         "webpack-sources": {
@@ -164,20 +142,31 @@
       }
     },
     "@angular-devkit/build-webpack": {
-      "version": "0.12.1",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.12.1.tgz",
-      "integrity": "sha512-eDNgR2EV9/l4xYTkvS3861TthUv8ERBroWpMkkniX3HhpyjgaLyI5P1OB7fVMcF3RvJsxIlqYGRZ6zx7PjCbcA==",
+      "version": "0.12.4",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.12.4.tgz",
+      "integrity": "sha512-1+t2MUB+dq+1LbfTnvzZwj2QTWiugyMywXqYjsyt0rrh7VcriD1lQ+P5yN8kgFz/R7Ut4LgvS05yDX1JHi20qw==",
       "dev": true,
       "requires": {
-        "@angular-devkit/architect": "0.12.1",
-        "@angular-devkit/core": "7.2.1",
+        "@angular-devkit/architect": "0.12.4",
+        "@angular-devkit/core": "7.2.4",
         "rxjs": "6.3.3"
+      },
+      "dependencies": {
+        "rxjs": {
+          "version": "6.3.3",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
+          "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
+          "dev": true,
+          "requires": {
+            "tslib": "^1.9.0"
+          }
+        }
       }
     },
     "@angular-devkit/core": {
-      "version": "7.2.1",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.2.1.tgz",
-      "integrity": "sha512-zOozPswSM1cTkltw5LeSPoZ/fJ2d3vN304IVgKgrM5/Fs54bd7nTaBcAK+HvjKS+5KmykYrXW47Q4CdFJikluQ==",
+      "version": "7.2.4",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.2.4.tgz",
+      "integrity": "sha512-XHF59tIHg2qEM1Wd415xhykBLjjfOK6yMB7CjNk1bToUMX2QDT87izJF4y1Vwa0lIw9G0jdgP/4/M/OqXcbYmA==",
       "dev": true,
       "requires": {
         "ajv": "6.6.2",
@@ -187,43 +176,34 @@
         "source-map": "0.7.3"
       },
       "dependencies": {
-        "ajv": {
-          "version": "6.6.2",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz",
-          "integrity": "sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==",
+        "rxjs": {
+          "version": "6.3.3",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
+          "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
           "dev": true,
           "requires": {
-            "fast-deep-equal": "^2.0.1",
-            "fast-json-stable-stringify": "^2.0.0",
-            "json-schema-traverse": "^0.4.1",
-            "uri-js": "^4.2.2"
+            "tslib": "^1.9.0"
           }
-        },
-        "source-map": {
-          "version": "0.7.3",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
-          "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
-          "dev": true
         }
       }
     },
     "@angular-devkit/schematics": {
-      "version": "7.1.4",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-7.1.4.tgz",
-      "integrity": "sha512-+rn3ppcC3grsi9vV2uUIYh/5mUBEJ+JRCKW11BJoUqLMeu8W7h+vbVonyfwJXsk3FSTf9ZY0C7F7UqggRS3cWw==",
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-7.3.1.tgz",
+      "integrity": "sha512-cd7usiasfSgw75INz72/VssrLr9tiVRYfo1TEdvr9ww0GuQbuQpB33xbV8W135eAV8+wzQ3Ce8ohaDHibvj6Yg==",
       "dev": true,
       "requires": {
-        "@angular-devkit/core": "7.1.4",
+        "@angular-devkit/core": "7.3.1",
         "rxjs": "6.3.3"
       },
       "dependencies": {
         "@angular-devkit/core": {
-          "version": "7.1.4",
-          "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.1.4.tgz",
-          "integrity": "sha512-3cBVHjSQjMyE/mIyOX82ekdybNRQlN+kUfmdZS6oVW9aV48vdxcVbEGdl8t1H4enMf89u8kXiAAET9jFaqWopg==",
+          "version": "7.3.1",
+          "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.1.tgz",
+          "integrity": "sha512-56XDWWfIzOAkEk69lBLgmCYybPUA4yjunhmMlCk7vVdb7gbQUyzNjFD04Uj0GjlejatAQ5F76tRwygD9C+3RXQ==",
           "dev": true,
           "requires": {
-            "ajv": "6.5.3",
+            "ajv": "6.7.0",
             "chokidar": "2.0.4",
             "fast-json-stable-stringify": "2.0.0",
             "rxjs": "6.3.3",
@@ -231,9 +211,9 @@
           }
         },
         "ajv": {
-          "version": "6.5.3",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz",
-          "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==",
+          "version": "6.7.0",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.7.0.tgz",
+          "integrity": "sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg==",
           "dev": true,
           "requires": {
             "fast-deep-equal": "^2.0.1",
@@ -242,62 +222,72 @@
             "uri-js": "^4.2.2"
           }
         },
-        "fast-deep-equal": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
-          "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
-          "dev": true
-        },
-        "source-map": {
-          "version": "0.7.3",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
-          "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
-          "dev": true
+        "rxjs": {
+          "version": "6.3.3",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
+          "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
+          "dev": true,
+          "requires": {
+            "tslib": "^1.9.0"
+          }
         }
       }
     },
     "@angular/animations": {
-      "version": "7.1.4",
-      "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-7.1.4.tgz",
-      "integrity": "sha512-877LZ83scksJtflVz97CUWlSsZnxduBxPD+ls5OTrTT/bq3muzHCm8rgTO7S8fBwwrEVXLorvMAlhDPpMg5Swg==",
+      "version": "7.2.4",
+      "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-7.2.4.tgz",
+      "integrity": "sha512-Wx6cqU6koFOASlyl4aCygtbtROoehU6OKwV2EZTkfzHx6Eu/QyTiSa5kyoApVM5LMmCNeb8SxJMSAnKXztNl0A==",
       "requires": {
         "tslib": "^1.9.0"
       }
     },
+    "@angular/cdk": {
+      "version": "7.3.2",
+      "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-7.3.2.tgz",
+      "integrity": "sha512-jnthvY1Kt+DpJTrkgyKTiVuYgBdp4iG7QDeZJPBQm0e8mL2K0Pi9AqFbo01E4CGPqZpvtEggvqM0OJpR8J+amw==",
+      "requires": {
+        "parse5": "^5.0.0",
+        "tslib": "^1.7.1"
+      }
+    },
     "@angular/cli": {
-      "version": "7.1.4",
-      "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-7.1.4.tgz",
-      "integrity": "sha512-SruaZsmyq3+ymMPeMJSzhytvgtvzyzb1q58pUYX+vZjff2aMYOo0TVxJALwTOPIABICTqUTZmujbLG9uxVgxFA==",
-      "dev": true,
-      "requires": {
-        "@angular-devkit/architect": "0.11.4",
-        "@angular-devkit/core": "7.1.4",
-        "@angular-devkit/schematics": "7.1.4",
-        "@schematics/angular": "7.1.4",
-        "@schematics/update": "0.11.4",
-        "inquirer": "6.2.0",
-        "opn": "5.3.0",
-        "semver": "5.5.1",
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-7.3.1.tgz",
+      "integrity": "sha512-8EvXYRhTqTaTk5PKv7VZxIWJiyG51R9RC9gtpRFx4bbnurqBHdEUxGMmaRsGT8QDbfvVsWnuakE0eeW1CrfZAQ==",
+      "dev": true,
+      "requires": {
+        "@angular-devkit/architect": "0.13.1",
+        "@angular-devkit/core": "7.3.1",
+        "@angular-devkit/schematics": "7.3.1",
+        "@schematics/angular": "7.3.1",
+        "@schematics/update": "0.13.1",
+        "@yarnpkg/lockfile": "1.1.0",
+        "ini": "1.3.5",
+        "inquirer": "6.2.1",
+        "npm-package-arg": "6.1.0",
+        "opn": "5.4.0",
+        "pacote": "9.4.0",
+        "semver": "5.6.0",
         "symbol-observable": "1.2.0"
       },
       "dependencies": {
         "@angular-devkit/architect": {
-          "version": "0.11.4",
-          "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.11.4.tgz",
-          "integrity": "sha512-2zi6S9tPlk52vyqN67IvFoeNgd0uxtrPlwl3TdvJ3wrH7sYGJnkQ+EzAE7cKUGWAV989BbNtx2YxhRDHnN21Fg==",
+          "version": "0.13.1",
+          "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.13.1.tgz",
+          "integrity": "sha512-QDmIbqde75ZZSEFbw6Q6kQWq4cY6C7D67yujXw6XTyubDNAs1tyXJyxTIB8vjSlEKwRizTTDd/B0ZXVcke3Mvw==",
           "dev": true,
           "requires": {
-            "@angular-devkit/core": "7.1.4",
+            "@angular-devkit/core": "7.3.1",
             "rxjs": "6.3.3"
           }
         },
         "@angular-devkit/core": {
-          "version": "7.1.4",
-          "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.1.4.tgz",
-          "integrity": "sha512-3cBVHjSQjMyE/mIyOX82ekdybNRQlN+kUfmdZS6oVW9aV48vdxcVbEGdl8t1H4enMf89u8kXiAAET9jFaqWopg==",
+          "version": "7.3.1",
+          "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.1.tgz",
+          "integrity": "sha512-56XDWWfIzOAkEk69lBLgmCYybPUA4yjunhmMlCk7vVdb7gbQUyzNjFD04Uj0GjlejatAQ5F76tRwygD9C+3RXQ==",
           "dev": true,
           "requires": {
-            "ajv": "6.5.3",
+            "ajv": "6.7.0",
             "chokidar": "2.0.4",
             "fast-json-stable-stringify": "2.0.0",
             "rxjs": "6.3.3",
@@ -305,9 +295,9 @@
           }
         },
         "ajv": {
-          "version": "6.5.3",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz",
-          "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==",
+          "version": "6.7.0",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.7.0.tgz",
+          "integrity": "sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg==",
           "dev": true,
           "requires": {
             "fast-deep-equal": "^2.0.1",
@@ -316,55 +306,43 @@
             "uri-js": "^4.2.2"
           }
         },
-        "fast-deep-equal": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
-          "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
-          "dev": true
-        },
-        "opn": {
-          "version": "5.3.0",
-          "resolved": "http://registry.npmjs.org/opn/-/opn-5.3.0.tgz",
-          "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==",
+        "rxjs": {
+          "version": "6.3.3",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
+          "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
           "dev": true,
           "requires": {
-            "is-wsl": "^1.1.0"
+            "tslib": "^1.9.0"
           }
         },
         "semver": {
-          "version": "5.5.1",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
-          "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==",
-          "dev": true
-        },
-        "source-map": {
-          "version": "0.7.3",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
-          "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+          "version": "5.6.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
+          "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
           "dev": true
         }
       }
     },
     "@angular/common": {
-      "version": "7.1.4",
-      "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.1.4.tgz",
-      "integrity": "sha512-oQPCilcf1H/OXmt4z6PfGoCSb1YPRBAXGs/KRBARm3tYan2r5CmV0BFwpWXWQrEMt8YQqqLiBQUQ64d8+VFm8Q==",
+      "version": "7.2.4",
+      "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.2.4.tgz",
+      "integrity": "sha512-3/i8RtnLTx/90gJHk5maE8zwsSiHgHvLItaa0qVfNlWiU0eCId/PL6TgDkut5vN9SQYL0oxhxFaVd35HmwsmuQ==",
       "requires": {
         "tslib": "^1.9.0"
       }
     },
     "@angular/compiler": {
-      "version": "7.1.4",
-      "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.1.4.tgz",
-      "integrity": "sha512-AvYXtjEJ27Rhv4c27DXNEa58Lit63jdydzbz7VuyFhNU+FwDUK2DC4gZe0nWZsf7HUniJezVRFkECDCZQeSKCQ==",
+      "version": "7.2.4",
+      "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.2.4.tgz",
+      "integrity": "sha512-+zyMzPCL45ePEV9nrnYJvhAVgp2Y19bDaq0f0YdZAqAjgDqHzXGGR6wX8GueyJWmUYWx5vwK6Apla4HwDrYA1w==",
       "requires": {
         "tslib": "^1.9.0"
       }
     },
     "@angular/compiler-cli": {
-      "version": "7.1.4",
-      "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.1.4.tgz",
-      "integrity": "sha512-fyEY58A53pmVEuhfyU1z1TAi1n5rOK5cCjhFnUUnnESQFEW/aQYOl0ZL6LDsMSYfa78fkDw4Nc0DytvZ1ymVMw==",
+      "version": "7.2.4",
+      "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.2.4.tgz",
+      "integrity": "sha512-UhLosSeuwFIfaGqGcYOh9WSOuzEpeuhIRAOt81MeqOQEqkoreUjfxrQq8XWNkdqsPZHtiptF5ZwXlMBxlj9jJg==",
       "dev": true,
       "requires": {
         "canonical-path": "1.0.0",
@@ -405,6 +383,12 @@
             "arr-flatten": "^1.0.1"
           }
         },
+        "array-unique": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
+          "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
+          "dev": true
+        },
         "braces": {
           "version": "1.8.5",
           "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
@@ -416,6 +400,12 @@
             "repeat-element": "^1.1.2"
           }
         },
+        "camelcase": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+          "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
+          "dev": true
+        },
         "chokidar": {
           "version": "1.7.0",
           "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
@@ -507,9 +497,18 @@
             "is-extglob": "^1.0.0"
           }
         },
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        },
         "load-json-file": {
           "version": "2.0.0",
-          "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+          "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
           "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
           "dev": true,
           "requires": {
@@ -549,6 +548,12 @@
             "regex-cache": "^0.4.2"
           }
         },
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+          "dev": true
+        },
         "os-locale": {
           "version": "2.1.0",
           "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz",
@@ -569,6 +574,12 @@
             "pify": "^2.0.0"
           }
         },
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+          "dev": true
+        },
         "read-pkg": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
@@ -615,6 +626,12 @@
             "ansi-regex": "^3.0.0"
           }
         },
+        "strip-bom": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+          "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+          "dev": true
+        },
         "which-module": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
@@ -660,55 +677,71 @@
       }
     },
     "@angular/core": {
-      "version": "7.1.4",
-      "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.1.4.tgz",
-      "integrity": "sha512-36uWLrmmlzf8JKaq2A5F2tPQEHvFSsbTQWOT559Drp1tzM2uSA7PysNHv5TXUshDn5i54S2EQFm4bj2YPp4Hzg==",
+      "version": "7.2.4",
+      "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.2.4.tgz",
+      "integrity": "sha512-kfAxhIxl89PmB7y81FR/RAv0yWRFcEYxEnTwV+o8jKGfemAXtQ0g/Vh+lJR0SD/TBgFilMxotN1mhwH4A8GShw==",
       "requires": {
         "tslib": "^1.9.0"
       }
     },
+    "@angular/flex-layout": {
+      "version": "7.0.0-beta.23",
+      "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-7.0.0-beta.23.tgz",
+      "integrity": "sha512-jH2i3i/M7SbK6scVlj2urVL5OhzwavbQ7KwvUjyc/UwccKnnzuOuWEGCINLja/aoaUO3I35LluCLv6a6VN0olA==",
+      "requires": {
+        "tslib": "^1.7.1"
+      }
+    },
     "@angular/forms": {
-      "version": "7.1.4",
-      "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.1.4.tgz",
-      "integrity": "sha512-YB2lDRe7aMsaO5ZlbeFZGH+uTQOcpotFDKCmTckpefRJ7id6TlUSBYdYRH80qOQfPJGpsnqQvLZkL24UMasKtQ==",
+      "version": "7.2.4",
+      "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.2.4.tgz",
+      "integrity": "sha512-DAtOrdlTRsgvmZrsvczCAkY8dhTwZb5DXBmPuSXh0UR9lvEiCgNHGbwEiIiIkAHpw1wSeXZrq0qyy/oJRvf18g==",
       "requires": {
         "tslib": "^1.9.0"
       }
     },
     "@angular/http": {
-      "version": "7.1.4",
-      "resolved": "https://registry.npmjs.org/@angular/http/-/http-7.1.4.tgz",
-      "integrity": "sha512-Lrjb99sHu0Cv7MBbVLev1I1Fe+DJcwG4v7juZB/5nE5opKmbQo3Lfqvt2dVrMOxHeju9ReFznEnuGNMK6+m6pQ==",
+      "version": "7.2.4",
+      "resolved": "https://registry.npmjs.org/@angular/http/-/http-7.2.4.tgz",
+      "integrity": "sha512-kazJREm7MtSCYbE+9zU/CcUXI5Csu53PooeQlAp80/TOHqry6fVKIMHCI892Db9ScY2ds0SzbyTmrxEQo7PP1A==",
       "requires": {
         "tslib": "^1.9.0"
       }
     },
     "@angular/language-service": {
-      "version": "7.1.4",
-      "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-7.1.4.tgz",
-      "integrity": "sha512-Pvrk3W3+6XfrmpCRcTumfyplv6AQJXKfDdPFSbhdpHJlpdcQRo6TckA85Yln5/CXZSAiPaZeiejQt2OogrIRLg==",
+      "version": "7.2.4",
+      "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-7.2.4.tgz",
+      "integrity": "sha512-A9Rud/27hHMSUUjpgn57nVeLsoYgdvFwJhtlZA/oCuSpmlD+LqqBsEpPhivwn++u44+DSrFXsic29jlFnsBotw==",
       "dev": true
     },
+    "@angular/material": {
+      "version": "7.3.2",
+      "resolved": "https://registry.npmjs.org/@angular/material/-/material-7.3.2.tgz",
+      "integrity": "sha512-x3it941KE8zuzDH1VLP4yfgmC3hwrqa1a2GHiekztF2KrbWPzEEGNvlyYOUV7nwiuwFw5twON73rW0HUKndwEA==",
+      "requires": {
+        "tslib": "^1.7.1"
+      }
+    },
     "@angular/platform-browser": {
-      "version": "7.1.4",
-      "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.1.4.tgz",
-      "integrity": "sha512-lIFBKo6Uqty7qYDI9T8quFCUzUpGBgpzvDe14aAHFwZCft9rMS1J7PB1F26/dy2RBQE8tUyN2zp2xZQnYAhMcA==",
+      "version": "7.2.4",
+      "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.2.4.tgz",
+      "integrity": "sha512-Klt8aKR5SP9bqfMfpSY5vQOY7AQEs8JGuZOk5Bfc2dUtYT2IEIvK2IqO8v2rcFRVO13HOPUxl328efyHqLgI7g==",
       "requires": {
         "tslib": "^1.9.0"
       }
     },
     "@angular/platform-browser-dynamic": {
-      "version": "7.1.4",
-      "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.1.4.tgz",
-      "integrity": "sha512-LtFd6XIz98BKjxrCRbaz2y0XSmVQSTzrvpAyNzKnzHAMn+4XpIpnzyV3Y6DeHolIBwLjFHFzGKMBwOHOwME4RQ==",
+      "version": "7.2.4",
+      "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.2.4.tgz",
+      "integrity": "sha512-J/xWlmaYOPUoCHZ5TiIRiyYa4uRMtCz3aGdBfY8k/NWtNo8SCYaS3aut7Sk4RS5rK8aAVi+aYFlY5YOrlW+Hbg==",
       "requires": {
         "tslib": "^1.9.0"
       }
     },
     "@angular/router": {
-      "version": "7.1.4",
-      "resolved": "https://registry.npmjs.org/@angular/router/-/router-7.1.4.tgz",
-      "integrity": "sha512-5VVVcRsmuKrIWPnh5zF1ExXmIpCx2tlisJ7YTS2FFDXnqrZ9i2QgaxyJuyZE+Btg3t7LPF4tkhRQpjauNiHJYA==",
+      "version": "7.2.4",
+      "resolved": "https://registry.npmjs.org/@angular/router/-/router-7.2.4.tgz",
+      "integrity": "sha512-T8Uqf2H1SV1MQI38WwYJ4aa+4NNnvlp2Tp/rkfg6tKcp/cLkKqE6OOfiy9lmW+i/624v8tMgYoBMOUNBjAG23g==",
       "requires": {
         "tslib": "^1.9.0"
       }
@@ -723,12 +756,12 @@
       }
     },
     "@babel/generator": {
-      "version": "7.2.2",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.2.2.tgz",
-      "integrity": "sha512-I4o675J/iS8k+P38dvJ3IBGqObLXyQLTxtrR4u9cSUJOURvafeEWb/pFMOTwtNrmq73mJzyF6ueTbO1BtN0Zeg==",
+      "version": "7.3.2",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.3.2.tgz",
+      "integrity": "sha512-f3QCuPppXxtZOEm5GWPra/uYUjmNQlu9pbAD8D/9jze4pTY83rTtB1igTBSwvkeNlC5gR24zFFkz+2WHLFQhqQ==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.2.2",
+        "@babel/types": "^7.3.2",
         "jsesc": "^2.5.1",
         "lodash": "^4.17.10",
         "source-map": "^0.5.0",
@@ -740,6 +773,12 @@
           "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
           "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
           "dev": true
+        },
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
         }
       }
     },
@@ -781,12 +820,20 @@
         "chalk": "^2.0.0",
         "esutils": "^2.0.2",
         "js-tokens": "^4.0.0"
+      },
+      "dependencies": {
+        "js-tokens": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+          "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+          "dev": true
+        }
       }
     },
     "@babel/parser": {
-      "version": "7.2.3",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.2.3.tgz",
-      "integrity": "sha512-0LyEcVlfCoFmci8mXx8A5oIkpkOgyo8dRHtxBnK9RRBwxO2+JZPNsqtVEZQ7mJFPxnXF9lfmU24mHOPI0qnlkA==",
+      "version": "7.3.2",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.3.2.tgz",
+      "integrity": "sha512-QzNUC2RO1gadg+fs21fi0Uu0OuGNzRKEmgCxoLNzbCdoprLwjfmZwzUrpUNfJPaVRwBpDY47A17yYEGWyRelnQ==",
       "dev": true
     },
     "@babel/template": {
@@ -826,6 +873,12 @@
             "ms": "^2.1.1"
           }
         },
+        "globals": {
+          "version": "11.11.0",
+          "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz",
+          "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==",
+          "dev": true
+        },
         "ms": {
           "version": "2.1.1",
           "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
@@ -835,36 +888,44 @@
       }
     },
     "@babel/types": {
-      "version": "7.2.2",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.2.2.tgz",
-      "integrity": "sha512-fKCuD6UFUMkR541eDWL+2ih/xFZBXPOg/7EQFeTluMDebfqR4jrpaCjLhkWlQS4hT6nRa2PMEgXKbRB5/H2fpg==",
+      "version": "7.3.2",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.2.tgz",
+      "integrity": "sha512-3Y6H8xlUlpbGR+XvawiH0UXehqydTmNmEpozWcXymqwcrwYAl5KMvKtQ+TF6f6E08V6Jur7v/ykdDSF+WDEIXQ==",
       "dev": true,
       "requires": {
         "esutils": "^2.0.2",
         "lodash": "^4.17.10",
         "to-fast-properties": "^2.0.0"
+      },
+      "dependencies": {
+        "to-fast-properties": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+          "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+          "dev": true
+        }
       }
     },
     "@compodoc/compodoc": {
-      "version": "1.1.7",
-      "resolved": "https://registry.npmjs.org/@compodoc/compodoc/-/compodoc-1.1.7.tgz",
-      "integrity": "sha512-bTqkv75T9b+3OxG517YL+5B0npAG0Q5nOMAk562Idr/fICTTHQ/xzSBsTptZ4QqYmtzRMV1OqOlk74FXfGPZWw==",
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/@compodoc/compodoc/-/compodoc-1.1.8.tgz",
+      "integrity": "sha512-MuTE/KyNfRxJwLC3Ih2WXsMgd3YWFsViGxFbnrMigNYSq6/syUYVZgoj0E/Hir1au6k5jshKX1Vg7S61wqCaWg==",
       "dev": true,
       "requires": {
         "@compodoc/ngd-transformer": "^2.0.0",
-        "chalk": "^2.4.1",
+        "chalk": "^2.4.2",
         "cheerio": "^1.0.0-rc.2",
-        "chokidar": "^2.0.4",
-        "colors": "^1.3.2",
+        "chokidar": "^2.1.0",
+        "colors": "^1.3.3",
         "commander": "2.19.0",
         "cosmiconfig": "^5.0.7",
-        "fancy-log": "^1.3.2",
+        "fancy-log": "^1.3.3",
         "findit2": "^2.2.3",
         "fs-extra": "^7.0.1",
         "glob": "^7.1.3",
         "handlebars": "4.0.10",
         "html-entities": "^1.2.1",
-        "i18next": "^12.1.0",
+        "i18next": "^14.1.1",
         "inside": "^1.0.0",
         "json5": "^2.1.0",
         "live-server": "1.2.1",
@@ -873,17 +934,12 @@
         "marked": "^0.4.0",
         "opencollective": "^1.0.3",
         "os-name": "^3.0.0",
+        "semver": "^5.6.0",
         "traverse": "^0.6.6",
         "ts-simple-ast": "12.4.0",
         "uuid": "^3.3.2"
       },
       "dependencies": {
-        "async": {
-          "version": "1.5.2",
-          "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
-          "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
-          "dev": true
-        },
         "camelcase": {
           "version": "1.2.1",
           "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
@@ -891,6 +947,26 @@
           "dev": true,
           "optional": true
         },
+        "chokidar": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.1.tgz",
+          "integrity": "sha512-gfw3p2oQV2wEt+8VuMlNsPjCxDxvvgnm/kz+uATu805mWVF8IJN7uz9DN7iBz+RMJISmiVbCOBFs9qBGMjtPfQ==",
+          "dev": true,
+          "requires": {
+            "anymatch": "^2.0.0",
+            "async-each": "^1.0.1",
+            "braces": "^2.3.2",
+            "fsevents": "^1.2.7",
+            "glob-parent": "^3.1.0",
+            "inherits": "^2.0.3",
+            "is-binary-path": "^1.0.0",
+            "is-glob": "^4.0.0",
+            "normalize-path": "^3.0.0",
+            "path-is-absolute": "^1.0.0",
+            "readdirp": "^2.2.1",
+            "upath": "^1.1.0"
+          }
+        },
         "cliui": {
           "version": "2.1.0",
           "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
@@ -903,12 +979,6 @@
             "wordwrap": "0.0.2"
           }
         },
-        "colors": {
-          "version": "1.3.3",
-          "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz",
-          "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==",
-          "dev": true
-        },
         "commander": {
           "version": "2.19.0",
           "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
@@ -927,20 +997,6 @@
             "parse-json": "^4.0.0"
           }
         },
-        "glob": {
-          "version": "7.1.3",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
-          "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
-          "dev": true,
-          "requires": {
-            "fs.realpath": "^1.0.0",
-            "inflight": "^1.0.4",
-            "inherits": "2",
-            "minimatch": "^3.0.4",
-            "once": "^1.3.0",
-            "path-is-absolute": "^1.0.0"
-          }
-        },
         "handlebars": {
           "version": "4.0.10",
           "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.10.tgz",
@@ -968,6 +1024,18 @@
           "integrity": "sha512-tMsdNBgOsrUophCAFQl0XPe6Zqk/uy9gnue+jIIKhykO51hxyu6uNx7zBPy0+y/WKYVZZMspV9YeXLNdKk+iYw==",
           "dev": true
         },
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+          "dev": true
+        },
+        "normalize-path": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+          "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+          "dev": true
+        },
         "parse-json": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
@@ -978,6 +1046,12 @@
             "json-parse-better-errors": "^1.0.1"
           }
         },
+        "semver": {
+          "version": "5.6.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
+          "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
+          "dev": true
+        },
         "source-map": {
           "version": "0.4.4",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
@@ -1008,6 +1082,13 @@
             }
           }
         },
+        "wordwrap": {
+          "version": "0.0.2",
+          "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
+          "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
+          "dev": true,
+          "optional": true
+        },
         "yargs": {
           "version": "3.10.0",
           "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
@@ -1097,18 +1178,27 @@
       }
     },
     "@ngtools/webpack": {
-      "version": "7.2.1",
-      "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-7.2.1.tgz",
-      "integrity": "sha512-/mpXSyaHBP+wfiEt/ZYNsnUmnDmdUkLL1rcNxDyxMxlrL246CtNUcMzYSVqYiKp7ufz6GNklY2QqUa9pcOlW6Q==",
+      "version": "7.2.4",
+      "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-7.2.4.tgz",
+      "integrity": "sha512-mXMDODmy53kk+Kb5jgLNQOsSrDBQQMf6C6KZNuGo8AdvUGdGaQeZDze4o7bcUz1KUjuaaP1Zh7pZtho8C4/T+Q==",
       "dev": true,
       "requires": {
-        "@angular-devkit/core": "7.2.1",
+        "@angular-devkit/core": "7.2.4",
         "enhanced-resolve": "4.1.0",
         "rxjs": "6.3.3",
         "tree-kill": "1.2.0",
         "webpack-sources": "1.2.0"
       },
       "dependencies": {
+        "rxjs": {
+          "version": "6.3.3",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
+          "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
+          "dev": true,
+          "requires": {
+            "tslib": "^1.9.0"
+          }
+        },
         "source-map": {
           "version": "0.6.1",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -1134,23 +1224,23 @@
       "dev": true
     },
     "@schematics/angular": {
-      "version": "7.1.4",
-      "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-7.1.4.tgz",
-      "integrity": "sha512-4QVSmvQtOELek+FDq+k2ROeH9YrRrPJ6jWK179+qOruKSd4uTgEti/jy+fS0rfr52kDSGdDhz7XTh/QvQB89fg==",
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-7.3.1.tgz",
+      "integrity": "sha512-0Ne8APPlTAjKg5CSZqluwCuW/5yPjr3ALCWzqwPxN0suE745usThtasBmqrjw0RMIt8nRqRgtg54Z7lCPO9ZFg==",
       "dev": true,
       "requires": {
-        "@angular-devkit/core": "7.1.4",
-        "@angular-devkit/schematics": "7.1.4",
-        "typescript": "3.1.6"
+        "@angular-devkit/core": "7.3.1",
+        "@angular-devkit/schematics": "7.3.1",
+        "typescript": "3.2.4"
       },
       "dependencies": {
         "@angular-devkit/core": {
-          "version": "7.1.4",
-          "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.1.4.tgz",
-          "integrity": "sha512-3cBVHjSQjMyE/mIyOX82ekdybNRQlN+kUfmdZS6oVW9aV48vdxcVbEGdl8t1H4enMf89u8kXiAAET9jFaqWopg==",
+          "version": "7.3.1",
+          "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.1.tgz",
+          "integrity": "sha512-56XDWWfIzOAkEk69lBLgmCYybPUA4yjunhmMlCk7vVdb7gbQUyzNjFD04Uj0GjlejatAQ5F76tRwygD9C+3RXQ==",
           "dev": true,
           "requires": {
-            "ajv": "6.5.3",
+            "ajv": "6.7.0",
             "chokidar": "2.0.4",
             "fast-json-stable-stringify": "2.0.0",
             "rxjs": "6.3.3",
@@ -1158,9 +1248,9 @@
           }
         },
         "ajv": {
-          "version": "6.5.3",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz",
-          "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==",
+          "version": "6.7.0",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.7.0.tgz",
+          "integrity": "sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg==",
           "dev": true,
           "requires": {
             "fast-deep-equal": "^2.0.1",
@@ -1169,49 +1259,46 @@
             "uri-js": "^4.2.2"
           }
         },
-        "fast-deep-equal": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
-          "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
-          "dev": true
-        },
-        "source-map": {
-          "version": "0.7.3",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
-          "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
-          "dev": true
+        "rxjs": {
+          "version": "6.3.3",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
+          "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
+          "dev": true,
+          "requires": {
+            "tslib": "^1.9.0"
+          }
         },
         "typescript": {
-          "version": "3.1.6",
-          "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.6.tgz",
-          "integrity": "sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==",
+          "version": "3.2.4",
+          "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.4.tgz",
+          "integrity": "sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==",
           "dev": true
         }
       }
     },
     "@schematics/update": {
-      "version": "0.11.4",
-      "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.11.4.tgz",
-      "integrity": "sha512-InfsMJtdWwoqCPmtlJeXBwRPPgIXUzpzIkCCcayRe9gy6PkPUIlOjfgEZ4Mqm/HR46lqsI8xwZfUK7SLV//a2g==",
+      "version": "0.13.1",
+      "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.13.1.tgz",
+      "integrity": "sha512-EHOqolT/d/jRGuVTCUESLpk8JNpuaPlsVHfeK7Kdp/t0wSEnmtOelZX4+leS25lGXDaDUF3138ntjrZR4n6bGw==",
       "dev": true,
       "requires": {
-        "@angular-devkit/core": "7.1.4",
-        "@angular-devkit/schematics": "7.1.4",
+        "@angular-devkit/core": "7.3.1",
+        "@angular-devkit/schematics": "7.3.1",
         "@yarnpkg/lockfile": "1.1.0",
         "ini": "1.3.5",
-        "pacote": "9.1.1",
+        "pacote": "9.4.0",
         "rxjs": "6.3.3",
-        "semver": "5.5.1",
+        "semver": "5.6.0",
         "semver-intersect": "1.4.0"
       },
       "dependencies": {
         "@angular-devkit/core": {
-          "version": "7.1.4",
-          "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.1.4.tgz",
-          "integrity": "sha512-3cBVHjSQjMyE/mIyOX82ekdybNRQlN+kUfmdZS6oVW9aV48vdxcVbEGdl8t1H4enMf89u8kXiAAET9jFaqWopg==",
+          "version": "7.3.1",
+          "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.1.tgz",
+          "integrity": "sha512-56XDWWfIzOAkEk69lBLgmCYybPUA4yjunhmMlCk7vVdb7gbQUyzNjFD04Uj0GjlejatAQ5F76tRwygD9C+3RXQ==",
           "dev": true,
           "requires": {
-            "ajv": "6.5.3",
+            "ajv": "6.7.0",
             "chokidar": "2.0.4",
             "fast-json-stable-stringify": "2.0.0",
             "rxjs": "6.3.3",
@@ -1219,9 +1306,9 @@
           }
         },
         "ajv": {
-          "version": "6.5.3",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz",
-          "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==",
+          "version": "6.7.0",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.7.0.tgz",
+          "integrity": "sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg==",
           "dev": true,
           "requires": {
             "fast-deep-equal": "^2.0.1",
@@ -1230,22 +1317,19 @@
             "uri-js": "^4.2.2"
           }
         },
-        "fast-deep-equal": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
-          "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
-          "dev": true
+        "rxjs": {
+          "version": "6.3.3",
+          "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
+          "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
+          "dev": true,
+          "requires": {
+            "tslib": "^1.9.0"
+          }
         },
         "semver": {
-          "version": "5.5.1",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
-          "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==",
-          "dev": true
-        },
-        "source-map": {
-          "version": "0.7.3",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
-          "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+          "version": "5.6.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
+          "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
           "dev": true
         }
       }
@@ -1257,9 +1341,9 @@
       "dev": true
     },
     "@types/jasmine": {
-      "version": "3.3.5",
-      "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.3.5.tgz",
-      "integrity": "sha512-LJtc52O1PNUffMvH6Q3fS0BOhQWYlkh3SVu/Jc4GoPgJkUytk5Y6YPbw+6lZK2mWWvG62BtVyOFw0ih7r8STsw==",
+      "version": "3.3.8",
+      "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.3.8.tgz",
+      "integrity": "sha512-BaOFpaddRVV8qykJoWHrHtamml880oh0+DIZWbtJgx0pu+KhDF1gER5hSfCIfzyMrbjMuYFnLUfyo1l0JUVU3Q==",
       "dev": true
     },
     "@types/jasminewd2": {
@@ -1272,9 +1356,9 @@
       }
     },
     "@types/node": {
-      "version": "8.10.39",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.39.tgz",
-      "integrity": "sha512-rE7fktr02J8ybFf6eysife+WF+L4sAHWzw09DgdCebEu+qDwMvv4zl6Bc+825ttGZP73kCKxa3dhJOoGJ8+5mA==",
+      "version": "8.10.40",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.40.tgz",
+      "integrity": "sha512-RRSjdwz63kS4u7edIwJUn8NqKLLQ6LyqF/X4+4jp38MBT3Vwetewi2N4dgJEshLbDwNgOJXNYoOwzVZUSSLhkQ==",
       "dev": true
     },
     "@types/q": {
@@ -1295,6 +1379,11 @@
       "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==",
       "dev": true
     },
+    "@types/sprintf-js": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@types/sprintf-js/-/sprintf-js-1.1.1.tgz",
+      "integrity": "sha512-h8jF5yPUoHH1QUIDfHgqFs7Y0UykKB5CJ1Z32PHGmHRLmW0pI+kYKAEnXVqeUZ3ygFmDkyvhD4vUZN+RZyTd1w=="
+    },
     "@types/webpack-sources": {
       "version": "0.1.5",
       "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.5.tgz",
@@ -1315,174 +1404,174 @@
       }
     },
     "@webassemblyjs/ast": {
-      "version": "1.7.10",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.10.tgz",
-      "integrity": "sha512-wTUeaByYN2EA6qVqhbgavtGc7fLTOx0glG2IBsFlrFG51uXIGlYBTyIZMf4SPLo3v1bgV/7lBN3l7Z0R6Hswew==",
+      "version": "1.7.11",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz",
+      "integrity": "sha512-ZEzy4vjvTzScC+SH8RBssQUawpaInUdMTYwYYLh54/s8TuT0gBLuyUnppKsVyZEi876VmmStKsUs28UxPgdvrA==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/helper-module-context": "1.7.10",
-        "@webassemblyjs/helper-wasm-bytecode": "1.7.10",
-        "@webassemblyjs/wast-parser": "1.7.10"
+        "@webassemblyjs/helper-module-context": "1.7.11",
+        "@webassemblyjs/helper-wasm-bytecode": "1.7.11",
+        "@webassemblyjs/wast-parser": "1.7.11"
       }
     },
     "@webassemblyjs/floating-point-hex-parser": {
-      "version": "1.7.10",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.10.tgz",
-      "integrity": "sha512-gMsGbI6I3p/P1xL2UxqhNh1ga2HCsx5VBB2i5VvJFAaqAjd2PBTRULc3BpTydabUQEGlaZCzEUQhLoLG7TvEYQ==",
+      "version": "1.7.11",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.11.tgz",
+      "integrity": "sha512-zY8dSNyYcgzNRNT666/zOoAyImshm3ycKdoLsyDw/Bwo6+/uktb7p4xyApuef1dwEBo/U/SYQzbGBvV+nru2Xg==",
       "dev": true
     },
     "@webassemblyjs/helper-api-error": {
-      "version": "1.7.10",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.10.tgz",
-      "integrity": "sha512-DoYRlPWtuw3yd5BOr9XhtrmB6X1enYF0/54yNvQWGXZEPDF5PJVNI7zQ7gkcKfTESzp8bIBWailaFXEK/jjCsw==",
+      "version": "1.7.11",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.11.tgz",
+      "integrity": "sha512-7r1qXLmiglC+wPNkGuXCvkmalyEstKVwcueZRP2GNC2PAvxbLYwLLPr14rcdJaE4UtHxQKfFkuDFuv91ipqvXg==",
       "dev": true
     },
     "@webassemblyjs/helper-buffer": {
-      "version": "1.7.10",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.10.tgz",
-      "integrity": "sha512-+RMU3dt/dPh4EpVX4u5jxsOlw22tp3zjqE0m3ftU2tsYxnPULb4cyHlgaNd2KoWuwasCQqn8Mhr+TTdbtj3LlA==",
+      "version": "1.7.11",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.11.tgz",
+      "integrity": "sha512-MynuervdylPPh3ix+mKZloTcL06P8tenNH3sx6s0qE8SLR6DdwnfgA7Hc9NSYeob2jrW5Vql6GVlsQzKQCa13w==",
       "dev": true
     },
     "@webassemblyjs/helper-code-frame": {
-      "version": "1.7.10",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.10.tgz",
-      "integrity": "sha512-UiytbpKAULOEab2hUZK2ywXen4gWJVrgxtwY3Kn+eZaaSWaRM8z/7dAXRSoamhKFiBh1uaqxzE/XD9BLlug3gw==",
+      "version": "1.7.11",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.11.tgz",
+      "integrity": "sha512-T8ESC9KMXFTXA5urJcyor5cn6qWeZ4/zLPyWeEXZ03hj/x9weSokGNkVCdnhSabKGYWxElSdgJ+sFa9G/RdHNw==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/wast-printer": "1.7.10"
+        "@webassemblyjs/wast-printer": "1.7.11"
       }
     },
     "@webassemblyjs/helper-fsm": {
-      "version": "1.7.10",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.10.tgz",
-      "integrity": "sha512-w2vDtUK9xeSRtt5+RnnlRCI7wHEvLjF0XdnxJpgx+LJOvklTZPqWkuy/NhwHSLP19sm9H8dWxKeReMR7sCkGZA==",
+      "version": "1.7.11",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.11.tgz",
+      "integrity": "sha512-nsAQWNP1+8Z6tkzdYlXT0kxfa2Z1tRTARd8wYnc/e3Zv3VydVVnaeePgqUzFrpkGUyhUUxOl5ML7f1NuT+gC0A==",
       "dev": true
     },
     "@webassemblyjs/helper-module-context": {
-      "version": "1.7.10",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.10.tgz",
-      "integrity": "sha512-yE5x/LzZ3XdPdREmJijxzfrf+BDRewvO0zl8kvORgSWmxpRrkqY39KZSq6TSgIWBxkK4SrzlS3BsMCv2s1FpsQ==",
+      "version": "1.7.11",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.11.tgz",
+      "integrity": "sha512-JxfD5DX8Ygq4PvXDucq0M+sbUFA7BJAv/GGl9ITovqE+idGX+J3QSzJYz+LwQmL7fC3Rs+utvWoJxDb6pmC0qg==",
       "dev": true
     },
     "@webassemblyjs/helper-wasm-bytecode": {
-      "version": "1.7.10",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.10.tgz",
-      "integrity": "sha512-u5qy4SJ/OrxKxZqJ9N3qH4ZQgHaAzsopsYwLvoWJY6Q33r8PhT3VPyNMaJ7ZFoqzBnZlCcS/0f4Sp8WBxylXfg==",
+      "version": "1.7.11",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.11.tgz",
+      "integrity": "sha512-cMXeVS9rhoXsI9LLL4tJxBgVD/KMOKXuFqYb5oCJ/opScWpkCMEz9EJtkonaNcnLv2R3K5jIeS4TRj/drde1JQ==",
       "dev": true
     },
     "@webassemblyjs/helper-wasm-section": {
-      "version": "1.7.10",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.10.tgz",
-      "integrity": "sha512-Ecvww6sCkcjatcyctUrn22neSJHLN/TTzolMGG/N7S9rpbsTZ8c6Bl98GpSpV77EvzNijiNRHBG0+JO99qKz6g==",
+      "version": "1.7.11",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.11.tgz",
+      "integrity": "sha512-8ZRY5iZbZdtNFE5UFunB8mmBEAbSI3guwbrsCl4fWdfRiAcvqQpeqd5KHhSWLL5wuxo53zcaGZDBU64qgn4I4Q==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/ast": "1.7.10",
-        "@webassemblyjs/helper-buffer": "1.7.10",
-        "@webassemblyjs/helper-wasm-bytecode": "1.7.10",
-        "@webassemblyjs/wasm-gen": "1.7.10"
+        "@webassemblyjs/ast": "1.7.11",
+        "@webassemblyjs/helper-buffer": "1.7.11",
+        "@webassemblyjs/helper-wasm-bytecode": "1.7.11",
+        "@webassemblyjs/wasm-gen": "1.7.11"
       }
     },
     "@webassemblyjs/ieee754": {
-      "version": "1.7.10",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.7.10.tgz",
-      "integrity": "sha512-HRcWcY+YWt4+s/CvQn+vnSPfRaD4KkuzQFt5MNaELXXHSjelHlSEA8ZcqT69q0GTIuLWZ6JaoKar4yWHVpZHsQ==",
+      "version": "1.7.11",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.7.11.tgz",
+      "integrity": "sha512-Mmqx/cS68K1tSrvRLtaV/Lp3NZWzXtOHUW2IvDvl2sihAwJh4ACE0eL6A8FvMyDG9abes3saB6dMimLOs+HMoQ==",
       "dev": true,
       "requires": {
         "@xtuc/ieee754": "^1.2.0"
       }
     },
     "@webassemblyjs/leb128": {
-      "version": "1.7.10",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.7.10.tgz",
-      "integrity": "sha512-og8MciYlA8hvzCLR71hCuZKPbVBfLQeHv7ImKZ4nlyxrYbG7uJHYtHiHu6OV9SqrGuD03H/HtXC4Bgdjfm9FHw==",
+      "version": "1.7.11",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.7.11.tgz",
+      "integrity": "sha512-vuGmgZjjp3zjcerQg+JA+tGOncOnJLWVkt8Aze5eWQLwTQGNgVLcyOTqgSCxWTR4J42ijHbBxnuRaL1Rv7XMdw==",
       "dev": true,
       "requires": {
         "@xtuc/long": "4.2.1"
       }
     },
     "@webassemblyjs/utf8": {
-      "version": "1.7.10",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.7.10.tgz",
-      "integrity": "sha512-Ng6Pxv6siyZp635xCSnH3mKmIFgqWPCcGdoo0GBYgyGdxu7cUj4agV7Uu1a8REP66UYUFXJLudeGgd4RvuJAnQ==",
+      "version": "1.7.11",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.7.11.tgz",
+      "integrity": "sha512-C6GFkc7aErQIAH+BMrIdVSmW+6HSe20wg57HEC1uqJP8E/xpMjXqQUxkQw07MhNDSDcGpxI9G5JSNOQCqJk4sA==",
       "dev": true
     },
     "@webassemblyjs/wasm-edit": {
-      "version": "1.7.10",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.10.tgz",
-      "integrity": "sha512-e9RZFQlb+ZuYcKRcW9yl+mqX/Ycj9+3/+ppDI8nEE/NCY6FoK8f3dKBcfubYV/HZn44b+ND4hjh+4BYBt+sDnA==",
+      "version": "1.7.11",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.11.tgz",
+      "integrity": "sha512-FUd97guNGsCZQgeTPKdgxJhBXkUbMTY6hFPf2Y4OedXd48H97J+sOY2Ltaq6WGVpIH8o/TGOVNiVz/SbpEMJGg==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/ast": "1.7.10",
-        "@webassemblyjs/helper-buffer": "1.7.10",
-        "@webassemblyjs/helper-wasm-bytecode": "1.7.10",
-        "@webassemblyjs/helper-wasm-section": "1.7.10",
-        "@webassemblyjs/wasm-gen": "1.7.10",
-        "@webassemblyjs/wasm-opt": "1.7.10",
-        "@webassemblyjs/wasm-parser": "1.7.10",
-        "@webassemblyjs/wast-printer": "1.7.10"
+        "@webassemblyjs/ast": "1.7.11",
+        "@webassemblyjs/helper-buffer": "1.7.11",
+        "@webassemblyjs/helper-wasm-bytecode": "1.7.11",
+        "@webassemblyjs/helper-wasm-section": "1.7.11",
+        "@webassemblyjs/wasm-gen": "1.7.11",
+        "@webassemblyjs/wasm-opt": "1.7.11",
+        "@webassemblyjs/wasm-parser": "1.7.11",
+        "@webassemblyjs/wast-printer": "1.7.11"
       }
     },
     "@webassemblyjs/wasm-gen": {
-      "version": "1.7.10",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.10.tgz",
-      "integrity": "sha512-M0lb6cO2Y0PzDye/L39PqwV+jvO+2YxEG5ax+7dgq7EwXdAlpOMx1jxyXJTScQoeTpzOPIb+fLgX/IkLF8h2yw==",
+      "version": "1.7.11",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.11.tgz",
+      "integrity": "sha512-U/KDYp7fgAZX5KPfq4NOupK/BmhDc5Kjy2GIqstMhvvdJRcER/kUsMThpWeRP8BMn4LXaKhSTggIJPOeYHwISA==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/ast": "1.7.10",
-        "@webassemblyjs/helper-wasm-bytecode": "1.7.10",
-        "@webassemblyjs/ieee754": "1.7.10",
-        "@webassemblyjs/leb128": "1.7.10",
-        "@webassemblyjs/utf8": "1.7.10"
+        "@webassemblyjs/ast": "1.7.11",
+        "@webassemblyjs/helper-wasm-bytecode": "1.7.11",
+        "@webassemblyjs/ieee754": "1.7.11",
+        "@webassemblyjs/leb128": "1.7.11",
+        "@webassemblyjs/utf8": "1.7.11"
       }
     },
     "@webassemblyjs/wasm-opt": {
-      "version": "1.7.10",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.10.tgz",
-      "integrity": "sha512-R66IHGCdicgF5ZliN10yn5HaC7vwYAqrSVJGjtJJQp5+QNPBye6heWdVH/at40uh0uoaDN/UVUfXK0gvuUqtVg==",
+      "version": "1.7.11",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.11.tgz",
+      "integrity": "sha512-XynkOwQyiRidh0GLua7SkeHvAPXQV/RxsUeERILmAInZegApOUAIJfRuPYe2F7RcjOC9tW3Cb9juPvAC/sCqvg==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/ast": "1.7.10",
-        "@webassemblyjs/helper-buffer": "1.7.10",
-        "@webassemblyjs/wasm-gen": "1.7.10",
-        "@webassemblyjs/wasm-parser": "1.7.10"
+        "@webassemblyjs/ast": "1.7.11",
+        "@webassemblyjs/helper-buffer": "1.7.11",
+        "@webassemblyjs/wasm-gen": "1.7.11",
+        "@webassemblyjs/wasm-parser": "1.7.11"
       }
     },
     "@webassemblyjs/wasm-parser": {
-      "version": "1.7.10",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.10.tgz",
-      "integrity": "sha512-AEv8mkXVK63n/iDR3T693EzoGPnNAwKwT3iHmKJNBrrALAhhEjuPzo/lTE4U7LquEwyvg5nneSNdTdgrBaGJcA==",
+      "version": "1.7.11",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.11.tgz",
+      "integrity": "sha512-6lmXRTrrZjYD8Ng8xRyvyXQJYUQKYSXhJqXOBLw24rdiXsHAOlvw5PhesjdcaMadU/pyPQOJ5dHreMjBxwnQKg==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/ast": "1.7.10",
-        "@webassemblyjs/helper-api-error": "1.7.10",
-        "@webassemblyjs/helper-wasm-bytecode": "1.7.10",
-        "@webassemblyjs/ieee754": "1.7.10",
-        "@webassemblyjs/leb128": "1.7.10",
-        "@webassemblyjs/utf8": "1.7.10"
+        "@webassemblyjs/ast": "1.7.11",
+        "@webassemblyjs/helper-api-error": "1.7.11",
+        "@webassemblyjs/helper-wasm-bytecode": "1.7.11",
+        "@webassemblyjs/ieee754": "1.7.11",
+        "@webassemblyjs/leb128": "1.7.11",
+        "@webassemblyjs/utf8": "1.7.11"
       }
     },
     "@webassemblyjs/wast-parser": {
-      "version": "1.7.10",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.7.10.tgz",
-      "integrity": "sha512-YTPEtOBljkCL0VjDp4sHe22dAYSm3ZwdJ9+2NTGdtC7ayNvuip1wAhaAS8Zt9Q6SW9E5Jf5PX7YE3XWlrzR9cw==",
+      "version": "1.7.11",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.7.11.tgz",
+      "integrity": "sha512-lEyVCg2np15tS+dm7+JJTNhNWq9yTZvi3qEhAIIOaofcYlUp0UR5/tVqOwa/gXYr3gjwSZqw+/lS9dscyLelbQ==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/ast": "1.7.10",
-        "@webassemblyjs/floating-point-hex-parser": "1.7.10",
-        "@webassemblyjs/helper-api-error": "1.7.10",
-        "@webassemblyjs/helper-code-frame": "1.7.10",
-        "@webassemblyjs/helper-fsm": "1.7.10",
+        "@webassemblyjs/ast": "1.7.11",
+        "@webassemblyjs/floating-point-hex-parser": "1.7.11",
+        "@webassemblyjs/helper-api-error": "1.7.11",
+        "@webassemblyjs/helper-code-frame": "1.7.11",
+        "@webassemblyjs/helper-fsm": "1.7.11",
         "@xtuc/long": "4.2.1"
       }
     },
     "@webassemblyjs/wast-printer": {
-      "version": "1.7.10",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.7.10.tgz",
-      "integrity": "sha512-mJ3QKWtCchL1vhU/kZlJnLPuQZnlDOdZsyP0bbLWPGdYsQDnSBvyTLhzwBA3QAMlzEL9V4JHygEmK6/OTEyytA==",
+      "version": "1.7.11",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.7.11.tgz",
+      "integrity": "sha512-m5vkAsuJ32QpkdkDOUPGSltrg8Cuk3KBx4YrmAGQwCZPRdUHXxG4phIOuuycLemHFr74sWL9Wthqss4fzdzSwg==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/ast": "1.7.10",
-        "@webassemblyjs/wast-parser": "1.7.10",
+        "@webassemblyjs/ast": "1.7.11",
+        "@webassemblyjs/wast-parser": "1.7.11",
         "@xtuc/long": "4.2.1"
       }
     },
@@ -1576,9 +1665,9 @@
       }
     },
     "ajv": {
-      "version": "6.5.3",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz",
-      "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==",
+      "version": "6.6.2",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz",
+      "integrity": "sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==",
       "dev": true,
       "requires": {
         "fast-deep-equal": "^2.0.1",
@@ -1594,9 +1683,9 @@
       "dev": true
     },
     "ajv-keywords": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz",
-      "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=",
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz",
+      "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==",
       "dev": true
     },
     "align-text": {
@@ -1604,11 +1693,21 @@
       "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
       "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
       "dev": true,
-      "optional": true,
       "requires": {
         "kind-of": "^3.0.2",
         "longest": "^1.0.1",
         "repeat-string": "^1.5.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
       }
     },
     "amdefine": {
@@ -1617,11 +1716,6 @@
       "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
       "dev": true
     },
-    "angular-bootstrap-md": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/angular-bootstrap-md/-/angular-bootstrap-md-7.2.0.tgz",
-      "integrity": "sha512-fwoedFbUXlIaRMIXxhxce5Q5n6uY64Z9dinYbWCUI9mc7YsDKzc3YmVei8LWet9QWY9acUqSs/iug/WbH9O/iA=="
-    },
     "angular2-chartjs": {
       "version": "0.5.1",
       "resolved": "https://registry.npmjs.org/angular2-chartjs/-/angular2-chartjs-0.5.1.tgz",
@@ -1637,9 +1731,9 @@
       "dev": true
     },
     "ansi-escapes": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
-      "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==",
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
+      "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
       "dev": true
     },
     "ansi-gray": {
@@ -1680,6 +1774,12 @@
           "requires": {
             "color-name": "1.1.3"
           }
+        },
+        "color-name": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "dev": true
         }
       }
     },
@@ -1740,7 +1840,6 @@
       "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
       "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
       "dev": true,
-      "optional": true,
       "requires": {
         "delegates": "^1.0.0",
         "readable-stream": "^2.0.6"
@@ -1814,9 +1913,9 @@
       "dev": true
     },
     "array-unique": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
-      "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+      "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
       "dev": true
     },
     "arraybuffer.slice": {
@@ -1875,7 +1974,7 @@
         },
         "util": {
           "version": "0.10.3",
-          "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz",
+          "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
           "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
           "dev": true,
           "requires": {
@@ -1897,13 +1996,10 @@
       "dev": true
     },
     "async": {
-      "version": "2.6.1",
-      "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
-      "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
-      "dev": true,
-      "requires": {
-        "lodash": "^4.17.10"
-      }
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+      "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
+      "dev": true
     },
     "async-each": {
       "version": "1.0.1",
@@ -1948,71 +2044,19 @@
         "num2fraction": "^1.2.2",
         "postcss": "^7.0.6",
         "postcss-value-parser": "^3.3.1"
-      },
-      "dependencies": {
-        "chalk": {
-          "version": "2.4.2",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
-          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "5.5.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-              "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-              "dev": true,
-              "requires": {
-                "has-flag": "^3.0.0"
-              }
-            }
-          }
-        },
-        "postcss": {
-          "version": "7.0.11",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.11.tgz",
-          "integrity": "sha512-9AXb//5UcjeOEof9T+yPw3XTa5SL207ZOIC/lHYP4mbUTEh4M0rDAQekQpVANCZdwQwKhBtFZCk3i3h3h2hdWg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^2.4.2",
-            "source-map": "^0.6.1",
-            "supports-color": "^6.1.0"
-          }
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "6.1.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
-          "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
       }
     },
     "aws-sign2": {
       "version": "0.7.0",
       "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
       "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
-      "dev": true,
-      "optional": true
+      "dev": true
     },
     "aws4": {
       "version": "1.8.0",
       "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
       "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
-      "dev": true,
-      "optional": true
+      "dev": true
     },
     "babel-code-frame": {
       "version": "6.26.0",
@@ -2044,15 +2088,9 @@
             "supports-color": "^2.0.0"
           }
         },
-        "js-tokens": {
-          "version": "3.0.2",
-          "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
-          "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
-          "dev": true
-        },
         "supports-color": {
           "version": "2.0.0",
-          "resolved": "http://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
           "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
           "dev": true
         }
@@ -2074,10 +2112,10 @@
         "trim-right": "^1.0.1"
       },
       "dependencies": {
-        "jsesc": {
-          "version": "1.3.0",
-          "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
-          "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
           "dev": true
         }
       }
@@ -2148,14 +2186,6 @@
         "globals": "^9.18.0",
         "invariant": "^2.2.2",
         "lodash": "^4.17.4"
-      },
-      "dependencies": {
-        "globals": {
-          "version": "9.18.0",
-          "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
-          "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
-          "dev": true
-        }
       }
     },
     "babel-types": {
@@ -2168,14 +2198,6 @@
         "esutils": "^2.0.2",
         "lodash": "^4.17.4",
         "to-fast-properties": "^1.0.3"
-      },
-      "dependencies": {
-        "to-fast-properties": {
-          "version": "1.0.3",
-          "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
-          "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=",
-          "dev": true
-        }
       }
     },
     "babylon": {
@@ -2248,12 +2270,6 @@
             "is-data-descriptor": "^1.0.0",
             "kind-of": "^6.0.2"
           }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
         }
       }
     },
@@ -2282,14 +2298,6 @@
       "dev": true,
       "requires": {
         "safe-buffer": "5.1.2"
-      },
-      "dependencies": {
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-          "dev": true
-        }
       }
     },
     "batch": {
@@ -2329,9 +2337,9 @@
       "dev": true
     },
     "binary-extensions": {
-      "version": "1.11.0",
-      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz",
-      "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=",
+      "version": "1.13.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.0.tgz",
+      "integrity": "sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw==",
       "dev": true
     },
     "blob": {
@@ -2357,6 +2365,14 @@
       "dev": true,
       "requires": {
         "minimist": "^1.2.0"
+      },
+      "dependencies": {
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+          "dev": true
+        }
       }
     },
     "bluebird": {
@@ -2387,17 +2403,6 @@
         "qs": "6.5.2",
         "raw-body": "2.3.3",
         "type-is": "~1.6.16"
-      },
-      "dependencies": {
-        "iconv-lite": {
-          "version": "0.4.23",
-          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
-          "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
-          "dev": true,
-          "requires": {
-            "safer-buffer": ">= 2.1.2 < 3"
-          }
-        }
       }
     },
     "bonjour": {
@@ -2421,9 +2426,9 @@
       "dev": true
     },
     "brace-expansion": {
-      "version": "1.1.8",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
-      "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
       "dev": true,
       "requires": {
         "balanced-match": "^1.0.0",
@@ -2448,12 +2453,6 @@
         "to-regex": "^3.0.1"
       },
       "dependencies": {
-        "array-unique": {
-          "version": "0.3.2",
-          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-          "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
-          "dev": true
-        },
         "extend-shallow": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
@@ -2506,14 +2505,6 @@
         "des.js": "^1.0.0",
         "inherits": "^2.0.1",
         "safe-buffer": "^5.1.2"
-      },
-      "dependencies": {
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-          "dev": true
-        }
       }
     },
     "browserify-rsa": {
@@ -2551,20 +2542,20 @@
       }
     },
     "browserslist": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.4.0.tgz",
-      "integrity": "sha512-tQkHS8VVxWbrjnNDXgt7/+SuPJ7qDvD0Y2e6bLtoQluR2SPvlmPUcfcU75L1KAalhqULlIFJlJ6BDfnYyJxJsw==",
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.4.1.tgz",
+      "integrity": "sha512-pEBxEXg7JwaakBXjATYw/D1YZh4QUSCX/Mnd/wnqSRPPSi1U39iDhDoKGoBUcraKdxDlrYqJxSI5nNvD+dWP2A==",
       "dev": true,
       "requires": {
-        "caniuse-lite": "^1.0.30000928",
-        "electron-to-chromium": "^1.3.100",
+        "caniuse-lite": "^1.0.30000929",
+        "electron-to-chromium": "^1.3.103",
         "node-releases": "^1.1.3"
       }
     },
     "browserstack": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.1.tgz",
-      "integrity": "sha512-O8VMT64P9NOLhuIoD4YngyxBURefaSdR4QdhG8l6HZ9VxtU7jc3m6jLufFwKA5gaf7fetfB2TnRJnMxyob+heg==",
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.2.tgz",
+      "integrity": "sha512-+6AFt9HzhKykcPF79W6yjEUJcdvZOV0lIXdkORXMJftGrDl0OKWqRF4GHqpDNkxiceDT/uB7Fb/aDwktvXX7dg==",
       "dev": true,
       "requires": {
         "https-proxy-agent": "^2.2.1"
@@ -2720,10 +2711,11 @@
       "dev": true
     },
     "camelcase": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
-      "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
-      "dev": true
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+      "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
+      "dev": true,
+      "optional": true
     },
     "camelcase-keys": {
       "version": "2.1.0",
@@ -2734,21 +2726,12 @@
       "requires": {
         "camelcase": "^2.0.0",
         "map-obj": "^1.0.0"
-      },
-      "dependencies": {
-        "camelcase": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
-          "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
-          "dev": true,
-          "optional": true
-        }
       }
     },
     "caniuse-lite": {
-      "version": "1.0.30000928",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000928.tgz",
-      "integrity": "sha512-aSpMWRXL6ZXNnzm8hgE4QDLibG5pVJ2Ujzsuj3icazlIkxXkPXtL+BWnMx6FBkWmkZgBHGUxPZQvrbRw2ZTxhg==",
+      "version": "1.0.30000936",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000936.tgz",
+      "integrity": "sha512-orX4IdpbFhdNO7bTBhSbahp1EBpqzBc+qrvTRVUFfZgA4zta7TdM6PN5ZxkEUgDnz36m+PfWGcdX7AVfFWItJw==",
       "dev": true
     },
     "canonical-path": {
@@ -2775,14 +2758,25 @@
       }
     },
     "chalk": {
-      "version": "2.4.1",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-      "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
       "dev": true,
       "requires": {
         "ansi-styles": "^3.2.1",
         "escape-string-regexp": "^1.0.5",
         "supports-color": "^5.3.0"
+      },
+      "dependencies": {
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
       }
     },
     "chardet": {
@@ -2993,23 +2987,6 @@
         "is-plain-object": "^2.0.4",
         "kind-of": "^6.0.0",
         "shallow-clone": "^1.0.0"
-      },
-      "dependencies": {
-        "for-own": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
-          "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
-          "dev": true,
-          "requires": {
-            "for-in": "^1.0.1"
-          }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        }
       }
     },
     "co": {
@@ -3044,6 +3021,12 @@
         "sprintf-js": "^1.1.1"
       },
       "dependencies": {
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
+        },
         "sprintf-js": {
           "version": "1.1.2",
           "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
@@ -3068,9 +3051,9 @@
       "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0="
     },
     "color-name": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
     },
     "color-support": {
       "version": "1.1.3",
@@ -3079,9 +3062,9 @@
       "dev": true
     },
     "colors": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
-      "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz",
+      "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==",
       "dev": true
     },
     "combine-lists": {
@@ -3098,7 +3081,6 @@
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
       "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==",
       "dev": true,
-      "optional": true,
       "requires": {
         "delayed-stream": "~1.0.0"
       }
@@ -3161,14 +3143,6 @@
         "on-headers": "~1.0.1",
         "safe-buffer": "5.1.2",
         "vary": "~1.1.2"
-      },
-      "dependencies": {
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-          "dev": true
-        }
       }
     },
     "concat-map": {
@@ -3199,12 +3173,35 @@
         "finalhandler": "1.1.0",
         "parseurl": "~1.3.2",
         "utils-merge": "1.0.1"
+      },
+      "dependencies": {
+        "finalhandler": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz",
+          "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=",
+          "dev": true,
+          "requires": {
+            "debug": "2.6.9",
+            "encodeurl": "~1.0.1",
+            "escape-html": "~1.0.3",
+            "on-finished": "~2.3.0",
+            "parseurl": "~1.3.2",
+            "statuses": "~1.3.1",
+            "unpipe": "~1.0.0"
+          }
+        },
+        "statuses": {
+          "version": "1.3.1",
+          "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
+          "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=",
+          "dev": true
+        }
       }
     },
     "connect-history-api-fallback": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz",
-      "integrity": "sha1-sGhzk0vF40T+9hGhlqb6rgruAVo=",
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz",
+      "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==",
       "dev": true
     },
     "console-browserify": {
@@ -3220,8 +3217,7 @@
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
       "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
-      "dev": true,
-      "optional": true
+      "dev": true
     },
     "constants-browserify": {
       "version": "1.0.0",
@@ -3299,9 +3295,9 @@
       }
     },
     "core-js": {
-      "version": "2.6.1",
-      "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.1.tgz",
-      "integrity": "sha512-L72mmmEayPJBejKIWe2pYtGis5r0tQ5NaJekdhyXgeMQTpJoBsH0NL4ElY2LfSoV15xeQWKQ+XTTOZdyero5Xg=="
+      "version": "2.6.4",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.4.tgz",
+      "integrity": "sha512-05qQ5hXShcqGkPZpXEFLIpxayZscVD2kuMBZewxiIPPEagukO4mqgPA9CWhUvFBJfy3ODdK2p9xyHh7FTU9/7A=="
     },
     "core-util-is": {
       "version": "1.0.2",
@@ -3553,6 +3549,14 @@
       "dev": true,
       "requires": {
         "strip-bom": "^3.0.0"
+      },
+      "dependencies": {
+        "strip-bom": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+          "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+          "dev": true
+        }
       }
     },
     "define-property": {
@@ -3593,12 +3597,6 @@
             "is-data-descriptor": "^1.0.0",
             "kind-of": "^6.0.2"
           }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
         }
       }
     },
@@ -3631,17 +3629,11 @@
           "dependencies": {
             "pify": {
               "version": "2.3.0",
-              "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+              "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
               "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
               "dev": true
             }
           }
-        },
-        "pify": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
-          "dev": true
         }
       }
     },
@@ -3661,8 +3653,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",
@@ -3731,12 +3722,11 @@
       }
     },
     "dir-glob": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz",
-      "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==",
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz",
+      "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==",
       "dev": true,
       "requires": {
-        "arrify": "^1.0.1",
         "path-type": "^3.0.0"
       }
     },
@@ -3839,9 +3829,9 @@
       "dev": true
     },
     "duplexify": {
-      "version": "3.6.1",
-      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz",
-      "integrity": "sha512-vM58DwdnKmty+FSPzT14K9JXb90H+j5emaR4KYbr2KTIz00WHGbWOe5ghQTx233ZCLZtrGDALzKwcjEtSt35mA==",
+      "version": "3.7.1",
+      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
+      "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
       "dev": true,
       "requires": {
         "end-of-stream": "^1.0.0",
@@ -3867,9 +3857,9 @@
       "dev": true
     },
     "electron-to-chromium": {
-      "version": "1.3.103",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.103.tgz",
-      "integrity": "sha512-tObPqGmY9X8MUM8i3MEimYmbnLLf05/QV5gPlkR8MQ3Uj8G8B2govE1U4cQcBYtv3ymck9Y8cIOu4waoiykMZQ==",
+      "version": "1.3.113",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.113.tgz",
+      "integrity": "sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g==",
       "dev": true
     },
     "elliptic": {
@@ -4072,15 +4062,9 @@
         "source-map": "~0.2.0"
       },
       "dependencies": {
-        "esprima": {
-          "version": "2.7.3",
-          "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
-          "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
-          "dev": true
-        },
         "source-map": {
           "version": "0.2.0",
-          "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz",
           "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=",
           "dev": true,
           "optional": true,
@@ -4109,9 +4093,9 @@
       }
     },
     "esprima": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
-      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+      "version": "2.7.3",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
+      "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
       "dev": true
     },
     "esrecurse": {
@@ -4171,9 +4155,9 @@
       "dev": true
     },
     "events": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
-      "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz",
+      "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==",
       "dev": true
     },
     "eventsource": {
@@ -4222,12 +4206,6 @@
             "shebang-command": "^1.2.0",
             "which": "^1.2.9"
           }
-        },
-        "semver": {
-          "version": "5.6.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
-          "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
-          "dev": true
         }
       }
     },
@@ -4248,6 +4226,12 @@
         "braces": "^0.1.2"
       },
       "dependencies": {
+        "array-unique": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
+          "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
+          "dev": true
+        },
         "braces": {
           "version": "0.1.5",
           "resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz",
@@ -4355,6 +4339,15 @@
           "requires": {
             "isarray": "1.0.0"
           }
+        },
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
         }
       }
     },
@@ -4401,33 +4394,6 @@
           "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
           "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
           "dev": true
-        },
-        "finalhandler": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
-          "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
-          "dev": true,
-          "requires": {
-            "debug": "2.6.9",
-            "encodeurl": "~1.0.2",
-            "escape-html": "~1.0.3",
-            "on-finished": "~2.3.0",
-            "parseurl": "~1.3.2",
-            "statuses": "~1.4.0",
-            "unpipe": "~1.0.0"
-          }
-        },
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-          "dev": true
-        },
-        "statuses": {
-          "version": "1.4.0",
-          "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
-          "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==",
-          "dev": true
         }
       }
     },
@@ -4467,6 +4433,17 @@
         "chardet": "^0.7.0",
         "iconv-lite": "^0.4.24",
         "tmp": "^0.0.33"
+      },
+      "dependencies": {
+        "iconv-lite": {
+          "version": "0.4.24",
+          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+          "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+          "dev": true,
+          "requires": {
+            "safer-buffer": ">= 2.1.2 < 3"
+          }
+        }
       }
     },
     "extglob": {
@@ -4485,12 +4462,6 @@
         "to-regex": "^3.0.1"
       },
       "dependencies": {
-        "array-unique": {
-          "version": "0.3.2",
-          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-          "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
-          "dev": true
-        },
         "define-property": {
           "version": "1.0.0",
           "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
@@ -4537,12 +4508,6 @@
             "is-data-descriptor": "^1.0.0",
             "kind-of": "^6.0.2"
           }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
         }
       }
     },
@@ -4571,9 +4536,9 @@
       "dev": true
     },
     "fast-glob": {
-      "version": "2.2.4",
-      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.4.tgz",
-      "integrity": "sha512-FjK2nCGI/McyzgNtTESqaWP3trPvHyRyoyY70hxjc3oKPNmDe8taohLZpoVKoUjW85tbU5txaYUZCNtVzygl1g==",
+      "version": "2.2.6",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.6.tgz",
+      "integrity": "sha512-0BvMaZc1k9F+MeWWMe8pL6YltFzZYcJsYU7D4JyDA6PAczaXvxqQQ/z+mDF7/4Mw01DeUc+i3CTKajnkANkV4w==",
       "dev": true,
       "requires": {
         "@mrmlnc/readdir-enhanced": "^2.2.1",
@@ -4681,26 +4646,18 @@
       }
     },
     "finalhandler": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz",
-      "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=",
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
+      "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
       "dev": true,
       "requires": {
         "debug": "2.6.9",
-        "encodeurl": "~1.0.1",
+        "encodeurl": "~1.0.2",
         "escape-html": "~1.0.3",
         "on-finished": "~2.3.0",
         "parseurl": "~1.3.2",
-        "statuses": "~1.3.1",
+        "statuses": "~1.4.0",
         "unpipe": "~1.0.0"
-      },
-      "dependencies": {
-        "statuses": {
-          "version": "1.3.1",
-          "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
-          "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=",
-          "dev": true
-        }
       }
     },
     "find-cache-dir": {
@@ -4736,13 +4693,13 @@
       "dev": true
     },
     "flush-write-stream": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz",
-      "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==",
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
+      "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
       "dev": true,
       "requires": {
-        "inherits": "^2.0.1",
-        "readable-stream": "^2.0.4"
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.3.6"
       }
     },
     "follow-redirects": {
@@ -4765,11 +4722,6 @@
         }
       }
     },
-    "font-awesome": {
-      "version": "4.7.0",
-      "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
-      "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM="
-    },
     "for-in": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@@ -4777,9 +4729,9 @@
       "dev": true
     },
     "for-own": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz",
-      "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=",
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
+      "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
       "dev": true,
       "requires": {
         "for-in": "^1.0.1"
@@ -4796,7 +4748,6 @@
       "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
       "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
       "dev": true,
-      "optional": true,
       "requires": {
         "asynckit": "^0.4.0",
         "combined-stream": "^1.0.6",
@@ -4888,9 +4839,9 @@
       "dev": true
     },
     "fsevents": {
-      "version": "1.2.4",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz",
-      "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==",
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz",
+      "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==",
       "dev": true,
       "optional": true,
       "requires": {
@@ -4900,29 +4851,24 @@
       "dependencies": {
         "abbrev": {
           "version": "1.1.1",
-          "resolved": "",
-          "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "ansi-regex": {
           "version": "2.1.1",
-          "resolved": "",
-          "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
-          "dev": true,
-          "optional": true
+          "bundled": true,
+          "dev": true
         },
         "aproba": {
           "version": "1.2.0",
-          "resolved": "",
-          "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "are-we-there-yet": {
-          "version": "1.1.4",
-          "resolved": "",
-          "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
+          "version": "1.1.5",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
@@ -4932,61 +4878,48 @@
         },
         "balanced-match": {
           "version": "1.0.0",
-          "resolved": "",
-          "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
-          "dev": true,
-          "optional": true
+          "bundled": true,
+          "dev": true
         },
         "brace-expansion": {
           "version": "1.1.11",
-          "resolved": "",
-          "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+          "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "balanced-match": "^1.0.0",
             "concat-map": "0.0.1"
           }
         },
         "chownr": {
-          "version": "1.0.1",
-          "resolved": "",
-          "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=",
+          "version": "1.1.1",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "code-point-at": {
           "version": "1.1.0",
-          "resolved": "",
-          "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
-          "dev": true,
-          "optional": true
+          "bundled": true,
+          "dev": true
         },
         "concat-map": {
           "version": "0.0.1",
-          "resolved": "",
-          "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
-          "dev": true,
-          "optional": true
+          "bundled": true,
+          "dev": true
         },
         "console-control-strings": {
           "version": "1.1.0",
-          "resolved": "",
-          "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
-          "dev": true,
-          "optional": true
+          "bundled": true,
+          "dev": true
         },
         "core-util-is": {
           "version": "1.0.2",
-          "resolved": "",
-          "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "debug": {
           "version": "2.6.9",
-          "resolved": "",
-          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
@@ -4994,30 +4927,26 @@
           }
         },
         "deep-extend": {
-          "version": "0.5.1",
-          "resolved": "",
-          "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==",
+          "version": "0.6.0",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "delegates": {
           "version": "1.0.0",
-          "resolved": "",
-          "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "detect-libc": {
           "version": "1.0.3",
-          "resolved": "",
-          "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "fs-minipass": {
           "version": "1.2.5",
-          "resolved": "",
-          "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
@@ -5026,15 +4955,13 @@
         },
         "fs.realpath": {
           "version": "1.0.0",
-          "resolved": "",
-          "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "gauge": {
           "version": "2.7.4",
-          "resolved": "",
-          "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
@@ -5049,9 +4976,8 @@
           }
         },
         "glob": {
-          "version": "7.1.2",
-          "resolved": "",
-          "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+          "version": "7.1.3",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
@@ -5065,25 +4991,22 @@
         },
         "has-unicode": {
           "version": "2.0.1",
-          "resolved": "",
-          "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "iconv-lite": {
-          "version": "0.4.21",
-          "resolved": "",
-          "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==",
+          "version": "0.4.24",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
-            "safer-buffer": "^2.1.0"
+            "safer-buffer": ">= 2.1.2 < 3"
           }
         },
         "ignore-walk": {
           "version": "3.0.1",
-          "resolved": "",
-          "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
@@ -5092,8 +5015,7 @@
         },
         "inflight": {
           "version": "1.0.6",
-          "resolved": "",
-          "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
@@ -5103,67 +5025,54 @@
         },
         "inherits": {
           "version": "2.0.3",
-          "resolved": "",
-          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
-          "dev": true,
-          "optional": true
+          "bundled": true,
+          "dev": true
         },
         "ini": {
           "version": "1.3.5",
-          "resolved": "",
-          "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "is-fullwidth-code-point": {
           "version": "1.0.0",
-          "resolved": "",
-          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+          "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "number-is-nan": "^1.0.0"
           }
         },
         "isarray": {
           "version": "1.0.0",
-          "resolved": "",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "minimatch": {
           "version": "3.0.4",
-          "resolved": "",
-          "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+          "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "brace-expansion": "^1.1.7"
           }
         },
         "minimist": {
           "version": "0.0.8",
-          "resolved": "",
-          "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
-          "dev": true,
-          "optional": true
+          "bundled": true,
+          "dev": true
         },
         "minipass": {
-          "version": "2.2.4",
-          "resolved": "",
-          "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==",
+          "version": "2.3.5",
+          "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
-            "safe-buffer": "^5.1.1",
+            "safe-buffer": "^5.1.2",
             "yallist": "^3.0.0"
           }
         },
         "minizlib": {
-          "version": "1.1.0",
-          "resolved": "",
-          "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==",
+          "version": "1.2.1",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
@@ -5172,25 +5081,21 @@
         },
         "mkdirp": {
           "version": "0.5.1",
-          "resolved": "",
-          "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+          "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "minimist": "0.0.8"
           }
         },
         "ms": {
           "version": "2.0.0",
-          "resolved": "",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "needle": {
-          "version": "2.2.0",
-          "resolved": "",
-          "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==",
+          "version": "2.2.4",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
@@ -5200,19 +5105,18 @@
           }
         },
         "node-pre-gyp": {
-          "version": "0.10.0",
-          "resolved": "",
-          "integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==",
+          "version": "0.10.3",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
             "detect-libc": "^1.0.2",
             "mkdirp": "^0.5.1",
-            "needle": "^2.2.0",
+            "needle": "^2.2.1",
             "nopt": "^4.0.1",
             "npm-packlist": "^1.1.6",
             "npmlog": "^4.0.2",
-            "rc": "^1.1.7",
+            "rc": "^1.2.7",
             "rimraf": "^2.6.1",
             "semver": "^5.3.0",
             "tar": "^4"
@@ -5220,8 +5124,7 @@
         },
         "nopt": {
           "version": "4.0.1",
-          "resolved": "",
-          "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
@@ -5230,16 +5133,14 @@
           }
         },
         "npm-bundled": {
-          "version": "1.0.3",
-          "resolved": "",
-          "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==",
+          "version": "1.0.5",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "npm-packlist": {
-          "version": "1.1.10",
-          "resolved": "",
-          "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==",
+          "version": "1.2.0",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
@@ -5249,8 +5150,7 @@
         },
         "npmlog": {
           "version": "4.1.2",
-          "resolved": "",
-          "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
@@ -5262,46 +5162,38 @@
         },
         "number-is-nan": {
           "version": "1.0.1",
-          "resolved": "",
-          "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
-          "dev": true,
-          "optional": true
+          "bundled": true,
+          "dev": true
         },
         "object-assign": {
           "version": "4.1.1",
-          "resolved": "",
-          "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "once": {
           "version": "1.4.0",
-          "resolved": "",
-          "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+          "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "wrappy": "1"
           }
         },
         "os-homedir": {
           "version": "1.0.2",
-          "resolved": "",
-          "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "os-tmpdir": {
           "version": "1.0.2",
-          "resolved": "",
-          "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "osenv": {
           "version": "0.1.5",
-          "resolved": "",
-          "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
@@ -5311,26 +5203,23 @@
         },
         "path-is-absolute": {
           "version": "1.0.1",
-          "resolved": "",
-          "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "process-nextick-args": {
           "version": "2.0.0",
-          "resolved": "",
-          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "rc": {
-          "version": "1.2.7",
-          "resolved": "",
-          "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==",
+          "version": "1.2.8",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
-            "deep-extend": "^0.5.1",
+            "deep-extend": "^0.6.0",
             "ini": "~1.3.0",
             "minimist": "^1.2.0",
             "strip-json-comments": "~2.0.1"
@@ -5338,8 +5227,7 @@
           "dependencies": {
             "minimist": {
               "version": "1.2.0",
-              "resolved": "",
-              "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+              "bundled": true,
               "dev": true,
               "optional": true
             }
@@ -5347,8 +5235,7 @@
         },
         "readable-stream": {
           "version": "2.3.6",
-          "resolved": "",
-          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
@@ -5362,63 +5249,53 @@
           }
         },
         "rimraf": {
-          "version": "2.6.2",
-          "resolved": "",
-          "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
+          "version": "2.6.3",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
-            "glob": "^7.0.5"
+            "glob": "^7.1.3"
           }
         },
         "safe-buffer": {
-          "version": "5.1.1",
-          "resolved": "",
-          "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
-          "dev": true,
-          "optional": true
+          "version": "5.1.2",
+          "bundled": true,
+          "dev": true
         },
         "safer-buffer": {
           "version": "2.1.2",
-          "resolved": "",
-          "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "sax": {
           "version": "1.2.4",
-          "resolved": "",
-          "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "semver": {
-          "version": "5.5.0",
-          "resolved": "",
-          "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
+          "version": "5.6.0",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "set-blocking": {
           "version": "2.0.0",
-          "resolved": "",
-          "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "signal-exit": {
           "version": "3.0.2",
-          "resolved": "",
-          "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "string-width": {
           "version": "1.0.2",
-          "resolved": "",
-          "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+          "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "code-point-at": "^1.0.0",
             "is-fullwidth-code-point": "^1.0.0",
@@ -5427,8 +5304,7 @@
         },
         "string_decoder": {
           "version": "1.1.1",
-          "resolved": "",
-          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
@@ -5437,67 +5313,57 @@
         },
         "strip-ansi": {
           "version": "3.0.1",
-          "resolved": "",
-          "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+          "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "ansi-regex": "^2.0.0"
           }
         },
         "strip-json-comments": {
           "version": "2.0.1",
-          "resolved": "",
-          "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "tar": {
-          "version": "4.4.1",
-          "resolved": "",
-          "integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==",
+          "version": "4.4.8",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
-            "chownr": "^1.0.1",
+            "chownr": "^1.1.1",
             "fs-minipass": "^1.2.5",
-            "minipass": "^2.2.4",
-            "minizlib": "^1.1.0",
+            "minipass": "^2.3.4",
+            "minizlib": "^1.1.1",
             "mkdirp": "^0.5.0",
-            "safe-buffer": "^5.1.1",
+            "safe-buffer": "^5.1.2",
             "yallist": "^3.0.2"
           }
         },
         "util-deprecate": {
           "version": "1.0.2",
-          "resolved": "",
-          "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+          "bundled": true,
           "dev": true,
           "optional": true
         },
         "wide-align": {
-          "version": "1.1.2",
-          "resolved": "",
-          "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
+          "version": "1.1.3",
+          "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
-            "string-width": "^1.0.2"
+            "string-width": "^1.0.2 || 2"
           }
         },
         "wrappy": {
           "version": "1.0.2",
-          "resolved": "",
-          "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
-          "dev": true,
-          "optional": true
+          "bundled": true,
+          "dev": true
         },
         "yallist": {
-          "version": "3.0.2",
-          "resolved": "",
-          "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=",
-          "dev": true,
-          "optional": true
+          "version": "3.0.3",
+          "bundled": true,
+          "dev": true
         }
       }
     },
@@ -5506,7 +5372,6 @@
       "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",
@@ -5519,7 +5384,6 @@
       "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
       "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
       "dev": true,
-      "optional": true,
       "requires": {
         "aproba": "^1.0.3",
         "console-control-strings": "^1.0.0",
@@ -5557,8 +5421,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",
@@ -5582,9 +5445,9 @@
       }
     },
     "glob": {
-      "version": "7.1.2",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
-      "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+      "version": "7.1.3",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+      "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
       "dev": true,
       "requires": {
         "fs.realpath": "^1.0.0",
@@ -5659,9 +5522,9 @@
       "dev": true
     },
     "globals": {
-      "version": "11.9.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-11.9.0.tgz",
-      "integrity": "sha512-5cJVtyXWH8PiJPVLZzzoIizXx944O4OmRro5MWKx5fT4MgcN7OfaMutPeaTdJCCURwbWdhhcCWcKIffPnmTzBg==",
+      "version": "9.18.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
+      "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
       "dev": true
     },
     "globby": {
@@ -5676,14 +5539,6 @@
         "ignore": "^3.3.5",
         "pify": "^3.0.0",
         "slash": "^1.0.0"
-      },
-      "dependencies": {
-        "pify": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
-          "dev": true
-        }
       }
     },
     "globule": {
@@ -5708,11 +5563,16 @@
       }
     },
     "graceful-fs": {
-      "version": "4.1.11",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
-      "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
+      "version": "4.1.15",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+      "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
       "dev": true
     },
+    "hammerjs": {
+      "version": "2.0.8",
+      "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
+      "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE="
+    },
     "handle-thing": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz",
@@ -5720,9 +5580,9 @@
       "dev": true
     },
     "handlebars": {
-      "version": "4.0.12",
-      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.12.tgz",
-      "integrity": "sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA==",
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.0.tgz",
+      "integrity": "sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w==",
       "dev": true,
       "requires": {
         "async": "^2.5.0",
@@ -5731,6 +5591,15 @@
         "uglify-js": "^3.1.4"
       },
       "dependencies": {
+        "async": {
+          "version": "2.6.1",
+          "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
+          "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
+          "dev": true,
+          "requires": {
+            "lodash": "^4.17.10"
+          }
+        },
         "source-map": {
           "version": "0.6.1",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -5743,33 +5612,16 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
       "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
-      "dev": true,
-      "optional": true
+      "dev": true
     },
     "har-validator": {
       "version": "5.1.3",
       "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
       "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
       "dev": true,
-      "optional": true,
       "requires": {
         "ajv": "^6.5.5",
         "har-schema": "^2.0.0"
-      },
-      "dependencies": {
-        "ajv": {
-          "version": "6.7.0",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.7.0.tgz",
-          "integrity": "sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "fast-deep-equal": "^2.0.1",
-            "fast-json-stable-stringify": "^2.0.0",
-            "json-schema-traverse": "^0.4.1",
-            "uri-js": "^4.2.2"
-          }
-        }
       }
     },
     "has-ansi": {
@@ -5814,8 +5666,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",
@@ -5838,26 +5689,6 @@
         "kind-of": "^4.0.0"
       },
       "dependencies": {
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "dev": true,
-          "requires": {
-            "kind-of": "^3.0.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "dev": true,
-              "requires": {
-                "is-buffer": "^1.1.5"
-              }
-            }
-          }
-        },
         "kind-of": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
@@ -5953,15 +5784,6 @@
             "string_decoder": "^1.1.1",
             "util-deprecate": "^1.0.1"
           }
-        },
-        "string_decoder": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz",
-          "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
         }
       }
     },
@@ -6056,7 +5878,6 @@
       "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
       "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
       "dev": true,
-      "optional": true,
       "requires": {
         "assert-plus": "^1.0.0",
         "jsprim": "^1.2.2",
@@ -6106,15 +5927,15 @@
       }
     },
     "i18next": {
-      "version": "12.1.0",
-      "resolved": "https://registry.npmjs.org/i18next/-/i18next-12.1.0.tgz",
-      "integrity": "sha512-AexmwGkKxwKfo5fGeXTWEY4xqzRPigQ1S/0InOUUVziGO54cd4fKyYK8ED1Thx9fd+WA3fRSZ+1iekvFQMbsFw==",
+      "version": "14.1.1",
+      "resolved": "https://registry.npmjs.org/i18next/-/i18next-14.1.1.tgz",
+      "integrity": "sha512-HItn9RHLyrDqe6pw6qXMYHGPHNc3y1FZndJfBlD6k4sRS0FAlYLvqCDVIWFc1XultBgsv348TtvL/lleG6JgBg==",
       "dev": true
     },
     "iconv-lite": {
-      "version": "0.4.24",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
-      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "version": "0.4.23",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
+      "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
       "dev": true,
       "requires": {
         "safer-buffer": ">= 2.1.2 < 3"
@@ -6304,9 +6125,9 @@
       "dev": true
     },
     "inquirer": {
-      "version": "6.2.0",
-      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz",
-      "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==",
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.1.tgz",
+      "integrity": "sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg==",
       "dev": true,
       "requires": {
         "ansi-escapes": "^3.0.0",
@@ -6320,7 +6141,7 @@
         "run-async": "^2.2.0",
         "rxjs": "^6.1.0",
         "string-width": "^2.1.0",
-        "strip-ansi": "^4.0.0",
+        "strip-ansi": "^5.0.0",
         "through": "^2.3.6"
       },
       "dependencies": {
@@ -6344,15 +6165,34 @@
           "requires": {
             "is-fullwidth-code-point": "^2.0.0",
             "strip-ansi": "^4.0.0"
+          },
+          "dependencies": {
+            "strip-ansi": {
+              "version": "4.0.0",
+              "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+              "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+              "dev": true,
+              "requires": {
+                "ansi-regex": "^3.0.0"
+              }
+            }
           }
         },
         "strip-ansi": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz",
+          "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==",
           "dev": true,
           "requires": {
-            "ansi-regex": "^3.0.0"
+            "ansi-regex": "^4.0.0"
+          },
+          "dependencies": {
+            "ansi-regex": {
+              "version": "4.0.0",
+              "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+              "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+              "dev": true
+            }
           }
         }
       }
@@ -6429,6 +6269,17 @@
       "dev": true,
       "requires": {
         "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
       }
     },
     "is-arrayish": {
@@ -6452,15 +6303,6 @@
       "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
       "dev": true
     },
-    "is-builtin-module": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
-      "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
-      "dev": true,
-      "requires": {
-        "builtin-modules": "^1.0.0"
-      }
-    },
     "is-data-descriptor": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
@@ -6468,6 +6310,17 @@
       "dev": true,
       "requires": {
         "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
       }
     },
     "is-descriptor": {
@@ -6562,7 +6415,18 @@
       "dev": true,
       "requires": {
         "kind-of": "^3.0.2"
-      }
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
     },
     "is-path-cwd": {
       "version": "1.0.0",
@@ -6571,9 +6435,9 @@
       "dev": true
     },
     "is-path-in-cwd": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz",
-      "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz",
+      "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==",
       "dev": true,
       "requires": {
         "is-path-inside": "^1.0.0"
@@ -6649,8 +6513,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",
@@ -6719,18 +6582,6 @@
         "wordwrap": "^1.0.0"
       },
       "dependencies": {
-        "async": {
-          "version": "1.5.2",
-          "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
-          "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
-          "dev": true
-        },
-        "esprima": {
-          "version": "2.7.3",
-          "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
-          "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
-          "dev": true
-        },
         "glob": {
           "version": "5.0.15",
           "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
@@ -6750,12 +6601,6 @@
           "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
           "dev": true
         },
-        "resolve": {
-          "version": "1.1.7",
-          "resolved": "http://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
-          "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
-          "dev": true
-        },
         "supports-color": {
           "version": "3.2.3",
           "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
@@ -6764,33 +6609,60 @@
           "requires": {
             "has-flag": "^1.0.0"
           }
-        },
-        "wordwrap": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
-          "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
-          "dev": true
         }
       }
     },
     "istanbul-api": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.0.6.tgz",
-      "integrity": "sha512-8W5oeAGWXhtTJjAyVfvavOLVyZCTNCKsyF6GON/INKlBdO7uJ/bv3qnPj5M6ERKzmMCJS1kntnjjGuJ86fn3rQ==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.0.tgz",
+      "integrity": "sha512-+Ygg4t1StoiNlBGc6x0f8q/Bv26FbZqP/+jegzfNpU7Q8o+4ZRoJxJPhBkgE/UonpAjtxnE4zCZIyJX+MwLRMQ==",
       "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",
+        "istanbul-lib-coverage": "^2.0.3",
+        "istanbul-lib-hook": "^2.0.3",
+        "istanbul-lib-instrument": "^3.1.0",
+        "istanbul-lib-report": "^2.0.4",
+        "istanbul-lib-source-maps": "^3.0.2",
+        "istanbul-reports": "^2.1.0",
         "js-yaml": "^3.12.0",
         "make-dir": "^1.3.0",
+        "minimatch": "^3.0.4",
         "once": "^1.4.0"
+      },
+      "dependencies": {
+        "async": {
+          "version": "2.6.1",
+          "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
+          "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
+          "dev": true,
+          "requires": {
+            "lodash": "^4.17.10"
+          }
+        },
+        "istanbul-lib-coverage": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
+          "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==",
+          "dev": true
+        },
+        "istanbul-lib-instrument": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz",
+          "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==",
+          "dev": true,
+          "requires": {
+            "@babel/generator": "^7.0.0",
+            "@babel/parser": "^7.0.0",
+            "@babel/template": "^7.0.0",
+            "@babel/traverse": "^7.0.0",
+            "@babel/types": "^7.0.0",
+            "istanbul-lib-coverage": "^2.0.3",
+            "semver": "^5.5.0"
+          }
+        }
       }
     },
     "istanbul-instrumenter-loader": {
@@ -6823,27 +6695,6 @@
           "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
           "dev": true
         },
-        "istanbul-lib-coverage": {
-          "version": "1.2.1",
-          "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz",
-          "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==",
-          "dev": true
-        },
-        "istanbul-lib-instrument": {
-          "version": "1.10.2",
-          "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz",
-          "integrity": "sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==",
-          "dev": true,
-          "requires": {
-            "babel-generator": "^6.18.0",
-            "babel-template": "^6.16.0",
-            "babel-traverse": "^6.18.0",
-            "babel-types": "^6.18.0",
-            "babylon": "^6.18.0",
-            "istanbul-lib-coverage": "^1.2.1",
-            "semver": "^5.3.0"
-          }
-        },
         "json-schema-traverse": {
           "version": "0.3.1",
           "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
@@ -6862,76 +6713,82 @@
       }
     },
     "istanbul-lib-coverage": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz",
-      "integrity": "sha512-nPvSZsVlbG9aLhZYaC3Oi1gT/tpyo3Yt5fNyf6NmcKIayz4VV/txxJFFKAK/gU4dcNn8ehsanBbVHVl0+amOLA==",
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz",
+      "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==",
       "dev": true
     },
     "istanbul-lib-hook": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.1.tgz",
-      "integrity": "sha512-ufiZoiJ8CxY577JJWEeFuxXZoMqiKpq/RqZtOAYuQLvlkbJWscq9n3gc4xrCGH9n4pW0qnTxOz1oyMmVtk8E1w==",
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.3.tgz",
+      "integrity": "sha512-CLmEqwEhuCYtGcpNVJjLV1DQyVnIqavMLFHV/DP+np/g3qvdxu3gsPqYoJMXm15sN84xOlckFB3VNvRbf5yEgA==",
       "dev": true,
       "requires": {
         "append-transform": "^1.0.0"
       }
     },
     "istanbul-lib-instrument": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.0.0.tgz",
-      "integrity": "sha512-eQY9vN9elYjdgN9Iv6NS/00bptm02EBBk70lRMaVjeA6QYocQgenVrSgC28TJurdnZa80AGO3ASdFN+w/njGiQ==",
+      "version": "1.10.2",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz",
+      "integrity": "sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==",
       "dev": true,
       "requires": {
-        "@babel/generator": "^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"
-      },
-      "dependencies": {
-        "semver": {
-          "version": "5.6.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
-          "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
-          "dev": true
-        }
+        "babel-generator": "^6.18.0",
+        "babel-template": "^6.16.0",
+        "babel-traverse": "^6.18.0",
+        "babel-types": "^6.18.0",
+        "babylon": "^6.18.0",
+        "istanbul-lib-coverage": "^1.2.1",
+        "semver": "^5.3.0"
       }
     },
     "istanbul-lib-report": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.2.tgz",
-      "integrity": "sha512-rJ8uR3peeIrwAxoDEbK4dJ7cqqtxBisZKCuwkMtMv0xYzaAnsAi3AHrHPAAtNXzG/bcCgZZ3OJVqm1DTi9ap2Q==",
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.4.tgz",
+      "integrity": "sha512-sOiLZLAWpA0+3b5w5/dq0cjm2rrNdAfHWaGhmn7XEFW6X++IV9Ohn+pnELAl9K3rfpaeBfbmH9JU5sejacdLeA==",
       "dev": true,
       "requires": {
-        "istanbul-lib-coverage": "^2.0.1",
+        "istanbul-lib-coverage": "^2.0.3",
         "make-dir": "^1.3.0",
-        "supports-color": "^5.4.0"
+        "supports-color": "^6.0.0"
+      },
+      "dependencies": {
+        "istanbul-lib-coverage": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
+          "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==",
+          "dev": true
+        }
       }
     },
     "istanbul-lib-source-maps": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-2.0.1.tgz",
-      "integrity": "sha512-30l40ySg+gvBLcxTrLzR4Z2XTRj3HgRCA/p2rnbs/3OiTaoj054gAbuP5DcLOtwqmy4XW8qXBHzrmP2/bQ9i3A==",
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.2.tgz",
+      "integrity": "sha512-JX4v0CiKTGp9fZPmoxpu9YEkPbEqCqBbO3403VabKjH+NRXo72HafD5UgnjTEqHL2SAjaZK1XDuDOkn6I5QVfQ==",
       "dev": true,
       "requires": {
-        "debug": "^3.1.0",
-        "istanbul-lib-coverage": "^2.0.1",
+        "debug": "^4.1.1",
+        "istanbul-lib-coverage": "^2.0.3",
         "make-dir": "^1.3.0",
         "rimraf": "^2.6.2",
         "source-map": "^0.6.1"
       },
       "dependencies": {
         "debug": {
-          "version": "3.2.6",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
-          "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
           "dev": true,
           "requires": {
             "ms": "^2.1.1"
           }
         },
+        "istanbul-lib-coverage": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
+          "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==",
+          "dev": true
+        },
         "ms": {
           "version": "2.1.1",
           "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
@@ -6947,9 +6804,9 @@
       }
     },
     "istanbul-reports": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.0.1.tgz",
-      "integrity": "sha512-CT0QgMBJqs6NJLF678ZHcquUAZIoBIUNzdJrRJfpkI9OnzG6MkUfHxbJC3ln981dMswC7/B1mfX3LNkhgJxsuw==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.1.0.tgz",
+      "integrity": "sha512-azQdSX+dtTtkQEfqq20ICxWi6eOHXyHIgMFw1VOOVi8iIPWeCWRgCyFh/CsBKIhcgskMI8ExXmU7rjXTRCIJ+A==",
       "dev": true,
       "requires": {
         "handlebars": "^4.0.11"
@@ -7356,6 +7213,14 @@
       "dev": true,
       "requires": {
         "colors": "1.1.2"
+      },
+      "dependencies": {
+        "colors": {
+          "version": "1.1.2",
+          "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
+          "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
+          "dev": true
+        }
       }
     },
     "jasminewd2": {
@@ -7365,26 +7230,34 @@
       "dev": true
     },
     "js-base64": {
-      "version": "2.5.0",
-      "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.0.tgz",
-      "integrity": "sha512-wlEBIZ5LP8usDylWbDNhKPEFVFdI5hCHpnVoT/Ysvoi/PRhJENm/Rlh9TvjYB38HFfKZN7OzEbRjmjvLkFw11g==",
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz",
+      "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==",
       "dev": true,
       "optional": true
     },
     "js-tokens": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
-      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+      "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
       "dev": true
     },
     "js-yaml": {
-      "version": "3.12.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
-      "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
+      "version": "3.12.1",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz",
+      "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==",
       "dev": true,
       "requires": {
         "argparse": "^1.0.7",
         "esprima": "^4.0.0"
+      },
+      "dependencies": {
+        "esprima": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+          "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+          "dev": true
+        }
       }
     },
     "jsbn": {
@@ -7394,9 +7267,9 @@
       "dev": true
     },
     "jsesc": {
-      "version": "0.5.0",
-      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
-      "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
+      "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
       "dev": true
     },
     "json-parse-better-errors": {
@@ -7460,14 +7333,6 @@
         "extsprintf": "1.3.0",
         "json-schema": "0.2.3",
         "verror": "1.10.0"
-      },
-      "dependencies": {
-        "assert-plus": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-          "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
-          "dev": true
-        }
       }
     },
     "jszip": {
@@ -7495,9 +7360,15 @@
           "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=",
           "dev": true
         },
+        "process-nextick-args": {
+          "version": "1.0.7",
+          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+          "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
+          "dev": true
+        },
         "readable-stream": {
           "version": "2.0.6",
-          "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
           "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
           "dev": true,
           "requires": {
@@ -7511,7 +7382,7 @@
         },
         "string_decoder": {
           "version": "0.10.31",
-          "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
           "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
           "dev": true
         }
@@ -7553,326 +7424,12 @@
         "useragent": "2.3.0"
       },
       "dependencies": {
-        "anymatch": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
-          "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
-          "dev": true,
-          "requires": {
-            "micromatch": "^3.1.4",
-            "normalize-path": "^2.1.1"
-          }
-        },
-        "arr-diff": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-          "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
-          "dev": true
-        },
-        "array-unique": {
-          "version": "0.3.2",
-          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-          "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
-          "dev": true
-        },
-        "braces": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
-          "dev": true,
-          "requires": {
-            "arr-flatten": "^1.1.0",
-            "array-unique": "^0.3.2",
-            "extend-shallow": "^2.0.1",
-            "fill-range": "^4.0.0",
-            "isobject": "^3.0.1",
-            "repeat-element": "^1.1.2",
-            "snapdragon": "^0.8.1",
-            "snapdragon-node": "^2.0.1",
-            "split-string": "^3.0.2",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "chokidar": {
-          "version": "2.0.4",
-          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz",
-          "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==",
-          "dev": true,
-          "requires": {
-            "anymatch": "^2.0.0",
-            "async-each": "^1.0.0",
-            "braces": "^2.3.0",
-            "fsevents": "^1.2.2",
-            "glob-parent": "^3.1.0",
-            "inherits": "^2.0.1",
-            "is-binary-path": "^1.0.0",
-            "is-glob": "^4.0.0",
-            "lodash.debounce": "^4.0.8",
-            "normalize-path": "^2.1.1",
-            "path-is-absolute": "^1.0.0",
-            "readdirp": "^2.0.0",
-            "upath": "^1.0.5"
-          }
-        },
-        "expand-brackets": {
-          "version": "2.1.4",
-          "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-          "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
-          "dev": true,
-          "requires": {
-            "debug": "^2.3.3",
-            "define-property": "^0.2.5",
-            "extend-shallow": "^2.0.1",
-            "posix-character-classes": "^0.1.0",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "define-property": {
-              "version": "0.2.5",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-              "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "^0.1.0"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            },
-            "is-accessor-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-              "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
-              "dev": true,
-              "requires": {
-                "kind-of": "^3.0.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "^1.1.5"
-                  }
-                }
-              }
-            },
-            "is-data-descriptor": {
-              "version": "0.1.4",
-              "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-              "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
-              "dev": true,
-              "requires": {
-                "kind-of": "^3.0.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "^1.1.5"
-                  }
-                }
-              }
-            },
-            "is-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
-              "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
-              "dev": true,
-              "requires": {
-                "is-accessor-descriptor": "^0.1.6",
-                "is-data-descriptor": "^0.1.4",
-                "kind-of": "^5.0.0"
-              }
-            },
-            "kind-of": {
-              "version": "5.1.0",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-              "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-              "dev": true
-            }
-          }
-        },
-        "extglob": {
-          "version": "2.0.4",
-          "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
-          "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
-          "dev": true,
-          "requires": {
-            "array-unique": "^0.3.2",
-            "define-property": "^1.0.0",
-            "expand-brackets": "^2.1.4",
-            "extend-shallow": "^2.0.1",
-            "fragment-cache": "^0.2.1",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "define-property": {
-              "version": "1.0.0",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-              "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "^1.0.0"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "fill-range": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
-          "dev": true,
-          "requires": {
-            "extend-shallow": "^2.0.1",
-            "is-number": "^3.0.0",
-            "repeat-string": "^1.6.1",
-            "to-regex-range": "^2.1.0"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "glob-parent": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
-          "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
-          "dev": true,
-          "requires": {
-            "is-glob": "^3.1.0",
-            "path-dirname": "^1.0.0"
-          },
-          "dependencies": {
-            "is-glob": {
-              "version": "3.1.0",
-              "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
-              "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
-              "dev": true,
-              "requires": {
-                "is-extglob": "^2.1.0"
-              }
-            }
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        },
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "dev": true,
-          "requires": {
-            "kind-of": "^3.0.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "dev": true,
-              "requires": {
-                "is-buffer": "^1.1.5"
-              }
-            }
-          }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+        "mime": {
+          "version": "2.4.0",
+          "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz",
+          "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==",
           "dev": true
         },
-        "micromatch": {
-          "version": "3.1.10",
-          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
-          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
-          "dev": true,
-          "requires": {
-            "arr-diff": "^4.0.0",
-            "array-unique": "^0.3.2",
-            "braces": "^2.3.1",
-            "define-property": "^2.0.2",
-            "extend-shallow": "^3.0.2",
-            "extglob": "^2.0.4",
-            "fragment-cache": "^0.2.1",
-            "kind-of": "^6.0.2",
-            "nanomatch": "^1.2.9",
-            "object.pick": "^1.3.0",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.2"
-          }
-        },
         "source-map": {
           "version": "0.6.1",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -7898,6 +7455,17 @@
       "dev": true,
       "requires": {
         "resolve": "^1.3.3"
+      },
+      "dependencies": {
+        "resolve": {
+          "version": "1.10.0",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
+          "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
+          "dev": true,
+          "requires": {
+            "path-parse": "^1.0.6"
+          }
+        }
       }
     },
     "karma-coverage-istanbul-reporter": {
@@ -7932,24 +7500,6 @@
       "dev": true,
       "requires": {
         "source-map-support": "^0.5.5"
-      },
-      "dependencies": {
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
-        },
-        "source-map-support": {
-          "version": "0.5.10",
-          "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz",
-          "integrity": "sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==",
-          "dev": true,
-          "requires": {
-            "buffer-from": "^1.0.0",
-            "source-map": "^0.6.0"
-          }
-        }
       }
     },
     "killable": {
@@ -7959,13 +7509,10 @@
       "dev": true
     },
     "kind-of": {
-      "version": "3.2.2",
-      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-      "dev": true,
-      "requires": {
-        "is-buffer": "^1.1.5"
-      }
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+      "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+      "dev": true
     },
     "lazy-cache": {
       "version": "1.0.4",
@@ -8000,13 +7547,6 @@
         "source-map": "~0.6.0"
       },
       "dependencies": {
-        "mime": {
-          "version": "1.6.0",
-          "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
-          "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
-          "dev": true,
-          "optional": true
-        },
         "source-map": {
           "version": "0.6.1",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -8025,14 +7565,6 @@
         "clone": "^2.1.1",
         "loader-utils": "^1.1.0",
         "pify": "^3.0.0"
-      },
-      "dependencies": {
-        "pify": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
-          "dev": true
-        }
       }
     },
     "levn": {
@@ -8141,21 +7673,14 @@
             "range-parser": "~1.2.0",
             "statuses": "~1.4.0"
           }
-        },
-        "statuses": {
-          "version": "1.4.0",
-          "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
-          "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==",
-          "dev": true
         }
       }
     },
     "load-json-file": {
       "version": "1.1.0",
-      "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
       "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
       "dev": true,
-      "optional": true,
       "requires": {
         "graceful-fs": "^4.1.2",
         "parse-json": "^2.2.0",
@@ -8164,22 +7689,18 @@
         "strip-bom": "^2.0.0"
       },
       "dependencies": {
-        "strip-bom": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
-          "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "is-utf8": "^0.2.0"
-          }
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+          "dev": true
         }
       }
     },
     "loader-runner": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.1.tgz",
-      "integrity": "sha512-By6ZFY7ETWOc9RFaAIb23IjJVcM4dvJC/N57nmdz9RSkMXvAXGI7SyVlAw3v8vjtDRlqThgVDVmTnr9fqMlxkw==",
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
+      "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==",
       "dev": true
     },
     "loader-utils": {
@@ -8281,8 +7802,7 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
       "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=",
-      "dev": true,
-      "optional": true
+      "dev": true
     },
     "loose-envify": {
       "version": "1.4.0",
@@ -8312,14 +7832,6 @@
       "requires": {
         "pseudomap": "^1.0.2",
         "yallist": "^2.1.2"
-      },
-      "dependencies": {
-        "yallist": {
-          "version": "2.1.2",
-          "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
-          "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
-          "dev": true
-        }
       }
     },
     "lunr": {
@@ -8335,12 +7847,12 @@
       "dev": true
     },
     "magic-string": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.1.tgz",
-      "integrity": "sha512-sCuTz6pYom8Rlt4ISPFn6wuFodbKMIHUMv4Qko9P17dpxb7s52KJTmRuZZqHdGmLCK9AOcDare039nRIcfdkEg==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.2.tgz",
+      "integrity": "sha512-iLs9mPjh9IuTtRsqqhNGYcZXGei0Nh/A4xirrsqW7c+QhKVFL2vm7U09ru6cHRD22azaP/wMDgI+HCqbETMTtg==",
       "dev": true,
       "requires": {
-        "sourcemap-codec": "^1.4.1"
+        "sourcemap-codec": "^1.4.4"
       }
     },
     "make-dir": {
@@ -8350,14 +7862,6 @@
       "dev": true,
       "requires": {
         "pify": "^3.0.0"
-      },
-      "dependencies": {
-        "pify": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
-          "dev": true
-        }
       }
     },
     "make-error": {
@@ -8418,26 +7922,6 @@
             }
           }
         },
-        "glob": {
-          "version": "7.1.3",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
-          "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
-          "dev": true,
-          "requires": {
-            "fs.realpath": "^1.0.0",
-            "inflight": "^1.0.4",
-            "inherits": "2",
-            "minimatch": "^3.0.4",
-            "once": "^1.3.0",
-            "path-is-absolute": "^1.0.0"
-          }
-        },
-        "graceful-fs": {
-          "version": "4.1.15",
-          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
-          "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
-          "dev": true
-        },
         "mississippi": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
@@ -8474,6 +7958,12 @@
           "requires": {
             "figgy-pudding": "^3.5.1"
           }
+        },
+        "yallist": {
+          "version": "3.0.3",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
+          "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
+          "dev": true
         }
       }
     },
@@ -8496,8 +7986,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-stream": {
       "version": "0.1.0",
@@ -8520,9 +8009,9 @@
       "integrity": "sha512-fdZvBa7/vSQIZCi4uuwo2N3q+7jJURpMVCcbaX0S1Mg65WZ5ilXvC67MviJAsdjqqgD+CEq4RKo5AYGgINkVAA=="
     },
     "math-random": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz",
-      "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=",
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz",
+      "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==",
       "dev": true
     },
     "mathjax": {
@@ -8539,14 +8028,6 @@
         "hash-base": "^3.0.0",
         "inherits": "^2.0.1",
         "safe-buffer": "^5.1.2"
-      },
-      "dependencies": {
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-          "dev": true
-        }
       }
     },
     "media-typer": {
@@ -8556,14 +8037,14 @@
       "dev": true
     },
     "mem": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/mem/-/mem-4.0.0.tgz",
-      "integrity": "sha512-WQxG/5xYc3tMbYLXoXPm81ET2WDULiU5FxbuIoNbJqLOOI8zehXFdZuiUEgfdrU2mVB1pxBZUGlYORSrpuJreA==",
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz",
+      "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==",
       "dev": true,
       "requires": {
         "map-age-cleaner": "^0.1.1",
         "mimic-fn": "^1.0.0",
-        "p-is-promise": "^1.1.0"
+        "p-is-promise": "^2.0.0"
       }
     },
     "memory-fs": {
@@ -8578,7 +8059,7 @@
     },
     "meow": {
       "version": "3.7.0",
-      "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
+      "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
       "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
       "dev": true,
       "optional": true,
@@ -8593,6 +8074,15 @@
         "read-pkg-up": "^1.0.1",
         "redent": "^1.0.0",
         "trim-newlines": "^1.0.0"
+      },
+      "dependencies": {
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+          "dev": true,
+          "optional": true
+        }
       }
     },
     "merge-descriptors": {
@@ -8632,20 +8122,6 @@
         "regex-not": "^1.0.0",
         "snapdragon": "^0.8.1",
         "to-regex": "^3.0.2"
-      },
-      "dependencies": {
-        "array-unique": {
-          "version": "0.3.2",
-          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-          "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
-          "dev": true
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        }
       }
     },
     "miller-rabin": {
@@ -8659,10 +8135,11 @@
       }
     },
     "mime": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz",
-      "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==",
-      "dev": true
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+      "dev": true,
+      "optional": true
     },
     "mime-db": {
       "version": "1.37.0",
@@ -8718,9 +8195,9 @@
       }
     },
     "minimist": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-      "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+      "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
       "dev": true
     },
     "minipass": {
@@ -8733,10 +8210,10 @@
         "yallist": "^3.0.0"
       },
       "dependencies": {
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+        "yallist": {
+          "version": "3.0.3",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
+          "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
           "dev": true
         }
       }
@@ -8814,20 +8291,12 @@
       "dev": true,
       "requires": {
         "minimist": "0.0.8"
-      },
-      "dependencies": {
-        "minimist": {
-          "version": "0.0.8",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
-          "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
-          "dev": true
-        }
       }
     },
     "moment": {
-      "version": "2.23.0",
-      "resolved": "https://registry.npmjs.org/moment/-/moment-2.23.0.tgz",
-      "integrity": "sha512-3IE39bHVqFbWWaPOMHZF98Q9c3LDKGTmypMiTM2QygGXXElkFWIH7GxfmlwmY2vwa+wmNsoYZmG2iusf1ZjJoA=="
+      "version": "2.24.0",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
+      "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
     },
     "morgan": {
       "version": "1.9.1",
@@ -8892,14 +8361,14 @@
     },
     "mute-stream": {
       "version": "0.0.7",
-      "resolved": "http://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
+      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
       "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
       "dev": true
     },
     "nan": {
-      "version": "2.10.0",
-      "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
-      "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
+      "version": "2.12.1",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz",
+      "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==",
       "dev": true,
       "optional": true
     },
@@ -8920,26 +8389,6 @@
         "regex-not": "^1.0.0",
         "snapdragon": "^0.8.1",
         "to-regex": "^3.0.1"
-      },
-      "dependencies": {
-        "arr-diff": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-          "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
-          "dev": true
-        },
-        "array-unique": {
-          "version": "0.3.2",
-          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-          "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
-          "dev": true
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        }
       }
     },
     "negotiator": {
@@ -8954,10 +8403,18 @@
       "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==",
       "dev": true
     },
+    "ngx-material-file-input": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/ngx-material-file-input/-/ngx-material-file-input-1.1.0.tgz",
+      "integrity": "sha512-+HJOJIp6uE21QU6nHOYjT9/Hd1/JXx57CLTh8XlqDi9/HGfa5SPlvgjkn/RcrjJxTMz+lnGBMhi8RmEs6bbENA==",
+      "requires": {
+        "tslib": "^1.9.0"
+      }
+    },
     "ngx-md": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/ngx-md/-/ngx-md-7.0.0.tgz",
-      "integrity": "sha512-IMqjkc33lS8jlQXqrLh2McuhUFXrUKBfYH5VqCAcGa3QDBkiPOakh/CuYbI5KC8KQDZMe5fngSGMKgDNVBzR7g==",
+      "version": "7.1.3",
+      "resolved": "https://registry.npmjs.org/ngx-md/-/ngx-md-7.1.3.tgz",
+      "integrity": "sha512-sSIr1pwg4eOO4F1xk+DkofgBWShQm4cKYTwe3oYXRZxdrhHXzN+oUief+3IMSHi2syCSi791PinmrcQ8+HYy5w==",
       "requires": {
         "marked": "^0.5.0",
         "prismjs": "^1.15.0",
@@ -9020,7 +8477,7 @@
       "dependencies": {
         "semver": {
           "version": "5.3.0",
-          "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
           "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
           "dev": true,
           "optional": true
@@ -9028,9 +8485,9 @@
       }
     },
     "node-libs-browser": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz",
-      "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==",
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz",
+      "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==",
       "dev": true,
       "requires": {
         "assert": "^1.1.1",
@@ -9040,7 +8497,7 @@
         "constants-browserify": "^1.0.0",
         "crypto-browserify": "^3.11.0",
         "domain-browser": "^1.1.1",
-        "events": "^1.0.0",
+        "events": "^3.0.0",
         "https-browserify": "^1.0.0",
         "os-browserify": "^0.3.0",
         "path-browserify": "0.0.0",
@@ -9054,14 +8511,22 @@
         "timers-browserify": "^2.0.4",
         "tty-browserify": "0.0.0",
         "url": "^0.11.0",
-        "util": "^0.10.3",
+        "util": "^0.11.0",
         "vm-browserify": "0.0.4"
+      },
+      "dependencies": {
+        "punycode": {
+          "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+          "dev": true
+        }
       }
     },
     "node-releases": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.3.tgz",
-      "integrity": "sha512-6VrvH7z6jqqNFY200kdB6HdzkgM96Oaj9v3dqGfgp6mF+cHmU4wyQKZ2/WPDRVoR0Jz9KqbamaBN0ZhdUaysUQ==",
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.7.tgz",
+      "integrity": "sha512-bKdrwaqJUPHqlCzDD7so/R+Nk0jGv9a11ZhLrD9f6i947qGLrGAhU3OxRENa19QQmwzGy/g6zCDEuLGDO8HPvA==",
       "dev": true,
       "requires": {
         "semver": "^5.3.0"
@@ -9118,7 +8583,7 @@
         },
         "supports-color": {
           "version": "2.0.0",
-          "resolved": "http://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
           "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
           "dev": true,
           "optional": true
@@ -9135,15 +8600,26 @@
       }
     },
     "normalize-package-data": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
-      "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
       "dev": true,
       "requires": {
         "hosted-git-info": "^2.1.4",
-        "is-builtin-module": "^1.0.0",
+        "resolve": "^1.10.0",
         "semver": "2 || 3 || 4 || 5",
         "validate-npm-package-license": "^3.0.1"
+      },
+      "dependencies": {
+        "resolve": {
+          "version": "1.10.0",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
+          "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
+          "dev": true,
+          "requires": {
+            "path-parse": "^1.0.6"
+          }
+        }
       }
     },
     "normalize-path": {
@@ -9162,9 +8638,9 @@
       "dev": true
     },
     "npm-bundled": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.5.tgz",
-      "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g==",
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz",
+      "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==",
       "dev": true
     },
     "npm-package-arg": {
@@ -9177,20 +8653,12 @@
         "osenv": "^0.1.5",
         "semver": "^5.5.0",
         "validate-npm-package-name": "^3.0.0"
-      },
-      "dependencies": {
-        "semver": {
-          "version": "5.6.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
-          "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
-          "dev": true
-        }
       }
     },
     "npm-packlist": {
-      "version": "1.1.12",
-      "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.12.tgz",
-      "integrity": "sha512-WJKFOVMeAlsU/pjXuqVdzU0WfgtIBCupkEVwn+1Y0ERAbUfWw8R4GjgVbaKnUjRoD2FoQbHOCbOyT5Mbs9Lw4g==",
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.3.0.tgz",
+      "integrity": "sha512-qPBc6CnxEzpOcc4bjoIBJbYdy0D/LFFPUdxvfwor4/w3vxeE0h6TiOVurCEPpQ6trjN77u/ShyfeJGsbAfB3dA==",
       "dev": true,
       "requires": {
         "ignore-walk": "^3.0.1",
@@ -9209,9 +8677,9 @@
       }
     },
     "npm-registry-fetch": {
-      "version": "3.8.0",
-      "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-3.8.0.tgz",
-      "integrity": "sha512-hrw8UMD+Nob3Kl3h8Z/YjmKamb1gf7D1ZZch2otrIXM3uFLB5vjEY6DhMlq80z/zZet6eETLbOXcuQudCB3Zpw==",
+      "version": "3.9.0",
+      "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-3.9.0.tgz",
+      "integrity": "sha512-srwmt8YhNajAoSAaDWndmZgx89lJwIZ1GWxOuckH4Coek4uHv5S+o/l9FLQe/awA+JwTnj4FJHldxhlXdZEBmw==",
       "dev": true,
       "requires": {
         "JSONStream": "^1.3.4",
@@ -9236,7 +8704,6 @@
       "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
       "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
       "dev": true,
-      "optional": true,
       "requires": {
         "are-we-there-yet": "~1.1.2",
         "console-control-strings": "~1.1.0",
@@ -9275,8 +8742,7 @@
       "version": "0.9.0",
       "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
       "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
-      "dev": true,
-      "optional": true
+      "dev": true
     },
     "object-assign": {
       "version": "4.1.1",
@@ -9307,7 +8773,16 @@
           "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
           "dev": true,
           "requires": {
-            "is-descriptor": "^0.1.0"
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
           }
         }
       }
@@ -9329,6 +8804,17 @@
       "requires": {
         "for-own": "^0.1.4",
         "is-extendable": "^0.1.1"
+      },
+      "dependencies": {
+        "for-own": {
+          "version": "0.1.5",
+          "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz",
+          "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=",
+          "dev": true,
+          "requires": {
+            "for-in": "^1.0.1"
+          }
+        }
       }
     },
     "object.pick": {
@@ -9468,6 +8954,12 @@
           "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
           "dev": true
         },
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+          "dev": true
+        },
         "opn": {
           "version": "4.0.2",
           "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz",
@@ -9508,9 +9000,9 @@
       }
     },
     "opn": {
-      "version": "5.3.0",
-      "resolved": "http://registry.npmjs.org/opn/-/opn-5.3.0.tgz",
-      "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==",
+      "version": "5.4.0",
+      "resolved": "https://registry.npmjs.org/opn/-/opn-5.4.0.tgz",
+      "integrity": "sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==",
       "dev": true,
       "requires": {
         "is-wsl": "^1.1.0"
@@ -9526,10 +9018,10 @@
         "wordwrap": "~0.0.2"
       },
       "dependencies": {
-        "minimist": {
-          "version": "0.0.10",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
-          "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=",
+        "wordwrap": {
+          "version": "0.0.3",
+          "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+          "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
           "dev": true
         }
       }
@@ -9546,14 +9038,6 @@
         "prelude-ls": "~1.1.2",
         "type-check": "~0.3.2",
         "wordwrap": "~1.0.0"
-      },
-      "dependencies": {
-        "wordwrap": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
-          "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
-          "dev": true
-        }
       }
     },
     "original": {
@@ -9579,7 +9063,7 @@
     },
     "os-locale": {
       "version": "1.4.0",
-      "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
+      "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
       "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
       "dev": true,
       "optional": true,
@@ -9626,9 +9110,9 @@
       "dev": true
     },
     "p-is-promise": {
-      "version": "1.1.0",
-      "resolved": "http://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz",
-      "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz",
+      "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==",
       "dev": true
     },
     "p-limit": {
@@ -9662,17 +9146,17 @@
       "dev": true
     },
     "pacote": {
-      "version": "9.1.1",
-      "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.1.1.tgz",
-      "integrity": "sha512-f28Rq5ozzKAA9YwIKw61/ipwAatUZseYmVssDbHHaexF0wRIVotapVEZPAjOT7Eu3LYVqEp0NVpNizoAnYBUaA==",
+      "version": "9.4.0",
+      "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.4.0.tgz",
+      "integrity": "sha512-WQ1KL/phGMkedYEQx9ODsjj7xvwLSpdFJJdEXrLyw5SILMxcTNt5DTxT2Z93fXuLFYJBlZJdnwdalrQdB/rX5w==",
       "dev": true,
       "requires": {
-        "bluebird": "^3.5.2",
-        "cacache": "^11.2.0",
+        "bluebird": "^3.5.3",
+        "cacache": "^11.3.2",
         "figgy-pudding": "^3.5.1",
         "get-stream": "^4.1.0",
         "glob": "^7.1.3",
-        "lru-cache": "^4.1.3",
+        "lru-cache": "^5.1.1",
         "make-fetch-happen": "^4.0.1",
         "minimatch": "^3.0.4",
         "minipass": "^2.3.5",
@@ -9681,7 +9165,7 @@
         "normalize-package-data": "^2.4.0",
         "npm-package-arg": "^6.1.0",
         "npm-packlist": "^1.1.12",
-        "npm-pick-manifest": "^2.1.0",
+        "npm-pick-manifest": "^2.2.3",
         "npm-registry-fetch": "^3.8.0",
         "osenv": "^0.1.5",
         "promise-inflight": "^1.0.1",
@@ -9691,7 +9175,7 @@
         "safe-buffer": "^5.1.2",
         "semver": "^5.6.0",
         "ssri": "^6.0.1",
-        "tar": "^4.4.6",
+        "tar": "^4.4.8",
         "unique-filename": "^1.1.1",
         "which": "^1.3.1"
       },
@@ -9716,17 +9200,6 @@
             "ssri": "^6.0.1",
             "unique-filename": "^1.1.1",
             "y18n": "^4.0.0"
-          },
-          "dependencies": {
-            "lru-cache": {
-              "version": "5.1.1",
-              "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
-              "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
-              "dev": true,
-              "requires": {
-                "yallist": "^3.0.2"
-              }
-            }
           }
         },
         "get-stream": {
@@ -9738,26 +9211,15 @@
             "pump": "^3.0.0"
           }
         },
-        "glob": {
-          "version": "7.1.3",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
-          "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+        "lru-cache": {
+          "version": "5.1.1",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+          "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
           "dev": true,
           "requires": {
-            "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"
+            "yallist": "^3.0.2"
           }
         },
-        "graceful-fs": {
-          "version": "4.1.15",
-          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
-          "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
-          "dev": true
-        },
         "mississippi": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
@@ -9786,12 +9248,6 @@
             "once": "^1.3.1"
           }
         },
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-          "dev": true
-        },
         "semver": {
           "version": "5.6.0",
           "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
@@ -9821,13 +9277,19 @@
             "safe-buffer": "^5.1.2",
             "yallist": "^3.0.2"
           }
+        },
+        "yallist": {
+          "version": "3.0.3",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
+          "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
+          "dev": true
         }
       }
     },
     "pako": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz",
-      "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==",
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.8.tgz",
+      "integrity": "sha512-6i0HVbUfcKaTv+EG8ZTr75az7GFXcLYk9UyLEg7Notv/Ma+z/UG3TCoz6GiNeOrn1E/e63I0X/Hpw18jHOTUnA==",
       "dev": true
     },
     "parallel-transform": {
@@ -9842,16 +9304,17 @@
       }
     },
     "parse-asn1": {
-      "version": "5.1.1",
-      "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz",
-      "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==",
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.3.tgz",
+      "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"
+        "pbkdf2": "^3.0.3",
+        "safe-buffer": "^5.1.1"
       }
     },
     "parse-glob": {
@@ -9893,16 +9356,16 @@
       }
     },
     "parse-node-version": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.0.tgz",
-      "integrity": "sha512-02GTVHD1u0nWc20n2G7WX/PgdhNFG04j5fi1OkaJzPWLTcf6vh6229Lta1wTmXG/7Dg42tCssgkccVt7qvd8Kg==",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
+      "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
       "dev": true
     },
     "parse5": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
-      "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==",
-      "dev": true
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
+      "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==",
+      "optional": true
     },
     "parseqs": {
       "version": "0.0.5",
@@ -9936,7 +9399,7 @@
     },
     "path-browserify": {
       "version": "0.0.0",
-      "resolved": "http://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
+      "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
       "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=",
       "dev": true
     },
@@ -9989,14 +9452,6 @@
       "dev": true,
       "requires": {
         "pify": "^3.0.0"
-      },
-      "dependencies": {
-        "pify": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
-          "dev": true
-        }
       }
     },
     "pause-stream": {
@@ -10025,13 +9480,12 @@
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
       "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
-      "dev": true,
-      "optional": true
+      "dev": true
     },
     "pify": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-      "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+      "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
       "dev": true
     },
     "pinkie": {
@@ -10067,14 +9521,6 @@
         "async": "^1.5.2",
         "debug": "^2.2.0",
         "mkdirp": "0.5.x"
-      },
-      "dependencies": {
-        "async": {
-          "version": "1.5.2",
-          "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
-          "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
-          "dev": true
-        }
       }
     },
     "posix-character-classes": {
@@ -10084,14 +9530,14 @@
       "dev": true
     },
     "postcss": {
-      "version": "7.0.5",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.5.tgz",
-      "integrity": "sha512-HBNpviAUFCKvEh7NZhw1e8MBPivRszIiUnhrJ+sBFVSYSqubrzwX3KG51mYgcRHX8j/cAgZJedONZcm5jTBdgQ==",
+      "version": "7.0.13",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.13.tgz",
+      "integrity": "sha512-h8SY6kQTd1wISHWjz+E6cswdhMuyBZRb16pSTv3W4zYZ3/YbyWeJdNUeOXB5IdZqE1U76OUEjjjqsC3z2f3hVg==",
       "dev": true,
       "requires": {
-        "chalk": "^2.4.1",
+        "chalk": "^2.4.2",
         "source-map": "^0.6.1",
-        "supports-color": "^5.5.0"
+        "supports-color": "^6.1.0"
       },
       "dependencies": {
         "source-map": {
@@ -10103,9 +9549,9 @@
       }
     },
     "postcss-import": {
-      "version": "12.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.0.tgz",
-      "integrity": "sha512-3KqKRZcaZAvxbY8DVLdd81tG5uKzbUQuiWIvy0o0fzEC42bKacqPYFWbfCQyw6L4LWUaqPz/idvIdbhpgQ32eQ==",
+      "version": "12.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.1.tgz",
+      "integrity": "sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw==",
       "dev": true,
       "requires": {
         "postcss": "^7.0.1",
@@ -10169,9 +9615,9 @@
       "dev": true
     },
     "process-nextick-args": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
-      "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+      "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
       "dev": true
     },
     "promise": {
@@ -10232,42 +9678,12 @@
         "webdriver-manager": "^12.0.6"
       },
       "dependencies": {
-        "ajv": {
-          "version": "6.6.2",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz",
-          "integrity": "sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==",
-          "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"
-          }
-        },
         "ansi-styles": {
           "version": "2.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
           "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
           "dev": true
         },
-        "assert-plus": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-          "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
-          "dev": true
-        },
-        "aws-sign2": {
-          "version": "0.7.0",
-          "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
-          "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
-          "dev": true
-        },
-        "aws4": {
-          "version": "1.8.0",
-          "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
-          "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
-          "dev": true
-        },
         "chalk": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
@@ -10281,15 +9697,6 @@
             "supports-color": "^2.0.0"
           }
         },
-        "combined-stream": {
-          "version": "1.0.7",
-          "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
-          "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==",
-          "dev": true,
-          "requires": {
-            "delayed-stream": "~1.0.0"
-          }
-        },
         "del": {
           "version": "2.2.2",
           "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
@@ -10305,29 +9712,6 @@
             "rimraf": "^2.2.8"
           }
         },
-        "extend": {
-          "version": "3.0.2",
-          "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
-          "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
-          "dev": true
-        },
-        "fast-deep-equal": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
-          "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
-          "dev": true
-        },
-        "form-data": {
-          "version": "2.3.3",
-          "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
-          "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
-          "dev": true,
-          "requires": {
-            "asynckit": "^0.4.0",
-            "combined-stream": "^1.0.6",
-            "mime-types": "^2.1.12"
-          }
-        },
         "globby": {
           "version": "5.0.0",
           "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
@@ -10342,128 +9726,39 @@
             "pinkie-promise": "^2.0.0"
           }
         },
-        "har-schema": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
-          "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
-          "dev": true
-        },
-        "har-validator": {
-          "version": "5.1.3",
-          "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
-          "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
-          "dev": true,
-          "requires": {
-            "ajv": "^6.5.5",
-            "har-schema": "^2.0.0"
-          }
-        },
-        "http-signature": {
+        "minimist": {
           "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
-          "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
-          "dev": true,
-          "requires": {
-            "assert-plus": "^1.0.0",
-            "jsprim": "^1.2.2",
-            "sshpk": "^1.7.0"
-          }
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+          "dev": true
         },
-        "json-schema-traverse": {
-          "version": "0.4.1",
-          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-          "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
           "dev": true
         },
-        "mime-db": {
-          "version": "1.37.0",
-          "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
-          "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==",
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
           "dev": true
         },
-        "mime-types": {
-          "version": "2.1.21",
-          "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
-          "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
+        "source-map-support": {
+          "version": "0.4.18",
+          "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
+          "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
           "dev": true,
           "requires": {
-            "mime-db": "~1.37.0"
-          }
-        },
-        "oauth-sign": {
-          "version": "0.9.0",
-          "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
-          "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
-          "dev": true
-        },
-        "performance-now": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-          "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
-          "dev": true
-        },
-        "qs": {
-          "version": "6.5.2",
-          "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
-          "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
-          "dev": true
-        },
-        "request": {
-          "version": "2.88.0",
-          "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
-          "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
-          "dev": true,
-          "requires": {
-            "aws-sign2": "~0.7.0",
-            "aws4": "^1.8.0",
-            "caseless": "~0.12.0",
-            "combined-stream": "~1.0.6",
-            "extend": "~3.0.2",
-            "forever-agent": "~0.6.1",
-            "form-data": "~2.3.2",
-            "har-validator": "~5.1.0",
-            "http-signature": "~1.2.0",
-            "is-typedarray": "~1.0.0",
-            "isstream": "~0.1.2",
-            "json-stringify-safe": "~5.0.1",
-            "mime-types": "~2.1.19",
-            "oauth-sign": "~0.9.0",
-            "performance-now": "^2.1.0",
-            "qs": "~6.5.2",
-            "safe-buffer": "^5.1.2",
-            "tough-cookie": "~2.4.3",
-            "tunnel-agent": "^0.6.0",
-            "uuid": "^3.3.2"
+            "source-map": "^0.5.6"
           }
         },
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-          "dev": true
-        },
         "supports-color": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
           "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
           "dev": true
         },
-        "tough-cookie": {
-          "version": "2.4.3",
-          "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
-          "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
-          "dev": true,
-          "requires": {
-            "psl": "^1.1.24",
-            "punycode": "^1.4.1"
-          }
-        },
-        "uuid": {
-          "version": "3.3.2",
-          "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
-          "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
-          "dev": true
-        },
         "webdriver-manager": {
           "version": "12.1.1",
           "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.1.tgz",
@@ -10531,14 +9826,6 @@
         "parse-asn1": "^5.0.0",
         "randombytes": "^2.0.1",
         "safe-buffer": "^5.1.2"
-      },
-      "dependencies": {
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-          "dev": true
-        }
       }
     },
     "pump": {
@@ -10563,9 +9850,9 @@
       }
     },
     "punycode": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
-      "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
       "dev": true
     },
     "q": {
@@ -10620,12 +9907,6 @@
           "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
           "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
           "dev": true
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
         }
       }
     },
@@ -10664,22 +9945,11 @@
         "http-errors": "1.6.3",
         "iconv-lite": "0.4.23",
         "unpipe": "1.0.0"
-      },
-      "dependencies": {
-        "iconv-lite": {
-          "version": "0.4.23",
-          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
-          "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
-          "dev": true,
-          "requires": {
-            "safer-buffer": ">= 2.1.2 < 3"
-          }
-        }
       }
     },
     "raw-loader": {
       "version": "0.5.1",
-      "resolved": "http://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz",
+      "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz",
       "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=",
       "dev": true
     },
@@ -10690,6 +9960,14 @@
       "dev": true,
       "requires": {
         "pify": "^2.3.0"
+      },
+      "dependencies": {
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+          "dev": true
+        }
       }
     },
     "read-pkg": {
@@ -10697,7 +9975,6 @@
       "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
       "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
       "dev": true,
-      "optional": true,
       "requires": {
         "load-json-file": "^1.0.0",
         "normalize-package-data": "^2.3.2",
@@ -10709,12 +9986,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"
           }
+        },
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+          "dev": true
         }
       }
     },
@@ -10723,7 +10005,6 @@
       "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
       "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
       "dev": true,
-      "optional": true,
       "requires": {
         "find-up": "^1.0.0",
         "read-pkg": "^1.0.0"
@@ -10734,7 +10015,6 @@
           "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
           "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
           "dev": true,
-          "optional": true,
           "requires": {
             "path-exists": "^2.0.0",
             "pinkie-promise": "^2.0.0"
@@ -10745,7 +10025,6 @@
           "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
           "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
           "dev": true,
-          "optional": true,
           "requires": {
             "pinkie-promise": "^2.0.0"
           }
@@ -10753,30 +10032,29 @@
       }
     },
     "readable-stream": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
-      "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
+      "version": "2.3.6",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+      "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
       "dev": true,
       "requires": {
         "core-util-is": "~1.0.0",
         "inherits": "~2.0.3",
         "isarray": "~1.0.0",
-        "process-nextick-args": "~1.0.6",
+        "process-nextick-args": "~2.0.0",
         "safe-buffer": "~5.1.1",
-        "string_decoder": "~1.0.3",
+        "string_decoder": "~1.1.1",
         "util-deprecate": "~1.0.1"
       }
     },
     "readdirp": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz",
-      "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=",
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+      "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
       "dev": true,
       "requires": {
-        "graceful-fs": "^4.1.2",
-        "minimatch": "^3.0.2",
-        "readable-stream": "^2.0.2",
-        "set-immediate-shim": "^1.0.1"
+        "graceful-fs": "^4.1.11",
+        "micromatch": "^3.1.10",
+        "readable-stream": "^2.0.2"
       }
     },
     "rechoir": {
@@ -10800,9 +10078,9 @@
       }
     },
     "reflect-metadata": {
-      "version": "0.1.12",
-      "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.12.tgz",
-      "integrity": "sha512-n+IyV+nGz3+0q3/Yf1ra12KpCyi001bi4XFxSjbiWWjfqb52iTTtpGXmCCAOWWIAn9KEuFZKGqBERHmrtScZ3A==",
+      "version": "0.1.13",
+      "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
+      "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==",
       "dev": true
     },
     "regenerate": {
@@ -10860,6 +10138,14 @@
       "dev": true,
       "requires": {
         "jsesc": "~0.5.0"
+      },
+      "dependencies": {
+        "jsesc": {
+          "version": "0.5.0",
+          "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+          "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
+          "dev": true
+        }
       }
     },
     "remove-trailing-separator": {
@@ -10869,9 +10155,9 @@
       "dev": true
     },
     "repeat-element": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz",
-      "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=",
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+      "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
       "dev": true
     },
     "repeat-string": {
@@ -10894,7 +10180,6 @@
       "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
       "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
       "dev": true,
-      "optional": true,
       "requires": {
         "aws-sign2": "~0.7.0",
         "aws4": "^1.8.0",
@@ -10916,15 +10201,6 @@
         "tough-cookie": "~2.4.3",
         "tunnel-agent": "^0.6.0",
         "uuid": "^3.3.2"
-      },
-      "dependencies": {
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-          "dev": true,
-          "optional": true
-        }
       }
     },
     "require-directory": {
@@ -10952,13 +10228,10 @@
       "dev": true
     },
     "resolve": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.9.0.tgz",
-      "integrity": "sha512-TZNye00tI67lwYvzxCxHGjwTNlUV70io54/Ed4j6PscB8xVfuBJpRenI/o6dVk0cY0PYTY27AgCoGGxRnYuItQ==",
-      "dev": true,
-      "requires": {
-        "path-parse": "^1.0.6"
-      }
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+      "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+      "dev": true
     },
     "resolve-cwd": {
       "version": "2.0.0",
@@ -11020,12 +10293,12 @@
       }
     },
     "rimraf": {
-      "version": "2.6.2",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
-      "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
+      "version": "2.6.3",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+      "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
       "dev": true,
       "requires": {
-        "glob": "^7.0.5"
+        "glob": "^7.1.3"
       }
     },
     "ripemd160": {
@@ -11063,29 +10336,22 @@
       "dev": true
     },
     "rxjs": {
-      "version": "6.3.3",
-      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
-      "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
+      "version": "6.4.0",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz",
+      "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==",
       "requires": {
         "tslib": "^1.9.0"
-      },
-      "dependencies": {
-        "tslib": {
-          "version": "1.9.3",
-          "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
-          "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
-        }
       }
     },
     "rxjs-compat": {
-      "version": "6.3.3",
-      "resolved": "https://registry.npmjs.org/rxjs-compat/-/rxjs-compat-6.3.3.tgz",
-      "integrity": "sha512-caGN7ixiabHpOofginKEquuHk7GgaCrC7UpUQ9ZqGp80tMc68msadOeP/2AKy2R4YJsT1+TX5GZCtxO82qWkyA=="
+      "version": "6.4.0",
+      "resolved": "https://registry.npmjs.org/rxjs-compat/-/rxjs-compat-6.4.0.tgz",
+      "integrity": "sha512-eo/O8RS83hJdJukCtA+IF6qnqa8FPOuVo+OPCzgVi+dbTle9KCdNv97IcQO0WwNVik7DJLKmf0F8uwzc0q40vw=="
     },
     "safe-buffer": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
-      "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
       "dev": true
     },
     "safe-regex": {
@@ -11128,20 +10394,6 @@
         "neo-async": "^2.5.0",
         "pify": "^3.0.0",
         "semver": "^5.5.0"
-      },
-      "dependencies": {
-        "pify": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
-          "dev": true
-        },
-        "semver": {
-          "version": "5.6.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
-          "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
-          "dev": true
-        }
       }
     },
     "saucelabs": {
@@ -11154,9 +10406,9 @@
       }
     },
     "sax": {
-      "version": "1.2.4",
-      "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
-      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+      "version": "0.5.8",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz",
+      "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=",
       "dev": true
     },
     "schema-utils": {
@@ -11183,7 +10435,7 @@
       "dependencies": {
         "source-map": {
           "version": "0.4.4",
-          "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
           "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
           "dev": true,
           "optional": true,
@@ -11238,9 +10490,9 @@
       }
     },
     "semver": {
-      "version": "5.4.1",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
-      "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==",
+      "version": "5.5.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
+      "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==",
       "dev": true
     },
     "semver-dsl": {
@@ -11287,12 +10539,6 @@
           "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
           "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==",
           "dev": true
-        },
-        "statuses": {
-          "version": "1.4.0",
-          "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
-          "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==",
-          "dev": true
         }
       }
     },
@@ -11335,12 +10581,6 @@
       "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
       "dev": true
     },
-    "set-immediate-shim": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
-      "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=",
-      "dev": true
-    },
     "set-value": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
@@ -11378,7 +10618,7 @@
     },
     "sha.js": {
       "version": "2.4.11",
-      "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+      "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
       "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
       "dev": true,
       "requires": {
@@ -11444,9 +10684,9 @@
       "dev": true
     },
     "smart-buffer": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.1.tgz",
-      "integrity": "sha512-RFqinRVJVcCAL9Uh1oVqE6FZkqsyLiVOYEZ20TqIOjuX7iFVJ+zsbs4RIghnw/pTs7mZvt8ZHhvm1ZUrR4fykg==",
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.2.tgz",
+      "integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw==",
       "dev": true
     },
     "snapdragon": {
@@ -11482,6 +10722,12 @@
           "requires": {
             "is-extendable": "^0.1.0"
           }
+        },
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
         }
       }
     },
@@ -11533,12 +10779,6 @@
             "is-data-descriptor": "^1.0.0",
             "kind-of": "^6.0.2"
           }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
         }
       }
     },
@@ -11549,6 +10789,17 @@
       "dev": true,
       "requires": {
         "kind-of": "^3.2.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
       }
     },
     "socket.io": {
@@ -11617,7 +10868,7 @@
     },
     "socket.io-parser": {
       "version": "3.2.0",
-      "resolved": "http://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz",
+      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz",
       "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==",
       "dev": true,
       "requires": {
@@ -11694,13 +10945,13 @@
       }
     },
     "socks": {
-      "version": "2.2.2",
-      "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.2.tgz",
-      "integrity": "sha512-g6wjBnnMOZpE0ym6e0uHSddz9p3a+WsBaaYQaBaSCJYvrC4IXykQR9MNGjLQf38e9iIIhp3b1/Zk8YZI3KGJ0Q==",
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.3.tgz",
+      "integrity": "sha512-+2r83WaRT3PXYoO/1z+RDEBE7Z2f9YcdQnJ0K/ncXXbV5gJ6wYfNAebYFYiiUjM6E4JyXnPY8cimwyvFYHVUUA==",
       "dev": true,
       "requires": {
         "ip": "^1.1.5",
-        "smart-buffer": "^4.0.1"
+        "smart-buffer": "4.0.2"
       }
     },
     "socks-proxy-agent": {
@@ -11720,9 +10971,9 @@
       "dev": true
     },
     "source-map": {
-      "version": "0.5.7",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-      "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+      "version": "0.7.3",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+      "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
       "dev": true
     },
     "source-map-loader": {
@@ -11733,6 +10984,17 @@
       "requires": {
         "async": "^2.5.0",
         "loader-utils": "^1.1.0"
+      },
+      "dependencies": {
+        "async": {
+          "version": "2.6.1",
+          "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
+          "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
+          "dev": true,
+          "requires": {
+            "lodash": "^4.17.10"
+          }
+        }
       }
     },
     "source-map-resolve": {
@@ -11749,12 +11011,21 @@
       }
     },
     "source-map-support": {
-      "version": "0.4.18",
-      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
-      "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+      "version": "0.5.9",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz",
+      "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==",
       "dev": true,
       "requires": {
-        "source-map": "^0.5.6"
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
       }
     },
     "source-map-url": {
@@ -11870,22 +11141,13 @@
             "string_decoder": "^1.1.1",
             "util-deprecate": "^1.0.1"
           }
-        },
-        "string_decoder": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz",
-          "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
         }
       }
     },
     "speed-measure-webpack-plugin": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.2.3.tgz",
-      "integrity": "sha512-p+taQ69VkRUXYMoZOx2nxV/Tz8tt79ahctoZJyJDHWP7fEYvwFNf5Pd73k5kZ6auu0pTsPNLEUwWpM8mCk85Zw==",
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.2.5.tgz",
+      "integrity": "sha512-S/guYjC4Izn5wY2d0+M4zowED/F77Lxh9yjkTZ+XAr244pr9c1MYNcXcRe9lx2hmAj0GPbOrBXgOF2YIp/CZ8A==",
       "dev": true,
       "requires": {
         "chalk": "^2.0.1"
@@ -11911,14 +11173,14 @@
     },
     "sprintf-js": {
       "version": "1.0.3",
-      "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
       "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
       "dev": true
     },
     "sshpk": {
-      "version": "1.16.0",
-      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.0.tgz",
-      "integrity": "sha512-Zhev35/y7hRMcID/upReIvRse+I9SVhyVre/KTJSJQWMz3C3+G+HpO7m1wK/yckEtujKZ7dS4hkVxAnmHaIGVQ==",
+      "version": "1.16.1",
+      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
+      "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
       "dev": true,
       "requires": {
         "asn1": "~0.2.3",
@@ -11972,9 +11234,9 @@
       }
     },
     "statuses": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
-      "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
+      "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==",
       "dev": true
     },
     "stdout-stream": {
@@ -11988,9 +11250,9 @@
       }
     },
     "stream-browserify": {
-      "version": "2.0.1",
-      "resolved": "http://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz",
-      "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=",
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
+      "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==",
       "dev": true,
       "requires": {
         "inherits": "~2.0.1",
@@ -12027,38 +11289,6 @@
         "readable-stream": "^2.3.6",
         "to-arraybuffer": "^1.0.0",
         "xtend": "^4.0.0"
-      },
-      "dependencies": {
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-          "dev": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        }
       }
     },
     "stream-shift": {
@@ -12098,7 +11328,7 @@
     },
     "string-width": {
       "version": "1.0.2",
-      "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
       "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
       "dev": true,
       "requires": {
@@ -12108,9 +11338,9 @@
       }
     },
     "string_decoder": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
-      "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
       "dev": true,
       "requires": {
         "safe-buffer": "~5.1.0"
@@ -12126,14 +11356,17 @@
       }
     },
     "strip-bom": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
-      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
-      "dev": true
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+      "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+      "dev": true,
+      "requires": {
+        "is-utf8": "^0.2.0"
+      }
     },
     "strip-eof": {
       "version": "1.0.0",
-      "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
       "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
       "dev": true
     },
@@ -12185,15 +11418,9 @@
             "path-is-absolute": "^1.0.0"
           }
         },
-        "sax": {
-          "version": "0.5.8",
-          "resolved": "http://registry.npmjs.org/sax/-/sax-0.5.8.tgz",
-          "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=",
-          "dev": true
-        },
         "source-map": {
           "version": "0.1.43",
-          "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz",
           "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=",
           "dev": true,
           "requires": {
@@ -12214,9 +11441,9 @@
       }
     },
     "supports-color": {
-      "version": "5.5.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+      "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
       "dev": true,
       "requires": {
         "has-flag": "^3.0.0"
@@ -12236,7 +11463,7 @@
     },
     "tar": {
       "version": "2.2.1",
-      "resolved": "http://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
       "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=",
       "dev": true,
       "optional": true,
@@ -12247,14 +11474,14 @@
       }
     },
     "terser": {
-      "version": "3.14.1",
-      "resolved": "https://registry.npmjs.org/terser/-/terser-3.14.1.tgz",
-      "integrity": "sha512-NSo3E99QDbYSMeJaEk9YW2lTg3qS9V0aKGlb+PlOrei1X02r1wSBHCNX/O+yeTRFSWPKPIGj6MqvvdqV4rnVGw==",
+      "version": "3.16.1",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-3.16.1.tgz",
+      "integrity": "sha512-JDJjgleBROeek2iBcSNzOHLKsB/MdDf+E/BOAJ0Tk9r7p9/fVobfv7LMJ/g/k3v9SXdmjZnIlFd5nfn/Rt0Xow==",
       "dev": true,
       "requires": {
         "commander": "~2.17.1",
         "source-map": "~0.6.1",
-        "source-map-support": "~0.5.6"
+        "source-map-support": "~0.5.9"
       },
       "dependencies": {
         "source-map": {
@@ -12262,23 +11489,13 @@
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
           "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
           "dev": true
-        },
-        "source-map-support": {
-          "version": "0.5.10",
-          "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz",
-          "integrity": "sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==",
-          "dev": true,
-          "requires": {
-            "buffer-from": "^1.0.0",
-            "source-map": "^0.6.0"
-          }
         }
       }
     },
     "terser-webpack-plugin": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.1.0.tgz",
-      "integrity": "sha512-61lV0DSxMAZ8AyZG7/A4a3UPlrbOBo8NIQ4tJzLPAdGOQ+yoNC7l5ijEow27lBAL2humer01KLS6bGIMYQxKoA==",
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.1.tgz",
+      "integrity": "sha512-GGSt+gbT0oKcMDmPx4SRSfJPE1XaN3kQRWG4ghxKQw9cn5G9x6aCKSsgYdvyM0na9NJ4Drv0RG6jbBByZ5CMjw==",
       "dev": true,
       "requires": {
         "cacache": "^11.0.2",
@@ -12333,26 +11550,6 @@
             "locate-path": "^3.0.0"
           }
         },
-        "glob": {
-          "version": "7.1.3",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
-          "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
-          "dev": true,
-          "requires": {
-            "fs.realpath": "^1.0.0",
-            "inflight": "^1.0.4",
-            "inherits": "2",
-            "minimatch": "^3.0.4",
-            "once": "^1.3.0",
-            "path-is-absolute": "^1.0.0"
-          }
-        },
-        "graceful-fs": {
-          "version": "4.1.15",
-          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
-          "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
-          "dev": true
-        },
         "locate-path": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
@@ -12447,6 +11644,12 @@
           "requires": {
             "figgy-pudding": "^3.5.1"
           }
+        },
+        "yallist": {
+          "version": "3.0.3",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
+          "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
+          "dev": true
         }
       }
     },
@@ -12464,38 +11667,6 @@
       "requires": {
         "readable-stream": "~2.3.6",
         "xtend": "~4.0.1"
-      },
-      "dependencies": {
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-          "dev": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        }
       }
     },
     "thunky": {
@@ -12520,9 +11691,9 @@
       }
     },
     "tiny-emitter": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.0.2.tgz",
-      "integrity": "sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+      "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
       "optional": true
     },
     "tmp": {
@@ -12547,9 +11718,9 @@
       "dev": true
     },
     "to-fast-properties": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
-      "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
+      "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=",
       "dev": true
     },
     "to-object-path": {
@@ -12559,6 +11730,17 @@
       "dev": true,
       "requires": {
         "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
       }
     },
     "to-regex": {
@@ -12581,17 +11763,6 @@
       "requires": {
         "is-number": "^3.0.0",
         "repeat-string": "^1.6.1"
-      },
-      "dependencies": {
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "dev": true,
-          "requires": {
-            "kind-of": "^3.0.2"
-          }
-        }
       }
     },
     "tough-cookie": {
@@ -12599,10 +11770,17 @@
       "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
       "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
       "dev": true,
-      "optional": true,
       "requires": {
         "psl": "^1.1.24",
         "punycode": "^1.4.1"
+      },
+      "dependencies": {
+        "punycode": {
+          "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+          "dev": true
+        }
       }
     },
     "traverse": {
@@ -12656,21 +11834,11 @@
         "yn": "^2.0.0"
       },
       "dependencies": {
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
           "dev": true
-        },
-        "source-map-support": {
-          "version": "0.5.9",
-          "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz",
-          "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==",
-          "dev": true,
-          "requires": {
-            "buffer-from": "^1.0.0",
-            "source-map": "^0.6.0"
-          }
         }
       }
     },
@@ -12692,6 +11860,16 @@
         "typescript": "2.9.1"
       },
       "dependencies": {
+        "dir-glob": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz",
+          "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==",
+          "dev": true,
+          "requires": {
+            "arrify": "^1.0.1",
+            "path-type": "^3.0.0"
+          }
+        },
         "fs-extra": {
           "version": "6.0.1",
           "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz",
@@ -12704,13 +11882,13 @@
           }
         },
         "globby": {
-          "version": "8.0.1",
-          "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.1.tgz",
-          "integrity": "sha512-oMrYrJERnKBLXNLVTqhm3vPEdJ/b2ZE28xN4YARiix1NOIOBPEpOUnm844K1iu/BkphCaf2WNFwMszv8Soi1pw==",
+          "version": "8.0.2",
+          "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz",
+          "integrity": "sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w==",
           "dev": true,
           "requires": {
             "array-union": "^1.0.1",
-            "dir-glob": "^2.0.0",
+            "dir-glob": "2.0.0",
             "fast-glob": "^2.0.2",
             "glob": "^7.1.2",
             "ignore": "^3.3.5",
@@ -12718,12 +11896,6 @@
             "slash": "^1.0.0"
           }
         },
-        "pify": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
-          "dev": true
-        },
         "typescript": {
           "version": "2.9.1",
           "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.1.tgz",
@@ -12738,9 +11910,9 @@
       "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
     },
     "tslint": {
-      "version": "5.12.0",
-      "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.12.0.tgz",
-      "integrity": "sha512-CKEcH1MHUBhoV43SA/Jmy1l24HJJgI0eyLbBNSRyFlsQvb9v6Zdq+Nz2vEOH00nC5SUx4SneJ59PZUS/ARcokQ==",
+      "version": "5.12.1",
+      "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.12.1.tgz",
+      "integrity": "sha512-sfodBHOucFg6egff8d1BvuofoOQ/nOeYNfbp7LDlKBcLNrL3lmS5zoiDGyOMdT7YsEXAwWpTdAHwOGOc8eRZAw==",
       "dev": true,
       "requires": {
         "babel-code-frame": "^6.22.0",
@@ -12755,6 +11927,17 @@
         "semver": "^5.3.0",
         "tslib": "^1.8.0",
         "tsutils": "^2.27.2"
+      },
+      "dependencies": {
+        "resolve": {
+          "version": "1.10.0",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
+          "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
+          "dev": true,
+          "requires": {
+            "path-parse": "^1.0.6"
+          }
+        }
       }
     },
     "tsutils": {
@@ -12768,7 +11951,7 @@
     },
     "tty-browserify": {
       "version": "0.0.0",
-      "resolved": "http://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
+      "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
       "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
       "dev": true
     },
@@ -12845,56 +12028,6 @@
       "dev": true,
       "optional": true
     },
-    "uglifyjs-webpack-plugin": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz",
-      "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"
-      },
-      "dependencies": {
-        "commander": {
-          "version": "2.13.0",
-          "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz",
-          "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==",
-          "dev": true
-        },
-        "schema-utils": {
-          "version": "0.4.7",
-          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz",
-          "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==",
-          "dev": true,
-          "requires": {
-            "ajv": "^6.1.0",
-            "ajv-keywords": "^3.1.0"
-          }
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
-        },
-        "uglify-es": {
-          "version": "3.3.9",
-          "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz",
-          "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==",
-          "dev": true,
-          "requires": {
-            "commander": "~2.13.0",
-            "source-map": "~0.6.1"
-          }
-        }
-      }
-    },
     "ultron": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
@@ -13031,14 +12164,6 @@
       "dev": true,
       "requires": {
         "punycode": "^2.1.0"
-      },
-      "dependencies": {
-        "punycode": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
-          "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
-          "dev": true
-        }
       }
     },
     "urix": {
@@ -13092,9 +12217,9 @@
       }
     },
     "util": {
-      "version": "0.10.4",
-      "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
-      "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
+      "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==",
       "dev": true,
       "requires": {
         "inherits": "2.0.3"
@@ -13152,14 +12277,6 @@
         "assert-plus": "^1.0.0",
         "core-util-is": "1.0.2",
         "extsprintf": "^1.2.0"
-      },
-      "dependencies": {
-        "assert-plus": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-          "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
-          "dev": true
-        }
       }
     },
     "viz.js": {
@@ -13170,7 +12287,7 @@
     },
     "vm-browserify": {
       "version": "0.0.4",
-      "resolved": "http://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
+      "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
       "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=",
       "dev": true,
       "requires": {
@@ -13214,15 +12331,15 @@
       }
     },
     "webpack": {
-      "version": "4.23.1",
-      "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.23.1.tgz",
-      "integrity": "sha512-iE5Cu4rGEDk7ONRjisTOjVHv3dDtcFfwitSxT7evtYj/rANJpt1OuC/Kozh1pBa99AUBr1L/LsaNB+D9Xz3CEg==",
+      "version": "4.28.4",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.28.4.tgz",
+      "integrity": "sha512-NxjD61WsK/a3JIdwWjtIpimmvE6UrRi3yG54/74Hk9rwNj5FPkA4DJCf1z4ByDWLkvZhTZE+P3C/eh6UD5lDcw==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/ast": "1.7.10",
-        "@webassemblyjs/helper-module-context": "1.7.10",
-        "@webassemblyjs/wasm-edit": "1.7.10",
-        "@webassemblyjs/wasm-parser": "1.7.10",
+        "@webassemblyjs/ast": "1.7.11",
+        "@webassemblyjs/helper-module-context": "1.7.11",
+        "@webassemblyjs/wasm-edit": "1.7.11",
+        "@webassemblyjs/wasm-parser": "1.7.11",
         "acorn": "^5.6.2",
         "acorn-dynamic-import": "^3.0.0",
         "ajv": "^6.1.0",
@@ -13240,7 +12357,7 @@
         "node-libs-browser": "^2.0.0",
         "schema-utils": "^0.4.4",
         "tapable": "^1.1.0",
-        "uglifyjs-webpack-plugin": "^1.2.4",
+        "terser-webpack-plugin": "^1.1.0",
         "watchpack": "^1.5.0",
         "webpack-sources": "^1.3.0"
       },
@@ -13275,7 +12392,7 @@
         },
         "source-map": {
           "version": "0.4.4",
-          "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
           "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
           "dev": true,
           "requires": {
@@ -13294,6 +12411,14 @@
         "mime": "^2.3.1",
         "range-parser": "^1.0.3",
         "webpack-log": "^2.0.0"
+      },
+      "dependencies": {
+        "mime": {
+          "version": "2.4.0",
+          "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz",
+          "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==",
+          "dev": true
+        }
       }
     },
     "webpack-dev-server": {
@@ -13340,6 +12465,12 @@
           "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
           "dev": true
         },
+        "camelcase": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+          "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
+          "dev": true
+        },
         "cliui": {
           "version": "4.1.0",
           "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
@@ -13535,6 +12666,15 @@
             }
           }
         },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        },
         "which-module": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
@@ -13661,7 +12801,6 @@
       "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
       "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
       "dev": true,
-      "optional": true,
       "requires": {
         "string-width": "^1.0.2 || 2"
       }
@@ -13683,9 +12822,9 @@
       }
     },
     "wordwrap": {
-      "version": "0.0.2",
-      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
-      "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+      "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
       "dev": true
     },
     "worker-farm": {
@@ -13699,7 +12838,7 @@
     },
     "wrap-ansi": {
       "version": "2.1.0",
-      "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
       "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
       "dev": true,
       "requires": {
@@ -13732,11 +12871,19 @@
       "requires": {
         "sax": ">=0.6.0",
         "xmlbuilder": "~9.0.1"
+      },
+      "dependencies": {
+        "sax": {
+          "version": "1.2.4",
+          "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+          "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+          "dev": true
+        }
       }
     },
     "xmlbuilder": {
       "version": "9.0.7",
-      "resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
+      "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
       "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=",
       "dev": true
     },
@@ -13765,9 +12912,9 @@
       "dev": true
     },
     "yallist": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
-      "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+      "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
       "dev": true
     },
     "yargs": {
@@ -13840,9 +12987,9 @@
       "dev": true
     },
     "zone.js": {
-      "version": "0.8.26",
-      "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.26.tgz",
-      "integrity": "sha512-W9Nj+UmBJG251wkCacIkETgra4QgBo/vgoEkb4a2uoLzpQG7qF9nzwoLXWU5xj3Fg2mxGvEDh47mg24vXccYjA=="
+      "version": "0.8.29",
+      "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.29.tgz",
+      "integrity": "sha512-mla2acNCMkWXBD+c+yeUrBUrzOxYMNFdQ6FGfigGGtEVBPJx07BQeJekjt9DmH1FtZek4E9rE1eRR9qQpxACOQ=="
     }
   }
 }
diff --git a/package.json b/package.json
index c8737ad12a0b36198e40bc71cb712ef4eccd2b34..77d7b5888f7620ff738500d9d9894f80ff68abec 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "nghyd",
-  "version": "1.1.0",
+  "version": "1.2.0",
   "license": "MIT",
   "scripts": {
     "ng": "ng",
@@ -20,37 +20,41 @@
   },
   "private": true,
   "dependencies": {
-    "@angular/animations": "^7.1.4",
-    "@angular/common": "^7.1.4",
-    "@angular/compiler": "^7.1.4",
-    "@angular/core": "^7.1.4",
-    "@angular/forms": "^7.1.4",
-    "@angular/http": "^7.1.4",
-    "@angular/platform-browser": "^7.1.4",
-    "@angular/platform-browser-dynamic": "^7.1.4",
-    "@angular/router": "^7.1.4",
-    "angular-bootstrap-md": "^7.2.0",
+    "@angular/animations": "^7.2.1",
+    "@angular/cdk": "^7.2.1",
+    "@angular/common": "^7.2.1",
+    "@angular/compiler": "^7.2.1",
+    "@angular/core": "^7.2.1",
+    "@angular/flex-layout": "^7.0.0-beta.23",
+    "@angular/forms": "^7.2.1",
+    "@angular/http": "^7.2.1",
+    "@angular/material": "^7.2.1",
+    "@angular/platform-browser": "^7.2.1",
+    "@angular/platform-browser-dynamic": "^7.2.1",
+    "@angular/router": "^7.2.1",
+    "@types/sprintf-js": "^1.1.1",
     "angular2-chartjs": "^0.5.1",
-    "core-js": "^2.6.1",
+    "core-js": "^2.6.3",
     "file-saver": "^2.0.0",
-    "font-awesome": "^4.7.0",
+    "hammerjs": "^2.0.8",
     "he": "^1.2.0",
     "jalhyd": "file:../jalhyd",
     "mathjax": "^2.7.5",
+    "ngx-material-file-input": "^1.1.0",
     "ngx-md": "^7.0.0",
     "rxjs": "^6.3.3",
     "rxjs-compat": "^6.3.3",
     "tslib": "^1.9.0",
-    "zone.js": "^0.8.26"
+    "zone.js": "^0.8.28"
   },
   "devDependencies": {
-    "@angular-devkit/build-angular": "^0.12.1",
-    "@angular/cli": "^7.1.4",
-    "@angular/compiler-cli": "^7.1.4",
-    "@angular/language-service": "^7.1.4",
+    "@angular-devkit/build-angular": "^0.12.2",
+    "@angular/cli": "^7.2.2",
+    "@angular/compiler-cli": "^7.2.1",
+    "@angular/language-service": "^7.2.1",
     "@compodoc/compodoc": "^1.1.7",
     "@types/file-saver": "^2.0.0",
-    "@types/jasmine": "~3.3.5",
+    "@types/jasmine": "^3.3.7",
     "@types/jasminewd2": "^2.0.6",
     "@types/node": "^8.10.39",
     "codelyzer": "^4.5.0",
@@ -64,7 +68,7 @@
     "karma-jasmine-html-reporter": "^1.4.0",
     "protractor": "^5.4.2",
     "ts-node": "~7.0.1",
-    "tslint": "~5.12.0",
+    "tslint": "^5.12.1",
     "typescript": "~3.1.1",
     "webpack-dev-server": "^3.1.14"
   }
diff --git a/src/app/app.component.html b/src/app/app.component.html
index b1e92164976b1c922802f3cb14ebc8994a369903..fbcfa67dbeadfe99ba0a0dc88d45bc44e1eaf537 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,85 +1,114 @@
+<app-layout>
+
 <header>
-  <!--Navbar -->
-  <nav class="navbar navbar-expand-sm navbar-dark indigo">
-    <!-- Ouverture du sidenav -->
-    <span id="open-menu" style="font-size:30px;cursor:pointer;color:white" (click)="openNav()">☰</span>
-
-    <!-- lien vide pour que le toggler des liens apparaisse à droite -->
-    <a class="navbar-brand"></a>
-
-    <!-- toggler des liens -->
-    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
-      aria-expanded="false" aria-label="Toggle navigation">
-      <!-- <span class="navbar-toggler-icon"> </span> -->
-      <i class="fa fa-ellipsis-v" aria-hidden="true"></i>
-    </button>
 
-    <!-- Collapsible content -->
-    <div class="collapse navbar-collapse" id="navbarSupportedContent">
-      <ul id="navbar" class="navbar-nav mr-auto">
-        <li class="nav-item calculator-tab" *ngFor="let c of calculators">
-          <a class="nav-link waves-light {{getHighlightClass(c.uid)}}" mdbRippleRadius [routerLink]="['/calculator/',c.uid]">{{ c.title }}</a>
-        </li>
-        <li class="nav-item" id="add-calculator-tab">
-            <i id="new-calculator" class="fa fa-plus-square fa-2x fa-inverse" style='vertical-align: middle' routerLink="/list" (click)='closeNav()'></i>
-        </li>
-      </ul>
+  <mat-toolbar #navbar id="main-toolbar" color="primary">
+
+    <span id="open-menu" style="font-size:30px;cursor:pointer;color:white" (click)="sidenav.toggle()">
+      <mat-icon>menu</mat-icon>
+    </span>
+
+    <!-- calculators list as a dropdown menu-->
+    <div [hidden]="tabsFitInNavbar" id="dropdown-calc-container">
+
+        <button *ngIf="! currentCalc" mat-button [matMenuTriggerFor]="menu" color="primary" class="calculators-menu-title">
+          <mat-icon class="dropdown-icon">arrow_drop_down</mat-icon>
+          <span class="calc-name">
+            {{ uitextSelectCalc }}
+          </span>
+        </button>
+        <button *ngIf="currentCalc" mat-button [matMenuTriggerFor]="menu" color="primary" class="calculators-menu-title">
+          <mat-icon class="dropdown-icon">arrow_drop_down</mat-icon>
+          <span class="calc-name">
+            {{ currentCalc.title }}
+          </span>
+          <span class="calc-type" [innerHTML]="'( ' + currentCalc ? currentCalc.type : '' + ' )'"></span>
+        </button>
+
+        <mat-menu #menu="matMenu" colo="accent">
+          <button mat-button *ngFor="let c of calculators" class="calculator-menu-item" [title]="c.title"
+          [routerLink]="['/calculator/',c.uid]" [color]="c.active ? 'primary' : ''" [class.active]="c.active"
+          (click)="setActiveCalc(c.uid)">
+
+            <span class="calc-name">
+              {{ c.title }}
+            </span>
+            <span class="calc-type" [innerHTML]="'(' + getCalculatorLabel(c.type) + ')'"></span>
+          </button>
+        </mat-menu>
     </div>
-  </nav>
-  <!--/Navbar -->
+
+    <!-- calculators list as a tabs bar-->
+    <div id="tabs-container" [hidden]="! tabsFitInNavbar">
+      <button mat-raised-button color="primary" *ngFor="let c of calculators" class="calculator-button" [title]="c.title"
+        [routerLink]="['/calculator/',c.uid]" [color]="c.active ? '' : 'primary'" [class.active]="c.active"
+        (click)="setActiveCalc(c.uid)">
+
+        <span class="calc-name">
+          {{ c.title }}
+        </span>
+        <span class="calc-type" [innerHTML]="'(' + getCalculatorLabel(c.type) + ')'"></span>
+      </button>
+      
+    </div>
+
+    <button mat-icon-button id="new-calculator" routerLink="/list" (click)="sidenav.close()">
+      <mat-icon>add_box</mat-icon>
+    </button>
+
+    <div id="toolbar-bottom-spacer"></div>
+  </mat-toolbar>
+
 </header>
 
 <main>
-  <!-- sidenav -->
-  <div class="container">
-    <div class="row">
-      <div id="mySidenav" class="sidenav">
-        <!-- ATTENTION ! pas de href="#" sous peine de rechargement de la page et réinitialisation de l'appli -->
-        <a id="close-side-nav" class="closebtn" (click)="closeNav()">×</a>
-        <a id="side-nav-list" routerLink="/list" (click)="closeNav()">{{ uitextSidenavNewCalc }}</a>
-        <a id="side-nav-load-session" (click)="loadSession()">{{ uitextSidenavLoadSession }}</a>
-        <a id="side-nav-setup" routerLink="/setup" (click)="closeNav()">{{ uitextSidenavParams }}</a>
-        <a id="side-nav-help" target="_blank" href="assets/docs-fr/" (click)="closeNav()">{{ uitextSidenavHelp }}</a>
-        <div class="hyd_fillvertical"></div>
+  <mat-sidenav-container fxFlexFill class="example-container">
+
+    <!-- side panel -->
+    <mat-sidenav class="sidenav" #sidenav fxLayout="column" >
+      <div fxLayout="column">
+        <a id="close-side-nav" class="closebtn" (click)="sidenav.close()">×</a>
+        
+        <a id="side-nav-list" routerLink="/list" (click)="sidenav.close()">
+          <mat-icon>add</mat-icon>
+          {{ uitextSidenavNewCalc }}
+        </a>
+        <a id="side-nav-empty-session" (click)="sidenav.close(); emptySession()">
+            <mat-icon>insert_drive_file</mat-icon>
+            {{ uitextSidenavEmptySession }}
+        </a>
+        <a id="side-nav-load-session" (click)="loadSession(); sidenav.close()">
+          <mat-icon>folder_open</mat-icon>
+          {{ uitextSidenavLoadSession }}
+        </a>
+        <a id="side-nav-save-session" (click)="saveForm(); sidenav.close()">
+            <mat-icon>save_alt</mat-icon>
+          {{ uitextSidenavSaveSession }}
+        </a>
+        <a id="side-nav-setup" routerLink="/setup" (click)="sidenav.close()">
+          <mat-icon>settings</mat-icon>
+          {{ uitextSidenavParams }}
+        </a>
+        <a id="side-nav-help" target="_blank" href="assets/docs-fr/" (click)="sidenav.close()">
+          <mat-icon>help</mat-icon>
+          {{ uitextSidenavHelp }}
+        </a>
+
         <div class="hyd_version">
           JaLHyd version: {{ getDateRevision()[0] }}<br/>
           ngHyd version: {{ getDateRevision()[1] }}
         </div>
       </div>
-    </div>
-  </div>
-
-  <!-- chargement des calculettes -->
-  <div appLoadCalcDialogAnchor></div>
-
-  <!-- sauvegarde des calculettes -->
-  <div appSaveCalcDialogAnchor></div>
-
-  <!-- règle gradée des colonnes Bootstrap -->
-  <div *ngIf="ruler" class="container-fluid">
-    <div class="row">
-      <div class="col 1 red">1</div>
-      <div class="col 1 green">2</div>
-      <div class="col 1 blue">3</div>
-      <div class="col 1 red">4</div>
-      <div class="col 1 green">5</div>
-      <div class="col 1 blue">6</div>
-      <div class="col 1 red">7</div>
-      <div class="col 1 green">8</div>
-      <div class="col 1 blue">9</div>
-      <div class="col 1 red">10</div>
-      <div class="col 1 green">11</div>
-      <div class="col 1 blue">12</div>
-    </div>
-  </div>
+    </mat-sidenav>
 
-  <div class="container-fluid">
-    <div class="row">
-      <div class="col-12">
+    <mat-sidenav-content class="sidenav-content" fxFlexFill>
+      <div id="app-content">
         <router-outlet (activate)="onRouterOutletActivated($event)"></router-outlet>
       </div>
-    </div>
-  </div>
+    </mat-sidenav-content>
+
+  </mat-sidenav-container>
+
 </main>
 
 <footer>
@@ -88,4 +117,6 @@
     <br>
     <a href="http://www.irstea.fr/">Institut national de recherche en sciences et technologies pour l'environnement et l'agriculture</a>
   </div> -->
-</footer>
\ No newline at end of file
+</footer>
+
+</app-layout>
diff --git a/src/app/app.component.scss b/src/app/app.component.scss
index 9a709694f5b416ba83ec193f9c35a427f232e76d..e3dee283cdb433f03f34c757032b5cd340ee8ba4 100644
--- a/src/app/app.component.scss
+++ b/src/app/app.component.scss
@@ -1,69 +1,209 @@
+// variables
+
+$navbar_height: 54px;
+
+// rules
+
 .dropdown-menu > li > a:hover {
     background-color: rgb(172, 172, 172);
     background-image: none;
 }
 
-// sidenav
-body {
-    font-family: "Lato", sans-serif;
+button:focus {
+    outline: 0;
 }
 
-.sidenav {
-    height: 100%;
-    width: 0;
+#main-toolbar {
     position: fixed;
-    z-index: 2000;
-    top: 0;
-    left: 0;
-    background-color: #111;
-    overflow-x: hidden;
-    transition: 0.5s;
-    padding-top: 60px;
-    display: flex;
-    flex-direction: column;
+    height: $navbar_height;
+    z-index: 200;
+}
+
+#toolbar-bottom-spacer {
+    height: 3px;
+    background-color: white;
+    z-index: 200;
+    position: absolute;
+    top: 54px;
+    width: 100%;
 }
 
-.sidenav a, .sidenav div.hyd_version {
-    padding: 8px 8px 8px 32px;
-    text-decoration: none;
-    font-size: 16px;
-    color: #aaaaaa;
-    display: block;
-    transition: 0.3s;
+#open-menu {
+    margin-right: 16px;
 }
 
-.sidenav div.hyd_version {
-    color: #888888;
-    padding: 0px 32px 0px 0px;
-    text-align: right;
+#open-menu mat-icon, #new-calculator mat-icon {
+    transform: scale(1.6);
 }
 
-.sidenav div.hyd_fillvertical {
-    flex: 1;
+#new-calculator {
+    cursor: pointer;
+    position: absolute;
+    right: 10px;
+
+    &:focus {
+        outline: 0;
+    }
 }
 
-.sidenav a:hover {
-    color: #f1f1f1;
+#tabs-container {
+    width: 100%;
 }
 
-.sidenav .closebtn {
-    position: absolute;
-    top: 0;
-    right: 25px;
-    font-size: 36px;
-    margin-left: 50px;
+#dropdown-calc-container {
+    width: 100%;
+    padding-right: 42px;
+}
+
+.calculator-button, .calculators-menu-title, .calculator-menu-item {
+
+    .calc-name {
+        display: block;
+        margin-top: -3px; // ark !
+        text-overflow: ellipsis;
+        overflow: hidden;
+    }
+
+    .calc-type {
+        display: block;
+        color: #bbb;
+        font-size: 0.7em;
+        line-height: 24px;
+        margin-top: -14px; // ark !
+        text-overflow: ellipsis;
+        overflow: hidden;
+    }
+}
+
+.calculator-button {
+    // @TODO remplacer par du Flex ?
+    width: 15.5%; // 6 larger tabs
+    @media screen and (max-width: 1200px) {
+        width: 14.5%; // 6 tabs
+    }
+    @media screen and (max-width: 800px) {
+        width: 21%; // 4 tabs
+    }
+    @media screen and (max-width: 640px) {
+        width: 27%; // 3 larger tabs
+    }
+    @media screen and (max-width: 480px) {
+        width: 24%; // 3 tabs
+    }
+    // tabs-looking buttons
+    margin-right: 3px;
+    margin-top: 11px;
+    border-bottom-left-radius: 0;
+    border-bottom-right-radius: 0;
+    border-bottom: solid #3F51B5 4px;
+
+    &.active {
+        border-bottom-color: white;
+        box-shadow: none;
+
+        .calc-type {
+            color: #777;
+        }
+    }
 }
 
-.navbar-brand {
-	color: #fff;
+.calculators-menu-title {
+    background-color: white;
+    margin-top: 8px;
+    width: 100%; // adapts to small screens
+    max-width: 400px;
+
+    .dropdown-icon {
+        float: right;
+        transform: scale(2);
+        margin-top: 8px;
+        margin-left: 10px;
+    }
+
+    .calc-type {
+        color: #777;
+    }
+}
+
+.calculator-menu-item {
+    width: 100%;
+    border-radius: 0;
+
+    &.active {
+        .calc-type {
+            color: #777;
+        }
+    }
+
+    &:hover {
+        background-color: #ddd;
+    }
+}
+
+// sidenav
+
+.sidenav {
+    position:fixed;
+    height: 100%;
+    background-color: #111;
+    overflow-x: hidden;
+    padding-top: 60px + $navbar_height;
+    padding-right: 4em;
+    z-index: 100;
+
+    a, .div.hyd_version {
+        padding: 8px 8px 8px 24px;
+        text-decoration: none;
+        font-size: 16px;
+        color: #aaaaaa;
+        display: block;
+        transition: 0.3s;
+
+        &:focus {
+            outline: 0;
+        }
+
+        &:hover {
+            color: #f1f1f1;
+        }
+    }
+
+    a {
+        cursor: pointer;
+    }
+
+    div.hyd_version {
+        position: absolute;
+        bottom: 0;
+        right: 0;
+        font-size: 0.8em;
+        color: #888888;
+        padding: 0 2em 1em 0;
+        text-align: right;
+    }
+
+    .closebtn {
+        position: absolute;
+        top: 54px;
+        right: 25px;
+        font-size: 36px;
+        margin-left: 50px;
+    }
+
+    mat-icon {
+        color: #fff;
+        vertical-align: bottom;
+        margin-right: 6px;
+    }
 }
 
-nav div.container {
-    display: contents;
+// page contents 
+
+.sidenav-content {
+    margin-top: $navbar_height;
 }
 
-@media screen and (max-height: 450px) {
-  .sidenav {padding-top: 15px;}
-  .sidenav a {font-size: 18px;}
+#app-content {
+    padding: 1em;
+    padding-bottom: 8em;
 }
-// sidenav
\ No newline at end of file
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 811356e827b358e5e6951a1ea80686d848e3129b..542a55cdb830a07f55308c501476969b8ba3ed0c 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,23 +1,24 @@
 import { Component, ApplicationRef, OnInit, OnDestroy, HostListener, ViewChild, ComponentRef } from "@angular/core";
-import { Router, ActivatedRoute } from "@angular/router";
+import { Router, Event, NavigationEnd, ActivationEnd } from "@angular/router";
 
-import { Observer, jalhydDateRev } from "jalhyd";
+import { Observer, jalhydDateRev, CalculatorType } from "jalhyd";
 
 import { environment } from "../environments/environment";
-import { InternationalisationService } from "./services/internationalisation/internationalisation.service";
+import { I18nService } from "./services/internationalisation/internationalisation.service";
 import { ErrorService } from "./services/error/error.service";
 import { FormulaireService } from "./services/formulaire/formulaire.service";
 import { FormulaireDefinition } from "./formulaire/definition/form-definition";
 import { ServiceFactory } from "./services/service-factory";
 import { ParamService } from "./services/param/param.service";
-import { ApplicationSetupService } from "./services/app-setup/app-setup.service";
 import { HttpService } from "./services/http/http.service";
-import { LoadCalcDialogAnchorDirective } from "./components/load-calculator/load-calculator-anchor.directive";
-import { LoadCalculatorComponent } from "./components/load-calculator/load-calculator.component";
-import { SaveCalcDialogAnchorDirective } from "./components/save-calculator/save-calculator-anchor.directive";
-import { SaveCalculatorComponent } from "./components/save-calculator/save-calculator.component";
+import { ApplicationSetupService } from "./services/app-setup/app-setup.service";
 import { nghydDateRev } from "../date_revision";
 
+import { MatSidenav, MatToolbar, MatDialog } from "@angular/material";
+import { DialogConfirmEmptySessionComponent } from "./components/dialog-confirm-empty-session/dialog-confirm-empty-session.component";
+import { DialogLoadSessionComponent } from "./components/dialog-load-session/dialog-load-session.component";
+import { DialogSaveSessionComponent } from "./components/dialog-save-session/dialog-save-session.component";
+
 
 @Component({
   selector: "nghyd-app",
@@ -26,24 +27,31 @@ import { nghydDateRev } from "../date_revision";
   providers: [ErrorService]
 })
 export class AppComponent implements OnInit, OnDestroy, Observer {
+
+  @ViewChild("sidenav")
+  public sidenav: MatSidenav;
+
+  @ViewChild("navbar")
+  public navbar: MatToolbar;
+
+  /** current calculator, inferred from _currentFormId by setActiveCalc() (used for navbar menu) */
+  public currentCalc: any;
+
   /**
-   * liste des calculettes. Forme des objets :
-   * "title": nom de la calculette
+   * liste des modules de calcul. Forme des objets :
+   * "title": nom du module de calcul
    * "uid": id unique du formulaire
    */
   private _calculators: any[] = [];
 
   /**
    * id du formulaire courant
-   * on utilise pas directement FormulaireService.currentFormId pour éviter l'erreur ExpressionChangedAfterItHasBeenCheckedError
+   * on utilise pas directement FormulaireService.currentFormId pour éviter l'erreur
+   * ExpressionChangedAfterItHasBeenCheckedError
    */
-  private _currentFormId: number;
-
-  @ViewChild(LoadCalcDialogAnchorDirective)
-  private appLoadCalcDialogAnchor: LoadCalcDialogAnchorDirective;
+  private _currentFormId: string;
 
-  @ViewChild(SaveCalcDialogAnchorDirective)
-  private appSaveCalcDialogAnchor: SaveCalcDialogAnchorDirective;
+  private _innerWidth: number;
 
   /**
    * composant actuellement affiché par l'élément <router-outlet>
@@ -51,33 +59,48 @@ export class AppComponent implements OnInit, OnDestroy, Observer {
   private _routerCurrentComponent: Component;
 
   constructor(
-    private intlService: InternationalisationService,
+    private intlService: I18nService,
     private paramService: ParamService,
     private appSetupService: ApplicationSetupService,
     private appRef: ApplicationRef,
     private errorService: ErrorService,
     private router: Router,
     private formulaireService: FormulaireService,
-    private httpService: HttpService
+    private httpService: HttpService,
+    private confirmEmptySessionDialog: MatDialog,
+    private saveSessionDialog: MatDialog,
+    private loadSessionDialog: MatDialog
   ) {
+    ServiceFactory.instance.httpService = httpService;
     ServiceFactory.instance.applicationSetupService = appSetupService;
     ServiceFactory.instance.paramService = paramService;
-    ServiceFactory.instance.internationalisationService = intlService;
+    ServiceFactory.instance.i18nService = intlService;
     ServiceFactory.instance.formulaireService = formulaireService;
-    ServiceFactory.instance.httpService = httpService;
-  }
 
-  // process.on('unhandledRejection', (reason, p) => {
-  //   console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
-  //   // Stack Trace
-  //   console.log(reason.stack);
-  // });
+    this.router.events.subscribe((event: Event) => {
+      // close side navigation when clicking a calculator tab
+      if (event instanceof NavigationEnd) {
+        this.sidenav.close();
+      }
+      // [de]activate calc tabs depending on loaded route
+      if (event instanceof ActivationEnd) {
+        const path = event.snapshot.url[0].path;
+        if (path === "calculator") {
+          const calcUid = event.snapshot.params.uid;
+          this.setActiveCalc(calcUid);
+        } else {
+          this.setActiveCalc(null);
+        }
+      }
+    });
+  }
 
   ngOnInit() {
     this.intlService.addObserver(this);
-    this.intlService.setLocale("fr");
+    this.intlService.setLocale(this.appSetupService.language);
     this.formulaireService.addObserver(this);
     this.subscribeErrorService();
+    this._innerWidth = window.innerWidth;
   }
 
   ngOnDestroy() {
@@ -85,6 +108,12 @@ export class AppComponent implements OnInit, OnDestroy, Observer {
     this.formulaireService.removeObserver(this);
   }
 
+  @HostListener("window:resize", ["$event"])
+  onResize(event) {
+    // keep track of window size for navbar tabs arrangement
+    this._innerWidth = window.innerWidth;
+  }
+
   public get uitextSidenavNewCalc() {
     return this.intlService.localizeText("INFO_MENU_NOUVELLE_CALC");
   }
@@ -97,14 +126,68 @@ export class AppComponent implements OnInit, OnDestroy, Observer {
     return this.intlService.localizeText("INFO_MENU_LOAD_SESSION_TITLE");
   }
 
+  public get uitextSidenavSaveSession() {
+    return this.intlService.localizeText("INFO_MENU_SAVE_SESSION_TITLE");
+  }
+
+  public get uitextSidenavEmptySession() {
+    return this.intlService.localizeText("INFO_MENU_EMPTY_SESSION_TITLE");
+  }
+
   public get uitextSidenavHelp() {
     return this.intlService.localizeText("INFO_MENU_HELP_TITLE");
   }
 
+  public get uitextSelectCalc() {
+    return this.intlService.localizeText("INFO_MENU_SELECT_CALC");
+  }
+
+  public getCalculatorLabel(t: CalculatorType) {
+    return this.formulaireService.getLocalisedTitleFromCalculatorType(t);
+  }
+
   public get calculators() {
     return this._calculators;
   }
 
+  public get currentFormId() {
+    return this._currentFormId;
+  }
+
+  public setActiveCalc(uid: string) {
+    this._calculators.forEach((calc) => {
+      calc.active = (calc.uid === uid);
+    });
+    // mark current calc for navbar menu
+    const index = this.getCalculatorIndexFromId(uid);
+    this.currentCalc = this._calculators[index];
+  }
+
+  /**
+   * Returns true if sum of open calculator tabs witdh is lower than navbar
+   * available space (ie. if navbar is not overflowing), false otherwise
+   */
+  public get tabsFitInNavbar() {
+    // manual breakpoints
+    // @WARNING keep in sync with .calculator-buttons sizes in app.component.scss
+    let tabsLimit = 0;
+    if (this._innerWidth > 480) {
+      tabsLimit = 3;
+    }
+    if (this._innerWidth > 640) {
+      tabsLimit = 4;
+    }
+    if (this._innerWidth > 800) {
+      tabsLimit = 6;
+    }
+    /*if (this._innerWidth > 1200) {
+      tabsLimit = 8;
+    }*/
+
+    const fits = this._calculators.length <= tabsLimit;
+    return fits;
+  }
+
   /**
    * abonnement au service d'erreurs
    */
@@ -117,12 +200,10 @@ export class AppComponent implements OnInit, OnDestroy, Observer {
   }
 
   private updateLocale() {
-    const tag = this.intlService.currentLanguage.tag;
+    const tag = this.appSetupService.language;
     document["locale"] = tag;
+    this.intlService.setLocale(tag);
 
-    // location.reload(true);
-    // this.cdRef.markForCheck();
-    // this.cdRef.detectChanges();
     this.appRef.tick();
   }
 
@@ -141,11 +222,13 @@ export class AppComponent implements OnInit, OnDestroy, Observer {
     } else if (sender instanceof FormulaireService) {
       switch (data["action"]) {
         case "createForm":
+        // add newly created form to calculators list
           const f: FormulaireDefinition = data["form"];
           this._calculators.push(
             {
               "title": f.calculatorName,
-              "uid": String(f.uid)
+              "type": f.calculatorType,
+              "uid": f.uid
             }
           );
 
@@ -158,13 +241,7 @@ export class AppComponent implements OnInit, OnDestroy, Observer {
           break;
 
         case "currentFormChanged":
-          /*
-           utilisation de setTimeout() pour éviter le message console ExpressionChangedAfterItHasBeenCheckedError
-           relatif au getter getHighlightClass() (qui change de valeur quand le formulaire courant change)
-           */
-          setTimeout(() => {
-            this._currentFormId = data["formId"];
-          }, 1);
+          this._currentFormId = data["formId"];
           break;
 
         case "saveForm":
@@ -176,7 +253,7 @@ export class AppComponent implements OnInit, OnDestroy, Observer {
           this.closeCalculator(form);
           break;
       }
-    } else if (sender instanceof InternationalisationService) {
+    } else if (sender instanceof I18nService) {
       this.updateLocale();
     } else if (sender instanceof FormulaireDefinition) {
       switch (data["action"]) {
@@ -203,46 +280,10 @@ export class AppComponent implements OnInit, OnDestroy, Observer {
     this._calculators[formIndex]["title"] = title;
   }
 
-  /**
-   * sauvegarde du/des formulaires
-   * @param form formulaire à sélectionner par défaut dans la liste
-   */
-  private saveForm(form: FormulaireDefinition) {
-    // création du dialogue de sélection des formulaires à sauver
-    const compRef: ComponentRef<SaveCalculatorComponent> = this.appSaveCalcDialogAnchor.createDialog();
-
-    // création de la liste des formulaires
-    const list = [];
-    for (const c of this._calculators) {
-      const uid = c["uid"];
-      list.push({
-        "selected": uid === form.uid,
-        "title": c["title"],
-        "uid": uid
-      });
-    }
-
-    // passage de la liste, récupération d'une Promise pour traiter le résultat
-    const prom: Promise<any[]> = compRef.instance.run(list);
-    prom.then(innerList => {
-      let name = compRef.instance.filename;
-
-      // ajout extension ".json"
-      const re = /.+\.json/;
-      const match = re.exec(name.toLowerCase());
-      if (match === null) {
-        name = name + ".json";
-      }
-
-      this.saveSession(innerList, name);
-    }).catch(err => { });
-  }
-
   private saveSession(calcList: any[], filename) {
     const elems = [];
     for (const c of calcList) {
       if (c.selected) {
-        console.log(c.title);
         const form: FormulaireDefinition = this.formulaireService.getFormulaireFromId(c.uid);
         elems.push(form.JSONserialise());
       }
@@ -251,23 +292,25 @@ export class AppComponent implements OnInit, OnDestroy, Observer {
     this.formulaireService.saveSession(session, filename);
   }
 
+  /**
+   * Supprime un module de calcul **de l'interface**
+   * ATTENTION, ne supprime pas le module de calcul en mémoire !
+   * Pour cela, utiliser FormulaireService.requestCloseForm(form.uid);
+   * @param form module de calcul à fermer
+   */
   private closeCalculator(form: FormulaireDefinition) {
     const formId: string = form.uid;
 
     // désabonnement en tant qu'observateur
-
     form.removeObserver(this);
-
-    // recherche de la calculette correspondante à formId
-
+    // recherche du module de calcul correspondant à formId
     const closedIndex = this.getCalculatorIndexFromId(formId);
 
     /*
-     * détermination de la nouvelle calculette à afficher :
-     * - celle après celle supprimée
-     * - ou celle avant celle supprimée si on supprime la dernière
+     * détermination du nouveau module de calcul à afficher :
+     * - celui après celui supprimé
+     * - ou celui avant celui supprimé si on supprime le dernier
      */
-
     let newId = null;
     const l = this._calculators.length;
     if (l > 1) {
@@ -298,8 +341,9 @@ export class AppComponent implements OnInit, OnDestroy, Observer {
     this.router.navigate(["/list"]);
   }
 
-  private toCalc(id: number) {
+  private toCalc(id: string) {
     this.router.navigate(["/calculator", id]);
+    this.setActiveCalc(id);
   }
 
   /**
@@ -309,38 +353,35 @@ export class AppComponent implements OnInit, OnDestroy, Observer {
     this._routerCurrentComponent = a;
   }
 
-  private getHighlightClass(uid: number) {
-    return uid === this._currentFormId ? "blue darken-2" : "";
-  }
-
-  // flag d'affichage des repères des colonnes Bootstrap : uniquement en mode dev
-  // cf. src/environments/*, ng build --env=<mode> (par ex : ng build --env=prod)
-  public get ruler(): boolean {
-    // return !environment.production;
-    return false;
-  }
-
-  // sidenav
-
-  public openNav() {
-    document.getElementById("mySidenav").style.width = "300px";
-  }
-
-  public closeNav() {
-    document.getElementById("mySidenav").style.width = "0";
+  /**
+   * restarts a fresh session by closing all calculators
+   */
+  public emptySession() {
+    const dialogRef = this.confirmEmptySessionDialog.open(
+      DialogConfirmEmptySessionComponent,
+      { disableClose: true }
+    );
+    dialogRef.afterClosed().subscribe(result => {
+      if (result) {
+        for (const c of this._calculators) {
+          const form = this.formulaireService.getFormulaireFromId(c.uid);
+          this.formulaireService.requestCloseForm(form.uid);
+        }
+      }
+    });
   }
 
   public loadSession() {
-    this.closeNav();
-
     // création du dialogue de sélection des formulaires à sauver
-    const compRef: ComponentRef<LoadCalculatorComponent> = this.appLoadCalcDialogAnchor.createDialog();
-
-    const prom: Promise<any[]> = compRef.instance.run();
-    prom.then(list => {
-      this.formulaireService.loadSession(compRef.instance.selectedFile, compRef.instance.calculators);
-      compRef.destroy();
-    }).catch(err => { });
+    const dialogRef = this.loadSessionDialog.open(
+      DialogLoadSessionComponent,
+      { disableClose: true }
+    );
+    dialogRef.afterClosed().subscribe(result => {
+      if (result) {
+        this.formulaireService.loadSession(result.file, result.calculators);
+      }
+    });
   }
 
   public getDateRevision(): string[] {
@@ -348,6 +389,47 @@ export class AppComponent implements OnInit, OnDestroy, Observer {
     return dr;
   }
 
+  /**
+   * sauvegarde du/des formulaires
+   * @param form formulaire à sélectionner par défaut dans la liste
+   */
+  public saveForm(form?: FormulaireDefinition) {
+    // liste des formulaires
+    const list = [];
+    for (const c of this._calculators) {
+      const uid = c["uid"];
+      list.push({
+        "selected": form ? (uid === form.uid) : true,
+        "title": c["title"],
+        "uid": uid
+      });
+    }
+    // dialogue de sélection des formulaires à sauver
+    const dialogRef = this.saveSessionDialog.open(
+      DialogSaveSessionComponent,
+      {
+        data: {
+          calculators: list
+        },
+        disableClose: true
+      }
+    );
+    dialogRef.afterClosed().subscribe(result => {
+      if (result) {
+        let name = result.filename;
+
+        // ajout extension ".json"
+        const re = /.+\.json/;
+        const match = re.exec(name.toLowerCase());
+        if (match === null) {
+          name = name + ".json";
+        }
+
+        this.saveSession(result.calculators, name);
+      }
+    });
+  }
+
   /**
    * détection de la fermeture de la page/navigateur et demande de confirmation
    */
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 0e611c9086bcec30853fd7b5ca3d525ccef61250..48eade02f7b3d44b7c105bf62c7aa985ad984597 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -1,28 +1,53 @@
 import { BrowserModule } from "@angular/platform-browser";
 import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
 import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
-import { MDBBootstrapModule } from "angular-bootstrap-md";
+
+import {
+  MatButtonModule,
+  MatCheckboxModule,
+  MatIconModule,
+  MatSelectModule,
+  MatTabsModule,
+  MatSidenavModule,
+  MatToolbarModule,
+  MatMenuModule,
+  MatDialogModule,
+  MatFormFieldModule,
+  MatInputModule,
+  MatListModule,
+  MatCardModule,
+  MatTableModule,
+  ErrorStateMatcher,
+  MatButtonToggleModule
+} from "@angular/material";
+import { MaterialFileInputModule } from "ngx-material-file-input";
+
+import { FlexLayoutModule } from "@angular/flex-layout";
+import {
+  CustomBreakPointsProvider,
+  FlexGtXxsShowHideDirective,
+  FlexXxsShowHideDirective,
+  FlexLtXsShowHideDirective
+} from "./directives/flex-xxs.directive";
+
 import { HttpClientModule } from "@angular/common/http";
-import { FormsModule } from "@angular/forms"; // <-- NgModel lives here
+import { FormsModule, ReactiveFormsModule } from "@angular/forms"; // <-- NgModel lives here
 import { ChartModule } from "angular2-chartjs";
 import { RouterModule, Routes } from "@angular/router";
 import { NgxMdModule } from "ngx-md";
 
 import { FormulaireService } from "./services/formulaire/formulaire.service";
 import { ParamService } from "./services/param/param.service";
-import { InternationalisationService } from "./services/internationalisation/internationalisation.service";
+import { I18nService } from "./services/internationalisation/internationalisation.service";
 import { HttpService } from "./services/http/http.service";
 import { ApplicationSetupService } from "./services/app-setup/app-setup.service";
+
 import { AppComponent } from "./app.component";
 import { NgParamInputComponent } from "./components/ngparam-input/ngparam-input.component";
 import { FieldSetComponent } from "./components/field-set/field-set.component";
 import { FieldsetContainerComponent } from "./components/fieldset-container/fieldset-container.component";
 import { ParamFieldLineComponent } from "./components/param-field-line/param-field-line.component";
-import { NgParamMinComponent } from "./components/param-values/ngparam-min.component";
-import { NgParamMaxComponent } from "./components/param-values/ngparam-max.component";
-import { NgParamStepComponent } from "./components/param-values/ngparam-step.component";
 import { ParamValuesComponent } from "./components/param-values/param-values.component";
-import { ValueListComponent } from "./components/param-values/value-list.component";
 import { SelectFieldLineComponent } from "./components/select-field-line/select-field-line.component";
 import { CheckFieldLineComponent } from "./components/check-field-line/check-field-line.component";
 import { CalculatorResultsComponent } from "./components/calculator-results/calculator-results.component";
@@ -41,16 +66,21 @@ import { ApplicationSetupComponent } from "./components/app-setup/app-setup.comp
 import { BaseParamInputComponent } from "./components/base-param-input/base-param-input.component";
 import { FixedResultsComponent } from "./components/fixedvar-results/fixed-results.component";
 import { VarResultsComponent } from "./components/fixedvar-results/var-results.component";
-import { ResultElementBaseComponent } from "./components/result-element/result-element-base.component";
-import { HorizontalResultElementComponent } from "./components/result-element/horizontal-result-element.component";
-import { VerticalResultElementComponent } from "./components/result-element/vertical-result-element.component";
 import { LogEntryComponent } from "./components/log-entry/log-entry.component";
-import { LoadCalculatorComponent } from "./components/load-calculator/load-calculator.component";
-import { LoadCalcDialogAnchorDirective } from "./components/load-calculator/load-calculator-anchor.directive";
-import { SaveCalculatorComponent } from "./components/save-calculator/save-calculator.component";
-import { SaveCalcDialogAnchorDirective } from "./components/save-calculator/save-calculator-anchor.directive";
 import { ParamLinkComponent } from "./components/param-link/param-link.component";
 
+import { DialogConfirmEmptySessionComponent } from "./components/dialog-confirm-empty-session/dialog-confirm-empty-session.component";
+import { DialogConfirmCloseCalcComponent } from "./components/dialog-confirm-close-calc/dialog-confirm-close-calc.component";
+import { DialogEditParamComputedComponent } from "./components/dialog-edit-param-computed/dialog-edit-param-computed.component";
+import { DialogEditParamValuesComponent } from "./components/dialog-edit-param-values/dialog-edit-param-values.component";
+import { DialogLoadSessionComponent } from "./components/dialog-load-session/dialog-load-session.component";
+import { DialogSaveSessionComponent } from "./components/dialog-save-session/dialog-save-session.component";
+
+import { JalhydAsyncModelValidationDirective } from "./directives/jalhyd-async-model-validation.directive";
+import { JalhydModelValidationDirective } from "./directives/jalhyd-model-validation.directive";
+import { ImmediateErrorStateMatcher } from "./formulaire/immediate-error-state-matcher";
+import { ParamComputedComponent } from "./components/param-computed/param-computed.component";
+
 const appRoutes: Routes = [
   { path: "list", component: CalculatorListComponent },
   { path: "calculator/:uid", component: GenericCalculatorComponent },
@@ -60,51 +90,100 @@ const appRoutes: Routes = [
 
 @NgModule({
   imports: [
+    FormsModule, // <-- import the FormsModule before binding with [(ngModel)]
+    ReactiveFormsModule,
+    BrowserAnimationsModule,
+    BrowserModule,
+    ChartModule,
+    HttpClientModule,
+    FlexLayoutModule,
+    MatButtonModule,
+    MatButtonToggleModule,
+    MatCardModule,
+    MatCheckboxModule,
+    MatDialogModule,
+    MaterialFileInputModule,
+    MatFormFieldModule,
+    MatIconModule,
+    MatInputModule,
+    MatListModule,
+    MatMenuModule,
+    MatSelectModule,
+    MatSidenavModule,
+    MatTableModule,
+    MatTabsModule,
+    MatToolbarModule,
+    NgxMdModule.forRoot(),
     RouterModule.forRoot(
       appRoutes,
       {
         useHash: true, // prevents reloading whole app when typing url in browser's navigation bar
         enableTracing: false // debugging purposes only
       }
-    ),
-    BrowserModule,
-    NgxMdModule.forRoot(),
-    BrowserAnimationsModule,
-    MDBBootstrapModule.forRoot(),
-    FormsModule, // <-- import the FormsModule before binding with [(ngModel)]
-    HttpClientModule,
-    // MdInputModule,
-    // MdDialogModule,
-    ChartModule
+    )
   ],
   declarations: [ // composants, pipes et directives
     AppComponent,
-    NgParamInputComponent,
-    FieldSetComponent, FieldsetContainerComponent,
-    ParamFieldLineComponent, NgParamMinComponent, NgParamMaxComponent, NgParamStepComponent,
-    ParamValuesComponent, ValueListComponent,
-    SelectFieldLineComponent, CheckFieldLineComponent,
-    LogComponent, LogEntryComponent,
-    CalculatorListComponent,
     ApplicationSetupComponent,
     BaseParamInputComponent,
-    GenericCalculatorComponent, CalculatorNameComponent,
-    // AlertDialog,
-    CalculatorResultsComponent, FixedVarResultsComponent, SectionResultsComponent, RemousResultsComponent,
-    ResultsGraphComponent, GraphTypeSelectComponent,
-    CalcCanvasComponent, SectionCanvasComponent,
-    ResultElementBaseComponent, HorizontalResultElementComponent, VerticalResultElementComponent,
-    FixedResultsComponent, VarResultsComponent,
-    LoadCalculatorComponent, LoadCalcDialogAnchorDirective,
-    SaveCalculatorComponent, SaveCalcDialogAnchorDirective,
-    ParamLinkComponent
+    CalcCanvasComponent,
+    CalculatorListComponent,
+    CalculatorNameComponent,
+    CalculatorResultsComponent,
+    CheckFieldLineComponent,
+    DialogConfirmCloseCalcComponent,
+    DialogConfirmEmptySessionComponent,
+    DialogEditParamComputedComponent,
+    DialogEditParamValuesComponent,
+    DialogLoadSessionComponent,
+    DialogSaveSessionComponent,
+    FieldSetComponent,
+    FieldsetContainerComponent,
+    FixedResultsComponent,
+    FixedVarResultsComponent,
+    FlexGtXxsShowHideDirective,
+    FlexLtXsShowHideDirective,
+    FlexXxsShowHideDirective,
+    GenericCalculatorComponent,
+    GraphTypeSelectComponent,
+    JalhydAsyncModelValidationDirective,
+    JalhydModelValidationDirective,
+    LogComponent,
+    LogEntryComponent,
+    NgParamInputComponent,
+    ParamComputedComponent,
+    ParamFieldLineComponent,
+    ParamLinkComponent,
+    ParamValuesComponent,
+    RemousResultsComponent,
+    ResultsGraphComponent,
+    SectionCanvasComponent,
+    SectionResultsComponent,
+    SelectFieldLineComponent,
+    VarResultsComponent
+  ],
+  entryComponents: [
+    DialogConfirmCloseCalcComponent,
+    DialogConfirmEmptySessionComponent,
+    DialogEditParamComputedComponent,
+    DialogEditParamValuesComponent,
+    DialogSaveSessionComponent,
+    DialogLoadSessionComponent
   ],
-  // entryComponents: [AlertDialog],
-  entryComponents: [LoadCalculatorComponent, SaveCalculatorComponent],
   providers: [ // services
-    ParamService, InternationalisationService, HttpService, FormulaireService, ApplicationSetupService
+    ApplicationSetupService,
+    CustomBreakPointsProvider,
+    FormulaireService,
+    HttpService,
+    I18nService,
+    ParamService,
+    {
+      provide: ErrorStateMatcher,
+      useClass: ImmediateErrorStateMatcher
+    }
   ],
-  schemas: [NO_ERRORS_SCHEMA],
-  bootstrap: [AppComponent]
+  schemas: [ NO_ERRORS_SCHEMA ],
+  bootstrap: [ AppComponent ]
 })
+
 export class AppModule { }
diff --git a/src/app/components/alert-dialog/alert-dialog.component.ts b/src/app/components/alert-dialog/alert-dialog.component.ts
deleted file mode 100644
index 82681813a99572f5c7ec02ab8cf90690bed70bae..0000000000000000000000000000000000000000
--- a/src/app/components/alert-dialog/alert-dialog.component.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-// import { Component, Input } from '@angular/core';
-// import { MdDialog } from '@angular/material';
-
-// @Component({
-//     selector: 'dialog1',
-//     template: `
-//   <h2>{{_text}}</h2>
-//   `,
-//     providers: [MdDialog]
-// })
-// export class AlertDialog {
-//     /**
-//      * text input attribute
-//      */
-//     private _text: string;
-
-//     // constructor(private _dialog: MdDialog) { }
-//     // public get dialog() {
-//     //     return this._dialog;
-//     // }
-
-//     @Input()
-//     public set text(s: string) {
-//         this._text = s;
-//     }
-
-//     // public run(t: string) {
-//     //     let dialogRef = this._dialog.open(AlertDialog);
-//     //     //        let ad: AlertDialog = dialogRef.componentInstance;
-//     //     //      ad.text = "azeaze";
-//     //     this._text = t;
-//     // }
-// }
diff --git a/src/app/components/app-setup/app-setup.component.html b/src/app/components/app-setup/app-setup.component.html
index f87d18777f7abf6238b39a1069deb4caa0b96c51..bd04a62caf6f8cb32150ac8f2472d7f5848b24c3 100644
--- a/src/app/components/app-setup/app-setup.component.html
+++ b/src/app/components/app-setup/app-setup.component.html
@@ -1,48 +1,76 @@
-<div class="container-fluid">
-    <div class="row">
-        <div class="col-4 mx-auto">
-            <h1>{{ uitextTitle }}</h1>
-            <br/>
-        </div>
-    </div>
-
-    <!-- précision d'affichage -->
-    <div class="row">
-        <div class="col-4 mx-auto">
-            <base-param-input #displayAccuracy title="{{uitextDisplayAccuracy}}"></base-param-input>
-        </div>
-    </div>
-
-    <!-- précision de calcul -->
-    <div class="row">
-        <div class="col-4 mx-auto">
-            <base-param-input #computeAccuracy title="{{uitextComputeAccuracy}}"></base-param-input>
-        </div>
-    </div>
-
-    <!-- nombre d'itérations max Newton -->
-    <div class="row">
-        <div class="col-4 mx-auto">
-            <base-param-input #newtonMaxIter title="{{uitextNewtonMaxIteration}}"></base-param-input>
-        </div>
-    </div>
-
-    <!-- langue -->
-
-    <div class="row">
-        <div class="col-4 mx-auto">
-            <div class="btn-group" dropdown>
-                <button dropdownToggle mdbRippleRadius type="button" class="btn btn-primary dropdown-toggle">
-                    Language ({{ currentLanguageLabel }})
-                    <span class="caret"></span>
-                </button>
-                <ul *dropdownMenu class="dropdown-menu" role="menu">
-                    <li role="menuitem" *ngFor="let l of intlService.languages">
-                        <a class="dropdown-item" (click)="selectLang(l.code)">{{ l.label }}</a>
-                    </li>
-                </ul>
-            </div>
-        </div>
-    </div>
-
-</div>
\ No newline at end of file
+
+<div class="container" fxLayout="row" fxLayoutAlign="center space-evenly">
+  <mat-card id="app-setup">
+
+    <mat-card-header>
+      <mat-card-title>
+        <h1>{{ uitextTitle }}</h1>
+      </mat-card-title>
+    </mat-card-header>
+
+    <mat-card-content>
+      <!-- template-driven form -->
+      <form>
+
+        <!-- précision d'affichage -->
+        <mat-form-field data-testclass="numeric-input">
+            <input matInput [placeholder]="uitextDisplayAccuracy" #dp="ngModel" name="dp" inputmode="numeric"
+                [ngModel]="displayPrec.value" (ngModelChange)="!dp.invalid ? displayPrec.setValue($event): null"
+                pattern="^-?([0-9]*\.)?([0-9]+[Ee]-?)?[0-9]+$" required [appJalhydModelValidation]="displayPrec">
+
+            <mat-error *ngIf="dp.invalid && (dp.dirty || dp.touched)">
+                <div *ngIf="dp.errors.required || dp.errors.pattern">
+                    {{ uitextMustBeANumber }}
+                </div>
+                <div *ngIf="! dp.errors.required && dp.errors.jalhydModel">
+                    {{ dp.errors.jalhydModel.message }}
+                </div>
+            </mat-error>
+        </mat-form-field>
+
+        <!-- précision de calcul -->
+        <mat-form-field data-testclass="numeric-input">
+            <input matInput [placeholder]="uitextComputeAccuracy" #cp="ngModel" name="cp" inputmode="numeric"
+                [ngModel]="computePrec.value" (ngModelChange)="!cp.invalid ? computePrec.setValue($event): null"
+                pattern="^-?([0-9]*\.)?([0-9]+[Ee]-?)?[0-9]+$" required [appJalhydModelValidation]="computePrec">
+
+            <mat-error *ngIf="cp.invalid">
+                <div *ngIf="cp.errors.required || cp.errors.pattern">
+                    {{ uitextMustBeANumber }}
+                </div>
+                <div *ngIf="! cp.errors.required && cp.errors.jalhydModel">
+                    {{ cp.errors.jalhydModel.message }}
+                </div>
+            </mat-error>
+        </mat-form-field>
+
+        <!-- nombre d'itérations max Newton -->
+        <mat-form-field data-testclass="numeric-input">
+            <input matInput [placeholder]="uitextNewtonMaxIteration" #nmi="ngModel" name="nmi" inputmode="numeric"
+                [ngModel]="newtonMaxIter.value" (ngModelChange)="!nmi.invalid ? newtonMaxIter.setValue($event): null"
+                pattern="^-?([0-9]*\.)?([0-9]+[Ee]-?)?[0-9]+$" required [appJalhydModelValidation]="newtonMaxIter">
+
+            <mat-error *ngIf="nmi.invalid && (nmi.dirty || nmi.touched)">
+                <div *ngIf="nmi.errors.required || nmi.errors.pattern">
+                    {{ uitextMustBeANumber }}
+                </div>
+                <div *ngIf="! nmi.errors.required && nmi.errors.jalhydModel">
+                    {{ nmi.errors.jalhydModel.message }}
+                </div>
+            </mat-error>
+        </mat-form-field>
+
+        <!-- langue -->
+        <mat-form-field>
+            <mat-select placeholder="Language" [(value)]="currentLanguageCode" data-testid="language-select">
+                <mat-option *ngFor="let l of availableLanguages" [value]="l.code">
+                    {{ l.label }}
+                </mat-option>
+            </mat-select>
+        </mat-form-field>
+
+        </form>
+    </mat-card-content>
+
+  </mat-card>
+</div>
diff --git a/src/app/components/app-setup/app-setup.component.scss b/src/app/components/app-setup/app-setup.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..f21f04ee05569edae10aee054c629b74b625c87c
--- /dev/null
+++ b/src/app/components/app-setup/app-setup.component.scss
@@ -0,0 +1,50 @@
+#app-setup {
+    width: 360px;
+    padding: 3em;
+
+    mat-card-header {
+        margin-bottom: 2em;
+        min-height: 110px;
+
+        // @WARNING ::ng-deep est déprécié, mais y a rien d'autre pour
+        // l'instant (en attente de normalisation par le W3C)
+        ::ng-deep .mat-card-header-text {
+            margin: 0;
+        }
+    }
+
+    mat-form-field {
+        margin-top: 1.2em;
+        display: block;
+
+        ::ng-deep .mat-form-field-label {
+            font-size: 1.2em;
+            line-height: 1.4em;
+            margin-top: -2px;
+
+            &.mat-form-field-empty {
+                font-size: 1em;
+            }
+
+            .mat-form-field-required-marker {
+                display: none; // all fields are mandatory anyway
+            }
+        }
+    }
+
+    mat-select {
+
+        ::ng-deep .mat-select-value {
+            > span {
+                > span {
+                    line-height: 1.3em;
+                }
+            }
+        }
+    }
+
+    mat-error {
+        font-weight: 500;
+        font-size: 1.2em;
+    }
+}
diff --git a/src/app/components/app-setup/app-setup.component.ts b/src/app/components/app-setup/app-setup.component.ts
index 79973d1dd788b3c7b86a327ca0447a6274cb8493..fc05bd484e5a30caefb9863824b51d4046f4c3fb 100644
--- a/src/app/components/app-setup/app-setup.component.ts
+++ b/src/app/components/app-setup/app-setup.component.ts
@@ -1,54 +1,52 @@
-import { Component, ViewChild, OnInit } from "@angular/core";
+import { Component, OnInit } from "@angular/core";
 
 import { ParamDomainValue, Observer } from "jalhyd";
 
 import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
-import { InternationalisationService, LanguageCode } from "../../services/internationalisation/internationalisation.service";
-import { NgBaseParam, BaseParamInputComponent } from "../base-param-input/base-param-input.component";
+import { I18nService, LanguageCode } from "../../services/internationalisation/internationalisation.service";
+import { NgBaseParam } from "../base-param-input/base-param-input.component";
 import { BaseComponent } from "../base/base.component";
+import { ErrorStateMatcher } from "@angular/material";
+
 
 @Component({
     selector: "setup",
-    templateUrl: "./app-setup.component.html"
+    templateUrl: "./app-setup.component.html",
+    styleUrls: ["./app-setup.component.scss"]
 })
 export class ApplicationSetupComponent extends BaseComponent implements Observer, OnInit {
-    /**
-     * précision d'affichage
-     */
-    private _displayPrec: NgBaseParam;
-
-    /**
-     * précision de calcul
-     */
-    private _computePrec: NgBaseParam;
 
-    /**
-     * nombre d'iterations max Newton
-     */
-    private _newtonMaxIter: NgBaseParam;
+    /** précision d'affichage */
+    public displayPrec: NgBaseParam;
 
-    @ViewChild("displayAccuracy")
-    private _displayAccuracyComponent: BaseParamInputComponent;
+    /** précision de calcul */
+    public computePrec: NgBaseParam;
 
-    @ViewChild("computeAccuracy")
-    private _computeAccuracyComponent: BaseParamInputComponent;
+    /** nombre d'iterations max Newton */
+    public newtonMaxIter: NgBaseParam;
 
-    @ViewChild("newtonMaxIter")
-    private _newtonMaxIterComponent: BaseParamInputComponent;
+    public matcher: ErrorStateMatcher;
 
     constructor(
         private appSetupService: ApplicationSetupService,
-        private intlService: InternationalisationService
+        private intlService: I18nService
     ) {
         super();
+        this.appSetupService.addObserver(this);
+    }
+
+    public get availableLanguages() {
+        return this.intlService.languages;
     }
 
-    public get currentLanguageLabel(): string {
-        return this.intlService.currentLanguage.label;
+    public get currentLanguageCode() {
+        return this.intlService.currentLanguage.code;
     }
 
-    private selectLang(lc: LanguageCode) {
+    public set currentLanguageCode(lc: LanguageCode) {
         this.intlService.setLocale(lc);
+        // keep language in sync in app-wide parameters service
+        this.appSetupService.language = this.intlService.currentLanguage.tag;
     }
 
     public get uitextTitle(): string {
@@ -67,21 +65,22 @@ export class ApplicationSetupComponent extends BaseComponent implements Observer
         return this.intlService.localizeText("INFO_SETUP_NEWTON_MAX_ITER");
     }
 
+    public get uitextMustBeANumber(): string {
+        return this.intlService.localizeText("ERROR_PARAM_MUST_BE_A_NUMBER");
+    }
+
     private init() {
         // modèle du composant BaseParamInputComponent de précision d'affichage
-        this._displayPrec = new NgBaseParam("dp", ParamDomainValue.POS, this.appSetupService.displayPrecision);
-        this._displayPrec.addObserver(this);
-        this._displayAccuracyComponent.model = this._displayPrec;
+        this.displayPrec = new NgBaseParam("dp", ParamDomainValue.POS, this.appSetupService.displayPrecision);
+        this.displayPrec.addObserver(this);
 
         // modèle du composant BaseParamInputComponent de précision de calcul
-        this._computePrec = new NgBaseParam("cp", ParamDomainValue.POS, this.appSetupService.computePrecision);
-        this._computePrec.addObserver(this);
-        this._computeAccuracyComponent.model = this._computePrec;
+        this.computePrec = new NgBaseParam("cp", ParamDomainValue.POS, this.appSetupService.computePrecision);
+        this.computePrec.addObserver(this);
 
         // modèle du composant BaseParamInputComponent du max d'itérations pour Newton
-        this._newtonMaxIter = new NgBaseParam("nmi", ParamDomainValue.POS, this.appSetupService.newtonMaxIter);
-        this._newtonMaxIter.addObserver(this);
-        this._newtonMaxIterComponent.model = this._newtonMaxIter;
+        this.newtonMaxIter = new NgBaseParam("nmi", ParamDomainValue.POS, this.appSetupService.newtonMaxIterations);
+        this.newtonMaxIter.addObserver(this);
     }
 
     ngOnInit() {
@@ -90,19 +89,26 @@ export class ApplicationSetupComponent extends BaseComponent implements Observer
 
     // interface Observer
     public update(sender: any, data: any): void {
-        const p: NgBaseParam = sender;
-        switch (p.symbol) {
-            case "dp":
-                this.appSetupService.displayPrecision = +data;
-                break;
-
-            case "cp":
-                this.appSetupService.computePrecision = +data;
-                break;
-
-            case "nmi":
-                this.appSetupService.newtonMaxIter = +data;
-                break;
+        if (sender instanceof NgBaseParam) {
+            // someone typed a new value in the app-setup form
+            const p: NgBaseParam = sender;
+            switch (p.symbol) {
+                case "dp":
+                    this.appSetupService.displayPrecision = +data;
+                    break;
+
+                case "cp":
+                    this.appSetupService.computePrecision = +data;
+                    break;
+
+                case "nmi":
+                    this.appSetupService.newtonMaxIterations = +data;
+                    break;
+            }
+        }
+        if (sender instanceof ApplicationSetupService) {
+            // if app starts on /setup page, wait for config file to be (re)loaded by app-setup service
+            this.init();
         }
     }
 }
diff --git a/src/app/components/base-param-input/base-param-input.component.ts b/src/app/components/base-param-input/base-param-input.component.ts
index 2a3588fc0a1f882b17b3d5a4579568a1ddd579de..fb65a845cc5c4c2d59a99968641c67d9e9fb5486 100644
--- a/src/app/components/base-param-input/base-param-input.component.ts
+++ b/src/app/components/base-param-input/base-param-input.component.ts
@@ -4,8 +4,9 @@ import { Component, ChangeDetectorRef } from "@angular/core";
 
 import { Message, ParamDefinition, ParamDomain, ParamDomainValue, Observable, isNumeric } from "jalhyd";
 
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
 import { GenericInputComponent } from "../generic-input/generic-input.component";
+import { ServiceFactory } from "../../services/service-factory";
 
 export class NgBaseParam extends Observable {
     private _param: ParamDefinition;
@@ -35,6 +36,38 @@ export class NgBaseParam extends Observable {
         this._param.setValue(val);
         this.notifyObservers(val);
     }
+
+    public get value() {
+        return this.getValue();
+    }
+
+    public set value(val: number) {
+        this.setValue(val);
+    }
+
+    public validateModelValue(v: any): { isValid: boolean, message: string } {
+        let msg: string;
+        let valid = false;
+
+        if (v === null || v === "") {
+            // NULL values are always invalid
+            msg = ServiceFactory.instance.i18nService.localizeText("ERROR_PARAM_NULL");
+        } else {
+            try {
+                this._param.checkValue(v);
+                valid = true;
+            } catch (e) {
+                if (e instanceof Message) {
+                    // @TODO ici au début le service de localisation n'a pas encore chargé ses messages…
+                    msg = ServiceFactory.instance.i18nService.localizeMessage(e);
+                } else {
+                    msg = "invalid value";
+                }
+            }
+        }
+
+        return { isValid: valid, message: msg };
+    }
 }
 
 @Component({
@@ -42,7 +75,7 @@ export class NgBaseParam extends Observable {
     templateUrl: "../generic-input/generic-input.component.html",
 })
 export class BaseParamInputComponent extends GenericInputComponent {
-    constructor(private intlService: InternationalisationService, cdRef: ChangeDetectorRef) {
+    constructor(private intlService: I18nService, cdRef: ChangeDetectorRef) {
         super(cdRef);
     }
 
@@ -77,41 +110,6 @@ export class BaseParamInputComponent extends GenericInputComponent {
     }
 
     protected validateModelValue(v: any): { isValid: boolean, message: string } {
-        let msg;
-        let valid = false;
-
-        try {
-            this._paramDef.checkValue(v);
-            valid = true;
-        } catch (e) {
-            if (e instanceof Message) {
-                msg = this.intlService.localizeMessage(e);
-            } else {
-                msg = "invalid value";
-            }
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected modelToUI(v: any): string {
-        return String(v);
-    }
-
-    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
-        let valid = false;
-        let msg: string;
-
-        if (! isNumeric(ui)) {
-            msg = "Veuillez entrer une valeur numérique";
-        } else {
-            valid = true;
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected uiToModel(ui: string) {
-        return +ui;
+        return this._model.validateModelValue(v);
     }
 }
diff --git a/src/app/components/calculator-list/calculator-list.component.css b/src/app/components/calculator-list/calculator-list.component.css
deleted file mode 100644
index 61c95428bd01c0e553f853c4053569dbfaf01a98..0000000000000000000000000000000000000000
--- a/src/app/components/calculator-list/calculator-list.component.css
+++ /dev/null
@@ -1,12 +0,0 @@
-.full-page {
-    width: 100%;
-    height: 100%;
-    min-height: 100%;
-    background: red;
-    display: block;
-}
-
-.full-height { 
-    min-height: 100%;
-    height: 100%;
-}
diff --git a/src/app/components/calculator-list/calculator-list.component.html b/src/app/components/calculator-list/calculator-list.component.html
index 7f8497ad9e951393574bbfd5ca62125eb75b7bef..925ebed1196278810f8b7be0598805d63c2730b2 100644
--- a/src/app/components/calculator-list/calculator-list.component.html
+++ b/src/app/components/calculator-list/calculator-list.component.html
@@ -1,10 +1,33 @@
-<div class="container-fluid">
-    <div class="row">
-        <div class="offset-1 col-10 mx-auto">
-            <ul *ngFor="let l of items" class="list-group">
-                <!-- on utilise [innerHTML] pour que les codes HTML comme &nbsp; soient interprétés correctement -->
-                <button class="list-group-item" (click)="create(l.type)" [innerHTML]="l.label"></button>
-            </ul>
+<div class="container" fxLayout="row wrap" fxLayoutAlign="space-evenly stretch">
+
+    <mat-card *ngFor="let theme of items" class="compute-nodes-theme">
+
+        <mat-card-header>
+            <!-- <img mat-card-avatar src="https://s3.amazonaws.com/pix.iemoji.com/images/emoji/apple/ios-12/256/water-wave.png"> -->
+            <mat-card-title>{{ theme.title }}</mat-card-title>
+            <!-- <mat-card-subtitle>{{ theme.description }}</mat-card-subtitle> -->
+        </mat-card-header>
+
+        <div class="mat-card-image-overlay-container">
+            <img mat-card-image [src]="theme.image.path">
+            <div class="mat-card-image-overlay">
+                <div class="theme-image-credits">
+                    {{ theme.image.credits }}
+                </div>
+            </div>
         </div>
-    </div>
-</div>
\ No newline at end of file
+
+        <mat-card-content>
+            <p>{{ theme.description }}</p>
+        </mat-card-content>
+
+        <mat-card-actions>
+            <div class="container" fxLayout="column" fxLayoutAlign="left" fxLayoutGap="10px">
+                <button mat-raised-button color="accent" *ngFor="let calc of theme.calculators" class="theme-calculator"
+                    (click)="create(calc.type)" [innerHTML]="calc.label"></button>
+            </div>
+        </mat-card-actions>
+    
+    </mat-card>
+
+</div>
diff --git a/src/app/components/calculator-list/calculator-list.component.scss b/src/app/components/calculator-list/calculator-list.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..460061c0f0be33188214acbe517642f587a8193b
--- /dev/null
+++ b/src/app/components/calculator-list/calculator-list.component.scss
@@ -0,0 +1,49 @@
+mat-card.compute-nodes-theme {
+    width: 300px;
+    margin: 1em;
+
+    button.theme-calculator {
+        margin-left: 0;
+        margin-right: 0;
+    }
+
+    .mat-card-actions:last-child {
+        margin-bottom: 0; // instead of -8px
+    }
+
+    mat-card-header {
+        min-height: 80px;
+    }
+
+    .mat-card-image-overlay-container {
+        position:relative;
+
+        .mat-card-image {
+            border-radius: 5px;
+        }
+
+        .mat-card-image-overlay {
+            position: absolute;
+            top: -16px;
+            height: 100%;
+            opacity: 0;
+            background-color: white;
+            // copied from mat-card-image :
+            width: calc(100% + 32px);
+            margin: 0 -16px 0 -16px;
+
+            &:hover {
+                opacity: 0.7;
+            }
+        }
+
+        .theme-image-credits {
+            position: absolute;
+            bottom: 8px;
+            right: 14px;
+            font-size: 0.9em;
+            font-weight: bold;
+            color: #444;
+        }
+    }
+}
diff --git a/src/app/components/calculator-list/calculator-list.component.ts b/src/app/components/calculator-list/calculator-list.component.ts
index 910568f99c394980c70bc56bcafc3ebfb981664c..aef92e3e136016e8bc40b94cc47ae1f8ee66c474 100644
--- a/src/app/components/calculator-list/calculator-list.component.ts
+++ b/src/app/components/calculator-list/calculator-list.component.ts
@@ -5,38 +5,85 @@ import { CalculatorType, EnumEx } from "jalhyd";
 
 import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
 import { ServiceFactory } from "../../services/service-factory";
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
 import { FormulaireParallelStructure } from "../../formulaire/definition/concrete/form-parallel-structures";
 import { FieldsetContainer } from "../../formulaire/fieldset-container";
 
-class ListElement {
-    private _label: string;
-
-    constructor(private _type: CalculatorType) {
-        this._label = ServiceFactory.instance.formulaireService.getLocalisedTitleFromCalculatorType(_type);
-    }
-
-    public get label() { return this._label; }
-    public get type() { return this._type; }
-}
 
 @Component({
     selector: "list",
     templateUrl: "./calculator-list.component.html",
-    styleUrls: ["./calculator-list.component.css"]
+    styleUrls: ["./calculator-list.component.scss"]
 })
 export class CalculatorListComponent implements OnInit {
-    private _items: ListElement[];
+    private _items: any[];
 
     constructor(private router: Router) {
-        ServiceFactory.instance.internationalisationService.addObserver(this);
+        ServiceFactory.instance.i18nService.addObserver(this);
+        ServiceFactory.instance.applicationSetupService.addObserver(this);
     }
 
-    private updateLocale() {
+    /** triggered on init */
+    private loadCalculatorsThemes() {
         this._items = [];
-        for (const t of EnumEx.getValues(CalculatorType)) {
-            if (t !== CalculatorType.Structure) {
-                this._items.push(new ListElement(t));
+        const unusedCalculators = EnumEx.getValues(CalculatorType);
+        const themes = ServiceFactory.instance.applicationSetupService.themes;
+        if (themes) {
+            // group by themes
+            for (const theme of themes) {
+                if (theme.name) {
+                    // get theme details from config
+                    const themeTitleKey = "INFO_THEME_" + theme.name + "_TITRE";
+                    const themeDescriptionKey = "INFO_THEME_" + theme.name + "_DESCRIPTION";
+                    const credits = ServiceFactory.instance.i18nService.localizeText("INFO_THEME_CREDITS");
+                    const item = {
+                        title: ServiceFactory.instance.i18nService.localizeText(themeTitleKey),
+                        description: ServiceFactory.instance.i18nService.localizeText(themeDescriptionKey),
+                        image: {
+                            path: "assets/images/themes/" + theme.image.path,
+                            title: theme.image.title,
+                            credits: credits + " : " + theme.image.credits
+                        },
+                        calculators: []
+                    };
+                    // get calculators for this theme
+                    for (const calcType of theme.calculators) {
+                        item.calculators.push({
+                            type: calcType,
+                            label: ServiceFactory.instance.formulaireService.getLocalisedTitleFromCalculatorType(calcType)
+                        });
+                        // mark as used
+                        const index = unusedCalculators.indexOf(calcType);
+                        if (index > -1) {
+                            unusedCalculators.splice(index, 1);
+                        }
+                    }
+                    this._items.push(item);
+                }
+                // else special theme for unused calculators
+            }
+            // extra card for unused calculators
+            if (unusedCalculators.length > 0) {
+                const unusedThemeConfig = themes.find(i => i.name === undefined);
+                const unusedTheme: any = {};
+                unusedTheme.calculators = [];
+                unusedTheme.title = ServiceFactory.instance.i18nService.localizeText("INFO_THEME_MODULES_INUTILISES_TITRE");
+                unusedTheme.description = ServiceFactory.instance.i18nService.localizeText("INFO_THEME_MODULES_INUTILISES_DESCRIPTION");
+                unusedTheme.image = {
+                    path: "assets/images/themes/" + unusedThemeConfig.image.path
+                };
+
+                for (const t of unusedCalculators) {
+                    if (t !== CalculatorType.Structure) {
+                        unusedTheme.calculators.push({
+                            type: t,
+                            label: ServiceFactory.instance.formulaireService.getLocalisedTitleFromCalculatorType(t)
+                        });
+                    }
+                }
+                if (unusedTheme.calculators.length > 0) {
+                    this._items.push(unusedTheme);
+                } // else the only remaining calculator was "Structure", the one we don't want
             }
         }
     }
@@ -47,7 +94,7 @@ export class CalculatorListComponent implements OnInit {
             this.router.navigate(["/calculator", f.uid]);
             return f;
         }).then(f => {
-            // on ajoute un ouvrage après l'ouverture de la calculette "ouvrages parallèles"
+            // on ajoute un ouvrage après l'ouverture du module de calcul "ouvrages parallèles"
             if (f instanceof FormulaireParallelStructure) {
                 for (const e of f.allFormElements) {
                     if (e instanceof FieldsetContainer) {
@@ -66,14 +113,12 @@ export class CalculatorListComponent implements OnInit {
     // interface Observer
 
     update(sender: any, data: any): void {
-        if (sender instanceof InternationalisationService) {
-            this.updateLocale();
+        if (sender instanceof I18nService) {
+            this.loadCalculatorsThemes();
         }
     }
 
-    // OnInit
-
     ngOnInit() {
-        this.updateLocale();
+        this.loadCalculatorsThemes();
     }
 }
diff --git a/src/app/components/calculator-results/calculator-results.component.html b/src/app/components/calculator-results/calculator-results.component.html
index 21cb31f8767953bdf74bac32b4b339d931d097f0..1cf1ee86e9e06e53a464453a59f86c8d0ae54081 100644
--- a/src/app/components/calculator-results/calculator-results.component.html
+++ b/src/app/components/calculator-results/calculator-results.component.html
@@ -1,5 +1,5 @@
 <div class="container-fluid">
-    <fixedvar-results> </fixedvar-results>
+    <fixedvar-results></fixedvar-results>
     <section-results></section-results>
     <remous-results></remous-results>
 </div>
diff --git a/src/app/components/dialog-confirm-close-calc/dialog-confirm-close-calc.component.html b/src/app/components/dialog-confirm-close-calc/dialog-confirm-close-calc.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..8018436a73c3111ceb3ee4e5888067a83e049ac4
--- /dev/null
+++ b/src/app/components/dialog-confirm-close-calc/dialog-confirm-close-calc.component.html
@@ -0,0 +1,12 @@
+<h1 mat-dialog-title [innerHTML]="uitextCloseCalcTitle"></h1>
+<div mat-dialog-content>
+  <p [innerHTML]="uitextCloseCalcBody"></p>
+</div>
+<div mat-dialog-actions>
+  <button mat-raised-button color="primary" [mat-dialog-close]="false" cdkFocusInitial>
+    {{ uitextNo }}
+  </button>
+  <button mat-raised-button color="warn" [mat-dialog-close]="true">
+    {{ uitextYes }}
+  </button>
+</div>
diff --git a/src/app/components/dialog-confirm-close-calc/dialog-confirm-close-calc.component.ts b/src/app/components/dialog-confirm-close-calc/dialog-confirm-close-calc.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e8a1f44ca145c27fef780d49aeaead3e49706aa1
--- /dev/null
+++ b/src/app/components/dialog-confirm-close-calc/dialog-confirm-close-calc.component.ts
@@ -0,0 +1,33 @@
+import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material";
+import { Inject, Component } from "@angular/core";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
+
+@Component({
+    selector: "dialog-confirm-close-calc",
+    templateUrl: "dialog-confirm-close-calc.component.html",
+})
+export class DialogConfirmCloseCalcComponent {
+
+    constructor(
+        public dialogRef: MatDialogRef<DialogConfirmCloseCalcComponent>,
+        private intlService: I18nService,
+        @Inject(MAT_DIALOG_DATA) public data: any
+    ) { }
+
+    public get uitextYes() {
+      return this.intlService.localizeText("INFO_OPTION_YES");
+    }
+
+    public get uitextNo() {
+      return this.intlService.localizeText("INFO_OPTION_NO");
+    }
+
+    public get uitextCloseCalcTitle() {
+      return this.intlService.localizeText("INFO_CLOSE_DIALOGUE_TITRE");
+    }
+
+    public get uitextCloseCalcBody() {
+      return this.intlService.localizeText("INFO_CLOSE_DIALOGUE_TEXT");
+    }
+
+}
diff --git a/src/app/components/dialog-confirm-empty-session/dialog-confirm-empty-session.component.html b/src/app/components/dialog-confirm-empty-session/dialog-confirm-empty-session.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..699ae97867ba412c354569bf1aab09429deffc7d
--- /dev/null
+++ b/src/app/components/dialog-confirm-empty-session/dialog-confirm-empty-session.component.html
@@ -0,0 +1,12 @@
+<h1 mat-dialog-title [innerHTML]="uitextEmptySessionTitle"></h1>
+<div mat-dialog-content>
+  <p [innerHTML]="uitextEmptySessionBody"></p>
+</div>
+<div mat-dialog-actions>
+  <button mat-raised-button color="primary" [mat-dialog-close]="false" cdkFocusInitial>
+    {{ uitextNo }}
+  </button>
+  <button mat-raised-button color="warn" [mat-dialog-close]="true">
+    {{ uitextYes }}
+  </button>
+</div>
diff --git a/src/app/components/dialog-confirm-empty-session/dialog-confirm-empty-session.component.ts b/src/app/components/dialog-confirm-empty-session/dialog-confirm-empty-session.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f2334de93b12bac03b00f68bac7d303a5d6914d1
--- /dev/null
+++ b/src/app/components/dialog-confirm-empty-session/dialog-confirm-empty-session.component.ts
@@ -0,0 +1,33 @@
+import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material";
+import { Inject, Component } from "@angular/core";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
+
+@Component({
+    selector: "dialog-confirm-empty-session",
+    templateUrl: "dialog-confirm-empty-session.component.html",
+})
+export class DialogConfirmEmptySessionComponent {
+
+    constructor(
+        public dialogRef: MatDialogRef<DialogConfirmEmptySessionComponent>,
+        private intlService: I18nService,
+        @Inject(MAT_DIALOG_DATA) public data: any
+    ) { }
+
+    public get uitextYes() {
+      return this.intlService.localizeText("INFO_OPTION_YES");
+    }
+
+    public get uitextNo() {
+      return this.intlService.localizeText("INFO_OPTION_NO");
+    }
+
+    public get uitextEmptySessionTitle() {
+      return this.intlService.localizeText("INFO_EMPTY_SESSION_DIALOGUE_TITRE");
+    }
+
+    public get uitextEmptySessionBody() {
+      return this.intlService.localizeText("INFO_EMPTY_SESSION_DIALOGUE_TEXT");
+    }
+
+}
diff --git a/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.html b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..9efaf288795c96ec273dc07655f1f13889cfad2a
--- /dev/null
+++ b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.html
@@ -0,0 +1,16 @@
+<h1 mat-dialog-title [innerHTML]="uitextEditParamComputedInitialValue"></h1>
+
+<form>
+
+  <div mat-dialog-content>
+    <ngparam-input [title]="param.title" ></ngparam-input>
+    <!-- (change)="onInputChange($event)" -->
+  </div>
+
+  <div mat-dialog-actions>
+    <button mat-raised-button [mat-dialog-close]="true" cdkFocusInitial>
+      {{ uitextClose }}
+    </button>
+  </div>
+
+</form>
diff --git a/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.scss b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..bc76a27e25280cc38af4ab2f9eb032683c35e758
--- /dev/null
+++ b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.scss
@@ -0,0 +1,3 @@
+mat-form-field {
+    width: 100%;
+}
diff --git a/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.ts b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5321f476f84fec449f58c0fb597e4e74f4f31b92
--- /dev/null
+++ b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.ts
@@ -0,0 +1,39 @@
+import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material";
+import { Inject, Component, ViewChild, OnInit } from "@angular/core";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
+import { NgParameter } from "../../formulaire/ngparam";
+import { NgParamInputComponent } from "../ngparam-input/ngparam-input.component";
+
+@Component({
+    selector: "dialog-edit-param-computed",
+    templateUrl: "dialog-edit-param-computed.component.html",
+    styleUrls: ["dialog-edit-param-computed.component.scss"]
+})
+export class DialogEditParamComputedComponent implements OnInit {
+
+    /** the related parameter to change the "fixed" value of */
+    public param: NgParameter;
+
+    @ViewChild(NgParamInputComponent)
+    private _ngParamInputComponent: NgParamInputComponent;
+
+    constructor(
+      public dialogRef: MatDialogRef<DialogEditParamComputedComponent>,
+      private intlService: I18nService,
+      @Inject(MAT_DIALOG_DATA) public data: any
+    ) {
+      this.param = data.param;
+    }
+
+    public get uitextClose() {
+      return this.intlService.localizeText("INFO_OPTION_CLOSE");
+    }
+
+    public get uitextEditParamComputedInitialValue() {
+      return this.intlService.localizeText("INFO_DIALOG_COMPUTED_VALUE_TITLE");
+    }
+
+    public ngOnInit() {
+      this._ngParamInputComponent.model = this.param;
+    }
+}
diff --git a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..53718ce0f5d69c711f5d0e0e2af8412932d85482
--- /dev/null
+++ b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html
@@ -0,0 +1,95 @@
+<h1 mat-dialog-title [innerHTML]="uitextEditParamVariableValues"></h1>
+
+  <div mat-dialog-content>
+
+    <mat-form-field>
+      <mat-select [placeholder]="uiTextModeSelection" [(value)]="selectedValueMode" (selectionChange)="onValueModeChange($event)"
+        data-testid="variable-value-mode-select">
+          <mat-option *ngFor="let e of valueModes" [value]="e.value">
+              {{ e.label }}
+          </mat-option>
+      </mat-select>
+    </mat-form-field>
+
+    <div *ngIf="isMinMax" class="min-max-step-container">
+        <form>
+            <mat-form-field>
+                <input matInput class="form-control" type="number" inputmode="numeric" name="min-value" step="0.01"
+                    [placeholder]="uitextValeurMini" [(ngModel)]="param.minValue" required pattern="^-?([0-9]*\.)?([0-9]+[Ee]-?)?[0-9]+$">
+
+                <mat-error>{{ uitextMustBeANumber }}</mat-error>
+            </mat-form-field>
+
+            <mat-form-field>
+                <input matInput class="form-control" type="number" inputmode="numeric" name="max-value" step="0.01"
+                    [placeholder]="uitextValeurMaxi" [(ngModel)]="param.maxValue" required pattern="^-?([0-9]*\.)?([0-9]+[Ee]-?)?[0-9]+$">
+
+                <mat-error>{{ uitextMustBeANumber }}</mat-error>
+            </mat-form-field>
+
+            <mat-form-field>
+                <input matInput class="form-control" type="number" inputmode="numeric" name="step-value" step="0.01"
+                    [placeholder]="uitextPasVariation" [(ngModel)]="param.stepValue" required pattern="^-?([0-9]*\.)?([0-9]+[Ee]-?)?[0-9]+$">
+
+                <mat-error>{{ uitextMustBeANumber }}</mat-error>
+            </mat-form-field>
+        </form>
+    </div>
+
+    <div *ngIf="isListe">
+        <form [formGroup]="valuesListForm">
+            <mat-form-field>
+                <textarea matInput matTextareaAutosize [placeholder]="uitextListeValeurs" formControlName="valuesList"
+                    [value]="valuesList"></textarea>
+                    <!-- (input)="valuesList = $event.target.value" -->
+                <mat-error>
+                    <span *ngIf="valuesListForm.controls.valuesList.hasError('model')">
+                        {{ valuesListForm.controls.valuesList.errors.model }}
+                    </span>
+                    <span *ngIf="! valuesListForm.controls.valuesList.hasError('model')">
+                        {{ uitextMustBeListOfNumbers }}
+                    </span>
+                </mat-error>
+            </mat-form-field>
+
+            <div class="decimal-separator-and-file-container" fxLayout="row wrap" fxLayoutAlign="space-between start">
+                <mat-form-field class="decimal-separator" fxFlex.gt-xs="1 0 auto" fxFlex.lt-sm="1 0 100%">
+                    <mat-select [placeholder]="uitextDecimalSeparator" [(value)]="decimalSeparator"
+                        data-testid="decimal-separator-select">
+                        <mat-option *ngFor="let e of decimalSeparators" [value]="e.value">
+                            {{ e.label }}
+                        </mat-option>
+                    </mat-select>
+                </mat-form-field>
+
+                <div fxHide.xs fxFlex.gt-xs="0 0 16px"></div>
+
+                <mat-form-field class="values-file" fxFlex.gt-xs="1 0 auto" fxFlex.lt-sm="1 0 100%">
+                    <ngx-mat-file-input #valuesFile [placeholder]="uitextImportFile"
+                        (change)="onFileSelected($event)" formControlName="file">
+                    </ngx-mat-file-input>
+                    <button mat-icon-button matSuffix *ngIf="!valuesFile.empty" (click)="valuesFile.clear($event)">
+                        <mat-icon>clear</mat-icon>
+                    </button>
+                </mat-form-field>
+            </div>
+        </form>
+    </div>
+
+  </div>
+
+  <div mat-dialog-actions>
+    <div *ngIf="isMinMax">
+        <button mat-raised-button [mat-dialog-close]="true" cdkFocusInitial>
+            {{ uitextClose }}
+        </button>
+    </div>
+    <div *ngIf="isListe">
+        <button mat-raised-button color="primary" [mat-dialog-close]="true" cdkFocusInitial>
+            {{ uitextCancel }}
+        </button>
+        <button mat-raised-button color="warn" (click)="onValidate()">
+            {{ uitextValidate }}
+        </button>
+    </div>
+  </div>
diff --git a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.scss b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..c8602bba0dc8bf4b943720441741f41cb94d6183
--- /dev/null
+++ b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.scss
@@ -0,0 +1,21 @@
+mat-form-field {
+    display: block;
+    margin-top: 0.5em;
+
+    textarea {
+        font-size: .8em;
+        max-height: 100px;
+    }
+}
+
+.min-max-step-container {
+    margin-top: -8px;
+}
+
+.mat-dialog-content {
+    overflow: hidden;
+}
+
+.decimal-separator-and-file-container {
+    margin-top: 1em;
+}
diff --git a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..651702a5d2ecf95e1eb03d448383234fd224565b
--- /dev/null
+++ b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts
@@ -0,0 +1,352 @@
+import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material";
+import { Inject, Component, OnInit } from "@angular/core";
+import { FormBuilder, FormGroup, Validators } from "@angular/forms";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
+import { NgParameter } from "../../formulaire/ngparam";
+import { ParamValueMode } from "jalhyd";
+import { sprintf } from "sprintf-js";
+
+@Component({
+    selector: "dialog-edit-param-values",
+    templateUrl: "dialog-edit-param-values.component.html",
+    styleUrls: ["dialog-edit-param-values.component.scss"]
+})
+export class DialogEditParamValuesComponent implements OnInit {
+
+    /** the related parameter to change the "variable" value of */
+    public param: NgParameter;
+
+    /** available value modes (min / max, list) */
+    public valueModes: { value: ParamValueMode; label: string; }[];
+
+    /** available decimal separators */
+    public decimalSeparators: { label: string; value: string; }[];
+
+    /** current decimal separator */
+    public decimalSeparator: string;
+
+    public valuesListForm: FormGroup;
+
+    constructor(
+        public dialogRef: MatDialogRef<DialogEditParamValuesComponent>,
+        private intlService: I18nService,
+        private fb: FormBuilder,
+        @Inject(MAT_DIALOG_DATA) public data: any
+    ) {
+        this.param = data.param;
+
+        // an explicit ReactiveForm is required for file input component
+        this.valuesListForm = this.fb.group({
+            file: [""],
+            valuesList: ["", [ // not initialized with valuesList because param mode is MIN/MAX at this time
+                Validators.required
+                // Validators.pattern(new RegExp(this.valuesListPattern)) // behaves weirdly
+            ]]
+        });
+
+        // available options for select controls
+        this.valueModes = [
+            {
+                value: ParamValueMode.MINMAX,
+                label: this.intlService.localizeText("INFO_PARAMMODE_MINMAX")
+            },
+            {
+                value: ParamValueMode.LISTE,
+                label: this.intlService.localizeText("INFO_PARAMMODE_LIST")
+            }
+        ];
+        this.decimalSeparators = [
+            {
+                label: this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_SEPARATEUR_POINT"),
+                value: "."
+            },
+            {
+                label: this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_SEPARATEUR_VIRGULE"),
+                value: ","
+            }
+        ];
+        this.decimalSeparator = this.decimalSeparators[0].value;
+    }
+
+    /**
+     * regular expression pattern for values list validation (depends on decimal separator)
+     */
+    public get valuesListPattern() {
+        // standard pattern for decimal separator "." : ^-?([0-9]*\.)?([0-9]+[Ee]-?)?[0-9]+$
+        const escapedDecimalSeparator = (this.decimalSeparator === "." ? "\\." : this.decimalSeparator);
+        const numberSubPattern = `-?([0-9]*${escapedDecimalSeparator})?([0-9]+[Ee]-?)?[0-9]+`;
+        const re = `^${numberSubPattern}(${this.separatorPattern}${numberSubPattern})*$`;
+        return re;
+    }
+
+    /**
+     * accepted separator: everything but [numbers, E, +, -, decimal separator], any length
+     */
+    public get separatorPattern() {
+        return "[^0-9-+Ee" + this.decimalSeparator  + "]+";
+    }
+
+    public get selectedValueMode() {
+        return this.param.valueMode;
+    }
+
+    public set selectedValueMode(v) {
+        this.param.valueMode = v;
+    }
+
+    public get isMinMax() {
+        return this.param.valueMode === ParamValueMode.MINMAX;
+    }
+
+    public get isListe() {
+        return this.param.valueMode === ParamValueMode.LISTE;
+    }
+
+    /**
+     * renders model's numbers list as text values list (semicolon separated)
+     */
+    public get valuesList() {
+        return (this.param.valueList || []).join(";");
+    }
+
+    /**
+     * injects text values list into model's numbers list
+     */
+    public set valuesList(list: string) {
+        const vals = [];
+        const separatorRE = new RegExp(this.separatorPattern);
+        const parts = list.trim().split(separatorRE);
+        parts.forEach((e) => {
+            if (e.length > 0) {
+                // ensure decimal separator is "." for Number()
+                if (this.decimalSeparator !== ".") {
+                    const re = new RegExp(this.decimalSeparator, "g"); // @TODO remove "g" ?
+                    e = e.replace(re, ".");
+                }
+                vals.push(Number(e));
+            }
+        });
+        this.param.valueList = vals;
+    }
+
+    public onValidate() {
+        const status = this.validateValuesListString(this.valuesListForm.controls.valuesList.value);
+
+        if (status.ok) {
+            this.valuesListForm.controls.valuesList.setErrors(null);
+            this.valuesList = this.valuesListForm.controls.valuesList.value;
+            this.dialogRef.close();
+        } else {
+            this.valuesListForm.controls.valuesList.setErrors({ "model": status.message });
+        }
+    }
+
+    /**
+     * Returns { ok: true } if every element of list is a valid Number, { ok: false, message: "reason" } otherwise
+     * @param list a string containing a list of numbers separated by this.separatorPattern
+     */
+    private validateValuesListString(list: string) {
+        let message: string;
+        // 1. validate against general pattern
+        let ok = new RegExp(this.valuesListPattern).test(list);
+
+        if (ok) {
+            // 2. validate each value
+            const separatorRE = new RegExp(this.separatorPattern);
+            const parts = list.trim().split(separatorRE);
+            for (let i = 0; i < parts.length && ok; i++) {
+                let e = parts[i];
+                if (e.length > 0) { // should always be true as separator might be several characters long
+                    // ensure decimal separator is "." for Number()
+                    if (this.decimalSeparator !== ".") {
+                        const re = new RegExp(this.decimalSeparator, "g"); // @TODO remove "g" ?
+                        e = e.replace(re, ".");
+                    }
+                    // 2.1 check it is a valid Number
+                    const n = (Number(e));
+                    // 2.2 validate against model
+                    let modelIsHappy = true;
+                    try {
+                        this.param.checkValue(n);
+                    } catch (e) {
+                        modelIsHappy = false;
+                        message = sprintf(this.intlService.localizeText("ERROR_INVALID_AT_POSITION"), i + 1)
+                            + " " + this.intlService.localizeMessage(e);
+                    }
+                    // synthesis
+                    ok = (
+                        ok
+                        && !isNaN(n)
+                        && isFinite(n)
+                        && modelIsHappy
+                    );
+                }
+            }
+        } else {
+            message = this.uitextMustBeListOfNumbers;
+        }
+
+        return { ok, message };
+    }
+
+    public onFileSelected(event: any) {
+        if (event.target.files && event.target.files.length) {
+            const fr = new FileReader();
+            fr.onload = () => {
+                this.valuesListForm.controls.valuesList.setErrors(null);
+                // this.valuesList = String(fr.result);
+                this.valuesListForm.controls.valuesList.setValue(String(fr.result));
+            };
+            fr.onerror = () => {
+                fr.abort();
+                throw new Error("Erreur de lecture du fichier");
+            };
+            fr.readAsText(event.target.files[0]);
+        }
+    }
+
+    public onValueModeChange(event) {
+        this.initVariableValues();
+    }
+
+    private initVariableValues() {
+        // init min / max / step
+        if (this.isMinMax) {
+            if (this.param.minValue === undefined) {
+                this.param.minValue = this.param.getValue() / 2;
+            }
+            if (this.param.maxValue === undefined) {
+                this.param.maxValue = this.param.getValue() * 2;
+            }
+            let step = this.param.stepValue;
+            if (step === undefined) {
+                step = (this.param.maxValue - this.param.minValue) / 20;
+            }
+            this.param.stepValue = step;
+        }
+        // init values list
+        if (this.isListe) {
+            if (this.param.valueList === undefined) {
+                if (this.param.isDefined) {
+                    this.param.valueList = [ this.param.getValue() ];
+                } else {
+                    this.param.valueList = [];
+                }
+                // set form control initial value
+                this.valuesListForm.controls.valuesList.setValue(this.valuesList);
+            }
+        }
+    }
+
+    public get uiTextModeSelection() {
+        return this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_MODE");
+    }
+
+    public get uitextValeurMini() {
+        return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMINI");
+    }
+
+    public get uitextValeurMaxi() {
+        return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMAXI");
+    }
+
+    public get uitextPasVariation() {
+        return this.intlService.localizeText("INFO_PARAMFIELD_PASVARIATION");
+    }
+
+    public get uitextClose() {
+        return this.intlService.localizeText("INFO_OPTION_CLOSE");
+    }
+
+    public get uitextCancel() {
+        return this.intlService.localizeText("INFO_OPTION_CANCEL");
+    }
+
+    public get uitextValidate() {
+        return this.intlService.localizeText("INFO_OPTION_VALIDATE");
+    }
+
+    public get uitextEditParamVariableValues() {
+        return this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_TITLE");
+    }
+
+    public get uitextListeValeurs() {
+        return this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES_FORMAT");
+    }
+
+    public get uitextMustBeANumber(): string {
+        return this.intlService.localizeText("ERROR_PARAM_MUST_BE_A_NUMBER");
+    }
+
+    public get uitextMustBeListOfNumbers() {
+        return sprintf(this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES_FORMAT_ERROR"), this.separatorPattern);
+    }
+
+    public get uitextDecimalSeparator() {
+        return this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_SEPARATEUR_DECIMAL");
+    }
+
+    public get uitextImportFile() {
+        return this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_IMPORT_FICHIER");
+    }
+
+    public ngOnInit() {
+        this.initVariableValues();
+    }
+
+  /*   protected validateModelValue(v: any): { isValid: boolean, message: string } {
+      let msg: string;
+      let valid = false;
+
+      if (this.param === undefined) {
+          msg = "internal error, model undefined";
+      } else {
+          if (!this.param.checkMin(v)) {
+              msg = "La valeur n'est pas dans [" + this.param.domain.minValue + " , " + this.param.maxValue + "[";
+          } else {
+              valid = true;
+          }
+      }
+
+      return { isValid: valid, message: msg };
+  }
+  protected validateModelValue(v: any): { isValid: boolean, message: string } {
+      let msg: string;
+      let valid = false;
+
+      if (this.param === undefined) {
+          msg = "internal error, model undefined";
+      } else {
+          if (!this.param.checkMax(v)) {
+              msg = "La valeur n'est pas dans ]" + this.param.minValue + " , " + this.param.domain.maxValue + "]";
+          } else {
+              valid = true;
+          }
+      }
+
+      return { isValid: valid, message: msg };
+  }
+  protected validateModelValue(v: any): { isValid: boolean, message: string } {
+      let msg: string;
+      let valid = false;
+
+      if (! this.param) {
+          msg = "internal error, model undefined";
+      } else {
+          if (this.param.isMinMaxValid) {
+              if (!this.param.checkStep(v)) {
+                  msg = "La valeur n'est pas dans " + this.param.stepRefValue.toString();
+              } else {
+                  valid = v > 0;
+                  if (!valid) {
+                      msg = "La valeur ne peut pas être <= 0";
+                  }
+              }
+          } else {
+              msg = "Veuillez corriger le min/max";
+          }
+      }
+
+      return { isValid: valid, message: msg };
+  } */
+}
diff --git a/src/app/components/dialog-load-session/dialog-load-session.component.html b/src/app/components/dialog-load-session/dialog-load-session.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..b9f411fb767fdff20042560145f24d00edfef737
--- /dev/null
+++ b/src/app/components/dialog-load-session/dialog-load-session.component.html
@@ -0,0 +1,42 @@
+<h1 mat-dialog-title [innerHTML]="uitextLoadSessionTitle"></h1>
+
+<form [formGroup]="loadSessionForm">
+
+  <div mat-dialog-content>
+
+    <mat-form-field>
+      <ngx-mat-file-input id="session-file-input" #sessionFile formControlName="file"[placeholder]="uitextLoadSessionFilename"
+        (change)="onFileSelected($event)"></ngx-mat-file-input>
+      <button mat-icon-button matSuffix *ngIf="!sessionFile.empty" (click)="sessionFile.clear($event)">
+        <mat-icon>clear</mat-icon>
+      </button>
+    </mat-form-field>
+
+    <div class="cb-container">
+      <mat-checkbox [name]="c.uid" *ngFor="let c of calculators" [(ngModel)]="c.selected" [ngModelOptions]="{standalone: true}">
+        {{ c.title }}
+      </mat-checkbox>
+    </div>
+
+    <div class="btn-container">
+      <button mat-raised-button (click)="selectAll()" [disabled]="calculators.length === 0">
+        {{ uitextAll }}
+      </button>
+      <button mat-raised-button (click)="selectNone()" [disabled]="calculators.length === 0">
+        {{ uitextNone }}
+      </button>
+    </div>
+  </div>
+
+  <div mat-dialog-actions>
+    <button mat-raised-button color="primary" [mat-dialog-close]="false" cdkFocusInitial>
+      {{ uitextCancel }}
+    </button>
+    <button mat-raised-button type="submit" color="warn" (click)="loadSession()"
+      [disabled]="(loadSessionForm.invalid || ! atLeastOneCheckboxSelected)">
+
+      {{ uitextLoad }}
+    </button>
+  </div>
+
+</form>
diff --git a/src/app/components/dialog-load-session/dialog-load-session.component.scss b/src/app/components/dialog-load-session/dialog-load-session.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e612ad2cf5490552d96d024c1122ef4cd1bbfc1f
--- /dev/null
+++ b/src/app/components/dialog-load-session/dialog-load-session.component.scss
@@ -0,0 +1,19 @@
+mat-form-field {
+    width: 100%;
+}
+
+.cb-container {
+    margin-bottom: 5px;
+
+    mat-checkbox {
+        display: block;
+    }
+}
+
+.btn-container {
+    margin-bottom: 20px;
+
+    button {
+        margin-right: 5px;
+    }
+}
diff --git a/src/app/components/dialog-load-session/dialog-load-session.component.ts b/src/app/components/dialog-load-session/dialog-load-session.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3aa55c836a420dd0966dd5df2ae72687a382911e
--- /dev/null
+++ b/src/app/components/dialog-load-session/dialog-load-session.component.ts
@@ -0,0 +1,97 @@
+import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material";
+import { Inject, Component } from "@angular/core";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
+import { FormGroup, FormBuilder, Validators } from "@angular/forms";
+import { ServiceFactory } from "../../services/service-factory";
+
+@Component({
+    selector: "dialog-load-session",
+    templateUrl: "dialog-load-session.component.html",
+    styleUrls: ["dialog-load-session.component.scss"]
+})
+export class DialogLoadSessionComponent {
+
+    public calculators: any[] = [];
+
+    public file: any;
+
+    public loadSessionForm: FormGroup;
+
+    constructor(
+      public dialogRef: MatDialogRef<DialogLoadSessionComponent>,
+      private intlService: I18nService,
+      private fb: FormBuilder,
+      @Inject(MAT_DIALOG_DATA) public data: any
+    ) {
+      this.loadSessionForm = this.fb.group({
+        file: [null, Validators.required]
+      });
+    }
+
+    public selectAll() {
+      for (const c of this.calculators) {
+        c.selected = true;
+      }
+    }
+
+    public selectNone() {
+      for (const c of this.calculators) {
+        c.selected = false;
+      }
+    }
+
+    public onFileSelected(event: any) {
+      if (event.target.files && event.target.files.length) {
+        this.file = event.target.files[0];
+
+        const formService = ServiceFactory.instance.formulaireService;
+        formService.calculatorInfosFromSessionFile(this.file).then(
+          calcInfos => {
+            this.calculators = calcInfos;
+            for (const n of this.calculators) {
+                n.selected = true;
+            }
+          }
+        );
+      }
+    }
+
+    public loadSession() {
+      this.dialogRef.close({
+        calculators: this.calculators,
+        file: this.file
+      });
+    }
+
+    public get atLeastOneCheckboxSelected() {
+      let ok = false;
+      for (const c of this.calculators) {
+        ok = (ok || c.selected);
+      }
+      return ok;
+    }
+
+    public get uitextLoad() {
+      return this.intlService.localizeText("INFO_OPTION_LOAD");
+    }
+
+    public get uitextCancel() {
+      return this.intlService.localizeText("INFO_OPTION_CANCEL");
+    }
+
+    public get uitextAll() {
+      return this.intlService.localizeText("INFO_OPTION_ALL");
+    }
+
+    public get uitextNone() {
+      return this.intlService.localizeText("INFO_OPTION_NONE");
+    }
+
+    public get uitextLoadSessionFilename() {
+      return this.intlService.localizeText("INFO_DIALOG_LOAD_SESSION_FILENAME");
+    }
+
+    public get uitextLoadSessionTitle() {
+      return this.intlService.localizeText("INFO_DIALOG_LOAD_SESSION_TITLE");
+    }
+}
diff --git a/src/app/components/dialog-save-session/dialog-save-session.component.html b/src/app/components/dialog-save-session/dialog-save-session.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..5c19a5bc6c607f1367d8cc2f3a6363ddc7625bc3
--- /dev/null
+++ b/src/app/components/dialog-save-session/dialog-save-session.component.html
@@ -0,0 +1,34 @@
+<h1 mat-dialog-title [innerHTML]="uitextSaveSessionTitle"></h1>
+
+<form>
+
+  <div mat-dialog-content>
+    <div class="cb-container">
+      <mat-checkbox [name]="c.uid" *ngFor="let c of calculators" [(ngModel)]="c.selected">
+        {{ c.title }}
+      </mat-checkbox>
+    </div>
+
+    <div class="btn-container">
+      <button mat-raised-button (click)="selectAll()">{{ uitextAll }}</button>
+      <button mat-raised-button (click)="selectNone()">{{ uitextNone }}</button>
+    </div>
+
+    <mat-form-field>
+      <input matInput required [placeholder]="uitextFilenameInput" [(ngModel)]="fileName"
+        name="filename" #filename="ngModel">
+    </mat-form-field>
+  </div>
+
+  <div mat-dialog-actions>
+    <button mat-raised-button color="primary" [mat-dialog-close]="false" cdkFocusInitial>
+      {{ uitextCancel }}
+    </button>
+    <button mat-raised-button type="submit" color="warn" (click)="saveSession()"
+      [disabled]="(! filename.valid) || (! atLeastOneCheckboxSelected)">
+
+      {{ uitextSave }}
+    </button>
+  </div>
+
+</form>
diff --git a/src/app/components/dialog-save-session/dialog-save-session.component.scss b/src/app/components/dialog-save-session/dialog-save-session.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e612ad2cf5490552d96d024c1122ef4cd1bbfc1f
--- /dev/null
+++ b/src/app/components/dialog-save-session/dialog-save-session.component.scss
@@ -0,0 +1,19 @@
+mat-form-field {
+    width: 100%;
+}
+
+.cb-container {
+    margin-bottom: 5px;
+
+    mat-checkbox {
+        display: block;
+    }
+}
+
+.btn-container {
+    margin-bottom: 20px;
+
+    button {
+        margin-right: 5px;
+    }
+}
diff --git a/src/app/components/dialog-save-session/dialog-save-session.component.ts b/src/app/components/dialog-save-session/dialog-save-session.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..032ed2c65e56e026db473bc9eaca0eea6a3349a1
--- /dev/null
+++ b/src/app/components/dialog-save-session/dialog-save-session.component.ts
@@ -0,0 +1,74 @@
+import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material";
+import { Inject, Component } from "@angular/core";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
+
+@Component({
+    selector: "dialog-save-session",
+    templateUrl: "dialog-save-session.component.html",
+    styleUrls: ["dialog-save-session.component.scss"]
+})
+export class DialogSaveSessionComponent {
+
+    public calculators: any[];
+
+    public fileName = "session";
+
+    constructor(
+      public dialogRef: MatDialogRef<DialogSaveSessionComponent>,
+      private intlService: I18nService,
+      @Inject(MAT_DIALOG_DATA) public data: any
+    ) {
+      this.calculators = data.calculators;
+    }
+
+    public selectAll() {
+      for (const c of this.calculators) {
+          c.selected = true;
+      }
+    }
+
+    public selectNone() {
+      for (const c of this.calculators) {
+        c.selected = false;
+      }
+    }
+
+    public saveSession() {
+      this.dialogRef.close({
+        calculators: this.calculators,
+        filename: this.fileName
+      });
+    }
+
+    public get atLeastOneCheckboxSelected() {
+      let ok = false;
+      for (const c of this.calculators) {
+        ok = (ok || c.selected);
+      }
+      return ok;
+    }
+
+    public get uitextSave() {
+      return this.intlService.localizeText("INFO_OPTION_SAVE");
+    }
+
+    public get uitextCancel() {
+      return this.intlService.localizeText("INFO_OPTION_CANCEL");
+    }
+
+    public get uitextAll() {
+      return this.intlService.localizeText("INFO_OPTION_ALL");
+    }
+
+    public get uitextNone() {
+      return this.intlService.localizeText("INFO_OPTION_NONE");
+    }
+
+    public get uitextSaveSessionTitle() {
+      return this.intlService.localizeText("INFO_DIALOG_SAVE_SESSION_TITLE");
+    }
+
+    public get uitextFilenameInput() {
+      return this.intlService.localizeText("INFO_DIALOG_SAVE_SESSION_FILENAME");
+    }
+}
diff --git a/src/app/components/field-set/field-set.component.html b/src/app/components/field-set/field-set.component.html
index 9fae14a33a4d08eb39ddaa0a71d2c2a894100b8a..3217d50f0ba485c834824abf92139f58b47292d8 100644
--- a/src/app/components/field-set/field-set.component.html
+++ b/src/app/components/field-set/field-set.component.html
@@ -1,31 +1,31 @@
-<div class="row fieldset_backgrd">
-    <div class="col fieldset_title">
+<mat-card-header class="bg-accent-light">
+    <mat-card-title>
         {{ title }}
+    </mat-card-title>
+    <div *ngIf="showButtons" class="hyd-window-btns">
+        <button mat-icon-button (click)="onAddClick()">
+            <mat-icon>add_box</mat-icon>
+        </button>
+        <button mat-icon-button [disabled]="! enableRemoveButton" (click)="onRemoveClick()">
+            <mat-icon>delete</mat-icon>
+        </button>
+        <button mat-icon-button [disabled]="! enableUpButton" (click)="onMoveUpClick()">
+            <mat-icon>arrow_upward</mat-icon>
+        </button>
+        <button mat-icon-button [disabled]="! enableDownButton" (click)="onMoveDownClick()">
+            <mat-icon>arrow_downward</mat-icon>
+        </button>
     </div>
-    <div *ngIf="showButtons" class="col-sm-4 fa-stack fa-2x hyd-window-btns">
-        <i class="fa fa-plus" (click)='onAddClick()'></i>
-        <i class="fa fa-trash" [style.color]='removeButtonColor' (click)='onRemoveClick()'></i>
-        <i class="fa fa-arrow-up" [style.color]='upButtonColor' (click)='onMoveUpClick()'></i>
-        <i class="fa fa-arrow-down" [style.color]='downButtonColor' (click)='onMoveDownClick()'></i>
-    </div>
-</div>
-
-<!--
-    <tag *ngFor="let var of array" *ngIf="...utilisation de var..." >
-    </tag>
-    peut être transformé en
-    <ng-template ngFor let-var [ngForOf]="array">
-        <tag *ngIf="...utilisation de var..." >
-        </tag>
-    </ng-template>
--->
+</mat-card-header>
 
-<ng-template ngFor let-p [ngForOf]="fields">
-    <param-field-line *ngIf="isInputField(p)" [param]=p (radio)=onRadioClick($event) (valid)=onParamLineValid() (inputChange)=onInputChange()>
-    </param-field-line>
+<mat-card-content>
+    <ng-template ngFor let-p [ngForOf]="fields">
+        <param-field-line *ngIf="isInputField(p)" [param]=p (radio)=onRadioClick($event) (valid)=onParamLineValid() (inputChange)=onInputChange()>
+        </param-field-line>
 
-    <select-field-line *ngIf="isSelectField(p)" [_select]=p>
-    </select-field-line>
+        <select-field-line *ngIf="isSelectField(p)" [_select]=p>
+        </select-field-line>
 
-    <check-field-line *ngIf="isCheckField(p)" [check]=p></check-field-line>
-</ng-template>
\ No newline at end of file
+        <check-field-line *ngIf="isCheckField(p)" [check]=p></check-field-line>
+    </ng-template>
+</mat-card-content>
diff --git a/src/app/components/field-set/field-set.component.scss b/src/app/components/field-set/field-set.component.scss
index dbe81a5108659a3fb67366443ca801ed88591dc0..373f5b26832ff9012eb9819ced8f1b4b23fcb914 100644
--- a/src/app/components/field-set/field-set.component.scss
+++ b/src/app/components/field-set/field-set.component.scss
@@ -1,12 +1,84 @@
-.fieldset_title {
-    font-weight: bold;
+param-field-line {
+    // border: solid green 2px;
+    display: block;
 }
-.radio_param_header {
-    width: 10em;
+
+select-field-line {
+    // border: solid blue 2px;
+    display: block;
 }
-.fieldset_backgrd {
-    background-color: #eeeeee;
+
+mat-card-header {
+    margin-left: -16px;
+    margin-right: -16px;
+    padding-left: 16px;
+    padding-top: 8px;
+    color: white;
+
+    // Pourquoi n'est-ce pas hérité de calculator.component.scss ?
+    // À cause de la surcharge de mat-card-header ci-dessus ?
+    mat-card-title {
+        font-size: 16px;
+        margin-bottom: 8px;
+    }
+
+    .hyd-window-btns {
+        text-align: right;
+    
+        mat-icon {
+            cursor: pointer;
+            transform: scale(1.2);
+            margin-top: 6px;
+            margin-right: 5px;
+        }
+    }
 }
-.hyd-window-btns {
-    text-align: right;
+
+mat-card-content {
+    margin-top: 1em;
+}
+
+mat-card-actions {
+
+    button {
+        width: 100%;
+    }
+}
+
+:host {
+
+    &.fieldset-inner {
+
+        mat-card-header {
+            margin-left: 0;
+            margin-right: 0;
+            padding-left: 12px;
+
+            mat-card-title {
+                font-size: 14px;
+            }
+            
+            .hyd-window-btns {
+                position: absolute;
+                right: 18px;
+                margin-top: -4px;
+
+                button.mat-icon-button {
+                    width: 26px;
+                    height: 20px;
+                    line-height: 20px;
+
+                    mat-icon {
+                        margin-top: 0;
+                        margin-right: 0;
+                        transform: scale(1);
+                    }
+                }
+            }
+        }
+
+        mat-card-content {
+            padding: 0 1em;
+        }
+    }
 }
diff --git a/src/app/components/field-set/field-set.component.ts b/src/app/components/field-set/field-set.component.ts
index 24a031d4df575d78117d2adf4c3c1e24bb8475a2..5848aa4c1f961ab145c586232f86d6717386db71 100644
--- a/src/app/components/field-set/field-set.component.ts
+++ b/src/app/components/field-set/field-set.component.ts
@@ -24,26 +24,6 @@ export class FieldSetComponent implements DoCheck {
         return this._fieldSet.kids;
     }
 
-    public get showButtons() {
-        return this._showButtons;
-    }
-
-    public set showButtons(b: boolean) {
-        this._showButtons = b;
-    }
-
-    public set enableUpButton(b: boolean) {
-        this._enableUpButton = b;
-    }
-
-    public set enableDownButton(b: boolean) {
-        this._enableDownButton = b;
-    }
-
-    public set enableRemoveButton(b: boolean) {
-        this._enableRemoveButton = b;
-    }
-
     public set fieldsetNumber(n: number) {
         this._fieldSet.labelNumber = n;
     }
@@ -62,26 +42,6 @@ export class FieldSetComponent implements DoCheck {
         return this._isValid;
     }
 
-    /**
-     * couleur du bouton monter
-     */
-    private get upButtonColor(): string {
-        return this._enableUpButton ? "black" : "lightgrey";
-    }
-
-    /**
-     * couleur du bouton descendre
-     */
-    private get downButtonColor(): string {
-        return this._enableDownButton ? "black" : "lightgrey";
-    }
-
-    /**
-     * couleur du bouton supprimer
-     */
-    private get removeButtonColor(): string {
-        return this._enableRemoveButton ? "black" : "lightgrey";
-    }
     /**
     * field set attribute
     */
@@ -134,22 +94,22 @@ export class FieldSetComponent implements DoCheck {
     /**
      * flag d'affichage des boutons ajouter, supprimer, monter, descendre
      */
-    private _showButtons = false;
+    public showButtons = false;
 
     /**
      * flag d'activation du bouton monter
      */
-    private _enableUpButton = true;
+    public enableUpButton = true;
 
     /**
      * flag d'activation du bouton descendre
      */
-    private _enableDownButton = true;
+    public enableDownButton = true;
 
     /**
      * flag d'activation du bouton supprimer
      */
-    private _enableRemoveButton = true;
+    public enableRemoveButton = true;
 
     /**
      * événement de changement d'état d'un radio
@@ -300,26 +260,20 @@ export class FieldSetComponent implements DoCheck {
      * clic sur le bouton supprimer
      */
     private onRemoveClick() {
-        if (this._enableRemoveButton) {
-            this.removeFieldset.emit(this._fieldSet);
-        }
+        this.removeFieldset.emit(this._fieldSet);
     }
 
     /**
      * clic sur le bouton monter
      */
     private onMoveUpClick() {
-        if (this._enableUpButton) {
-            this.moveFieldsetUp.emit(this._fieldSet);
-        }
+        this.moveFieldsetUp.emit(this._fieldSet);
     }
 
     /**
      * clic sur le bouton descendre
      */
     private onMoveDownClick() {
-        if (this._enableDownButton) {
-            this.moveFieldsetDown.emit(this._fieldSet);
-        }
+        this.moveFieldsetDown.emit(this._fieldSet);
     }
 }
diff --git a/src/app/components/fieldset-container/fieldset-container.component.html b/src/app/components/fieldset-container/fieldset-container.component.html
index 84e4f78e5a5c47b81372383aafb6a8126856e52f..08e323f4465f7fad1027429de458464fbd5232c8 100644
--- a/src/app/components/fieldset-container/fieldset-container.component.html
+++ b/src/app/components/fieldset-container/fieldset-container.component.html
@@ -1,10 +1,13 @@
-<div class="container-fluid" style="border-style:solid; border-color: lightgray; border-radius: 10px; margin-bottom: 10px;">
-    <div class="row">
-        <h4 class="col">{{ title }}</h4>
-    </div>
+<mat-card-header class="bg-accent-light">
+    <mat-card-title>
+        {{ title }}
+    </mat-card-title>
+</mat-card-header>
 
-    <field-set *ngFor="let fs of fieldsets" [fieldSet]=fs (radio)=onRadioClick($event) (valid)=onFieldsetValid() (inputChange)=onInputChange()
-        (addFieldset)=onAddFieldset($event) (removeFieldset)=onRemoveFieldset($event) (moveFieldsetUp)=onMoveFieldsetUp($event)
-        (moveFieldsetDown)=onMoveFieldsetDown($event)>
+<mat-card-content>
+    <field-set *ngFor="let fs of fieldsets" class="fieldset-inner" [fieldSet]=fs
+        (radio)=onRadioClick($event) (valid)=onFieldsetValid() (inputChange)=onInputChange()
+        (addFieldset)=onAddFieldset($event) (removeFieldset)=onRemoveFieldset($event)
+        (moveFieldsetDown)=onMoveFieldsetDown($event) (moveFieldsetUp)=onMoveFieldsetUp($event)>
     </field-set>
-</div>
\ No newline at end of file
+</mat-card-content>
diff --git a/src/app/components/fieldset-container/fieldset-container.component.scss b/src/app/components/fieldset-container/fieldset-container.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..bbc65715fe3bf96979687811fbac7ffc9273c3d0
--- /dev/null
+++ b/src/app/components/fieldset-container/fieldset-container.component.scss
@@ -0,0 +1,26 @@
+:host {
+    display: block;
+    background-color: #f0f0f0;
+    // reduce margins to avoid inner field-sets being too narrow on 360px display
+    margin-left: -8px;
+    margin-right: -8px;
+}
+
+mat-card-header {
+    margin-left: -8px;
+    margin-right: -8px;
+    padding-left: 16px;
+    padding-top: 8px;
+    color: white;
+
+    // Pourquoi n'est-ce pas hérité de calculator.component.scss ?
+    // À cause de la surcharge de mat-card-header ci-dessus ?
+    mat-card-title {
+        font-size: 16px;
+        margin-bottom: 8px;
+    }
+}
+
+mat-card-content {
+    margin-top: 1em;
+}
diff --git a/src/app/components/fieldset-container/fieldset-container.component.ts b/src/app/components/fieldset-container/fieldset-container.component.ts
index ccfedcec387e31e40bb4f141f2b30797459dbc1d..75caa880fcfb96ab8300ad8a00a09e162b39874a 100644
--- a/src/app/components/fieldset-container/fieldset-container.component.ts
+++ b/src/app/components/fieldset-container/fieldset-container.component.ts
@@ -7,15 +7,17 @@ import { FormulaireDefinition } from "../../formulaire/definition/form-definitio
 
 @Component({
     selector: "fieldset-container",
-    templateUrl: "./fieldset-container.component.html"
+    templateUrl: "./fieldset-container.component.html",
+    styleUrls: [
+        "./fieldset-container.component.scss"
+    ]
 })
 export class FieldsetContainerComponent implements DoCheck, AfterViewInit {
 
     public get title(): string {
-        if (this._container === undefined) {
-            return undefined;
+        if (this._container) {
+            return this._container.label;
         }
-        return this._container.label;
     }
 
     public get fieldsets() {
diff --git a/src/app/components/fixedvar-results/fixed-results.component.html b/src/app/components/fixedvar-results/fixed-results.component.html
index 703075f95dc6c55653e78138b4bdf88a723a8752..b35f932d4c58789bd991bd20f33c13d0dd2ad727 100644
--- a/src/app/components/fixedvar-results/fixed-results.component.html
+++ b/src/app/components/fixedvar-results/fixed-results.component.html
@@ -1,30 +1,17 @@
-<div class="container-fluid">
+<div class="fixed-results-container" *ngIf="hasFixedParameters">
+    <!-- table des résultats fixés -->
+    <table mat-table [dataSource]="dataSet">
 
-    <!-- [style.display]="hasResults"> -->
+        <ng-container matColumnDef="parametre">
+            <th mat-header-cell *matHeaderCellDef>{{ uitextParamFixes }}</th>
+            <td mat-cell *matCellDef="let element" [ngClass]="{'highlightedResult': element.isCalcResult}">{{ element.label }}</td>
+        </ng-container>
+        <ng-container matColumnDef="valeur">
+            <th mat-header-cell *matHeaderCellDef>{{ uitextValeurs }}</th>
+            <td mat-cell *matCellDef="let element" [ngClass]="{'highlightedResult': element.isCalcResult}">{{ element.value }}</td>
+        </ng-container>
 
-    <div *ngIf="hasFixedParameters" class="row">
-        <!-- table des résultats fixés -->
-        <div class="col mx-auto">
-            <table class="table" style="border: 1px solid rgb(230,230,230);">
-                <tr>
-                    <th class="result_center">
-                        {{ uitextParamFixes }}
-                    </th>
-                    <th class="result_center">
-                        {{ uitextValeurs }}
-                    </th>
-                </tr>
-                <tr *ngFor="let r of fixedParams; let i=index">
-                    <td class="result_right {{getFixedParamClass(i)}}">
-                        {{ formattedLabel(r) }}
-                    </td>
-                    <td class="result_center {{getFixedParamClass(i)}}">
-                        {{ formattedValue(r) }}
-                    </td>
-                </tr>
-                <tr *ngIf="hasParameterResult" vertical-result-element [result-element]=resultElement [_label]=resultLabel>
-                </tr>
-            </table>
-        </div>
-    </div>
-</div>
\ No newline at end of file
+        <tr mat-header-row *matHeaderRowDef="tableColumns"></tr>
+        <tr mat-row *matRowDef="let row; columns: tableColumns;"></tr>
+    </table>
+</div>
diff --git a/src/app/components/fixedvar-results/fixed-results.component.scss b/src/app/components/fixedvar-results/fixed-results.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..6884f3f370e00c829b993ac10a651536f736868c
--- /dev/null
+++ b/src/app/components/fixedvar-results/fixed-results.component.scss
@@ -0,0 +1,33 @@
+:host {
+    display: block;
+}
+
+.fixed-results-container {
+    margin-top: 1em;
+    border: solid #ccc 1px;
+    border-radius: 2px;
+}
+
+table.mat-table {
+
+    .mat-header-row {
+        height: 40px;
+    }
+
+    .mat-row {
+        height: 32px;
+
+        &:nth-child(odd) {
+            background-color: #f4f4f4;
+        }
+    }
+
+    ::ng-deep .mat-header-cell {
+        font-size: 1em;
+        color: black;
+    }
+}
+
+.highlightedResult {
+    font-weight: bold;
+}
diff --git a/src/app/components/fixedvar-results/fixed-results.component.ts b/src/app/components/fixedvar-results/fixed-results.component.ts
index 0b7056b8f244a26aa4ef8da2dde10109002e0bbf..d9485bf0e7d14a84175d5a6c78f59869f57aab06 100644
--- a/src/app/components/fixedvar-results/fixed-results.component.ts
+++ b/src/app/components/fixedvar-results/fixed-results.component.ts
@@ -1,42 +1,29 @@
-import { Component } from "@angular/core";
+import { Component, ViewChild } from "@angular/core";
 
 import { FixedResults } from "../../results/fixed-results";
 import { NgParameter } from "../../formulaire/ngparam";
 import { CalculatorResults } from "../../results/calculator-results";
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
 import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
-import { FixedVarResultsComponent } from "./fixedvar-results.component";
-import { ResultElement } from "jalhyd";
+import { MatTable } from "@angular/material";
 
 @Component({
     selector: "fixed-results",
     templateUrl: "./fixed-results.component.html",
-    styles: [`
-    .result_right {
-        text-align: right;
-    }
-    .result_center {
-        text-align: center;
-    }
-    .result_id_0 {
-        background-color: #f0f0f0;
-    }
-    .result_id_1 {
-        background-color: #ffffff;
-    }
-    .result_id_2 {
-        font-weight: bolder;
-        background-color: #00d0ff;
-    }`]
+    styleUrls: [
+        "./fixed-results.component.scss"
+    ]
 })
 export class FixedResultsComponent {
-    /**
-     * résultats non mis en forme
-     */
+    /** résultats non mis en forme */
     private _fixedResults: FixedResults;
 
+    @ViewChild(MatTable) table: MatTable<any>;
+
+    public tableColumns = [ "parametre", "valeur" ];
+
     constructor(
-        private intlService: InternationalisationService,
+        private intlService: I18nService,
         private appSetupService: ApplicationSetupService,
     ) { }
 
@@ -48,14 +35,6 @@ export class FixedResultsComponent {
         return this._fixedResults && this._fixedResults.hasResults;
     }
 
-    private get resultElement(): ResultElement {
-        return this._fixedResults && this._fixedResults.result && this._fixedResults.result.resultElement;
-    }
-
-    private get resultLabel(): string {
-        return this._fixedResults && this._fixedResults.calculatedParameter && this._fixedResults.calculatedParameterHeader;
-    }
-
     private get uitextParamFixes() {
         return this.intlService.localizeText("INFO_CALCULATOR_PARAMFIXES");
     }
@@ -64,13 +43,6 @@ export class FixedResultsComponent {
         return this.intlService.localizeText("INFO_CALCULATOR_VALEURS");
     }
 
-    private getFixedParamClass(i: number) {
-        // if (this._results.isFixed && i === this._results.fixedResults.length - 1)
-        // return "font-weight-bold";
-        // tslint:disable-next-line:no-bitwise
-        return "result_id_" + String(i & 1);
-    }
-
     private get fixedParams() {
         return this._fixedResults && this._fixedResults.fixedParameters;
     }
@@ -88,7 +60,37 @@ export class FixedResultsComponent {
         return p.getValue().toFixed(nDigits);
     }
 
-    private get hasResults(): boolean {
-        return this._fixedResults && this._fixedResults.hasResults;
+    /**
+     * Returns a combination of and results and extraResults for mat-table
+     */
+    public get dataSet() {
+        const data = [];
+        // 1. fixed parameters
+        for (const fp of this.fixedParams) {
+            data.push({
+                label: this.formattedLabel(fp),
+                value: this.formattedValue(fp),
+                isCalcResult: false // for CSS
+            });
+        }
+        // 2. calculation results
+        if (
+            this._fixedResults.result
+            && this._fixedResults.result.resultElement
+            && this._fixedResults.result.resultElement.extraResults
+        ) {
+            const extraResults = this._fixedResults.result.resultElement.extraResults;
+            for (const k in extraResults) {
+                if (extraResults.hasOwnProperty(k)) {
+                    const er: number = extraResults[k];
+                    data.push({
+                        label: this.intlService.getExtraResLabel(k),
+                        value: this.intlService.formatResult(k, er),
+                        isCalcResult: true // for CSS
+                    });
+                }
+            }
+        }
+        return data;
     }
 }
diff --git a/src/app/components/fixedvar-results/fixedvar-results.component.html b/src/app/components/fixedvar-results/fixedvar-results.component.html
index a1fdb27695d8feb5ec033c2e7914fcf83ad55690..3ce5407546563ed4768f6c3990648ac5d0a341b5 100644
--- a/src/app/components/fixedvar-results/fixedvar-results.component.html
+++ b/src/app/components/fixedvar-results/fixedvar-results.component.html
@@ -1,23 +1,15 @@
-<div class="container-fluid">
+<div class="container">
     <!-- journal -->
     <log></log>
 
     <results-graph *ngIf="showVarResults"></results-graph>
 
-    <!-- 
-    classe conditionnelle :
-    [ngClass]="(condition) ? 'classe-si-vrai' : 'classe-si-faux'"
- -->
-
-    <div class="row">
+    <div>
         <!-- table des résultats fixés -->
-        <!-- <div class="col mx-auto" *ngIf="showFixedResults"> -->
-        <div class="col mx-auto">
-            <fixed-results [results]=fixedResults></fixed-results>
-        </div>
+        <fixed-results [results]=fixedResults></fixed-results>
 
         <!-- table des résultats variés -->
-        <div class="col" *ngIf="showVarResults">
+        <div *ngIf="showVarResults">
             <var-results [results]=varResults></var-results>
         </div>
     </div>
diff --git a/src/app/components/fixedvar-results/fixedvar-results.component.ts b/src/app/components/fixedvar-results/fixedvar-results.component.ts
index 8ef80d00cd31c5d87f31be25ce9ab12d22620246..d6dca7a41a47100383d7af0cc518c1190b680ad1 100644
--- a/src/app/components/fixedvar-results/fixedvar-results.component.ts
+++ b/src/app/components/fixedvar-results/fixedvar-results.component.ts
@@ -1,6 +1,6 @@
 import { Component, ViewChild, DoCheck } from "@angular/core";
 
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
 import { LogComponent } from "../../components/log/log.component";
 import { FixedResults } from "../../results/fixed-results";
 import { VarResults } from "../../results/var-results";
@@ -66,7 +66,7 @@ export class FixedVarResultsComponent implements DoCheck {
     private resultsGraphComponent: ResultsGraphComponent;
 
     constructor(
-        private intlService: InternationalisationService,
+        private intlService: I18nService,
         private appSetupService: ApplicationSetupService,
     ) { }
 
diff --git a/src/app/components/fixedvar-results/var-results.component.html b/src/app/components/fixedvar-results/var-results.component.html
index 8ace914cfaca4b2895cb63e8bcb3aaca8b4defd5..f89799dfd48f3e55d2f516df358b3ccf908c93a3 100644
--- a/src/app/components/fixedvar-results/var-results.component.html
+++ b/src/app/components/fixedvar-results/var-results.component.html
@@ -1,21 +1,15 @@
-<div class="container-fluid" [style.display]="hasResults">
-    <div class="row">
-        <!-- table des résultats variés -->
-        <div class="col">
-            <table class="table table-striped" style="border: 1px solid rgb(230,230,230);">
-                <tr>
-                    <th *ngFor="let h of headers">{{ h }}</th>
-                </tr>
-                <tr *ngFor="let r of rawResults; let i=index">
-                    <!-- paramètre varié -->
-                    <td class="result_center">
-                        {{ r.param }}
-                    </td>
-                    <!-- résultat -->
-                    <td horizontal-result-element [result-element]=r.result [headerKeys]=extraResultKeys>
-                    </td>
-                </tr>
-            </table>
-        </div>
+<div class="var-results-container">
+    <!-- scrollable -->
+    <div class="var-results-inner-container">
+
+        <table mat-table [dataSource]="dataSet">
+            <ng-container *ngFor="let h of headers; let i = index" [matColumnDef]="h">
+                <th mat-header-cell *matHeaderCellDef>{{ h }}</th>
+                <td mat-cell *matCellDef="let element">{{ element[i] }}</td>
+            </ng-container>
+
+            <tr mat-header-row *matHeaderRowDef="headers"></tr>
+            <tr mat-row *matRowDef="let row; columns: headers;"></tr>
+        </table>
     </div>
-</div>
\ No newline at end of file
+</div>
diff --git a/src/app/components/fixedvar-results/var-results.component.scss b/src/app/components/fixedvar-results/var-results.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..d8b8f970ea6ea458e1f1ccf4acccb0a6839af5f1
--- /dev/null
+++ b/src/app/components/fixedvar-results/var-results.component.scss
@@ -0,0 +1,34 @@
+:host {
+    display: block;
+}
+
+.var-results-container {
+    overflow-x: scroll;
+    margin-top: 2em;
+    border: solid #ccc 1px;
+}
+
+table.mat-table {
+
+    .mat-header-row {
+        height: 40px;
+    }
+
+    .mat-row {
+        height: 32px;
+
+        &:nth-child(odd) {
+            background-color: #f4f4f4;
+        }
+    }
+
+    ::ng-deep .mat-cell {
+        padding: 5px;
+    }
+
+    ::ng-deep .mat-header-cell {
+        font-size: 1em;
+        color: black;
+        padding: 5px;
+    }
+}
diff --git a/src/app/components/fixedvar-results/var-results.component.ts b/src/app/components/fixedvar-results/var-results.component.ts
index a7df06053b68ab943b682b826c78fbe4f5e0ef6c..ddc5eee572aec11b83c9f2db8b9e53a8d498e73d 100644
--- a/src/app/components/fixedvar-results/var-results.component.ts
+++ b/src/app/components/fixedvar-results/var-results.component.ts
@@ -1,33 +1,34 @@
-import { Component, Input } from "@angular/core";
-
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { Component, ViewChild } from "@angular/core";
+import { MatTable } from "@angular/material";
 import { VarResults } from "../../results/var-results";
 import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
 import { ResultElement } from "jalhyd";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
+import { nbind } from 'q';
 
 @Component({
     selector: "var-results",
-    templateUrl: "./var-results.component.html"
+    templateUrl: "./var-results.component.html",
+    styleUrls: [
+        "./var-results.component.scss"
+    ]
 })
 export class VarResultsComponent {
-    /**
-     * résultats non mis en forme
-     */
+
+    /** résultats non mis en forme */
     private _varResults: VarResults;
 
-    /**
-     * résultats mis en forme
-     */
+    /** résultats mis en forme */
     private _results: any[];
 
-    /**
-     * entêtes des colonnes (param à varier, à calculer + extraResults)
-     */
+    /** entêtes des colonnes (param à varier, à calculer + extraResults) */
     private _headers: string[];
 
+    @ViewChild(MatTable) table: MatTable<any>;
+
     constructor(
-        private intlService: InternationalisationService,
         private appSetupService: ApplicationSetupService,
+        private intlService: I18nService
     ) { }
 
     public set results(r: VarResults) {
@@ -55,15 +56,43 @@ export class VarResultsComponent {
         return this._varResults && this._varResults.hasResults;
     }
 
-    private get extraResultKeys() {
-        return this._varResults && this._varResults.extraResultKeys;
-    }
-
     public get headers() {
         return this._headers;
     }
 
-    public get rawResults() {
-        return this._results;
+    /**
+     * Returns a combination of and results and extraResults for mat-table
+     */
+    public get dataSet() {
+        const data = [];
+        const nDigits = this.appSetupService.displayDigits;
+        if (this._results) {
+            for (let i = 0; i < this._results.length; i++) {
+                const r = this._results[i];
+                const re: ResultElement = this._varResults.resultElements[i];
+
+                // for each computation step, build ordered list of : variable param value; result; extra results
+
+                // 1. variable param value
+                const list = [ r.param ];
+
+                // 2. result
+                // list.push(re.vCalc.toFixed(nDigits));
+                list.push(re.vCalc.toFixed(nDigits));
+
+                // 3. extra results
+                for (const erk of this._varResults.extraResultKeys) {
+                    const er = re.getExtraResult(erk);
+                    if (er !== undefined) {
+                        list.push(this.intlService.formatResult(erk, er));
+                    } else {
+                        list.push(er); // keep list ordered
+                    }
+
+                }
+                data.push(list);
+            }
+        }
+        return data;
     }
 }
diff --git a/src/app/components/generic-calculator/calc-name.component.html b/src/app/components/generic-calculator/calc-name.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..3866997a9293167d49281314dc156818649c9b17
--- /dev/null
+++ b/src/app/components/generic-calculator/calc-name.component.html
@@ -0,0 +1,5 @@
+<mat-form-field>
+    <input matInput #inputControl="ngModel" class="form-control" type="text"
+        [id]="inputId" [(ngModel)]="uiValue" [placeholder]="title" required>
+    <mat-error>{{ errorMessage }}</mat-error>
+</mat-form-field>
diff --git a/src/app/components/generic-calculator/calc-name.component.scss b/src/app/components/generic-calculator/calc-name.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..4227b604dd8b6e9ab63bdb4445e3a2292ec2077c
--- /dev/null
+++ b/src/app/components/generic-calculator/calc-name.component.scss
@@ -0,0 +1,21 @@
+:host {
+    display: block;
+}
+
+mat-form-field {
+    width: 100%;
+
+    &.mat-form-field-invalid {
+        margin-bottom: 1em;
+    }
+
+    ::ng-deep .mat-form-field-label {
+        font-size: 1.1em;
+        line-height: 1.4em;
+        margin-top: -2px;
+
+        &.mat-form-field-empty {
+            font-size: 1em;
+        }
+    }
+}
diff --git a/src/app/components/generic-calculator/calc-name.component.ts b/src/app/components/generic-calculator/calc-name.component.ts
index 21e9d67ac354a551f1432f3422421d273ffcce74..70c236305fa3c4f667eb8fc1b8b95fb8914d65dc 100644
--- a/src/app/components/generic-calculator/calc-name.component.ts
+++ b/src/app/components/generic-calculator/calc-name.component.ts
@@ -4,7 +4,10 @@ import { FormulaireDefinition } from "../../formulaire/definition/form-definitio
 
 @Component({
     selector: "calc-name",
-    templateUrl: "../generic-input/generic-input.component.html",
+    templateUrl: "./calc-name.component.html",
+    styleUrls: [
+        "./calc-name.component.scss"
+    ]
 })
 export class CalculatorNameComponent extends GenericInputComponent {
 
@@ -23,10 +26,9 @@ export class CalculatorNameComponent extends GenericInputComponent {
      * retourne la valeur du modèle
      */
     protected getModelValue(): any {
-        if (this._form === undefined) {
-            return undefined;
+        if (this._form) {
+            return this._form.calculatorName;
         }
-        return this._form.calculatorName;
     }
 
     /**
@@ -56,13 +58,6 @@ export class CalculatorNameComponent extends GenericInputComponent {
         return { isValid: valid, message: msg };
     }
 
-    /**
-     * convertit le modèle en valeur affichable par l'UI
-     */
-    protected modelToUI(v: any): string {
-        return v;
-    }
-
     /**
      * valide une valeur saisie dans l'UI (forme de la saisie : est ce bien une date, un nombre, ...)
      * @param ui valide la valeur saisie
diff --git a/src/app/components/generic-calculator/calculator.component.html b/src/app/components/generic-calculator/calculator.component.html
index 1afebb5b545461c5004a1af63507d6cc11b172a3..97446a3a9ce637f766b03da297a2b9882472b0e1 100644
--- a/src/app/components/generic-calculator/calculator.component.html
+++ b/src/app/components/generic-calculator/calculator.component.html
@@ -1,74 +1,64 @@
-<div class="row">
-    <!-- titre -->
-    <div class="col">
+<mat-card id="calculator-card">
+
+    <mat-card-header>
+
+        <div class="hyd-window-btns">
+            <!-- bouton d'aide -->
+            <mat-icon *ngIf="enableHelpButton" (click)="openHelp()" color="accent">help</mat-icon>
+            <!-- bouton de sauvegarde -->
+            <mat-icon (click)="saveCalculator()">save_alt</mat-icon>
+            <!-- bouton de fermeture -->
+            <mat-icon (click)="closeCalculator()">close</mat-icon>
+        </div>
+
+        <!-- titre -->
         <!-- on utilise [innerHTML] pour que les codes HTML comme &nbsp; soient interprétés correctement -->
-        <h1 [innerHTML]="uitextTitre"></h1>
-    </div>
+        <mat-card-title>
+            <h1 [innerHTML]="uitextTitre"></h1>
+        </mat-card-title>
 
-    <div class="col-sm-3 px-0 mx-0 fa-stack fa-2x hyd-window-btns">
-        <!-- bouton d'aide -->
-        <i *ngIf="enableHelpButton" class="fa fa-question-circle" style="color:blue" (click)="openHelp()"></i>
-        <!-- bouton de sauvegarde -->
-        <i class="fa fa-save" (click)="saveCalculator()"></i>
-        <!-- bouton de fermeture -->
-        <i class="fa fa-window-close" (click)="confirmModal.show()"></i>
-    </div>
-</div>
+    </mat-card-header>
 
-<!-- nom de la calculette -->
-<div class="row">
-    <div class="col-md-6">
-        <calc-name title="Nom de la calculette"></calc-name>
-    </div>
-</div>
+    <form>
 
-<div class="row">
-    <div [ngClass]="(hasResults) ? 'col-12 col-lg-6' : 'col-12'">
-        <div class="container-fluid">
-            <!-- chapitres -->
-            <ng-template ngFor let-fe [ngForOf]="formElements">
-                <field-set *ngIf="isFieldset(fe)" [style.display]="getFieldsetStyleDisplay(fe.id)" [fieldSet]=fe (radio)=onRadioClick($event)
-                    (validChange)=OnFieldsetValid() (inputChange)=onInputChange()></field-set>
+        <mat-card-content>
 
-                <fieldset-container *ngIf="isFieldsetContainer(fe)" [_container]=fe (radio)=onRadioClick($event) (validChange)=onFieldsetContainerValid()></fieldset-container>
-            </ng-template>
-        </div>
+            <!-- nom du module de calcul -->
+            <calc-name [title]="uitextCalculatorName"></calc-name>
 
-        <!-- bouton calculer -->
-        <div class="row">
-            <div class="col-12 text-center">
-                <button type="button" [ngClass]="(isCalculateDisabled) ? 'button_compute_err' : 'button_compute_ok'" name="Calculer" (click)="doCompute()"
-                    [disabled]="isCalculateDisabled">{{ uitextCalculer }}</button>
-                <p></p>
-                <p></p>
-            </div>
-        </div>
+            <div id="calc-cards-container" class="container" fxLayout="row wrap" fxLayoutAlign="space-around start">
+                <!-- chapitres -->
+                <mat-card id="calc-card-field-sets" fxFlex.gt-xs="1 0 400px" fxFlex.lt-sm="1 0 300px">
+                    <ng-template ngFor let-fe [ngForOf]="formElements">
+                        <field-set *ngIf="isFieldset(fe)" [style.display]="getFieldsetStyleDisplay(fe.id)" [fieldSet]=fe (radio)=onRadioClick($event)
+                            (validChange)=OnFieldsetValid() (inputChange)=onInputChange()></field-set>
 
-    </div>
+                        <fieldset-container *ngIf="isFieldsetContainer(fe)" [_container]=fe (radio)=onRadioClick($event) (validChange)=onFieldsetContainerValid()></fieldset-container>
+                    </ng-template>
 
-    <!-- résultats -->
-    <div [ngClass]="(hasResults) ? 'col-12 col-lg-6' : 'col-12'">
-        <calc-results id="resultsComp" (afterViewChecked)="onCalcResultsViewChecked()"></calc-results>
-    </div>
-</div>
+                    <mat-card-actions>
+                        <!-- bouton calculer -->
+                        <button mat-raised-button color="accent" name="Calculer" (click)="doCompute()"[disabled]="isCalculateDisabled">
+                            {{ uitextCalculer }}
+                        </button>
+                    </mat-card-actions>
+                </mat-card>
+
+                <!-- résultats -->
+                <mat-card id="calc-card-results" fxFlex.gt-xs="1 0 400px" fxFlex.lt-sm="1 0 300px">
+                    <mat-card-header>
+                        <mat-card-title>
+                            <h1 [innerHTML]="uitextResultsTitle"></h1>
+                        </mat-card-title>
+                    </mat-card-header>
+                    <mat-card-content>
+                        <calc-results id="resultsComp" (afterViewChecked)="onCalcResultsViewChecked()"></calc-results>
+                    </mat-card-content>
+                </mat-card>
 
-<!-- dialogue de confirmation de fermeture -->
-<div mdbModal #confirmModal="mdb-modal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true" [config]="{backdrop: false, ignoreBackdropClick: true}">
-    <div class="modal-dialog" role="document">
-        <div class="modal-content">
-            <div class="modal-header">
-                <button type="button" class="close pull-right" aria-label="Close" (click)="confirmModal.hide()">
-                    <span aria-hidden="true">×</span>
-                </button>
-                <h4 class="modal-title w-100" id="myModalLabel">{{ uitextCloseDialogTitle }}</h4>
-            </div>
-            <div class="modal-body">
-                <h4 [innerHTML]="uitextCloseDialogText"></h4>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-danger relative waves-light" (click)="confirmModal.hide();onCloseForm()" mdbRippleRadius>{{ uitextCloseDialogYes }}</button>
-                <button type="button" class="btn btn-success waves-light" aria-label="Close" (click)="confirmModal.hide()" mdbRippleRadius>{{ uitextCloseDialogNo }}</button>
             </div>
-        </div>
-    </div>
-</div>
\ No newline at end of file
+        </mat-card-content>
+
+    </form>
+
+</mat-card>
diff --git a/src/app/components/generic-calculator/calculator.component.scss b/src/app/components/generic-calculator/calculator.component.scss
index 161ef097e90f68ce7fc97d37d3befcbea5fdb574..b9275137bf8288272c846be0520777d73c5e211f 100644
--- a/src/app/components/generic-calculator/calculator.component.scss
+++ b/src/app/components/generic-calculator/calculator.component.scss
@@ -1,19 +1,66 @@
+.hyd-window-btns {
+    position: absolute;
+    right: 1em;
+    text-align: right;
 
-    .button_compute_ok {
-        height: 3em;
-        width: 30%;
+    mat-icon {
+        cursor: pointer;
+        margin-right: 5px;
     }
+}
 
-    .button_compute_err {
-        height: 3em;
-        width: 30%;
-        color: red
+#calc-cards-container {
+    margin-left: -1em;
+    margin-right: -1em;
+}
+
+mat-card {
+    margin-bottom: 2em;
+
+    // main card
+    &#calculator-card {
+
+        > mat-card-header {
+            margin-bottom: .5em;
+            padding-right: 6em; // space for right corner buttons
+        }
+    }
+
+    // cards inside main card
+
+    &#calc-card-field-sets {
+        margin-left: 1em;
+        margin-right: 1em;
+
+        mat-card-actions {
+            text-align: center;
+
+            button {
+                padding: .5em 3em;
+            }
+        }
     }
 
-    .hyd-window-btns {
-        text-align: right;
-    
-        i.fa {
-            margin-right: 5px;
+    &#calc-card-results {
+        margin-left: 1em;
+        margin-right: 1em;
+
+        mat-card-header {
+            margin-bottom: 1em;
         }
-    }
\ No newline at end of file
+    }
+
+    // @WARNING ::ng-deep est déprécié, mais y a rien d'autre pour
+    // l'instant (en attente de normalisation par le W3C)
+    ::ng-deep .mat-card-header-text {
+        margin: 0;
+    }
+
+    mat-card-header {
+
+        mat-card-title {
+            font-size: 16px;
+            margin-bottom: 8px;
+        }
+    }
+}
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index c71f47b8597e18dc8d76f7aad080d6289e0eab7a..ffc2cf9d8fe41c3d9a92fa0beffdbfdd59f30eed 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -4,7 +4,7 @@ import { ActivatedRoute } from "@angular/router";
 import { Observer } from "jalhyd";
 
 import { FormulaireService } from "../../services/formulaire/formulaire.service";
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
 import { FieldSet } from "../../formulaire/fieldset";
 import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
 import { CalculatorResultsComponent } from "../../components/calculator-results/calculator-results.component";
@@ -16,6 +16,8 @@ import { FormulaireElement } from "../../formulaire/formulaire-element";
 import { FieldsetContainer } from "../../formulaire/fieldset-container";
 import { FieldsetContainerComponent } from "../fieldset-container/fieldset-container.component";
 import { ServiceFactory } from "../../services/service-factory";
+import { MatDialog } from "@angular/material";
+import { DialogConfirmCloseCalcComponent } from "../dialog-confirm-close-calc/dialog-confirm-close-calc.component";
 
 @Component({
     selector: "hydrocalc",
@@ -42,7 +44,7 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
     private resultsComponent: CalculatorResultsComponent;
 
     /**
-     * composant "nom de la calculette"
+     * composant "nom du module de calcul"
      */
     @ViewChild(CalculatorNameComponent)
     private _calculatorNameComponent: CalculatorNameComponent;
@@ -82,12 +84,15 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
 
     // services
 
-    private intlService: InternationalisationService;
+    private intlService: I18nService;
     private formulaireService: FormulaireService;
 
-    constructor(private route: ActivatedRoute) {
+    constructor(
+        private route: ActivatedRoute,
+        private confirmCloseCalcDialog: MatDialog
+    ) {
         super();
-        this.intlService = ServiceFactory.instance.internationalisationService;
+        this.intlService = ServiceFactory.instance.i18nService;
         this.formulaireService = ServiceFactory.instance.formulaireService;
     }
 
@@ -127,27 +132,18 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
         if (this.hasForm) {
             return this.formulaireService.getLocalisedTitleFromCalculatorType(this._formulaire.calculatorType);
         }
-        return undefined;
     }
 
     public get uitextCalculer() {
         return this.intlService.localizeText("INFO_CALCULATOR_CALCULER");
     }
 
-    public get uitextCloseDialogTitle() {
-        return this.intlService.localizeText("INFO_CLOSE_DIALOGUE_TITRE");
+    public get uitextCalculatorName() {
+        return this.intlService.localizeText("INFO_CALCULATOR_CALC_NAME");
     }
 
-    public get uitextCloseDialogText() {
-        return this.intlService.localizeText("INFO_CLOSE_DIALOGUE_TEXT");
-    }
-
-    public get uitextCloseDialogYes() {
-        return this.intlService.localizeText("INFO_OPTION_YES");
-    }
-
-    public get uitextCloseDialogNo() {
-        return this.intlService.localizeText("INFO_OPTION_NO");
+    public get uitextResultsTitle() {
+        return this.intlService.localizeText("INFO_CALCULATOR_RESULTS_TITLE");
     }
 
     ngOnInit() {
@@ -275,7 +271,7 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
     // interface Observer
 
     update(sender: any, data: any): void {
-        if (sender instanceof InternationalisationService) {
+        if (sender instanceof I18nService) {
             this.formulaireService.updateLocalisation();
         } else if (sender instanceof FormulaireService) {
             switch (data["action"]) {
@@ -391,4 +387,16 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
     public saveCalculator() {
         this.formulaireService.saveForm(this._formulaire);
     }
+
+    public closeCalculator() {
+        const dialogRef = this.confirmCloseCalcDialog.open(
+            DialogConfirmCloseCalcComponent,
+            { disableClose: true }
+        );
+        dialogRef.afterClosed().subscribe(result => {
+            if (result) {
+                this.onCloseForm();
+            }
+        });
+    }
 }
diff --git a/src/app/components/generic-input/generic-input.component.html b/src/app/components/generic-input/generic-input.component.html
index 1f81ebe23535ecddd6fc1f3d1d846c80ec8be0c8..f6ca6506f4d99ffcef937546366fca94cbcf63eb 100644
--- a/src/app/components/generic-input/generic-input.component.html
+++ b/src/app/components/generic-input/generic-input.component.html
@@ -1,6 +1,7 @@
-<div class="md-form form-sm">
-    <input mdbInputDirective [mdbValidate]="false" type="text" [id]="inputId" class="form-control" [disabled]="isDisabled" [(ngModel)]="uiValue">
-    <!-- on utilise [innerHTML] pour que les codes HTML comme &nbsp; soient interprétés correctement -->
-    <label [for]="inputId" [innerHTML]="title"></label>
-    <small *ngIf="showError" class="text-danger" [innerHTML]="errorMessage"></small>
-</div>
\ No newline at end of file
+<mat-form-field>
+    <input matInput #inputControl="ngModel" class="form-control" type="text" inputmode="numeric"
+        [id]="inputId" [name]="inputId" [disabled]="isDisabled" [(ngModel)]="uiValue" [placeholder]="title"
+        pattern="^-?([0-9]*\.)?([0-9]+[Ee]-?)?[0-9]+$" required>
+
+    <mat-error>{{ errorMessage }}</mat-error>
+</mat-form-field>
diff --git a/src/app/components/generic-input/generic-input.component.ts b/src/app/components/generic-input/generic-input.component.ts
index 90c2e7c034564425ab9aad6bb68fb724a5f724a2..a7cd9064ce906ff59382e4dc0382751f45fd1073 100644
--- a/src/app/components/generic-input/generic-input.component.ts
+++ b/src/app/components/generic-input/generic-input.component.ts
@@ -1,17 +1,7 @@
-import { Input, Output, EventEmitter, ChangeDetectorRef, OnChanges } from "@angular/core";
-
+import { Input, Output, EventEmitter, ChangeDetectorRef, OnChanges, ViewChild } from "@angular/core";
+import { NgModel } from "@angular/forms";
 import { BaseComponent } from "../base/base.component";
-
-/*
-exemple de template :
-
-<div class="md-form form-sm">
-    <input mdbActive type="text" id="form1" class="form-control" [disabled]="isDisabled"
-        [ngModel]="uiValue" (ngModelChange)="setUIValue($event)">
-    <label for="form1">{{title}}</label>
-    <small class="text-danger">{{_message}}</small>
-</div>
-*/
+import { isNumeric } from "jalhyd";
 
 /**
  * classe de gestion générique d'un champ de saisie avec titre, validation et message d'erreur
@@ -27,7 +17,7 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC
     /**
      * entité mémoire gérée
      */
-    protected _model: any;
+    protected _model: any; // NgBaseParam mais aussi FormDefinition parfois (!?)
 
     /**
      * flag de désactivation de l'input
@@ -85,6 +75,8 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC
      */
     private _errorMessageModel: string;
 
+    @ViewChild("inputControl") inputField: NgModel;
+
     constructor(private cdRef: ChangeDetectorRef) {
         super();
         // generate "unique" input id
@@ -142,6 +134,12 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC
         if (this.isValid !== old) {
             this.emitValidChanged();
         }
+        // répercussion des erreurs sur le Form angular, pour faire apparaître/disparaître les mat-error
+        if (b) {
+            this.inputField.control.setErrors(null);
+        } else {
+            this.inputField.control.setErrors({ "incorrect": true });
+        }
     }
 
     private validateModel() {
@@ -160,7 +158,7 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC
      * getter du message d'erreur affiché.
      * L'erreur de forme (UI) est prioritaire
      */
-    private get errorMessage() {
+    public get errorMessage() {
         if (this._errorMessageUI !== undefined) {
             return this._errorMessageUI;
         }
@@ -268,7 +266,9 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC
     /**
      * convertit le modèle en valeur affichable par l'UI
      */
-    protected abstract modelToUI(v: any): string;
+    protected modelToUI(v: any): string {
+        return String(v);
+    }
 
     /**
      * valide une valeur saisie dans l'UI (forme de la saisie : est ce bien une date, un nombre, ...)
@@ -276,12 +276,25 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC
      * @returns isValid : true si la valeur est valide, false sinon
      * @returns message : message d'erreur
      */
-    protected abstract validateUIValue(ui: string): { isValid: boolean, message: string };
+    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
+        let valid = false;
+        let msg: string;
+
+        if (! isNumeric(ui)) {
+            msg = "Veuillez entrer une valeur numérique";
+        } else {
+            valid = true;
+        }
+
+        return { isValid: valid, message: msg };
+    }
 
     /**
      * convertit une valeur saisie dans l'UI en valeur affectable au modèle
      */
-    protected abstract uiToModel(ui: string): any;
+    protected uiToModel(ui: string): any {
+        return +ui;
+    }
 }
 
 /*
@@ -298,7 +311,7 @@ import { isNumeric, Message } from "jalhyd";
 @Component({
     selector: "test-input",
     template: `<div class="md-form form-sm">
-    <input mdbActive type="text" id="form1" class="form-control" [disabled]="isDisabled" [(ngModel)]="uiValue">
+    <input  type="text" id="form1" class="form-control" [disabled]="isDisabled" [(ngModel)]="uiValue">
     <label for="form1">{{title}}</label>
     <small *ngIf="showError" class="text-danger">{{errorMessage}}</small>
 </div>`
@@ -329,24 +342,6 @@ export class TestInputComponent extends GenericInputComponent {
         return { isValid: valid, message: msg };
     }
 
-    protected modelToUI(v: any): string {
-        if (typeof (v) === "number")
-            return String(v);
-        return undefined;
-    }
-
-    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
-        let valid: boolean = false;
-        let msg: string = undefined;
-
-        if (! isNumeric(ui))
-            msg = "Veuillez entrer une valeur numérique";
-        else
-            valid = true;
-
-        return { isValid: valid, message: msg };
-    }
-
     protected uiToModel(ui: string): any {
         return +ui;
     }
@@ -360,7 +355,7 @@ import { ParamDefinition } from "jalhyd";
 @Component({
     selector: "test2-input",
     template: `<div class="md-form form-sm">
-    <input mdbActive type="text" id="form1" class="form-control" [disabled]="isDisabled" [(ngModel)]="uiValue">
+    <input type="text" id="form1" class="form-control" [disabled]="isDisabled" [(ngModel)]="uiValue">
     <label for="form1">{{title}}</label>
     <small *ngIf="showError" class="text-danger">{{errorMessage}}</small>
 </div>`
@@ -394,27 +389,5 @@ export class Test2InputComponent extends GenericInputComponent {
 
         return { isValid: valid, message: msg };
     }
-
-    protected modelToUI(v: any): string {
-        if (typeof (v) === "number")
-            return String(v);
-        return undefined;
-    }
-
-    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
-        let valid: boolean = false;
-        let msg: string = undefined;
-
-        if (! isNumeric(ui))
-            msg = "Veuillez entrer une valeur numérique";
-        else
-            valid = true;
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected uiToModel(ui: string): any {
-        return +ui;
-    }
 }
 /**/
diff --git a/src/app/components/generic-select/generic-select.component.html b/src/app/components/generic-select/generic-select.component.html
index 8edaab39d123f794448d7abf0090a5cc81ad6419..da7a82515f0406dfc56beb7a36b4e61fc37ac7ac 100644
--- a/src/app/components/generic-select/generic-select.component.html
+++ b/src/app/components/generic-select/generic-select.component.html
@@ -1,8 +1,7 @@
-<div class="btn-group" dropdown (click)="onSelect($event)">
-    <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius>
-        {{ currentLabel }}
-    </button>
-    <div class="dropdown-menu">
-        <a class="dropdown-item" *ngFor="let e of entries" [value]=e>{{ entryLabel(e) }}</a>
-    </div>
-</div>
\ No newline at end of file
+<mat-form-field>
+    <mat-select [placeholder]="label" [(value)]="selectedValue">
+        <mat-option *ngFor="let e of entries" [value]="e">
+            {{ entryLabel(e) }}
+        </mat-option>
+    </mat-select>
+</mat-form-field>
diff --git a/src/app/components/generic-select/generic-select.component.ts b/src/app/components/generic-select/generic-select.component.ts
deleted file mode 100644
index 6614882ab63ed2f13e6b8fdcfe329dc97f7a6d80..0000000000000000000000000000000000000000
--- a/src/app/components/generic-select/generic-select.component.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import { Component, Output, EventEmitter } from "@angular/core";
-import { BaseComponent } from "../base/base.component";
-
-/*
-  exemple de template :
-
-<div class="btn-group" dropdown (click)="onSelect($event)">
-    <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius>
-        {{currentLabel}}
-    </button>
-    <div class="dropdown-menu">
-        <a class="dropdown-item" *ngFor="let e of entries" [value]=e>{{entryLabel(e)}}</a>
-    </div>
-</div>
-*/
-
-export abstract class GenericSelectComponent<T> {
-
-    private _currentLabel: string;
-
-    public get currentLabel(): string {
-        if (! this._currentLabel) {
-            this._currentLabel = this.selectedLabel;
-        }
-        return this._currentLabel;
-    }
-
-    /**
-     * appelé quand la valeur courante change dans l'UI
-     */
-    public onSelect(event: any) {
-        const val = event.target.value;
-        if (val !== undefined && val !== "") { // might be 0; opening the menu returns ""
-            this.selectedValue = val;
-            this._currentLabel = this.selectedLabel;
-        }
-    }
-
-    private get selectedLabel(): string {
-        for (const e of this.entries) {
-            if (e === this.selectedValue) {
-                return this.entryLabel(e);
-            }
-        }
-    }
-
-    /**
-     * liste des objets sélectionnables
-     */
-    public abstract get entries(): any[];
-
-    /**
-     * calcule l'étiquette d'une entrée (ce qui est affiché dans la liste déroulante)
-     */
-    protected abstract entryLabel(entry: any): string;
-
-    /**
-     *  valeur actuellement sélectionnée
-     * la valeur repère une entrée de la liste (cf. [value] dans le template)
-     */
-    public abstract get selectedValue(): T;
-
-
-    public abstract set selectedValue(v: T);
-}
diff --git a/src/app/components/load-calculator/load-calculator-anchor.directive.ts b/src/app/components/load-calculator/load-calculator-anchor.directive.ts
deleted file mode 100644
index 8d9b713edb554873a7758d464c188aef87ea5041..0000000000000000000000000000000000000000
--- a/src/app/components/load-calculator/load-calculator-anchor.directive.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { Directive, ComponentFactoryResolver, ComponentFactory, ComponentRef } from "@angular/core";
-
-import { ViewContainerRef } from "@angular/core";
-import { LoadCalculatorComponent } from "./load-calculator.component";
-
-@Directive({
-    selector: "[appLoadCalcDialogAnchor]"
-})
-export class LoadCalcDialogAnchorDirective {
-    constructor(
-        private viewContainer: ViewContainerRef,
-        private componentFactoryResolver: ComponentFactoryResolver
-    ) { }
-
-    public createDialog(): ComponentRef<LoadCalculatorComponent> {
-        this.viewContainer.clear();
-
-        const compFactory: ComponentFactory<LoadCalculatorComponent>
-            = this.componentFactoryResolver.resolveComponentFactory(LoadCalculatorComponent);
-        const compRef: ComponentRef<LoadCalculatorComponent> = this.viewContainer.createComponent(compFactory);
-
-        // compRef.instance.confirmResult.subscribe(() => {
-        // compRef.destroy();
-        // });
-
-        return compRef;
-    }
-}
diff --git a/src/app/components/load-calculator/load-calculator.component.html b/src/app/components/load-calculator/load-calculator.component.html
deleted file mode 100644
index e5233cc4725179ef87203859116bdcf6596ae2d7..0000000000000000000000000000000000000000
--- a/src/app/components/load-calculator/load-calculator.component.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<div mdbModal #loadDialog="mdb-modal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true" [config]="{backdrop: false, ignoreBackdropClick: true,show:true}">
-    <div class="modal-dialog" role="document">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title w-100" id="myModalLabel">{{ uitextDialogTitle }}</h4>
-            </div>
-            <div class="modal-body">
-                <div>
-                    <input type="file" #fileSelector multiple="false" accept="*.json" (change)="onFileSelect()">
-                </div>
-                <div *ngFor="let c of calculators">
-                    <input type="checkbox" value={{c.uid}} checked={{isSelected(c)}} (change)="onCheckCalc($event)">{{ c.title }}
-                </div>
-                <button *ngIf="showSelectButtons" type="button" class="btn btn-mdb-color waves-light" (click)="selectAll()" mdbRippleRadius>{{ uitextSelectAll }}</button>
-                <button *ngIf="showSelectButtons" type="button" class="btn btn-mdb-color waves-light" (click)="deselectAll()" mdbRippleRadius>{{ uitextDeselectAll }}</button>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-danger relative waves-light" (click)="loadDialog.hide();cancelLoad()" mdbRippleRadius>{{ uitextCancel }}</button>
-                <button type="button" class="btn btn-success waves-light" [disabled]="disableLoadButton" (click)="loadDialog.hide();confirmLoad()"
-                    mdbRippleRadius>{{ uitextLoad }}</button>
-            </div>
-        </div>
-    </div>
-</div>
\ No newline at end of file
diff --git a/src/app/components/load-calculator/load-calculator.component.ts b/src/app/components/load-calculator/load-calculator.component.ts
deleted file mode 100644
index 655d3baf237c65fd83cb55a0b718a97623ae2c94..0000000000000000000000000000000000000000
--- a/src/app/components/load-calculator/load-calculator.component.ts
+++ /dev/null
@@ -1,172 +0,0 @@
-import { Component, EventEmitter, ViewChild } from "@angular/core";
-
-import { ServiceFactory } from "../../services/service-factory";
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
-
-@Component({
-    selector: "load-calc",
-    templateUrl: "./load-calculator.component.html"
-})
-export class LoadCalculatorComponent {
-    @ViewChild("fileSelector") fileSelector;
-
-    private _selectFile: File;
-
-    /**
-     * liste des calculettes affichées. Forme des objets :
-     * "title": nom de la calculette
-     * "selected": flag de sélection pour la sauvegarde
-     */
-    private _calculators: any[];
-
-    /**
-     * événement émis lors du clic sur "annuler"/"charger"
-     * utilisé par la promise de gestion de la confirmation/annulation de la sauvegarde
-     */
-    private _confirmResult = new EventEmitter();
-
-    // services
-    private intlService: InternationalisationService;
-
-    constructor() {
-        this.intlService = ServiceFactory.instance.internationalisationService;
-    }
-
-    public get uitextDialogTitle() {
-        return "Charger des calculettes";
-    }
-
-    public get uitextCancel() {
-        // return this.intlService.localizeText("INFO_OPTION_NO");
-        return "Annuler";
-    }
-
-    public get uitextLoad() {
-        // return this.intlService.localizeText("INFO_OPTION_NO");
-        return "Charger";
-    }
-
-    public get uitextSelectAll() {
-        return "Toutes";
-    }
-
-    public get uitextDeselectAll() {
-        return "Aucune";
-    }
-
-    /**
-     * calcule l'état du bouton charger
-     */
-    public get disableLoadButton() {
-        // pas de fichier sélectionné -> bouton grisé
-        if (this._selectFile === undefined) {
-            return true;
-        }
-
-        // au moins une calculette sélectionnée -> dégrisé
-        if (this._calculators !== undefined) {
-            for (const c of this._calculators) {
-                if (c.selected) {
-                    return false;
-                }
-            }
-        }
-
-        // grisé sinon
-        return true;
-    }
-
-    public run(): Promise<any[]> {
-        // promise de gestion de la confirmation/annulation de la sauvegarde
-        return new Promise((resolve, reject) => {
-            this._confirmResult.subscribe((confirm) => {
-                if (confirm) {
-                    resolve(this._calculators);
-                } else {
-                    reject("canceled");
-                }
-            });
-        });
-    }
-
-    public get showSelectButtons(): boolean {
-        return this._calculators && this._calculators.length !== 0;
-    }
-
-    private getSelectedFile(): File {
-        const files: { [key: string]: File } = this.fileSelector.nativeElement.files;
-        for (const key in files) {
-            if (!isNaN(Number(key))) {
-                return files[key];
-            }
-        }
-        return undefined;
-    }
-
-    public onFileSelect() {
-        const formService = ServiceFactory.instance.formulaireService;
-        this._selectFile = this.getSelectedFile();
-        if (this._selectFile !== undefined) {
-            formService.calculatorInfosFromSessionFile(this._selectFile).then(
-                calcInfos => {
-                    this._calculators = calcInfos;
-                    for (const n of this._calculators) {
-                        n["selected"] = true;
-                    }
-                });
-        }
-    }
-
-    public get selectedFile(): File {
-        return this._selectFile;
-    }
-
-    public get calculators(): any[] {
-        return this._calculators;
-    }
-
-    private isSelected(c: any) {
-        return c.selected ? "checked" : undefined;
-    }
-
-    private onCheckCalc(event: any) {
-        for (const c of this._calculators) {
-            if (c.uid === event.target.value) {
-                c.selected = event.target.checked;
-                break;
-            }
-        }
-    }
-
-    public selectAll() {
-        for (const c of this._calculators) {
-            c.selected = true;
-        }
-    }
-
-    public deselectAll() {
-        for (const c of this._calculators) {
-            c.selected = false;
-        }
-    }
-
-    private set confirmed(b: boolean) {
-        setTimeout(() => {
-            this._confirmResult.next(b);
-        }, 0);
-    }
-
-    /**
-     * appelé quand on clique sur annuler
-     */
-    public cancelLoad() {
-        this.confirmed = false;
-    }
-
-    /**
-     * appelé quand on clique sur charger
-     */
-    public confirmLoad() {
-        this.confirmed = true;
-    }
-}
diff --git a/src/app/components/log-entry/log-entry.component.html b/src/app/components/log-entry/log-entry.component.html
index b603829536bc89da7a1d9c8a52de720ade88136d..666b2665ec957279afab7bfb9b4be8f8ad413dbc 100644
--- a/src/app/components/log-entry/log-entry.component.html
+++ b/src/app/components/log-entry/log-entry.component.html
@@ -1,11 +1,8 @@
-<div class="row entry">
-    <div class="col-1" style="text-align: center;">
-        <i *ngIf="levelInfo" class="fa fa-check" style="color:green"></i>
-        <i *ngIf="levelWarning" class="fa fa-exclamation" style="color:orange"></i>
-        <i *ngIf="levelError" class="fa fa-exclamation-triangle" style="color:red"></i>
-    </div>
-    <div class="col-11">
-        <!-- on utilise [innerHTML] pour que les codes HTML comme &nbsp; soient interprétés correctement -->
-        <span [innerHTML]="text"></span>
-    </div>
-</div>
\ No newline at end of file
+<div class="entry">
+    <mat-icon *ngIf="levelInfo" style="color:green">check_circle</mat-icon>
+    <mat-icon *ngIf="levelWarning" style="color:orange">error_outline</mat-icon>
+    <mat-icon *ngIf="levelError" style="color:red">warning</mat-icon>
+
+    <!-- on utilise [innerHTML] pour que les codes HTML comme &nbsp; soient interprétés correctement -->
+    <div class="entry-text" [innerHTML]="text"></div>
+</div>
diff --git a/src/app/components/log-entry/log-entry.component.scss b/src/app/components/log-entry/log-entry.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..ba2c18727d2a51835e7d5a9674d8af2ed50d8d90
--- /dev/null
+++ b/src/app/components/log-entry/log-entry.component.scss
@@ -0,0 +1,13 @@
+.entry {
+    margin-top: 0.5em;
+
+    mat-icon {
+        float: left;
+    }
+
+    .entry-text {
+        padding-top: 4px;
+        padding-left: 32px;
+        margin-bottom: 12px;
+    }
+}
diff --git a/src/app/components/log-entry/log-entry.component.ts b/src/app/components/log-entry/log-entry.component.ts
index 3b53858779aff9a9eef7182fbe49a396640e6c46..5bdc15e530f0f25d6e03906fbaa3c122d8e1cd53 100644
--- a/src/app/components/log-entry/log-entry.component.ts
+++ b/src/app/components/log-entry/log-entry.component.ts
@@ -2,20 +2,15 @@ import { Component, Input, OnChanges } from "@angular/core";
 
 import { Message, MessageSeverity } from "jalhyd";
 
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
 import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
 
 @Component({
     selector: "log-entry",
     templateUrl: "./log-entry.component.html",
-    styles: [`
-        .entry {
-            margin-left: 1em;
-            margin-right: 1em;
-            margin-top: 0.5em;
-            margin-bottom: 0.5em;
-        }
-    `]
+    styleUrls: [
+        "./log-entry.component.scss"
+    ]
 
 })
 export class LogEntryComponent implements OnChanges {
@@ -28,7 +23,7 @@ export class LogEntryComponent implements OnChanges {
     private _text: string;
 
     constructor(
-        private intlService: InternationalisationService,
+        private intlService: I18nService,
         private appSetupService: ApplicationSetupService
     ) { }
 
diff --git a/src/app/components/log/log.component.html b/src/app/components/log/log.component.html
index 15d92c0f171cb2dff50912c2fd91978c5af3a986..4d3d0424a7c33bd9da1816c6f61e0c749bcacfef 100644
--- a/src/app/components/log/log.component.html
+++ b/src/app/components/log/log.component.html
@@ -1,13 +1,8 @@
-<div class="row" *ngIf="hasEntries">
-    <div class="col-12">
-        <div class="hyd_log">
-            <!-- titre -->
-            <div class="titre">{{ uitextTitreJournal }}</div>
-
-            <!-- entrées du journal -->
-            <ng-template ngFor let-m [ngForOf]="messages">
-                <log-entry [_message]=m></log-entry>
-            </ng-template>
-        </div>
+<div *ngIf="hasEntries">
+    <div class="hyd_log">
+        <!-- titre -->
+        <div class="titre">{{ uitextTitreJournal }}</div>
+        <!-- entrées du journal -->
+        <log-entry *ngFor="let m of messages" [_message]="m"></log-entry>
     </div>
-</div>
\ No newline at end of file
+</div>
diff --git a/src/app/components/log/log.component.scss b/src/app/components/log/log.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..7dee1f9239623da8254eb25fedcaebd2acff4697
--- /dev/null
+++ b/src/app/components/log/log.component.scss
@@ -0,0 +1,22 @@
+:host {
+    display: block;
+}
+
+.hyd_log {
+    margin-top: 1em;
+    border: solid #ccc 1px;
+    border-radius: 2px;
+    padding: 1em;
+    padding-top : 1.3em;
+}
+
+.titre {
+    background-color: white;
+    border: 1px solid #ccc;
+    border-radius: 2px;
+    float: left;
+    font-weight: bold;
+    margin-bottom: -1em;
+    margin-top: -2.3em;
+    padding: 0.2em 0.5em;
+}
diff --git a/src/app/components/log/log.component.ts b/src/app/components/log/log.component.ts
index 67234e62f6a8bf6b9ec639660ac59bab4069c41b..bd710cb56a43ed8a4c927fe06ef90c9ae1f94c9a 100644
--- a/src/app/components/log/log.component.ts
+++ b/src/app/components/log/log.component.ts
@@ -2,31 +2,14 @@ import { Component } from "@angular/core";
 
 import { cLog, Message } from "jalhyd";
 
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
 
 @Component({
     selector: "log",
     templateUrl: "./log.component.html",
-    styles: [`
-        .hyd_log {
-            border : 1px solid black;
-            margin : 1.8em 0;
-            padding-top : 1.3em;
-            border-radius: 10px;
-        }
-        .titre {
-            background-color: #00b0ff;
-            border: 1px solid black;
-            border-radius: 5px;
-            float: left;
-            font-size: 1.15em;
-            font-weight: bold;
-            margin-bottom: -1em;
-            margin-left: 0.5em;
-            margin-top: -2.3em;
-            padding: 0.2em 0.5em;
-        }
-    `]
+    styleUrls: [
+        "./log.component.scss"
+    ]
 })
 export class LogComponent {
     /**
@@ -35,7 +18,7 @@ export class LogComponent {
     private _log: cLog;
 
     constructor(
-        private intlService: InternationalisationService,
+        private intlService: I18nService,
     ) { }
 
     private get uitextTitreJournal() {
diff --git a/src/app/components/ngparam-input/ngparam-input.component.scss b/src/app/components/ngparam-input/ngparam-input.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..6d5f7d7edbaa8d3cd7e36d7d4bc4298200d0ea4f
--- /dev/null
+++ b/src/app/components/ngparam-input/ngparam-input.component.scss
@@ -0,0 +1,23 @@
+:host {
+    display: block;
+    margin-top: 14px;
+}
+
+mat-form-field {
+    width: calc(100% - 16px);
+    margin-right: 16px;
+    
+    ::ng-deep input.mat-input-element {
+        line-height: 1.3em;
+    }
+
+    ::ng-deep .mat-form-field-label {
+        font-size: 1.1em;
+        line-height: 1.4em;
+        margin-top: -2px;
+
+        &.mat-form-field-empty {
+            font-size: 1em;
+        }
+    }
+}
diff --git a/src/app/components/ngparam-input/ngparam-input.component.ts b/src/app/components/ngparam-input/ngparam-input.component.ts
index 44ba1ed15637c76ef47855b772446d73eecc82d6..7e1af55cead345ce13871671d3174e359cfbc07f 100644
--- a/src/app/components/ngparam-input/ngparam-input.component.ts
+++ b/src/app/components/ngparam-input/ngparam-input.component.ts
@@ -2,15 +2,18 @@
 
 import { Component, ChangeDetectorRef, OnDestroy } from "@angular/core";
 
-import { isNumeric, Message, Observer } from "jalhyd";
+import { Message, Observer } from "jalhyd";
 
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
 import { NgParameter } from "../../formulaire/ngparam";
 import { GenericInputComponent } from "../generic-input/generic-input.component";
 
 @Component({
     selector: "ngparam-input",
-    templateUrl: "../generic-input/generic-input.component.html"
+    templateUrl: "../generic-input/generic-input.component.html",
+    styleUrls: [
+        "./ngparam-input.component.scss"
+    ]
 })
 export class NgParamInputComponent extends GenericInputComponent implements Observer, OnDestroy {
     /**
@@ -26,7 +29,7 @@ export class NgParamInputComponent extends GenericInputComponent implements Obse
      */
     private _tmp: number;
 
-    constructor(private intlService: InternationalisationService, cdRef: ChangeDetectorRef) {
+    constructor(private intlService: I18nService, cdRef: ChangeDetectorRef) {
         super(cdRef);
     }
 
@@ -86,27 +89,6 @@ export class NgParamInputComponent extends GenericInputComponent implements Obse
         return { isValid: valid, message: msg };
     }
 
-    protected modelToUI(v: any): string {
-        return String(v);
-    }
-
-    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
-        let valid = false;
-        let msg: string;
-
-        if (! isNumeric(ui)) {
-            msg = "Veuillez entrer une valeur numérique";
-        } else {
-            valid = true;
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected uiToModel(ui: string) {
-        return +ui;
-    }
-
     public update(sender: any, data: any): void {
         switch (data["action"]) {
             case "ngparamAfterValue":
diff --git a/src/app/components/param-computed/param-computed.component.html b/src/app/components/param-computed/param-computed.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..0751e36bf1dc93de19a451f8aefd886085dc6130
--- /dev/null
+++ b/src/app/components/param-computed/param-computed.component.html
@@ -0,0 +1,7 @@
+<!-- a fake input bound to nothing, for the sake of UI consistency -->
+<mat-form-field>
+    <input matInput disabled class="form-control" type="text" [ngModel]="infoText" [placeholder]="param.title">
+    <button *ngIf="isDicho" mat-icon-button class="param-computed-more" (click)="openDialog()">
+        <mat-icon>more_horiz</mat-icon>
+    </button>
+</mat-form-field>
diff --git a/src/app/components/param-computed/param-computed.component.scss b/src/app/components/param-computed/param-computed.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..40469133691fccaf31a9664ceb49d99627bf7b3e
--- /dev/null
+++ b/src/app/components/param-computed/param-computed.component.scss
@@ -0,0 +1,31 @@
+:host {
+    display: block;
+    margin-top: 14px;
+
+    mat-form-field {
+        width: calc(100% - 16px);
+        margin-right: 16px;
+
+        ::ng-deep input.mat-input-element {
+            line-height: 1.3em;
+            width: calc(100% - 40px);
+            text-overflow: ellipsis;
+        }
+
+        .param-computed-more {
+            position: absolute;
+            bottom: 0;
+            right: 0;
+        }
+
+        ::ng-deep .mat-form-field-label {
+            font-size: 1.1em;
+            line-height: 1.4em;
+            margin-top: -2px;
+    
+            &.mat-form-field-empty {
+                font-size: 1em;
+            }
+        }
+    }
+}
diff --git a/src/app/components/param-computed/param-computed.component.ts b/src/app/components/param-computed/param-computed.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6bfced00bab90372af36a4d7946a5aee44dae579
--- /dev/null
+++ b/src/app/components/param-computed/param-computed.component.ts
@@ -0,0 +1,53 @@
+import { Component, Input } from "@angular/core";
+import { MatDialog } from "@angular/material";
+import { NgParameter } from "../../formulaire/ngparam";
+import { ParamCalculability } from "jalhyd";
+import { DialogEditParamComputedComponent } from "../dialog-edit-param-computed/dialog-edit-param-computed.component";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
+
+@Component({
+    selector: "param-computed",
+    templateUrl: "./param-computed.component.html",
+    styleUrls: [
+        "./param-computed.component.scss"
+    ]
+})
+export class ParamComputedComponent {
+
+    @Input()
+    public param: NgParameter;
+
+    @Input()
+    public title: string;
+
+    constructor(
+        private editInitialValueDialog: MatDialog,
+        private intlService: I18nService
+    ) { }
+
+    public get isDicho() {
+        return this.param.paramDefinition.calculability === ParamCalculability.DICHO;
+    }
+
+    public get infoText() {
+        let text = this.intlService.localizeText("INFO_PARAMFIELD_IN_CALCULATION");
+        if (this.isDicho) {
+            text += " (" + this.intlService.localizeText("INFO_PARAMFIELD_IN_CALCULATION_INITIAL_VALUE")
+                + ": " + this.param.getValue() + ")";
+        }
+        return text;
+    }
+
+    public openDialog() {
+        // modification de la valeur initiale, sans avoir à remettre le mode de
+        // paramètre sur "fixé"
+        this.editInitialValueDialog.open(
+            DialogEditParamComputedComponent,
+            {
+                data: {
+                    param: this.param
+                }
+            }
+        );
+    }
+}
diff --git a/src/app/components/param-field-line/param-field-line.component.html b/src/app/components/param-field-line/param-field-line.component.html
index 41c5d1fead0e3e3405def7577ef33328870a42f3..f1ea01b5c1bd30da5d008c0a6a00601f180d398d 100644
--- a/src/app/components/param-field-line/param-field-line.component.html
+++ b/src/app/components/param-field-line/param-field-line.component.html
@@ -1,43 +1,49 @@
-<div class="row">
-    <!-- input de saisie de la valeur -->
-    <div [ngClass]="(formHasResults) ? 'col-xl-6 pt-3':'col-md-6 col-xl-8 pt-3'">
-        <ngparam-input [_inputDisabled]="isInputDisabled" [title]="title" (change)="onInputChange($event)"></ngparam-input>
-    </div>
 
-    <div class="btn-group col" role="group">
-        <!-- px-3 : padding left/right 3 -->
-        <!-- py-3 : padding top/bottom 3 -->
-        <!-- h-50 : hauteur relative de l'élément par rapport au parent à 50%-->
-        <!-- cf. https://getbootstrap.com/docs/4.0/utilities/spacing -->
+<div class="container" fxLayout="row wrap" fxLayoutAlign="space-between start">
+
+    <!-- input de saisie de la valeur -->
+    <div fxFlex="1 0 120px">
+        <!-- composant pour gérer le cas général (valeur numérique à saisir) -->
+        <ngparam-input [title]="param.title" [hidden]="! isRadioFixChecked" (change)="onInputChange($event)"></ngparam-input>
+ 
+        <!-- composant pour gérer le cas "paramètre calculé" -->
+        <param-computed *ngIf="isRadioCalChecked" [title]="title" [param]="param"></param-computed>
 
-        <!-- radio "fixé" -->
-        <label *ngIf="hasRadioFix()" class="{{radioFixClass}} h-75 px-3 py-3" [(ngModel)]="radioModel" mdbRadio="Left" name="radio_param_{{symbol}}"
-            value="fix" (click)="onRadioClick('fix')" [checked]=radioFixCheck [disabled]=isDisabled id="radio_fix">
-            {{ uitextParamFixe }}
-        </label>
+        <!-- composant pour gérer le cas "paramètre à varier" (min-max/liste de valeurs) -->
+        <param-values *ngIf="isRadioVarChecked" [title]="title" [param]="param" (valid)=onParamValuesValid($event)></param-values>
+    
+        <!-- composant pour gérer le cas "paramètre lié" -->
+        <param-link *ngIf="isRadioLinkChecked" [title]="title" [param]="param" (valid)=onParamValuesValid($event)></param-link>
+    </div>
 
-        <!-- radio "varier" -->
-        <label *ngIf="hasRadioVar()" class="{{radioVarClass}} h-75 px-3 py-3" [(ngModel)]="radioModel" mdbRadio="Middle" name="radio_param_{{symbol}}"
-            value="var" (click)="onRadioClick('var')" [checked]=radioVarCheck [disabled]=isDisabled id="radio_var">
-            {{ uitextParamVarier }}
-        </label>
+    <div class="toggle-group-container" fxFlex="0 0 auto">
+        <mat-button-toggle-group *ngIf="hasRadioFix() || hasRadioVar() || hasRadioCal() || hasRadioLink()">
 
-        <!-- radio "calculer" -->
-        <label *ngIf="hasRadioCal()" class="{{radioCalClass}} h-75 px-3 py-3" [(ngModel)]="radioModel" mdbRadio="Right" name="radio_param_{{symbol}}"
-            value="cal" (click)="onRadioClick('cal')" [checked]=radioCalCheck [disabled]=isDisabled id="radio_cal">
-            {{ uitextParamCalculer }}
-        </label>
+            <mat-button-toggle id="radio_fix" value="radio_fix" 
+                (click)="onRadioClick('fix')" [checked]="isRadioFixChecked">
+                <span fxHide.xxs>{{ uitextParamFixe }}</span>
+                <span fxHide.gt-xxs>F</span>
+            </mat-button-toggle>
+        
+            <mat-button-toggle id="radio_var" value="radio_var" *ngIf="hasRadioVar()"
+                (click)="onRadioClick('var')" [checked]="isRadioVarChecked">
+                <span fxHide.xxs>{{ uitextParamVarier }}</span>
+                <span fxHide.gt-xxs>V</span>
+            </mat-button-toggle>
+        
+            <mat-button-toggle id="radio_cal" value="radio_cal" *ngIf="hasRadioCal()"
+                (click)="onRadioClick('cal')" [checked]="isRadioCalChecked">
+                <span fxHide.xxs>{{ uitextParamCalculer }}</span>
+                <span fxHide.gt-xxs>C</span>
+            </mat-button-toggle>
+        
+            <mat-button-toggle id="radio_link" value="radio_link" *ngIf="hasRadioLink()"
+                (click)="onRadioClick('link')" [checked]="isRadioLinkChecked">
+                <span fxHide.xxs>{{ uitextParamLie }}</span>
+                <span fxHide.gt-xxs>L</span>
+            </mat-button-toggle>
 
-        <!-- radio "lié" -->
-        <label *ngIf="hasRadioLink()" class="{{radioLinkClass}} h-75 px-3 py-3" [(ngModel)]="radioModel" mdbRadio="Right" name="radio_param_{{symbol}}"
-            value="link" (click)="onRadioClick('link')" [checked]=radioLinkCheck [disabled]=isDisabled id="radio_link">
-            {{ uitextParamLie }}
-        </label>
+        </mat-button-toggle-group>
     </div>
-</div>
 
-<!-- composant pour gérer le cas "paramètre à varier" (min-max/liste de valeurs) -->
-<param-values *ngIf="isRadioVarChecked" [param]="param" (valid)=onParamValuesValid($event)></param-values>
-
-<!-- composant pour gérer le cas "paramètre lié" -->
-<param-link *ngIf="isRadioLinkChecked" [param]="param" (valid)=onParamValuesValid($event)></param-link>
\ No newline at end of file
+</div>
diff --git a/src/app/components/param-field-line/param-field-line.component.scss b/src/app/components/param-field-line/param-field-line.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..4cfe3dad92ebfe608a3bef8eda844f0af394d3f4
--- /dev/null
+++ b/src/app/components/param-field-line/param-field-line.component.scss
@@ -0,0 +1,13 @@
+.toggle-group-container {
+    text-align: right;
+}
+
+mat-button-toggle-group {
+    margin-top: 4px;
+    box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12);
+
+    /*::ng-deep .mat-button-toggle-label-content {
+        line-height: 32px;
+        padding: 0 10px;
+    }*/
+}
diff --git a/src/app/components/param-field-line/param-field-line.component.ts b/src/app/components/param-field-line/param-field-line.component.ts
index 6282e176e28510041610c853da5c6eedc24cb1f3..435f63cfbf3c00df06a53b986e5008c731abc8d3 100644
--- a/src/app/components/param-field-line/param-field-line.component.ts
+++ b/src/app/components/param-field-line/param-field-line.component.ts
@@ -1,53 +1,34 @@
 import { Component, ViewChild, Input, Output, EventEmitter, OnChanges } from "@angular/core";
 
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
 import { NgParameter, ParamRadioConfig } from "../../formulaire/ngparam";
 import { NgParamInputComponent } from "../ngparam-input/ngparam-input.component";
 import { ServiceFactory } from "../../services/service-factory";
 import { ParamValueMode, CalculatorType, ParallelStructure } from "jalhyd";
 import { FormulaireService } from "../../services/formulaire/formulaire.service";
 import { ParamLinkComponent } from "../param-link/param-link.component";
+import { ParamComputedComponent } from "../param-computed/param-computed.component";
+import { ParamValuesComponent } from "../param-values/param-values.component";
 
+/**
+ * Sélecteur de mode pour chaque paramètre: fixé, varier, calculer, lié
+ */
 @Component({
     selector: "param-field-line",
     templateUrl: "./param-field-line.component.html",
-    styles: [
-        `.btn-on {
-            color:#505050;
-            border: 3px solid #505050;
-            background-color:white;
-            text-transform: uppercase;
-            font-size: 0.8em;
-        }`,
-        `.btn-off {
-            color:white;
-            border: 3px solid #505050;
-            background-color:#505050;
-            text-transform: uppercase;
-            font-size: 0.8em;
-        }`
+    styleUrls: [
+        "./param-field-line.component.scss"
     ]
 })
 export class ParamFieldLineComponent implements OnChanges {
 
     constructor() {
-        this.intlService = ServiceFactory.instance.internationalisationService;
+        this.intlService = ServiceFactory.instance.i18nService;
         this._formService = ServiceFactory.instance.formulaireService;
         this.valid = new EventEmitter();
         this.inputChange = new EventEmitter();
     }
 
-    public get title(): string {
-        let t = "";
-        if (this.param.label !== undefined) {
-            t = this.param.label;
-        }
-        if (this.param.unit !== undefined && this.param.unit !== "") {
-            t = t + " (" + this.param.unit + ")";
-        }
-        return t;
-    }
-
     private get uitextParamFixe() {
         return this.intlService.localizeText("INFO_PARAMFIELD_PARAMFIXE");
     }
@@ -71,112 +52,36 @@ export class ParamFieldLineComponent implements OnChanges {
         return this.param.symbol;
     }
 
-    /**
-    * calcule l'état du radio "paramètre fixé"
-    */
+    // états des boutons pour l'interface
+
     private get radioFixCheck(): string {
         return this.isRadioFixChecked ? "checked" : undefined;
     }
-
-    /**
-    * calcule l'état du radio "paramètre à varier"
-    */
     private get radioVarCheck(): string {
         return this.isRadioVarChecked ? "checked" : undefined;
     }
-
-    /**
-    * calcule l'état du radio "paramètre à calculer"
-    */
     private get radioCalCheck(): string {
-        if (this.param.radioState === ParamRadioConfig.CAL) {
-            return "checked";
-        }
-        return undefined;
+        return this.isRadioCalChecked ? "checked" : undefined;
     }
-
-    /**
-    * calcule l'état du radio "paramètre lié"
-    */
     private get radioLinkCheck(): string {
-        if (this.param.radioState === ParamRadioConfig.LINK) {
-            return "checked";
-        }
-        return undefined;
+        return this.isRadioLinkChecked ? "checked" : undefined;
     }
 
-    /**
-     * retourne l'état du radio "paramètre fixé" sous forme booléenne
-     */
+    // états booléens des boutons
+
     public get isRadioFixChecked(): boolean {
         return this.param.radioState === ParamRadioConfig.FIX;
     }
-
-    /**
-     * retourne l'état du radio "paramètre à varier" sous forme booléenne
-     */
     public get isRadioVarChecked(): boolean {
         return this.param.radioState === ParamRadioConfig.VAR;
     }
-
-    /**
-     * retourne l'état du radio "paramètre lié" sous forme booléenne
-     */
+    public get isRadioCalChecked(): boolean {
+        return this.param.radioState === ParamRadioConfig.CAL;
+    }
     public get isRadioLinkChecked(): boolean {
         return this.param.radioState === ParamRadioConfig.LINK;
     }
 
-    /**
-     * désactivation de tous les boutons radio si paramètre par défaut à "CAL"
-     */
-    private get isDisabled(): boolean {
-        return this.param.isDefault && this.param.radioState === ParamRadioConfig.CAL;
-    }
-
-    /**
-     * désactivation du champ de saisie
-     */
-    public get isInputDisabled(): boolean {
-        return this.param.radioState !== ParamRadioConfig.FIX;
-    }
-
-    private get radioFixClass(): string {
-        if (this.on) {
-            return this.radioFixCheck ? this.onClass : this.offClass;
-        }
-        return "";
-    }
-
-    /**
-     * classe du radio "varier"
-     */
-    private get radioVarClass(): string {
-        if (this.on) {
-            return this.radioVarCheck ? this.onClass : this.offClass;
-        }
-        return "";
-    }
-
-    /**
-     * classe du radio "calculer"
-     */
-    private get radioCalClass(): string {
-        if (this.on) {
-            return this.radioCalCheck ? this.onClass : this.offClass;
-        }
-        return "";
-    }
-
-    /**
-     * classe du radio "lié"
-     */
-    private get radioLinkClass(): string {
-        if (this.on) {
-            return this.radioLinkCheck ? this.onClass : this.offClass;
-        }
-        return "";
-    }
-
     /**
      * validité des saisies du composant
      */
@@ -188,6 +93,10 @@ export class ParamFieldLineComponent implements OnChanges {
             case ParamRadioConfig.VAR:
                 return this._isRangeValid;
 
+            case ParamRadioConfig.LINK:
+                // at first this._paramLinkComponent is undefined until view is refreshed
+                return this._paramLinkComponent ? this._paramLinkComponent.isValid : true;
+
             default:
                 return true;
         }
@@ -198,11 +107,17 @@ export class ParamFieldLineComponent implements OnChanges {
     }
 
     @Input()
-    private param: NgParameter;
+    public param: NgParameter;
 
     @ViewChild(NgParamInputComponent)
     private _ngParamInputComponent: NgParamInputComponent;
 
+    @ViewChild(ParamComputedComponent)
+    private _paramComputedComponent: ParamComputedComponent;
+
+    @ViewChild(ParamValuesComponent)
+    private _paramValuesComponent: ParamValuesComponent;
+
     @ViewChild(ParamLinkComponent)
     private _paramLinkComponent: ParamLinkComponent;
 
@@ -212,17 +127,13 @@ export class ParamFieldLineComponent implements OnChanges {
     @Output()
     private inputChange: EventEmitter<void>;
 
-    /**
-     * true si la valeur saisie est valide
-     */
+    /** true si la valeur saisie est valide */
     private _isInputValid = false;
 
-    /**
-     * true si le min-max/liste est valide
-     */
+    /** true si le min-max/liste est valide */
     private _isRangeValid = true;
 
-    private intlService: InternationalisationService;
+    private intlService: I18nService;
 
     private _formService: FormulaireService;
 
@@ -231,7 +142,6 @@ export class ParamFieldLineComponent implements OnChanges {
      * envoi d'un message au composant parent
      * cf. https://angular.io/guide/component-interaction#parent-listens-for-child-event
      */
-
     @Output()
     private radio = new EventEmitter<any>();
 
@@ -248,7 +158,7 @@ export class ParamFieldLineComponent implements OnChanges {
     public hasRadioFix(): boolean {
         switch (this.param.radioConfig) {
             case ParamRadioConfig.FIX:
-                return this.hasRadioLink();
+                return this.hasRadioLink(); // gné ?
 
             default:
                 return true;
@@ -283,16 +193,16 @@ export class ParamFieldLineComponent implements OnChanges {
     }
 
     /**
-    * calcule la présence du radio "paramètre lié" (importé d'une autre calculette)
+    * calcule la présence du radio "paramètre lié" (importé d'un autre module de calcul)
     */
    public hasRadioLink(): boolean {
         if (this._formService.formulaires.length > 0) {
-            // au moins 2 calculettes ouvertes
+            // au moins 2 modules de calcul ouverts
             if (this._formService.formulaires.length > 1) {
                 return this._formService.filterLinkableValues(this._formService.getLinkableValues(this.param)).length > 0;
             }
 
-            // ou une seule calculette "ouvrages parallèles"
+            // ou un seul module de calcul "ouvrages parallèles"
             if (this._formService.formulaires[0].calculatorType === CalculatorType.ParallelStructure) {
                 const ps: ParallelStructure = this._formService.formulaires[0].currentNub as ParallelStructure;
                 if (ps.structures.length > 1) {
@@ -306,16 +216,23 @@ export class ParamFieldLineComponent implements OnChanges {
 
     private onRadioClick(option: string) {
         const oldValue = this.param.valueMode;
-
         switch (option) {
             case "fix":
-                const oldValueMode = this.param.valueMode;
                 this.param.valueMode = ParamValueMode.SINGLE;
+                // @WTF why do we reset the value here ?
                 this.param.setValue(this, this.param.paramDefinition.paramValues.singleValue);
                 break;
 
             case "var":
-                this.param.valueMode = ParamValueMode.MINMAX; // min/max par défaut
+                // prevent setting LISTE mode back to MINMAX if someone clicks "variable" again
+                // after setting variable mode to LISTE
+                if (oldValue !== ParamValueMode.MINMAX && oldValue !== ParamValueMode.LISTE) {
+                    this.param.valueMode = ParamValueMode.MINMAX; // min/max par défaut
+                }
+                if (this._paramValuesComponent) {
+                    // re-open modal when clicking the "var" mode button again (PoLS)
+                    this._paramValuesComponent.openDialog();
+                }
                 break;
 
             case "cal":
@@ -326,12 +243,10 @@ export class ParamFieldLineComponent implements OnChanges {
                 this.param.valueMode = ParamValueMode.LINK;
                 break;
         }
-
         this.radio.emit({
             "param": this.param,
             "oldValueMode": oldValue
         });
-
         // MAJ validité
         this.emitValidity();
     }
diff --git a/src/app/components/param-link/param-link.component.html b/src/app/components/param-link/param-link.component.html
index f7289b7393df1263526546072753c8c72459fd87..5b198816bf96e4b7b3df1aec2f428f7e681d1ec2 100644
--- a/src/app/components/param-link/param-link.component.html
+++ b/src/app/components/param-link/param-link.component.html
@@ -1,13 +1,10 @@
-<div class="row">
-    <div class="btn-group col-6 col-sm-3" dropdown (click)="onSelectLinkableParam($event)">
-        <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius>
-            {{ currentLinkedParamLabel }}
-        </button>
-        <div class="dropdown-menu">
-            <a class="dropdown-item" *ngFor="let e of linkableParams" [value]=e>{{ selectItemLabel(e) }}</a>
-        </div>
-    </div>
-    <div class="col-6 text-danger">
+<mat-form-field>
+    <mat-select [name]='"linked-param_" + param.uid' required [placeholder]="param.title" [(ngModel)]="currentLinkedParam">
+        <mat-option *ngFor="let e of linkableParams" [value]="e">
+            {{ selectItemLabel(e) }}
+        </mat-option>
+    </mat-select>
+    <mat-error>
         {{ message }}
-    </div>
-</div>
\ No newline at end of file
+    </mat-error>
+</mat-form-field>
diff --git a/src/app/components/param-link/param-link.component.scss b/src/app/components/param-link/param-link.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..55d123d5ac4e3b9be8aa5a87b3d49a9ba0ec92d7
--- /dev/null
+++ b/src/app/components/param-link/param-link.component.scss
@@ -0,0 +1,33 @@
+:host {
+    display: block;
+    // width: 100%;
+    // width: 70%; min-width: 264px; // for smallest screens (360)
+    // get the select closer to the related param line above
+    margin-top: 14px;
+
+    mat-form-field {
+        width: calc(100% - 16px);
+        margin-right: 16px;
+
+        mat-select {
+    
+            ::ng-deep .mat-select-value {
+                > span {
+                    > span {
+                        line-height: 1.3em;
+                    }
+                }
+            }
+        }
+
+        ::ng-deep .mat-form-field-label {
+            font-size: 1.1em;
+            line-height: 1.4em;
+            margin-top: -2px;
+    
+            &.mat-form-field-empty {
+                font-size: 1em;
+            }
+        }
+    }
+}
diff --git a/src/app/components/param-link/param-link.component.ts b/src/app/components/param-link/param-link.component.ts
index 256558035ff3170f49f973fea7332db36e221de4..9e28788f706eecf48a47c5836360e400ac127a98 100644
--- a/src/app/components/param-link/param-link.component.ts
+++ b/src/app/components/param-link/param-link.component.ts
@@ -4,15 +4,22 @@ import { NgParameter } from "../../formulaire/ngparam";
 import { ServiceFactory } from "../../services/service-factory";
 import { ParamValueMode, Observer } from "jalhyd";
 import { FormulaireService } from "../../services/formulaire/formulaire.service";
+import { I18nService } from "../..//services/internationalisation/internationalisation.service";
 
 @Component({
     selector: "param-link",
-    templateUrl: "./param-link.component.html"
+    templateUrl: "./param-link.component.html",
+    styleUrls: [
+        "./param-link.component.scss"
+    ]
 })
 export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
     // paramètre géré (qui sera lié à une valeur, cad qui importe cette valeur)
     @Input()
-    private param: NgParameter;
+    public param: NgParameter;
+
+    @Input()
+    public title: string;
 
     @Output()
     private valid: EventEmitter<boolean>;
@@ -29,7 +36,7 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
 
     /**
      * liste des paramètres liables sous la forme
-     * {"name":<étiquette>, "value":<valeur liable>, "nub":<Nub d'origine du paramètre>, "formTitle":<nom de la calculette liée au nub>}
+     * {"name":<étiquette>, "value":<valeur liable>, "nub":<Nub d'origine du paramètre>, "formTitle":<nom du module de calcul lié au nub>}
      *
      * l'étiquette "name" (cf. INubReference.defineReference dans jalhyd) est de la forme <n | ouvrage[n] | N1>[.[N2]]
      *     n : indice de de l'ouvrage dans le cas des ouvrages parallèles
@@ -47,7 +54,9 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
 
     private _formService: FormulaireService;
 
-    constructor() {
+    constructor(
+        private intlService: I18nService
+    ) {
         this.valid = new EventEmitter();
         this._formService = ServiceFactory.instance.formulaireService;
         this._formService.addObserver(this);
@@ -61,27 +70,25 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
         return this._message;
     }
 
-    /**
-     * envoi d'un événement de validité
-     */
-    private emitValidity() {
-        // this.valid.emit(this._validList);
+    public get label() {
+        return this.intlService.localizeText("INFO_PARAMFIELD_PARAMLIE_LABEL");
     }
 
-    /**
-     * réception d'un événement du menu des paramètres liables
-     */
-    public onSelectLinkableParam(event: any) {
-        const next = event.target.value;
-        if (next !== undefined && next !== "") { // opening the dropdown returns ""
-            let i = 0;
-            for (const e of this._linkableParams) {
-                if (this._linkableParams[i].value.uid === next.value.uid) {
-                    this.linkTo(i);
-                    break;
-                } else {
-                    i++;
-                }
+    public set currentLinkedParam(p: any) {
+        for (let i = 0; i < this._linkableParams.length; i++) {
+            if (this._linkableParams[i].value.uid === p.uid) {
+                this.linkTo(i);
+                break;
+            } else {
+                i++;
+            }
+        }
+    }
+
+    public get currentLinkedParam() {
+        if (this._linkableParams !== undefined) {
+            if (this._currentIndex !== -1 && this._currentIndex < this._linkableParams.length) {
+                return this._linkableParams[this._currentIndex];
             }
         }
     }
@@ -90,15 +97,14 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
      * valeur courante affichée dans le select des paramètres liables
      */
     public get currentLinkedParamLabel(): string {
-        if (this._linkableParams !== undefined) {
-            if (this._currentIndex === -1 || this._currentIndex >= this._linkableParams.length) {
-                return undefined;
-            }
-
-            return this.selectItemLabel(this._linkableParams[this._currentIndex]);
+        const clp = this.currentLinkedParam();
+        if (clp) {
+            return this.selectItemLabel(clp);
         }
+    }
 
-        return undefined;
+    public get isValid(): boolean {
+        return this._currentIndex !== -1 && this.param.isValid;
     }
 
     /**
@@ -106,7 +112,7 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
      */
     private selectItemLabel(i: any) {
         const s = i.name; // nom associé au paramètre/à la valeur
-        const c = i.formTitle; // nom de la calculette
+        const c = i.formTitle; // nom du module de calcul
 
         const re5 = /(\d+)\.(.+)\.$/;  // forme <nombre>.xxx. (résultat d'ouvrage)
         const match5 = re5.exec(s);
@@ -208,6 +214,7 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
     public update(sender: any, data: any) {
         if (sender instanceof FormulaireService) {
             switch (data["action"]) {
+                // scan newly created form for available linkable params
                 case "createForm":
                     this.updateParamList();
                     break;
diff --git a/src/app/components/param-values/ngparam-max.component.ts b/src/app/components/param-values/ngparam-max.component.ts
deleted file mode 100644
index de13b6e24272dea9836d02097446c03b202ec1e0..0000000000000000000000000000000000000000
--- a/src/app/components/param-values/ngparam-max.component.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-import { Component, Input, ChangeDetectorRef } from "@angular/core";
-
-import { isNumeric } from "jalhyd";
-
-import { GenericInputComponent } from "../generic-input/generic-input.component";
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
-import { NgParameter } from "../../formulaire/ngparam";
-
-@Component({
-    selector: "ngparam-max",
-    templateUrl: "../generic-input/generic-input.component.html"
-})
-export class NgParamMaxComponent extends GenericInputComponent {
-    constructor(private intlService: InternationalisationService, cdRef: ChangeDetectorRef) {
-        super(cdRef);
-    }
-
-    /**
-     * paramètre géré
-     */
-    private get _param(): NgParameter {
-        return this._model;
-    }
-
-    protected getModelValue(): any {
-        if (this._param === undefined) {
-            return undefined;
-        }
-        return this._param.maxValue;
-    }
-
-    protected setModelValue(sender: any, v: any) {
-        this._param.maxValue = v;
-    }
-
-    protected validateModelValue(v: any): { isValid: boolean, message: string } {
-        let msg;
-        let valid = false;
-
-        if (this._param === undefined) {
-            msg = "internal error, model undefined";
-        } else {
-            if (!this._param.checkMax(v)) {
-                msg = "La valeur n'est pas dans ]" + this._param.minValue + " , " + this._param.domain.maxValue + "]";
-            } else {
-                valid = true;
-            }
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected modelToUI(v: any): string {
-        if (typeof (v) === "number") {
-            return String(v);
-        }
-        return undefined;
-    }
-
-    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
-        let valid = false;
-        let msg: string;
-
-        if (! isNumeric(ui)) {
-            msg = "Veuillez entrer une valeur numérique";
-        } else {
-            valid = true;
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected uiToModel(ui: string) {
-        return +ui;
-    }
-}
diff --git a/src/app/components/param-values/ngparam-min.component.ts b/src/app/components/param-values/ngparam-min.component.ts
deleted file mode 100644
index 89e119f03533bf517b83940c73f6db176b192270..0000000000000000000000000000000000000000
--- a/src/app/components/param-values/ngparam-min.component.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-import { Component, Input, ChangeDetectorRef } from "@angular/core";
-
-import { isNumeric } from "jalhyd";
-
-import { GenericInputComponent } from "../generic-input/generic-input.component";
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
-import { NgParameter } from "../../formulaire/ngparam";
-
-@Component({
-    selector: "ngparam-min",
-    templateUrl: "../generic-input/generic-input.component.html"
-})
-export class NgParamMinComponent extends GenericInputComponent {
-    constructor(private intlService: InternationalisationService, cdRef: ChangeDetectorRef) {
-        super(cdRef);
-    }
-
-    /**
-     * paramètre géré
-     */
-    private get _param(): NgParameter {
-        return this._model;
-    }
-
-    protected getModelValue(): any {
-        if (this._param === undefined) {
-            return undefined;
-        }
-        return this._param.minValue;
-    }
-
-    protected setModelValue(sender: any, v: any) {
-        this._param.minValue = v;
-    }
-
-    protected validateModelValue(v: any): { isValid: boolean, message: string } {
-        let msg: string;
-        let valid = false;
-
-        if (this._param === undefined) {
-            msg = "internal error, model undefined";
-        } else {
-            if (!this._param.checkMin(v)) {
-                msg = "La valeur n'est pas dans [" + this._param.domain.minValue + " , " + this._param.maxValue + "[";
-            } else {
-                valid = true;
-            }
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected modelToUI(v: any): string {
-        if (typeof (v) === "number") {
-            return String(v);
-        }
-        return undefined;
-    }
-
-    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
-        let valid = false;
-        let msg: string;
-
-        if (! isNumeric(ui)) {
-            msg = "Veuillez entrer une valeur numérique";
-        } else {
-            valid = true;
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected uiToModel(ui: string): any {
-        return +ui;
-    }
-}
diff --git a/src/app/components/param-values/ngparam-step.component.ts b/src/app/components/param-values/ngparam-step.component.ts
deleted file mode 100644
index bbddcebf2f27f0a2ac55e75d3957ebf292283e2e..0000000000000000000000000000000000000000
--- a/src/app/components/param-values/ngparam-step.component.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import { Component, Input, ChangeDetectorRef } from "@angular/core";
-
-import { isNumeric } from "jalhyd";
-
-import { GenericInputComponent } from "../generic-input/generic-input.component";
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
-import { NgParameter } from "../../formulaire/ngparam";
-
-@Component({
-    selector: "ngparam-step",
-    templateUrl: "../generic-input/generic-input.component.html"
-})
-export class NgParamStepComponent extends GenericInputComponent {
-    constructor(private intlService: InternationalisationService, cdRef: ChangeDetectorRef) {
-        super(cdRef);
-    }
-
-    /**
-     * paramètre géré
-     */
-    private get _param(): NgParameter {
-        return this._model;
-    }
-
-    protected getModelValue(): any {
-        if (! this._param) {
-            return undefined;
-        }
-        return this._param.stepValue;
-    }
-
-    protected setModelValue(sender: any, v: any) {
-        this._param.stepValue = v;
-    }
-
-    protected validateModelValue(v: any): { isValid: boolean, message: string } {
-        let msg: string;
-        let valid = false;
-
-        if (! this._param) {
-            msg = "internal error, model undefined";
-        } else {
-            if (this._param.isMinMaxValid) {
-                if (!this._param.checkStep(v)) {
-                    msg = "La valeur n'est pas dans " + this._param.stepRefValue.toString();
-                } else {
-                    valid = v > 0;
-                    if (!valid) {
-                        msg = "La valeur ne peut pas être <= 0";
-                    }
-                }
-            } else {
-                msg = "Veuillez corriger le min/max";
-            }
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected modelToUI(v: any): string {
-        if (typeof (v) === "number") {
-            return String(v);
-        }
-        return "<invalid>";
-    }
-
-    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
-        let valid = false;
-        let msg: string;
-
-        if (! isNumeric(ui)) {
-            msg = "Veuillez entrer une valeur numérique";
-        } else {
-            valid = true;
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected uiToModel(ui: string) {
-        return +ui;
-    }
-}
diff --git a/src/app/components/param-values/param-values.component.html b/src/app/components/param-values/param-values.component.html
index f4dc496878e94c63d55d6e5e1b59bc277430f16d..37cda318bfbb8ac4581d6f45ab2188656e7f89fd 100644
--- a/src/app/components/param-values/param-values.component.html
+++ b/src/app/components/param-values/param-values.component.html
@@ -1,24 +1,7 @@
-<div class="row">
-    <div class="btn-group col-12 col-sm-3" dropdown (click)="onSelectValueMode($event)">
-        <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius>
-            {{ currentModeSelectLabel }}
-        </button>
-        <div class="dropdown-menu">
-            <a class="dropdown-item" *ngFor="let e of valueModes" [value]=e.value>{{ e.label }}</a>
-        </div>
-    </div>
-
-    <div *ngIf="isMinMax" class="col-12 col-sm-3">
-        <ngparam-min [title]="uitextValeurMini" (onChange)="onMinChanged($event)"></ngparam-min>
-    </div>
-    <div *ngIf="isMinMax" class="col-12 col-sm-3">
-        <ngparam-max [title]="uitextValeurMaxi" (onChange)="onMaxChanged($event)"></ngparam-max>
-    </div>
-    <div *ngIf="isMinMax" class="col-12 col-sm-3">
-        <ngparam-step [title]="uitextPasVariation" [param]="param" (onChange)="onStepChanged($event)"></ngparam-step>
-    </div>
-
-    <div *ngIf="isList" class="col-12 col-sm-6">
-        <value-list title="valeurs séparées par ';'" (onChange)="onListChanged($event)"></value-list>
-    </div>
-</div>
\ No newline at end of file
+<!-- a fake input bound to nothing, for the sake of UI consistency -->
+<mat-form-field>
+    <input matInput disabled class="form-control" type="text" [ngModel]="infoText" [placeholder]="param.title">
+    <button mat-icon-button class="param-values-more" (click)="openDialog()">
+        <mat-icon>more_horiz</mat-icon>
+    </button>
+</mat-form-field>
diff --git a/src/app/components/param-values/param-values.component.scss b/src/app/components/param-values/param-values.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..1edcbe0ed9b4f6fe9e3ae7c76035729ac1bc1384
--- /dev/null
+++ b/src/app/components/param-values/param-values.component.scss
@@ -0,0 +1,31 @@
+:host {
+    display: block;
+    margin-top: 14px;
+
+    mat-form-field {
+        width: calc(100% - 16px);
+        margin-right: 16px;
+
+        ::ng-deep input.mat-input-element {
+            line-height: 1.3em;
+            width: calc(100% - 40px);
+            text-overflow: ellipsis;
+        }
+
+        .param-values-more {
+            position: absolute;
+            bottom: 0;
+            right: 0;
+        }
+
+        ::ng-deep .mat-form-field-label {
+            font-size: 1.1em;
+            line-height: 1.4em;
+            margin-top: -2px;
+    
+            &.mat-form-field-empty {
+                font-size: 1em;
+            }
+        }
+    }
+}
diff --git a/src/app/components/param-values/param-values.component.ts b/src/app/components/param-values/param-values.component.ts
index ef8c20a92bf590e6a63c3ad1d25c01ab051dd581..9a8f34c34252f0afb4bed6171d83232ba8c4c816 100644
--- a/src/app/components/param-values/param-values.component.ts
+++ b/src/app/components/param-values/param-values.component.ts
@@ -1,361 +1,89 @@
-import { Component, Input, Output, EventEmitter, ViewChild, AfterViewChecked, OnChanges } from "@angular/core";
-
-import { ParamValueMode } from "jalhyd";
-
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { Component, Input, AfterViewInit } from "@angular/core";
 import { NgParameter } from "../../formulaire/ngparam";
-import { NgParamMinComponent } from "./ngparam-min.component";
-import { NgParamMaxComponent } from "./ngparam-max.component";
-import { NgParamStepComponent } from "./ngparam-step.component";
-import { BaseComponent } from "../base/base.component";
-import { ValueListComponent } from "./value-list.component";
+import { DialogEditParamValuesComponent } from "../dialog-edit-param-values/dialog-edit-param-values.component";
+import { MatDialog } from "@angular/material";
+import { ParamValueMode } from "jalhyd";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
+import { sprintf } from "sprintf-js";
+import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
 
 @Component({
     selector: "param-values",
     templateUrl: "./param-values.component.html",
-    styles: [
-        `.btn-on {
-            color:#505050;
-            border: 3px solid #505050;
-            background-color:white;
-            text-transform: uppercase;
-            font-size: 0.8em;
-        }`,
-        `.btn-off {
-            color:white;
-            border: 3px solid #505050;
-            background-color:#505050;
-            text-transform: uppercase;
-            font-size: 0.8em;
-        }`
+    styleUrls: [
+        "./param-values.component.scss"
     ]
 })
-export class ParamValuesComponent extends BaseComponent implements AfterViewChecked, OnChanges {
-    @Input()
-    private param: NgParameter;
-
-    private _valueModes = [];
-
-    /**
-     * true quand les champs du composant et de ses enfants sont initialisés
-     */
-    private _initCompleted = false;
-
-    /**
-     * true si la valeur min est valide
-     */
-    private _validMin = false;
-
-    /**
-     * true si la valeur max est valide
-     */
-    private _validMax = false;
-
-    /**
-     * true si la valeur du pas est valide
-     */
-    private _validStep = false;
-
-    /**
-     * true si la liste de valeurs est valide
-     */
-    private _validList = false;
-
-    /**
-     * flag signalant qu'il faut initialiser le composant ValueListComponent (par ex quand on a sélectionné le mode "liste")
-     */
-    private _doInitList = false;
-
-    /**
-     * flag signalant qu'il faut initialiser les composants min/max/pas
-     */
-    private _doInitMinmax = false;
-
-    /**
-     * composant de saisie du minimum
-     */
-    @ViewChild(NgParamMinComponent)
-    private _minComponent: NgParamMinComponent;
-
-    /**
-     * composant de saisie du maximum
-     */
-    @ViewChild(NgParamMaxComponent)
-    private _maxComponent: NgParamMaxComponent;
-
-    /**
-     * composant de saisie du pas de variation
-     */
-    @ViewChild(NgParamStepComponent)
-    private _stepComponent: NgParamStepComponent;
-
-    /**
-     * composant de saisie d'une liste de valeurs
-     */
-    @ViewChild(ValueListComponent)
-    private _listComponent: ValueListComponent;
-
-    @Output()
-    private valid: EventEmitter<boolean>;
-
-    constructor(private intlService: InternationalisationService) {
-        super();
-        this._valueModes.push({ "value": ParamValueMode.MINMAX, "label": "Min/max" });
-        this._valueModes.push({ "value": ParamValueMode.LISTE, "label": "Liste" });
-        this.valid = new EventEmitter();
-    }
-
-    /**
-     * init des champs min/max/pas
-     */
-    private initMinMaxStep() {
-        if (this.isMinMax && this._doInitMinmax) {
-            this._doInitMinmax = false;
-
-            // valeur pour min : celle déjà définie ou celle déduite de la valeur saisie
-            let min: number = this.param.minValue;
-            if (min === undefined) {
-                min = this.param.getValue() / 2;
-            }
-
-            // valeur pour max : celle déjà définie ou celle déduite de la valeur saisie
-            let max: number = this.param.maxValue;
-            if (max === undefined) {
-                max = this.param.getValue() * 2;
-            }
+export class ParamValuesComponent implements AfterViewInit {
 
-            this.param.minValue = min;
-            this._minComponent.model = this.param;
-
-            this.param.maxValue = max;
-            this._maxComponent.model = this.param;
-
-            // valeur du pas
-            let step = this.param.stepValue;
-            if (step === undefined) {
-                step = (max - min) / 20;
-            }
-            this.param.stepValue = step;
-            this._stepComponent.model = this.param;
-
-            this.validateAll();
-
-            this._validMin = this._minComponent.isValid;
-            this._validMax = this._maxComponent.isValid;
-            this._validStep = this._stepComponent.isValid;
-            this.emitValidity();
+    @Input()
+    public param: NgParameter;
 
-            this._initCompleted = true;
-        }
-    }
+    @Input()
+    public title: string;
 
-    /**
-     * initialisation de la liste de valeurs avec celle du paramètre géré
-     */
-    private initList() {
-        if (this._doInitList && this._listComponent !== undefined) {
-            this._doInitList = false;
-            let l = this.param.valueList;
-            if (l === undefined) {
-                if (this.param.isDefined) {
-                    l = [this.param.getValue()];
-                } else {
-                    l = [];
-                }
-            }
 
-            this.param.valueList = l;
-            this._listComponent.model = this.param;
-        }
-    }
+    constructor(
+        private editValuesDialog: MatDialog,
+        private intlService: I18nService,
+        private appSetupService: ApplicationSetupService
+    ) { }
 
-    /**
-     * revalidation de tous les composants enfants
-     */
-    private validateAll() {
-        if (this._minComponent !== undefined) {
-            this._minComponent.validate();
-        }
-        if (this._maxComponent !== undefined) {
-            this._maxComponent.validate();
-        }
-        if (this._stepComponent !== undefined) {
-            this._stepComponent.validate();
-        }
-        if (this._listComponent !== undefined) {
-            this._listComponent.validate();
-        }
+    public get isMinMax() {
+        return this.param.valueMode === ParamValueMode.MINMAX;
     }
 
-    /**
-     * envoi d'un événement de validité
-     */
-    private emitValidity() {
-        switch (this.param.valueMode) {
-            case ParamValueMode.LISTE:
-                this.valid.emit(this._validList);
-                break;
-
-            case ParamValueMode.MINMAX:
-                this.valid.emit(this._validMin && this._validMax && this._validStep);
-                break;
-        }
+    public get isListe() {
+        return this.param.valueMode === ParamValueMode.LISTE;
     }
 
-    /**
-     * réception d'un événement de NgParamMinComponent
-     */
-    private onMinChanged(event: any) {
-        if (this._initCompleted) {
-            switch (event.action) {
-                case "model":
-                    this.validateAll();
-                    break;
-
-                case "valid":
-                    this._validMin = event.value;
-                    this.emitValidity();
-                    break;
+    public get infoText() {
+        let text: string;
+        if (this.isMinMax) {
+            const nDigits = this.appSetupService.displayDigits;
+            let min: any = this.param.minValue;
+            let max: any = this.param.maxValue;
+            let step: any = this.param.stepValue;
+            if (min) {
+                min = min.toFixed(nDigits);
             }
-        }
-    }
-
-    /**
-     * réception d'un événement de NgParamMaxComponent
-     */
-    private onMaxChanged(event: any) {
-        if (this._initCompleted) {
-            switch (event.action) {
-                case "model":
-                    this.validateAll();
-                    break;
-
-                case "valid":
-                    this._validMax = event.value;
-                    this.emitValidity();
-                    break;
+            if (max) {
+                max = max.toFixed(nDigits);
             }
-        }
-    }
-
-    /**
-     * réception d'un événement de NgParamStepComponent
-     */
-    private onStepChanged(event: any) {
-        if (this._initCompleted) {
-            switch (event.action) {
-                case "model":
-                    this.validateAll();
-                    break;
-
-                case "valid":
-                    this._validStep = event.value;
-                    this.emitValidity();
-                    break;
+            if (step) {
+                step = step.toFixed(nDigits);
             }
+            text = this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_MINMAXSTEP");
+            text = sprintf(text, min, max, step);
+        } else if (this.isListe) {
+            text = this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES");
+            const vals = this.param.valueList || [];
+            text += " " + vals.slice(0, 20).join(";");
         }
+        return text;
     }
 
-    /**
-     * réception d'un événement de ValueListComponent
-     */
-    private onListChanged(event: any) {
-        if (this._initCompleted) {
-            switch (event.action) {
-                case "model":
-                    this.validateAll();
-                    break;
-
-                case "valid":
-                    this._validList = event.value;
-                    this.emitValidity();
-                    break;
+    public openDialog() {
+        // modification des valeurs variables
+        this.editValuesDialog.open(
+            DialogEditParamValuesComponent,
+            {
+                disableClose: true,
+                data: {
+                    param: this.param
+                }
             }
-        }
-    }
-
-    private get uitextValeurMini() {
-        return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMINI");
+        );
     }
 
-    private get uitextValeurMaxi() {
-        return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMAXI");
-    }
-
-    private get uitextPasVariation() {
-        return this.intlService.localizeText("INFO_PARAMFIELD_PASVARIATION");
-    }
-
-    /**
-     * true si mode "liste de valeurs"
-     */
-    public get isList(): boolean {
-        return this.param.valueMode === ParamValueMode.LISTE;
-    }
-
-    /**
-     * true si mode "lié"
-     */
-    public get isLink(): boolean {
-        return this.param.valueMode === ParamValueMode.LINK;
-    }
-
-    /**
-     * true si mode "min/max/pas"
-     */
-    public get isMinMax(): boolean {
-        return this.param.valueMode === ParamValueMode.MINMAX;
-    }
-
-    /**
-     * valeur courante affichée dans le select min-max/list
-     */
-    public get currentModeSelectLabel(): string {
-        return ParamValueMode[this.param.valueMode];
-    }
-
-    /**
-     * réception d'un événement du menu "min/max/liste"
-     */
-    public onSelectValueMode(event: any) {
-        const next = event.target.value;
-
-        switch (next) {
-            // on a sélectionné "min/max" ?
-            case ParamValueMode.MINMAX:
-                this._doInitMinmax = true;
-                break;
-
-            // on a sélectionné "liste" ?
-            case ParamValueMode.LISTE:
-                this._doInitList = true;
-                break;
-
-            default:
-                throw new Error("valeur " + next + " de ParamValueMode non prise en charge");
-        }
-
-        this.param.valueMode = next;
-    }
-
-    public get valueModes() {
-        return this._valueModes;
-    }
-
-    /**
-     * appelé quand les @Input changent
-     */
-    ngOnChanges() {
-        if (this.isMinMax) {
-            this._doInitMinmax = true;
-        } else {
-            this._doInitList = true;
+    public ngAfterViewInit() {
+        // open dialog when switching to this mode, but only the first time this component is built,
+        // otherwise switching back from another calc tab will trigger the dialog again
+        if (this.param.minValue === undefined) {
+            // use Promise trick to introduce a pseudo-timeout and avoid ExpressionChangedAfterItHasBeenCheckedError
+            Promise.resolve().then(() => {
+                this.openDialog();
+            });
         }
     }
-
-    ngAfterViewChecked() {
-        super.ngAfterViewChecked();
-        this.initMinMaxStep();
-        this.initList();
-    }
 }
diff --git a/src/app/components/param-values/value-list.component.ts b/src/app/components/param-values/value-list.component.ts
deleted file mode 100644
index 542e770b68e351baa6601afd2604cba6a8b8bb23..0000000000000000000000000000000000000000
--- a/src/app/components/param-values/value-list.component.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-import { Component, Input, ChangeDetectorRef } from "@angular/core";
-
-import { GenericInputComponent } from "../generic-input/generic-input.component";
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
-import { NgParameter } from "../../formulaire/ngparam";
-import { Message } from "jalhyd";
-
-@Component({
-    selector: "value-list",
-    templateUrl: "../generic-input/generic-input.component.html"
-})
-export class ValueListComponent extends GenericInputComponent {
-    constructor(private intlService: InternationalisationService, cdRef: ChangeDetectorRef) {
-        super(cdRef);
-    }
-
-    /**
-     * paramètre géré
-     */
-    private get _param(): NgParameter {
-        return this._model;
-    }
-
-    protected getModelValue(): any {
-        if (this._param === undefined || this._param === null) {
-            return undefined;
-        }
-        return this._param.valueList;
-    }
-
-    protected setModelValue(sender: any, l: any) {
-        if (typeof (l) === "number") {
-            this._param.valueList = [];
-            this._param.valueList.push(l);
-        } else {
-            this._param.valueList = l;
-        }
-    }
-
-    protected validateModelValue(v: any): { isValid: boolean, message: string } {
-        let msg;
-        let valid = false;
-
-        if (v instanceof Array) {
-            valid = true;
-            try {
-                this._param.checkList(v);
-            } catch (ex) {
-                valid = false;
-                if (ex instanceof Message) {
-                    msg = this.intlService.localizeMessage(ex);
-                } else {
-                    msg = "invalid value";
-                }
-            }
-        } else {
-            msg = "Veuillez entrer une liste de nombres";
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected modelToUI(v: any): string {
-        let res = "";
-        if (v !== undefined && v !== null) {
-            for (const e of v) {
-                if (res !== "") {
-                    res += ";";
-                }
-                res += String(e);
-            }
-        }
-        return res;
-    }
-
-    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
-        let valid = false;
-        let msg: string;
-
-        const tmp: string[] = ui.split(";");
-        let res = true;
-        for (const v of tmp) {
-            const isnum = v !== "" && (+v === +v);
-            res = res && isnum;
-            if (!res) {
-                break;
-            }
-        }
-
-        if (!res) {
-            msg = "Veuillez entrer une liste de nombres";
-        } else {
-            valid = true;
-        }
-
-        return { isValid: valid, message: msg };
-    }
-
-    protected uiToModel(ui: string) {
-        const tmp: string[] = ui.split(";");
-        const res = [];
-        for (const v of tmp) {
-            res.push(+v);
-        }
-        return res;
-    }
-}
diff --git a/src/app/components/remous-results/remous-results.component.html b/src/app/components/remous-results/remous-results.component.html
index 391f0ddaf6d95aa137e768f02b7ee66b5b3f1ad4..24c15f01effb82c4ca45e46839b49138fe3a8f09 100644
--- a/src/app/components/remous-results/remous-results.component.html
+++ b/src/app/components/remous-results/remous-results.component.html
@@ -1,19 +1,15 @@
-<div class="container-fluid" *ngIf="hasResults">
-    <div class="row">
-        <div class="col">
-            <chart [type]="graph1_type" [data]="graph1_data" [options]="graph1_options"></chart>
-        </div>
+<div class="container" *ngIf="hasResults">
+    <div>
+        <chart [type]="graph1_type" [data]="graph1_data" [options]="graph1_options"></chart>
     </div>
-    <div class="row">
-        <div class="col">
-            <chart *ngIf="extraGraph" [type]="graph2_type" [data]="graph2_data" [options]="graph2_options"></chart>
-        </div>
+    <div>
+        <chart *ngIf="extraGraph" [type]="graph2_type" [data]="graph2_data" [options]="graph2_options"></chart>
     </div>
 
     <!-- journal -->
     <log></log>
 
-    <div *ngIf="hasData" class="row">
+    <div *ngIf="hasData">
         <!-- résultats numériques -->
         <var-results></var-results>
     </div>
diff --git a/src/app/components/remous-results/remous-results.component.ts b/src/app/components/remous-results/remous-results.component.ts
index 3d47a77084d5c90028e3fb5f7916593f843429ce..a64d46ce41d264b9370a6654be5d667281a374fc 100644
--- a/src/app/components/remous-results/remous-results.component.ts
+++ b/src/app/components/remous-results/remous-results.component.ts
@@ -2,11 +2,13 @@ import { Component, ViewChild, DoCheck } from "@angular/core";
 
 import { ArrayReverseIterator, ResultElement, INumberIterator } from "jalhyd";
 
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
 import { LogComponent } from "../../components/log/log.component";
 import { RemousResults } from "../../results/remous-results";
 import { CalculatorResults } from "../../results/calculator-results";
 import { VarResultsComponent } from "../fixedvar-results/var-results.component";
+import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
+import { ServiceFactory } from "../../services/service-factory";
 
 /**
  * données pour une ligne dans le graphe
@@ -32,6 +34,8 @@ class LineData {
      */
     private _data = {};
 
+    private appSetup: ApplicationSetupService;
+
     /**
      * profondeur à laquelle est dessinée la ligne
      * les profondeurs les plus petites sont dessinées derrière les profondeurs les plus grandes
@@ -44,6 +48,7 @@ class LineData {
         for (let i = this._tx.length - 1; i >= 0; i--) {
             this._ty.push(null);
         }
+        this.appSetup = ServiceFactory.instance.applicationSetupService;
     }
 
     public getYat(x: number) {
@@ -248,8 +253,10 @@ export class RemousResultsComponent implements DoCheck {
     @ViewChild(LogComponent)
     private logComponent: LogComponent;
 
-    constructor(private intlService: InternationalisationService) {
-    }
+    constructor(
+        private intlService: I18nService,
+        private appSetup: ApplicationSetupService
+    ) { }
 
     private get uitextLigneFluviale() {
         return this.intlService.getExtraResLabel("FLU");
@@ -549,6 +556,7 @@ export class RemousResultsComponent implements DoCheck {
 
         this.graph1_data = gr1.data;
 
+        const nDigits = this.appSetup.displayDigits;
         this.graph1_options = {
             responsive: true,
             maintainAspectRatio: true,
@@ -563,6 +571,26 @@ export class RemousResultsComponent implements DoCheck {
                 display: true,
                 text: this.uitextAbscisse,
                 position: "bottom"
+            },
+            scales: {
+                xAxes: [{
+                    gridLines: {
+                        offsetGridLines: true
+                    },
+                    ticks: {
+                        precision: nDigits,
+                        callback: function(value, index, values) {
+                            return Number(value).toFixed(nDigits);
+                        }
+                    }
+                }]
+            },
+            tooltips: {
+                callbacks: {
+                    label: function(tooltipItem, data) {
+                        return Number(tooltipItem.yLabel).toFixed(nDigits);
+                    }
+                }
             }
         };
 
diff --git a/src/app/components/result-element/horizontal-result-element.component.html b/src/app/components/result-element/horizontal-result-element.component.html
deleted file mode 100644
index 5b40239d3b81eee340e96c805916fdc6ac7f330f..0000000000000000000000000000000000000000
--- a/src/app/components/result-element/horizontal-result-element.component.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!-- template pour le popup -->
-<ng-template #popTemplate>
-    <div [innerHtml]="htmlTooltip"></div>
-</ng-template>
-
-<!--
-    <ng-template #tmplxxxx let-xxx="yyy">
-       {{xxx}}>
-    </ng-template>
-
-    #tmplxxxx : nom utilisé pour référencer ce template
-    let-xxx : définition du nom d'une variable d'entrée xxx du template
-    yyy : clé de la valeur de xxx dans le contexte passé au template
-
-    instancié avec :
-    <ng-container *ngTemplateOutlet="tmplxxxx;context:ctx"></ng-container>
-    ctx : contexte passé au template. Défini dans le .ts du composant :
-    private ctx = { yyy: 0.1 };
-
-    cf. https://blog.angular-university.io/angular-ng-template-ng-container-ngtemplateoutlet/
--->
-
-<!-- icône en cas d'erreur -->
-<i *ngIf="hasError" class="fa fa-exclamation-triangle" style="color:red" aria-hidden="true" [mdbTooltip]="popTemplate" [isDisabled]="tooltipDisabled"></i>
-
-<!-- valeur  -->
-<span *ngIf="!hasError" [mdbTooltip]="popTemplate" [isDisabled]="tooltipDisabled">
-    {{ resultValue }}
-</span>
-
-<!-- template de création de td supplémentaires pour les extraResult -->
-<ng-template #extraResultTd let-v="extraResultValue">
-    <td>
-        {{ v }}
-    </td>
-</ng-template>
\ No newline at end of file
diff --git a/src/app/components/result-element/horizontal-result-element.component.ts b/src/app/components/result-element/horizontal-result-element.component.ts
deleted file mode 100644
index faa0174d6a02fb277d505d55fde34d25ec4846e1..0000000000000000000000000000000000000000
--- a/src/app/components/result-element/horizontal-result-element.component.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { Component, Input, ViewChild, TemplateRef, ViewContainerRef } from "@angular/core";
-import { OnChanges } from "@angular/core/src/metadata/lifecycle_hooks";
-import { ResultElementBaseComponent } from "./result-element-base.component";
-
-@Component({
-    selector: "[horizontal-result-element]",
-    templateUrl: "./horizontal-result-element.component.html"
-})
-export class HorizontalResultElementComponent extends ResultElementBaseComponent implements OnChanges {
-    // template des td pour les extraResult
-    @ViewChild("extraResultTd") tdTemplate: TemplateRef<any>;
-
-    /**
-     * clés des résultats complémentaires à afficher (la cellule est vide s'il n'existe pas dans le ResultElement)
-     */
-    private _headerKeys: string[];
-
-    @Input()
-    public set headerKeys(h: string[]) {
-        this._headerKeys = h;
-    }
-
-    constructor(private vcRef: ViewContainerRef) {
-        super();
-    }
-
-    ngOnChanges() {
-        super.ngOnChanges();
-
-        this.vcRef.clear();
-        if (this._headerKeys && this._resultElement) {
-            for (const h of this._headerKeys) {
-                let v = this._resultElement.extraResults[h];
-                if (typeof (v) === "number") {
-                    v = this.intlService.formatResult(h, v);
-                }
-                this.vcRef.createEmbeddedView(this.tdTemplate, { extraResultValue: v });
-            }
-        }
-    }
-}
diff --git a/src/app/components/result-element/result-element-base.component.ts b/src/app/components/result-element/result-element-base.component.ts
deleted file mode 100644
index d30921bbea27adaa69cd3fec8d07b63b524a1f52..0000000000000000000000000000000000000000
--- a/src/app/components/result-element/result-element-base.component.ts
+++ /dev/null
@@ -1,115 +0,0 @@
-import { Component, Input } from "@angular/core";
-
-import { ResultElement } from "jalhyd";
-
-import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
-import { OnChanges } from "@angular/core/src/metadata/lifecycle_hooks";
-import { ServiceFactory } from "../../services/service-factory";
-
-/**
- * classe de base pour l'affichage d'un ResultElement
- * étendue par HorizontalResultElementComponent et VerticalResultElementComponent
- */
-@Component({
-    selector: "[result-element-base]",
-    template: `<div></div>`
-})
-export class ResultElementBaseComponent implements OnChanges {
-    /**
-     * ResultElement à afficher
-     */
-    protected _resultElement: ResultElement;
-
-    @Input("result-element")
-    private set resultElement(re: ResultElement) {
-        this._resultElement = re;
-    }
-
-    /**
-     * valeur numérique du résultat
-     */
-    protected _value: string;
-
-    /**
-     * true si la valeur est en erreur
-     */
-    protected _hasError: boolean;
-
-    /**
-     * true si le result element a une valeur
-     */
-    protected _hasValue: boolean;
-
-    /**
-     * code HTML du tooltip
-     */
-    protected _htmlTooltip: string;
-
-    /**
-     * true si pas de texte à afficher dans le tooltip
-     */
-    protected _emptyTooltip = false;
-
-    protected appSetupService: ApplicationSetupService;
-
-    protected intlService: InternationalisationService;
-
-    constructor(
-    ) {
-        this.appSetupService = ServiceFactory.instance.applicationSetupService;
-        this.intlService = ServiceFactory.instance.internationalisationService;
-    }
-
-    /**
-     * appelé quand les @Input changent
-     */
-    ngOnChanges() {
-        this.updateTooltip();
-    }
-
-    private updateTooltip() {
-        // valeur à afficher
-
-        const nDigits = this.appSetupService.displayDigits;
-        const r: ResultElement = this._resultElement;
-        this._hasValue = r !== undefined && r.vCalc !== undefined;
-        this._hasError = r === undefined || (r.vCalc === undefined && r.extraResults.length > 0);
-        this._value = this._hasValue ? this._value = r.vCalc.toFixed(nDigits) : " ";
-
-        // texte du tooltip
-
-        let res = "";
-
-        if (this._resultElement !== undefined) {
-            for (const m of this._resultElement.log.messages) {
-                if (res.length > 0) {
-                    res += "<br/>";
-                }
-                res += this.intlService.localizeMessage(m);
-            }
-        }
-        this._htmlTooltip = res;
-        this._emptyTooltip = this._htmlTooltip.length === 0;
-    }
-
-    public get hasValue() {
-        return this._hasValue;
-    }
-
-    private get resultValue(): string {
-        return this._value;
-    }
-
-    public get hasError() {
-        return this._hasError && !this._emptyTooltip;
-    }
-
-    private get htmlTooltip(): string {
-        return this._htmlTooltip;
-    }
-
-    private get tooltipDisabled(): boolean {
-        return this._emptyTooltip;
-    }
-}
diff --git a/src/app/components/result-element/vertical-result-element.component.html b/src/app/components/result-element/vertical-result-element.component.html
deleted file mode 100644
index 38226ca2658c039fda1df8ff5b4e4e6b684204bc..0000000000000000000000000000000000000000
--- a/src/app/components/result-element/vertical-result-element.component.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- template pour le popup -->
-<ng-template #popTemplate>
-    <div [innerHtml]="htmlTooltip"></div>
-</ng-template>
-
-<td *ngIf="hasValue" class="label2">
-    {{ resultLabel }}
-</td>
-
-<td *ngIf="hasValue||hasError" [mdbTooltip]="popTemplate" [isDisabled]="tooltipDisabled" class="value2">
-    <!-- icône en cas d'erreur -->
-    <i *ngIf="hasError" class="fa fa-exclamation-triangle" style="color:red" aria-hidden="true"></i>
-
-    <!-- valeur  -->
-    <span *ngIf="hasValue">
-        {{ resultValue }}
-    </span>
-</td>
-
-<!-- template de création de tr supplémentaires pour les extraResult -->
-<ng-template #extraResultTr let-r="extraRes" let-c="classes">
-    <!-- résultats complémentaires -->
-    <tr>
-        <td class={{c.label_class}}>
-            {{ r.label }}
-        </td>
-        <td class={{c.value_class}}>
-            {{ r.value }}
-        </td>
-    </tr>
-</ng-template>
\ No newline at end of file
diff --git a/src/app/components/result-element/vertical-result-element.component.ts b/src/app/components/result-element/vertical-result-element.component.ts
deleted file mode 100644
index 8da84bab6b4f81e70a524568f8797f90f5f1cbfe..0000000000000000000000000000000000000000
--- a/src/app/components/result-element/vertical-result-element.component.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-import { Component, Input, ViewChild, TemplateRef, ViewContainerRef } from "@angular/core";
-import { OnChanges } from "@angular/core/src/metadata/lifecycle_hooks";
-import { ResultElementBaseComponent } from "./result-element-base.component";
-
-@Component({
-    selector: "tr[vertical-result-element]",
-    templateUrl: "./vertical-result-element.component.html",
-    styles: [
-        `.label1 {
-            text-align: right; background-color: #f0f0f0; font-weight: bold
-        }`,
-        `.value1 {
-            text-align: center; background-color: #f0f0f0; font-weight: bold
-        }`,
-        `.label2 {
-            text-align: right; background-color: #ffffff; font-weight: bold
-        }`,
-        `.value2 {
-            text-align: center; background-color: #ffffff; font-weight: bold
-        }`
-    ]
-})
-export class VerticalResultElementComponent extends ResultElementBaseComponent implements OnChanges {
-    /**
-     * nom de la variable
-     */
-    @Input()
-    private _label: string;
-
-    // template des tr pour les extraResult
-    @ViewChild("extraResultTr") trTemplate: TemplateRef<any>;
-
-    constructor(private vcRef: ViewContainerRef) {
-        super();
-    }
-
-    private get resultLabel() {
-        return this._label;
-    }
-
-    ngOnChanges() {
-        super.ngOnChanges();
-
-        this.vcRef.clear();
-        if (this._resultElement) {
-            let i = 0;
-            for (const k in this._resultElement.extraResults) {
-                if (this._resultElement.extraResults.hasOwnProperty(k)) {
-                    const er: number = this._resultElement.extraResults[k];
-                    const lblClass = (i % 2) === 0 ? "label1" : "label2";
-                    const valueClass = (i % 2) === 0 ? "value1" : "value2";
-                    this.vcRef.createEmbeddedView(this.trTemplate, {
-                        extraRes: { "label": this.intlService.getExtraResLabel(k), "value": this.intlService.formatResult(k, er) },
-                        classes: { "label_class": lblClass, "value_class": valueClass }
-                    });
-                    i++;
-                }
-            }
-        }
-    }
-}
-
-/*
-exemple de composant générant plusieurs tr d'une table
-
-@Component({
-  selector: 'tr[my-row]',
-  template: `
-    <td>{{ firstWord }}</td>
-    <td>{{ secondWord }}</td>
-
-    <ng-template #secondRow>
-      <tr>
-        <td>fooooooooooooooo</td>
-        <td>bar</td>
-      </tr>
-    </ng-template>
-  `
-})
-export class MyRowComponent {
-  @Input() firstWord = 'first';
-  @Input() secondWord = 'second';
-  @Input() secondRow = false;
-
-  @ViewChild('secondRow') template: TemplateRef<any>;
-
-  constructor(private vcRef: ViewContainerRef) {}
-
-  ngOnInit() {
-    if(this.secondRow) {
-      this.vcRef.createEmbeddedView(this.template);
-    }
-  }
-}
-
-parent.html
-
-<table class="table">
-  <tr>
-    <td>column 1</td>
-    <td>column 2</td>
-  </tr>
-  <tr my-row firstWord="hello world" secondWord="good bye">
-  <tr my-row secondRow="true">
-  <tr my-row>
-</table>
-*/
diff --git a/src/app/components/results-graph/graph-type.component.scss b/src/app/components/results-graph/graph-type.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..46070b5e341c3e12e97c41533c28c5508e6ba8a4
--- /dev/null
+++ b/src/app/components/results-graph/graph-type.component.scss
@@ -0,0 +1,16 @@
+:host {
+    display: block;
+    margin-top: 5px;
+    text-align: center;
+}
+
+mat-select {
+
+    ::ng-deep .mat-select-value {
+        > span {
+            > span {
+                line-height: 1.3em;
+            }
+        }
+    }
+}
diff --git a/src/app/components/results-graph/graph-type.component.ts b/src/app/components/results-graph/graph-type.component.ts
index 5ccfbf764b89e8dec1ff38b08a42e52380908f52..c96f6c53257d276a45ee07e3a7d8f936d861af85 100644
--- a/src/app/components/results-graph/graph-type.component.ts
+++ b/src/app/components/results-graph/graph-type.component.ts
@@ -1,24 +1,28 @@
 import { Component } from "@angular/core";
-
 import { Observable, IObservable, Observer } from "jalhyd";
-
-import { GenericSelectComponent } from "../generic-select/generic-select.component";
 import { GraphType } from "../../results/var-results";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
 
 @Component({
     selector: "graph-type",
-    templateUrl: "../generic-select/generic-select.component.html"
+    templateUrl: "../generic-select/generic-select.component.html",
+    styleUrls: [
+        "./graph-type.component.scss"
+    ]
 })
-export class GraphTypeSelectComponent extends GenericSelectComponent<GraphType> implements IObservable {
+export class GraphTypeSelectComponent implements IObservable {
     private _entries: GraphType[] = [GraphType.Histogram, GraphType.Scatter];
-    private _entriesLabels: string[] = ["Histogramme", "XY"];
+    private _entriesLabels: string[] = [];
     private _selected: GraphType;
 
     private _observable: Observable;
 
-    constructor() {
-        super();
+    constructor(private intlService: I18nService) {
         this._observable = new Observable();
+        this._entriesLabels = [
+            this.intlService.localizeText("INFO_PARAMFIELD_GRAPH_TYPE_HISTOGRAM"),
+            "XY"
+        ];
     }
 
     public get entries(): GraphType[] {
@@ -44,6 +48,10 @@ export class GraphTypeSelectComponent extends GenericSelectComponent<GraphType>
         }
     }
 
+    public get label() {
+        return this.intlService.localizeText("INFO_PARAMFIELD_GRAPH_TYPE");
+    }
+
     // interface IObservable
 
     /**
diff --git a/src/app/components/results-graph/results-graph.component.html b/src/app/components/results-graph/results-graph.component.html
index 73ad69b83bac77af7b831eb1196232bc2248ad29..9f0dc91e235577fd23ee2138e13f465f8b40409c 100644
--- a/src/app/components/results-graph/results-graph.component.html
+++ b/src/app/components/results-graph/results-graph.component.html
@@ -1,12 +1,4 @@
-<div class="row">
-    <div class="col-12">
-        <chart [type]="graph_type" [data]="graph_data" [options]="graph_options">
-        </chart>
-    </div>
-</div>
+<chart [type]="graph_type" [data]="graph_data" [options]="graph_options">
+</chart>
 
-<div class="row">
-    <div class="col-4 mx-auto">
-        <graph-type></graph-type>
-    </div>
-</div>
\ No newline at end of file
+<graph-type></graph-type>
diff --git a/src/app/components/results-graph/results-graph.component.ts b/src/app/components/results-graph/results-graph.component.ts
index 36d0ed25044c7884a4b3d4713060d039126d2399..35dbf11cda534be7b9733acd4538dec58c29c931 100644
--- a/src/app/components/results-graph/results-graph.component.ts
+++ b/src/app/components/results-graph/results-graph.component.ts
@@ -4,6 +4,7 @@ import { Observer } from "jalhyd";
 
 import { VarResults, GraphType } from "../../results/var-results";
 import { GraphTypeSelectComponent } from "./graph-type.component";
+import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
 
 @Component({
     selector: "results-graph",
@@ -35,6 +36,13 @@ export class ResultsGraphComponent implements AfterContentInit, Observer {
         }
     };
 
+    public constructor(
+        private appSetup: ApplicationSetupService
+    ) {
+        // limit display precision according to app preferences
+        const nDigits = this.appSetup.displayDigits;
+    }
+
     public set results(r: VarResults) {
         this._results = r;
         if (this._results && this._graphTypeComponent) {
@@ -73,8 +81,7 @@ export class ResultsGraphComponent implements AfterContentInit, Observer {
         const dat = [];
         let i = 0;
         for (const x of this._results.variatedParameter.valuesIterator) {
-            labs.push(String(x));
-
+            labs.push(x);
             const y = this._results.yValues[i];
             dat.push(y);
             i++;
@@ -84,12 +91,10 @@ export class ResultsGraphComponent implements AfterContentInit, Observer {
 
         this.graph_data = {
             labels: labs,
-            datasets: [
-                {
-                    label: "",
-                    data: dat
-                }
-            ]
+            datasets: [{
+                label: "",
+                data: dat
+            }]
         };
     }
 
@@ -102,30 +107,40 @@ export class ResultsGraphComponent implements AfterContentInit, Observer {
         let i = 0;
         for (const x of this._results.variatedParameter.valuesIterator) {
             labs.push(x);
-
             const y = this._results.yValues[i];
             dat.push(y);
-
             i++;
         }
 
         this.graph_options.title.text = this._results.graphTitle;
+        const nDigits = this.appSetup.displayDigits;
         this.graph_options["scales"] = {
             xAxes: [{
                 gridLines: {
                     offsetGridLines: true
+                },
+                ticks: {
+                    precision: nDigits,
+                    callback: function(value, index, values) {
+                        return Number(value).toFixed(nDigits);
+                    }
                 }
             }]
         };
+        this.graph_options["tooltips"] = {
+            callbacks: {
+                label: function(tooltipItem, data) {
+                    return Number(tooltipItem.yLabel).toFixed(nDigits);
+                }
+            }
+        };
 
         this.graph_data = {
             labels: labs,
-            datasets: [
-                {
-                    label: "",
-                    data: dat
-                }
-            ]
+            datasets: [{
+                label: "",
+                data: dat
+            }]
         };
     }
 
@@ -137,32 +152,47 @@ export class ResultsGraphComponent implements AfterContentInit, Observer {
         let i = 0;
         for (const x of this._results.variatedParameter.valuesIterator) {
             const y = this._results.yValues[i];
-            dat.push({ x: x, y: y });
+            dat.push({
+                x: x,
+                y: y
+            });
             i++;
         }
 
         this.graph_options.title.text = this._results.graphTitle;
+        const nDigits = this.appSetup.displayDigits;
         this.graph_options["scales"] = {
             xAxes: [{
                 type: "linear",
-                position: "bottom"
+                position: "bottom",
+                ticks: {
+                    precision: nDigits
+                }
             }],
             yAxes: [{
                 type: "linear",
-                position: "left"
+                position: "left",
+                ticks: {
+                    precision: nDigits
+                }
             }]
         };
+        this.graph_options["tooltips"] = {
+            callbacks: {
+                label: function(tooltipItem, data) {
+                    return "(" + Number(tooltipItem.xLabel).toFixed(nDigits) + ", " + Number(tooltipItem.yLabel).toFixed(nDigits) + ")";
+                }
+            }
+        };
 
         this.graph_data = {
-            datasets: [
-                {
-                    label: "",
-                    data: dat,
-                    borderColor: "#808080", // couleur de la ligne
-                    backgroundColor: "rgba(0,0,0,0)",  // couleur de remplissage sous la courbe : transparent
-                    showLine: "true"
-                }
-            ]
+            datasets: [{
+                label: "",
+                data: dat,
+                borderColor: "#808080", // couleur de la ligne
+                backgroundColor: "rgba(0,0,0,0)",  // couleur de remplissage sous la courbe : transparent
+                showLine: "true"
+            }]
         };
     }
 
diff --git a/src/app/components/save-calculator/save-calculator-anchor.directive.ts b/src/app/components/save-calculator/save-calculator-anchor.directive.ts
deleted file mode 100644
index fa2d02f81584d9e9c2f5c32c6f0d3408f35bfa1e..0000000000000000000000000000000000000000
--- a/src/app/components/save-calculator/save-calculator-anchor.directive.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { Directive, ComponentFactoryResolver, ComponentFactory, ComponentRef } from "@angular/core";
-
-import { ViewContainerRef } from "@angular/core";
-import { SaveCalculatorComponent } from "./save-calculator.component";
-
-@Directive({
-    selector: "[appSaveCalcDialogAnchor]"
-})
-export class SaveCalcDialogAnchorDirective {
-    constructor(
-        private viewContainer: ViewContainerRef,
-        private componentFactoryResolver: ComponentFactoryResolver
-    ) { }
-
-    public createDialog(): ComponentRef<SaveCalculatorComponent> {
-        this.viewContainer.clear();
-
-        const compFactory: ComponentFactory<SaveCalculatorComponent>
-            = this.componentFactoryResolver.resolveComponentFactory(SaveCalculatorComponent);
-        const compRef: ComponentRef<SaveCalculatorComponent> = this.viewContainer.createComponent(compFactory);
-
-        // dialogComponentRef.instance.close.subscribe(() => {
-        //     dialogComponentRef.destroy();
-        // });
-
-        return compRef;
-    }
-}
diff --git a/src/app/components/save-calculator/save-calculator.component.html b/src/app/components/save-calculator/save-calculator.component.html
deleted file mode 100644
index 1c48d43c7dfd4aed16531596fc430da7bfb6536f..0000000000000000000000000000000000000000
--- a/src/app/components/save-calculator/save-calculator.component.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<div mdbModal #saveDialog="mdb-modal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true" [config]="{backdrop: false, ignoreBackdropClick: true,show:true}">
-    <div class="modal-dialog" role="document">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title w-100" id="myModalLabel">{{ uitextDialogTitle }}</h4>
-            </div>
-            <div class="modal-body">
-                <!-- liste de calculettes avec check -->
-                <div *ngFor="let c of calculators">
-                    <input type="checkbox" value={{c.uid}} checked={{isSelected(c)}} (change)="onCheckCalc($event)">{{ c.title }}
-                </div>
-
-                <!-- bouton "tout sélectionnner" -->
-                <button type="button" class="btn btn-mdb-color waves-light" (click)="selectAll()" mdbRippleRadius>{{ uitextSelectAll }}</button>
-
-                <!-- bouton "tout désélectionnner" -->
-                <button type="button" class="btn btn-mdb-color waves-light py-10" (click)="deselectAll()" mdbRippleRadius>{{ uitextDeselectAll }}</button>
-
-                <!-- nom du fichier -->
-                <div class="md-form form-sm mt-4">
-                    <input mdbInputDirective [mdbValidate]="false" type="text" id="form1" class="form-control" [(ngModel)]="filename">
-                    <!-- on utilise [innerHTML] pour que les codes HTML comme &nbsp; soient interprétés correctement -->
-                    <label for="form1" [innerHTML]="filenameTitle"></label>
-                </div>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-danger relative waves-light" (click)="saveDialog.hide();cancelSave()" mdbRippleRadius>{{ uitextCloseDialogNo }}</button>
-                <button type="button" class="btn btn-success waves-light" aria-label="Close " (click)="saveDialog.hide();confirmSave()" mdbRippleRadius>{{ uitextCloseDialogYes }}</button>
-            </div>
-        </div>
-    </div>
-</div>
\ No newline at end of file
diff --git a/src/app/components/save-calculator/save-calculator.component.ts b/src/app/components/save-calculator/save-calculator.component.ts
deleted file mode 100644
index 653e76c4bffce16c3a14360ffa357c5ac5987810..0000000000000000000000000000000000000000
--- a/src/app/components/save-calculator/save-calculator.component.ts
+++ /dev/null
@@ -1,135 +0,0 @@
-import { Component, EventEmitter } from "@angular/core";
-import { ServiceFactory } from "../../services/service-factory";
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
-
-@Component({
-    selector: "save-calc",
-    templateUrl: "./save-calculator.component.html"
-})
-export class SaveCalculatorComponent {
-    /**
-     * liste des calculettes affichées. Forme des objets :
-     * "title": nom de la calculette
-     * "selected": flag de sélection pour la sauvegarde
-     * "uid": id unique du formulaire
-     */
-    private _calculators: any[];
-
-    /**
-     * nom du fichier
-     */
-    private _filename = "session";
-
-    private _filenameTitle = "Nom de fichier";
-
-    /**
-     * événement émis lors du clic sur "annuler"/"enregister"
-     * utilisé par la promise de gestion de la confirmation/annulation de la sauvegarde
-     */
-    private confirmResult = new EventEmitter();
-
-    // services
-    private intlService: InternationalisationService;
-
-    constructor() {
-        this.intlService = ServiceFactory.instance.internationalisationService;
-    }
-
-    public get uitextDialogTitle() {
-        return "Enregister les calculettes";
-    }
-
-    public get uitextCloseDialogYes() {
-        // return this.intlService.localizeText("INFO_OPTION_YES");
-        return "Sauver";
-    }
-
-    public get uitextCloseDialogNo() {
-        // return this.intlService.localizeText("INFO_OPTION_NO");
-        return "Annuler";
-    }
-
-    public get uitextSelectAll() {
-        return "Toutes";
-    }
-
-    public get uitextDeselectAll() {
-        return "Aucune";
-    }
-
-    public get filename(): string {
-        return this._filename;
-    }
-
-    public set filename(f: string) {
-        this._filename = f;
-    }
-
-    public get filenameTitle(): string {
-        return this._filenameTitle;
-    }
-
-
-    public run(calcList: any[]): Promise<any[]> {
-        this._calculators = calcList;
-
-        // promise de gestion de la confirmation/annulation de la sauvegarde
-        return new Promise((resolve, reject) => {
-            this.confirmResult.subscribe((confirm) => {
-                if (confirm) {
-                    resolve(this._calculators);
-                } else {
-                    reject();
-                }
-            });
-        });
-    }
-
-    private isSelected(c: any) {
-        return c.selected ? "checked" : undefined;
-    }
-
-    private onCheckCalc(event: any) {
-        for (const c of this._calculators) {
-            if (c.uid === event.target.value) {
-                c.selected = event.target.checked;
-            }
-        }
-    }
-
-    public selectAll() {
-        for (const c of this._calculators) {
-            c.selected = true;
-        }
-    }
-
-    public deselectAll() {
-        for (const c of this._calculators) {
-            c.selected = false;
-        }
-    }
-
-    private set confirmed(b: boolean) {
-        setTimeout(() => {
-            this.confirmResult.next(b);
-        }, 0);
-    }
-
-    public get calculators() {
-        return this._calculators;
-    }
-
-    /**
-     * appelé quand on clique sur annuler
-     */
-    public cancelSave() {
-        this.confirmed = false;
-    }
-
-    /**
-     * appelé quand on clique sur sauver
-     */
-    public confirmSave() {
-        this.confirmed = true;
-    }
-}
diff --git a/src/app/components/section-results/section-results.component.ts b/src/app/components/section-results/section-results.component.ts
index ae3598d0fc1a560b047b9480d228f34caac61771..83ad69517fa2676d6d94324a98e2b4b52f89042f 100644
--- a/src/app/components/section-results/section-results.component.ts
+++ b/src/app/components/section-results/section-results.component.ts
@@ -6,7 +6,7 @@ import { SectionCanvasComponent } from "../section-canvas/section-canvas.compone
 import { SectionResults } from "../../results/section-results";
 import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
 import { CalculatorResults } from "../../results/calculator-results";
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
 
 @Component({
     selector: "section-results",
@@ -33,7 +33,7 @@ import { InternationalisationService } from "../../services/internationalisation
 })
 export class SectionResultsComponent implements DoCheck {
 
-    constructor(private appSetupService: ApplicationSetupService, private intlService: InternationalisationService) { }
+    constructor(private appSetupService: ApplicationSetupService, private intlService: I18nService) { }
 
     public set results(rs: CalculatorResults[]) {
         this._resultElement = undefined;
diff --git a/src/app/components/select-field-line/select-field-line.component.html b/src/app/components/select-field-line/select-field-line.component.html
deleted file mode 100644
index c298402bd3203e34793e85c773167d2c1dc9aa19..0000000000000000000000000000000000000000
--- a/src/app/components/select-field-line/select-field-line.component.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<div class="row">
-    <!-- titre -->
-    <div class="col-12 col-sm-3">
-        {{ label }}
-    </div>
-
-    <!-- liste déroulante -->
-    <div class="btn-group col-12 col-sm-9" dropdown (click)="onSelect($event)">
-        <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius>
-            {{ currentLabel }}
-        </button>
-        <div class="dropdown-menu">
-            <a class="dropdown-item" *ngFor="let e of entries" [value]=e>{{ entryLabel(e) }}</a>
-        </div>
-    </div>
-</div>
\ No newline at end of file
diff --git a/src/app/components/select-field-line/select-field-line.component.scss b/src/app/components/select-field-line/select-field-line.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..7b16e54279684cba3f58937e588f36f560dda449
--- /dev/null
+++ b/src/app/components/select-field-line/select-field-line.component.scss
@@ -0,0 +1,24 @@
+mat-form-field {
+    width: 100%;
+
+    mat-select {
+
+        ::ng-deep .mat-select-value {
+            > span {
+                > span {
+                    line-height: 1.3em;
+                }
+            }
+        }
+
+        ::ng-deep .mat-form-field-label {
+            font-size: 1.1em;
+            line-height: 1.4em;
+            margin-top: -2px;
+    
+            &.mat-form-field-empty {
+                font-size: 1em;
+            }
+        }
+    }
+}
diff --git a/src/app/components/select-field-line/select-field-line.component.ts b/src/app/components/select-field-line/select-field-line.component.ts
index 59f111c2364ca042154a310ad9d4809bbd74271d..a7840110a5aec8492dcacf86f4fd16775fc2c8b0 100644
--- a/src/app/components/select-field-line/select-field-line.component.ts
+++ b/src/app/components/select-field-line/select-field-line.component.ts
@@ -2,13 +2,16 @@ import { Component, Input } from "@angular/core";
 
 import { SelectField } from "../../formulaire/select-field";
 import { SelectEntry } from "../../formulaire/select-entry";
-import { GenericSelectComponent } from "../generic-select/generic-select.component";
 
 @Component({
     selector: "select-field-line",
-    templateUrl: "./select-field-line.component.html"
+    // templateUrl: "./select-field-line.component.html",
+    templateUrl: "../generic-select/generic-select.component.html",
+    styleUrls: [
+        "./select-field-line.component.scss"
+    ]
 })
-export class SelectFieldLineComponent extends GenericSelectComponent<SelectEntry> {
+export class SelectFieldLineComponent {
     @Input()
     private _select: SelectField;
 
diff --git a/src/app/config.json b/src/app/config.json
new file mode 100644
index 0000000000000000000000000000000000000000..cb7754e64b271455f1e1d128346868fd88441224
--- /dev/null
+++ b/src/app/config.json
@@ -0,0 +1,63 @@
+{
+    "params": {
+        "displayPrecision": 0.001,
+        "computePrecision": 0.0001,
+        "newtonMaxIterations": 50,
+        "language": "fr"
+    },
+    "themes": [
+        {
+            "name": "PASSE_A_BASSIN",
+            "image": {
+                "path": "passe-bassin.jpg",
+                "title": "Passe à poisson sur le Lez, entre Bollène et Suze",
+                "credits": "Hervé Capra / Irstea"
+            },
+            "calculators": [ 5, 6, 10, 9 ]
+        },
+        {
+            "name": "PASSE_NATURELLE",
+            "image": {
+                "path": "passe-naturelle.jpg",
+                "title": "Passe à poisson sur le petit Buech, seuil de Chiala ou des Savoillons",
+                "credits": "Catherine Tailleux / Irstea"
+            },
+            "calculators": [ 11 ]
+        },
+        {
+            "name": "HYDRAULIQUE_A_SURFACE_LIBRE",
+            "image": {
+                "path": "surface-libre.jpg",
+                "title": "Jaugeage sur le canal du Congrès (Domaine du Merle, Salon de Provence)",
+                "credits": "David Dorchies / Irstea"
+            },
+            "calculators": [ 2, 3, 4 ]
+        },
+        {
+            "name": "HYDRAULIQUE_EN_CHARGE",
+            "image": {
+                "path": "en-charge.jpg",
+                "title": "Asperseurs dans la vallée de la Méouge",
+                "credits": "Catherine Tailleux / Irstea"
+            },
+            "calculators": [ 1, 0 ]
+        },
+        {
+            "name": "LOIS_D_OUVRAGES",
+            "image": {
+                "path": "ouvrages.jpg",
+                "title": "Seuil triangulaire (Halle hydraulique SupAgro Montpellier)",
+                "credits": "David Dorchies / Irstea"
+            },
+            "calculators": [ 8, 9, 10 ]
+        },
+        {
+            "_comment": "card for calculators not used in any theme",
+            "image": {
+                "path": "autres.jpg",
+                "title": "",
+                "credits": ""
+            }
+        }
+    ]
+}
diff --git a/src/app/directives/flex-xxs.directive.ts b/src/app/directives/flex-xxs.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e4df90248cfa5b2c76f0b77b7d804d7e10fd217e
--- /dev/null
+++ b/src/app/directives/flex-xxs.directive.ts
@@ -0,0 +1,65 @@
+import { Directive } from "@angular/core";
+import { BREAKPOINT, ShowHideDirective, FlexDirective } from "@angular/flex-layout";
+
+const XXS_BREAKPOINTS = [
+    {
+        alias: "xxs",
+        mediaQuery: "screen and (max-width: 479px)",
+        overlapping: false
+    },
+    {
+        alias: "xs", // redéfinition
+        mediaQuery: "screen and (min-width: 480px) screen and (max-width: 599px)",
+        overlapping: false
+    },
+    {
+        alias: "gt-xxs",
+        mediaQuery: "screen and (min-width: 480px)",
+        overlapping: false
+    },
+    {
+        alias: "lt-xs",
+        mediaQuery: "screen and (max-width: 479px)",
+        overlapping: false
+    }
+];
+
+export const CustomBreakPointsProvider = {
+    provide: BREAKPOINT,
+    useValue: XXS_BREAKPOINTS,
+    multi: true
+};
+
+const inputsXxs = [ "fxHide.xxs" ];
+const inputsGtXxs = [ "fxHide.gt-xxs" ];
+const inputsLtXs = [ "fxHide.lt-xs" ];
+
+@Directive({
+    // tslint:disable-next-line:directive-selector
+    selector: `[fxHide.xxs]`,
+    // tslint:disable-next-line:use-input-property-decorator
+    inputs: inputsXxs
+})
+export class FlexXxsShowHideDirective extends ShowHideDirective {
+    protected inputs = inputsXxs;
+}
+
+@Directive({
+    // tslint:disable-next-line:directive-selector
+    selector: `[fxHide.gt-xxs]`,
+    // tslint:disable-next-line:use-input-property-decorator
+    inputs: inputsGtXxs
+})
+export class FlexGtXxsShowHideDirective extends ShowHideDirective {
+    protected inputs = inputsGtXxs;
+}
+
+@Directive({
+    // tslint:disable-next-line:directive-selector
+    selector: `[fxHide.lt-xs]`,
+    // tslint:disable-next-line:use-input-property-decorator
+    inputs: inputsLtXs
+})
+export class FlexLtXsShowHideDirective extends ShowHideDirective {
+    protected inputs = inputsLtXs;
+}
diff --git a/src/app/directives/jalhyd-async-model-validation.directive.ts b/src/app/directives/jalhyd-async-model-validation.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b8c7bebd00cc6bb8b8eafacc61b5713837442655
--- /dev/null
+++ b/src/app/directives/jalhyd-async-model-validation.directive.ts
@@ -0,0 +1,37 @@
+import { Validator, AbstractControl, ValidationErrors, NG_ASYNC_VALIDATORS } from "@angular/forms";
+import { Directive, Input, forwardRef } from "@angular/core";
+import { NgBaseParam } from "../components/base-param-input/base-param-input.component";
+import { Observable } from "rxjs";
+
+/**
+ * Asynchronous validator for Ngparam, relying on JaLHyd ParamDefinition model
+ */
+@Directive({
+    selector: "[appAsyncJalhydModelValidation]",
+    providers: [ {
+        provide: NG_ASYNC_VALIDATORS,
+        useExisting: forwardRef(() => JalhydAsyncModelValidationDirective),
+        multi: true
+    } ]
+})
+export class JalhydAsyncModelValidationDirective implements Validator {
+    @Input("appAsyncJalhydModelValidation") ngBaseParam: NgBaseParam;
+
+    validate(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
+        let errorPromiseReturn = new Promise(res => {
+            res(null);
+        }); // no error, everything OK
+        const result = this.ngBaseParam.validateModelValue(control.value);
+        if (! result.isValid) {
+            errorPromiseReturn = new Promise<ValidationErrors>(res => {
+                res({
+                    "jalhydModel": {
+                        value: control.value,
+                        message: result.message
+                    }
+                });
+            });
+        }
+        return errorPromiseReturn;
+    }
+}
diff --git a/src/app/directives/jalhyd-model-validation.directive.ts b/src/app/directives/jalhyd-model-validation.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..06a9bf67363afa05e468202f6417b94247a126be
--- /dev/null
+++ b/src/app/directives/jalhyd-model-validation.directive.ts
@@ -0,0 +1,39 @@
+import { NG_VALIDATORS, Validator, AbstractControl, ValidatorFn } from "@angular/forms";
+import { Directive, Input } from "@angular/core";
+import { NgBaseParam } from "../components/base-param-input/base-param-input.component";
+
+/**
+ * Synchronous validator for Ngparam, relying on JaLHyd ParamDefinition model
+ */
+@Directive({
+    selector: "[appJalhydModelValidation]",
+    providers: [ {
+        provide: NG_VALIDATORS,
+        useExisting: JalhydModelValidationDirective,
+        multi: true
+    } ]
+})
+export class JalhydModelValidationDirective implements Validator {
+    @Input("appJalhydModelValidation") ngBaseParam: NgBaseParam;
+
+    validate(control: AbstractControl): { [key: string]: any } | null {
+        const mv = jalhydModelValidator(this.ngBaseParam)(control);
+        return mv;
+    }
+}
+
+export function jalhydModelValidator(ngBaseParam: NgBaseParam): ValidatorFn {
+    return (control: AbstractControl): { [key: string]: any } | null => {
+        let errorReturn = null; // no error, everything OK
+        const result = ngBaseParam.validateModelValue(control.value);
+        if (result && ! result.isValid) {
+            errorReturn = {
+                "jalhydModel": {
+                    value: control.value,
+                    message: result.message
+                }
+            };
+        }
+        return errorReturn;
+    };
+}
diff --git a/src/app/formulaire/definition/concrete/form-parallel-structures.ts b/src/app/formulaire/definition/concrete/form-parallel-structures.ts
index 4d5697248f5612f0a8fa5eb16aa24ac64d12132a..8e563f732817ee5ff32917aefa00ea3a4764fb40 100644
--- a/src/app/formulaire/definition/concrete/form-parallel-structures.ts
+++ b/src/app/formulaire/definition/concrete/form-parallel-structures.ts
@@ -223,11 +223,9 @@ export class FormulaireParallelStructure extends FormulaireDefinition {
             }
         }
 
-        if (select === undefined) {
-            return undefined;
+        if (select !== undefined) {
+            return { "select": select, "entry": selectEntry };
         }
-
-        return { "select": select, "entry": selectEntry };
     }
 
     /**
@@ -244,8 +242,6 @@ export class FormulaireParallelStructure extends FormulaireDefinition {
                 }
             }
         }
-
-        return undefined;
     }
 
     /**
diff --git a/src/app/formulaire/definition/concrete/form-regime-uniforme.ts b/src/app/formulaire/definition/concrete/form-regime-uniforme.ts
index b7549d6764dfdb11b89f4b6e65f90b4871981aec..63ed5d5a8249cba95173d3ce91ed355774f7accf 100644
--- a/src/app/formulaire/definition/concrete/form-regime-uniforme.ts
+++ b/src/app/formulaire/definition/concrete/form-regime-uniforme.ts
@@ -1,5 +1,5 @@
 import { FormDefFixedVar } from "../form-def-fixedvar";
-import { CalculatorType, ComputeNodeType, IObservable, Observer } from "jalhyd";
+import { IObservable, Observer } from "jalhyd";
 import { FormResultFixedVar } from "../form-result-fixedvar";
 import { FormulaireDefinition } from "../form-definition";
 import { FormDefSection } from "../form-def-section";
diff --git a/src/app/formulaire/definition/concrete/form-section-parametree.ts b/src/app/formulaire/definition/concrete/form-section-parametree.ts
index 5b2e2bd2d29f3564f37d141ae3f8364fc464aea9..e5d22bcf919c95bb75d229c64fe0da09be0dd916 100644
--- a/src/app/formulaire/definition/concrete/form-section-parametree.ts
+++ b/src/app/formulaire/definition/concrete/form-section-parametree.ts
@@ -6,7 +6,7 @@ import { NgParameter } from "../../ngparam";
 import { InputField } from "../../input-field";
 import { FormComputeSectionParametree } from "../form-compute-section-parametree";
 import { FormulaireDefinition } from "../form-definition";
-import { InternationalisationService } from "../../../services/internationalisation/internationalisation.service";
+import { I18nService } from "../../../services/internationalisation/internationalisation.service";
 import { CalculatorResults } from "../../../results/calculator-results";
 import { FormDefFixedVar } from "../form-def-fixedvar";
 import { FieldSet } from "../../fieldset";
diff --git a/src/app/formulaire/definition/form-compute-fixedvar.ts b/src/app/formulaire/definition/form-compute-fixedvar.ts
index 873488018729347e8e215267e12933ac9a3eda7f..7b64ec15da3be9b36d88fe94169fe05562f711c0 100644
--- a/src/app/formulaire/definition/form-compute-fixedvar.ts
+++ b/src/app/formulaire/definition/form-compute-fixedvar.ts
@@ -26,7 +26,6 @@ export class FormComputeFixedVar extends FormCompute {
                 return p;
             }
         }
-        return undefined;
     }
 
     private getComputedParameter(): NgParameter {
diff --git a/src/app/formulaire/definition/form-def-fixedvar.ts b/src/app/formulaire/definition/form-def-fixedvar.ts
index 9e72537aad47170faa27b75b90e5a6df09bd57d5..d34f67110ac5f75bf50c5fc2e78637133d63ba95 100644
--- a/src/app/formulaire/definition/form-def-fixedvar.ts
+++ b/src/app/formulaire/definition/form-def-fixedvar.ts
@@ -66,7 +66,7 @@ export class FormDefFixedVar {
      * action2 : reset (à FIX) de tous les autres paramètres que celui modifié sauf celui/ceux à VAR
      * action3 : reset (à FIX) de tous les autres paramètres que celui modifié
      * action4 : mettre le paramètre désigné par la conf comme "par défault" à CAL
-     * action5 : mettre le 1er paramètre de la calculette à CAL
+     * action5 : mettre le 1er paramètre du module de calcul à CAL
      */
     protected processRadioStateChange(sourceParam: NgParameter, oldState: ParamValueMode) {
         switch (oldState) {
@@ -143,6 +143,7 @@ export class FormDefFixedVar {
 
             for (const p of this._formBase.allFormElements) {
                 if (p instanceof NgParameter) {
+                    // change all radio button groups except the one that sent the event
                     if (p.radioConfig === ParamRadioConfig.CAL && p.radioState === ParamRadioConfig.FIX && p !== sourceParam) {
                         newCal = p;
                         break;
@@ -152,7 +153,8 @@ export class FormDefFixedVar {
                     break;
                 }
             }
-
+            // if the current calculated parameter was set to another mode, set a new param
+            // to calculated mode (there must always be at least one)
             if (newCal) {
                 newCal.valueMode = ParamValueMode.CALCUL;
             }
@@ -173,9 +175,6 @@ export class FormDefFixedVar {
     public onRadioClick(info: any) {
         const param: NgParameter = info.param; // paramètre source de l'événement radio
         const old: ParamValueMode = info.oldValueMode; // ancien état (radio)
-
-        // this.logParams();
         this.resetRadiosAndResults(param, old);
-        // this.logParams();
     }
 }
diff --git a/src/app/formulaire/definition/form-def-section.ts b/src/app/formulaire/definition/form-def-section.ts
index 734243db275a0149fe78bdf08219e4bd7df11932..f4fe312ca445de92751bd3f3db6fa3ea73b42120 100644
--- a/src/app/formulaire/definition/form-def-section.ts
+++ b/src/app/formulaire/definition/form-def-section.ts
@@ -53,7 +53,7 @@ export class FormDefSection {
      * @param fs nouveau FieldSet
      */
     public afterParseFieldset(fs: FieldSet) {
-        if (this.hasSectionNodeTypeSource) { // s'il existe un menu de choix de section dans la calculette
+        if (this.hasSectionNodeTypeSource) { // s'il existe un menu de choix de section dans le module de calcul
             const sel = fs.getFormulaireNodeById(this._sectionSourceId);
             if (sel) {
                 // on abonne le formulaire aux propriétés du FieldSet pour MAJ du nub, reset du formulaire, ...
diff --git a/src/app/formulaire/definition/form-definition.ts b/src/app/formulaire/definition/form-definition.ts
index a8a00aaa62ccf32b5f1c5bd0fddf85c8805b0439..390718dcd6c94fe67c1efa92400c9b13a1958617 100644
--- a/src/app/formulaire/definition/form-definition.ts
+++ b/src/app/formulaire/definition/form-definition.ts
@@ -20,7 +20,7 @@ import { CalculatorResults } from "../../results/calculator-results";
  */
 export abstract class FormulaireDefinition extends FormulaireNode implements Observer {
     /**
-     * nom de la calculette
+     * nom du module de calcul
      */
     private _calculatorName: string;
 
@@ -70,8 +70,7 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs
         this.notifyObservers({
             "action": "nameChanged",
             "name": name
-        },
-            this);
+        }, this);
     }
 
     public get jsonConfig(): {} {
@@ -133,8 +132,6 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs
         if (json["type"] === "options") {
             return json[option];
         }
-
-        return undefined;
     }
 
     public afterParseFieldset(fs: FieldSet) {
@@ -147,6 +144,14 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs
         return res;
     }
 
+    /**
+     * @return true si le SessionNub attaché (ou un de ses enfants) a l'uid donné
+     * @param uid id à rechercher
+     */
+    public hasNubId(uid: string): boolean {
+        return this._currentNub.uid === uid;
+    }
+
     public moveFieldsetUp(fs: FieldSet) {
     }
 
@@ -206,7 +211,7 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs
         this.initParse();
 
         // analyse des options globales
-        // il est utile de le faire avant le reste pour les calculettes utilisant
+        // il est utile de le faire avant le reste pour les modules de calcul utilisant
         // des sections (id des selects type de section/variable à calculer)
 
         // tslint:disable-next-line:forin
@@ -224,7 +229,7 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs
     /**
      * 2ème passe d'analyse de la configuration
      */
-    public parseConfig(json: {}) {
+    public parseConfig(json?: {}) {
         if (json !== undefined) {
             this._jsonConfig = json;
         }
@@ -255,7 +260,7 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs
                     break;
 
                 default:
-                    throw new Error(`type d'objet de calculette ${type} non pris en charge`);
+                    throw new Error(`type d'objet de module de calcul ${type} non pris en charge`);
             }
         }
 
@@ -293,7 +298,6 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs
                 }
             }
         }
-        return undefined;
     }
 
     public getDisplayedParamFromState(st: ParamRadioConfig): NgParameter {
@@ -304,7 +308,6 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs
                 }
             }
         }
-        return undefined;
     }
 
     public getDisplayedParamListFromState(st: ParamRadioConfig): NgParameter[] {
@@ -322,7 +325,6 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs
         if (res instanceof Field) {
             return res;
         }
-        return undefined;
     }
 
     public getParameterValue(symbol: string): number {
@@ -523,6 +525,44 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs
         }
     }
 
+    /**
+     * MAJ des liens entre paramètres lors de la désérialisation JSON
+     */
+
+    private getNthFieldset(n: number): FieldSet {
+        let i = 0;
+        for (const e of this.topFormElements) {
+            if (e instanceof FieldSet) {
+                if (i === n) {
+                    return e;
+                }
+                i++;
+            }
+        }
+
+        return undefined;
+    }
+
+    /**
+      * @param json conf du formulaire
+      * @param uidMap table de correspondance uid dans le fichier de conf <-> uid en mémoire
+      */
+    public updateParamsLinks(json: {}, uidMap: {}[]) {
+        for (const ks in json) {
+            switch (ks) {
+                case "elements":
+                    let n = 0;
+                    for (const e of json[ks]) {
+                        if (Object.keys(e)[0] === "fieldset") {
+                            this.getNthFieldset(n).updateParamsLinks(e["fieldset"], uidMap);
+                            n++;
+                        }
+                    }
+                    break;
+            }
+        }
+    }
+
     //  interface Observer
 
     public update(sender: any, data: any) {
diff --git a/src/app/formulaire/dependency/dependency.ts b/src/app/formulaire/dependency/dependency.ts
index 368ac35a0078f35f65e8ea9b807484535e93223e..fe7426f55bed6b9e75365cce30b1a5e9c1aad49a 100644
--- a/src/app/formulaire/dependency/dependency.ts
+++ b/src/app/formulaire/dependency/dependency.ts
@@ -28,7 +28,6 @@ export abstract class Dependency {
             }
             parentNode = parentNode.parent;
         }
-        return undefined;
     }
 
     protected get masterElement(): FormulaireElement {
diff --git a/src/app/formulaire/fieldset.ts b/src/app/formulaire/fieldset.ts
index ed645c97bd70120813916c1d8e63e160e7d9b9bd..73740f7ecee0809098836692fa016d378084349b 100644
--- a/src/app/formulaire/fieldset.ts
+++ b/src/app/formulaire/fieldset.ts
@@ -76,7 +76,6 @@ export class FieldSet extends FormulaireElement implements Observer {
                 n++;
             }
         }
-        return undefined;
     }
 
     public get isValid(): boolean {
@@ -129,7 +128,7 @@ export class FieldSet extends FormulaireElement implements Observer {
 
     /**
      * crée un input
-     * @param json definition de l'input, extrait du fichier de conf de la calculette
+     * @param json definition de l'input, extrait du fichier de conf du module de calcul
      * @param node_type_filter filtre sur le type de noeud (input créé si undefined ou égal)
      * @param default_radio_config config du radio fixé/à varier/à calculer
      */
@@ -221,7 +220,7 @@ export class FieldSet extends FormulaireElement implements Observer {
         this.updateLocalisation();
 
         // MAJ des selects avec les valeurs actuelles des propriétés
-        // spécifique à chaque calculette, à revoir
+        // spécifique à chaque modul de calcul, à revoir
 
         if (this._confId === "fs_ouvrage") {
             const sf1: SelectField = this.getFormulaireNodeById("select_ouvrage") as SelectField;
@@ -314,8 +313,6 @@ export class FieldSet extends FormulaireElement implements Observer {
                 }
             }
         }
-
-        return undefined;
     }
 
     public getNodeParameterValue(symbol: string): number {
@@ -346,7 +343,6 @@ export class FieldSet extends FormulaireElement implements Observer {
                 return FormulaireElement.removePrefix(value, selectFieldId + "_");
             }
         }
-        return undefined;
     }
 
     // interface Observer
@@ -445,4 +441,46 @@ export class FieldSet extends FormulaireElement implements Observer {
             }
         }
     }
+
+    /**
+     * MAJ des liens entre paramètres lors de la désérialisation JSON
+     * @param json conf du fieldset issue du fichier
+     * @param uidMap table de correspondance uid dans le fichier de conf <-> uid en mémoire
+     */
+    public updateParamsLinks(json: {}, uidMap: {}[]) {
+        for (const ks in json) {
+            switch (ks) {
+                case "elements":
+                    for (const e of json[ks]) {
+                        if (Object.keys(e)[0] === "param") {
+                            const prm = e["param"];
+                            if (prm["values"]["mode"] === "LINK") {
+                                // id du formulaire cible dans le fichier
+                                const oldFormUid = +prm["values"]["form_uid"];
+
+                                // correspondance avec l'objet mémoire
+                                let newFormUid;
+                                for (const m of uidMap) {
+                                    if (m["type"] === "form" && m["old"] === oldFormUid) {
+                                        newFormUid = m["new"];
+                                        break;
+                                    }
+                                }
+
+                                // formulaire dont le Nub est la cible du lien
+                                const destForm: FormulaireDefinition
+                                    = ServiceFactory.instance.formulaireService.getFormulaireFromId(newFormUid);
+
+                                // paramètre source (celui qui est lié à une valeur)
+                                const src: NgParameter = this.getFormulaireNodeById(prm["id"]) as NgParameter;
+
+                                // création du lien
+                                src.paramDefinition.defineReference(destForm.currentNub, prm["values"]["ref"]);
+                            }
+                            break;
+                        }
+                    }
+            }
+        }
+    }
 }
diff --git a/src/app/formulaire/formulaire-element.ts b/src/app/formulaire/formulaire-element.ts
index 6933766e0ac491e997304af136818297768afbc8..c2347b6ee8574b780f72c560a8e617024fd087bf 100644
--- a/src/app/formulaire/formulaire-element.ts
+++ b/src/app/formulaire/formulaire-element.ts
@@ -5,7 +5,7 @@ import { DependencyCondition, DependencyConditionType } from "./dependency/depen
 import { ValueDependencyCondition } from "./dependency/value-dependency-condition";
 import { ExistenceDependency } from "./dependency/existence-dependency";
 import { DeepFormulaireElementIterator } from "./form-iterator/deep-element-iterator";
-import { InternationalisationService } from "../services/internationalisation/internationalisation.service";
+import { I18nService } from "../services/internationalisation/internationalisation.service";
 import { ServiceFactory } from "../services/service-factory";
 
 /**
@@ -27,7 +27,7 @@ export abstract class FormulaireElement extends FormulaireNode {
      */
     private _labelNumber: number;
 
-    private intlService: InternationalisationService;
+    private intlService: I18nService;
 
     protected _dependencies: Dependency[] = [];
 
@@ -36,13 +36,12 @@ export abstract class FormulaireElement extends FormulaireNode {
             const l = prefix.length;
             return s.substr(l, s.length - l);
         }
-        return undefined;
     }
 
     constructor(parent: FormulaireNode) {
         super(parent);
         this._isDisplayed = true;
-        this.intlService = ServiceFactory.instance.internationalisationService;
+        this.intlService = ServiceFactory.instance.i18nService;
     }
 
     get isDisplayed(): boolean {
@@ -69,7 +68,7 @@ export abstract class FormulaireElement extends FormulaireNode {
     }
 
     public getKids(): FormulaireElement[] {
-        return super.kids as FormulaireElement[];
+        return this.kids as FormulaireElement[];
     }
 
     /**
diff --git a/src/app/formulaire/formulaire-node.ts b/src/app/formulaire/formulaire-node.ts
index 44245795e47a5a381c6f6382603a7d392ff6f5ac..627e38f7d9225419a966d63c0d0d96801dede6c0 100644
--- a/src/app/formulaire/formulaire-node.ts
+++ b/src/app/formulaire/formulaire-node.ts
@@ -70,8 +70,6 @@ export abstract class FormulaireNode implements IObservable {
                 return res;
             }
         }
-
-        return undefined;
     }
 
     /**
@@ -88,8 +86,6 @@ export abstract class FormulaireNode implements IObservable {
                 return res;
             }
         }
-
-        return undefined;
     }
 
     /**
diff --git a/src/app/formulaire/immediate-error-state-matcher.ts b/src/app/formulaire/immediate-error-state-matcher.ts
new file mode 100644
index 0000000000000000000000000000000000000000..687edca5be1449ddc2dbe80742ba451a0eeb0269
--- /dev/null
+++ b/src/app/formulaire/immediate-error-state-matcher.ts
@@ -0,0 +1,16 @@
+import { ErrorStateMatcher } from "@angular/material";
+import { FormControl, FormGroupDirective, NgForm } from "@angular/forms";
+
+/**
+ * An error state matcher for Angular Forms that displays errors immediately,
+ * instead of waiting for the focus to get out of the input at least once
+ */
+export class ImmediateErrorStateMatcher implements ErrorStateMatcher {
+
+    constructor() { }
+
+    isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
+        const isSubmitted = form && form.submitted;
+        return !!(control && control.invalid /* && (control.dirty || control.touched || isSubmitted) */ );
+    }
+}
diff --git a/src/app/formulaire/ngparam.ts b/src/app/formulaire/ngparam.ts
index 50125790a4fd68293ae9ae70ebc2d952eb698f0b..82cca313a78d8406fadd269a9385505202866da5 100644
--- a/src/app/formulaire/ngparam.ts
+++ b/src/app/formulaire/ngparam.ts
@@ -54,7 +54,7 @@ export class NgParameter extends InputField implements Observer {
         this._confId = id;
     }
 
-    public get _paramValues() { // @TODO remettre en private ! (debug)
+    private get _paramValues() {
         return this._paramDef.paramValues;
     }
 
@@ -148,6 +148,17 @@ export class NgParameter extends InputField implements Observer {
         return this._paramDef.isValid;
     }
 
+    public get title(): string {
+        let t = "";
+        if (this.label !== undefined) {
+            t = this.label;
+        }
+        if (this.unit !== undefined && this.unit !== "") {
+            t = t + " (" + this.unit + ")";
+        }
+        return t;
+    }
+
     public get valuesIterator(): INumberIterator {
         return this._paramDef.valuesIterator;
     }
@@ -155,7 +166,8 @@ export class NgParameter extends InputField implements Observer {
     public radioConfig: ParamRadioConfig;
 
     /**
-     * true si ce paramètre est celui par défaut dans un formulaire (cf. fichier de conf des calculettes, objet "options", champ "idCal")
+     * true si ce paramètre est celui par défaut dans un formulaire
+     * (cf. fichier de conf des modules de calcul, objet "options", champ "idCal")
      */
     public isDefault = false; // archi bug du langage ! si on relit cette propriété sans l'avoir modifiée entre-temps, elle vaut undefined !
 
@@ -326,6 +338,12 @@ export class NgParameter extends InputField implements Observer {
             case ParamValueMode.LISTE:
                 res["values"] = this._paramValues.valueList;
                 break;
+
+            case ParamValueMode.LINK:
+                // @TODO copié à l'arrache, vérifier que ça marche
+                res["form_uid"] = ServiceFactory.instance.formulaireService.getFormulaireFromNubId(this._paramDef.referencedNub["uid"]).uid;
+                res["ref"] = this.paramDefinition.referenceDefinition;
+                break;
         }
         return res;
     }
@@ -359,6 +377,9 @@ export class NgParameter extends InputField implements Observer {
                 this._paramValues.valueMode = ParamValueMode.CALCUL;
                 break;
 
+            case ParamValueMode.LINK:
+                break;  // cf FormulaireService.updateParamsLinks()
+
             default:
                 throw new Error(`session file : invalid value mode '${json["mode"]}' in param object`);
         }
diff --git a/src/app/formulaire/select-field.ts b/src/app/formulaire/select-field.ts
index 3e3bd899d61a39a0613b217174e73a1f540842c3..3bfadc6cf957cd3df97395df4cb7344aafd4ee75 100644
--- a/src/app/formulaire/select-field.ts
+++ b/src/app/formulaire/select-field.ts
@@ -35,7 +35,6 @@ export class SelectField extends Field {
                 return se;
             }
         }
-        return undefined;
     }
 
     public getValue() {
diff --git a/src/app/results/var-results.ts b/src/app/results/var-results.ts
index 4776f5d8837a41013655fb42cb618e8bf34f8064..657dd44f690b3ea84912c475aa618697e1421765 100644
--- a/src/app/results/var-results.ts
+++ b/src/app/results/var-results.ts
@@ -140,7 +140,7 @@ export class VarResults extends CalculatedParamResults {
 
         // entêtes des résultats supplémentaires
 
-        const intlService = ServiceFactory.instance.internationalisationService;
+        const intlService = ServiceFactory.instance.i18nService;
         for (const k of this._extraResultKeys) {
             this._extraResultHeaders.push(intlService.getExtraResLabel(k));
         }
diff --git a/src/app/services/app-setup/app-setup.service.ts b/src/app/services/app-setup/app-setup.service.ts
index 9c24f2d1131c05fdd11e14ad8d8e13fc199cf59d..6a6069c637bfdc6986892a5e09ac8833bbb10a21 100644
--- a/src/app/services/app-setup/app-setup.service.ts
+++ b/src/app/services/app-setup/app-setup.service.ts
@@ -1,14 +1,56 @@
+import { HttpService } from "../http/http.service";
+import { Injectable } from "@angular/core";
+import { Observable } from "jalhyd";
+
 /**
  * Stores app preferences
- * @TODO save in cookie / localStorage ?
  */
-export class ApplicationSetupService {
+@Injectable()
+export class ApplicationSetupService extends Observable {
+
+    private CONFIG_FILE_PATH = "app/config.json";
 
+    // default builtin values
     public displayPrecision = 0.001;
     public computePrecision = 0.0001;
-    public newtonMaxIter = 50;
+    public newtonMaxIterations = 50;
+    private _language = "fr";
+    /** themes to group calculators, for displaying on the front page */
+    public themes: any[];
+
+    public constructor(
+        private httpService: HttpService
+    ) {
+
+        super();
+        this.readValuesFromConfig();
+    }
 
     public get displayDigits() {
         return -Math.log10(this.displayPrecision);
     }
+
+    public set language(lang: string) {
+        this._language = lang;
+        this.notifyObservers(null);
+    }
+
+    public get language(): string {
+        return this._language;
+    }
+
+    // @TODO save preferences in cookie / localStorage ?
+
+    // read default values from config and notify observers
+    private readValuesFromConfig() {
+        this.httpService.httpGetRequestPromise(this.CONFIG_FILE_PATH).then((data: any) => {
+            this.displayPrecision = data.params.displayPrecision;
+            this.computePrecision = data.params.computePrecision;
+            this.newtonMaxIterations = data.params.newtonMaxIterations;
+            this.language = data.params.language;
+            this.themes = data.themes;
+
+            this.notifyObservers(null);
+        });
+    }
 }
diff --git a/src/app/services/formulaire/formulaire.service.ts b/src/app/services/formulaire/formulaire.service.ts
index e6570d34fbaf970365ac24660d389b6c70030b76..dda5ad7a3f3d766173caec498ca3309bfdd1fcc6 100644
--- a/src/app/services/formulaire/formulaire.service.ts
+++ b/src/app/services/formulaire/formulaire.service.ts
@@ -1,14 +1,12 @@
 
 import { Injectable } from "@angular/core";
-import "rxjs/add/operator/toPromise";
 import { decode } from "he";
 import { saveAs } from "file-saver";
 
 import { CalculatorType, EnumEx, Observable, ParamDefinition } from "jalhyd";
 
-import { ServiceFactory } from "../service-factory";
 import { HttpService } from "../../services/http/http.service";
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
 import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
 import { FormulaireElement } from "../../formulaire/formulaire-element";
 import { InputField } from "../../formulaire/input-field";
@@ -29,17 +27,20 @@ export class FormulaireService extends Observable {
 
     private _currentFormId: string = null;
 
-    constructor() {
+    constructor(
+        private i18nService: I18nService,
+        private httpService: HttpService) {
+
         super();
         this._formulaires = [];
     }
 
-    private get _intlService(): InternationalisationService {
-        return ServiceFactory.instance.internationalisationService;
+    private get _intlService(): I18nService {
+        return this.i18nService;
     }
 
     private get _httpService(): HttpService {
-        return ServiceFactory.instance.httpService;
+        return this.httpService;
     }
 
     public get formulaires(): FormulaireDefinition[] {
@@ -48,7 +49,7 @@ export class FormulaireService extends Observable {
 
     private loadLocalisation(calc: CalculatorType): Promise<any> {
         const f: string = this.getConfigPathPrefix(calc) + this._intlService.currentLanguage.tag + ".json";
-        const prom = this._httpService.httpGetRequestPromise(undefined, undefined, undefined, f);
+        const prom = this._httpService.httpGetRequestPromise(f);
 
         return prom.then((j) => {
             return j as StringMap;
@@ -101,7 +102,7 @@ export class FormulaireService extends Observable {
 
     public loadConfig(ct: CalculatorType): Promise<any> {
         const f: string = this.getConfigPathPrefix(ct) + "config.json";
-        return this._httpService.httpGetRequestPromise(undefined, undefined, undefined, f);
+        return this._httpService.httpGetRequestPromise(f);
     }
 
     private newFormulaire(ct: CalculatorType, jsonState?: {}): FormulaireDefinition {
@@ -137,7 +138,7 @@ export class FormulaireService extends Observable {
                 break;
 
             default:
-                throw new Error(`FormulaireService.newFormulaire() : type de calculette ${CalculatorType[ct]} non pris en charge`);
+                throw new Error(`FormulaireService.newFormulaire() : type de module de calcul ${CalculatorType[ct]} non pris en charge`);
         }
 
         f.defaultProperties["calcType"] = ct;
@@ -157,30 +158,19 @@ export class FormulaireService extends Observable {
         const prom: Promise<any> = this.loadConfig(ct);
         return prom.then(s => {
             f.preparseConfig(s);
-            return f;
-        }).then(fi => {
             if (jsonState === undefined) {
-                fi.calculatorName = decode(this.getLocalisedTitleFromCalculatorType(ct) + " (" + fi.uid + ")");
+                f.calculatorName = decode(this.getLocalisedTitleFromCalculatorType(ct));
             }
-            fi.initNub();
-            return fi;
-        }).then(fi => {
-            fi.parseConfig(undefined);
-            return fi;
-        }).then(fi => {
+            f.initNub();
+            f.parseConfig();
             if (jsonState !== undefined) {
-                fi.deserialiseJSON(jsonState);
-                const props = jsonState["props"];
+                f.deserialiseJSON(jsonState);
             }
-            return fi;
-        }).then(fi => {
             // la méthode loadUpdateFormulaireLocalisation retourne une Promise; le fait de retourner une Promise dans un then
             // fait que le then suivant est exécuté juste après.
-            return this.loadUpdateFormulaireLocalisation(fi);
+            return this.loadUpdateFormulaireLocalisation(f);
         }).then(fi => {
             fi.applyDependencies();
-            return fi;
-        }).then(fi => {
             this.notifyObservers(
                 {
                     "action": "createForm",
@@ -196,8 +186,6 @@ export class FormulaireService extends Observable {
                 return f;
             }
         }
-
-        return undefined;
     }
 
     public getInputField(formId: string, elemId: string): InputField {
@@ -233,7 +221,6 @@ export class FormulaireService extends Observable {
                 }
             }
         }
-        return undefined;
     }
 
     public getParamdefParentForm(prm: ParamDefinition): FormulaireDefinition {
@@ -246,7 +233,17 @@ export class FormulaireService extends Observable {
                 }
             }
         }
-        return undefined;
+    }
+
+    /**
+     * retrouve un formulaire à partir d'un uid de Nub
+     */
+    public getFormulaireFromNubId(uid: string) {
+        for (const f of this._formulaires) {
+            if (f.hasNubId(uid)) {
+                return f;
+            }
+        }
     }
 
     public getConfigPathPrefix(ct: CalculatorType): string {
@@ -360,9 +357,9 @@ export class FormulaireService extends Observable {
     }
 
     /**
-     * charge une session en tenant compte des calculettes sélectionnées
+     * charge une session en tenant compte des modules de calcul sélectionnées
      * @param f fichier session
-     * @param formInfos infos sur les calculettes @see LoadCalculatorComponent._calculators
+     * @param formInfos infos sur les modules de calcul @see DialogLoadSessionComponent.calculators
      */
     public loadSession(f: File, formInfos: any[]) {
         this.readSingleFile(f).then(s => {
@@ -388,7 +385,7 @@ export class FormulaireService extends Observable {
     }
 
     /**
-     * obtient des infos (nom, uid des calculettes) d'un fichier session
+     * obtient des infos (nom, uid des modules de calcul) d'un fichier session
      * @param f fichier session
      */
     public calculatorInfosFromSessionFile(f: File): Promise<any[]> {
@@ -396,7 +393,7 @@ export class FormulaireService extends Observable {
             const res: any[] = [];
             const session = JSON.parse(s);
 
-            // liste des noms de calculettes
+            // liste des noms de modules de calcul
             for (const k in session) {
                 switch (k) {
                     case "session":
@@ -427,13 +424,13 @@ export class FormulaireService extends Observable {
         });
     }
 
-    private deserialiseForm(elements: {}) {
+    private deserialiseForm(elements: {}): Promise<FormulaireDefinition> {
         const props = elements["props"];
         const ct: CalculatorType = props["calcType"];
-        this.createFormulaire(ct, elements);
+        return this.createFormulaire(ct, elements);
     }
 
-    private deserialiseSessionElement(element: {}, formInfos: any[]) {
+    private deserialiseSessionElement(element: {}, formInfos: any[]): Promise<FormulaireDefinition> {
         const keys = Object.keys(element);
         if (keys.length !== 1) {
             throw new Error(`session file : invalid session object '${element}'`);
@@ -445,7 +442,7 @@ export class FormulaireService extends Observable {
 
                 for (const i of formInfos) {
                     if (i["uid"] === form["uid"] && i["selected"]) {
-                        this.deserialiseForm(form);
+                        return this.deserialiseForm(form);
                     }
                 }
                 break;
@@ -455,12 +452,85 @@ export class FormulaireService extends Observable {
         }
     }
 
+    /**
+     * met à jour les liens d'un formulaire
+     * @param json conf du formulaire
+     * @param formInfos métadonnées sur les formulaires chargés
+     * @param form formulaire dont on met à jour les liens
+     * @param uidMap table de correspondance uid dans le fichier de conf <-> uid en mémoire
+     */
+    private updateFormLinks(json: {}, formInfos: any[], form: FormulaireDefinition, uidMap: {}[]) {
+        for (const i of formInfos) {
+            if (i["uid"] === json["uid"] && i["selected"]) {
+                form.updateParamsLinks(json, uidMap);
+            }
+        }
+    }
+
+    /**
+     * MAJ des liens entre paramètres lors de la désérialisation JSON
+     */
+
+    private updateParamsLinks(json: {}, formInfos: any[], oldFormCount: number) {
+        // table de correspondance des uid fichier <-> objets mémoire
+        // forme : tableau d'objets de la forme :
+        // { "type" : <type de l'objet. "form" pour formulaire>,
+        //   "old": <uid dans le fichier>,
+        //   "new": <uid de l'objet mémoire>}
+
+        const uidMap = [];
+        for (const ks in json) {
+            switch (ks) {
+                case "elements":
+                    let n = oldFormCount;
+                    for (const e of json[ks]) {
+                        if (Object.keys(e)[0] === "form") {
+                            uidMap.push({
+                                "type": "form",
+                                "old": e["form"]["uid"],
+                                "new": this._formulaires[n].uid
+                            });
+                            n++;
+                        }
+                    }
+            }
+        }
+
+        // MAJ liens
+
+        for (const ks in json) {
+            switch (ks) {
+                case "elements":
+                    let n = 0;
+                    for (const e of json[ks]) {
+                        if (Object.keys(e)[0] === "form") {
+                            this.updateFormLinks(e["form"], formInfos, this._formulaires[n + oldFormCount], uidMap);
+                            n++;
+                        }
+                    }
+                    break;
+
+                default:
+                    throw new Error(`session file : invalid key '${ks}' in session object`);
+            }
+        }
+    }
+
     private deserialiseSession(elements: {}, formInfos: any[]) {
+        let p: Promise<FormulaireDefinition>;
+
+        const oldFormCount = this._formulaires.length;
         for (const ks in elements) {
             switch (ks) {
                 case "elements":
                     for (const e of elements[ks]) {
-                        this.deserialiseSessionElement(e, formInfos);
+                        if (p === undefined) {
+                            p = this.deserialiseSessionElement(e, formInfos);
+                        } else {
+                            p = p.then(_ => {
+                                return this.deserialiseSessionElement(e, formInfos);
+                            });
+                        }
                     }
                     break;
 
@@ -468,11 +538,12 @@ export class FormulaireService extends Observable {
                     throw new Error(`session file : invalid key '${ks}' in session object`);
             }
         }
+        p.then(_ => this.updateParamsLinks(elements, formInfos, oldFormCount));
     }
 
     /**
      * @returns liste des valeurs liables à un paramètre sous la forme d'un tableau d'objets
-     * {"param":<paramètre lié>, "nub":<Nub d'origine du paramètre lié>, "formTitle":<nom de la calculette liée au nub>}
+     * {"param":<paramètre lié>, "nub":<Nub d'origine du paramètre lié>, "formTitle":<nom du module de calcul liée au nub>}
      * @param p paramètre qui sert de clé de recherche des paramètres liables
      */
     public getLinkableValues(p: NgParameter): any[] {
diff --git a/src/app/services/http/http.service.ts b/src/app/services/http/http.service.ts
index 613c8fd3de71d5e92493f2ee06974aa18db08aa0..580ffee36e0cc66d25d3c35202180008b424fa45 100644
--- a/src/app/services/http/http.service.ts
+++ b/src/app/services/http/http.service.ts
@@ -10,52 +10,25 @@ export class HttpService {
 
     /**
      * Lance une requête GET (version standard Observable)
-     * @param protocol ex: "https"
-     * @param host ex: "domaine.fr"
-     * @param port ex: 8080
      * @param path ex: /mon-service/toto
      * @param headers ex: new HttpHeaders { "Authorization": "bla" }
      */
-    public httpGetRequest(protocol: string, host: string, port: number, path: string,
-      headers?: HttpHeaders): Observable<Object> {
-
-        let url = "";
-        // 1. construire URL
-        if (host) {
-            url = host;
-        } // else throw Error ?
-        if (protocol) {
-            url = protocol + "://" + url;
-        }
-        if (port) {
-            url += ":" + String(port);
-        }
-        const p = (path === "/") ? "" : path;
-        if (url === "") {
-            url = p;
-        } else {
-            url += "/" + p;
-        }
-        url = encodeURI(url);
-
-        // 2. ajout entêtes
+    public httpGetRequest(path: string, headers?: HttpHeaders): Observable<Object> {
+        // ajout entêtes
         const opts = {};
         if (headers) {
             opts["headers"] = headers;
         }
-
-        // 3. lancement requête
-        return this.http.get(url, opts);
+        // lancement requête
+        return this.http.get(encodeURI(path), opts);
     }
 
     /**
      * Lance une requête GET (version Promise)
      * @see httpGetRequest
      */
-    public httpGetRequestPromise(protocol: string, host: string, port: number, path: string,
-        headers?: HttpHeaders): Promise<Object> {
-
-        const res$: Observable<Object> = this.httpGetRequest(protocol, host, port, path, headers);
+    public httpGetRequestPromise(path: string, headers?: HttpHeaders): Promise<Object> {
+        const res$: Observable<Object> = this.httpGetRequest(path, headers);
         return res$.toPromise();
     }
 
@@ -65,12 +38,12 @@ export class HttpService {
      * @param errorCallback callback en cas d'erreur
      * @see httpGetRequest
      */
-    public httpGetRequestCallbacks(protocol: string, host: string, port: number, path: string,
+    public httpGetRequestCallbacks(path: string,
         processDataCallback: (s: any) => void,
         errorCallback?: (err: any) => void,
         headers?: HttpHeaders): Subscription {
 
-        const res$: Observable<Object> = this.httpGetRequest(protocol, host, port, path, headers);
+        const res$: Observable<Object> = this.httpGetRequest(path, headers);
 
         const annulable = res$.subscribe(
             data => {
diff --git a/src/app/services/internationalisation/internationalisation.service.ts b/src/app/services/internationalisation/internationalisation.service.ts
index 3d571aa73dfef737f65f017f8389b1fb96fb9eb1..5eb90bcc6c84752e416a77a2d2184de4e5e33a93 100644
--- a/src/app/services/internationalisation/internationalisation.service.ts
+++ b/src/app/services/internationalisation/internationalisation.service.ts
@@ -1,10 +1,10 @@
 import { Injectable } from "@angular/core";
-import { Response } from "@angular/http";
 
 import { Message, MessageCode, Observable } from "jalhyd";
 
 import { StringMap } from "../../stringmap";
-import { ServiceFactory } from "../service-factory";
+import { ApplicationSetupService } from "../app-setup/app-setup.service";
+import { HttpService } from "../http/http.service";
 
 /*
   language tag : fr-FR
@@ -43,14 +43,16 @@ export class Language {
 }
 
 @Injectable()
-export class InternationalisationService extends Observable {
+export class I18nService extends Observable {
+
     private _currLang: Language;
-    private _sLang: string;
     private _Messages: StringMap;
-
     private _languages: Language[];
 
-    public constructor() {
+    constructor(
+        private applicationSetupService: ApplicationSetupService,
+        private httpService: HttpService) {
+
         super();
         this._languages = [];
         this._languages.push(new Language(LanguageCode.FRENCH, "fr", "Français"));
@@ -106,7 +108,7 @@ export class InternationalisationService extends Observable {
             this._Messages = undefined;
             const prom = this.httpGetMessages();
 
-            const is: InternationalisationService = this;
+            const is: I18nService = this;
             prom.then((res) => {
                 is.notifyObservers(undefined);
             });
@@ -114,7 +116,7 @@ export class InternationalisationService extends Observable {
     }
 
     private httpGetMessages(): Promise<void> {
-        const is: InternationalisationService = this;
+        const is: I18nService = this;
         const processData = function (s: string) {
             // fermeture nécessaire pour capturer la valeur de this (undefined sinon)
             is._Messages = JSON.parse(s);
@@ -131,12 +133,15 @@ export class InternationalisationService extends Observable {
         }
 
         const f: string = "messages." + l + ".json";
-        return ServiceFactory.instance.httpService.httpGetRequestPromise(undefined, undefined, undefined, "locale/" + f).then(
+        return this.httpService.httpGetRequestPromise("locale/" + f).then(
             (res: any) => { is._Messages = res; }
         );
     }
 
     private getMessageFromCode(c: MessageCode): string {
+        if (! this._Messages) {
+            return `*** Messages not loaded yet ***`;
+        }
         if (this._Messages[MessageCode[c]] === undefined) {
             return `*** Message ${MessageCode[c]} non traduit ***`;
         }
@@ -222,7 +227,7 @@ export class InternationalisationService extends Observable {
         if (match > -1) {
                 return this.localizeText(`INFO_EXTRARES_${label.substring(match).toUpperCase()}_${value}`);
         }
-        const nDigits = ServiceFactory.instance.applicationSetupService.displayDigits;
+        const nDigits = this.applicationSetupService.displayDigits;
         return value.toFixed(nDigits);
     }
 
diff --git a/src/app/services/param/param.service.ts b/src/app/services/param/param.service.ts
index 7f3ef4039a349d0262f1b77a944c5d906dea55e6..d8d36ce021e17b7983832358c5f14dcdcea10a99 100644
--- a/src/app/services/param/param.service.ts
+++ b/src/app/services/param/param.service.ts
@@ -2,19 +2,24 @@ import { ParamDomain, ParamDefinition, ParamDomainValue, ParamCalculability, Ses
 
 import { NgParameter } from "../../formulaire/ngparam";
 import { Injectable } from "@angular/core";
-import { ServiceFactory } from "../service-factory";
-import { InternationalisationService } from "../internationalisation/internationalisation.service";
+import { I18nService } from "../internationalisation/internationalisation.service";
 import { ApplicationSetupService } from "../app-setup/app-setup.service";
 import { FormulaireNode } from "../../formulaire/formulaire-node";
 
 @Injectable()
 export class ParamService {
-    private get _intlService(): InternationalisationService {
-        return ServiceFactory.instance.internationalisationService;
+
+    constructor(
+        private i18nService: I18nService,
+        private applicationSetupService: ApplicationSetupService
+        ) { }
+
+    private get _intlService(): I18nService {
+        return this.i18nService;
     }
 
     private get _appSetupService(): ApplicationSetupService {
-        return ServiceFactory.instance.applicationSetupService;
+        return this.applicationSetupService;
     }
 
     private createAccuracyParameter(): ParamDefinition {
diff --git a/src/app/services/service-factory.ts b/src/app/services/service-factory.ts
index 682cc5c087028c83d8fa68fe58282fe8e783ab5e..0e84febabaaa2f1120406de9e2370b4be7b5d9da 100644
--- a/src/app/services/service-factory.ts
+++ b/src/app/services/service-factory.ts
@@ -1,7 +1,7 @@
 import { ApplicationSetupService } from "./app-setup/app-setup.service";
 import { ParamService } from "./param/param.service";
 import { FormulaireService } from "./formulaire/formulaire.service";
-import { InternationalisationService } from "./internationalisation/internationalisation.service";
+import { I18nService } from "./internationalisation/internationalisation.service";
 import { HttpService } from "./http/http.service";
 
 export class ServiceFactory {
@@ -15,7 +15,7 @@ export class ServiceFactory {
 
     public formulaireService: FormulaireService;
 
-    public internationalisationService: InternationalisationService;
+    public i18nService: I18nService;
 
     public httpService: HttpService;
 
diff --git a/src/assets/images/themes/autres.jpg b/src/assets/images/themes/autres.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..504bcc57bfb9ca3731a109a8ef3866653dc0ef07
Binary files /dev/null and b/src/assets/images/themes/autres.jpg differ
diff --git a/src/assets/images/themes/en-charge.jpg b/src/assets/images/themes/en-charge.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..f78e1369a0f4b57397270f3ce7138d5a33a7c60d
Binary files /dev/null and b/src/assets/images/themes/en-charge.jpg differ
diff --git a/src/assets/images/themes/ouvrages.jpg b/src/assets/images/themes/ouvrages.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..d33f5bea990e5b5891d27a9ef454e22a9882b321
Binary files /dev/null and b/src/assets/images/themes/ouvrages.jpg differ
diff --git a/src/assets/images/themes/passe-bassin.jpg b/src/assets/images/themes/passe-bassin.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..8a6fd88f8dbddb0a90931ca8d4519e824f5d571b
Binary files /dev/null and b/src/assets/images/themes/passe-bassin.jpg differ
diff --git a/src/assets/images/themes/passe-naturelle.jpg b/src/assets/images/themes/passe-naturelle.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..697068b99d6492ec246cb2687813993bf7ff8e51
Binary files /dev/null and b/src/assets/images/themes/passe-naturelle.jpg differ
diff --git a/src/assets/images/themes/surface-libre.jpg b/src/assets/images/themes/surface-libre.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..9d82f1452b0608abd108e7ccf4220b97b86f268d
Binary files /dev/null and b/src/assets/images/themes/surface-libre.jpg differ
diff --git a/src/index.html b/src/index.html
index 3f118aead63915bdeb163647acea383a103f96a6..f62afd41000d81648d86cc29163e458c7e7045c9 100644
--- a/src/index.html
+++ b/src/index.html
@@ -8,29 +8,8 @@
 
   <meta name="viewport" content="width=device-width, initial-scale=1">
 
-  <!-- ressources pour faire fonctionner le toggler de la navbar -->
-  <!-- <script src="https://code.jquery.com/jquery-3.1.1.slim.min.js" integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n" -->
-  <script href="dependencies/jquery-3.1.1.slim.min.js" integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n"
-    crossorigin="anonymous"></script>
-
-  <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js" integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb" -->
-  <script href="dependencies/tether.min.js" integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb"
-    crossorigin="anonymous"></script>
-
-  <!-- <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn" -->
-  <script href="dependencies/bootstrap.min.js" integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn"
-    crossorigin="anonymous"></script>
-
-  <!-- Load the Angular Material stylesheet -->
-  <!--
-  <link href="https://unpkg.com/@angular/material/prebuilt-themes/indigo-pink.css" rel="stylesheet">
   <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
-  <style>
-    body {
-      font-family: Roboto, Arial, sans-serif;
-    }
-  </style>
--->
+  <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet">
 </head>
 
 <body>
diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index 01e69db11bc9fde4f4effc872fd0e6d299e02c3f..34099e643b960efc8817c7fcda4d0e3c7a1225d2 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -8,8 +8,11 @@
     "ERROR_DICHO_NULL_STEP": "Dichotomy (initial interval search): invalid null step",
     "ERROR_INTERVAL_OUTSIDE": "Interval: value %value% is outside of %interval",
     "ERROR_INTERVAL_UNDEF": "Interval: invalid 'undefined' value",
+    "ERROR_INVALID_AT_POSITION": "Position %s:",
     "ERROR_LANG_UNSUPPORTED": "internationalisation: unsupported '%locale%' locale",
     "ERROR_NEWTON_DERIVEE_NULLE": "Null function derivative in Newton computation",
+    "ERROR_PARAM_NULL": "Parameter value must not be NULL",
+    "ERROR_PARAM_MUST_BE_A_NUMBER": "Please type a numeric value",
     "ERROR_PARAMDEF_CALC_UNDEFINED": "calculability of '%symbol%' parameter is undefined",
     "ERROR_PARAMDEF_VALUE_FIXED": "value of '%symbol%' parameter cannot be changed",
     "ERROR_PARAMDEF_VALUE_INTERVAL": "parameter '%symbol%': value %value% is out of [%minValue%, %maxValue%] interval",
@@ -29,8 +32,10 @@
     "ERROR_SECTION_NON_CONVERGENCE_NEWTON_HTOR": "Non convergence of the calculation of the corresponding height (Newton's method) for the calculation of the supercritical depth",
     "ERROR_SECTION_PENTE_NEG_NULLE_HNORMALE_INF": "The slope is negative or zero, the normal depth is infinite",
     "ERROR_STRUCTURE_Q_TROP_ELEVE": "The flow passing through the other devices is too high: the requested parameter is not calculable.",
+    "INFO_CALCULATOR_CALC_NAME": "Calculator name",
     "INFO_CALCULATOR_CALCULER": "Compute",
     "INFO_CALCULATOR_PARAMFIXES": "Fixed parameters",
+    "INFO_CALCULATOR_RESULTS_TITLE": "Results",
     "INFO_CALCULATOR_VALEURS": "Values",
     "INFO_CLOISONS_TITRE": "Fish ladder: Cross walls",
     "INFO_CLOSE_DIALOGUE_TEXT": "Warning ! Parameters and results will be lost. Really close?",
@@ -48,6 +53,20 @@
     "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_1": "Partially submerged",
     "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_2": "Submerged",
     "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_3": "Zero flow",
+    "INFO_DIALOG_COMPUTED_VALUE_TITLE": "Edit initial value",
+    "INFO_DIALOG_LOAD_SESSION_FILENAME": "Choose a file",
+    "INFO_DIALOG_LOAD_SESSION_TITLE": "Load calculator modules",
+    "INFO_DIALOG_SAVE_SESSION_FILENAME": "File name",
+    "INFO_DIALOG_SAVE_SESSION_TITLE": "Save calculator modules",
+    "INFO_EMPTY_SESSION_DIALOGUE_TEXT": "Warning ! All open calculators will be lost. Continue ?",
+    "INFO_EMPTY_SESSION_DIALOGUE_TITRE": "New session",
+    "INFO_EXTRARES_ENUM_OUVRAGE_Q_MODE_0": "Weir",
+    "INFO_EXTRARES_ENUM_OUVRAGE_Q_MODE_1": "Orifice",
+    "INFO_EXTRARES_ENUM_OUVRAGE_Q_MODE_2": "Zero flow",
+    "INFO_EXTRARES_ENUM_OUVRAGE_Q_REGIME_0": "Free flow",
+    "INFO_EXTRARES_ENUM_OUVRAGE_Q_REGIME_1": "Partially submerged",
+    "INFO_EXTRARES_ENUM_OUVRAGE_Q_REGIME_2": "Submerged",
+    "INFO_EXTRARES_ENUM_OUVRAGE_Q_REGIME_3": "Zero flow",
     "INFO_EXTRARES_LIB_B": "Surface width (m)",
     "INFO_EXTRARES_LIB_CV": "Cv: Velocity coefficient",
     "INFO_EXTRARES_LIB_CVQT": "CV.QT: Corrected discharge (m³/s)",
@@ -98,23 +117,52 @@
     "INFO_LIB_ZDV": "Crest weir elevation or gate base",
     "INFO_LIB_ZRAM": "Upstream apron elevation (m)",
     "INFO_LIB_ZT": "Triangle top elevation (m)",
+    "INFO_MACRORUGO_TITRE": "Rock-ramp fishpasses",
     "INFO_MENU_HELP_TITLE": "Help",
     "INFO_MENU_LOAD_SESSION_TITLE": "Load session",
     "INFO_MENU_NOUVELLE_CALC": "New calculation module",
-    "INFO_MACRORUGO_TITRE": "Rock-ramp fishpasses",
+    "INFO_MENU_SAVE_SESSION_TITLE": "Save session",
+    "INFO_MENU_SELECT_CALC": "Select calculator module",
+    "INFO_MENU_EMPTY_SESSION_TITLE": "New session",
     "INFO_OPTION_NO": "No",
     "INFO_OPTION_YES": "Yes",
+    "INFO_OPTION_CANCEL": "Cancel",
+    "INFO_OPTION_CLOSE": "Close",
+    "INFO_OPTION_LOAD": "Load",
+    "INFO_OPTION_SAVE": "Save",
+    "INFO_OPTION_VALIDATE": "Validate",
+    "INFO_OPTION_ALL": "All",
+    "INFO_OPTION_ALL_F": "All",
+    "INFO_OPTION_NONE": "None",
+    "INFO_OPTION_NONE_F": "None",
     "INFO_OUVRAGE": "Structure",
     "INFO_PABDIMENSIONS_TITRE": "Fish ladder: dimensions",
     "INFO_PABPUISSANCE_TITRE": "Fish ladder: dissipated power",
     "INFO_PARALLELSTRUCTURE_TITRE": "Parallel structures",
+    "INFO_PARAMFIELD_GRAPH_TYPE": "Graph type",
+    "INFO_PARAMFIELD_GRAPH_TYPE_HISTOGRAM": "Histogram",
+    "INFO_PARAMFIELD_IN_CALCULATION": "In calculation",
+    "INFO_PARAMFIELD_IN_CALCULATION_INITIAL_VALUE": "initial value",
     "INFO_PARAMFIELD_PARAMCALCULER": "Calculate",
     "INFO_PARAMFIELD_PARAMFIXE": "Fixed",
     "INFO_PARAMFIELD_PARAMLIE": "Link",
+    "INFO_PARAMFIELD_PARAMLIE_LABEL": "Linked parameter",
     "INFO_PARAMFIELD_PARAMVARIER": "Vary",
-    "INFO_PARAMFIELD_PASVARIATION": "with a variation step of:",
-    "INFO_PARAMFIELD_VALEURMAXI": "to maximum value",
+    "INFO_PARAMFIELD_PASVARIATION": "With a variation step of",
+    "INFO_PARAMFIELD_PARAMVARIER_IMPORT_FICHIER": "Import file",
+    "INFO_PARAMFIELD_PARAMVARIER_MINMAXSTEP": "min: %s, max: %s, step: %s",
+    "INFO_PARAMFIELD_PARAMVARIER_SEPARATEUR_DECIMAL": "Decimal separator",
+    "INFO_PARAMFIELD_PARAMVARIER_SEPARATEUR_POINT": ". (dot)",
+    "INFO_PARAMFIELD_PARAMVARIER_SEPARATEUR_VIRGULE": ", (comma)",
+    "INFO_PARAMFIELD_PARAMVARIER_TITLE": "Multiple values",
+    "INFO_PARAMFIELD_PARAMVARIER_MODE": "Mode",
+    "INFO_PARAMFIELD_PARAMVARIER_VALUES": "Values:",
+    "INFO_PARAMFIELD_PARAMVARIER_VALUES_FORMAT": "Values list",
+    "INFO_PARAMFIELD_PARAMVARIER_VALUES_FORMAT_ERROR": "Incorrect format; accepted separator: %s",
+    "INFO_PARAMFIELD_VALEURMAXI": "To maximum value",
     "INFO_PARAMFIELD_VALEURMINI": "From minimum value",
+    "INFO_PARAMMODE_MINMAX": "Min/max",
+    "INFO_PARAMMODE_LIST": "Values list",
     "INFO_REGIMEUNIFORME_TITRE": "Uniform flow calculation",
     "INFO_REMOUSRESULTS_ABSCISSE": "Abscissa (m)",
     "INFO_REMOUSRESULTS_BERGE": "Embankment",
@@ -135,6 +183,19 @@
     "INFO_SETUP_PRECISION_AFFICHAGE": "Display accuracy",
     "INFO_SETUP_PRECISION_CALCUL": "Computation accuracy",
     "INFO_SETUP_TITLE": "Application setup",
+    "INFO_THEME_CREDITS": "Credit",
+    "INFO_THEME_MODULES_INUTILISES_TITRE": "Other calculation modules",
+    "INFO_THEME_MODULES_INUTILISES_DESCRIPTION": "Various calculation modules",
+    "INFO_THEME_PASSE_A_BASSIN_TITRE": "Fish ladder",
+    "INFO_THEME_PASSE_A_BASSIN_DESCRIPTION": "Tools for sizing a fish pass made with pools also called fish steps",
+    "INFO_THEME_PASSE_NATURELLE_TITRE": "Natural pass",
+    "INFO_THEME_PASSE_NATURELLE_DESCRIPTION": "Tools for sizing a natural fish pass also called macroroughness pass or rock-ramp fish pass",
+    "INFO_THEME_HYDRAULIQUE_A_SURFACE_LIBRE_TITRE": "Open-channel flow",
+    "INFO_THEME_HYDRAULIQUE_A_SURFACE_LIBRE_DESCRIPTION": "Calculation modules for flows in channels and rivers",
+    "INFO_THEME_HYDRAULIQUE_EN_CHARGE_TITRE": "Pipe flow",
+    "INFO_THEME_HYDRAULIQUE_EN_CHARGE_DESCRIPTION": "Modules for calculating head losses in pressure pipes",
+    "INFO_THEME_LOIS_D_OUVRAGES_TITRE": "Hydraulic structures",
+    "INFO_THEME_LOIS_D_OUVRAGES_DESCRIPTION": "Flow calculation for hydraulic devices (gates, weir, orifices)",
     "WARNING_REMOUS_ARRET_CRITIQUE": "Calculation stopped: critical elevation reached at abscissa %x%",
     "WARNING_STRUCTUREKIVI_HP_TROP_ELEVE": "h/p must not be greater than 2.5. h/p is forced to 2.5",
     "WARNING_STRUCTUREKIVI_PELLE_TROP_FAIBLE": "Threshold height should be greater than 0.1 m. Beta coefficient is forced to 0"
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index cd700e267286a4ab7e0a38c2af12f168cce5a949..505f25e4aaa91256a7698fe58474de8a03df6644 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -8,8 +8,11 @@
     "ERROR_DICHO_NULL_STEP": "Dichotomie&nbsp;: le pas pour la recherche de l'intervalle de départ ne devrait pas être nul",
     "ERROR_INTERVAL_OUTSIDE": "Interval&nbsp;: la valeur %value% est hors de l'intervalle %interval",
     "ERROR_INTERVAL_UNDEF": "Interval&nbsp;: valeur 'undefined' incorrecte",
+    "ERROR_INVALID_AT_POSITION": "Position %s :",
     "ERROR_LANG_UNSUPPORTED": "Internationalisation&nbsp;: locale '%locale%' non prise en charge",
     "ERROR_NEWTON_DERIVEE_NULLE": "Dérivée nulle dans un calcul par la méthode de Newton",
+    "ERROR_PARAM_NULL": "La valeur du paramètre ne peut pas être NULL",
+    "ERROR_PARAM_MUST_BE_A_NUMBER": "Veuillez entrer une valeur numérique",
     "ERROR_PARAMDEF_CALC_UNDEFINED": "La calculabilité du paramètre %symbol% n'est pas définie",
     "ERROR_PARAMDEF_VALUE_FIXED": "La valeur du paramètre %symbol% ne peut pas être changée",
     "ERROR_PARAMDEF_VALUE_INTERVAL": "Paramètre '%symbol%'&nbsp;: la valeur %value% est en dehors de l'intervalle [%minValue%, %maxValue%]",
@@ -29,11 +32,13 @@
     "ERROR_SECTION_NON_CONVERGENCE_NEWTON_HTOR": "Non convergence du calcul de la hauteur correspondante (Méthode de Newton) pour le calcul de la hauteur torrentielle",
     "ERROR_SECTION_PENTE_NEG_NULLE_HNORMALE_INF": "La pente est négative ou nulle, la hauteur normale est infinie",
     "ERROR_STRUCTURE_Q_TROP_ELEVE": "Le débit passant par les autres ouvrages est trop élevé: le paramètre demandé n'est pas calculable.",
+    "INFO_CALCULATOR_CALC_NAME": "Nom du module de calcul",
     "INFO_CALCULATOR_CALCULER": "Calculer",
     "INFO_CALCULATOR_PARAMFIXES": "Paramètres fixés",
+    "INFO_CALCULATOR_RESULTS_TITLE": "Résultats",
     "INFO_CALCULATOR_VALEURS": "Valeurs",
     "INFO_CLOISONS_TITRE": "Passe à bassin&nbsp;: Cloisons",
-    "INFO_CLOSE_DIALOGUE_TEXT": "Attention&nbsp! Les paramètres et résultats de la calculette seront perdus. Vraiment fermer&nbsp;?",
+    "INFO_CLOSE_DIALOGUE_TEXT": "Attention&nbsp;! Les paramètres et résultats du module de calcul seront perdus. Vraiment fermer&nbsp;?",
     "INFO_CLOSE_DIALOGUE_TITRE": "Confirmer la fermeture",
     "INFO_CONDUITEDISTRIBUTRICE_TITRE": "Conduite distributrice",
     "INFO_COURBEREMOUS_TITRE": "Courbes de remous",
@@ -48,6 +53,20 @@
     "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_1": "Partiellement noyé",
     "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_2": "Noyé",
     "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_3": "Débit nul",
+    "INFO_DIALOG_COMPUTED_VALUE_TITLE": "Modifier la valeur initiale",
+    "INFO_DIALOG_LOAD_SESSION_FILENAME": "Choisir un fichier",
+    "INFO_DIALOG_LOAD_SESSION_TITLE": "Charger des modules de calcul",
+    "INFO_DIALOG_SAVE_SESSION_FILENAME": "Nom de fichier",
+    "INFO_DIALOG_SAVE_SESSION_TITLE": "Enregistrer les modules de calcul",
+    "INFO_EMPTY_SESSION_DIALOGUE_TEXT": "Attention&nbsp;! Tous les modules de calcul ouverts seront perdus. Continuer&nbsp;?",
+    "INFO_EMPTY_SESSION_DIALOGUE_TITRE": "Démarrer une nouvelle session",
+    "INFO_EXTRARES_ENUM_OUVRAGE_Q_MODE_0": "Surface libre",
+    "INFO_EXTRARES_ENUM_OUVRAGE_Q_MODE_1": "En charge",
+    "INFO_EXTRARES_ENUM_OUVRAGE_Q_MODE_2": "Débit nul",
+    "INFO_EXTRARES_ENUM_OUVRAGE_Q_REGIME_0": "Dénoyé",
+    "INFO_EXTRARES_ENUM_OUVRAGE_Q_REGIME_1": "Partiellement noyé",
+    "INFO_EXTRARES_ENUM_OUVRAGE_Q_REGIME_2": "Noyé",
+    "INFO_EXTRARES_ENUM_OUVRAGE_Q_REGIME_3": "Débit nul",
     "INFO_EXTRARES_LIB_B": "Largeur au miroir (m)",
     "INFO_EXTRARES_LIB_CV": "Cv: Coefficient de vitesse d'approche",
     "INFO_EXTRARES_LIB_CVQT": "CV.QT: Débit corrigé (m³/s)",
@@ -100,21 +119,50 @@
     "INFO_LIB_ZT": "Cote haute du triangle (m)",
     "INFO_MENU_HELP_TITLE": "Aide",
     "INFO_MENU_LOAD_SESSION_TITLE": "Charger une session",
+    "INFO_MENU_SAVE_SESSION_TITLE": "Enregistrer la session",
+    "INFO_MENU_SELECT_CALC": "Choisir un module de calcul",
+    "INFO_MENU_EMPTY_SESSION_TITLE": "Nouvelle session",
     "INFO_MENU_NOUVELLE_CALC": "Nouveau module de calcul",
-    "INFO_MACRORUGO_TITRE": "Passe à macro-rugosité",
+    "INFO_MACRORUGO_TITRE": "Passe à macro-rugosités",
     "INFO_OPTION_NO": "Non",
     "INFO_OPTION_YES": "Oui",
+    "INFO_OPTION_CANCEL": "Annuler",
+    "INFO_OPTION_CLOSE": "Fermer",
+    "INFO_OPTION_LOAD": "Charger",
+    "INFO_OPTION_SAVE": "Enregistrer",
+    "INFO_OPTION_VALIDATE": "Valider",
+    "INFO_OPTION_ALL": "Tous",
+    "INFO_OPTION_ALL_F": "Toutes",
+    "INFO_OPTION_NONE": "Aucun",
+    "INFO_OPTION_NONE_F": "Aucune",
     "INFO_OUVRAGE": "Ouvrage",
     "INFO_PABDIMENSIONS_TITRE": "Passe à bassin&nbsp;: dimensions",
     "INFO_PABPUISSANCE_TITRE": "Passe à bassin&nbsp;: puissance dissipée",
     "INFO_PARALLELSTRUCTURE_TITRE": "Lois d'ouvrages",
+    "INFO_PARAMFIELD_GRAPH_TYPE": "Type de graphe",
+    "INFO_PARAMFIELD_GRAPH_TYPE_HISTOGRAM": "Histogramme",
+    "INFO_PARAMFIELD_IN_CALCULATION": "En calcul",
+    "INFO_PARAMFIELD_IN_CALCULATION_INITIAL_VALUE": "valeur initiale",
     "INFO_PARAMFIELD_PARAMCALCULER": "calculer",
     "INFO_PARAMFIELD_PARAMFIXE": "fixé",
     "INFO_PARAMFIELD_PARAMLIE": "lié",
+    "INFO_PARAMFIELD_PARAMLIE_LABEL": "Paramètre lié",
     "INFO_PARAMFIELD_PARAMVARIER": "varier",
-    "INFO_PARAMFIELD_PASVARIATION": "avec un pas de&nbsp;:",
-    "INFO_PARAMFIELD_VALEURMAXI": "à la valeur maximum",
+    "INFO_PARAMFIELD_PASVARIATION": "Avec un pas de",
+    "INFO_PARAMFIELD_PARAMVARIER_IMPORT_FICHIER": "Importer un fichier",
+    "INFO_PARAMFIELD_PARAMVARIER_MINMAXSTEP": "min : %s, max : %s, pas : %s",
+    "INFO_PARAMFIELD_PARAMVARIER_SEPARATEUR_DECIMAL": "Séparateur décimal",
+    "INFO_PARAMFIELD_PARAMVARIER_SEPARATEUR_POINT": ". (point)",
+    "INFO_PARAMFIELD_PARAMVARIER_SEPARATEUR_VIRGULE": ", (virgule)",
+    "INFO_PARAMFIELD_PARAMVARIER_TITLE": "Valeurs multiples",
+    "INFO_PARAMFIELD_PARAMVARIER_MODE": "Mode",
+    "INFO_PARAMFIELD_PARAMVARIER_VALUES": "Valeurs :",
+    "INFO_PARAMFIELD_PARAMVARIER_VALUES_FORMAT": "Liste de valeurs",
+    "INFO_PARAMFIELD_PARAMVARIER_VALUES_FORMAT_ERROR": "Format incorrect; séparateurs acceptés: %s",
+    "INFO_PARAMFIELD_VALEURMAXI": "À la valeur maximum",
     "INFO_PARAMFIELD_VALEURMINI": "De la valeur minimum",
+    "INFO_PARAMMODE_MINMAX": "Min/max",
+    "INFO_PARAMMODE_LIST": "Liste de valeurs",
     "INFO_REGIMEUNIFORME_TITRE": "Régime uniforme",
     "INFO_REMOUSRESULTS_ABSCISSE": "Abscisse (m)",
     "INFO_REMOUSRESULTS_BERGE": "Berge",
@@ -135,6 +183,19 @@
     "INFO_SETUP_PRECISION_AFFICHAGE": "Précision d'affichage",
     "INFO_SETUP_PRECISION_CALCUL": "Précision de calcul",
     "INFO_SETUP_TITLE": "Paramètres de l'application",
+    "INFO_THEME_CREDITS": "Crédit",
+    "INFO_THEME_MODULES_INUTILISES_TITRE": "Autres modules de calcul",
+    "INFO_THEME_MODULES_INUTILISES_DESCRIPTION": "Modules de calculs divers",
+    "INFO_THEME_PASSE_A_BASSIN_TITRE": "Passe à bassin",
+    "INFO_THEME_PASSE_A_BASSIN_DESCRIPTION": "Outils de dimensionnement d'une passe à poissons de type passe à bassin ou encore appelée échelle à poisson",
+    "INFO_THEME_PASSE_NATURELLE_TITRE": "Passe naturelle",
+    "INFO_THEME_PASSE_NATURELLE_DESCRIPTION": "Outils de dimensionnement d'une passe à poissons de type passe naturelle ou encore appelée passe à macro-rugosités",
+    "INFO_THEME_HYDRAULIQUE_A_SURFACE_LIBRE_TITRE": "Hydraulique à surface libre",
+    "INFO_THEME_HYDRAULIQUE_A_SURFACE_LIBRE_DESCRIPTION": "Modules de calcul pour les écoulements en canaux et rivières",
+    "INFO_THEME_HYDRAULIQUE_EN_CHARGE_TITRE": "Hydraulique en charge",
+    "INFO_THEME_HYDRAULIQUE_EN_CHARGE_DESCRIPTION": "Modules de calcul de perte de charge dans les conduites sous pression",
+    "INFO_THEME_LOIS_D_OUVRAGES_TITRE": "Lois d'ouvrages",
+    "INFO_THEME_LOIS_D_OUVRAGES_DESCRIPTION": "Calculs basés sur les lois de débits d'ouvrages hydrauliques (Vannes, seuils, orifices)",
     "WARNING_REMOUS_ARRET_CRITIQUE": "Arrêt du calcul&nbsp;: hauteur critique atteinte à l'abscisse %x%",
     "WARNING_STRUCTUREKIVI_HP_TROP_ELEVE": "h/p ne doit pas être supérieur à 2,5. h/p est forcé à 2,5",
     "WARNING_STRUCTUREKIVI_PELLE_TROP_FAIBLE": "La pelle du seuil doit mesurer au moins 0,1 m. Le coefficient béta est forcé à 0"
diff --git a/src/main.ts b/src/main.ts
index 6373f498c5643cadce8e0e8d3ddd4ba9a6e521e1..9b8fff7b96bcd6f42218db26ef282901d5136450 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,3 +1,4 @@
+import "hammerjs";
 import { enableProdMode } from "@angular/core";
 import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
 
diff --git a/src/styles.scss b/src/styles.scss
index 90d4ee0072ce3fc41812f8af910219f9eea3c3de..013d064f64343a703326db51277fd2409092bfa2 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -1 +1,69 @@
 /* You can add global styles to this file, and also import other style files */
+// @import "~@angular/material/prebuilt-themes/indigo-pink.css";
+// @import "~@angular/material/prebuilt-themes/deeppurple-amber.css";
+// @import "~@angular/material/prebuilt-themes/pink-bluegrey.css";
+// @import "~@angular/material/prebuilt-themes/purple-green.css";
+
+html, body { height: 100%; }
+
+body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
+
+button {
+    &:focus {
+        outline: 0;
+    }
+}
+
+mat-dialog-container {
+    min-width: 280px;
+
+    .mat-dialog-actions {
+        margin-bottom: -5px;
+    }
+}
+
+mat-error {
+    font-weight: 500;
+    font-size: 1.1em;
+}
+
+mat-form-field {
+    // not applied globally; why ??
+    ::ng-deep .mat-form-field-label {
+        font-size: 1.1em;
+        line-height: 1.4em;
+        margin-top: -2px;
+
+        &.mat-form-field-empty {
+            font-size: 1em;
+        }
+    }
+}
+
+.eight-em-bottom-padding {
+    padding-bottom: 8em;
+}
+
+table.mat-table {
+    width: 100%;
+}
+
+// bootstrap inspired styles
+
+h1 {
+    margin-top: 0;
+    margin-bottom: .5rem;
+    font-size: 2.5rem;
+    font-weight: 300;
+    line-height: 1.2;
+}
+
+// debug
+field-set {
+    margin-bottom: 2em;
+}
+
+// hide elements having "hidden" attribute even if they have explicit "display:" property
+[hidden] {
+    display: none !important;
+}
diff --git a/src/theme.scss b/src/theme.scss
new file mode 100644
index 0000000000000000000000000000000000000000..3ff27ba26eb9f75fb36ca08593d3c606d7e87e66
--- /dev/null
+++ b/src/theme.scss
@@ -0,0 +1,218 @@
+@import '~@angular/material/theming';
+@include mat-core();
+
+// palettes générées sur http://mcg.mbitson.com
+$mat-irstea-marine: (
+    50 : #e0e7f0,
+    100 : #b3c4d9,
+    200 : #809dc0,
+    300 : #4d75a6,
+    400 : #265893,
+    500 : #003a80,
+    600 : #003478,
+    700 : #002c6d,
+    800 : #002563,
+    900 : #001850,
+    A100 : #839cff,
+    A200 : #5073ff,
+    A400 : #1d4bff,
+    A700 : #0336ff,
+    contrast: (
+        50 : #000000,
+        100 : #000000,
+        200 : #000000,
+        300 : #ffffff,
+        400 : #ffffff,
+        500 : #ffffff,
+        600 : #ffffff,
+        700 : #ffffff,
+        800 : #ffffff,
+        900 : #ffffff,
+        A100 : #000000,
+        A200 : #ffffff,
+        A400 : #ffffff,
+        A700 : #ffffff,
+    )
+);
+
+$mat-irstea-ocean: (
+    50 : #e0f3fb,
+    100 : #b3e2f6,
+    200 : #80cff0,
+    300 : #4dbbe9,
+    400 : #26ade5,
+    500 : #009ee0,
+    600 : #0096dc,
+    700 : #008cd8,
+    800 : #0082d3,
+    900 : #0070cb,
+    A100 : #f3f9ff,
+    A200 : #c0dfff,
+    A400 : #8dc5ff,
+    A700 : #74b9ff,
+    contrast: (
+        50 : #000000,
+        100 : #000000,
+        200 : #000000,
+        300 : #000000,
+        400 : #000000,
+        500 : #ffffff,
+        600 : #ffffff,
+        700 : #ffffff,
+        800 : #ffffff,
+        900 : #ffffff,
+        A100 : #000000,
+        A200 : #000000,
+        A400 : #000000,
+        A700 : #000000,
+    )
+);
+
+$mat-irstea-prune: (
+    50 : #f5e3f0,
+    100 : #e7b8d9,
+    200 : #d789c0,
+    300 : #c759a6,
+    400 : #bb3693,
+    500 : #af1280,
+    600 : #a81078,
+    700 : #9f0d6d,
+    800 : #960a63,
+    900 : #860550,
+    A100 : #ffb3db,
+    A200 : #ff80c3,
+    A400 : #ff4dab,
+    A700 : #ff349f,
+    contrast: (
+        50 : #000000,
+        100 : #000000,
+        200 : #000000,
+        300 : #000000,
+        400 : #ffffff,
+        500 : #ffffff,
+        600 : #ffffff,
+        700 : #ffffff,
+        800 : #ffffff,
+        900 : #ffffff,
+        A100 : #000000,
+        A200 : #000000,
+        A400 : #000000,
+        A700 : #ffffff,
+    )
+);
+
+$mat-irstea-brique: (
+    50 : #fae3e3,
+    100 : #f2b8b9,
+    200 : #ea898b,
+    300 : #e25a5d,
+    400 : #db363a,
+    500 : #d51317,
+    600 : #d01114,
+    700 : #ca0e11,
+    800 : #c40b0d,
+    900 : #ba0607,
+    A100 : #ffe4e4,
+    A200 : #ffb1b1,
+    A400 : #ff7e7f,
+    A700 : #ff6465,
+    contrast: (
+        50 : #000000,
+        100 : #000000,
+        200 : #000000,
+        300 : #000000,
+        400 : #ffffff,
+        500 : #ffffff,
+        600 : #ffffff,
+        700 : #ffffff,
+        800 : #ffffff,
+        900 : #ffffff,
+        A100 : #000000,
+        A200 : #000000,
+        A400 : #000000,
+        A700 : #000000,
+    )
+);
+
+$mat-irstea-rouille: (
+    50 : #fcebe2,
+    100 : #f8ceb6,
+    200 : #f4ad86,
+    300 : #f08c55,
+    400 : #ec7430,
+    500 : #e95b0c,
+    600 : #e6530a,
+    700 : #e34908,
+    800 : #df4006,
+    900 : #d92f03,
+    A100 : #ffffff,
+    A200 : #ffd5ce,
+    A400 : #ffaa9b,
+    A700 : #ff9481,
+    contrast: (
+        50 : #000000,
+        100 : #000000,
+        200 : #000000,
+        300 : #000000,
+        400 : #000000,
+        500 : #ffffff,
+        600 : #ffffff,
+        700 : #ffffff,
+        800 : #ffffff,
+        900 : #ffffff,
+        A100 : #000000,
+        A200 : #000000,
+        A400 : #000000,
+        A700 : #000000,
+    )
+);
+
+
+$nghyd-primary: mat-palette($mat-irstea-marine);
+$nghyd-accent:  mat-palette($mat-irstea-ocean);
+$nghyd-warn:    mat-palette($mat-irstea-prune);
+// $nghyd-warn:    mat-palette($mat-irstea-rouille);
+
+$nghyd-theme: mat-light-theme($nghyd-primary, $nghyd-accent, $nghyd-warn);
+
+@include angular-material-theme($nghyd-theme);
+
+// reuse custom base colors anywhere
+$primary: map-get($nghyd-theme, primary);
+$accent: map-get($nghyd-theme, accent);
+$warn: map-get($nghyd-theme, warn);
+
+// convenience classes (functions mat-* cannot be used outside of this file)
+.color-primary {
+    color: mat-color($primary);
+}
+.bg-primary {
+    background-color: mat-color($primary);
+}
+.color-accent {
+    color: mat-color($accent);
+}
+.bg-accent {
+    background-color: mat-color($accent);
+}
+.bg-accent-light {
+    background-color: mat-color($accent, 300);
+}
+.color-warn {
+    color: mat-color($warn);
+}
+.bg-warn {
+    background-color: mat-color($warn);
+}
+
+// make toggle buttons more visible
+/* .mat-button-toggle {
+    background-color: mat-color($primary);
+    color: mat-color($primary, default-contrast);
+}
+ */
+
+.mat-button-toggle-checked {
+    background-color: mat-color($accent);
+    color: mat-color($accent, default-contrast) !important;
+}
diff --git a/tsconfig.json b/tsconfig.json
index cb174fd8a71047dfd9d30bce1acb17757e54f155..f6d913f28b3fb0497654c6b9cc9f5c726ac0ad09 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -20,7 +20,6 @@
     "baseUrl": "./"
   },
   "include": [
-    "node_modules/angular-bootstrap-md/**/*.ts",
     "src/**/*.ts"
   ]
 }
\ No newline at end of file