diff --git a/.gitignore b/.gitignore
index 54bfd2001e64d03a43570130180635049cd8f0b2..339da7fb56721c66af03a90404fe6c9dd5739a7a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,3 +40,10 @@ testem.log
 # System Files
 .DS_Store
 Thumbs.db
+
+Makefile
+commit
+debug
+git.readme
+grep
+install_jalhyd
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index feca6431f1ccd45142cd15968fc55617f3d6f6b8..f78f761a468bd89227fa66a19ea8101ca137ecad 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -2,15 +2,13 @@ import { Component, ApplicationRef, OnInit, OnDestroy } from '@angular/core';
 //import { MdDialog } from '@angular/material';
 import { Router } from '@angular/router';
 
-import { ComputeNodeType, ComputeNode } from 'jalhyd';
-
 import { environment } from '../environments/environment';
 import { InternationalisationService, Language, LanguageCode } from './services/internationalisation/internationalisation.service';
 import { Observer } from './services/observer';
 import { ErrorService } from './services/error/error.service';
 // import { AlertDialog } from './components/alert-dialog/alert-dialog.component';
 import { FormulaireService } from './services/formulaire/formulaire.service';
-import { FormulaireDefinition, CalculatorType } from './formulaire/formulaire-definition';
+import { FormulaireDefinition } from './formulaire/definition/form-definition';
 
 
 @Component({
@@ -56,8 +54,6 @@ export class AppComponent implements OnInit, OnDestroy, Observer {
     this.formulaireService.removeObserver(this);
   }
 
-  private _displayedCalc: ComputeNodeType;
-
   private get uitextSidenavNewCalc() {
     return this.intlService.localizeText("INFO_MENU_NOUVELLE_CALC");
   }
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index b73ea0bc2a71f3a2b52fc5faaae303a074e55306..7da99f5ba1066afacafccdc58a9c02f53bc59972 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -16,6 +16,7 @@ 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';
@@ -70,7 +71,7 @@ const appRoutes: Routes = [
   declarations: [ // composants, pipes et directives
     AppComponent,
     NgParamInputComponent,
-    FieldSetComponent,
+    FieldSetComponent, FieldsetContainerComponent,
     ParamFieldLineComponent, NgParamMinComponent, NgParamMaxComponent, NgParamStepComponent,
     ParamValuesComponent, ValueListComponent,
     SelectFieldLineComponent, CheckFieldLineComponent,
diff --git a/src/app/calculators/cond_distri/cond_distri.config.json b/src/app/calculators/cond_distri/cond_distri.config.json
index 5c7f270d23badfb4de79d24c6abc3d624a930f66..06ef5a56c9a85540e1a3100e6a190345c4358f72 100644
--- a/src/app/calculators/cond_distri/cond_distri.config.json
+++ b/src/app/calculators/cond_distri/cond_distri.config.json
@@ -1,6 +1,7 @@
 [
     {
         "id": "fs_hydraulique",
+        "type": "fieldset",
         "option": "cal",
         "fields": [
             {
@@ -37,6 +38,7 @@
     },
     {
         "id": "fs_param_calc",
+        "type": "fieldset",
         "option": "fix",
         "fields": [
             {
@@ -46,8 +48,7 @@
         ]
     },
     {
-        "id": "options",
-        "idCal": "J",
-        "nodeType": "CondDistri"
+        "type": "options",
+        "idCal": "J"
     }
 ]
\ No newline at end of file
diff --git a/src/app/calculators/lechapt-calmon/lechapt-calmon.config.json b/src/app/calculators/lechapt-calmon/lechapt-calmon.config.json
index 410ea3435bf04656aea8c615aaf3ea99bff318e3..1cc7e88615c807c55c24f05fbf35b38d4fe95fbc 100644
--- a/src/app/calculators/lechapt-calmon/lechapt-calmon.config.json
+++ b/src/app/calculators/lechapt-calmon/lechapt-calmon.config.json
@@ -1,6 +1,7 @@
 [
     {
         "id": "fs_materiau",
+        "type": "fieldset",
         "option": "fix",
         "fields": [
             {
@@ -196,6 +197,7 @@
     },
     {
         "id": "fs_hydraulique",
+        "type": "fieldset",
         "option": "cal",
         "fields": [
             {
@@ -226,6 +228,7 @@
     },
     {
         "id": "fs_param_calc",
+        "type": "fieldset",
         "option": "fix",
         "fields": [
             {
@@ -235,8 +238,7 @@
         ]
     },
     {
-        "id": "options",
-        "idCal": "J",
-        "nodeType": "LechaptCalmon"
+        "type": "options",
+        "idCal": "J"
     }
 ]
\ No newline at end of file
diff --git a/src/app/calculators/ouvrages/ouvrages.config.json b/src/app/calculators/ouvrages/ouvrages.config.json
index eb8e89618a9118993b4069f5f083f44e14d25041..840ccc54408b107250fb9d1083d2a18fac2c7c3d 100644
--- a/src/app/calculators/ouvrages/ouvrages.config.json
+++ b/src/app/calculators/ouvrages/ouvrages.config.json
@@ -1,6 +1,7 @@
 [
     {
         "id": "fs_ouvrage",
+        "type": "fieldset",
         "fields": [
             {
                 "id": "select_ouvrage",
@@ -75,6 +76,7 @@
     },
     {
         "id": "fs_caract_1",
+        "type": "fieldset",
         "dep_exist": [
             {
                 "refid": "select_ouvrage_vanne",
@@ -108,6 +110,7 @@
     },
     {
         "id": "fs_caract_2",
+        "type": "fieldset",
         "dep_exist": [
             {
                 "refid": "select_ouvrage_vanne",
@@ -131,6 +134,7 @@
     },
     {
         "id": "fs_caract_3",
+        "type": "fieldset",
         "dep_exist": [
             {
                 "refid": "select_ouvrage_vanne",
@@ -170,6 +174,7 @@
     },
     {
         "id": "fs_surverse",
+        "type": "fieldset",
         "dep_exist": [
             {
                 "id": "select_ouvrage_vanne_rect"
@@ -191,6 +196,7 @@
     },
     {
         "id": "fs_hydraulique",
+        "type": "fieldset",
         "name": "Caractéristiques globales",
         "option": "cal",
         "fields": [
@@ -222,6 +228,7 @@
     },
     {
         "id": "fs_param_calc",
+        "type": "fieldset",
         "option": "fix",
         "fields": [
             {
@@ -231,7 +238,7 @@
         ]
     },
     {
-        "id": "options",
+        "type": "options",
         "idCal": "J"
     }
 ]
\ No newline at end of file
diff --git a/src/app/calculators/pab-dimensions/pab-dimensions.config.json b/src/app/calculators/pab-dimensions/pab-dimensions.config.json
index 6449d508f61fb6db66599db2f791300f75af13ce..8ef2539adcf667ed17296a7b106feaa13acc78e6 100644
--- a/src/app/calculators/pab-dimensions/pab-dimensions.config.json
+++ b/src/app/calculators/pab-dimensions/pab-dimensions.config.json
@@ -1,6 +1,7 @@
 [
     {
         "id": "fs_dimensions",
+        "type": "fieldset",
         "option": "cal",
         "fields": [
             {
@@ -30,8 +31,7 @@
         ]
     },
     {
-        "id": "options",
-        "idCal": "V",
-        "nodeType": "PabDimensions"
+        "type": "options",
+        "idCal": "V"
     }
 ]
\ No newline at end of file
diff --git a/src/app/calculators/pab-puissance/pab-puissance.config.json b/src/app/calculators/pab-puissance/pab-puissance.config.json
index 378584f6555118141485a2f2fa56876154c538ab..0a9d8cc4b28217e8cd2682673f54ced64172bbc6 100644
--- a/src/app/calculators/pab-puissance/pab-puissance.config.json
+++ b/src/app/calculators/pab-puissance/pab-puissance.config.json
@@ -1,6 +1,7 @@
 [
     {
         "id": "fs_puissance",
+        "type": "fieldset",
         "option": "cal",
         "fields": [
             {
@@ -31,6 +32,7 @@
     },
     {
         "id": "fs_param_calc",
+        "type": "fieldset",
         "option": "fix",
         "fields": [
             {
@@ -40,8 +42,7 @@
         ]
     },
     {
-        "id": "options",
-        "idCal": "Pv",
-        "nodeType": "PabPuissance"
+        "type": "options",
+        "idCal": "Pv"
     }
 ]
\ No newline at end of file
diff --git a/src/app/calculators/parallel-structures/parallel-structures.config.json b/src/app/calculators/parallel-structures/parallel-structures.config.json
new file mode 100644
index 0000000000000000000000000000000000000000..165ecec7293575eac838c0a397f6b72fccc0a45e
--- /dev/null
+++ b/src/app/calculators/parallel-structures/parallel-structures.config.json
@@ -0,0 +1,187 @@
+[
+    {
+        "id": "fs_param_hydro",
+        "type": "fieldset",
+        "calcType": "ParallelStructure",
+        "option": "cal",
+        "fields": [
+            {
+                "type": "input",
+                "id": "Q",
+                "symbol": "Q",
+                "unit": "m³/s",
+                "value": 0.5
+            },
+            {
+                "type": "input",
+                "id": "Z1",
+                "unit": "m",
+                "value": 2
+            },
+            {
+                "type": "input",
+                "id": "Z2",
+                "unit": "m",
+                "value": 1
+            }
+        ]
+    },
+    {
+        "id": "fs_ouvrage",
+        "type": "fieldset_template",
+        "calcType": "Structure",
+        "nodeType": "None",
+        "option": "cal",
+        "fields": [
+            {
+                "id": "select_ouvrage",
+                "type": "select",
+                "select": [
+                    {
+                        "id": "select_ouvrage_vanne_rect"
+                    },
+                    {
+                        "id": "select_ouvrage_seuil_rect"
+                    }
+                ]
+            },
+            {
+                "id": "select_loidebit1",
+                "type": "select",
+                "select": [
+                    {
+                        "id": "select_loidebit1_cem88d"
+                    },
+                    {
+                        "id": "select_loidebit1_cem88v"
+                    },
+                    {
+                        "id": "select_loidebit1_seuildenoye"
+                    },
+                    {
+                        "id": "select_loidebit1_cunge80"
+                    }
+                ],
+                "dep_exist": [
+                    {
+                        "refid": "select_ouvrage",
+                        "refvalue": "select_ouvrage_seuil_rect"
+                    }
+                ]
+            },
+            {
+                "id": "select_loidebit2",
+                "type": "select",
+                "select": [
+                    {
+                        "id": "select_loidebit2_cem88v"
+                    },
+                    {
+                        "id": "select_loidebit2_cem88d"
+                    },
+                    {
+                        "id": "select_loidebit2_vannedenoye"
+                    },
+                    {
+                        "id": "select_loidebit2_vannenoye"
+                    },
+                    {
+                        "id": "select_loidebit2_cunge80"
+                    }
+                ],
+                "dep_exist": [
+                    {
+                        "refid": "select_ouvrage",
+                        "refvalue": "select_ouvrage_vanne_rect"
+                    }
+                ]
+            },
+            {
+                "type": "input",
+                "id": "ZDV",
+                "unit": "m",
+                "value": 0.2,
+                "nodeType": "StructureRectangle",
+                "dep_exist": [
+                    {
+                        "refid": "select_ouvrage",
+                        "refvalue": "select_ouvrage_vanne_rect"
+                    },
+                    {
+                        "refid": "select_ouvrage",
+                        "refvalue": "select_ouvrage_seuil_rect"
+                    }
+                ]
+            },
+            {
+                "type": "input",
+                "id": "L",
+                "unit": "m",
+                "value": 2,
+                "nodeType": "StructureRectangle",
+                "dep_exist": [
+                    {
+                        "refid": "select_ouvrage",
+                        "refvalue": "select_ouvrage_vanne_rect"
+                    },
+                    {
+                        "refid": "select_ouvrage",
+                        "refvalue": "select_ouvrage_seuil_rect"
+                    }
+                ]
+            },
+            {
+                "type": "input",
+                "id": "W",
+                "unit": "m",
+                "value": 0.5,
+                "dep_exist": [
+                    {
+                        "refid": "select_ouvrage",
+                        "refvalue": "select_ouvrage_vanne_rect"
+                    }
+                ]
+            },
+            {
+                "type": "input",
+                "id": "Cd",
+                "unit": "",
+                "value": 0.4,
+                "nodeType": "StructureRectangle",
+                "dep_exist": [
+                    {
+                        "refid": "select_ouvrage",
+                        "refvalue": "select_ouvrage_vanne_rect"
+                    },
+                    {
+                        "refid": "select_ouvrage",
+                        "refvalue": "select_ouvrage_seuil_rect"
+                    }
+                ]
+            }
+        ]
+    },
+    {
+        "id": "struct_container",
+        "type": "template_container",
+        "templates": [
+            "fs_ouvrage"
+        ]
+    },
+    {
+        "id": "fs_param_calc",
+        "type": "fieldset",
+        "calcType": "ParallelStructure",
+        "option": "fix",
+        "fields": [
+            {
+                "type": "input",
+                "id": "Pr"
+            }
+        ]
+    },
+    {
+        "type": "options",
+        "idCal": "Q"
+    }
+]
\ No newline at end of file
diff --git a/src/app/calculators/parallel-structures/parallel-structures.fr.json b/src/app/calculators/parallel-structures/parallel-structures.fr.json
new file mode 100644
index 0000000000000000000000000000000000000000..258adb9d2f577d66eb6e70e410db475c0c497c41
--- /dev/null
+++ b/src/app/calculators/parallel-structures/parallel-structures.fr.json
@@ -0,0 +1,31 @@
+{
+    "fs_param_hydro": "Paramètres hydrauliques",
+    "Q": "Débit total",
+    "Z1": "Cote amont",
+    "Z2": "Cote aval",
+    "fs_ouvrage": "Ouvrage",
+    "select_ouvrage": "Ouvrage",
+    "select_ouvrage_vanne_circ": "Vanne circulaire",
+    "select_ouvrage_vanne_rect": "Vanne rectangulaire",
+    "select_ouvrage_seuil_rect": "Seuil rectangulaire",
+    "select_ouvrage_seuil_trap": "Seuil trapézoïdal",
+    "select_ouvrage_vanne_trap": "Vanne trapézoïdale",
+    "W": "Ouverture de vanne",
+    "select_loidebit1": "Loi de débit",
+    "select_loidebit1_seuildenoye": "Seuil dénoyé",
+    "select_loidebit1_cunge80": "Cunge 80",
+    "select_loidebit1_cem88d": "Déversoir/Orifice Cemagref 88",
+    "select_loidebit1_cem88v": "Déversoir/Vanne de fond Cemagref 88",
+    "select_loidebit2": "Loi de débit",
+    "select_loidebit2_vannedenoye": "Vanne dénoyé",
+    "select_loidebit2_vannenoye": "Vanne noyé",
+    "select_loidebit2_cunge80": "Cunge 80",
+    "select_loidebit2_cem88d": "Déversoir/Orifice Cemagref 88",
+    "select_loidebit2_cem88v": "Déversoir/Vanne de fond Cemagref 88",
+    "ZDV": "Cote de la crête du déversoir ou du radier de la vanne",
+    "L": "Largeur du déversoir",
+    "Cd": "Coefficient de débit",
+    "struct_container": "Ouvrages",
+    "fs_param_calc": "Paramètres de calcul",
+    "Pr": "Précision de calcul"
+}
diff --git a/src/app/calculators/regime-uniforme/regime-uniforme.config.json b/src/app/calculators/regime-uniforme/regime-uniforme.config.json
index 95a6926e06dc302ba430a5a417e3ead0fc011e22..29ba446e2abcd91ced3777d73c9babf0933ea356 100644
--- a/src/app/calculators/regime-uniforme/regime-uniforme.config.json
+++ b/src/app/calculators/regime-uniforme/regime-uniforme.config.json
@@ -1,6 +1,7 @@
 [
     {
         "id": "fs_section",
+        "type": "fieldset",
         "fields": [
             {
                 "id": "select_section",
@@ -24,7 +25,8 @@
     },
     {
         "id": "fs_section_trapez",
-        "nodeType": "RegimeUniformeTrapeze",
+        "type": "fieldset",
+        "nodeType": "SectionTrapeze",
         "option": "cal",
         "dep_exist": [
             {
@@ -49,7 +51,8 @@
     },
     {
         "id": "fs_section_rect",
-        "nodeType": "RegimeUniformeRectangle",
+        "type": "fieldset",
+        "nodeType": "SectionRectangle",
         "option": "cal",
         "dep_exist": [
             {
@@ -68,7 +71,8 @@
     },
     {
         "id": "fs_section_circ",
-        "nodeType": "RegimeUniformeCercle",
+        "type": "fieldset",
+        "nodeType": "SectionCercle",
         "option": "cal",
         "dep_exist": [
             {
@@ -87,7 +91,8 @@
     },
     {
         "id": "fs_section_puiss",
-        "nodeType": "RegimeUniformePuissance",
+        "type": "fieldset",
+        "nodeType": "SectionPuissance",
         "option": "cal",
         "dep_exist": [
             {
@@ -112,6 +117,7 @@
     },
     {
         "id": "fs_bief",
+        "type": "fieldset",
         "option": "cal",
         "fields": [
             {
@@ -136,6 +142,7 @@
     },
     {
         "id": "fs_hydraulique",
+        "type": "fieldset",
         "option": "cal",
         "fields": [
             {
@@ -154,6 +161,7 @@
     },
     {
         "id": "fs_param_calc",
+        "type": "fieldset",
         "option": "fix",
         "fields": [
             {
@@ -163,9 +171,8 @@
         ]
     },
     {
-        "id": "options",
+        "type": "options",
         "idCal": "Q",
-        "nodeType": "RegimeUniforme",
         "sectionSelectId": "select_section"
     }
 ]
\ No newline at end of file
diff --git a/src/app/calculators/remous/remous.config.json b/src/app/calculators/remous/remous.config.json
index ff72637298435365dd8adfaf265bfd6f0edd4412..61afbc02d7169ac14410ad57926c2b96a6d1d4ff 100644
--- a/src/app/calculators/remous/remous.config.json
+++ b/src/app/calculators/remous/remous.config.json
@@ -1,6 +1,7 @@
 [
     {
         "id": "fs_section",
+        "type": "fieldset",
         "fields": [
             {
                 "id": "select_section",
@@ -24,7 +25,8 @@
     },
     {
         "id": "fs_section_trapez",
-        "nodeType": "CourbeRemousTrapeze",
+        "type": "fieldset",
+        "nodeType": "SectionTrapeze",
         "option": "fix",
         "dep_exist": [
             {
@@ -49,7 +51,8 @@
     },
     {
         "id": "fs_section_rect",
-        "nodeType": "CourbeRemousRectangle",
+        "type": "fieldset",
+        "nodeType": "SectionRectangle",
         "option": "fix",
         "dep_exist": [
             {
@@ -68,7 +71,8 @@
     },
     {
         "id": "fs_section_circ",
-        "nodeType": "CourbeRemousCercle",
+        "type": "fieldset",
+        "nodeType": "SectionCercle",
         "option": "fix",
         "dep_exist": [
             {
@@ -87,7 +91,8 @@
     },
     {
         "id": "fs_section_puiss",
-        "nodeType": "CourbeRemousPuissance",
+        "type": "fieldset",
+        "nodeType": "SectionPuissance",
         "option": "fix",
         "dep_exist": [
             {
@@ -112,6 +117,7 @@
     },
     {
         "id": "fs_bief",
+        "type": "fieldset",
         "option": "fix",
         "fields": [
             {
@@ -142,6 +148,7 @@
     },
     {
         "id": "fs_condlim",
+        "type": "fieldset",
         "option": "fix",
         "fields": [
             {
@@ -166,6 +173,7 @@
     },
     {
         "id": "fs_param_calc",
+        "type": "fieldset",
         "option": "fix",
         "fields": [
             {
@@ -197,6 +205,7 @@
     },
     {
         "id": "fs_target_data",
+        "type": "fieldset",
         "fields": [
             {
                 "id": "select_target",
@@ -255,8 +264,7 @@
         ]
     },
     {
-        "id": "options",
-        "nodeType": "CourbeRemous",
+        "type": "options",
         "sectionSelectId": "select_section"
     }
 ]
\ No newline at end of file
diff --git a/src/app/calculators/section-param/section-param.config.json b/src/app/calculators/section-param/section-param.config.json
index 55ac1abe4db96dc06f1de77f1d9fde9a02aa6786..a082a2db933175586f7dc2615e9290072d91b1cc 100644
--- a/src/app/calculators/section-param/section-param.config.json
+++ b/src/app/calculators/section-param/section-param.config.json
@@ -1,6 +1,7 @@
 [
     {
         "id": "fs_section",
+        "type": "fieldset",
         "fields": [
             {
                 "id": "select_section",
@@ -24,6 +25,7 @@
     },
     {
         "id": "fs_section_trapez",
+        "type": "fieldset",
         "nodeType": "SectionTrapeze",
         "option": "var",
         "dep_exist": [
@@ -49,6 +51,7 @@
     },
     {
         "id": "fs_section_rect",
+        "type": "fieldset",
         "nodeType": "SectionRectangle",
         "option": "var",
         "dep_exist": [
@@ -68,6 +71,7 @@
     },
     {
         "id": "fs_section_circ",
+        "type": "fieldset",
         "nodeType": "SectionCercle",
         "option": "var",
         "dep_exist": [
@@ -87,6 +91,7 @@
     },
     {
         "id": "fs_section_puissance",
+        "type": "fieldset",
         "nodeType": "SectionPuissance",
         "option": "var",
         "dep_exist": [
@@ -112,6 +117,7 @@
     },
     {
         "id": "fs_bief",
+        "type": "fieldset",
         "option": "var",
         "fields": [
             {
@@ -136,6 +142,7 @@
     },
     {
         "id": "fs_hydraulique",
+        "type": "fieldset",
         "option": "var",
         "fields": [
             {
@@ -154,6 +161,7 @@
     },
     {
         "id": "fs_param_calc",
+        "type": "fieldset",
         "option": "fix",
         "fields": [
             {
@@ -164,6 +172,7 @@
     },
     {
         "id": "fs_computed_var",
+        "type": "fieldset",
         "dep_exist": [
             {
                 "refid": "LargeurFond",
@@ -267,8 +276,7 @@
         ]
     },
     {
-        "id": "options",
-        "nodeType": "SectionParametree",
+        "type": "options",
         "sectionSelectId": "select_section"
     }
 ]
\ No newline at end of file
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 7f9812f48be1b91fbf4f512b84f00815d02f0bc9..9d836d6d67451f6e2f8bf6c4193334aa57c57c71 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
@@ -44,8 +44,8 @@ export class NgBaseParam extends Observable {
     templateUrl: "../generic-input/generic-input.component.html",
 })
 export class BaseParamInputComponent extends GenericInputComponent {
-    constructor(private intlService: InternationalisationService) {
-        super();
+    constructor(private intlService: InternationalisationService, cdRef: ChangeDetectorRef) {
+        super(cdRef);
     }
 
     /**
diff --git a/src/app/components/base/base.component.ts b/src/app/components/base/base.component.ts
index 3d7cef25df2ae91b3ea70da0b5718ce23077e120..d1c3efca70e802d4b6e0ebeaacd8088168ebccac 100644
--- a/src/app/components/base/base.component.ts
+++ b/src/app/components/base/base.component.ts
@@ -1,6 +1,6 @@
-import { Output, EventEmitter, AfterViewChecked } from "@angular/core";
+import { Output, EventEmitter, AfterViewChecked, OnChanges } from "@angular/core";
 
-export abstract class BaseComponent implements AfterViewChecked {
+export abstract class BaseComponent implements AfterViewChecked, OnChanges {
     /**
      * true après le 1er affichage du composant
      */
@@ -32,8 +32,12 @@ export abstract class BaseComponent implements AfterViewChecked {
         }
     }
 
+    public ngOnChanges() {
+    }
+
     /**
      * appelé une fois, après l'affichage complet du composant
      */
-    protected abstract afterFirstViewChecked();
+    protected afterFirstViewChecked() {
+    }
 }
\ No newline at end of file
diff --git a/src/app/components/calculator-list/calculator-list.component.ts b/src/app/components/calculator-list/calculator-list.component.ts
index 9ce84d292495902ff634fef4cbfbdd05d42ad049..0d674c2e11e8c5a0e6957e2f59294557d011d617 100644
--- a/src/app/components/calculator-list/calculator-list.component.ts
+++ b/src/app/components/calculator-list/calculator-list.component.ts
@@ -1,9 +1,9 @@
 import { Component, OnInit } from "@angular/core";
 import { Router } from '@angular/router';
 
-import { EnumEx } from "jalhyd";
+import { CalculatorType, EnumEx } from "jalhyd";
 
-import { CalculatorType, FormulaireDefinition } from "../../formulaire/formulaire-definition";
+import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
 import { FormulaireService } from "../../services/formulaire/formulaire.service";
 import { InternationalisationService } from '../../services/internationalisation/internationalisation.service';
 
@@ -37,7 +37,8 @@ export class CalculatorListComponent implements OnInit {
     private updateLocale() {
         this._items = [];
         for (let t of EnumEx.getValues(CalculatorType))
-            this._items.push(new ListElement(t, this.formulaireService));
+            if (t !== CalculatorType.Structure)
+                this._items.push(new ListElement(t, this.formulaireService));
     }
 
     private create(t: CalculatorType) {
diff --git a/src/app/components/calculator-results/calculator-results.component.html b/src/app/components/calculator-results/calculator-results.component.html
index b9150353414ca58ca645d6a2c2b7402996559063..21cb31f8767953bdf74bac32b4b339d931d097f0 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 [style.display]="getFixedVarResultsStyleDisplay()"> </fixedvar-results>
-    <section-results [style.display]="getSectionResultsStyleDisplay()"></section-results>
-    <remous-results [style.display]="getRemousResultsStyleDisplay()"></remous-results>
+    <fixedvar-results> </fixedvar-results>
+    <section-results></section-results>
+    <remous-results></remous-results>
 </div>
diff --git a/src/app/components/calculator-results/calculator-results.component.ts b/src/app/components/calculator-results/calculator-results.component.ts
index 15546ce01f67fa861ac53c7b4fc83196f06ed376..76eb9842a035c4d881165502268979895bb0df81 100644
--- a/src/app/components/calculator-results/calculator-results.component.ts
+++ b/src/app/components/calculator-results/calculator-results.component.ts
@@ -3,7 +3,7 @@ import { Component, ViewChild, Output, EventEmitter, AfterViewChecked } from "@a
 import { FixedVarResultsComponent } from "../../components/fixedvar-results/fixedvar-results.component";
 import { SectionResultsComponent } from "../../components/section-results/section-results.component";
 import { RemousResultsComponent } from "../../components/remous-results/remous-results.component";
-import { FormulaireDefinition } from "../../formulaire/formulaire-definition";
+import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
 
 @Component({
     selector: "calc-results",
@@ -44,9 +44,9 @@ export class CalculatorResultsComponent implements AfterViewChecked {
             this.remousResultsComponent.results = undefined;
         }
         else {
-            this.fixedVarResultsComponent.results = f.fixVarResults;
-            this.sectionResultsComponent.results = f.sectionResults;
-            this.remousResultsComponent.results = f.remousResults;
+            this.fixedVarResultsComponent.results = f.results;
+            this.sectionResultsComponent.results = f.results;
+            this.remousResultsComponent.results = f.results;
         }
     }
 
@@ -59,16 +59,4 @@ export class CalculatorResultsComponent implements AfterViewChecked {
     public ngAfterViewChecked() {
         this.afterViewChecked.emit();
     }
-
-    private getFixedVarResultsStyleDisplay() {
-        return this._formulaire != undefined && this._formulaire.hasFixVarResults ? "block" : "none";
-    }
-
-    private getSectionResultsStyleDisplay() {
-        return this._formulaire != undefined && this._formulaire.hasSectionResults ? "block" : "none";
-    }
-
-    private getRemousResultsStyleDisplay() {
-        return this._formulaire != undefined && this._formulaire.hasRemousResults ? "block" : "none";
-    }
 }
diff --git a/src/app/components/field-set/field-set.component.html b/src/app/components/field-set/field-set.component.html
index 32b9d79f5f47cb32a09cd3f37a8da8f166e4c9f3..cd33a4983f88f4856124c8d689120633478c725d 100644
--- a/src/app/components/field-set/field-set.component.html
+++ b/src/app/components/field-set/field-set.component.html
@@ -15,11 +15,11 @@
 -->
 
 <ng-template ngFor let-p [ngForOf]="fields">
-    <param-field-line *ngIf="p.isInput" [param]=p (onRadio)=onRadioClick($event) (onValid)=onParamLineValid()>
+    <param-field-line *ngIf="isInputField(p)" [param]=p (onRadio)=onRadioClick($event) (onValid)=onParamLineValid()>
     </param-field-line>
 
-    <select-field-line *ngIf="p.isSelect" [param]=p (selectChange)=onSelectChanged($event)>
+    <select-field-line *ngIf="isSelectField(p)" [param]=p>
     </select-field-line>
 
-    <check-field-line *ngIf="p.isCheck" [param]=p></check-field-line>
+    <check-field-line *ngIf="isCheckField(p)" [param]=p></check-field-line>
 </ng-template>
\ No newline at end of file
diff --git a/src/app/components/field-set/field-set.component.ts b/src/app/components/field-set/field-set.component.ts
index 7f40c3d060a45dfaa4ccbb3abc69f139b06efaff..4626446278cd5d8a8fce6d640dd4da9e8099aeee 100644
--- a/src/app/components/field-set/field-set.component.ts
+++ b/src/app/components/field-set/field-set.component.ts
@@ -1,12 +1,15 @@
-import { Component, Input, Output, EventEmitter, ViewChildren, QueryList } from "@angular/core";
+import { Component, Input, Output, EventEmitter, ViewChildren, QueryList, DoCheck } from "@angular/core";
 
 import { ParamDefinition } from "jalhyd";
 
 import { NgParameter, ParamRadioConfig } from "../../formulaire/ngparam";
 import { FieldSet } from "../../formulaire/fieldset";
 import { ParamFieldLineComponent } from "../param-field-line/param-field-line.component";
-import { BaseComponent } from "../base/base.component";
 import { SelectEntry } from "../../formulaire/select-entry";
+import { Field } from "../../formulaire/field";
+import { InputField } from "../../formulaire/input-field";
+import { SelectField } from "../../formulaire/select-field";
+import { CheckField } from "../../formulaire/check-field";
 
 @Component({
     selector: "field-set",
@@ -22,7 +25,7 @@ import { SelectEntry } from "../../formulaire/select-entry";
         }`
     ]
 })
-export class FieldSetComponent extends BaseComponent {
+export class FieldSetComponent implements DoCheck {
     /**
     * field set attribute
     */
@@ -40,7 +43,7 @@ export class FieldSetComponent extends BaseComponent {
      * événément de changement de validité
      */
     @Output()
-    private onValid = new EventEmitter();
+    private validChange = new EventEmitter();
 
     /**
      * flag de validité de la saisie
@@ -96,12 +99,33 @@ export class FieldSetComponent extends BaseComponent {
         return this._fieldSet.label;
     }
 
+    /**
+     * détermine si un Field est du type InputField
+     */
+    private isInputField(f: Field): boolean {
+        return f instanceof InputField && f.isDisplayed;
+    }
+
+    /**
+     * détermine si un Field est du type SelectField
+     */
+    private isSelectField(f: Field): boolean {
+        return f instanceof SelectField && f.isDisplayed;
+    }
+
+    /**
+     * détermine si un Field est du type CheckField
+     */
+    private isCheckField(f: Field): boolean {
+        return f instanceof CheckField && f.isDisplayed;
+    }
+
     /*
-   * gestion des événements clic sur les radios :
-   * réception d'un message du composant enfant (param-field)
-   * cf. https://angular.io/guide/component-interaction#parent-listens-for-child-event
-   */
-    private onRadioClick(info: string) {
+     * gestion des événements clic sur les radios :
+     * réception d'un message du composant enfant (param-field)
+     * cf. https://angular.io/guide/component-interaction#parent-listens-for-child-event
+     */
+    private onRadioClick(info: any) {
         // on renvoie l'info au parent
         this.onRadio.emit(info);
     }
@@ -110,14 +134,7 @@ export class FieldSetComponent extends BaseComponent {
      * événément de changement d'état d'un radio
      */
     @Output()
-    private onRadio = new EventEmitter<string>();
-
-    /**
-     * réception d'un événement d'un select
-     */
-    private onSelectChanged(val: SelectEntry) {
-        this.onSelectChange.emit(val); // on transmet au parent
-    }
+    private onRadio = new EventEmitter<any>();
 
     public get isValid() {
         return this._isValid;
@@ -127,6 +144,8 @@ export class FieldSetComponent extends BaseComponent {
      * calcul de la validité de tous les ParamFieldLineComponent de la vue
      */
     private updateValidity() {
+        this._isValid = false;
+
         if (this._paramComponents != undefined)
             this._isValid = this._paramComponents.reduce(
                 // callback
@@ -144,9 +163,11 @@ export class FieldSetComponent extends BaseComponent {
                 }
                 // valeur initiale
                 , true);
+
+        this.validChange.emit();
     }
 
-    protected afterFirstViewChecked() {
+    public ngDoCheck() {
         this.updateValidity();
     }
 
@@ -155,12 +176,5 @@ export class FieldSetComponent extends BaseComponent {
      */
     private onParamLineValid(event: boolean) {
         this.updateValidity();
-        this.onValid.emit();
     }
-
-    /**
-     * événément de changement d'état d'un select
-     */
-    @Output()
-    private onSelectChange = new EventEmitter<SelectEntry>();
 }
diff --git a/src/app/components/fieldset-container/fieldset-container.component.html b/src/app/components/fieldset-container/fieldset-container.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..1546960415c226045779381f33adac85c5996c4f
--- /dev/null
+++ b/src/app/components/fieldset-container/fieldset-container.component.html
@@ -0,0 +1,11 @@
+<div class="container-fluid" style="border-style:solid; border-color: lightgray">
+    <div class="row">
+        <h4>{{title}}</h4>
+    </div>
+    <div class="row">
+        <!-- bouton d'ajout d'un ouvrage -->
+        <button type="button" class="btn btn-grey waves-light" mdbRippleRadius (click)="addStructure()">Ajouter un ouvrage</button>
+    </div>
+    <field-set *ngFor="let fs of fieldsets" [fieldSet]=fs (onRadio)=onRadioClick($event) (onValid)=onFieldsetValid()>
+    </field-set>
+</div>
\ No newline at end of file
diff --git a/src/app/components/fieldset-container/fieldset-container.component.ts b/src/app/components/fieldset-container/fieldset-container.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..26ac2f5f1ca3a1f15857b18c9719ff613ea6a902
--- /dev/null
+++ b/src/app/components/fieldset-container/fieldset-container.component.ts
@@ -0,0 +1,104 @@
+import { Component, Input, Output, EventEmitter, QueryList, ViewChildren, DoCheck } from "@angular/core";
+
+import { FieldsetContainer } from "../../formulaire/fieldset-container";
+import { SelectEntry } from "../../formulaire/select-entry";
+import { FieldSet } from "../../formulaire/fieldset";
+import { FieldSetComponent } from "../field-set/field-set.component";
+
+@Component({
+    selector: "fieldset-container",
+    templateUrl: "./fieldset-container.component.html"
+})
+export class FieldsetContainerComponent implements DoCheck {
+    @Input("container")
+    private _container: FieldsetContainer;
+
+    /**
+     * liste des composants FieldSet enfants
+     */
+    @ViewChildren(FieldSetComponent)
+    private _fieldsetComponents: QueryList<FieldSetComponent>;
+
+    /**
+     * flag de validité des FieldSet enfants
+     */
+    private _isValid: boolean = false;
+
+    private get title(): string {
+        if (this._container == undefined)
+            return undefined;
+        return this._container.label;
+    }
+
+    private get fieldsets() {
+        return this._container.fieldsets;
+    }
+
+    private addStructure() {
+        this._container.addFromTemplate(this._container.templates[0].id);
+    }
+
+    /*
+     * gestion des événements clic sur les radios :
+     * réception d'un message du composant enfant (field-set)
+     * cf. https://angular.io/guide/component-interaction#parent-listens-for-child-event
+     */
+    private onRadioClick(info: any) {
+        // on renvoie l'info au parent
+        this.onRadio.emit(info);
+    }
+
+    /**
+     * événément de changement d'état d'un radio
+     */
+    @Output()
+    private onRadio = new EventEmitter<any>();
+
+    public ngDoCheck() {
+        this.updateValidity();
+    }
+
+    /**
+    * calcul de la validité de tous les FieldSet de la vue
+    */
+    private updateValidity() {
+        this._isValid = false;
+
+        if (this._fieldsetComponents != undefined)
+            this._isValid = this._fieldsetComponents.reduce(
+                // callback
+                (
+                    // accumulator (valeur précédente du résultat)
+                    acc,
+                    // currentValue (élément courant dans le tableau)
+                    fielset,
+                    // currentIndex (indice courant dans le tableau)
+                    currIndex,
+                    // array (tableau parcouru)
+                    array
+                ) => {
+                    return acc && fielset.isValid;
+                }
+                // valeur initiale
+                , this._fieldsetComponents.length > 0);
+
+        this.validChange.emit();
+    }
+
+    public get isValid() {
+        return this._isValid;
+    }
+
+    /**
+     * événément de changement de validité
+     */
+    @Output()
+    private validChange = new EventEmitter();
+
+    /**
+     * réception d'un événement de validité de FieldSet
+     */
+    private onFieldsetValid() {
+        this.updateValidity();
+    }
+}
\ No newline at end of file
diff --git a/src/app/components/fixedvar-results/fixedvar-results.component.html b/src/app/components/fixedvar-results/fixedvar-results.component.html
index 05555d9311b47416db8fa15a9d858ed63ec7ff02..b470858758b4dccc38a91803a4c158427d942aa3 100644
--- a/src/app/components/fixedvar-results/fixedvar-results.component.html
+++ b/src/app/components/fixedvar-results/fixedvar-results.component.html
@@ -1,45 +1,47 @@
-<!-- journal -->
-<log></log>
+<div class="container-fluid" [style.display]="hasResults">
+    <!-- journal -->
+    <log></log>
 
-<results-graph *ngIf="showVarResults"></results-graph>
+    <results-graph *ngIf="showVarResults"></results-graph>
 
-<!-- 
+    <!-- 
     classe conditionnelle :
     [ngClass]="(condition) ? 'classe-si-vrai' : 'classe-si-faux'"
  -->
 
-<div class="row">
-    <!-- table des résultats fixés -->
-    <div class="col mx-auto" *ngIf="showFixedResults">
-        <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 fixedResults; let i=index">
-                <td class="result_right {{getFixedResultClass(i)}}">{{r.label}}</td>
-                <td class="result_center {{getFixedResultClass(i)}}">
-                    <single-result [result]=r.value> </single-result>
-                </td>
-            </tr>
-        </table>
-    </div>
+    <div class="row">
+        <!-- table des résultats fixés -->
+        <div class="col mx-auto" *ngIf="showFixedResults">
+            <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 fixedResults; let i=index">
+                    <td class="result_right {{getFixedResultClass(i)}}">{{r.label}}</td>
+                    <td class="result_center {{getFixedResultClass(i)}}">
+                        <single-result [result]=r.value> </single-result>
+                    </td>
+                </tr>
+            </table>
+        </div>
 
-    <!-- table des résultats variés -->
-    <div class="col" *ngIf="showVarResults">
-        <table class="table table-striped" style="border: 1px solid rgb(230,230,230);">
-            <tr>
-                <th class="result_center">{{_results.variableParamHeader}}</th>
-                <th class="result_center">{{_results.variableResultHeader}}</th>
-            </tr>
-            <tr *ngFor="let r of varResults; let i=index">
-                <td class="result_center">
-                    <single-result [result]=r.param></single-result>
-                </td>
-                <td class="result_center">
-                    <single-result [result]=r.result></single-result>
-                </td>
-            </tr>
-        </table>
+        <!-- table des résultats variés -->
+        <div class="col" *ngIf="showVarResults">
+            <table class="table table-striped" style="border: 1px solid rgb(230,230,230);">
+                <tr>
+                    <th class="result_center">{{_results.variableParamHeader}}</th>
+                    <th class="result_center">{{_results.variableResultHeader}}</th>
+                </tr>
+                <tr *ngFor="let r of varResults; let i=index">
+                    <td class="result_center">
+                        <single-result [result]=r.param></single-result>
+                    </td>
+                    <td class="result_center">
+                        <single-result [result]=r.result></single-result>
+                    </td>
+                </tr>
+            </table>
+        </div>
     </div>
 </div>
\ No newline at end of file
diff --git a/src/app/components/fixedvar-results/fixedvar-results.component.ts b/src/app/components/fixedvar-results/fixedvar-results.component.ts
index f55c8cd547955ef16eb057eb9c14d2efdee51e7d..ee52e907b6cda48c5c893d54a04e5be6448049e0 100644
--- a/src/app/components/fixedvar-results/fixedvar-results.component.ts
+++ b/src/app/components/fixedvar-results/fixedvar-results.component.ts
@@ -4,6 +4,7 @@ import { InternationalisationService } from "../../services/internationalisation
 import { LogComponent } from '../../components/log/log.component';
 import { FixedVarResults } from "../../results/fixed-var-results";
 import { ResultsGraphComponent } from "../results-graph/results-graph.component";
+import { CalculatorResults } from "../../results/calculator-results";
 
 @Component({
     selector: "fixedvar-results",
@@ -37,7 +38,11 @@ export class FixedVarResultsComponent implements DoCheck {
     /**
      * true si le graphe des résultats doit être remis à jour
      */
-    private _updateResultsGraph: boolean;
+    private _doUpdate: boolean = false;
+
+    private _logUpdated: boolean = false;
+
+    private _graphUpdated: boolean = false;
 
     /**
      * composant journal
@@ -52,31 +57,49 @@ export class FixedVarResultsComponent implements DoCheck {
         private intlService: InternationalisationService
     ) { }
 
-    public set results(r: FixedVarResults) {
-        this._results = r;
-        this._updateResultsGraph = true;
+    public set results(rs: CalculatorResults[]) {
+        this._results = undefined;
+        if (rs != undefined)
+            for (const r of rs) {
+                if (r instanceof FixedVarResults)
+                    this._results = r;
+                break;
+            }
+        this.updateView();
     }
 
     public updateView() {
+        this._logUpdated = false;
+        this._graphUpdated = false;
         this.logComponent.log = undefined;
-        this.generateResults();
-        this._updateResultsGraph = true;
+
+        if (this._results != undefined)
+            this._doUpdate = this._results.hasResults;
     }
 
     public ngDoCheck() {
-        if (this._updateResultsGraph) {
+        if (this._doUpdate)
+            this._doUpdate = !this.updateResults();
+    }
+
+    /** 
+     * met à jour l'affichage des résultats
+     * @returns true si les résultats ont pu être mis à jour
+     */
+    private updateResults() {
+        if (this._results != undefined) {
             if (this.resultsGraphComponent != undefined) {
                 this.resultsGraphComponent.results = this._results;
                 this.resultsGraphComponent.updateView();
-                this._updateResultsGraph = false;
+                this._graphUpdated = true;
             }
+            if (this.logComponent != undefined) {
+                this.logComponent.log = this._results.log;
+                this._logUpdated = true;
+            }
+            return this._logUpdated && this._graphUpdated;
         }
-    }
-
-    private generateResults() {
-        if (this._results != undefined) {
-            this.logComponent.log = this._results.log;
-        }
+        return false;
     }
 
     /**
@@ -114,4 +137,8 @@ export class FixedVarResultsComponent implements DoCheck {
     private get varResults() {
         return this._results.varResults;
     }
+
+    private get hasResults(): boolean {
+        return this.results != undefined && this._results.hasResults;
+    }
 }
diff --git a/src/app/components/generic-calculator/calc-name.component.ts b/src/app/components/generic-calculator/calc-name.component.ts
index f20cb215d6c08e0728d3564234481a465982d474..4c37b10e88e9dc0290573eaab207d802e6b293d0 100644
--- a/src/app/components/generic-calculator/calc-name.component.ts
+++ b/src/app/components/generic-calculator/calc-name.component.ts
@@ -1,6 +1,6 @@
 import { Component, Input } from "@angular/core";
 import { GenericInputComponent } from "../generic-input/generic-input.component";
-import { FormulaireDefinition } from "../../formulaire/formulaire-definition";
+import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
 
 @Component({
     selector: 'calc-name',
diff --git a/src/app/components/generic-calculator/calculator.component.html b/src/app/components/generic-calculator/calculator.component.html
index 8c7d3b1c44e41940e90563601ce17598b500b286..47e2a2e53aeb9f5bd4f5702cc34821eb0a9dee44 100644
--- a/src/app/components/generic-calculator/calculator.component.html
+++ b/src/app/components/generic-calculator/calculator.component.html
@@ -22,8 +22,12 @@
     <div [ngClass]="(hasResults) ? 'col-12 col-lg-6' : 'col-12'">
         <div class="container-fluid">
             <!-- chapitres -->
-            <field-set *ngFor="let fs of fieldSets" [style.display]="getFieldsetStyleDisplay(fs.id)" [fieldSet]=fs (onRadio)=onRadioClick($event)
-                (onSelectChange)=onSelectChanged($event) (onValid)=OnFieldsetValid()></field-set>
+            <ng-template ngFor let-fe [ngForOf]="formElements">
+                <field-set *ngIf="isFieldset(fe)" [style.display]="getFieldsetStyleDisplay(fe.id)" [fieldSet]=fe (onRadio)=onRadioClick($event)
+                    (validChange)=OnFieldsetValid()></field-set>
+
+                <fieldset-container *ngIf="isFieldsetContainer(fe)" [container]=fe (onRadio)=onRadioClick($event) (validChange)=onFieldsetContainerValid()></fieldset-container>
+            </ng-template>
         </div>
 
         <!-- bouton calculer -->
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index 2228bf7bbc6d32c674534b5c51d49bb9c667dc11..4121ce7bc3e15f55b59f9acf2085f08d7d574f4a 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -4,8 +4,7 @@ import { ActivatedRoute } from '@angular/router';
 import { FormulaireService } from "../../services/formulaire/formulaire.service";
 import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
 import { FieldSet } from "../../formulaire/fieldset";
-import { FormulaireDefinition } from "../../formulaire/formulaire-definition";
-import { CalculatorType } from "../../formulaire/formulaire-definition";
+import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
 import { CalculatorResultsComponent } from "../../components/calculator-results/calculator-results.component";
 import { Observer } from "../../services/observer";
 import { Subscription } from "rxjs/Subscription";
@@ -13,6 +12,9 @@ import { FieldSetComponent } from "../field-set/field-set.component";
 import { BaseComponent } from "../base/base.component";
 import { CalculatorNameComponent } from "./calc-name.component";
 import { SelectEntry } from "../../formulaire/select-entry";
+import { FormulaireElement } from "../../formulaire/formulaire-element";
+import { FieldsetContainer } from "../../formulaire/fieldset-container";
+import { FieldsetContainerComponent } from "../fieldset-container/fieldset-container.component";
 
 @Component({
     selector: 'hydrocalc',
@@ -42,6 +44,12 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
     @ViewChildren(FieldSetComponent)
     private _fieldsetComponents: QueryList<FieldSetComponent>;
 
+    /**
+     * liste des FieldsetContainerComponent
+     */
+    @ViewChildren(FieldsetContainerComponent)
+    private _fieldsetContainerComponents: QueryList<FieldsetContainerComponent>;
+
     /**
      * composant d'affichage des résultats
      */
@@ -85,10 +93,24 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
         super();
     }
 
-    private get fieldSets(): FieldSet[] {
+    private get formElements(): FormulaireElement[] {
         if (this._formulaire == undefined)
             return [];
-        return this._formulaire.getFieldSets();
+        return this._formulaire.formElements;
+    }
+
+    /**
+     * détermine si un FormulaireElement est du type FieldSet
+     */
+    private isFieldset(fe): boolean {
+        return fe instanceof FieldSet && !fe.isTemplate;
+    }
+
+    /**
+     * détermine si un FormulaireElement est du type FieldsetContainer
+     */
+    private isFieldsetContainer(fe): boolean {
+        return fe instanceof FieldsetContainer;
     }
 
     private get hasForm() {
@@ -159,16 +181,10 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
     }
 
     /*
-     * gestion des événements clic sur les radios :
-     * envoi d'un message au composant parent
-     * cf. https://angular.io/guide/component-interaction#parent-listens-for-child-event
+     * gestion des événements clic sur les radios
      */
-    private onRadioClick(info: string) {
-        let tmp: string[] = info.split("_");
-        let symbol: string = tmp[0]; // symbole du paramètre dont vient l'événement radio
-        let option: string = tmp[1]; // nouvel état (radio)
-
-        this._formulaire.resetRadiosAndResults(symbol, option);
+    private onRadioClick(info: any) {
+        this._formulaire.onRadioClick(info);
     }
 
     private onCloseForm() {
@@ -178,10 +194,13 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
     /**
      * met à jour l'interface
      */
-    //    private updateUI() {
-    // this.appRef.tick();
-    // this.changeDetectorRef.detectChanges();  // provoque une détection des changements dans les contrôles
-    //}
+    // private updateUI() {
+    //     // this.appRef.tick();
+    //     // this.changeDetectorRef.detectChanges();  // provoque une détection des changements dans les contrôles
+    //     // if (!this.changeDetectorRef['destroyed']) // pour éviter l'erreur "Attempt to use a destroyed view: detectChanges"
+    //     //     this.changeDetectorRef.detectChanges();
+    //     this.changeDetectorRef.markForCheck();
+    // }
 
     private doCompute() {
         this._formulaire.doCompute();
@@ -227,13 +246,15 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
                     this.setForm(this.formulaireService.getFormulaireFromId(uid));
                     this.resultsComponent.formulaire = this._formulaire;
                     this._calculatorNameComponent.model = this._formulaire;
-                    if (this._formulaire != undefined)
-                        this._formulaire.updateNodeType();
                     break;
             }
         }
         else if (sender instanceof FormulaireDefinition) {
             switch (data["action"]) {
+                // case "reset": // réinitialisation du formulaire
+                //     this.updateUI();
+                //     break;
+
                 case "resultsUpdated":
                     const f = sender as FormulaireDefinition;
                     if (this._formulaire.uid == f.uid)
@@ -242,15 +263,6 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
         }
     }
 
-    /**
-     * réception d'un événement d'un select
-     */
-    private onSelectChanged(val: SelectEntry) {
-        this._formulaire.resetResults();
-        this._formulaire.updateNodeType();
-        this._formulaire.applyDependencies();
-    }
-
     /**
      * appelé après le 1er affichage du composant
      */
@@ -262,6 +274,8 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
      * calcul de la validité globale de la vue
      */
     private updateUIValidity() {
+        this._isUIValid = false;
+
         if (this._fieldsetComponents != undefined)
             this._isUIValid = this._fieldsetComponents.reduce(
                 // callback
@@ -278,6 +292,24 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
                     return acc && fieldset.isValid;
                 }
                 // valeur initiale
+                , this._fieldsetComponents.length > 0);
+
+        if (this._fieldsetContainerComponents != undefined)
+            this._isUIValid = this._isUIValid && this._fieldsetContainerComponents.reduce(
+                // callback
+                (
+                    // accumulator (valeur précédente du résultat)
+                    acc,
+                    // currentValue (élément courant dans le tableau)
+                    fieldsetContainer,
+                    // currentIndex (indice courant dans le tableau)
+                    currIndex,
+                    // array (tableau parcouru)
+                    array
+                ) => {
+                    return acc && fieldsetContainer.isValid;
+                }
+                // valeur initiale
                 , true);
     }
 
@@ -287,4 +319,11 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
     private OnFieldsetValid() {
         this.updateUIValidity();
     }
+
+    /**
+     * réception d'un événement de validité d'un FieldsetContainerComponent
+     */
+    private onFieldsetContainerValid() {
+        this.updateUIValidity();
+    }
 }
diff --git a/src/app/components/generic-input/generic-input.component.ts b/src/app/components/generic-input/generic-input.component.ts
index e3359da1222fbaf06fc625b55869ad2eeb63f72d..ed55114a0fbc9417b0e56c822c6507ee471f98ca 100644
--- a/src/app/components/generic-input/generic-input.component.ts
+++ b/src/app/components/generic-input/generic-input.component.ts
@@ -1,4 +1,4 @@
-import { Input, Output, EventEmitter } from "@angular/core";
+import { Input, Output, EventEmitter, ChangeDetectorRef } from "@angular/core";
 
 import { BaseComponent } from "../base/base.component";
 
@@ -79,6 +79,10 @@ export abstract class GenericInputComponent extends BaseComponent {
      */
     private _errorMessageModel: string;
 
+    constructor(private cdRef: ChangeDetectorRef) {
+        super();
+    }
+
     private get isDisabled(): boolean {
         return this._inputDisabled;
     }
@@ -90,6 +94,16 @@ export abstract class GenericInputComponent extends BaseComponent {
         this.onChange.emit({ "action": "valid", "value": this.isValid });
     }
 
+    /**
+     * détection des changements dans l'UI par le ChangeDetector du framework
+     */
+    protected detectChanges() {
+        if (this.cdRef != undefined)
+            // if (!this.cdRef['destroyed']) // pour éviter l'erreur "Attempt to use a destroyed view: detectChanges"
+            //     this.cdRef.detectChanges();
+            this.cdRef.markForCheck();
+    }
+
     /**
      * calcul de la validité globale du composant (UI+modèle)
      */
@@ -107,6 +121,7 @@ export abstract class GenericInputComponent extends BaseComponent {
     private validateUI() {
         let { isValid, message } = this.validateUIValue(this._uiValue);
         this._errorMessageUI = message;
+        this.detectChanges();
         this.setUIValid(isValid);
         return isValid;
     }
@@ -121,6 +136,7 @@ export abstract class GenericInputComponent extends BaseComponent {
     private validateModel() {
         let { isValid, message } = this.validateModelValue(this.getModelValue());
         this._errorMessageModel = message;
+        this.detectChanges();
         this.setModelValid(isValid);
     }
 
@@ -150,8 +166,8 @@ export abstract class GenericInputComponent extends BaseComponent {
         this.onChange.emit({ "action": "model", "value": this.getModelValue() });
     }
 
-    private setAndValidateModel(v: any) {
-        this.setModelValue(v);
+    private setAndValidateModel(sender: any, v: any) {
+        this.setModelValue(sender, v);
         this.emitModelChanged();
 
         this.validateModel();
@@ -162,6 +178,7 @@ export abstract class GenericInputComponent extends BaseComponent {
         this._model = v;
         this.afterSetModel();
         this.updateAll();
+        this.detectChanges();
     }
 
     /**
@@ -183,7 +200,7 @@ export abstract class GenericInputComponent extends BaseComponent {
     private set uiValue(ui: any) {
         this._uiValue = ui;
         if (this.validateUI())
-            this.setAndValidateModel(this.uiToModel(ui));
+            this.setAndValidateModel(this, this.uiToModel(ui));
     }
 
     private updateAll() {
@@ -192,10 +209,9 @@ export abstract class GenericInputComponent extends BaseComponent {
     }
 
     /**
-     * appelé après le 1er affichage du composant
-     * @see BaseComponent
+     * appelé quand les @Input changent
      */
-    protected afterFirstViewChecked() {
+    public ngOnChanges() {
         this.updateAll();
     }
 
@@ -217,7 +233,7 @@ export abstract class GenericInputComponent extends BaseComponent {
     /**
      * affecte la valeur du modèle
      */
-    protected abstract setModelValue(v: any);
+    protected abstract setModelValue(sender: any, v: any);
 
     /**
      * valide une valeur de modèle : est ce une valeur acceptable ? (par ex, nombre dans un intervalle, valeur dans une liste, ...)
diff --git a/src/app/components/generic-select/generic-select.component.ts b/src/app/components/generic-select/generic-select.component.ts
index 56cd4633a5e32bea1e23e55de0b2af257bfad6b9..e4182c434bc2a2019441a555745d9f446a6d2284 100644
--- a/src/app/components/generic-select/generic-select.component.ts
+++ b/src/app/components/generic-select/generic-select.component.ts
@@ -14,32 +14,16 @@ import { BaseComponent } from "../base/base.component";
 </div>
 */
 
-export abstract class GenericSelectComponent<T> extends BaseComponent {
-    // valeur actuellement sélectionnée
-    // la valeur repère une entrée de la liste (cf. [value] dans le template)
-    private _selectedValue: T;
-
+export abstract class GenericSelectComponent<T> {
     /**
      * selected value event
      */
     @Output()
     private selectChange = new EventEmitter<T>();
 
-    constructor() {
-        super();
-    }
-
-    public get selectedValue(): T {
-        return this._selectedValue;
-    }
-
-    public set selectedValue(v: T) {
-        this._selectedValue = v;
-    }
-
     private get currentLabel(): string {
         for (let e of this.entries)
-            if (e == this._selectedValue)
+            if (e == this.selectedValue)
                 return this.entryLabel(e);
 
         return "<no selection>";
@@ -51,26 +35,11 @@ export abstract class GenericSelectComponent<T> extends BaseComponent {
     private onSelect(event: any) {
         const val = event.target.value;
         if (val != undefined) {
-            this._selectedValue = val;
-            this.selectedValueChanged(val);
-            this.selectChange.emit(this._selectedValue);
+            this.selectedValue = val;
+            this.selectChange.emit(this.selectedValue);
         }
     }
 
-    /**
-     * appelé une fois, après l'affichage complet du composant
-     */
-    protected afterFirstViewChecked() {
-        if (this.entries.length > 0)
-            this._selectedValue = this.entries[0];
-    }
-
-    /**
-     * appelé après que la valeur courante change
-     */
-    protected selectedValueChanged(val: T) {
-    }
-
     /**
      * liste des objets sélectionnables
      */
@@ -80,4 +49,13 @@ export abstract class GenericSelectComponent<T> extends BaseComponent {
      * 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/ngparam-input/ngparam-input.component.ts b/src/app/components/ngparam-input/ngparam-input.component.ts
index 9b5939134c44c75ab74929fa5afc2e04ac9c44b4..10e33c675ffb82d1998d72433a773198ffd5c76e 100644
--- a/src/app/components/ngparam-input/ngparam-input.component.ts
+++ b/src/app/components/ngparam-input/ngparam-input.component.ts
@@ -3,12 +3,12 @@
 import { Component, Input, forwardRef, OnInit, DoCheck, ChangeDetectorRef } from "@angular/core";
 import { ControlValueAccessor, NG_VALUE_ACCESSOR, NG_VALIDATORS, FormControl } from "@angular/forms";
 
-import { ComputeNodeType, ParamDefinition, NumericalString, Message, MessageCode } from "jalhyd";
+import { NumericalString, Message } from "jalhyd";
 
 import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
 import { NgParameter } from "../../formulaire/ngparam";
 import { GenericInputComponent } from "../generic-input/generic-input.component";
-import { Observer, IObservable } from "../../services/observer";
+import { Observer } from "../../services/observer";
 
 @Component({
     selector: "ngparam-input",
@@ -28,13 +28,13 @@ export class NgParamInputComponent extends GenericInputComponent implements Obse
      */
     private _tmp: number;
 
-    constructor(private intlService: InternationalisationService) {
-        super();
+    constructor(private intlService: InternationalisationService, cdRef: ChangeDetectorRef) {
+        super(cdRef);
     }
 
     /**
-       * appelé avant le changement de modèle
-       */
+     * appelé avant le changement de modèle
+     */
     protected beforeSetModel() {
         if (this._paramDef != undefined)
             this._paramDef.removeObserver(this);
@@ -55,10 +55,10 @@ export class NgParamInputComponent extends GenericInputComponent implements Obse
         return this._tmp;
     }
 
-    protected setModelValue(v: any) {
+    protected setModelValue(sender: any, v: any) {
         this._tmp = v;
         try {
-            this._paramDef.setValue(v);
+            this._paramDef.setValue(sender, v);
         }
         catch (e) {
             // géré par validateModelValue()
@@ -107,10 +107,15 @@ export class NgParamInputComponent extends GenericInputComponent implements Obse
         return +ui;
     }
 
-    public update(sender: IObservable, data: any): void {
-        if (data["action"] == "value") {
-            this._tmp = data["value"];
-            this.updateAndValidateUI();
+    public update(sender: any, data: any): void {
+        switch (data["action"]) {
+            case "ngparamAfterValue":
+                // on ne fait rien au cas où la modif vient de l'interface (on ne remet pas à jour _uiValue ce qui permet
+                // de garder par ex le '.' si on supprime le '2' de '1.2')
+                if (sender !== this) {
+                    this._tmp = data["value"];
+                    this.updateAndValidateUI();
+                }
         }
     }
 }
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 248783aa69176ffb583ad2bbfcf5efa7b69d8f04..b504852524f154d58adcfce7ab77d1030ab30036 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
@@ -12,19 +12,19 @@
 
         <!-- 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(symbol, 'fix')" [checked]=radioFixCheck [disabled]=isDisabled id="radio_fix">
+            value="fix" (click)="onRadioClick('fix')" [checked]=radioFixCheck [disabled]=isDisabled id="radio_fix">
             {{uitextParamFixe}}
         </label>
 
         <!-- 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(symbol, 'var')" [checked]=radioVarCheck [disabled]=isDisabled id="radio_var">
+            value="var" (click)="onRadioClick('var')" [checked]=radioVarCheck [disabled]=isDisabled id="radio_var">
             {{uitextParamVarier}}
         </label>
 
         <!-- 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(symbol, 'cal')" [checked]=radioCalCheck [disabled]=isDisabled id="radio_cal">
+            value="cal" (click)="onRadioClick('cal')" [checked]=radioCalCheck [disabled]=isDisabled id="radio_cal">
             {{uitextParamCalculer}}
         </label>
     </div>
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 604f87eb0a2c206c2d149749f9591215acaf4089..51df675e6978975bf3c820d9d7bef531d87e5265 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,4 +1,4 @@
-import { Component, ViewChild, Input, Output, EventEmitter, AfterViewChecked } from "@angular/core";
+import { Component, ViewChild, Input, Output, EventEmitter, OnChanges } from "@angular/core";
 
 import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
 import { NgParameter, ParamRadioConfig } from "../../formulaire/ngparam";
@@ -25,7 +25,7 @@ import { FormulaireService } from "../../services/formulaire/formulaire.service"
         }`
     ]
 })
-export class ParamFieldLineComponent implements AfterViewChecked {
+export class ParamFieldLineComponent implements OnChanges {
     @Input("param")
     private _param: NgParameter;
 
@@ -163,10 +163,13 @@ export class ParamFieldLineComponent implements AfterViewChecked {
      */
 
     @Output()
-    private onRadio = new EventEmitter<string>();
+    private onRadio = new EventEmitter<any>();
 
-    private onRadioClick(symbol: string, option: string) {
-        this.onRadio.emit(symbol + "_" + option);
+    private onRadioClick(option: string) {
+        this.onRadio.emit({
+            "param": this._param,
+            "option": option
+        });
 
         // MAJ validité
         this.emitValidity();
@@ -257,7 +260,7 @@ export class ParamFieldLineComponent implements AfterViewChecked {
         this.emitValidity()
     }
 
-    public ngAfterViewChecked() {
+    public ngOnChanges() {
         this._ngParamInputComponent.model = this._param;
         this._ngParamInputComponent.showError = this.isRadioFixChecked;
     }
diff --git a/src/app/components/param-values/ngparam-max.component.ts b/src/app/components/param-values/ngparam-max.component.ts
index f6cbbbeaad606a29534d9e0e4464d1d4c4ef302d..ae6852ef8e79280815c026b34f15fca5ccf84e6f 100644
--- a/src/app/components/param-values/ngparam-max.component.ts
+++ b/src/app/components/param-values/ngparam-max.component.ts
@@ -1,4 +1,4 @@
-import { Component, Input } from "@angular/core";
+import { Component, Input, ChangeDetectorRef } from "@angular/core";
 
 import { NumericalString } from "jalhyd";
 
@@ -11,8 +11,8 @@ import { NgParameter } from "../../formulaire/ngparam";
     templateUrl: "../generic-input/generic-input.component.html"
 })
 export class NgParamMaxComponent extends GenericInputComponent {
-    constructor(private intlService: InternationalisationService) {
-        super();
+    constructor(private intlService: InternationalisationService, cdRef: ChangeDetectorRef) {
+        super(cdRef);
     }
 
     /**
diff --git a/src/app/components/param-values/ngparam-min.component.ts b/src/app/components/param-values/ngparam-min.component.ts
index cd00396c1899ee7f42deeb0eb795b6b9953804e8..162307df29dc144bc54d98b6fb3857c49b03812d 100644
--- a/src/app/components/param-values/ngparam-min.component.ts
+++ b/src/app/components/param-values/ngparam-min.component.ts
@@ -1,4 +1,4 @@
-import { Component, Input } from "@angular/core";
+import { Component, Input, ChangeDetectorRef } from "@angular/core";
 
 import { NumericalString } from "jalhyd";
 
@@ -11,8 +11,8 @@ import { NgParameter } from "../../formulaire/ngparam";
     templateUrl: "../generic-input/generic-input.component.html"
 })
 export class NgParamMinComponent extends GenericInputComponent {
-    constructor(private intlService: InternationalisationService) {
-        super();
+    constructor(private intlService: InternationalisationService, cdRef: ChangeDetectorRef) {
+        super(cdRef);
     }
 
     /**
diff --git a/src/app/components/param-values/ngparam-step.component.ts b/src/app/components/param-values/ngparam-step.component.ts
index 3825d5280be572d00466d63928d8c399412533d4..a1467d2953dfa1783dce05e5dc8568eb863c8372 100644
--- a/src/app/components/param-values/ngparam-step.component.ts
+++ b/src/app/components/param-values/ngparam-step.component.ts
@@ -1,4 +1,4 @@
-import { Component, Input } from "@angular/core";
+import { Component, Input, ChangeDetectorRef } from "@angular/core";
 
 import { NumericalString } from "jalhyd";
 
@@ -11,8 +11,8 @@ import { NgParameter } from "../../formulaire/ngparam";
     templateUrl: "../generic-input/generic-input.component.html"
 })
 export class NgParamStepComponent extends GenericInputComponent {
-    constructor(private intlService: InternationalisationService) {
-        super();
+    constructor(private intlService: InternationalisationService, cdRef: ChangeDetectorRef) {
+        super(cdRef);
     }
 
     /**
diff --git a/src/app/components/param-values/param-values.component.ts b/src/app/components/param-values/param-values.component.ts
index 8ec032838f757a9982acd1939fafc05a476ed9ee..ff493337bb98d38f71e9f800bae5dbc9cc2aba63 100644
--- a/src/app/components/param-values/param-values.component.ts
+++ b/src/app/components/param-values/param-values.component.ts
@@ -317,10 +317,10 @@ export class ParamValuesComponent extends BaseComponent implements AfterViewChec
         this._param.valueMode = next;
     }
 
-    /**
-     * appelé au 1er affichage du composant
+    /** 
+     * appelé quand les @Input changent
      */
-    protected afterFirstViewChecked() {
+    ngOnChanges() {
         if (this.isMinMax)
             this._doInitMinmax = true;
         else
diff --git a/src/app/components/param-values/value-list.component.ts b/src/app/components/param-values/value-list.component.ts
index 2286d317e256af2e408c6495f276d1ea9d81bd53..86e37fe7c4bca6a176c8def0583e2def7b57a81d 100644
--- a/src/app/components/param-values/value-list.component.ts
+++ b/src/app/components/param-values/value-list.component.ts
@@ -1,4 +1,4 @@
-import { Component, Input } from "@angular/core";
+import { Component, Input, ChangeDetectorRef } from "@angular/core";
 
 import { GenericInputComponent } from "../generic-input/generic-input.component";
 import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
@@ -10,8 +10,8 @@ import { Message } from "jalhyd";
     templateUrl: "../generic-input/generic-input.component.html"
 })
 export class ValueListComponent extends GenericInputComponent {
-    constructor(private intlService: InternationalisationService) {
-        super();
+    constructor(private intlService: InternationalisationService, cdRef: ChangeDetectorRef) {
+        super(cdRef);
     }
 
     /**
diff --git a/src/app/components/remous-results/remous-results.component.html b/src/app/components/remous-results/remous-results.component.html
index a2eecd6ce48e05577319392fd258f9023363bac9..bf217e66982630e43c971bd5682b1de09792f422 100644
--- a/src/app/components/remous-results/remous-results.component.html
+++ b/src/app/components/remous-results/remous-results.component.html
@@ -1,54 +1,56 @@
-<div class="row">
-    <div class="col">
-        <chart [type]="graph1_type" [data]="graph1_data" [options]="graph1_options"></chart>
+<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>
-</div>
-<div class="row">
-    <div class="col">
-        <chart *ngIf="extraGraph" [type]="graph2_type" [data]="graph2_data" [options]="graph2_options"></chart>
+    <div class="row">
+        <div class="col">
+            <chart *ngIf="extraGraph" [type]="graph2_type" [data]="graph2_data" [options]="graph2_options"></chart>
+        </div>
     </div>
-</div>
 
-<!-- journal -->
-<log></log>
+    <!-- journal -->
+    <log></log>
 
-<div *ngIf="hasSeries" class="row">
-    <!-- résultats numériques -->
-    <table class="table">
-        <thead>
-            <tr>
-                <th></th>
-                <th>{{uitextLigneFluviale}}</th>
-                <th></th>
-                <th>{{uitextLigneTorrentielle}}</th>
-                <th></th>
+    <div *ngIf="hasSeries" class="row">
+        <!-- résultats numériques -->
+        <table class="table">
+            <thead>
+                <tr>
+                    <th></th>
+                    <th>{{uitextLigneFluviale}}</th>
+                    <th></th>
+                    <th>{{uitextLigneTorrentielle}}</th>
+                    <th></th>
+                </tr>
+                <tr>
+                    <th>{{uitextAbscisse}}</th>
+                    <th>{{uitextTirant}}</th>
+                    <th>{{extraParamLabel}}</th>
+                    <th>{{uitextTirant}}</th>
+                    <th>{{extraParamLabel}}</th>
+                </tr>
+            </thead>
+            <tr *ngFor="let r of values; let i=index" [class]="getResultClass(i)">
+                <td>{{r.abs}}</td>
+                <!-- <td>{{r.flu}}</td> -->
+                <td>
+                    <single-result [result]=r.flu></single-result>
+                </td>
+                <!-- <td>{{r.extraFlu}}</td> -->
+                <td>
+                    <single-result [result]=r.extraFlu></single-result>
+                </td>
+                <!-- <td>{{r.tor}}</td> -->
+                <td>
+                    <single-result [result]=r.tor></single-result>
+                </td>
+                <!-- <td>{{r.extraTor}}</td> -->
+                <td>
+                    <single-result [result]=r.extraTor></single-result>
+                </td>
             </tr>
-            <tr>
-                <th>{{uitextAbscisse}}</th>
-                <th>{{uitextTirant}}</th>
-                <th>{{extraParamLabel}}</th>
-                <th>{{uitextTirant}}</th>
-                <th>{{extraParamLabel}}</th>
-            </tr>
-        </thead>
-        <tr *ngFor="let r of values; let i=index" [class]="getResultClass(i)">
-            <td>{{r.abs}}</td>
-            <!-- <td>{{r.flu}}</td> -->
-            <td>
-                <single-result [result]=r.flu></single-result>
-            </td>
-            <!-- <td>{{r.extraFlu}}</td> -->
-            <td>
-                <single-result [result]=r.extraFlu></single-result>
-            </td>
-            <!-- <td>{{r.tor}}</td> -->
-            <td>
-                <single-result [result]=r.tor></single-result>
-            </td>
-            <!-- <td>{{r.extraTor}}</td> -->
-            <td>
-                <single-result [result]=r.extraTor></single-result>
-            </td>
-        </tr>
-    </table>
+        </table>
+    </div>
 </div>
\ No newline at end of file
diff --git a/src/app/components/remous-results/remous-results.component.ts b/src/app/components/remous-results/remous-results.component.ts
index c72b5b10c3e68b38eb40b531a3fac592f290871c..e414ced117f44168ee75dac1ee6b44b2f0ede8c0 100644
--- a/src/app/components/remous-results/remous-results.component.ts
+++ b/src/app/components/remous-results/remous-results.component.ts
@@ -5,6 +5,7 @@ import { Result, ArrayReverseIterator, ResultElement } from "jalhyd";
 import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
 import { LogComponent } from "../../components/log/log.component";
 import { RemousResults } from "../../results/remous-results";
+import { CalculatorResults } from "../../results/calculator-results";
 
 @Component({
     selector: "remous-results",
@@ -52,6 +53,11 @@ export class RemousResultsComponent {
      */
     private _values: Object[];
 
+    /**
+     * true si les résultats doivent être mis à jour
+     */
+    private _doUpdate: boolean = false;
+
     /**
      * composant journal
      */
@@ -109,8 +115,15 @@ export class RemousResultsComponent {
         return this._results == undefined ? undefined : this._results.extraParamLabel;
     }
 
-    public set results(r: RemousResults) {
-        this._results = r;
+    public set results(rs: CalculatorResults[]) {
+        this._results = undefined;
+        if (rs != undefined)
+            for (const r of rs) {
+                if (r instanceof RemousResults) {
+                    this._results = r;
+                    break;
+                }
+            }
         this.updateView();
     }
 
@@ -119,12 +132,29 @@ export class RemousResultsComponent {
         this.graph1_options = {};
         this.graph2_data = {};
         this.graph2_options = {};
-        this.logComponent.log = undefined;
+        if (this.logComponent != undefined)
+            this.logComponent.log = undefined;
         this._values = [];
-        if (this._results != undefined) {
+
+        if (this._results != undefined)
+            this._doUpdate = this._results.hasResults;
+    }
+
+    /** 
+     * appelé pour gérer les changements non détectés par Angular
+     */
+    public ngDoCheck() {
+        if (this._doUpdate)
+            this._doUpdate = !this.updateResults();
+    }
+
+    private updateResults() {
+        if (this.logComponent != undefined && this._results != undefined) {
             this.logComponent.log = this._results.log;
             this.generateGraph();
+            return true;
         }
+        return false;
     }
 
     private connectRessaut(lineFlu: LineData, lineTor: LineData) {
@@ -327,6 +357,10 @@ export class RemousResultsComponent {
     private getResultClass(i: number) {
         return "result_id_" + String(i & 1);
     }
+
+    private get hasResults(): boolean {
+        return this._results != undefined && this._results.hasResults;
+    }
 }
 
 /**
diff --git a/src/app/components/results-graph/graph-type.component.ts b/src/app/components/results-graph/graph-type.component.ts
index 4415170097085256f94e1ee1163a6a852817acbd..efe9d3527014fe75ac92bd1e6461a28687c4e5a4 100644
--- a/src/app/components/results-graph/graph-type.component.ts
+++ b/src/app/components/results-graph/graph-type.component.ts
@@ -10,6 +10,7 @@ import { GraphType } from "../../results/fixed-var-results";
 export class GraphTypeSelectComponent extends GenericSelectComponent<GraphType> {
     private _entries: GraphType[] = [GraphType.Histogram, GraphType.Scatter];
     private _entriesLabels: string[] = ["Histogramme", "XY"];
+    private _selected: GraphType;
 
     protected get entries(): GraphType[] {
         return this._entries;
@@ -19,4 +20,13 @@ export class GraphTypeSelectComponent extends GenericSelectComponent<GraphType>
         const i = this._entries.indexOf(entry);
         return this._entriesLabels[i];
     }
+
+    public get selectedValue(): GraphType {
+        return this._selected;
+    }
+
+
+    public set selectedValue(v: GraphType) {
+        this._selected = v;
+    }
 }
diff --git a/src/app/components/section-canvas/section-canvas.component.ts b/src/app/components/section-canvas/section-canvas.component.ts
index fcac05278683d9789d1e87f277b82c15da8e3f36..ce5d8ef7cee5bcc60a3de392b9c1fee9b35fac52 100644
--- a/src/app/components/section-canvas/section-canvas.component.ts
+++ b/src/app/components/section-canvas/section-canvas.component.ts
@@ -40,7 +40,7 @@ export class SectionCanvasComponent {
     private _section: acSection;
 
     // tirants
-    private _levels: Object[];
+    private _levels: Object[] = [];
 
     @ViewChild("calcCanvas")
     private _calcCanvas: CalcCanvasComponent;
diff --git a/src/app/components/section-results/section-results.component.html b/src/app/components/section-results/section-results.component.html
index c9143041f40990f44d6d3e28e8670cedbe7a5927..02e93f9881ab3a923f15644d3510854ce2ec3d5a 100644
--- a/src/app/components/section-results/section-results.component.html
+++ b/src/app/components/section-results/section-results.component.html
@@ -1,20 +1,22 @@
-<!-- graphique -->
-<div class="row">
-    <div class="col" style="text-align: center">
-        <section-canvas></section-canvas>
+<div class="container-fluid" *ngIf="hasResults">
+    <!-- graphique -->
+    <div class="row">
+        <div class="col" style="text-align: center">
+            <section-canvas></section-canvas>
+        </div>
     </div>
-</div>
 
-<!-- tableau de valeurs -->
-<div *ngIf="hasResults" class="row">
-    <div class="col mx-auto">
-        <table style="width: 100%">
-            <tr *ngFor="let r of sectionResults; let i=index" [class]="getResultClass(i)">
-                <td class="result_label">{{r.label}}</td>
-                <td class="result_value">
-                    <single-result [result]=r.value></single-result>
-                </td>
-            </tr>
-        </table>
+    <!-- tableau de valeurs -->
+    <div class="row">
+        <div class="col mx-auto">
+            <table style="width: 100%">
+                <tr *ngFor="let r of sectionResults; let i=index" [class]="getResultClass(i)">
+                    <td class="result_label">{{r.label}}</td>
+                    <td class="result_value">
+                        <single-result [result]=r.value></single-result>
+                    </td>
+                </tr>
+            </table>
+        </div>
     </div>
-</div>
\ No newline at end of file
+</div>
diff --git a/src/app/components/section-results/section-results.component.ts b/src/app/components/section-results/section-results.component.ts
index 528e3ef84f8c009fa76e60e055cbce42ba8bb30c..ea6daaae41a7af3c770e26ad684c1dceed25af32 100644
--- a/src/app/components/section-results/section-results.component.ts
+++ b/src/app/components/section-results/section-results.component.ts
@@ -1,10 +1,11 @@
-import { Component, ViewChild } from '@angular/core';
+import { Component, ViewChild, DoCheck } from '@angular/core';
 
 import { acSection, Result } from 'jalhyd';
 
 import { SectionCanvasComponent } from '../section-canvas/section-canvas.component';
 import { SectionResults } from '../../results/section-results';
 import { ApplicationSetupService } from '../../services/app-setup/app-setup.service';
+import { CalculatorResults } from '../../results/calculator-results';
 
 @Component({
     selector: 'section-results',
@@ -30,11 +31,11 @@ import { ApplicationSetupService } from '../../services/app-setup/app-setup.serv
     `
     ]
 })
-export class SectionResultsComponent {
+export class SectionResultsComponent implements DoCheck {
     /**
     * résultats non mis en forme
     */
-    private _sectionResults: SectionResults;
+    private _results: SectionResults;
 
     constructor(private appSetupService: ApplicationSetupService) { }
 
@@ -48,25 +49,44 @@ export class SectionResultsComponent {
         "Yco": { r: 255, g: 0, b: 255 },
     };
 
+    private _doUpdate: boolean = false;
+
     @ViewChild(SectionCanvasComponent)
     private _sectionCanvas: SectionCanvasComponent;
 
-    public set results(r: SectionResults) {
-        this._sectionResults = r;
+    public set results(rs: CalculatorResults[]) {
+        this._results = undefined;
+        if (rs != undefined)
+            for (const r of rs) {
+                if (r instanceof SectionResults) {
+                    this._results = r;
+                    break;
+                }
+            }
         this.updateView();
     }
 
     public updateView() {
-        this._sectionCanvas.reset();
-        if (this._sectionResults != undefined)
-            this.formatResults();
+        if (this._sectionCanvas != undefined)
+            this._sectionCanvas.reset();
+
+        if (this._results != undefined)
+            this._doUpdate = this._results.hasResults;
+    }
+
+    /** 
+     * appelé pour gérer les changements non détectés par Angular
+     */
+    public ngDoCheck() {
+        if (this._doUpdate)
+            this._doUpdate = !this.updateResults();
     }
 
-    private formatResults() {
-        if (this._sectionResults.results.length > 0) {
+    private updateResults() {
+        if (this._results != undefined && this._sectionCanvas != undefined) {
             const nDigits = this.appSetupService.displayDigits;
 
-            for (let r of this._sectionResults.results) {
+            for (let r of this._results.results) {
                 const v: Result = r["value"];
                 const l = r["label"];
 
@@ -74,16 +94,18 @@ export class SectionResultsComponent {
                 if (drawLabel != undefined && v.vCalc != undefined)
                     this._sectionCanvas.addLevel(v.vCalc, drawLabel + " = " + v.vCalc.toFixed(nDigits), SectionResultsComponent.labelColors[drawLabel]);
             }
-            this._sectionCanvas.section = this._sectionResults.section;
+            this._sectionCanvas.section = this._results.section;
+            return true;
         }
+        return false;
     }
 
     private get hasResults(): boolean {
-        return this._sectionResults != undefined;
+        return this._results != undefined && this._results.hasResults;
     }
 
     private get sectionResults() {
-        return this._sectionResults.results;
+        return this._results.results;
     }
 
     private getResultClass(i: number) {
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
index f1facb0c075f826390f52a083c6ac1da847f0f5d..f3e7ae806429ca653a341a374718f70a5e1295e0 100644
--- a/src/app/components/select-field-line/select-field-line.component.html
+++ b/src/app/components/select-field-line/select-field-line.component.html
@@ -13,4 +13,4 @@
             <a class="dropdown-item" *ngFor="let e of entries" [value]=e>{{entryLabel(e)}}</a>
         </div>
     </div>
-</div>
+</div>
\ No newline at end of file
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 a6911f97fd2c554bd671900c4ce09e3a333fc99f..5578fe79b75e677c49d5552f72ec90c6de8eec24 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
@@ -27,7 +27,11 @@ export class SelectFieldLineComponent extends GenericSelectComponent<SelectEntry
         return entry.label;
     }
 
-    protected selectedValueChanged(val: SelectEntry) {
-        this._select.setValue(val.value);
+    public get selectedValue(): SelectEntry {
+        return this._select.getValue();
+    }
+
+    public set selectedValue(v: SelectEntry) {
+        this._select.setValue(v);
     }
 }
diff --git a/src/app/formulaire/check-field.ts b/src/app/formulaire/check-field.ts
index 098e3abc6d9c756b8c935f18e3bd3bf814442eb3..b33f214ef554564eefda72e3dab4d18a84efecce 100644
--- a/src/app/formulaire/check-field.ts
+++ b/src/app/formulaire/check-field.ts
@@ -1,14 +1,13 @@
-import { ComputeNodeType } from "jalhyd";
-
-import { Field, FieldType } from "./field";
-import { Dependency } from "./dependency";
-import { DependencyConditionType } from "./dependency-condition";
+import { Field } from "./field";
+import { Dependency } from "./dependency/dependency";
+import { DependencyConditionType } from "./dependency/dependency-condition";
+import { FormulaireDefinition } from "./definition/form-definition";
 
 export class CheckField extends Field {
     private _value: boolean;
 
-    constructor(nodeType: ComputeNodeType, id: string, formId: number) {
-        super(nodeType, id, FieldType.Check, formId);
+    constructor(isTmpl: boolean = false) {
+        super(isTmpl);
         this._value = false;
     }
 
@@ -27,4 +26,24 @@ export class CheckField extends Field {
     public get isValid(): boolean {
         return true;
     }
+
+    /**
+     * copie des membres
+     */
+    protected copyMembers(n: CheckField) {
+        super.copyMembers(n);
+        n._value = this._value;
+    }
+
+    /**
+     * crée une nouvelle instance
+     */
+    protected clone(): CheckField {
+        return new CheckField();
+    }
+
+    public parseConfig(json: {}, data?: {}) {
+        this._confId = json["id"];
+        this.setValue(json["value"] == "true");
+    }
 }
diff --git a/src/app/formulaire/definition/concrete/form-cond-distri.ts b/src/app/formulaire/definition/concrete/form-cond-distri.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9c11da38b6b95b32215ea6ecd4022a1cee2e5d47
--- /dev/null
+++ b/src/app/formulaire/definition/concrete/form-cond-distri.ts
@@ -0,0 +1,61 @@
+import { FormDefFixedVar } from "../form-def-fixedvar";
+import { CalculatorType } from "jalhyd";
+import { ParamService } from "../../../services/param/param.service";
+import { ApplicationSetupService } from "../../../services/app-setup/app-setup.service";
+import { FormResultFixedVar } from "../form-result-fixedvar";
+import { FormComputeConduiteDistributrice } from "../form-compute-cond-distri";
+import { FormulaireDefinition } from "../form-definition";
+import { CalculatorResults } from "../../../results/calculator-results";
+import { FormDefParamToCalculate } from "../form-def-paramcalc";
+
+export class FormulaireConduiteDistributrice extends FormulaireDefinition {
+    private _formFixedVar: FormDefFixedVar;
+
+    private _formParamCalc: FormDefParamToCalculate;
+
+    private _formCompute: FormComputeConduiteDistributrice;
+
+    private _formResult: FormResultFixedVar;
+
+    constructor(paramService: ParamService, appSetupService: ApplicationSetupService) {
+        super(CalculatorType.ConduiteDistributrice, paramService, appSetupService);
+        this._formFixedVar = new FormDefFixedVar(this);
+        this._formParamCalc = new FormDefParamToCalculate(this);
+        this._formResult = new FormResultFixedVar(this, false);
+        this._formCompute = new FormComputeConduiteDistributrice(this, this._formResult);
+    }
+
+    protected initParse() {
+        this._formParamCalc.initParse();
+    }
+
+    protected parseOptions(json: {}) {
+        this._formParamCalc.parseOptions(json);
+    }
+
+    protected completeParse() {
+    }
+
+    /**
+     * gestion du clic sur les radios "paramètre fixé, à varier, à calculer"
+     */
+    public onRadioClick(info: string) {
+        this._formParamCalc.onRadioClick(info);
+    }
+
+    public resetResults() {
+        this._formResult.resetResults();
+    }
+
+    public doCompute() {
+        this._formCompute.doCompute();
+    }
+
+    public get hasResults(): boolean {
+        return this._formResult.hasResults;
+    }
+
+    public get results(): CalculatorResults[] {
+        return this._formResult.results;
+    }
+}
diff --git a/src/app/formulaire/definition/concrete/form-courbe-remous.ts b/src/app/formulaire/definition/concrete/form-courbe-remous.ts
new file mode 100644
index 0000000000000000000000000000000000000000..88bd5a352d1358df0515f6e71c5c90eb7bcc181c
--- /dev/null
+++ b/src/app/formulaire/definition/concrete/form-courbe-remous.ts
@@ -0,0 +1,52 @@
+import { CalculatorType } from "jalhyd";
+
+import { ParamService } from "../../../services/param/param.service";
+import { ApplicationSetupService } from "../../../services/app-setup/app-setup.service";
+import { FormResultRemous } from "../form-result-remous";
+import { FormDefSection } from "../form-def-section";
+import { FormComputeCourbeRemous } from "../form-compute-courbe-remous";
+import { FormulaireDefinition } from "../form-definition";
+import { CalculatorResults } from "../../../results/calculator-results";
+
+export class FormulaireCourbeRemous extends FormulaireDefinition {
+    private _formSection: FormDefSection;
+
+    private _formCompute: FormComputeCourbeRemous;
+
+    private _formResult: FormResultRemous;
+
+    constructor(paramService: ParamService, appSetupService: ApplicationSetupService) {
+        super(CalculatorType.CourbeRemous, paramService, appSetupService)
+        this._formSection = new FormDefSection(this, appSetupService);
+        this._formResult = new FormResultRemous(this);
+        this._formCompute = new FormComputeCourbeRemous(this, this._formSection, this._formResult);
+    }
+
+    protected initParse() {
+        this._formSection.initParse();
+    }
+
+    protected parseOptions(json: {}) {
+        this._formSection.parseOptions(json);
+    }
+
+    protected completeParse() {
+        this._formSection.completeParse();
+    }
+
+    public resetResults() {
+        this._formResult.resetResults();
+    }
+
+    public doCompute() {
+        this._formCompute.doCompute();
+    }
+
+    public get hasResults(): boolean {
+        return this._formResult.hasResults;
+    }
+
+    public get results(): CalculatorResults[] {
+        return this._formResult.results;
+    }
+}
\ No newline at end of file
diff --git a/src/app/formulaire/definition/concrete/form-lechapt-calmon.ts b/src/app/formulaire/definition/concrete/form-lechapt-calmon.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8a7070d00abc5f91ef54a72f0870982e50289a31
--- /dev/null
+++ b/src/app/formulaire/definition/concrete/form-lechapt-calmon.ts
@@ -0,0 +1,67 @@
+import { CalculatorType } from "jalhyd";
+
+import { FormResultFixedVar } from "../form-result-fixedvar";
+import { FormResultSection } from "../form-result-section";
+import { FormulaireElement } from "../../formulaire-element";
+import { NgParameter, ParamRadioConfig } from "../../ngparam";
+import { Field } from "../../field";
+import { ParamService } from "../../../services/param/param.service";
+import { ApplicationSetupService } from "../../../services/app-setup/app-setup.service";
+import { FormDefSection } from "../form-def-section";
+import { FormDefFixedVar } from "../form-def-fixedvar";
+import { FormComputeLechaptCalmon } from "../form-compute-lechapt-calmon";
+import { FormulaireDefinition } from "../form-definition";
+import { CalculatorResults } from "../../../results/calculator-results";
+import { FormDefParamToCalculate } from "../form-def-paramcalc";
+
+export class FormulaireLechaptCalmon extends FormulaireDefinition {
+    private _formFixedVar: FormDefFixedVar;
+
+    private _formParamCalc: FormDefParamToCalculate;
+
+    private _formCompute: FormComputeLechaptCalmon;
+
+    private _formResult: FormResultFixedVar;
+
+    constructor(paramService: ParamService, appSetupService: ApplicationSetupService) {
+        super(CalculatorType.LechaptCalmon, paramService, appSetupService)
+        this._formFixedVar = new FormDefFixedVar(this);
+        this._formParamCalc = new FormDefParamToCalculate(this);
+        this._formResult = new FormResultFixedVar(this, false);
+        this._formCompute = new FormComputeLechaptCalmon(this, this._formResult);
+    }
+
+    protected initParse() {
+        this._formParamCalc.initParse();
+    }
+
+    protected parseOptions(json: {}) {
+        this._formParamCalc.parseOptions(json);
+    }
+
+    protected completeParse() {
+    }
+
+    /**
+     * gestion du clic sur les radios "paramètre fixé, à varier, à calculer"    
+     */
+    public onRadioClick(info: string) {
+        this._formParamCalc.onRadioClick(info);
+    }
+
+    public resetResults() {
+        this._formResult.resetResults();
+    }
+
+    public doCompute() {
+        this._formCompute.doCompute();
+    }
+
+    public get hasResults(): boolean {
+        return this._formResult.hasResults;
+    }
+
+    public get results(): CalculatorResults[] {
+        return this._formResult.results;
+    }
+}
diff --git a/src/app/formulaire/definition/concrete/form-parallel-structures.ts b/src/app/formulaire/definition/concrete/form-parallel-structures.ts
new file mode 100644
index 0000000000000000000000000000000000000000..af41d8efb6a4acf6e8c7334a928db358969dc19e
--- /dev/null
+++ b/src/app/formulaire/definition/concrete/form-parallel-structures.ts
@@ -0,0 +1,176 @@
+import { FormulaireDefinition } from "../form-definition";
+import { CalculatorResults } from "../../../results/calculator-results";
+import { CalculatorType, CreateStructure, StructureType, LoiDebit } from "jalhyd";
+import { ParamService } from "../../../services/param/param.service";
+import { ApplicationSetupService } from "../../../services/app-setup/app-setup.service";
+import { FormDefParamToCalculate } from "../form-def-paramcalc";
+import { FormDefFixedVar } from "../form-def-fixedvar";
+import { FormDefParallelStructures } from "../form-def-parallel-structures";
+import { FormComputeParallelStructures } from "../form-compute-parallel-structures";
+import { FormResultFixedVar } from "../form-result-fixedvar";
+import { FieldsetContainer } from "../../fieldset-container";
+import { FieldSet } from "../../fieldset";
+import { SelectField } from "../../select-field";
+import { NgParameter } from "../../ngparam";
+
+export class FormulaireParallelStructure extends FormulaireDefinition {
+    private _formFixedVar: FormDefFixedVar;
+
+    private _formParallelStruct: FormDefParallelStructures;
+
+    private _formParamCalc: FormDefParamToCalculate;
+
+    private _formCompute: FormComputeParallelStructures;
+
+    private _formResult: FormResultFixedVar;
+
+    constructor(paramService: ParamService, appSetupService: ApplicationSetupService) {
+        super(CalculatorType.ParallelStructure, paramService, appSetupService)
+        this._formFixedVar = new FormDefFixedVar(this);
+        this._formParamCalc = new FormDefParamToCalculate(this);
+        this._formResult = new FormResultFixedVar(this, false);
+        this._formParallelStruct = new FormDefParallelStructures();
+        this._formCompute = new FormComputeParallelStructures(this, this._formParallelStruct, this._formResult);
+    }
+
+    protected initParse() {
+        this._formParamCalc.initParse();
+    }
+
+    protected parseOptions(json: {}) {
+        this._formParamCalc.parseOptions(json);
+    }
+
+    protected completeParse() {
+        this.subscribeFieldsetContainer();
+    }
+
+    /**
+     * gestion du clic sur les radios "paramètre fixé, à varier, à calculer"    
+     */
+    public onRadioClick(info: string) {
+        this._formParamCalc.onRadioClick(info);
+    }
+
+    /**
+     * @return une chaîne représentant le "contexte" courant (ici, combinaison type d'ouvrage-loi de débit)
+     * @param fs FieldSet contenant les listes déroulantes type d'ouvrage et loi de débit
+     */
+    private getContext(fs: FieldSet): string {
+        // type d'ouvrage courant
+        const fsStructType = this._formParallelStruct.getStructureType(fs);
+
+        // loi de débit courante
+        const loiDebit = this._formParallelStruct.getLoiDebit(fs);
+
+        return `${StructureType[fsStructType]}-${LoiDebit[loiDebit]}`
+    }
+
+    /**
+     * réinitialisation du formulaire.
+     * en plus de du comportement par défaut, on remet les valeurs des champs en fonction du contexte
+     * (type de structure, loi de débit)
+     */
+    public reset() {
+        super.reset();
+
+        for (const e of this.allFormElements) {
+            if (e instanceof FieldSet) {
+                const fs = e as FieldSet;
+                if (!fs.isTemplate && fs.calculatorType === CalculatorType.Structure) {
+                    const fsStructType: StructureType = this._formParallelStruct.getStructureType(fs);
+                    const loiDebit: LoiDebit = this._formParallelStruct.getLoiDebit(fs);
+                    const st = CreateStructure(fsStructType, loiDebit);
+
+                    for (const p of fs.allFormElements)
+                        if (p instanceof NgParameter) {
+                            const defaultParam = st.prms.map[p.symbol];
+                            p.resetValue(this, this.getContext(fs), defaultParam.v);
+                        }
+                }
+            }
+        }
+
+        // prévenir les composants qu'il faut détecter les changements
+        // this.notifyReset();
+    }
+
+    // private notifyReset() {
+    //     this.notifyObservers({
+    //         "action": "reset"
+    //     }, this);
+    // }
+
+    public resetResults() {
+        this._formResult.resetResults();
+    }
+
+    public doCompute() {
+        this._formCompute.doCompute();
+    }
+
+    public get hasResults(): boolean {
+        return this._formResult.hasResults;
+    }
+
+    public get results(): CalculatorResults[] {
+        return this._formResult.results;
+    }
+
+    /**
+     * abonnement en tant qu'observateur du FieldsetContainer
+     */
+    private subscribeFieldsetContainer() {
+        const n = this.getFormulaireNodeById("struct_container");
+        if (n == undefined || !(n instanceof FieldsetContainer))
+            throw new Error("l'élément 'struct_container' n'est pas du type FieldsetContainer");
+        const fsc: FieldsetContainer = n as FieldsetContainer;
+        fsc.addObserver(this);
+    }
+
+    /**
+     * abonnement en tant qu'observateur des NgParameter des FieldSet contenus dans le FieldsetContainer
+     */
+    private subscribeStructureInputFields(fs: FieldSet) {
+        for (const n of fs.allFormElements)
+            if (n instanceof NgParameter)
+                n.addObserver(this);
+    }
+    /**
+     * abonnement en tant qu'observateur du SelectField des FieldSet contenus dans le FieldsetContainer
+     */
+    private subscribeStructureSelectFields(fs: FieldSet) {
+        for (const n of fs.allFormElements)
+            if (n instanceof SelectField)
+                n.addObserver(this);
+    }
+
+    // interface Observer
+
+    public update(sender: any, data: any) {
+        if (sender instanceof FieldsetContainer)
+            switch (data.action) {
+                case "newFieldset":
+                    this.reset();
+                    this.subscribeStructureInputFields(data["fieldset"]);
+                    this.subscribeStructureSelectFields(data["fieldset"]);
+            }
+        else if (sender instanceof SelectField) {
+            // la valeur d'un des select (type d'ouvrage, loi de débit) a changé
+            this.reset();
+        }
+        // else if (sender instanceof NgParameter) {
+        else {
+            switch (data.action) {
+                case "ngparamBeforeValue":  // la valeur d'un NgParameter est sur le point d'être modifiée
+                    const param: NgParameter = data.param;
+
+                    // FieldSet parent    
+                    const parentFieldset = param.getDirectParent(this) as FieldSet;
+
+                    // contexte dans lequel la valeur du paramètre est fixée manuellement
+                    param.currentContextId = this.getContext(parentFieldset);
+            }
+        }
+    }
+}
diff --git a/src/app/formulaire/definition/concrete/form-passe-bassin-dim.ts b/src/app/formulaire/definition/concrete/form-passe-bassin-dim.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6a7041919fe57e9235b48eced4381f4985a94271
--- /dev/null
+++ b/src/app/formulaire/definition/concrete/form-passe-bassin-dim.ts
@@ -0,0 +1,62 @@
+import { CalculatorType } from "jalhyd";
+
+import { CalculatorResults } from "../../../results/calculator-results";
+import { FormulaireDefinition } from "../form-definition";
+import { FormDefFixedVar } from "../form-def-fixedvar";
+import { FormDefParamToCalculate } from "../form-def-paramcalc";
+import { FormComputePasseBassinDimensions } from "../form-compute-pab-bassin-dim";
+import { FormResultFixedVar } from "../form-result-fixedvar";
+import { ParamService } from "../../../services/param/param.service";
+import { ApplicationSetupService } from "../../../services/app-setup/app-setup.service";
+
+export class FormulairePasseBassinDimensions extends FormulaireDefinition {
+    private _formFixedVar: FormDefFixedVar;
+
+    private _formParamCalc: FormDefParamToCalculate;
+
+    private _formCompute: FormComputePasseBassinDimensions;
+
+    private _formResult: FormResultFixedVar;
+
+    constructor(paramService: ParamService, appSetupService: ApplicationSetupService) {
+        super(CalculatorType.PabDimensions, paramService, appSetupService);
+        this._formFixedVar = new FormDefFixedVar(this);
+        this._formParamCalc = new FormDefParamToCalculate(this);
+        this._formResult = new FormResultFixedVar(this, false);
+        this._formCompute = new FormComputePasseBassinDimensions(this, this._formResult);
+    }
+
+    protected initParse() {
+        this._formParamCalc.initParse();
+    }
+
+    protected parseOptions(json: {}) {
+        this._formParamCalc.parseOptions(json);
+    }
+
+    protected completeParse() {
+    }
+
+    /**
+     * gestion du clic sur les radios "paramètre fixé, à varier, à calculer"    
+     */
+    public onRadioClick(info: string) {
+        this._formParamCalc.onRadioClick(info);
+    }
+
+    public resetResults() {
+        this._formResult.resetResults();
+    }
+
+    public doCompute() {
+        this._formCompute.doCompute();
+    }
+
+    public get hasResults(): boolean {
+        return this._formResult.hasResults;
+    }
+
+    public get results(): CalculatorResults[] {
+        return this._formResult.results;
+    }
+}
diff --git a/src/app/formulaire/definition/concrete/form-passe-bassin-puissance.ts b/src/app/formulaire/definition/concrete/form-passe-bassin-puissance.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7ade60201d1fa4a3506fba467549993984dc2472
--- /dev/null
+++ b/src/app/formulaire/definition/concrete/form-passe-bassin-puissance.ts
@@ -0,0 +1,61 @@
+import { FormDefFixedVar } from "../form-def-fixedvar";
+import { FormResultFixedVar } from "../form-result-fixedvar";
+import { ApplicationSetupService } from "../../../services/app-setup/app-setup.service";
+import { ParamService } from "../../../services/param/param.service";
+import { CalculatorType } from "jalhyd";
+import { FormulaireDefinition } from "../form-definition";
+import { FormComputePasseBassinPuissance } from "../form-compute-pab-bassin-puissance";
+import { CalculatorResults } from "../../../results/calculator-results";
+import { FormDefParamToCalculate } from "../form-def-paramcalc";
+
+export class FormulairePasseBassinPuissance extends FormulaireDefinition {
+    private _formFixedVar: FormDefFixedVar;
+
+    private _formParamCalc: FormDefParamToCalculate;
+
+    private _formCompute: FormComputePasseBassinPuissance;
+
+    private _formResult: FormResultFixedVar;
+
+    constructor(paramService: ParamService, appSetupService: ApplicationSetupService) {
+        super(CalculatorType.PabPuissance, paramService, appSetupService);
+        this._formFixedVar = new FormDefFixedVar(this);
+        this._formParamCalc = new FormDefParamToCalculate(this);
+        this._formResult = new FormResultFixedVar(this, false);
+        this._formCompute = new FormComputePasseBassinPuissance(this, this._formResult);
+    }
+
+    protected initParse() {
+        this._formParamCalc.initParse();
+    }
+
+    protected parseOptions(json: {}) {
+        this._formParamCalc.parseOptions(json);
+    }
+
+    protected completeParse() {
+    }
+
+    /**
+     * gestion du clic sur les radios "paramètre fixé, à varier, à calculer"    
+     */
+    public onRadioClick(info: string) {
+        this._formParamCalc.onRadioClick(info);
+    }
+
+    public resetResults() {
+        this._formResult.resetResults();
+    }
+
+    public doCompute() {
+        this._formCompute.doCompute();
+    }
+
+    public get hasResults(): boolean {
+        return this._formResult.hasResults;
+    }
+
+    public get results(): CalculatorResults[] {
+        return this._formResult.results;
+    }
+}
diff --git a/src/app/formulaire/definition/concrete/form-regime-uniforme.ts b/src/app/formulaire/definition/concrete/form-regime-uniforme.ts
new file mode 100644
index 0000000000000000000000000000000000000000..48207f7bb713654de399ea8ecf86f80ffd4b5a48
--- /dev/null
+++ b/src/app/formulaire/definition/concrete/form-regime-uniforme.ts
@@ -0,0 +1,68 @@
+import { FormDefFixedVar } from "../form-def-fixedvar";
+import { CalculatorType } from "jalhyd";
+import { ParamService } from "../../../services/param/param.service";
+import { ApplicationSetupService } from "../../../services/app-setup/app-setup.service";
+import { FormResultFixedVar } from "../form-result-fixedvar";
+import { FormulaireDefinition } from "../form-definition";
+import { FormComputeRegimeUniforme } from "../form-compute-regime-uniforme";
+import { FormDefSection } from "../form-def-section";
+import { CalculatorResults } from "../../../results/calculator-results";
+import { FormDefParamToCalculate } from "../form-def-paramcalc";
+
+export class FormulaireRegimeUniforme extends FormulaireDefinition {
+    private _formFixedVar: FormDefFixedVar;
+
+    private _formParamCalc: FormDefParamToCalculate;
+
+    private _formSection: FormDefSection;
+
+    private _formCompute: FormComputeRegimeUniforme;
+
+    private _formResult: FormResultFixedVar;
+
+    constructor(paramService: ParamService, appSetupService: ApplicationSetupService) {
+        super(CalculatorType.RegimeUniforme, paramService, appSetupService)
+        this._formFixedVar = new FormDefFixedVar(this);
+        this._formParamCalc = new FormDefParamToCalculate(this);
+        this._formSection = new FormDefSection(this, appSetupService);
+        this._formResult = new FormResultFixedVar(this, true);
+        this._formCompute = new FormComputeRegimeUniforme(this, this._formSection, this._formResult);
+    }
+
+    protected initParse() {
+        this._formSection.initParse();
+        this._formParamCalc.initParse();
+    }
+
+    protected parseOptions(json: {}) {
+        this._formSection.parseOptions(json);
+        this._formParamCalc.parseOptions(json);
+    }
+
+    protected completeParse() {
+        this._formSection.completeParse();
+    }
+
+    /**
+     * gestion du clic sur les radios "paramètre fixé, à varier, à calculer"    
+     */
+    public onRadioClick(info: string) {
+        this._formParamCalc.onRadioClick(info);
+    }
+
+    public resetResults() {
+        this._formResult.resetResults();
+    }
+
+    public doCompute() {
+        this._formCompute.doCompute();
+    }
+
+    public get hasResults(): boolean {
+        return this._formResult.hasResults;
+    }
+
+    public get results(): CalculatorResults[] {
+        return this._formResult.results;
+    }
+}
diff --git a/src/app/formulaire/definition/concrete/form-section-parametree.ts b/src/app/formulaire/definition/concrete/form-section-parametree.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a599f05d88eaa43f243a0e3df9b4d540bcdcac51
--- /dev/null
+++ b/src/app/formulaire/definition/concrete/form-section-parametree.ts
@@ -0,0 +1,70 @@
+import { CalculatorType, acSection, ParamsEquation } from "jalhyd";
+
+import { FormResultSection } from "../form-result-section";
+import { ParamService } from "../../../services/param/param.service";
+import { ApplicationSetupService } from "../../../services/app-setup/app-setup.service";
+import { FormDefSection } from "../form-def-section";
+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 { CalculatorResults } from "../../../results/calculator-results";
+import { FormDefFixedVar } from "../form-def-fixedvar";
+
+export class FormulaireSectionParametree extends FormulaireDefinition {
+    private _formFixedVar: FormDefFixedVar;
+
+    private _formSection: FormDefSection;
+
+    private _formCompute: FormComputeSectionParametree;
+
+    private _formSectionResult: FormResultSection;
+
+    constructor(
+        paramService: ParamService,
+        appSetupService: ApplicationSetupService,
+        intlService: InternationalisationService
+    ) {
+        super(CalculatorType.SectionParametree, paramService, appSetupService);
+        this._formFixedVar = new FormDefFixedVar(this);
+        this._formSection = new FormDefSection(this, appSetupService);
+        this._formSectionResult = new FormResultSection(this, this._formSection);
+        this._formCompute = new FormComputeSectionParametree(this, this._formSection, this._formSectionResult, intlService);
+    }
+
+    protected initParse() {
+        this._formSection.initParse();
+    }
+
+    protected parseOptions(json: {}) {
+        this._formSection.parseOptions(json);
+    }
+
+    protected completeParse() {
+        this._formSection.completeParse();
+    }
+
+    /**
+     * gestion du clic sur les radios "paramètre fixé, à varier, à calculer"    
+     */
+    public onRadioClick(info: string) {
+        this._formFixedVar.onRadioClick(info);
+    }
+
+    public resetResults() {
+        this._formSectionResult.resetResults();
+    }
+
+    public doCompute() {
+        this._formCompute.doCompute();
+    }
+
+    public get hasResults(): boolean {
+        return this._formSectionResult.hasResults;
+    }
+
+    public get results(): CalculatorResults[] {
+        return this._formSectionResult.results;
+    }
+}
diff --git a/src/app/formulaire/definition/form-compute-cond-distri.ts b/src/app/formulaire/definition/form-compute-cond-distri.ts
new file mode 100644
index 0000000000000000000000000000000000000000..62099c01bb4b4fff4111a6e521c53e637bf5c4e8
--- /dev/null
+++ b/src/app/formulaire/definition/form-compute-cond-distri.ts
@@ -0,0 +1,22 @@
+import { ComputeNode, ParamsEquation, ConduiteDistribParams, ConduiteDistrib } from "jalhyd";
+
+import { FormComputeFixedVar } from "./form-compute-fixedvar";
+import { FormulaireDefinition } from "./form-definition";
+import { FormResultFixedVar } from "./form-result-fixedvar";
+
+export class FormComputeConduiteDistributrice extends FormComputeFixedVar {
+    constructor(formBase: FormulaireDefinition, formResult: FormResultFixedVar) {
+        super(formBase, formResult);
+    }
+
+    public getNubAndParameters(): [ComputeNode, ParamsEquation] {
+        let Q: number = this._formBase.getParameterValue("Q"); // débit Q
+        let D: number = this._formBase.getParameterValue("D"); // diamètre D
+        let J: number = this._formBase.getParameterValue("J"); // perte de charge J
+        let Lg: number = this._formBase.getParameterValue("Lg"); // Longueur de la conduite Lg
+        let Nu: number = this._formBase.getParameterValue("Nu"); // viscosité dynamique 
+        let prms = new ConduiteDistribParams(Q, D, J, Lg, Nu);
+        let nub = new ConduiteDistrib(prms);
+        return [nub, prms];
+    }
+}
diff --git a/src/app/formulaire/definition/form-compute-courbe-remous.ts b/src/app/formulaire/definition/form-compute-courbe-remous.ts
new file mode 100644
index 0000000000000000000000000000000000000000..94a2c1c422ef39a5eb94501f58181366219cb130
--- /dev/null
+++ b/src/app/formulaire/definition/form-compute-courbe-remous.ts
@@ -0,0 +1,81 @@
+import { acSection, ParamsEquation, ComputeNode, Result, MethodeResolution, CourbeRemousParams, CourbeRemous, ResultElement } from "jalhyd";
+
+import { SelectField } from "../select-field";
+import { RemousResults } from "../../results/remous-results";
+import { FormulaireDefinition } from "./form-definition";
+import { FormDefSection } from "./form-def-section";
+import { FormCompute } from "./form-compute";
+import { FormResultRemous } from "./form-result-remous";
+
+export class FormComputeCourbeRemous extends FormCompute {
+    constructor(formBase: FormulaireDefinition, private _formSection: FormDefSection, formResult: FormResultRemous) {
+        super(formBase, formResult);
+    }
+
+    protected getNubAndParameters(): [ComputeNode, ParamsEquation] {
+        return this._formSection.getSectionNubAndParameters(false);
+    }
+
+    private get remousResults(): RemousResults {
+        const f = this._formResult as FormResultRemous;
+        return f.remousResults;
+    }
+
+    protected compute() {
+        var np: [ComputeNode, ParamsEquation] = this.getNubAndParameters();
+
+        let sect: acSection = np[0] as acSection;
+        let prmSect: ParamsEquation = np[1];
+
+        let Yamont: number = this._formBase.getParameterValue("Yamont"); // tirant amont
+        let Yaval: number = this._formBase.getParameterValue("Yaval"); // tirant aval
+        let Dx: number = this._formBase.getParameterValue("Dx"); // pas de discrétisation
+        let Long: number = this._formBase.getParameterValue("Long"); // longueur du bief
+        let If: number = this._formBase.getParameterValue("If"); // pente du fond
+        let YB: number = this._formBase.getParameterValue("YB"); // hauteur de berge
+        let Yn: Result = sect.Calc("Yn"); // hauteur normale
+        let Yc: Result = sect.Calc("Yc"); // hauteur critique
+
+        this.remousResults.penteFond = If;
+
+        // méthode de résolution
+
+        let msf: SelectField = <SelectField>this._formBase.getFormulaireNodeById("select_resolution");
+        let smeth: string = msf.getValue().value;
+        let methRes: MethodeResolution;
+        if (smeth == "select_resolution_trap")
+            methRes = MethodeResolution.Trapezes;
+        else if (smeth == "select_resolution_rk4")
+            methRes = MethodeResolution.RungeKutta4;
+        else if (smeth == "select_resolution_euler")
+            methRes = MethodeResolution.EulerExplicite;
+        else
+            throw "GenericCalculatorComponent.doComputeRemous() : type de méthode de résolution '" + smeth + "' inconnu";
+
+        // paramètre supplémentaire à calculer
+
+        const extraSymbol: string = this._formBase.getSelectedValue("select_target");
+
+        // calcul
+
+        let prmCR: CourbeRemousParams = new CourbeRemousParams(sect, Yamont, Yaval, Long, Dx, methRes);
+        let cr = new CourbeRemous(prmCR);
+        let res: Result = cr.calculRemous(extraSymbol == "none" ? undefined : extraSymbol);
+
+        // affichage du graphe
+
+        this.remousResults.hauteurBerge = new ResultElement(YB);
+        this.remousResults.hauteurNormale = Yn.result;
+        this.remousResults.hauteurCritique = Yc.result;
+        if (extraSymbol != "none") {
+            this.remousResults.extraParamLabel = this._formBase.getSelectedLabel("select_target");
+            this.remousResults.extraGraph = ["Hs", "Hsc", "Yf", "Yt", "Yco"].indexOf(extraSymbol) == -1;
+        }
+        else
+            this.remousResults.extraGraph = false;
+
+        // résultats numériques
+
+        this.remousResults.addResult(res);
+    }
+}
diff --git a/src/app/formulaire/definition/form-compute-fixedvar.ts b/src/app/formulaire/definition/form-compute-fixedvar.ts
new file mode 100644
index 0000000000000000000000000000000000000000..03393744d7c7eb0475dde22f1484afce6ada9cc4
--- /dev/null
+++ b/src/app/formulaire/definition/form-compute-fixedvar.ts
@@ -0,0 +1,145 @@
+import { Nub, ParamsEquation, Result, ParamDomainValue, ComputeNode } from "jalhyd";
+
+import { FormCompute } from "./form-compute";
+import { NgParameter, ParamValueMode, ParamRadioConfig } from "../ngparam";
+import { FormResultFixedVar } from "./form-result-fixedvar";
+import { FixedVarResults, GraphType } from "../../results/fixed-var-results";
+import { FormulaireDefinition } from "./form-definition";
+
+export abstract class FormComputeFixedVar extends FormCompute {
+    constructor(formBase: FormulaireDefinition, formResult: FormResultFixedVar) {
+        super(formBase, formResult);
+    }
+
+    protected get formResult(): FormResultFixedVar {
+        return this._formResult as FormResultFixedVar;
+    }
+
+    private getVariatedParameter(): NgParameter {
+        return this._formBase.getDisplayedParamFromState(ParamRadioConfig.VAR);
+    }
+
+    private getComputedParameter(): NgParameter {
+        return this._formBase.getDisplayedParamFromState(ParamRadioConfig.CAL);
+    }
+
+    /**
+     * retourne un identifiant du paramètre dans le formulaire
+     * surchargé dans le cas des ouvrages //
+     */
+    protected getParameterRefid(p: NgParameter) {
+        return p.symbol;
+    }
+
+    /**
+     * lance le calcul d'un paramètre en déterminant une valeur initiale
+     */
+    private runNubCalc(nub: Nub, p: NgParameter, prec: number): Result {
+        let init: number;
+        switch (p.domain.domain) {
+            case ParamDomainValue.ANY:
+            case ParamDomainValue.POS_NULL:
+                init = 0;
+                break;
+
+            case ParamDomainValue.INTERVAL:
+                init = p.domain.minValue;
+                break;
+
+            case ParamDomainValue.NOT_NULL:
+            case ParamDomainValue.POS:
+                init = 1e-8;
+                break;
+        }
+
+        if (prec == undefined)
+            return nub.Calc(this.getParameterRefid(p), init);
+
+        return nub.Calc(this.getParameterRefid(p), init, prec);
+    }
+
+    /**
+     * fixe la valeur d'un paramètre de ComputeNode
+     * @param node ComputeNode contenant le paramètre
+     * @param p paramètre dont on fixe la valeur
+     * @param val valeur à fixer
+     */
+    protected setParameterValue(node: ComputeNode, p: NgParameter, val: number) {
+        node.getParameter(p.symbol).v = val;
+    }
+
+    protected compute() {
+        let nub: Nub;
+        let prms: ParamsEquation;
+        let np: [ComputeNode, ParamsEquation] = this.getNubAndParameters();
+        nub = np[0] as Nub;
+        prms = np[1];
+
+        if (this._formBase.hasParameter("Pr"))
+            var computePrec: number = this._formBase.getParameterValue("Pr"); // précision de calcul
+
+        let computedParam: NgParameter = this.getComputedParameter();
+
+        let varParam: NgParameter = this.getVariatedParameter();
+        if (varParam == undefined) {
+            // pas de paramètre à varier
+
+            let res: Result = this.runNubCalc(nub, computedParam, computePrec);
+            if (res.ok) {
+                this.formResult.addFixedResults();
+                this.formResult.addFixedResultElement(computedParam, res.result);
+            }
+            else {
+                this.formResult.addLogMessages(res.log);
+            }
+        }
+        else {
+            // il y a un paramètre à varier
+
+            this.formResult.addFixedResults();
+            this.formResult.setVariableParamHeaderFromParameter(varParam);
+            this.formResult.setVariableResultHeaderFromParameter(computedParam);
+
+            switch (varParam.valueMode) {
+                case ParamValueMode.MINMAX:
+                    this.formResult.graphType = GraphType.Scatter;
+
+                    let min: number = +varParam.minValue;
+                    let max: number = +varParam.maxValue;
+                    let step: number = +varParam.stepValue;
+
+                    for (let val = min; val <= max; val += step) {
+                        this.setParameterValue(nub, varParam, val);
+
+                        let res: Result = this.runNubCalc(nub, computedParam, computePrec);
+                        if (res.ok) {
+                            this.formResult.addVarResultElement(val, res.result);
+                        }
+                        else {
+                            this.formResult.addLogMessages(res.log);
+                        }
+                    }
+                    break;
+
+                case ParamValueMode.LISTE:
+                    this.formResult.graphType = GraphType.Histogram;
+
+                    for (let val of varParam.valueList) {
+                        this.setParameterValue(nub, varParam, val);
+
+                        let res: Result = this.runNubCalc(nub, computedParam, computePrec);
+                        if (res.ok) {
+                            this.formResult.addVarResultElement(val, res.result);
+                        }
+                        else {
+                            this.formResult.addLogMessages(res.log);
+                        }
+                    }
+
+                    break;
+            }
+
+            this.formResult.graphTitle = computedParam.symbol + " = f( " + varParam.symbol + " )";
+        }
+    }
+}
diff --git a/src/app/formulaire/definition/form-compute-lechapt-calmon.ts b/src/app/formulaire/definition/form-compute-lechapt-calmon.ts
new file mode 100644
index 0000000000000000000000000000000000000000..21f1e8b38a239f141ff5c3d7f097831d8a3cb484
--- /dev/null
+++ b/src/app/formulaire/definition/form-compute-lechapt-calmon.ts
@@ -0,0 +1,24 @@
+import { ComputeNode, ParamsEquation, LechaptCalmonParams, LechaptCalmon } from "jalhyd";
+
+import { FormComputeFixedVar } from "./form-compute-fixedvar";
+import { FormulaireDefinition } from "./form-definition";
+import { FormResultFixedVar } from "./form-result-fixedvar";
+
+export class FormComputeLechaptCalmon extends FormComputeFixedVar {
+    constructor(formBase: FormulaireDefinition, formResult: FormResultFixedVar) {
+        super(formBase, formResult);
+    }
+
+    public getNubAndParameters(): [ComputeNode, ParamsEquation] {
+        let Q: number = this._formBase.getParameterValue("Q"); // débit Q
+        let D: number = this._formBase.getParameterValue("D"); // diamètre D
+        let J: number = this._formBase.getParameterValue("J"); // perte de charge J
+        let Lg: number = this._formBase.getParameterValue("Lg"); // Longueur de la conduite Lg
+        let L: number = this._formBase.getParameterValue("L"); // paramètre de matériau 1
+        let M: number = this._formBase.getParameterValue("M"); // paramètre de matériau 2
+        let N: number = this._formBase.getParameterValue("N"); // paramètre de matériau 3
+        let prms = new LechaptCalmonParams(Q, D, J, Lg, L, M, N);
+        let nub = new LechaptCalmon(prms);
+        return [nub, prms];
+    }
+}
diff --git a/src/app/formulaire/definition/form-compute-pab-bassin-dim.ts b/src/app/formulaire/definition/form-compute-pab-bassin-dim.ts
new file mode 100644
index 0000000000000000000000000000000000000000..26681298825a02b69c5af14a155b245a62c4a4ec
--- /dev/null
+++ b/src/app/formulaire/definition/form-compute-pab-bassin-dim.ts
@@ -0,0 +1,21 @@
+import { ComputeNode, ParamsEquation, PabDimensionParams, PabDimension } from "jalhyd";
+
+import { FormComputeFixedVar } from "./form-compute-fixedvar";
+import { FormulaireDefinition } from "./form-definition";
+import { FormResultFixedVar } from "./form-result-fixedvar";
+
+export class FormComputePasseBassinDimensions extends FormComputeFixedVar {
+    constructor(formBase: FormulaireDefinition, formResult: FormResultFixedVar) {
+        super(formBase, formResult);
+    }
+
+    public getNubAndParameters(): [ComputeNode, ParamsEquation] {
+        let L: number = this._formBase.getParameterValue("L"); // longueur L
+        let W: number = this._formBase.getParameterValue("W"); // largeur W
+        let Y: number = this._formBase.getParameterValue("Y"); // tirant d'eau Y
+        let V: number = this._formBase.getParameterValue("V"); // volume V
+        let prms = new PabDimensionParams(L, W, Y, V);
+        let nub = new PabDimension(prms); // pour initialiser la calculabilité des paramètres
+        return [nub, prms];
+    }
+}
\ No newline at end of file
diff --git a/src/app/formulaire/definition/form-compute-pab-bassin-puissance.ts b/src/app/formulaire/definition/form-compute-pab-bassin-puissance.ts
new file mode 100644
index 0000000000000000000000000000000000000000..829300d29b8ac1cf25344cd367844e76ad6a88dc
--- /dev/null
+++ b/src/app/formulaire/definition/form-compute-pab-bassin-puissance.ts
@@ -0,0 +1,21 @@
+import { ComputeNode, ParamsEquation, PabPuissanceParams, PabPuissance } from "jalhyd";
+
+import { FormComputeFixedVar } from "./form-compute-fixedvar";
+import { FormulaireDefinition } from "./form-definition";
+import { FormResultFixedVar } from "./form-result-fixedvar";
+
+export class FormComputePasseBassinPuissance extends FormComputeFixedVar {
+    constructor(formBase: FormulaireDefinition, formResult: FormResultFixedVar) {
+        super(formBase, formResult);
+    }
+
+    public getNubAndParameters(): [ComputeNode, ParamsEquation] {
+        let DH: number = this._formBase.getParameterValue("DH");  // Chute entre bassins
+        let Q: number = this._formBase.getParameterValue("Q"); // Débit 
+        let V: number = this._formBase.getParameterValue("V"); // volume V
+        let Pv: number = this._formBase.getParameterValue("Pv"); // puissance dissipée
+        let prms = new PabPuissanceParams(DH, Q, V, Pv);
+        let nub = new PabPuissance(prms); // pour initialiser la calculabilité des paramètres
+        return [nub, prms];
+    }
+}
\ No newline at end of file
diff --git a/src/app/formulaire/definition/form-compute-parallel-structures.ts b/src/app/formulaire/definition/form-compute-parallel-structures.ts
new file mode 100644
index 0000000000000000000000000000000000000000..572bc88a1b9e8523dc53ae08d95751a691456882
--- /dev/null
+++ b/src/app/formulaire/definition/form-compute-parallel-structures.ts
@@ -0,0 +1,110 @@
+import {
+    ComputeNode, ParamsEquation, ParallelStructure, ParallelStructureParams, CalculatorType, StructureParams,
+    Structure, CreateStructure, StructureType, ComputeNodeType, LoiDebit
+} from "jalhyd";
+
+import { FormComputeFixedVar } from "./form-compute-fixedvar";
+import { FormResultFixedVar } from "./form-result-fixedvar";
+import { FormulaireDefinition } from "./form-definition";
+import { SelectField } from "../select-field";
+import { FormulaireElement } from "../formulaire-element";
+import { NgParameter } from "../ngparam";
+import { FormulaireNode } from "../formulaire-node";
+import { FieldSet } from "../fieldset";
+import { FieldsetContainer } from "../fieldset-container";
+import { FormDefParallelStructures } from "./form-def-parallel-structures";
+
+export class FormComputeParallelStructures extends FormComputeFixedVar {
+    private _formParallelStruct: FormDefParallelStructures;
+
+    constructor(formBase: FormulaireDefinition, formParallelStruct: FormDefParallelStructures, formResult: FormResultFixedVar) {
+        super(formBase, formResult);
+        this._formParallelStruct = formParallelStruct;
+    }
+
+    /**
+     * @return dans le cas d'un paramètre d'ouvrage, le FieldSet parent d'un paramètre,
+     * le FieldsetContainer parent du FieldSet
+     * ainsi que l'indice du FieldSet parent dans le FieldsetContainer
+     */
+    private structureParents(p: NgParameter): [FieldsetContainer, FieldSet, number] {
+        const parent1: FormulaireNode = p.getDirectParent(this._formBase);
+        if (parent1 !== undefined)
+            if (parent1 instanceof FieldSet) {
+                const parent2 = parent1.getDirectParent(this._formBase);
+                if (parent2 instanceof FieldsetContainer) {
+                    const fsIndex = parent1.indexAsKid(this._formBase);
+                    return [parent2, parent1, fsIndex];
+                }
+            }
+
+        return [undefined, undefined, -1];
+    }
+
+    /**
+     * construit un identifiant de type "n.X" avec "n" l'index de l'ouvrage auquel appartient le paramètre et "X" son symbole
+     */
+    protected getParameterRefid(p: NgParameter) {
+        const [fsc, fs, i] = this.structureParents(p);
+        if (i == -1)
+            return super.getParameterRefid(p);
+
+        return `${i}.${p.symbol}`;
+    }
+
+    protected setParameterValue(node: ComputeNode, p: NgParameter, val: number) {
+        const [fsc, fs, i] = this.structureParents(p);
+        if (i == -1)
+            super.setParameterValue(node, p, val); // paramètre "normal" (pas un paramètre d'ouvrage)
+        else {
+            const n: ParallelStructure = node as ParallelStructure;
+            const prm = n.structures[i].getParameter(p.symbol);
+            prm.v = val;
+        }
+    }
+
+    public getNubAndParameters(): [ComputeNode, ParamsEquation] {
+        // paramètres communs
+
+        const Q: number = this._formBase.getParameterValue("Q"); // Débit 
+        const Z1: number = this._formBase.getParameterValue("Z1"); // Cote de l'eau amont (m)
+        const Z2: number = this._formBase.getParameterValue("Z2"); // Cote de l'eau aval (m)
+        const prms = new ParallelStructureParams(Q, Z1, Z2);
+
+        const nub: ParallelStructure = new ParallelStructure(prms);
+
+        // ouvrages
+
+        for (const fs of this._formBase.allFieldsets)
+            if (fs.calculatorType == CalculatorType.Structure) {
+                const ts: StructureType = this._formParallelStruct.getStructureType(fs);
+                const ld: LoiDebit = this._formParallelStruct.getLoiDebit(fs);
+
+                // cote de radier
+                const ZDV = fs.getNodeParameterValue("ZDV");
+
+                // ouverture de vanne
+                try {
+                    var W = fs.getNodeParameterValue("W");
+                } catch (e) {
+                }
+
+                // création de l'ouvrage
+                const ouvr = CreateStructure(ts, ld);
+
+                // affectation des valeurs (en prenant garde de ne pas écraser les valeurs par défaut dans le cas d'un paramètre à calculer)
+                if (Z1 != undefined)
+                    ouvr.prms.Z1.v = Z1;
+                if (Z2 != undefined)
+                    ouvr.prms.Z2.v = Z2;
+                if (ZDV != undefined)
+                    ouvr.prms.ZDV.v = ZDV;
+                if (W != undefined)
+                    ouvr.prms.W.v = W;
+
+                nub.addStructure(ouvr);
+            }
+
+        return [nub, prms];
+    }
+}
diff --git a/src/app/formulaire/definition/form-compute-regime-uniforme.ts b/src/app/formulaire/definition/form-compute-regime-uniforme.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2f004b30385d9e959968984adfb09167fc9c977e
--- /dev/null
+++ b/src/app/formulaire/definition/form-compute-regime-uniforme.ts
@@ -0,0 +1,17 @@
+import { ComputeNode, ParamsEquation, RegimeUniforme, acSection } from "jalhyd";
+
+import { FormComputeFixedVar } from "./form-compute-fixedvar";
+import { FormulaireDefinition } from "./form-definition";
+import { FormResultFixedVar } from "./form-result-fixedvar";
+import { FormDefSection } from "./form-def-section";
+
+export class FormComputeRegimeUniforme extends FormComputeFixedVar {
+    constructor(formBase: FormulaireDefinition, private _formSection: FormDefSection, formResult: FormResultFixedVar) {
+        super(formBase, formResult);
+    }
+
+    protected getNubAndParameters(): [ComputeNode, ParamsEquation] {
+        const snp: [acSection, ParamsEquation] = this._formSection.getSectionNubAndParameters();
+        return [new RegimeUniforme(snp[0]), snp[1]];
+    }
+}
\ No newline at end of file
diff --git a/src/app/formulaire/definition/form-compute-section-parametree.ts b/src/app/formulaire/definition/form-compute-section-parametree.ts
new file mode 100644
index 0000000000000000000000000000000000000000..22469eae923754291e528df0a808c7f0af881130
--- /dev/null
+++ b/src/app/formulaire/definition/form-compute-section-parametree.ts
@@ -0,0 +1,163 @@
+import { FormCompute } from "./form-compute";
+import { NgParameter } from "../ngparam";
+import { acSection, ParamsEquation, ComputeNodeType, ComputeNode } from "jalhyd";
+import { FormResult } from "./form-result";
+import { Form } from "@angular/forms";
+import { FormDefSection } from "./form-def-section";
+import { FormResultSection } from "./form-result-section";
+import { InputField } from "../input-field";
+import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { FixedVarResults } from "../../results/fixed-var-results";
+import { SectionResults } from "../../results/section-results";
+import { FormulaireDefinition } from "./form-definition";
+
+export class FormComputeSectionParametree extends FormCompute {
+    constructor(formBase: FormulaireDefinition, private _formSection: FormDefSection, formResult: FormResult,
+        private intlService: InternationalisationService) {
+        super(formBase, formResult);
+    }
+
+    private get _formSectionResult(): FormResultSection {
+        return this._formResult as FormResultSection;
+    }
+
+    private get _fixVarResults(): FixedVarResults {
+        return this._formSectionResult.fixedVarResults;
+    }
+
+    private get _sectionResults(): SectionResults {
+        return this._formSectionResult.sectionResults;
+    }
+
+    protected getNubAndParameters(): [ComputeNode, ParamsEquation] {
+        return this._formSection.getSectionNubAndParameters();
+    }
+
+    /**
+     * calcul de la section paramétrée dans le cas d'un paramètre à varier
+     * @param varParam paramètre à varier
+     */
+    private doComputeSectionVar(varParam: NgParameter) {
+        let computePrec: number = this._formBase.getParameterValue("Pr"); // précision de calcul
+        let nDigits = -Math.log10(computePrec);
+
+        this._formSectionResult.addSectionFixedResults(false);
+
+        // paramètre à calculer en fonction du paramètre à varier
+        let computedParam = this._formSection.getSectionComputedParam();
+
+        this._fixVarResults.setVariableParamHeaderFromParameter(varParam, false);
+        this._fixVarResults.setVariableResultHeader(computedParam["label"]);
+
+        var np: [ComputeNode, ParamsEquation] = this.getNubAndParameters();
+        let sect: acSection = np[0] as acSection;
+        let prms: ParamsEquation = np[1];
+
+        this._sectionResults.section = sect;
+
+        let min: number = +varParam.minValue;
+        let max: number = +varParam.maxValue;
+        let step: number = +varParam.stepValue;
+
+        let yField: InputField = <InputField>this._formBase.getFormulaireNodeById("Y");
+        let Y: number = yField.getValue();
+
+        let compSymbol = computedParam["symbol"];
+        for (let val = min; val <= max; val += step) {
+            prms[varParam.symbol].v = val;
+
+            let res = sect.Calc(compSymbol, Y);
+            this._fixVarResults.addVarResultElement(val, res.result);
+        }
+
+        this._fixVarResults.graphTitle = computedParam.symbol + " = f( " + varParam.symbol + " )";
+    }
+
+    protected compute() {
+        let varParam = this._formSection.getSectionVariatedParameter();
+        if (varParam != undefined) {
+            this.doComputeSectionVar(varParam);
+            return;
+        }
+
+        var np: [ComputeNode, ParamsEquation] = this.getNubAndParameters();
+
+        let sect: acSection = np[0] as acSection;
+        let prms: ParamsEquation = np[1];
+
+        this._sectionResults.section = sect;
+
+        let computePrec: number = this._formBase.getParameterValue("Pr"); // précision de calcul
+        let nDigits = -Math.log10(computePrec);
+
+        let Y = prms.map.Y.v; // tirant d'eau original (doit être fourni à acSection.Calc() sous peine d'être modifié par les appels successifs car c'est en même temps un paramètre et une variable temporaire)
+
+        // charge spécifique
+        let Hs = sect.Calc("Hs", Y);
+        this._formSectionResult.addSectionFixedResult(Hs.result, this.intlService.localizeText("INFO_GRANDEUR_HS"), "Hs");
+
+        // charge critique
+        let Hsc = sect.Calc("Hsc", Y);
+        this._formSectionResult.addSectionFixedResult(Hsc.result, this.intlService.localizeText("INFO_GRANDEUR_HSC"), "Hsc");
+
+        // largeur au miroir
+        let B = sect.Calc("B", Y);
+        this._formSectionResult.addSectionFixedResult(B.result, this.intlService.localizeText("INFO_GRANDEUR_B"));
+
+        // périmètre hydraulique
+        let P = sect.Calc("P", Y);
+        this._formSectionResult.addSectionFixedResult(P.result, this.intlService.localizeText("INFO_GRANDEUR_P"));
+
+        // surface hydraulique
+        let S = sect.Calc("S", Y);
+        this._formSectionResult.addSectionFixedResult(S.result, this.intlService.localizeText("INFO_GRANDEUR_S"));
+
+        // rayon hydraulique
+        let R = sect.Calc("R", Y);
+        this._formSectionResult.addSectionFixedResult(R.result, this.intlService.localizeText("INFO_GRANDEUR_R"));
+
+        // vitesse moyenne
+        let V = sect.Calc("V", Y);
+        this._formSectionResult.addSectionFixedResult(V.result, this.intlService.localizeText("INFO_GRANDEUR_V"));
+
+        // nombre de Froude
+        let Fr = sect.Calc("Fr", Y);
+        this._formSectionResult.addSectionFixedResult(Fr.result, this.intlService.localizeText("INFO_GRANDEUR_FR"), );
+
+        // tirant d'eau critique
+        let Yc = sect.Calc("Yc", Y);
+        this._formSectionResult.addSectionFixedResult(Yc.result, this.intlService.localizeText("INFO_GRANDEUR_YC"), "Yc");
+
+        // tirant d'eau normal
+        let Yn = sect.Calc("Yn", Y);
+        this._formSectionResult.addSectionFixedResult(Yn.result, this.intlService.localizeText("INFO_GRANDEUR_YN"), "Yn");
+
+        // tirant d'eau fluvial
+        let Yf = sect.Calc("Yf", Y);
+        this._formSectionResult.addSectionFixedResult(Yf.result, this.intlService.localizeText("INFO_GRANDEUR_YF"), "Yf");
+
+        // tirant d'eau torrentiel
+        let Yt = sect.Calc("Yt", Y);
+        this._formSectionResult.addSectionFixedResult(Yt.result, this.intlService.localizeText("INFO_GRANDEUR_YT"), "Yt");
+
+        // tirant d'eau conjugué
+        let Yco = sect.Calc("Yco", Y);
+        this._formSectionResult.addSectionFixedResult(Yco.result, this.intlService.localizeText("INFO_GRANDEUR_YCO"), "Yco");
+
+        // perte de charge
+        let J = sect.Calc("J", Y);
+        this._formSectionResult.addSectionFixedResult(J.result, this.intlService.localizeText("INFO_GRANDEUR_J"));
+
+        // Variation linéaire de l'énergie spécifique
+        let IJ = sect.Calc("I-J", Y);
+        this._formSectionResult.addSectionFixedResult(IJ.result, this.intlService.localizeText("INFO_GRANDEUR_I-J"));
+
+        // impulsion hydraulique
+        let Imp = sect.Calc("Imp", Y);
+        this._formSectionResult.addSectionFixedResult(Imp.result, this.intlService.localizeText("INFO_GRANDEUR_IMP"));
+
+        // contrainte de cisaillement
+        let Tau0 = sect.Calc("Tau0", Y);
+        this._formSectionResult.addSectionFixedResult(Tau0.result, this.intlService.localizeText("INFO_GRANDEUR_TAU0"));
+    }
+}
diff --git a/src/app/formulaire/definition/form-compute.ts b/src/app/formulaire/definition/form-compute.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ae5ca83e53650e81c378a98eee8528410d685359
--- /dev/null
+++ b/src/app/formulaire/definition/form-compute.ts
@@ -0,0 +1,26 @@
+import { FormResult } from "./form-result";
+import { ParamsEquation, ComputeNode } from "jalhyd";
+import { FormulaireDefinition } from "./form-definition";
+
+export abstract class FormCompute {
+    constructor(protected _formBase: FormulaireDefinition, protected _formResult: FormResult) {
+    }
+
+    protected abstract getNubAndParameters(): [ComputeNode, ParamsEquation];
+
+    protected abstract compute();
+
+    protected get formResult(): FormResult {
+        return this._formResult;
+    }
+
+    public doCompute() {
+        this._formResult.resetResults();
+
+        this.compute();
+
+        this._formBase.notifyObservers({
+            "action": "resultsUpdated",
+        }, this._formBase);
+    }
+}
diff --git a/src/app/formulaire/definition/form-def-fixedvar.ts b/src/app/formulaire/definition/form-def-fixedvar.ts
new file mode 100644
index 0000000000000000000000000000000000000000..75e0c71aaab516b2b2a9fe57754e65eac9cf84a3
--- /dev/null
+++ b/src/app/formulaire/definition/form-def-fixedvar.ts
@@ -0,0 +1,96 @@
+import { ParamRadioConfig, NgParameter } from "../ngparam";
+import { FormulaireDefinition } from "./form-definition";
+import { FormulaireElement } from "../formulaire-element";
+import { InputField } from "../input-field";
+import { CheckField } from "../check-field";
+
+/**
+ * gestion des formulaires avec "paramètre fixé" et "paramètre à varier"
+ */
+export class FormDefFixedVar {
+    protected _formBase: FormulaireDefinition;
+
+    constructor(base: FormulaireDefinition) {
+        this._formBase = base;
+    }
+
+    /**
+     * remet les radios de tous les paramètres à FIX sauf "me" et ceux (celui) à l'état "except"
+     */
+    protected resetOtherRadio(me: NgParameter, except: ParamRadioConfig) {
+        for (const p of this._formBase.allFormElements) {
+            if (p instanceof NgParameter)
+                if (p != me && p.radioState != except && p.radioConfig != ParamRadioConfig.FIX)
+                    p.radioState = ParamRadioConfig.FIX;
+        }
+    }
+
+    protected processRadioStateChange(sourceParam: NgParameter, oldState: ParamRadioConfig, newState: ParamRadioConfig) {
+        switch (oldState) {
+            case ParamRadioConfig.FIX:
+                switch (newState) {
+                    case ParamRadioConfig.VAR:
+                        this.resetOtherRadio(sourceParam, ParamRadioConfig.CAL);
+                        break;
+
+                    case ParamRadioConfig.CAL:
+                        this.resetOtherRadio(sourceParam, ParamRadioConfig.VAR);
+                        break;
+                }
+                break;
+
+            case ParamRadioConfig.VAR:
+                switch (newState) {
+                    case ParamRadioConfig.CAL:
+                        this.resetOtherRadio(sourceParam, ParamRadioConfig.VAR);
+                        break;
+                }
+                break;
+        }
+    }
+
+    /**
+     * modifie les boutons radio "fix", "var", "cal" de tous les paramètres
+     * en fonction de la modification de l'état d'un des paramètres
+     * @param uid id numérique unique du paramètre source
+     * @param option nouvel état "fix", "var" ou "cal" du paramètre source
+     */
+    protected resetRadiosAndResults(sourceParam: NgParameter, option: string) {
+        const oldState: ParamRadioConfig = sourceParam.radioState;
+        const newState: ParamRadioConfig = ParamRadioConfig[option.toUpperCase()];
+
+        this.processRadioStateChange(sourceParam, oldState, newState);
+
+        sourceParam.radioState = newState;
+
+        // on vérifie qu'il y a au moins un paramètre "à calculer" et sinon, on prend le 1er qui est à "fixé"
+        if (this._formBase.getDisplayedParamFromState(ParamRadioConfig.CAL) == undefined) {
+            let newCal: NgParameter = undefined;
+
+            for (const p of this._formBase.allFormElements) {
+                if (p instanceof NgParameter)
+                    if (p.radioConfig == ParamRadioConfig.CAL && p.radioState == ParamRadioConfig.FIX && p != sourceParam) {
+                        newCal = p;
+                        break;
+                    }
+                if (newCal != undefined)
+                    break;
+            }
+
+            if (newCal != undefined)
+                newCal.radioState = ParamRadioConfig.CAL;
+        }
+
+        this._formBase.reset();
+    }
+
+    /**
+     * gestion des événements clic sur les radios
+     */
+    public onRadioClick(info: any) {
+        const param: NgParameter = info.param; // paramètre source de l'événement radio
+        const option: string = info.option; // nouvel état (radio)
+
+        this.resetRadiosAndResults(param, option);
+    }
+}
diff --git a/src/app/formulaire/definition/form-def-parallel-structures.ts b/src/app/formulaire/definition/form-def-parallel-structures.ts
new file mode 100644
index 0000000000000000000000000000000000000000..75a5aa89c5a5822c6d4f918a8030ab84d7446ab0
--- /dev/null
+++ b/src/app/formulaire/definition/form-def-parallel-structures.ts
@@ -0,0 +1,66 @@
+import { CalculatorType, StructureType, LoiDebit } from "jalhyd";
+
+import { FieldSet } from "../fieldset";
+
+/**
+ * gestion des formulaires "ouvrages parallèles"
+ */
+export class FormDefParallelStructures {
+    /**
+     * dictionnaire des types d'ouvrages indexés par les valeurs de liste déroulante
+     * la clé est la chaîne utilisée dans le fichier de configuration de la calculette pour les valeurs de la liste déroulante
+     */
+    private static tsMap = {
+        "vanne_rect": StructureType.VanneRectangulaire,
+        "seuil_rect": StructureType.SeuilRectangulaire
+    }
+
+    /**
+     * dictionnaire des LoiDebit indexés par les valeurs de liste déroulante
+     * la clé est la chaîne utilisée dans le fichier de configuration de la calculette pour les valeurs de la liste déroulante
+     */
+    private static ldMap = {
+        "cem88d": LoiDebit.Cem88d,
+        "cem88v": LoiDebit.Cem88d,
+        "cunge80": LoiDebit.Cunge80,
+        "seuildenoye": LoiDebit.WeirFree,
+        "vannenoye": LoiDebit.OrificeSubmerged,
+        "vannedenoye": LoiDebit.OrificeFree
+    }
+
+    /**
+     * @return type d'ouvrage courant du FieldSet donné
+     */
+    public getStructureType(fs: FieldSet): StructureType {
+        if (fs.calculatorType !== CalculatorType.Structure)
+            throw new Error(`FormDefParallelStructures.getStructureType() : le FieldSet n'est pas du type Structure`);
+
+        let structType: string = fs.getSelectedValue("select_ouvrage");
+        if (structType == undefined)
+            throw new Error(`FormDefParallelStructures.getStructureType() : aucun ouvrage trouvé dans le FieldSet`);
+
+        const res = FormDefParallelStructures.tsMap[structType];
+        if (res == undefined)
+            throw new Error(`FormDefParallelStructures.getStructureType() : type d'ouvrage ${StructureType[structType]} non pris en charge`);
+        return res;
+    }
+
+    /**
+     * @return type d'ouvrage courant du FieldSet donné
+     */
+    public getLoiDebit(fs: FieldSet): LoiDebit {
+        if (fs.calculatorType !== CalculatorType.Structure)
+            throw new Error(`FormDefParallelStructures.getLoiDebit() : le FieldSet n'est pas du type Structure`);
+
+        let loiDebit: string = fs.getSelectedValue("select_loidebit1");
+        if (loiDebit == undefined)
+            loiDebit = fs.getSelectedValue("select_loidebit2");
+        if (loiDebit == undefined)
+            throw new Error(`FormDefParallelStructures.getStructureType() : aucune loi de débit trouvée dans le FieldSet`);
+
+        const res = FormDefParallelStructures.ldMap[loiDebit];
+        if (res == undefined)
+            throw new Error(`FormDefParallelStructures.getStructureType() : loi de débit ${LoiDebit[loiDebit]} non prise en charge`);
+        return res;
+    }
+}
diff --git a/src/app/formulaire/definition/form-def-paramcalc.ts b/src/app/formulaire/definition/form-def-paramcalc.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1ff777b1d9c1e7589f6567be531c5c01d4888263
--- /dev/null
+++ b/src/app/formulaire/definition/form-def-paramcalc.ts
@@ -0,0 +1,58 @@
+import { ParamRadioConfig, NgParameter } from "../ngparam";
+import { FormulaireDefinition } from "./form-definition";
+import { FormDefFixedVar } from "./form-def-fixedvar";
+
+/** 
+ * gestion des formulaires avec "paramètre à calculer" (conduite distributrice, Lechapt-Calmon, régime uniforme, passes à bassin)
+ */
+export class FormDefParamToCalculate extends FormDefFixedVar {
+    /**
+     * symbole du paramètre à calculer par défaut (cf config "idCal")
+     */
+    private _defaultCalculatedParam: string;
+
+    constructor(base: FormulaireDefinition) {
+        super(base);
+    }
+
+    public initParse() {
+        this._defaultCalculatedParam = undefined;
+    }
+
+    public parseOptions(json: {}) {
+        // id du paramètre à calculer par défaut
+
+        this._defaultCalculatedParam = json["idCal"];
+        if (this._defaultCalculatedParam != undefined) {
+            let p = this._formBase.getParamFromSymbol(this._defaultCalculatedParam);
+            p.isDefault = true;
+            p.radioState = ParamRadioConfig.CAL;
+        }
+    }
+
+    /**
+     * met le paramètre par défaut à CAL
+     */
+    private setDefault() {
+        let defaultParamCal = this._formBase.getParamFromSymbol(this._defaultCalculatedParam);
+        defaultParamCal.radioState = ParamRadioConfig.CAL;
+    }
+
+    protected processRadioStateChange(sourceParam: NgParameter, oldState: ParamRadioConfig, newState: ParamRadioConfig) {
+        super.processRadioStateChange(sourceParam, oldState, newState);
+
+        switch (oldState) {
+            case ParamRadioConfig.CAL:
+                switch (newState) {
+                    case ParamRadioConfig.FIX:
+                        this.setDefault();
+                        break;
+
+                    case ParamRadioConfig.VAR:
+                        super.resetOtherRadio(sourceParam, ParamRadioConfig.CAL);
+                        this.setDefault();
+                        break;
+                }
+        }
+    }
+}
diff --git a/src/app/formulaire/definition/form-def-section.ts b/src/app/formulaire/definition/form-def-section.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9a3a4950328d04062b5f500cb8a11faaf03d2930
--- /dev/null
+++ b/src/app/formulaire/definition/form-def-section.ts
@@ -0,0 +1,164 @@
+import {
+    ComputeNodeType, ParamsSectionTrapez, cSnTrapez, ParamsSectionRectang, cSnRectang,
+    ParamsSectionCirc, cSnCirc, ParamsSectionPuiss, cSnPuiss, acSection, ParamsEquation
+} from "jalhyd";
+
+import { SelectField } from "../select-field";
+import { Field } from "../field";
+import { IObservable, Observer } from "../../services/observer";
+import { NgParameter, ParamRadioConfig } from "../ngparam";
+import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
+import { FormulaireDefinition } from "./form-definition";
+
+export class FormDefSection implements Observer {
+    /**
+     * id du SelectField configurant le type de section
+     */
+    private _sectionSelectFieldId: string;
+
+    /**
+     * Type de noeud de calcul actuel de la section (si la calculette est basée sur une section et qu'un menu permet 
+     * de sélectionner le type de section)
+     */
+    private _sectionNodeType: ComputeNodeType = undefined;
+
+    private _formBase: FormulaireDefinition;
+
+    constructor(base: FormulaireDefinition, private appSetupService: ApplicationSetupService) {
+        this._formBase = base;
+    }
+
+    private get hasSectionNodeTypeSelect(): boolean {
+        return this._sectionSelectFieldId != undefined;
+    }
+
+    private updateSectionNodeType() {
+        const nt = this.getNodeTypeFromSelectField();
+        if (this._sectionNodeType !== nt) {
+            this._sectionNodeType = nt;
+            this._formBase.reset();
+        }
+    }
+
+    public getSectionVariatedParameter(): NgParameter {
+        return this._formBase.getDisplayedParamFromState(ParamRadioConfig.VAR);
+    }
+
+    public getSectionNubAndParameters(getY: boolean = true): [acSection, ParamsEquation] {
+        // bief
+        let Ks = this._formBase.getParameterValue("Ks"); // Strickler
+        let If: number = this._formBase.getParameterValue("If"); // Pente du fond
+        let YB: number = this._formBase.getParameterValue("YB"); // Hauteur de berge
+
+        // caractéristiques hydro
+        let Q: number = this._formBase.getParameterValue("Q"); // débit Q
+        if (getY)
+            var Y: number = this._formBase.getParameterValue("Y"); // tirant d'eau
+        else
+            Y = undefined;
+
+        let Prec = this._formBase.getParameterValue("Pr"); // précision calcul/affichage
+
+        // ??
+        // let YCL: number = f.getParameterValue("YCL"); // Condition limite en cote à l'amont ou à l'aval
+        // let Dx: number = f.getParameterValue("Dx"); // Pas d'espace (positif en partant de l'aval, négatif en partant de l'amont)
+        // let Long: number = f.getParameterValue("Long"); // Longueur du bief
+
+        switch (this._sectionNodeType) {
+            case ComputeNodeType.SectionTrapeze:
+                {
+                    let LargeurFond = this._formBase.getParameterValue("LargeurFond"); // Largeur au fond
+                    let Fruit = this._formBase.getParameterValue("Fruit"); // Fruit des berges
+                    let prms = new ParamsSectionTrapez(LargeurFond, Fruit, Y, Ks, Q, If, Prec, YB);
+                    let cn = new cSnTrapez(prms);
+                    cn.newtonMaxIter = this.appSetupService.newtonMaxIter;
+                    return [cn, prms];
+                }
+
+            case ComputeNodeType.SectionRectangle:
+                {
+                    let LargeurFond = this._formBase.getParameterValue("LargeurBerge"); // Largeur au fond
+                    let prms = new ParamsSectionRectang(Y, LargeurFond, Ks, Q, If, Prec, YB);
+                    let cn = new cSnRectang(prms);
+                    cn.newtonMaxIter = this.appSetupService.newtonMaxIter;
+                    return [cn, prms];
+                }
+
+            case ComputeNodeType.SectionCercle:
+                {
+                    let D = this._formBase.getParameterValue("D"); // Largeur au fond
+                    let prms = new ParamsSectionCirc(D, Y, Ks, Q, If, Prec, YB);
+                    let cn = new cSnCirc(prms);
+                    cn.newtonMaxIter = this.appSetupService.newtonMaxIter;
+                    return [cn, prms];
+                }
+
+            case ComputeNodeType.SectionPuissance:
+                {
+                    let k = this._formBase.getParameterValue("k"); // coefficient
+                    let LargeurBerge = this._formBase.getParameterValue("LargeurBerge"); // Largeur au niveau des berges
+                    let prms = new ParamsSectionPuiss(k, Y, LargeurBerge, Ks, Q, If, Prec, YB);
+                    let cn = new cSnPuiss(prms);
+                    cn.newtonMaxIter = this.appSetupService.newtonMaxIter;
+                    return [cn, prms];
+                }
+
+            default:
+                throw new Error(`FormWithSection.getSectionNubAndParameters() : valeur de ComputeNodeType ${this._sectionNodeType} non prise en charge`);
+        }
+    }
+
+    public getSectionComputedParam(): { symbol: string, label: string } {
+        const symbol = this._formBase.getSelectedValue("select_target");
+        const label = this._formBase.getSelectedLabel("select_target");
+        return { symbol, label };
+    }
+
+    private getNodeTypeFromSelectField(): ComputeNodeType {
+        const sect = this._formBase.getSelectedValue(this._sectionSelectFieldId);
+        switch (sect) {
+            case "trapez":
+                return ComputeNodeType.SectionTrapeze;
+
+            case "rect":
+                return ComputeNodeType.SectionRectangle;
+
+            case "circ":
+                return ComputeNodeType.SectionCercle;
+
+            case "puiss":
+                return ComputeNodeType.SectionPuissance;
+
+            default:
+                throw new Error(`getComputeNodeTypeFromSection() : section ${sect} non pris en charge`);
+        }
+    }
+
+    public initParse() {
+    }
+
+    public parseOptions(json: {}) {
+        // id du SelectField configurant le type de section
+        this._sectionSelectFieldId = this._formBase.getOption(json, "sectionSelectId");
+    }
+
+    public completeParse() {
+        // si le formulaire a un menu pour le type de section, on s'abonne à la valeur du champ correspondant
+        if (this.hasSectionNodeTypeSelect) {
+            const f: Field = this._formBase.getFieldById(this._sectionSelectFieldId);
+            if (f == undefined || !(f instanceof SelectField))
+                throw new Error(`le champ ${this._sectionSelectFieldId} n'est pas du type SelectField`);
+            const sectTypeField: SelectField = f as SelectField;
+            sectTypeField.addObserver(this);
+            this.updateSectionNodeType()
+        }
+    }
+
+    // interface Observer 
+
+    update(sender: any, data: any): void {
+        if (sender instanceof SelectField) {
+            this.updateSectionNodeType();
+        }
+    }
+}
diff --git a/src/app/formulaire/definition/form-definition.ts b/src/app/formulaire/definition/form-definition.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e838c009d49902969e30e23b449e204e89f515f8
--- /dev/null
+++ b/src/app/formulaire/definition/form-definition.ts
@@ -0,0 +1,319 @@
+import { CalculatorType, ComputeNodeType } from "jalhyd";
+import { FormulaireElement } from "../formulaire-element";
+import { NgParameter, ParamRadioConfig } from "../ngparam";
+import { Field } from "../field";
+import { StringMap } from "../../stringmap";
+import { FormulaireNode } from "../formulaire-node";
+import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
+import { ParamService } from "../../services/param/param.service";
+import { FieldSet } from "../fieldset";
+import { FieldsetContainer } from "../fieldset-container";
+import { SelectField } from "../select-field";
+import { DeepFieldsetIterator } from "../form-iterator/deep-fieldset-iterator";
+import { DeepFormulaireElementIterator } from "../form-iterator/deep-element-iterator";
+import { TopFormulaireElementIterator } from "../form-iterator/top-element-iterator";
+import { CalculatorResults } from "../../results/calculator-results";
+
+/**
+ * classe de base pour tous les formulaires
+ */
+export abstract class FormulaireDefinition extends FormulaireNode {
+    /**
+     * type de calculette
+     */
+    private _calcType: CalculatorType;
+
+    /**
+     * nom de la calculette
+     */
+    private _calculatorName: string;
+
+    constructor(calcType: CalculatorType,
+        private paramService: ParamService,
+        private appSetupService: ApplicationSetupService
+    ) {
+        super();
+        this._calcType = calcType;
+    }
+
+    public get calculatorType(): CalculatorType {
+        return this._calcType;
+    }
+
+    public get calculatorName() {
+        return this._calculatorName;
+    }
+
+    public set calculatorName(name: string) {
+        this._calculatorName = name;
+    }
+
+    protected abstract initParse();
+    protected abstract parseOptions(json: {});
+    protected abstract completeParse();
+
+    public getOption(json: {}, option: string): string {
+        if (json["type"] === "options")
+            return json[option];
+
+        return undefined;
+    }
+
+    private parse_fieldset(json: {}): FieldSet {
+        const ct: string = json["calcType"];
+        let calc_type: CalculatorType = ct == undefined ? this._calcType : CalculatorType[ct];
+
+        const res: FieldSet = new FieldSet(calc_type, json["type"] === "fieldset_template");
+        res.parseConfig(json,
+            {
+                "paramService": this.paramService,
+                "appSetupService": this.appSetupService
+            });
+        return res;
+    }
+
+    private parse_template_container(json: {}, templates: { [key: string]: FieldSet }) {
+        const fsc: FieldsetContainer = new FieldsetContainer(this);
+        fsc.parseConfig(json, templates);
+        this.formElements.push(fsc);
+    }
+
+    public parseDependencies(json: {}) {
+        for (let conf_index in json) {
+            const conf = json[conf_index];
+            const type: string = conf["type"];
+
+            switch (type) {
+                // field set
+                case "fieldset":
+                case "template_container":
+                    for (const k of this.kids)
+                        if (k.id == conf["id"]) {
+                            k.parseDependencies(conf, this);
+                            break;
+                        }
+                    break;
+
+                case "fieldset_template":
+                    for (const k of this.kids)
+                        if (k instanceof FieldsetContainer)
+                            k.parseDependencies(conf);
+                    break;
+            }
+        }
+    }
+
+    public parseConfig(json: {}) {
+        this.initParse();
+
+        const templates: { [key: string]: FieldSet } = {};
+
+        for (let conf_index in json) {
+            const conf = json[conf_index];
+            const type: string = conf["type"];
+
+            switch (type) {
+                // field set
+                case "fieldset":
+                case "fieldset_template":
+                    const fs: FieldSet = this.parse_fieldset(conf);
+                    if (fs.isTemplate)
+                        templates[fs.id] = fs;
+                    else
+                        this.kids.push(fs);
+                    break;
+
+                // options globales
+                case "options":
+                    this.parseOptions(conf);
+                    break;
+
+                case "template_container":
+                    this.parse_template_container(conf, templates);
+                    break;
+
+                default:
+                    throw new Error(`type d'objet de calculette ${type} non pris en charge`);
+            }
+        }
+
+        this.completeParse();
+
+        // logObject(this._fieldSets, "fieldsets");
+        // logObject(this._dependencies, "dependences");
+
+        this.parseDependencies(json);
+    }
+
+    public hasParameter(symbol: string): boolean {
+        for (const p of this.allFormElements) {
+            if (p instanceof NgParameter)
+                if (p.symbol === symbol)
+                    return true;
+        }
+        return false;
+    }
+
+    public getParamFromSymbol(symbol: string): NgParameter {
+        for (const p of this.allFormElements) {
+            if (p instanceof NgParameter)
+                if (p.symbol === symbol)
+                    return p;
+        }
+        return undefined;
+    }
+
+    public getDisplayedParamFromState(st: ParamRadioConfig): NgParameter {
+        for (const p of this.allFormElements) {
+            if (p.isDisplayed && p instanceof NgParameter)
+                if (p.radioState == st)
+                    return p;
+        }
+        return undefined;
+    }
+
+    public getDisplayedParamListFromState(st: ParamRadioConfig): NgParameter[] {
+        const res = [];
+        for (const p of this.allFormElements)
+            if (p.isDisplayed && p instanceof NgParameter && p.radioState == st)
+                res.push(p)
+        return res;
+    }
+
+    public getFieldById(id: string): Field {
+        let res = this.getFormulaireNodeById(id);
+        if (res instanceof Field)
+            return res;
+        return undefined;
+    }
+
+    public getParameterValue(symbol: string): number {
+        for (const fs of this.allFieldsets) {
+            const p = fs.getNodeParameter(symbol);
+            if (p != undefined)
+                switch (p.radioState) {
+                    case ParamRadioConfig.FIX:
+                        return p.getValue();
+
+                    case ParamRadioConfig.VAR:
+                    case ParamRadioConfig.CAL:
+                        return undefined;
+                }
+        }
+
+        throw new Error(`Formulaire.getNodeParameterValue() : pas de paramètre ${symbol} trouvé`);
+    }
+
+    /**
+     * réinitialisation du formulaire suite à un changement d'une valeur, d'une option, ... :
+     * effacement des résultats, application des dépendances, ...
+     */
+    public reset() {
+        this.resetResults();
+        this.applyDependencies();
+    }
+
+    /**
+     * retourne la valeur actuellement sélectionnée d'un SelectField
+     * @param selectFieldId id du SelectField
+     * @returns valeur courante du select sans le préfixe
+     */
+    public getSelectedValue(selectFieldId: string): string {
+        let select: SelectField = <SelectField>this.getFieldById(selectFieldId);
+        let value: string = select.getValue().value;
+        return FormulaireElement.removePrefix(value, selectFieldId + "_");
+    }
+
+    /**
+     * retourne le label actuellement sélectionnée d'un SelectField
+     * @param selectFieldId id du SelectField
+     * @returns label courant du select
+     */
+    public getSelectedLabel(selectFieldId: string): string {
+        let select: SelectField = <SelectField>this.getFieldById(selectFieldId);
+        return select.getValue().label;
+    }
+
+    public get formElements(): FormulaireElement[] {
+        return this.kids as FormulaireElement[];
+    }
+
+    public applyDependencies() {
+        for (const fe of this.allFormElements)
+            fe.applyDependencies(this);
+    }
+
+    protected abstract resetResults();
+    public abstract doCompute();
+    public abstract get hasResults(): boolean;
+    public abstract get results(): CalculatorResults[];
+
+    public updateLocalisation(localisation: StringMap) {
+        for (let fe of this.topFormElements)
+            fe.updateLocalisation(localisation);
+
+        if (this.hasResults)
+            this.doCompute(); // pour mettre à jour la langue
+    }
+
+    public isDisplayed(id: string) {
+        return (<FormulaireElement>this.getFormulaireNodeById(id)).isDisplayed;
+    }
+
+    public get isValid(): boolean {
+        let res: boolean = true;
+        for (let fs of this.allFieldsets)
+            res = res && fs.isValid;
+        return res;
+    }
+
+    /**
+     * gestion d'un clic sur les radios
+     */
+    public onRadioClick(info: any) {
+    }
+
+    /**
+     * itère sur tous les FieldSet
+     */
+    public get allFieldsets(): IterableIterator<FieldSet> {
+        return new DeepFieldsetIterator(this);
+    }
+
+    /**
+     * itère sur tous les FormulaireElement
+     */
+    public get allFormElements(): IterableIterator<FormulaireElement> {
+        return new DeepFormulaireElementIterator(this);
+    }
+
+    /**
+     * itère sur tous les FormulaireElement de 1er niveau
+    */
+    private get topFormElements(): IterableIterator<FormulaireElement> {
+        return new TopFormulaireElementIterator(this);
+    }
+
+    /**
+     * copie des membres
+     */
+    protected copyMembers(n: FormulaireNode) {
+        throw new Error("FormulaireDefinition.copyMembers() non implémenté");
+    }
+
+    /**
+     * crée une nouvelle instance
+     */
+    protected clone(): FormulaireNode {
+        throw new Error("FormulaireDefinition.clone() non implémenté");
+    }
+
+    // interface Observer
+
+    // update(sender: IObservable, data: any) {
+    // switch (data["action"]) {
+    //     case "resetForm":  // événement demandant une réinitialisation du formulaire (changement d'un SelectField, ...)
+    //         this.onReset();
+    // }
+    // }
+}
diff --git a/src/app/formulaire/definition/form-result-fixedvar.ts b/src/app/formulaire/definition/form-result-fixedvar.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5508e98ac359b62ab988f9caea7267ec2898505e
--- /dev/null
+++ b/src/app/formulaire/definition/form-result-fixedvar.ts
@@ -0,0 +1,74 @@
+import { ResultElement, cLog } from "jalhyd";
+
+import { FixedVarResults, GraphType } from "../../results/fixed-var-results";
+import { ParamRadioConfig, NgParameter, ParamValueMode } from "../ngparam";
+import { FormResult } from "./form-result";
+import { FormulaireDefinition } from "./form-definition";
+import { CalculatorResults } from "../../results/calculator-results";
+
+export class FormResultFixedVar extends FormResult {
+    /**
+     * résultats fixes/variables
+     */
+    private _fixVarResults: FixedVarResults;
+
+    private _formBase: FormulaireDefinition;
+
+    constructor(base: FormulaireDefinition, private displaySymbol: boolean) {
+        super();
+        this._formBase = base;
+        this._fixVarResults = new FixedVarResults();
+    }
+
+    public get fixVarResults() {
+        return this._fixVarResults;
+    }
+
+    public resetResults() {
+        this._fixVarResults.reset();
+    }
+
+    public addFixedResults() {
+        for (const p of this._formBase.getDisplayedParamListFromState(ParamRadioConfig.FIX))
+            if (p.symbol !== "Pr")
+                this._fixVarResults.addFixedResultElement(p, p.getValue(), this.displaySymbol);
+    }
+
+    public addFixedResultElement(p: NgParameter, v: ResultElement | number) {
+        this._fixVarResults.addFixedResultElement(p, v, this.displaySymbol);
+    }
+
+    public addVarResultElement(paramVal: number, resVal: ResultElement) {
+        this._fixVarResults.addVarResultElement(paramVal, resVal);
+    }
+
+    public setVariableParamHeaderFromParameter(p: NgParameter) {
+        this.fixVarResults.setVariableParamHeaderFromParameter(p, this.displaySymbol);
+    }
+
+    public setVariableResultHeaderFromParameter(p: NgParameter) {
+        this.fixVarResults.setVariableResultHeaderFromParameter(p);
+    }
+
+    public addLogMessages(l: cLog) {
+        this._fixVarResults.addLogMessages(l);
+    }
+
+    public set graphType(t: GraphType) {
+        this._fixVarResults.graphType = t;
+    }
+
+    public set graphTitle(t: string) {
+        this._fixVarResults.graphTitle = t;
+    }
+
+    public get hasResults(): boolean {
+        return this._fixVarResults.hasResults;
+    }
+
+    public get results(): CalculatorResults[] {
+        const res: CalculatorResults[] = [];
+        res.push(this._fixVarResults)
+        return res;
+    }
+}
diff --git a/src/app/formulaire/definition/form-result-remous.ts b/src/app/formulaire/definition/form-result-remous.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9709b6deba57b8464638e85d74fcaef63d43ad45
--- /dev/null
+++ b/src/app/formulaire/definition/form-result-remous.ts
@@ -0,0 +1,40 @@
+import { acSection, ParamsEquation, Result, MethodeResolution, CourbeRemousParams, CourbeRemous, ResultElement } from "jalhyd";
+
+import { RemousResults } from "../../results/remous-results";
+import { SelectField } from "../select-field";
+import { FormResult } from "./form-result";
+import { FormulaireDefinition } from "./form-definition";
+import { CalculatorResults } from "../../results/calculator-results";
+
+export class FormResultRemous extends FormResult {
+    private _formBase: FormulaireDefinition;
+
+    /**
+     * résultats de courbes de remous
+     */
+    private _remousResults: RemousResults;
+
+    constructor(base: FormulaireDefinition) {
+        super();
+        this._formBase = base;
+        this._remousResults = new RemousResults();
+    }
+
+    public get remousResults() {
+        return this._remousResults;
+    }
+
+    public resetResults() {
+        this._remousResults.reset();
+    }
+
+    public get hasResults(): boolean {
+        return this._remousResults.hasResults;
+    }
+
+    public get results(): CalculatorResults[] {
+        const res: CalculatorResults[] = [];
+        res.push(this._remousResults)
+        return res;
+    }
+}
\ No newline at end of file
diff --git a/src/app/formulaire/definition/form-result-section.ts b/src/app/formulaire/definition/form-result-section.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c17a65e194b9bdddfe9d1353501a25262f30ca59
--- /dev/null
+++ b/src/app/formulaire/definition/form-result-section.ts
@@ -0,0 +1,66 @@
+import { SectionResults } from "../../results/section-results";
+import { ResultElement } from "jalhyd";
+import { ParamRadioConfig, NgParameter } from "../ngparam";
+import { FormDefSection } from "./form-def-section";
+import { FixedVarResults } from "../../results/fixed-var-results";
+import { FormResult } from "./form-result";
+import { FormulaireDefinition } from "./form-definition";
+import { CalculatorResults } from "../../results/calculator-results";
+
+export class FormResultSection extends FormResult {
+    private _formBase: FormulaireDefinition;
+
+    private _formSection: FormDefSection;
+
+    /**
+     * résultats fixes/variables
+     */
+    private _fixedVarResults: FixedVarResults;
+
+    /**
+     * résultats de section paramétrée
+     */
+    private _sectionResults: SectionResults;
+
+    constructor(base: FormulaireDefinition, formSection: FormDefSection) {
+        super();
+        this._formBase = base;
+        this._fixedVarResults = new FixedVarResults();
+        this._sectionResults = new SectionResults();
+        this._formSection = formSection;
+    }
+
+    public get fixedVarResults() {
+        return this._fixedVarResults;
+    }
+
+    public get sectionResults() {
+        return this._sectionResults;
+    }
+
+    public resetResults() {
+        this._fixedVarResults.reset();
+        this._sectionResults.reset();
+    }
+
+    public addSectionFixedResult(val: ResultElement, label: string, drawLabel: string = undefined) {
+        this._sectionResults.addResult(val, label, drawLabel);
+    }
+
+    public addSectionFixedResults(displaySymbol: boolean) {
+        for (let p of this._formBase.getDisplayedParamListFromState(ParamRadioConfig.FIX))
+            if (p.symbol !== "Pr")
+                this._fixedVarResults.addFixedResultElement(p, p.getValue(), displaySymbol);
+    }
+
+    public get hasResults(): boolean {
+        return (this._fixedVarResults != undefined && this._fixedVarResults.hasResults) || (this._sectionResults != undefined && this._sectionResults.hasResults);
+    }
+
+    public get results(): CalculatorResults[] {
+        const res: CalculatorResults[] = [];
+        res.push(this._fixedVarResults)
+        res.push(this._sectionResults)
+        return res;
+    }
+}
diff --git a/src/app/formulaire/definition/form-result.ts b/src/app/formulaire/definition/form-result.ts
new file mode 100644
index 0000000000000000000000000000000000000000..07a3c55a057fe65b801eedf739aec2c03d478315
--- /dev/null
+++ b/src/app/formulaire/definition/form-result.ts
@@ -0,0 +1,9 @@
+import { CalculatorResults } from "../../results/calculator-results";
+
+export abstract class FormResult {
+    public abstract resetResults();
+
+    public abstract get hasResults(): boolean;
+
+    public abstract get results(): CalculatorResults[];
+}
diff --git a/src/app/formulaire/dependency-condition.ts b/src/app/formulaire/dependency/dependency-condition.ts
similarity index 70%
rename from src/app/formulaire/dependency-condition.ts
rename to src/app/formulaire/dependency/dependency-condition.ts
index 876e46426f2bbf57e5c796dc3e66a847f601322d..28480a1ad8da998d495dbcf18bc9a1de575eb33c 100644
--- a/src/app/formulaire/dependency-condition.ts
+++ b/src/app/formulaire/dependency/dependency-condition.ts
@@ -13,4 +13,8 @@ export class DependencyCondition {
     public toString(): string {
         return "cond=" + DependencyConditionType[this._type];
     }
+
+    public clone(): DependencyCondition {
+        throw new Error("la méthode DependencyCondition.clone() doit être redéfinie !")
+    }
 }
diff --git a/src/app/formulaire/dependency.ts b/src/app/formulaire/dependency/dependency.ts
similarity index 53%
rename from src/app/formulaire/dependency.ts
rename to src/app/formulaire/dependency/dependency.ts
index 0f493637459b38d3827d5d883933f566c3eca72c..aa2a1bc0957c3bbca262c105a6fc6472d38b12e2 100644
--- a/src/app/formulaire/dependency.ts
+++ b/src/app/formulaire/dependency/dependency.ts
@@ -1,16 +1,18 @@
-import { FormulaireElement } from "./formulaire-element";
+import { FormulaireElement } from "../formulaire-element";
 import { DependencyCondition } from "./dependency-condition";
 
+/** 
+ * Dépendance entre un élément maître et un élément esclave
+ * Si le maître vérifie une condition (masterCondition), alors la dépendance est appliquée à l'élément esclave,
+ * cad que son affichage, sa valeur, ... sont modifiés
+ */
 export abstract class Dependency {
     private _master: FormulaireElement;
 
-    private _slave: FormulaireElement;
-
     private _masterCondition: DependencyCondition;
 
-    constructor(m: FormulaireElement, s: FormulaireElement, mc: DependencyCondition) {
+    constructor(m: FormulaireElement, mc: DependencyCondition) {
         this._master = m;
-        this._slave = s;
         this._masterCondition = mc;
     }
 
@@ -18,15 +20,13 @@ export abstract class Dependency {
         return this._master;
     }
 
-    public get slaveElement(): FormulaireElement {
-        return this._slave;
-    }
-
     public get masterCondition(): DependencyCondition {
         return this._masterCondition;
     }
 
     public toString(): string {
-        return "master=" + this._master.toString() + "\n  " + this._masterCondition.toString() + "\n  slave=" + this._slave.toString();
+        return "master=" + this._master.toString() + "\n  " + this._masterCondition.toString();
     }
-}
\ No newline at end of file
+
+    public abstract clone(master: FormulaireElement): Dependency;
+}
diff --git a/src/app/formulaire/dependency/existence-dependency-condition.ts b/src/app/formulaire/dependency/existence-dependency-condition.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9a60f25b3ce9847548e9e73de68b24155675fe29
--- /dev/null
+++ b/src/app/formulaire/dependency/existence-dependency-condition.ts
@@ -0,0 +1,12 @@
+import { DependencyCondition, DependencyConditionType } from "./dependency-condition";
+
+
+export class ExistenceDependencyCondition extends DependencyCondition {
+    constructor() {
+        super(DependencyConditionType.IsDisplayed);
+    }
+
+    public clone(): ExistenceDependencyCondition {
+        return new ExistenceDependencyCondition();
+    }
+}
diff --git a/src/app/formulaire/existence-dependency.ts b/src/app/formulaire/dependency/existence-dependency.ts
similarity index 58%
rename from src/app/formulaire/existence-dependency.ts
rename to src/app/formulaire/dependency/existence-dependency.ts
index d9485e6252f7d49ff973836e6cb2793b76fc34b1..20f0e4f574bc425440f616ec1b45d61118077ff5 100644
--- a/src/app/formulaire/existence-dependency.ts
+++ b/src/app/formulaire/dependency/existence-dependency.ts
@@ -1,5 +1,9 @@
 import { Dependency } from "./dependency";
+import { FormulaireElement } from "../formulaire-element";
 
+/** 
+ * dépendance déterminant l'affichage de l'élément esclave
+ */
 export class ExistenceDependency extends Dependency {
     /**
      * true : l'élément slave est affiché si le master est affiché
@@ -10,4 +14,8 @@ export class ExistenceDependency extends Dependency {
     public toString() {
         return "existdep\n  " + super.toString();
     }
+
+    public clone(master: FormulaireElement): Dependency {
+        return new ExistenceDependency(master, this.masterCondition.clone());
+    }
 }
diff --git a/src/app/formulaire/value-dependency-condition.ts b/src/app/formulaire/dependency/value-dependency-condition.ts
similarity index 78%
rename from src/app/formulaire/value-dependency-condition.ts
rename to src/app/formulaire/dependency/value-dependency-condition.ts
index b0e3095688779361b463d3c59edf7a7fee71214f..8ba997c9d177d5dd5e45476789e676b8c8c19a7e 100644
--- a/src/app/formulaire/value-dependency-condition.ts
+++ b/src/app/formulaire/dependency/value-dependency-condition.ts
@@ -13,4 +13,8 @@ export class ValueDependencyCondition extends DependencyCondition {
     public toString(): string {
         return super.toString() + " " + this._value;
     }
+
+    public clone(): ValueDependencyCondition {
+        return new ValueDependencyCondition(this._value);
+    }
 }
diff --git a/src/app/formulaire/dependency/value-dependency.ts b/src/app/formulaire/dependency/value-dependency.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c973ecd6c5a1eedaf84dbe907ce6bde1ffd5af8f
--- /dev/null
+++ b/src/app/formulaire/dependency/value-dependency.ts
@@ -0,0 +1,22 @@
+import { Dependency } from "./dependency";
+import { FormulaireElement } from "../formulaire-element";
+import { ValueDependencyCondition } from "./value-dependency-condition";
+
+/** 
+ * dépendance déterminant la valeur de l'élément esclave
+ */
+export class ValueDependency extends Dependency {
+    public slaveValue: any;
+
+    constructor(m: FormulaireElement, masterValue: any) {
+        super(m, new ValueDependencyCondition(masterValue));
+    }
+
+    public toString() {
+        return "valdep\n  " + super.toString() + "\n  slave val " + this.slaveValue;
+    }
+
+    public clone(master: FormulaireElement): Dependency {
+        return new ValueDependency(master, this.masterCondition.clone());
+    }
+}
diff --git a/src/app/formulaire/field.ts b/src/app/formulaire/field.ts
index 88bceabd7bd1dc070a627e7e3e2f81cedcb24226..34411775bf650f29fcaa476213407f350b3b8b05 100644
--- a/src/app/formulaire/field.ts
+++ b/src/app/formulaire/field.ts
@@ -1,30 +1,37 @@
-import { ComputeNodeType } from "jalhyd";
-
 import { FormulaireElement } from "./formulaire-element";
-
-export enum FieldType {
-    Input, Select, Check
-}
+import { FormulaireDefinition } from "./definition/form-definition";
+import { ValueDependency } from "./dependency/value-dependency";
 
 export abstract class Field extends FormulaireElement {
-    constructor(nodeType: ComputeNodeType, id: string, private _fieldType: FieldType, formId: number) {
-        super(nodeType, id, formId);
+    constructor(isTmpl: boolean = false) {
+        super(isTmpl);
     }
 
-    public get isInput(): boolean {
-        return this._fieldType == FieldType.Input;
-    }
-
-    public get isSelect(): boolean {
-        return this._fieldType == FieldType.Select;
-    }
+    public abstract get isValid();
 
-    public get isCheck(): boolean {
-        return this._fieldType == FieldType.Check;
+    public abstract getValue(): any;
+    public abstract setValue(sender: any, val: any): void;
+
+    private parse_value_dependencies(json: {}, parentForm: FormulaireDefinition) {
+        for (let di in json) {
+            let d = json[di];
+            let masterField: FormulaireElement = parentForm.getFormulaireNodeById(d["refid"]) as FormulaireElement;
+            if (masterField != undefined) {
+                let masterValue = d["refvalue"];
+                let dep = new ValueDependency(masterField, masterValue);
+                dep.slaveValue = d["value"];
+                this._dependencies.push(dep);
+            }
+            else
+                throw new Error(`la dépendance de valeur de '${this.id}' fait référence à un élément inconnu '${d["refid"]}'`);
+        }
     }
 
-    public abstract get isValid();
+    public parseDependencies(json: {}, parentForm: FormulaireDefinition) {
+        super.parseDependencies(json, parentForm);
 
-    public abstract getValue(): any;
-    public abstract setValue(val: any): void;
+        const dep = json["dep_value"];
+        if (dep != undefined)
+            this.parse_value_dependencies(dep, parentForm);
+    }
 }
diff --git a/src/app/formulaire/fieldset-container.ts b/src/app/formulaire/fieldset-container.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5999f7c6545fcf7a5470df486d82d79819fe562a
--- /dev/null
+++ b/src/app/formulaire/fieldset-container.ts
@@ -0,0 +1,134 @@
+import { FormulaireElement } from "./formulaire-element";
+import { FieldSet } from "./fieldset";
+import { Dependency } from "./dependency/dependency";
+import { StringMap } from "../stringmap";
+import { FormulaireDefinition } from "./definition/form-definition";
+import { FormulaireNode } from "./formulaire-node";
+
+export class FieldsetContainer extends FormulaireElement {
+    private _templates: FieldSet[];
+
+    private _localisation: StringMap;
+
+    public title: string
+
+    constructor(private parentForm: FormulaireDefinition) {
+        super();
+        this._templates = [];
+    }
+
+    /**
+     * cherche un FormulaireNode par son id de conf
+     */
+    public getFormulaireNodeById(id: string): FormulaireNode {
+        const res = super.getFormulaireNodeById(id);
+        if (res !== undefined)
+            return res;
+
+        for (const t of this._templates) {
+            const res = t.getFormulaireNodeById(id);
+            if (res !== undefined)
+                return res;
+        }
+
+        return undefined;
+    }
+
+    private checkTemplate(fs: FieldSet) {
+        if (!fs.isTemplate)
+            throw new Error(`le Fieldset ${fs.id} n'est pas un template`);
+    }
+
+    public addTemplate(fs: FieldSet) {
+        if (this.hasTemplate(fs))
+            console.log(`Warning : le Fieldset template ${fs.id} a déjà été ajouté`);
+        else
+            this._templates.push(fs);
+    }
+
+    private hasTemplate(fs: FieldSet): boolean {
+        this.checkTemplate(fs);
+
+        for (const f of this._templates)
+            if (f.id === fs.id)
+                return true;
+        return false;
+    }
+
+    private getTemplate(id: string) {
+        for (const f of this._templates)
+            if (f.id === id)
+                return f;
+        return undefined;
+    }
+
+    public addFromTemplate(templId: string) {
+        const templ: FieldSet = this.getTemplate(templId);
+        const inst = templ.instanciateTemplate();
+        this.fieldsets.push(inst);
+        inst.applyDependencies(this.parentForm);
+        this.updateLocalisation()
+
+        // notification de création d'un FieldSet
+        this.notifyObservers({
+            "action": "newFieldset",
+            "fieldset": inst
+        }, this);
+    }
+
+    public get fieldsets(): FieldSet[] {
+        return this.kids as FieldSet[];
+    }
+
+    public get templates(): FieldSet[] {
+        return this._templates;
+    }
+
+    public parseConfig(json: {}, data?: {}) {
+        this._confId = json["id"];
+
+        const templs: string[] = json["templates"];
+        for (const t of templs)
+            // this.addTemplate(this.getFieldsetTemplate(t));
+            this.addTemplate(data[t]);
+    }
+
+    public parseDependencies(json: {}) {
+        super.parseDependencies(json, this.parentForm);
+
+        for (const t of this._templates)
+            t.parseDependencies(json, this.parentForm);
+    }
+
+    protected verifyDependency(d: Dependency): boolean {
+        return true;
+    }
+
+    public updateLocalisation(loc?: StringMap) {
+        if (loc == undefined)
+            loc = this._localisation;
+        else
+            this._localisation = loc;
+
+        super.updateLocalisation(loc);
+
+        for (let t of this._templates)
+            t.updateLocalisation(loc);
+    }
+
+    /**
+     * copie des membres
+     */
+    protected copyMembers(n: FieldsetContainer) {
+        super.copyMembers(n);
+        n._localisation = this._localisation;
+        n.title = this.title;
+    }
+
+    /**
+     * crée une nouvelle instance
+     */
+    protected clone(): FieldsetContainer {
+        return new FieldsetContainer(this.parentForm);
+    }
+}
diff --git a/src/app/formulaire/fieldset.ts b/src/app/formulaire/fieldset.ts
index 6e7911c5f879b38082bc0aa1de276688191b3bd2..0598aa7c7c0253519c47c1f587c60773065ad955 100644
--- a/src/app/formulaire/fieldset.ts
+++ b/src/app/formulaire/fieldset.ts
@@ -1,30 +1,51 @@
-import { ComputeNodeType } from "jalhyd";
+import { CalculatorType, ComputeNodeType } from "jalhyd";
 
 import { FormulaireElement } from "./formulaire-element";
-import { Dependency } from "./dependency";
-import { DependencyConditionType } from "./dependency-condition";
+import { Dependency } from "./dependency/dependency";
+import { DependencyConditionType } from "./dependency/dependency-condition";
 import { Field } from "./field";
-import { NgParameter } from "./ngparam";
-
+import { CheckField } from "./check-field";
+import { SelectField } from "./select-field";
+import { NgParameter, ParamRadioConfig } from "./ngparam";
+import { ParamService } from "../services/param/param.service";
+import { ApplicationSetupService } from "../services/app-setup/app-setup.service";
+import { FormulaireDefinition } from "./definition/form-definition";
 
 export class FieldSet extends FormulaireElement {
-    private _fields: Field[];
+    private _calcType: CalculatorType;
+
+    constructor(calcType: CalculatorType, isTmpl: boolean = false) {
+        super(isTmpl);
+        this._calcType = calcType;
+    }
+
+    public get calculatorType(): CalculatorType {
+        return this._calcType;
+    }
+
+    public get fields(): Field[] {
+        return this.kids as Field[];
+    }
 
-    constructor(nodeType: ComputeNodeType, id: string, formId: number) {
-        super(nodeType, id, formId);
-        this._fields = [];
+    /**
+     * crée une nouvelle instance
+     */
+    protected clone(): FieldSet {
+        return new FieldSet(this._calcType);
     }
 
-    public get fields() {
-        return this._fields;
+    public instanciateTemplate(): FieldSet {
+        const res = this.getDeepClone() as FieldSet;
+        this.copyDependencies(res, res);
+        return res;
     }
 
     public addField(f: Field) {
-        this._fields.push(f);
+        this.fields.push(f);
     }
 
     public get hasInputs(): boolean {
-        for (let f of this._fields)
+        for (let f of this.fields)
             if (f instanceof NgParameter)
                 return true;
         return false;
@@ -32,7 +53,7 @@ export class FieldSet extends FormulaireElement {
 
     public getInput(i: number): NgParameter {
         let n = 0;
-        for (let f of this._fields) {
+        for (let f of this.fields) {
             if (f instanceof NgParameter) {
                 if (n == i)
                     return f;
@@ -48,8 +69,125 @@ export class FieldSet extends FormulaireElement {
 
     public get isValid(): boolean {
         let res: boolean = true;
-        for (let f of this._fields)
+        for (let f of this.fields)
             res = res && f.isValid;
         return res;
     }
-}
\ No newline at end of file
+
+    private parse_check(json: {}): CheckField {
+        let res: CheckField = new CheckField(this.isTemplate);
+        res.parseConfig(json)
+        return res;
+    }
+
+    private parse_select(json: {}): SelectField {
+        let res: SelectField = new SelectField(this.isTemplate);
+        res.parseConfig(json);
+        return res;
+    }
+
+    private parse_input(json: {}, default_calc_type: CalculatorType, default_node_type: ComputeNodeType, default_radio_config: string,
+        paramService: ParamService, appSetupService: ApplicationSetupService): NgParameter {
+        let input_id: string = json["id"];
+
+        const nt: string = json["nodeType"];
+        let node_type: ComputeNodeType = nt == undefined ? default_node_type : ComputeNodeType[nt];
+
+        let res: NgParameter = paramService.createParameter(default_calc_type, node_type, input_id, this.isTemplate);
+        if (res == undefined)
+            throw new Error(`pas de paramètre '${input_id}' trouvé dans le noeud ${ComputeNodeType[node_type]} (${node_type}) ou ComputeNodeType.None`);
+
+        res.parseConfig(json, { "appSetupService": appSetupService, "radioConfig": default_radio_config });
+
+        return res;
+    }
+
+    public parseConfig(json: {}, data?: {}) {
+        const paramService: ParamService = data["paramService"];
+        const appSetupService: ApplicationSetupService = data["appSetupService"];
+
+        this._confId = json["id"];
+
+        const nt: string = json["nodeType"];
+        let node_type: ComputeNodeType = nt == undefined ? ComputeNodeType.None : ComputeNodeType[nt];
+
+        const fields = json["fields"];
+        for (const field_index in fields) {
+            const field = fields[field_index];
+
+            if (field["type"] === "input") {
+                const default_radio_config = json["option"];
+                const param = this.parse_input(field, this._calcType, node_type, default_radio_config, paramService, appSetupService);
+                this.addField(param);
+            } else if (field["type"] === "select") {
+                const param = this.parse_select(field);
+                this.addField(param);
+            } else if (field["type"] === "check") {
+                const param = this.parse_check(field);
+                this.addField(param);
+            }
+        }
+    }
+
+    public parseDependencies(json: {}, parentForm: FormulaireDefinition) {
+        super.parseDependencies(json, parentForm);
+
+        for (const k1 in json) {
+            if (k1 === "fields") {
+                const fields = json[k1];
+                for (const k2 in fields) {
+                    const field = fields[k2];
+                    switch (field["type"]) {
+                        case "input":
+                        case "select":
+                        case "check":
+                            for (const k of this.kids)
+                                if (k.id == field["id"]) {
+                                    k.parseDependencies(field, parentForm);
+                                    break;
+                                }
+                            break;
+                    }
+                }
+            }
+        }
+    }
+
+    public getNodeParameter(symbol: string): NgParameter {
+        for (let p of this.fields)
+            if (p instanceof NgParameter)
+                if (p.isDisplayed && p.symbol === symbol)
+                    return p;
+
+        return undefined;
+    }
+
+    public getNodeParameterValue(symbol: string): number {
+        const p = this.getNodeParameter(symbol);
+        if (p == undefined)
+            throw new Error(`FieldSet.getNodeParameterValue() : pas de paramètre ${symbol} trouvé`);
+
+        switch (p.radioState) {
+            case ParamRadioConfig.FIX:
+                return p.getValue();
+
+            case ParamRadioConfig.VAR:
+            case ParamRadioConfig.CAL:
+                return undefined;
+        }
+    }
+
+    /**
+     * retourne la valeur actuellement sélectionnée d'un SelectField (qui doit être affiché)
+     * @param selectFieldId id du SelectField
+     * @returns valeur courante du select sans le préfixe
+     */
+    public getSelectedValue(selectFieldId: string): string {
+        for (let p of this.fields)
+            if (p instanceof SelectField && p.isDisplayed && p.id === selectFieldId) {
+                let value: string = p.getValue().value;
+                return FormulaireElement.removePrefix(value, selectFieldId + "_");
+            }
+        return undefined;
+    }
+}
diff --git a/src/app/formulaire/form-iterator/abstract-node-iterator.ts b/src/app/formulaire/form-iterator/abstract-node-iterator.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c7d726faf34e5b8c09b7ae2ab30bcf561b2c184b
--- /dev/null
+++ b/src/app/formulaire/form-iterator/abstract-node-iterator.ts
@@ -0,0 +1,47 @@
+import { FormulaireNode } from "../formulaire-node";
+
+/**
+ * classe de construction d'itérateurs qui parcourent un arbre de FormulaireNode
+ */
+export class AbstractFormulaireNodeIterator<T extends FormulaireNode> {
+    private _array: T[] = [];
+
+    private _index: number = 0;
+
+    constructor(n: FormulaireNode) {
+        this.flatten(n.kids, this._array);
+    }
+
+    private flatten(input: FormulaireNode[], out: FormulaireNode[]) {
+        for (let fe of input) {
+            if (this.isIterable(fe))
+                out.push(fe);
+            if (this.isDeepIterator())
+                this.flatten(fe.kids, out);
+        }
+    }
+
+    protected isIterable(fe: FormulaireNode): boolean {
+        return true;
+    }
+
+    protected isDeepIterator(): boolean {
+        return true;
+    }
+
+    public next(): IteratorResult<T> {
+        const i = this._index;
+        if (this._index < this._array.length) {
+            this._index = i + 1;
+            return {
+                done: false,
+                value: this._array[i]
+            };
+        } else {
+            return {
+                done: true,
+                value: undefined
+            };
+        }
+    }
+}
diff --git a/src/app/formulaire/form-iterator/deep-element-iterator.ts b/src/app/formulaire/form-iterator/deep-element-iterator.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a4a1aae30b0f7793ce161c01ac0c7bef915214be
--- /dev/null
+++ b/src/app/formulaire/form-iterator/deep-element-iterator.ts
@@ -0,0 +1,14 @@
+import { AbstractFormulaireNodeIterator } from "./abstract-node-iterator";
+import { FormulaireElement } from "../formulaire-element";
+
+/**
+ * itérateur qui extrait récursivement les FormulaireElement dans un tableau de FormulaireElement
+ * (qui peut contenir des FieldsetContainer)
+ */
+export class DeepFormulaireElementIterator extends AbstractFormulaireNodeIterator<FormulaireElement> implements IterableIterator<FormulaireElement> {
+    // interface IterableIterator
+
+    [Symbol.iterator](): IterableIterator<FormulaireElement> {
+        return this;
+    }
+}
diff --git a/src/app/formulaire/form-iterator/deep-fieldset-iterator.ts b/src/app/formulaire/form-iterator/deep-fieldset-iterator.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7bc3624682ac639aa44f48fcb5d89fa8e5e202f8
--- /dev/null
+++ b/src/app/formulaire/form-iterator/deep-fieldset-iterator.ts
@@ -0,0 +1,19 @@
+import { AbstractFormulaireNodeIterator } from "./abstract-node-iterator";
+import { FieldSet } from "../fieldset";
+import { FormulaireNode } from "../formulaire-node";
+
+/**
+ * itérateur qui extrait récursivement les FieldSet dans un tableau de FormulaireElement
+ * (qui peut contenir des FieldsetContainer)
+ */
+export class DeepFieldsetIterator extends AbstractFormulaireNodeIterator<FieldSet> implements IterableIterator<FieldSet> {
+    protected isIterable(fe: FormulaireNode) {
+        return fe instanceof FieldSet;
+    }
+
+    // interface IterableIterator
+
+    [Symbol.iterator](): IterableIterator<FieldSet> {
+        return this;
+    }
+}
diff --git a/src/app/formulaire/form-iterator/deep-node-iterator.ts b/src/app/formulaire/form-iterator/deep-node-iterator.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ad3aa95e0f7afc538b9168293a4f5a2eaa1dba9f
--- /dev/null
+++ b/src/app/formulaire/form-iterator/deep-node-iterator.ts
@@ -0,0 +1,10 @@
+import { AbstractFormulaireNodeIterator } from "./abstract-node-iterator";
+import { FormulaireNode } from "../formulaire-node";
+
+export class DeepFormulaireNodeIterator extends AbstractFormulaireNodeIterator<FormulaireNode> implements IterableIterator<FormulaireNode> {
+    // interface IterableIterator
+
+    [Symbol.iterator](): IterableIterator<FormulaireNode> {
+        return this;
+    }
+}
\ No newline at end of file
diff --git a/src/app/formulaire/form-iterator/top-element-iterator.ts b/src/app/formulaire/form-iterator/top-element-iterator.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1b1f35dc67707e0b49512f258775962a4f98dd93
--- /dev/null
+++ b/src/app/formulaire/form-iterator/top-element-iterator.ts
@@ -0,0 +1,18 @@
+import { AbstractFormulaireNodeIterator } from "./abstract-node-iterator";
+import { FormulaireElement } from "../formulaire-element";
+
+/**
+ * itérateur qui extrait les FormulaireElement de 1er niveau dans un tableau de FormulaireElement
+ * (qui peut contenir des FieldsetContainer)
+ */
+export class TopFormulaireElementIterator extends AbstractFormulaireNodeIterator<FormulaireElement> implements IterableIterator<FormulaireElement> {
+    protected isDeepIterator(): boolean {
+        return false;
+    }
+
+    // interface IterableIterator
+
+    [Symbol.iterator](): IterableIterator<FormulaireElement> {
+        return this;
+    }
+}
diff --git a/src/app/formulaire/formulaire-definition.ts b/src/app/formulaire/formulaire-definition.ts
deleted file mode 100644
index 287bee8e2bb13be1718f76020c811261d468795e..0000000000000000000000000000000000000000
--- a/src/app/formulaire/formulaire-definition.ts
+++ /dev/null
@@ -1,1234 +0,0 @@
-import { ComputeNodeType, ParamsEquation, Nub, acSection, RegimeUniforme, MethodeResolution, CourbeRemousParams, CourbeRemous, ParamDomainValue, Result, ResultElement } from "jalhyd";
-import { ParamsSectionRectang, cSnRectang, ParamsSectionCirc, cSnCirc, ParamsSectionPuiss, cSnPuiss } from "jalhyd";
-import { ConduiteDistrib, ConduiteDistribParams, LechaptCalmon, LechaptCalmonParams, ParamsSectionTrapez, cSnTrapez } from "jalhyd";
-import { PabDimension, PabDimensionParams, PabPuissance, PabPuissanceParams } from "jalhyd";
-
-import { ParamService } from "../services/param/param.service";
-import { InternationalisationService } from "../services/internationalisation/internationalisation.service";
-import { ApplicationSetupService } from "../services/app-setup/app-setup.service";
-import { Field } from "./field";
-import { NgParameter, ParamRadioConfig, ParamValueMode } from "./ngparam";
-import { InputField } from "./input-field";
-import { CheckField } from "./check-field";
-import { SelectField } from "./select-field";
-import { SelectEntry } from "./select-entry";
-import { FieldSet } from "./fieldset";
-import { Dependency } from "./dependency";
-import { DependencyCondition, DependencyConditionType } from "./dependency-condition";
-import { FormulaireElement } from "./formulaire-element";
-import { ValueDependency } from "./value-dependency";
-import { ValueDependencyCondition } from "./value-dependency-condition";
-import { ExistenceDependency } from "./existence-dependency";
-import { FixedVarResults, GraphType } from "../results/fixed-var-results";
-import { SectionResults } from "../results/section-results";
-import { RemousResults } from "../results/remous-results";
-import { StringMap } from "../stringmap";
-import { Observable } from "../services/observer";
-
-
-export enum CalculatorType {
-    ConduiteDistributrice, LechaptCalmon, SectionParametree, RegimeUniforme, CourbeRemous,
-    PabDimensions, // passe à bassin rectangulaire
-    PabPuissance, // passe à bassin : puissance dissipée
-}
-
-
-export class FormulaireDefinition extends Observable {
-    /**
-     * objet JSON chargé depuis le fichier de configuration de la calculette
-     */
-    private _config = {};
-
-    /**
-     * symbole du paramètre à calculer par défaut (cf config "idCal")
-     */
-    private _defaultCalculatedParam: string;
-
-    private _fieldSets: FieldSet[] = [];
-
-    private _dependencies: Dependency[] = [];
-
-    /**
-     * id du SelectField configurant le type de section
-     */
-    private _sectionSelectFieldId: string;
-
-    /**
-     * type de noeud de calcul par défaut
-     */
-    private _defaultNodeType: ComputeNodeType;
-
-    /**
-     * type de noeud de calcul actuel (utilisé pour les sections)
-     */
-    private _nodeType: ComputeNodeType;
-
-    /**
-     * résultats fixes/variables
-     */
-    private _fixVarResults: FixedVarResults;
-
-    /**
-     * résultats de section paramétrée
-     */
-    private _sectionResults: SectionResults;
-
-    /**
-     * résultats de courbes de remous
-     */
-    private _remousResults: RemousResults;
-
-    /**
-     * nom de de la calculette
-     */
-    private _calculatorName: string;
-
-    constructor(
-        private _calcType: CalculatorType,
-        private paramService: ParamService,
-        private intlService: InternationalisationService,
-        private appSetupService: ApplicationSetupService
-    ) {
-        super();
-        this._uid = FormulaireDefinition.uidGenerator();
-        this._fixVarResults = new FixedVarResults();
-        this._sectionResults = new SectionResults();
-        this._remousResults = new RemousResults();
-    }
-
-    public get calculatorType(): CalculatorType {
-        return this._calcType;
-    }
-
-    /**
-     * id unique
-     */
-    private _uid: number;
-
-    private static _uidSequence: number = 0;
-
-    private static uidGenerator(): number {
-        let res = this._uidSequence;
-        this._uidSequence++;
-        return res;
-    }
-
-    public get uid() {
-        return this._uid;
-    }
-
-    /**
-     * /id unique
-     */
-
-    public get fixVarResults() {
-        return this._fixVarResults;
-    }
-
-    public get sectionResults() {
-        return this._sectionResults;
-    }
-
-    public get remousResults() {
-        return this._remousResults;
-    }
-
-    public getFieldSets(): FieldSet[] {
-        let res: FieldSet[] = [];
-
-        for (let fs of this._fieldSets)
-            if (fs.computeNodeType == this._nodeType || fs.computeNodeType == this._defaultNodeType)
-                res.push(fs);
-
-        return res;
-    }
-
-    public get dependencies(): Dependency[] {
-        return this._dependencies;
-    }
-
-    private getFieldSet(id: string) {
-        for (let fs of this._fieldSets) {
-            if (fs.id == id)
-                return fs;
-        }
-        return undefined;
-    }
-
-    private hasParameter(symbol: string): boolean {
-        for (let fs of this._fieldSets) {
-            for (let p of fs.fields) {
-                if (p instanceof NgParameter)
-                    if (p.symbol === symbol)
-                        return true;
-            }
-        }
-        return false;
-    }
-
-    public getParamFromSymbol(symbol: string): NgParameter {
-        for (let fs of this._fieldSets) {
-            for (let p of fs.fields) {
-                if (p instanceof NgParameter)
-                    if (p.symbol === symbol)
-                        return p;
-            }
-        }
-        return undefined;
-    }
-
-    public getParamFromState(st: ParamRadioConfig): NgParameter {
-        for (let fs of this._fieldSets) {
-            for (let p of fs.fields) {
-                if (p instanceof NgParameter)
-                    if (p.radioState == st)
-                        return p;
-            }
-        }
-        return undefined;
-    }
-
-    private removePrefix(s: string, prefix: string): string {
-        if (s.startsWith(prefix)) {
-            let l = prefix.length;
-            return s.substr(l, s.length - l);
-        }
-        return undefined;
-    }
-
-    public updateNodeType() {
-        this._nodeType = this.getNodeTypeFromSelectField(this._calcType);
-    }
-
-    private getNodeTypeFromSelectField(calcType: CalculatorType): ComputeNodeType {
-        if (this._sectionSelectFieldId != undefined) {
-            let ssf: SelectField = <SelectField>this.getFormulaireElementById(this._sectionSelectFieldId);
-            let val = ssf.getValue();
-            let type: string = this.removePrefix(val, this._sectionSelectFieldId + "_");
-            if (type != undefined)
-                return this.getComputeNodeTypeFromSection(type, calcType);
-        }
-
-        return undefined;
-    }
-
-    private getComputeNodeTypeFromSection(sect: string, calc: CalculatorType): ComputeNodeType {
-        switch (calc) {
-            case CalculatorType.SectionParametree:
-                switch (sect) {
-                    case "trapez":
-                        return ComputeNodeType.SectionTrapeze;
-
-                    case "rect":
-                        return ComputeNodeType.SectionRectangle;
-
-                    case "circ":
-                        return ComputeNodeType.SectionCercle;
-
-                    case "puiss":
-                        return ComputeNodeType.SectionPuissance;
-
-                    default:
-                        throw "getComputeNodeTypeFromSection() : section " + sect + " / type de calculette " + CalculatorType[calc] + " non pris en charge"
-                }
-
-            case CalculatorType.RegimeUniforme:
-                switch (sect) {
-                    case "trapez":
-                        return ComputeNodeType.RegimeUniformeTrapeze;
-
-                    case "rect":
-                        return ComputeNodeType.RegimeUniformeRectangle;
-
-                    case "circ":
-                        return ComputeNodeType.RegimeUniformeCercle;
-
-                    case "puiss":
-                        return ComputeNodeType.RegimeUniformePuissance;
-
-                    default:
-                        throw "getComputeNodeTypeFromSection() : section " + sect + " / type de calculette " + CalculatorType[calc] + " non pris en charge"
-                }
-
-            case CalculatorType.CourbeRemous:
-                switch (sect) {
-                    case "trapez":
-                        return ComputeNodeType.CourbeRemousTrapeze;
-
-                    case "rect":
-                        return ComputeNodeType.CourbeRemousRectangle;
-
-                    case "circ":
-                        return ComputeNodeType.CourbeRemousCercle;
-
-                    case "puiss":
-                        return ComputeNodeType.CourbeRemousPuissance;
-
-                    default:
-                        throw "getComputeNodeTypeFromSection() : section " + sect + " / type de calculette " + CalculatorType[calc] + " non pris en charge"
-                }
-
-            default:
-                throw "getComputeNodeTypeFromSection() : section " + sect + " / type de calculette " + CalculatorType[calc] + " non pris en charge"
-        }
-    }
-
-    /**
-     * remet les radios de tous les paramètres à FIX sauf "me" et ceux (celui) à l'état "except"
-     */
-    private resetOtherRadio(me: NgParameter, except: ParamRadioConfig) {
-        for (let fs of this._fieldSets) {
-            for (let p of fs.fields) {
-                if (p instanceof NgParameter)
-                    if (p != me && p.radioState != except && p.radioConfig != ParamRadioConfig.FIX)
-                        p.radioState = ParamRadioConfig.FIX;
-            }
-        }
-    }
-
-    /**
-     * modifie les boutons radio "fix", "var", "cal" de tous les paramètres
-     * en fonction de la modification de l'état d'un des paramètres
-     * @param symbol symbole du paramètre source
-     * @param option nouvel état "fix", "var" ou "cal" du paramètre source
-     */
-    public resetRadiosAndResults(symbol: string, option: string) {
-        let sourceParam = this.getParamFromSymbol(symbol);
-        let oldState: ParamRadioConfig = sourceParam.radioState;
-        let newState: ParamRadioConfig = ParamRadioConfig[option.toUpperCase()];
-
-        switch (oldState) {
-            case ParamRadioConfig.FIX:
-                switch (newState) {
-                    case ParamRadioConfig.VAR:
-                        this.resetOtherRadio(sourceParam, ParamRadioConfig.CAL);
-                        break;
-
-                    case ParamRadioConfig.CAL:
-                        this.resetOtherRadio(sourceParam, ParamRadioConfig.VAR);
-                        break;
-                }
-                break;
-
-            case ParamRadioConfig.VAR:
-                switch (newState) {
-                    case ParamRadioConfig.CAL:
-                        this.resetOtherRadio(sourceParam, ParamRadioConfig.VAR);
-                        break;
-                }
-                break;
-
-            case ParamRadioConfig.CAL:
-                switch (newState) {
-                    case ParamRadioConfig.FIX:
-                        this.setDefault();
-                        break;
-
-                    case ParamRadioConfig.VAR:
-                        this.resetOtherRadio(sourceParam, ParamRadioConfig.CAL);
-                        this.setDefault();
-                        break;
-                }
-        }
-
-        sourceParam.radioState = newState;
-
-        // on vérifie qu'il y a au moins un paramètre "à calculer" et sinon, on prend le 1er qui est à "fixé"
-        if (this.getParamFromState(ParamRadioConfig.CAL) == undefined) {
-            let newCal: NgParameter = undefined;
-
-            for (let fs of this._fieldSets) {
-                for (let p of fs.fields) {
-                    if (p instanceof NgParameter)
-                        if (p.radioConfig == ParamRadioConfig.CAL && p.radioState == ParamRadioConfig.FIX && p != sourceParam) {
-                            newCal = p;
-                            break;
-                        }
-                }
-                if (newCal != undefined)
-                    break;
-            }
-
-            if (newCal != undefined)
-                newCal.radioState = ParamRadioConfig.CAL;
-        }
-
-        this.resetResults();
-        this.applyDependencies();
-    }
-
-    public resetResults() {
-        this._fixVarResults.reset();
-        this._sectionResults.reset();
-        this._remousResults.reset();
-    }
-
-    /**
-     * met le paramètre par défaut à CAL
-     */
-    private setDefault() {
-        let defaultParamCal = this.getParamFromSymbol(this._defaultCalculatedParam);
-        defaultParamCal.radioState = ParamRadioConfig.CAL;
-    }
-
-    private getInputParameters(): NgParameter[] {
-        let res = [];
-        for (let fs of this._fieldSets)
-            for (let p of fs.fields)
-                if (p instanceof NgParameter)
-                    res.push(p);
-        return res;
-    }
-
-    private getDisplayedInputParameters(): NgParameter[] {
-        let res = [];
-        for (let fs of this._fieldSets)
-            if (fs.isDisplayed)
-                for (let p of fs.fields)
-                    if (p instanceof NgParameter && p.isDisplayed)
-                        res.push(p);
-        return res;
-    }
-
-    private getNodeParameterValue(nodeType: ComputeNodeType, symbol: string): number {
-        for (let fs of this._fieldSets) {
-            for (let p of fs.fields) {
-                if (p instanceof NgParameter)
-                    // if (p.computeNodeType == nodeType && (p.symbol === symbol || p.alias == symbol)) {
-                    if (p.computeNodeType == nodeType && p.symbol === symbol) {
-                        switch (p.radioState) {
-                            case ParamRadioConfig.FIX:
-                                return p.getValue();
-
-                            case ParamRadioConfig.VAR:
-                            case ParamRadioConfig.CAL:
-                                return undefined;
-                        }
-                    }
-            }
-        }
-
-        if (nodeType != this._defaultNodeType)
-            return this.getNodeParameterValue(this._defaultNodeType, symbol);
-
-        throw "Formulaire.getNodeParameterValue() : pas de paramètre " + symbol + " trouvé pour le noeud " + ComputeNodeType[nodeType] + "(" + nodeType + ")";
-    }
-
-    private getParameterValue(symbol: string): number {
-        return this.getNodeParameterValue(this._defaultNodeType, symbol);
-    }
-
-    private getSectionComputedParam(): { symbol: string, label: string } {
-        let targetSelect: SelectField = <SelectField>this.getFieldById("select_target");
-        let targetValue: string = targetSelect.getValue();
-
-        let symbol: string = this.removePrefix(targetValue, "select_target_");
-
-        let label = targetSelect.getLabel();
-        return { symbol, label };
-    }
-
-    public getFormulaireElementById(id: string): FormulaireElement {
-        for (let fs of this._fieldSets) {
-            if (fs.id == id)
-                return fs;
-
-            for (let p of fs.fields)
-                if (p.id === id)
-                    return p;
-        }
-        return undefined;
-    }
-
-    private getFieldById(id: string): Field {
-        let res = this.getFormulaireElementById(id);
-        if (res instanceof Field)
-            return res;
-        return undefined;
-    }
-
-    private parse_value_dependencies(json: {}, slave: FormulaireElement) {
-        for (let di in json) {
-            let d = json[di];
-            let masterField: FormulaireElement = this.getFormulaireElementById(d["refid"]);
-            if (masterField != undefined) {
-                let masterValue = d["refvalue"];
-                let dep = new ValueDependency(masterField, slave, masterValue);
-                dep.slaveValue = d["value"];
-                this._dependencies.push(dep);
-            }
-        }
-    }
-
-    private parse_existence_dependencies(json: {}, slave: FormulaireElement) {
-        for (let di in json) {
-            let d = json[di];
-            let masterField: FormulaireElement = this.getFormulaireElementById(d["refid"]);
-            if (masterField != undefined) {
-                let rv = d["refvalue"];
-                if (rv != undefined)
-                    var mc: DependencyCondition = new ValueDependencyCondition(rv);
-                else {
-                    let cond = d["cond"];
-                    if (cond != undefined) {
-                        switch (cond) {
-                            case "isvar":
-                                var mc = new DependencyCondition(DependencyConditionType.IsVariable);
-                                break;
-
-                            case "isdisp":
-                                var mc = new DependencyCondition(DependencyConditionType.IsDisplayed);
-                                break;
-
-                            default:
-                                throw "Formulaire.parse_existence_dependencies() : type de condition '" + cond + "' non pris en charge";
-                        }
-                    }
-                    else
-                        throw "Formulaire.parse_existence_dependencies() : infos de dépendance manquantes/non prises en charge";
-                }
-                let dep = new ExistenceDependency(masterField, slave, mc);
-                this._dependencies.push(dep);
-            }
-        }
-    }
-
-    private parse_dependencies(slave: FormulaireElement, json: {}) {
-        let dep = json["dep_value"];
-        if (dep != undefined) {
-            this.parse_value_dependencies(dep, slave);
-        }
-        else {
-            dep = json["dep_exist"];
-            if (dep != undefined) {
-                this.parse_existence_dependencies(dep, slave);
-            }
-        }
-    }
-
-    private parse_check(node_type: ComputeNodeType, field: {}): CheckField {
-        let id = field["id"];
-        let res: CheckField = new CheckField(node_type, id, this._uid);
-        let value = field["value"];
-
-        res.setValue(value == "true");
-
-        this.parse_dependencies(res, field);
-
-        return res;
-    }
-
-    private parse_select(node_type: ComputeNodeType, field: {}): SelectField {
-        let id = field["id"];
-        let res: SelectField = new SelectField(node_type, id, this._uid);
-        let values = field["select"];
-
-        for (let v of values) {
-            let e: SelectEntry = new SelectEntry(v["id"], undefined);
-            res.addEntry(e);
-        }
-
-        this.parse_dependencies(res, field);
-
-        return res;
-    }
-
-    private parse_input(node_type: ComputeNodeType, fieldset: {}, field: {}): NgParameter {
-        let input_id: string = field["id"];
-        let res: NgParameter = this.paramService.createParameter(node_type, input_id, this._uid);
-        if (res == undefined)
-            throw "pas de paramètre '" + input_id + "' trouvé dans le noeud " + ComputeNodeType[node_type] + "(" + node_type + ") ou " + ComputeNodeType[this._defaultNodeType] + "(" + this._defaultNodeType + ")";
-
-        res.unit = field["unit"];
-        if (input_id == "Pr")
-            var val = this.appSetupService.computePrecision;
-        else
-            val = field["value"];
-        if (val != undefined)
-            res.setValue(+val);
-        res.radioConfig = NgParameter.getRadioConfig(fieldset["option"]);
-        res.radioState = ParamRadioConfig.FIX;
-        res.isDefault = false; // malgré le fait qu'il soit initialisé dans la déclaration de la classe NgParam à false, quand on relit sa valeur, il vaut undefined (merci Microsoft)
-
-        this.parse_dependencies(res, field);
-
-        return res;
-    }
-
-    private parse_fieldset(fieldset: {}, conf_id: string) {
-        let nt: string = fieldset["nodeType"];
-        let node_type: ComputeNodeType;
-        node_type = nt == undefined ? this._defaultNodeType : ComputeNodeType[nt];
-
-        let res: FieldSet = new FieldSet(node_type, conf_id, this._uid);
-        this._fieldSets.push(res);
-
-        let fields = fieldset["fields"];
-        for (let field_index in fields) {
-            let field = fields[field_index];
-            if (field["type"] === "input") {
-                let param = this.parse_input(node_type, fieldset, field);
-                if (param != undefined)
-                    res.addField(param);
-            } else if (field["type"] === "select") {
-                let param = this.parse_select(node_type, field);
-                res.addField(param);
-            } else if (field["type"] === "check") {
-                let param = this.parse_check(node_type, field);
-                res.addField(param);
-            }
-        }
-
-        this.parse_dependencies(res, fieldset);
-    }
-
-    private getOption(option: string): string {
-        for (let conf_index in this._config) {
-            let conf = this._config[conf_index];
-            if (conf["id"] === "options")
-                return conf[option];
-        }
-
-        return undefined;
-    }
-
-    private parseDefaultNodeType() {
-        let nt: string = this.getOption("nodeType");
-        if (nt == undefined)
-            throw "l'option obligatoire 'nodeType' est absente du fichier de définition de formulaire";
-        this._defaultNodeType = ComputeNodeType[nt];
-    }
-
-    public parseConfig(config: {}) {
-        this._config = config;
-        this._dependencies = [];
-        this._defaultCalculatedParam = undefined;
-
-        this.parseDefaultNodeType();
-
-        for (let conf_index in this._config) {
-            let conf = this._config[conf_index];
-            let conf_id: string = conf["id"];
-
-            // field set
-            if (conf_id.startsWith("fs_")) {
-                this.parse_fieldset(conf, conf_id);
-            }
-            // options globales
-            else if (conf_id === "options") {
-                // id du paramètre à calculer par défaut
-
-                this._defaultCalculatedParam = conf["idCal"];
-                if (this._defaultCalculatedParam != undefined) {
-                    let p = this.getParamFromSymbol(this._defaultCalculatedParam);
-                    p.isDefault = true;
-                    p.radioState = ParamRadioConfig.CAL;
-                }
-
-                // id du SelectField configurant le type de section
-                this._sectionSelectFieldId = this.getOption("sectionSelectId");
-            }
-        }
-
-        // logObject(this._fieldSets, "fieldsets");
-        // logObject(this._dependencies, "dependences");
-    }
-
-    private getDependencyFromMasterValue(v: any): Dependency {
-        for (let d of this._dependencies)
-            if (d.masterCondition.type == DependencyConditionType.HasValue) {
-                let mv = (<ValueDependencyCondition>d.masterCondition).value;
-                if (mv === v)
-                    return d;
-            }
-        return undefined;
-    }
-
-    private isNumber(s: string): boolean {
-        return Number(s) != NaN;
-    }
-
-    private prepareExistenceDependencies() {
-        // si des FormulaireElement sont présents dans des ExistenceDependency, on met leur membre isDisplayed à false
-
-        for (let d of this.dependencies)
-            if (d instanceof ExistenceDependency) {
-                let slave: FormulaireElement = this.getFormulaireElementById(d.slaveElement.id);
-                slave.isDisplayed = false;
-            }
-    }
-
-    public applyDependencies() {
-        this.prepareExistenceDependencies();
-
-        for (let d of this.dependencies) {
-            let master: FormulaireElement = this.getFormulaireElementById(d.masterElement.id);
-            if (d instanceof ExistenceDependency) {
-                let slave: FormulaireElement = this.getFormulaireElementById(d.slaveElement.id);
-                slave.isDisplayed = slave.isDisplayed || master.verifiesDependency(d);
-            }
-            else if (d instanceof ValueDependency) {
-                let vd = <ValueDependency>d;
-                /*
-                let master: HTMLElement = this.getHtmlElementFromId(d.masterElement.id);
-                if (this.getHtmlElementValue(master) == vd.masterValue) {
-                    let slave: HTMLElement = this.getHtmlElementFromId(d.slaveElement.id);
-                    this.setHtmlElementValue(slave, vd.slaveValue);
-                }
-                */
-                if (master.verifiesDependency(d)) {
-                    let slave = this.getFieldById(d.slaveElement.id);
-                    if (this.isNumber(vd.slaveValue))
-                        slave.setValue(+vd.slaveValue);
-                    else
-                        slave.setValue(vd.slaveValue);
-                }
-            }
-        }
-    }
-
-    private isMySectionType(nodeType: ComputeNodeType, strict: boolean) {
-        let res: boolean = nodeType == this._nodeType;
-        if (strict)
-            return res;
-
-        return res || nodeType == ComputeNodeType.SectionParametree
-    }
-
-    private addFixedResults(displaySymbol: boolean) {
-        for (let p of this.getInputParameters())
-            if (p.radioState == ParamRadioConfig.FIX && p.symbol !== "Pr")
-                this._fixVarResults.addFixedResultElement(p, p.getValue(), displaySymbol);
-    }
-
-    private addSectionFixedResult(val: ResultElement, label: string, drawLabel: string = undefined) {
-        this._sectionResults.addResult(val, label, drawLabel);
-    }
-
-    private addSectionFixedResults(displaySymbol: boolean) {
-        for (let p of this.getDisplayedInputParameters())
-            if (p.radioState == ParamRadioConfig.FIX && this.isMySectionType(p.computeNodeType, false) && p.symbol !== "Pr")
-                this._fixVarResults.addFixedResultElement(p, p.getValue(), displaySymbol);
-    }
-
-    private getSectionVariatedParameter(): NgParameter {
-        let res: NgParameter = this.getParamFromState(ParamRadioConfig.VAR);
-        if (res != undefined && this.isMySectionType(res.computeNodeType, false))
-            return res;
-        return undefined;
-    }
-
-    private doComputeSectionVar(varParam: NgParameter) {
-        let computePrec: number = this.getParameterValue("Pr"); // précision de calcul
-        let nDigits = -Math.log10(computePrec);
-
-        this.addSectionFixedResults(false);
-
-        let computedParam = this.getSectionComputedParam();
-
-        this._fixVarResults.setVariableParamHeaderFromParameter(varParam, false);
-        this._fixVarResults.setVariableResultHeader(computedParam["label"]);
-
-        let nodeType: ComputeNodeType = this.getNodeTypeFromSelectField(CalculatorType.SectionParametree);
-
-        var np: [acSection, ParamsEquation] = this.getSectionNubAndParameters(nodeType);
-        let sect: acSection = np[0];
-        let prms: ParamsEquation = np[1];
-
-        this._sectionResults.section = sect;
-
-        let min: number = +varParam.minValue;
-        let max: number = +varParam.maxValue;
-        let step: number = +varParam.stepValue;
-
-        let yField: InputField = <InputField>this.getFormulaireElementById("Y");
-        let Y: number = yField.getValue();
-
-        let compSymbol = computedParam["symbol"];
-        for (let val = min; val <= max; val += step) {
-            prms[varParam.symbol].v = val;
-
-            sect.Reset(true);
-            let res = sect.Calc(compSymbol, Y);
-            this._fixVarResults.addVarResultElement(val, res.result);
-        }
-
-        this._fixVarResults.graphTitle = computedParam.symbol + " = f( " + varParam.symbol + " )";
-    }
-
-    private doComputeSection() {
-        let varParam = this.getSectionVariatedParameter();
-        if (varParam != undefined) {
-            this.doComputeSectionVar(varParam);
-            return;
-        }
-
-        let nodeType: ComputeNodeType = this.getNodeTypeFromSelectField(CalculatorType.SectionParametree);
-        var np: [acSection, ParamsEquation] = this.getSectionNubAndParameters(nodeType);
-
-        let sect: acSection = np[0];
-        let prms: ParamsEquation = np[1];
-
-        this._sectionResults.section = sect;
-
-        let computePrec: number = this.getNodeParameterValue(ComputeNodeType.SectionParametree, "Pr"); // précision de calcul
-        let nDigits = -Math.log10(computePrec);
-
-        let Y = prms.map.Y.v; // tirant d'eau original (doit être fourni à acSection.Calc() sous peine d'être modifié par les appels successifs car c'est en même temps un paramètre et une variable temporaire)
-
-        // charge spécifique
-        let Hs = sect.Calc("Hs", Y);
-        this.addSectionFixedResult(Hs.result, this.intlService.localizeText("INFO_GRANDEUR_HS"), "Hs");
-
-        // charge critique
-        let Hsc = sect.Calc("Hsc", Y);
-        this.addSectionFixedResult(Hsc.result, this.intlService.localizeText("INFO_GRANDEUR_HSC"), "Hsc");
-
-        // largeur au miroir
-        let B = sect.Calc("B", Y);
-        this.addSectionFixedResult(B.result, this.intlService.localizeText("INFO_GRANDEUR_B"));
-
-        // périmètre hydraulique
-        let P = sect.Calc("P", Y);
-        this.addSectionFixedResult(P.result, this.intlService.localizeText("INFO_GRANDEUR_P"));
-
-        // surface hydraulique
-        let S = sect.Calc("S", Y);
-        this.addSectionFixedResult(S.result, this.intlService.localizeText("INFO_GRANDEUR_S"));
-
-        // rayon hydraulique
-        let R = sect.Calc("R", Y);
-        this.addSectionFixedResult(R.result, this.intlService.localizeText("INFO_GRANDEUR_R"));
-
-        // vitesse moyenne
-        let V = sect.Calc("V", Y);
-        this.addSectionFixedResult(V.result, this.intlService.localizeText("INFO_GRANDEUR_V"));
-
-        // nombre de Froude
-        let Fr = sect.Calc("Fr", Y);
-        this.addSectionFixedResult(Fr.result, this.intlService.localizeText("INFO_GRANDEUR_FR"), );
-
-        // tirant d'eau critique
-        let Yc = sect.Calc("Yc", Y);
-        this.addSectionFixedResult(Yc.result, this.intlService.localizeText("INFO_GRANDEUR_YC"), "Yc");
-
-        // tirant d'eau normal
-        let Yn = sect.Calc("Yn", Y);
-        this.addSectionFixedResult(Yn.result, this.intlService.localizeText("INFO_GRANDEUR_YN"), "Yn");
-
-        // tirant d'eau fluvial
-        let Yf = sect.Calc("Yf", Y);
-        this.addSectionFixedResult(Yf.result, this.intlService.localizeText("INFO_GRANDEUR_YF"), "Yf");
-
-        // tirant d'eau torrentiel
-        let Yt = sect.Calc("Yt", Y);
-        this.addSectionFixedResult(Yt.result, this.intlService.localizeText("INFO_GRANDEUR_YT"), "Yt");
-
-        // tirant d'eau conjugué
-        let Yco = sect.Calc("Yco", Y);
-        this.addSectionFixedResult(Yco.result, this.intlService.localizeText("INFO_GRANDEUR_YCO"), "Yco");
-
-        // perte de charge
-        let J = sect.Calc("J", Y);
-        this.addSectionFixedResult(J.result, this.intlService.localizeText("INFO_GRANDEUR_J"));
-
-        // Variation linéaire de l'énergie spécifique
-        let IJ = sect.Calc("I-J", Y);
-        this.addSectionFixedResult(IJ.result, this.intlService.localizeText("INFO_GRANDEUR_I-J"));
-
-        // impulsion hydraulique
-        let Imp = sect.Calc("Imp", Y);
-        this.addSectionFixedResult(Imp.result, this.intlService.localizeText("INFO_GRANDEUR_IMP"));
-
-        // contrainte de cisaillement
-        let Tau0 = sect.Calc("Tau0", Y);
-        this.addSectionFixedResult(Tau0.result, this.intlService.localizeText("INFO_GRANDEUR_TAU0"));
-    }
-
-    private doComputeRemous() {
-        let nodeType: ComputeNodeType = this.getNodeTypeFromSelectField(CalculatorType.CourbeRemous);
-        var np: [acSection, ParamsEquation] = this.getSectionNubAndParameters(nodeType, false);
-
-        let sect: acSection = np[0];
-        let prmSect: ParamsEquation = np[1];
-
-        let Yamont: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "Yamont"); // tirant amont
-        let Yaval: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "Yaval"); // tirant aval
-        let Dx: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "Dx"); // pas de discrétisation
-        let Long: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "Long"); // longueur du bief
-        let If: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "If"); // pente du fond
-        let YB: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "YB"); // hauteur de berge
-        let Yn: Result = sect.Calc("Yn"); // hauteur normale
-        let Yc: Result = sect.Calc("Yc"); // hauteur critique
-
-        this._remousResults.penteFond = If;
-
-        // méthode de résolution
-
-        let msf: SelectField = <SelectField>this.getFormulaireElementById("select_resolution");
-        let smeth: string = msf.getValue();
-        let methRes: MethodeResolution;
-        if (smeth == "select_resolution_trap")
-            methRes = MethodeResolution.Trapezes;
-        else if (smeth == "select_resolution_rk4")
-            methRes = MethodeResolution.RungeKutta4;
-        else if (smeth == "select_resolution_euler")
-            methRes = MethodeResolution.EulerExplicite;
-        else
-            throw "GenericCalculatorComponent.doComputeRemous() : type de méthode de résolution '" + smeth + "' inconnu";
-
-        // paramètre supplémentaire à calculer
-
-        let dsf: SelectField = <SelectField>this.getFormulaireElementById("select_target");
-        let extraSymbol: string = this.removePrefix(dsf.getValue(), "select_target_");
-
-        // calcul
-
-        let prmCR: CourbeRemousParams = new CourbeRemousParams(sect, Yamont, Yaval, Long, Dx, methRes);
-        let cr = new CourbeRemous(prmCR);
-        let res: Result = cr.calculRemous(extraSymbol == "none" ? undefined : extraSymbol);
-
-        // affichage du graphe
-
-        this._remousResults.hauteurBerge = new ResultElement(YB);
-        this._remousResults.hauteurNormale = Yn.result;
-        this._remousResults.hauteurCritique = Yc.result;
-        if (extraSymbol != "none") {
-            this._remousResults.extraParamLabel = dsf.selectedEntry.label;
-            this._remousResults.extraGraph = ["Hs", "Hsc", "Yf", "Yt", "Yco"].indexOf(extraSymbol) == -1;
-        }
-        else
-            this._remousResults.extraGraph = false;
-
-        // résultats numériques
-
-        this._remousResults.addResult(res);
-    }
-
-    private getVariatedParameter(): NgParameter {
-        return this.getParamFromState(ParamRadioConfig.VAR);
-    }
-
-    private getComputedParameter(): NgParameter {
-        return this.getParamFromState(ParamRadioConfig.CAL);
-    }
-
-    private getNubAndParameters(): [Nub, ParamsEquation] {
-        switch (this.calculatorType) {
-            case CalculatorType.ConduiteDistributrice:
-                {
-                    let Q: number = this.getParameterValue("Q"); // débit Q
-                    let D: number = this.getParameterValue("D"); // diamètre D
-                    let J: number = this.getParameterValue("J"); // perte de charge J
-                    let Lg: number = this.getParameterValue("Lg"); // Longueur de la conduite Lg
-                    let Nu: number = this.getParameterValue("Nu"); // viscosité dynamique 
-                    let prms = new ConduiteDistribParams(Q, D, J, Lg, Nu);
-                    let nub = new ConduiteDistrib(prms);
-                    return [nub, prms];
-                }
-
-            case CalculatorType.LechaptCalmon:
-                {
-                    let Q: number = this.getParameterValue("Q"); // débit Q
-                    let D: number = this.getParameterValue("D"); // diamètre D
-                    let J: number = this.getParameterValue("J"); // perte de charge J
-                    let Lg: number = this.getParameterValue("Lg"); // Longueur de la conduite Lg
-                    let L: number = this.getParameterValue("L"); // paramètre de matériau 1
-                    let M: number = this.getParameterValue("M"); // paramètre de matériau 2
-                    let N: number = this.getParameterValue("N"); // paramètre de matériau 3
-                    let prms = new LechaptCalmonParams(Q, D, J, Lg, L, M, N);
-                    let nub = new LechaptCalmon(prms);
-                    return [nub, prms];
-                }
-
-            case CalculatorType.PabDimensions:
-                {
-                    let L: number = this.getParameterValue("L"); // longueur L
-                    let W: number = this.getParameterValue("W"); // largeur W
-                    let Y: number = this.getParameterValue("Y"); // tirant d'eau Y
-                    let V: number = this.getParameterValue("V"); // volume V
-                    let prms = new PabDimensionParams(L, W, Y, V);
-                    let nub = new PabDimension(prms); // pour initialiser la calculabilité des paramètres
-                    return [nub, prms];
-                }
-
-            case CalculatorType.PabPuissance:
-                {
-                    let DH: number = this.getParameterValue("DH");  // Chute entre bassins
-                    let Q: number = this.getParameterValue("Q"); // Débit 
-                    let V: number = this.getParameterValue("V"); // volume V
-                    let Pv: number = this.getParameterValue("Pv"); // puissance dissipée
-                    let prms = new PabPuissanceParams(DH, Q, V, Pv);
-                    let nub = new PabPuissance(prms); // pour initialiser la calculabilité des paramètres
-                    return [nub, prms];
-                }
-
-            default:
-                throw "FormulaireService.getNubAndParameters() : valeur de CalculatorType " + this.calculatorType + " non implémentée"
-        }
-    }
-
-    private getSectionNubAndParameters(nt: ComputeNodeType, getY: boolean = true): [acSection, ParamsEquation] {
-        // bief
-        let Ks = this.getParameterValue("Ks"); // Strickler
-        let If: number = this.getParameterValue("If"); // Pente du fond
-        let YB: number = this.getParameterValue("YB"); // Hauteur de berge
-
-        // caractéristiques hydro
-        let Q: number = this.getParameterValue("Q"); // débit Q
-        if (getY)
-            var Y: number = this.getParameterValue("Y"); // tirant d'eau
-        else
-            Y = undefined;
-
-        let Prec = this.getParameterValue("Pr"); // précision calcul/affichage
-
-        // ??
-        // let YCL: number = f.getParameterValue("YCL"); // Condition limite en cote à l'amont ou à l'aval
-        // let Dx: number = f.getParameterValue("Dx"); // Pas d'espace (positif en partant de l'aval, négatif en partant de l'amont)
-        // let Long: number = f.getParameterValue("Long"); // Longueur du bief
-
-        switch (nt) {
-            case ComputeNodeType.SectionTrapeze:
-            case ComputeNodeType.RegimeUniformeTrapeze:
-            case ComputeNodeType.CourbeRemousTrapeze:
-                {
-                    let LargeurFond = this.getNodeParameterValue(nt, "LargeurFond"); // Largeur au fond
-                    let Fruit = this.getNodeParameterValue(nt, "Fruit"); // Fruit des berges
-                    let prms = new ParamsSectionTrapez(LargeurFond, Fruit, Y, Ks, Q, If, Prec, YB);
-                    let cn = new cSnTrapez(prms);
-                    cn.newtonMaxIter = this.appSetupService.newtonMaxIter;
-                    return [cn, prms];
-                }
-
-            case ComputeNodeType.SectionRectangle:
-            case ComputeNodeType.RegimeUniformeRectangle:
-            case ComputeNodeType.CourbeRemousRectangle:
-                {
-                    let LargeurFond = this.getNodeParameterValue(nt, "LargeurBerge"); // Largeur au fond
-                    let prms = new ParamsSectionRectang(Y, LargeurFond, Ks, Q, If, Prec, YB);
-                    let cn = new cSnRectang(prms);
-                    cn.newtonMaxIter = this.appSetupService.newtonMaxIter;
-                    return [cn, prms];
-                }
-
-            case ComputeNodeType.SectionCercle:
-            case ComputeNodeType.RegimeUniformeCercle:
-            case ComputeNodeType.CourbeRemousCercle:
-                {
-                    let D = this.getNodeParameterValue(nt, "D"); // Largeur au fond
-                    let prms = new ParamsSectionCirc(D, Y, Ks, Q, If, Prec, YB);
-                    let cn = new cSnCirc(prms);
-                    cn.newtonMaxIter = this.appSetupService.newtonMaxIter;
-                    return [cn, prms];
-                }
-
-            case ComputeNodeType.SectionPuissance:
-            case ComputeNodeType.RegimeUniformePuissance:
-            case ComputeNodeType.CourbeRemousPuissance:
-                {
-                    let k = this.getNodeParameterValue(nt, "k"); // coefficient
-                    let LargeurBerge = this.getNodeParameterValue(nt, "LargeurBerge"); // Largeur au niveau des berges
-                    let prms = new ParamsSectionPuiss(k, Y, LargeurBerge, Ks, Q, If, Prec, YB);
-                    let cn = new cSnPuiss(prms);
-                    cn.newtonMaxIter = this.appSetupService.newtonMaxIter;
-                    return [cn, prms];
-                }
-
-            default:
-                throw "FormulaireDefinition.getSectionNubAndParameters() : valeur de ComputeNodeType " + nt + " non implémentée"
-        }
-    }
-
-    /**
-     * lance le calcul d'un paramètre en déterminant une valeur initiale
-     */
-    private runNubCalc(nub: Nub, p: NgParameter, prec: number): Result {
-        let init: number;
-        switch (p.domain.domain) {
-            case ParamDomainValue.ANY:
-            case ParamDomainValue.POS_NULL:
-                init = 0;
-                break;
-
-            case ParamDomainValue.INTERVAL:
-                init = p.domain.minValue;
-                break;
-
-            case ParamDomainValue.NOT_NULL:
-            case ParamDomainValue.POS:
-                init = 1e-8;
-                break;
-        }
-        if (prec == undefined)
-            return nub.Calc(p.symbol, init);
-
-        return nub.Calc(p.symbol, init, prec);
-    }
-
-    private doComputeFixedVar() {
-        let np: [Nub, ParamsEquation];
-        let nub: Nub;
-        let prms: ParamsEquation;
-        let rg: boolean = this.calculatorType == CalculatorType.RegimeUniforme;
-        if (rg) {
-            let snp: [acSection, ParamsEquation] = this.getSectionNubAndParameters(this._nodeType);
-            nub = new RegimeUniforme(snp[0]);
-            prms = snp[1];
-        }
-        else {
-            np = this.getNubAndParameters();
-            nub = np[0];
-            prms = np[1];
-        }
-
-        if (this.hasParameter("Pr"))
-            var computePrec: number = this.getParameterValue("Pr"); // précision de calcul
-
-        let computedParam: NgParameter = this.getComputedParameter();
-
-        let varParam: NgParameter = this.getVariatedParameter();
-        if (varParam == undefined) {
-            // pas de paramètre à varier
-
-            let res: Result = this.runNubCalc(nub, computedParam, computePrec);
-            if (res.ok) {
-                this.addFixedResults(!rg);
-                this._fixVarResults.addFixedResultElement(computedParam, res.result, !rg);
-            }
-            else {
-                this._fixVarResults.addLogMessages(res.log);
-            }
-        }
-        else {
-            // il y a un paramètre à varier
-
-            this.addFixedResults(!rg);
-            this._fixVarResults.setVariableParamHeaderFromParameter(varParam, !rg);
-            this._fixVarResults.setVariableResultHeaderFromParameter(computedParam);
-
-            switch (varParam.valueMode) {
-                case ParamValueMode.MINMAX:
-                    this._fixVarResults.graphType = GraphType.Scatter;
-
-                    let min: number = +varParam.minValue;
-                    let max: number = +varParam.maxValue;
-                    let step: number = +varParam.stepValue;
-
-                    for (let val = min; val <= max; val += step) {
-                        prms[varParam.symbol].v = val;
-
-                        let res: Result = this.runNubCalc(nub, computedParam, computePrec);
-                        if (res.ok) {
-                            this._fixVarResults.addVarResultElement(val, res.result);
-                        }
-                        else {
-                            this._fixVarResults.addLogMessages(res.log);
-                        }
-                    }
-                    break;
-
-                case ParamValueMode.LISTE:
-                    this._fixVarResults.graphType = GraphType.Histogram;
-
-                    for (let val of varParam.valueList) {
-                        prms[varParam.symbol].v = val;
-
-                        let res: Result = this.runNubCalc(nub, computedParam, computePrec);
-                        if (res.ok) {
-                            this._fixVarResults.addVarResultElement(val, res.result);
-                        }
-                        else {
-                            this._fixVarResults.addLogMessages(res.log);
-                        }
-                    }
-
-                    break;
-            }
-
-            this._fixVarResults.graphTitle = computedParam.symbol + " = f( " + varParam.symbol + " )";
-        }
-    }
-
-    public doCompute() {
-        this.resetResults();
-
-        switch (this.calculatorType) {
-            case CalculatorType.SectionParametree:
-                this.doComputeSection();
-                break;
-
-            case CalculatorType.CourbeRemous:
-                this.doComputeRemous();
-                break;
-
-            default:
-                this.doComputeFixedVar();
-                break;
-        }
-
-        this.notifyObservers({
-            "action": "resultsUpdated",
-        });
-    }
-
-    public printDependencies() {
-        for (let d of this.dependencies)
-            console.log(d.toString());
-    }
-
-    public isDisplayed(id: string) {
-        return this.getFormulaireElementById(id).isDisplayed;
-    }
-
-    public get hasFixVarResults(): boolean {
-        return this._fixVarResults.hasResults;
-    }
-
-    public get hasSectionResults(): boolean {
-        return this._sectionResults.hasResults;
-    }
-
-    public get hasRemousResults(): boolean {
-        return this._remousResults.hasResults;
-    }
-
-    public get hasResults(): boolean {
-        return this.hasFixVarResults || this.hasSectionResults || this.hasRemousResults;
-    }
-
-    public updateLocalisation(localisation: StringMap) {
-        for (let loc_id in localisation) {
-            for (let fs of this._fieldSets) {
-                if (fs.id == loc_id)
-                    fs.updateLocalisation(localisation);
-
-                for (let p of fs.fields)
-                    if (p.id === loc_id)
-                        p.updateLocalisation(localisation);
-            }
-        }
-
-        if (this.hasResults)
-            this.doCompute(); // pour mettre à jour la langue
-    }
-
-    public get isValid(): boolean {
-        let res: boolean = true;
-        for (let fs of this._fieldSets)
-            res = res && fs.isValid;
-        return res;
-    }
-
-    public get calculatorName() {
-        return this._calculatorName;
-    }
-
-    public set calculatorName(n: string) {
-        this._calculatorName = n;
-        this.notifyObservers({
-            "action": "nameChanged",
-            "name": n
-        });
-    }
-}
diff --git a/src/app/formulaire/formulaire-element.ts b/src/app/formulaire/formulaire-element.ts
index bc1ee5f95a0723463cc520f1a60b6a1c47c30c44..318eb5b6715f88bca0102d93262223ef7ad76b7a 100644
--- a/src/app/formulaire/formulaire-element.ts
+++ b/src/app/formulaire/formulaire-element.ts
@@ -1,54 +1,240 @@
-import { ComputeNodeType } from "jalhyd";
-
-import { Dependency } from "./dependency"
-import { DependencyConditionType } from "./dependency-condition"
+import { FormulaireNode } from "./formulaire-node"
 import { StringMap } from "../stringmap";
+import { Dependency } from "./dependency/dependency";
+import { DependencyCondition, DependencyConditionType } from "./dependency/dependency-condition";
+import { ValueDependency } from "./dependency/value-dependency";
+import { ValueDependencyCondition } from "./dependency/value-dependency-condition";
+import { ExistenceDependency } from "./dependency/existence-dependency";
+import { FormulaireDefinition } from "./definition/form-definition";
+import { DeepFormulaireElementIterator } from "./form-iterator/deep-element-iterator";
 
+/** 
+ * élément (enfant) du formulaire : fieldset, input, container, ...
+ */
+export abstract class FormulaireElement extends FormulaireNode {
+    /**
+     * affiché ?
+     */
+    private _isDisplayed: boolean;
 
-export abstract class FormulaireElement {
-    private _nodeType: ComputeNodeType;
-    private _id: string;
-    private _formId: number;
-    public isDisplayed: boolean;
+    /**
+     * étiquette affichée dans l'UI
+     */
     private _label: string;
 
-    constructor(nodeType: ComputeNodeType, id: string, formId: number) {
-        this._nodeType = nodeType;
-        this._id = id;
-        this.isDisplayed = true;
-        this._formId = formId;
-    }
+    protected _dependencies: Dependency[] = [];
 
-    get computeNodeType(): ComputeNodeType {
-        return this._nodeType;
+    constructor(isTmpl: boolean = false) {
+        super(isTmpl);
+        this._isDisplayed = true;
     }
 
-    get id(): string {
-        return this._id;
+    get isDisplayed(): boolean {
+        return this._isDisplayed;
     }
 
-    get formId(): number {
-        return this._formId;
+    set isDisplayed(b: boolean) {
+        this._isDisplayed = b;
+        for (const k of this.getKids())
+            k.isDisplayed = b;
     }
 
     get label(): string {
         return this._label;
     }
 
+    public getKids(): FormulaireElement[] {
+        return super.kids as FormulaireElement[];
+    }
+
+    private isNumber(s: string): boolean {
+        return Number(s) != NaN;
+    }
+
+    private parse_existence_dependencies(json: {}, parentForm: FormulaireDefinition) {
+        for (let di in json) {
+            let d = json[di];
+            let masterField: FormulaireElement = parentForm.getFormulaireNodeById(d["refid"]) as FormulaireElement;
+            if (masterField != undefined) {
+                let rv = d["refvalue"];
+                if (rv != undefined)
+                    var mc: DependencyCondition = new ValueDependencyCondition(rv);
+                else {
+                    let cond = d["cond"];
+                    if (cond != undefined) {
+                        switch (cond) {
+                            case "isvar":
+                                var mc = new DependencyCondition(DependencyConditionType.IsVariable);
+                                break;
+
+                            case "isdisp":
+                                var mc = new DependencyCondition(DependencyConditionType.IsDisplayed);
+                                break;
+
+                            default:
+                                throw "Formulaire.parse_existence_dependencies() : type de condition '" + cond + "' non pris en charge";
+                        }
+                    }
+                    else
+                        throw "Formulaire.parse_existence_dependencies() : infos de dépendance manquantes/non prises en charge";
+                }
+                let dep = new ExistenceDependency(masterField, mc);
+                this._dependencies.push(dep);
+            }
+            else
+                throw new Error(`la dépendance d'existence de '${this.id}' fait référence à un élément inconnu '${d["refid"]}'`);
+        }
+    }
+
+    public parseDependencies(json: {}, parentForm: FormulaireDefinition) {
+        const dep = json["dep_exist"];
+        if (dep != undefined)
+            this.parse_existence_dependencies(dep, parentForm);
+    }
+
     protected abstract verifyDependency(d: Dependency): boolean;
 
     public verifiesDependency(d: Dependency): boolean {
         if (d.masterCondition.type == DependencyConditionType.IsDisplayed)
-            return this.isDisplayed;
+            return this._isDisplayed;
 
         return this.verifyDependency(d);
     }
 
+    private getDependencyFromMasterValue(v: any): Dependency {
+        for (let d of this._dependencies)
+            if (d.masterCondition.type == DependencyConditionType.HasValue) {
+                let mv = (<ValueDependencyCondition>d.masterCondition).value;
+                if (mv === v)
+                    return d;
+            }
+        return undefined;
+    }
+
+    private prepareExistenceDependencies() {
+        // si des FormulaireElement sont présents dans des ExistenceDependency, on met leur membre isDisplayed à false
+
+        for (let d of this._dependencies)
+            if (d instanceof ExistenceDependency) {
+                this.isDisplayed = false;
+                break;
+            }
+    }
+
+    public applyDependencies(parentForm: FormulaireDefinition) {
+        this.prepareExistenceDependencies();
+
+        for (let d of this._dependencies) {
+            let master: FormulaireElement = d.masterElement;
+            if (d instanceof ExistenceDependency) {
+                let slave: FormulaireElement = this;
+                slave.isDisplayed = slave._isDisplayed || master.verifiesDependency(d);
+            }
+            else if (d instanceof ValueDependency) {
+                let vd = <ValueDependency>d;
+                /*
+                let master: HTMLElement = this.getHtmlElementFromId(d.masterElement.id);
+                if (this.getHtmlElementValue(master) == vd.masterValue) {
+                    let slave: HTMLElement = this.getHtmlElementFromId(d.slaveElement.id);
+                    this.setHtmlElementValue(slave, vd.slaveValue);
+                }
+                */
+                if (master.verifiesDependency(d)) {
+                    let slave = parentForm.getFieldById(this.id);
+                    if (this.isNumber(vd.slaveValue))
+                        slave.setValue(this, +vd.slaveValue);
+                    else
+                        slave.setValue(this, vd.slaveValue);
+                }
+            }
+        }
+
+        for (const k of this.getKids())
+            k.applyDependencies(parentForm);
+    }
+
+    public printDependencies() {
+        for (let d of this._dependencies)
+            console.log(d.toString());
+    }
+
+    /**
+     * copie les dépendances de l'élément "this" vers un autre
+     * @param dest élément vers lequel copier les dépendances
+     * @param root élément racine instancié si on copie des dépendances de template
+     */
+    public copyDependencies(dest: FormulaireElement, root: FormulaireElement) {
+        if (dest.isTemplate || dest._dependencies.length != 0 || root.isTemplate || this.id !== dest.id)
+            throw new Error("copyDependencies() : erreur de cohérence (1)");
+
+        // copie des dépendances
+        // à priori, elles ne sont qu'avec des FormulaireElement en dehors du FieldsetContainer
+        // ou avec des FormulaireElement du même FieldSet
+        // cad pas avec des FormulaireElement dans des FieldSet dans le même FieldsetContainer
+
+        for (const d of this._dependencies) {
+            // si l'élément esclave est un template
+            if (this.isTemplate) {
+                // élément maître original
+                const ome: FormulaireElement = d.masterElement;
+                if (!ome.isTemplate)
+                    throw new Error("copyDependencies() : erreur de cohérence (2)"); // la dépendance doit être entre éléments templates
+
+                // on recherche l'instanciation de l'élément maître original dans l'élement racine
+                let nme = root.getFormulaireNodeById(ome.id) as FormulaireElement;
+                if (nme === undefined)
+                    throw new Error("copyDependencies() : correspondance non trouvée");
+
+                // copie de la dépendance
+
+                const nd = d.clone(nme);
+                dest._dependencies.push(nd);
+            }
+            else
+                // pas de template impliqué, on réutilise la dépendance    
+                dest._dependencies.push(d);
+        }
+
+        for (const k of this.kids) {
+            const destKid = dest.getFormulaireNodeById(k.id) as FormulaireElement;
+            (k as FormulaireElement).copyDependencies(destKid, root);
+        }
+    }
+
     public updateLocalisation(loc: StringMap) {
         this._label = loc[this.id];
+        if (this._label == undefined)
+            console.log(`WARNING : pas de traduction pour l'id ${this.id}`);
+
+        for (let f of this.getKids())
+            f.updateLocalisation(loc);
+    }
+
+    /**
+     * copie des membres
+     */
+    protected copyMembers(n: FormulaireElement) {
+        super.copyTo(n);
+        n._isDisplayed = this._isDisplayed;
+        n._label = this._label;
     }
 
     public toString() {
-        return "id:" + this._id + (this.isDisplayed ? " displayed" : " NOT displayed") + " label:" + this.label;
+        return "id:" + this.id + (this._isDisplayed ? " displayed" : " NOT displayed") + " label:" + this.label;
+    }
+
+    public static removePrefix(s: string, prefix: string): string {
+        if (s.startsWith(prefix)) {
+            let l = prefix.length;
+            return s.substr(l, s.length - l);
+        }
+        return undefined;
+    }
+
+    /**
+     * itère sur tous les FormulaireElement
+     */
+    public get allFormElements(): IterableIterator<FormulaireElement> {
+        return new DeepFormulaireElementIterator(this);
     }
 }
diff --git a/src/app/formulaire/formulaire-node.ts b/src/app/formulaire/formulaire-node.ts
new file mode 100644
index 0000000000000000000000000000000000000000..285e8917d43581a44362ed7f74f9a4456ea7e1ef
--- /dev/null
+++ b/src/app/formulaire/formulaire-node.ts
@@ -0,0 +1,210 @@
+import { JalhydObject } from "jalhyd"
+
+import { FormulaireDefinition } from "./definition/form-definition";
+import { DeepFormulaireNodeIterator } from "./form-iterator/deep-node-iterator";
+import { IObservable, Observer, Observable } from "../services/observer";
+
+/**
+ * représentation sous forme d'arbre du formulaire et de ses éléments
+ */
+export abstract class FormulaireNode implements IObservable {
+    /**
+     * identifiant dans le fichier de conf
+     */
+    protected _confId: string;
+
+    /**
+    * id numérique unique
+    */
+    private _uid: number;
+
+    /**
+     * enfants (utilisé entre autres pour FormulaireDefinition, FieldSet et FieldsetContainer)
+     */
+    private _kids: FormulaireNode[];
+
+    /**
+     * true si fait partie d'un template
+     */
+    private _isTemplate: boolean;
+
+    /**
+    * élément d'origine si this est issu d'un template
+    */
+    private _fromTemplate: FormulaireNode;
+
+    /**
+     * implémentation par délégation de IObservable
+     */
+    private _observable: Observable;
+
+    constructor(isTmpl: boolean = false) {
+        this._kids = [];
+        this._uid = JalhydObject.nextUID;
+        this._isTemplate = isTmpl;
+        this._observable = new Observable()
+    }
+
+    get id(): string {
+        return this._confId;
+    }
+
+    public get kids(): FormulaireNode[] {
+        return this._kids;
+    }
+
+    public get isTemplate(): boolean {
+        return this._isTemplate;
+    }
+
+    public get fromTemplate(): FormulaireNode {
+        return this._fromTemplate;
+    }
+
+    public get uid() {
+        return this._uid;
+    }
+
+    protected copyTo(n: FormulaireNode) {
+        if (this._isTemplate) {
+            n._isTemplate = false;
+            n._fromTemplate = this;
+        }
+
+        n._confId = this._confId;
+        for (const k of this._kids) {
+            const nk = k.clone();
+            k.copyMembers(nk);
+            n._kids.push(nk);
+        }
+    }
+
+    /**
+     * obtient une copie en profondeur
+     */
+    public getDeepClone(): FormulaireNode {
+        const res = this.clone();
+        this.copyMembers(res);
+        return res;
+    }
+
+    /**
+     * cherche un FormulaireNode par son id de conf
+     */
+    public getFormulaireNodeById(id: string): FormulaireNode {
+        if (this.id === id)
+            return this;
+
+        for (const k of this._kids) {
+            const res = k.getFormulaireNodeById(id);
+            if (res !== undefined)
+                return res;
+        }
+
+        return undefined;
+    }
+
+    /**
+     * cherche un FormulaireNode par son id numérique unique
+     */
+    public getFormulaireNodeByUid(uid: number): FormulaireNode {
+        if (this.uid == uid)
+            return this;
+
+        for (const k of this._kids) {
+            const res = k.getFormulaireNodeByUid(uid);
+            if (res !== undefined)
+                return res;
+        }
+
+        return undefined;
+    }
+
+    private hasDirectKid(n: FormulaireNode): boolean {
+        for (const k of this._kids)
+            if (k._uid == n._uid)
+                return true;
+        return false;
+    }
+
+    /**
+     * retourne le parent direct de this
+     * @param root FormulaireNode racine dont on part pour rechercher le parent
+     */
+    public getDirectParent(root: FormulaireNode): FormulaireNode {
+        if (root.hasDirectKid(this))
+            return root;
+
+        const it = new DeepFormulaireNodeIterator(root);
+        for (const n of it)
+            if (n.hasDirectKid(this))
+                return n;
+        return undefined;
+    }
+
+    /**
+     * retourne l'indice d'un FormulaireNode enfant dans la liste des enfants
+     * @param kid FormulaireNode enfant dont on cherche l'indice
+     * @return -1 si non trouvé, indice commençant à 0 sinon
+     */
+    private kidIndex(kid: FormulaireNode): number {
+        let n = 0;
+        for (const k of this._kids) {
+            if (k._uid == kid._uid)
+                return n;
+            n++;
+        }
+
+        return -1;
+    }
+
+    /**
+     * retourne l'indice de this dans la liste des enfants de son parent
+     * (cad rang par rapport à ses frères)
+     * @param root FormulaireNode racine dont on part pour rechercher le parent
+     */
+    public indexAsKid(root: FormulaireNode): number {
+        // parent de this
+        const p = this.getDirectParent(root);
+
+        // indice
+        return p.kidIndex(this);
+    }
+
+    /**
+     * crée une nouvelle instance (appel simple du constructeur)
+     */
+    protected abstract clone(): FormulaireNode;
+
+    /**
+     * copie des membres
+     */
+    protected abstract copyMembers(n: FormulaireNode);
+
+    public abstract parseConfig(json: {}, data?: {});
+
+    public abstract parseDependencies(json: {}, parentForm: FormulaireDefinition);
+
+    // interface IObservable
+
+    /**
+     * ajoute un observateur à la liste
+     */
+    public addObserver(o: Observer) {
+        this._observable.addObserver(o);
+    }
+
+    /**
+     * supprime un observateur de la liste
+     */
+    public removeObserver(o: Observer) {
+        this._observable.removeObserver(o);
+    }
+
+    /**
+     * notifie un événement aux observateurs
+     */
+    notifyObservers(data: any, sender?: any) {
+        this._observable.notifyObservers(data, sender);
+    }
+}
diff --git a/src/app/formulaire/input-field.ts b/src/app/formulaire/input-field.ts
index 414e2e6ea92619fee6b656598bab450019ae1e7e..fa0110d526609698f7f16c9bb37d7c38d3d901bc 100644
--- a/src/app/formulaire/input-field.ts
+++ b/src/app/formulaire/input-field.ts
@@ -1,20 +1,19 @@
-import { ComputeNodeType } from "jalhyd";
-
-import { Field, FieldType } from "./field"
+import { Field } from "./field"
+import { FormulaireDefinition } from "./definition/form-definition";
 
 
 export abstract class InputField extends Field {
     private _value: any;
 
-    constructor(type: ComputeNodeType, id: string, formId: number) {
-        super(type, id, FieldType.Input, formId);
+    constructor(isTmpl = false) {
+        super(isTmpl);
     }
 
     public getValue() {
         return this._value;
     }
 
-    public setValue(val: any) {
+    public setValue(sender: any, val: any) {
         this._value = val;
     }
 }
diff --git a/src/app/formulaire/ngparam.ts b/src/app/formulaire/ngparam.ts
index dd7fb6554cfbcd3e01fae5d742329d07252e28f6..f495294f06199b2fe1b46178e86c4bc0d7264127 100644
--- a/src/app/formulaire/ngparam.ts
+++ b/src/app/formulaire/ngparam.ts
@@ -1,10 +1,11 @@
 import { ParamDefinition, Pair, ParamDomain } from "jalhyd";
 
 import { InputField } from "./input-field";
-import { Dependency } from "./dependency";
-import { DependencyConditionType } from "./dependency-condition";
-import { ValueDependencyCondition } from "./value-dependency-condition";
-import { Observable, IObservable, Observer } from "../services/observer";
+import { Dependency } from "./dependency/dependency";
+import { DependencyConditionType } from "./dependency/dependency-condition";
+import { ValueDependencyCondition } from "./dependency/value-dependency-condition";
+import { FormulaireDefinition } from "./definition/form-definition";
+import { ApplicationSetupService } from "../services/app-setup/app-setup.service";
 
 export enum ParamRadioConfig {
     /**
@@ -39,13 +40,25 @@ export enum ParamValueMode {
     LISTE
 }
 
+/**
+ * infos sur le contexte, les valeurs par défaut et saisie manuellement
+ */
+class Context {
+    public default: number;
+    public current: number;
+}
+
 /**
  * classe englobante de ParamDefinition (champs supplémentaires pour l'affichage, radio boutons, ...)
  */
-export class NgParameter extends InputField implements IObservable {
+export class NgParameter extends InputField {
     public unit: string;
     public radioConfig: ParamRadioConfig;
     public radioState: ParamRadioConfig;
+
+    /**
+     * true si ce paramètre est celui par défaut dans un formulaire (cf. fichier de conf des calculettes, objet "options", champ "idCal")
+     */
     public isDefault: boolean = false; // archi bug du langage ! si on relit cette propriété sans l'avoir modifiée entre-temps, elle vaut undefined !
 
     /**
@@ -74,14 +87,19 @@ export class NgParameter extends InputField implements IObservable {
     private _valueList: number[];
 
     /**
-     * implémentation par délégation de IObservable
+     * dictionnaire indiquant la valeur du paramètre dans différents contextes
+     * clé : contexte représenté par une chaîne
+     * valeur : instance de Context
      */
-    private _observable: Observable;
+    private _contexts: { [key: string]: Context } = {};
 
-    constructor(private _paramDef: ParamDefinition, formId: number) {
-        super(_paramDef.computeNodeType, _paramDef.symbol, formId);
-        this._observable = new Observable();
-        this._observable.sender = this;
+    /**
+     * id du contexte courant
+     */
+    public currentContextId: string;
+
+    constructor(private _paramDef: ParamDefinition, isTmpl = false) {
+        super(isTmpl);
     }
 
     get symbol(): string {
@@ -96,14 +114,105 @@ export class NgParameter extends InputField implements IObservable {
         return this._paramDef.v;
     }
 
-    public setValue(val: number) {
-        this._paramDef.v = val;
+    /**
+     * notification envoyée après la modification de la valeur du paramètre
+     */
+    private notifyValueModified(sender: any) {
         this.notifyObservers(
             {
-                "action": "value",
+                "action": "ngparamAfterValue",
+                "param": this,
+                "value": this._paramDef.v
+            }, sender
+        );
+    }
+
+    /**
+     * @return true si la valeur du paramètre a été modifiée manuellement dans un contexte donné
+     * @param contextId id du contexte
+     */
+    private isOverriden(contextId: string) {
+        const cnt = this._contexts[contextId];
+        if (cnt == undefined)
+            return false;
+        return cnt.current != undefined;
+    }
+
+    /**
+     * fixe la valeur du paramètre dans un contexte donné
+     * @param contextId id du contexte (par ex dans les ouvrages //, valeur de StructureType+LoiDebit)
+     * @param defaultValue valeur par défaut si la valeur du paramètre n'a pas été modifiée par setValue() depuis la création de l'objet
+     */
+    public resetValue(sender: any, contextId: string, defaultValue: number) {
+        if (!this.isOverriden(contextId)) {
+            this._paramDef.v = defaultValue;
+            this.setContextValue(contextId, defaultValue, false);
+        }
+        else
+            this._paramDef.v = this.getContextValue(contextId);
+        this.notifyValueModified(sender);
+    }
+
+    /**
+     * @return la valeur du paramètre dans un contexte donné
+     * @param contextId id du contexte
+     */
+    private getContextValue(contextId: string): number {
+        if (contextId == undefined)
+            return this._paramDef.v;
+
+        const cnt = this._contexts[contextId];
+        if (cnt == undefined)
+            return this._paramDef.v;
+
+        if (cnt.current != undefined)
+            return cnt.current;
+
+        return cnt.default;
+    }
+
+    /**
+     * fixe la valeur du paramètre dans un contexte donné
+     * @param contextId id du contexte
+     * @param val valeur du paramètre
+     * @param currentOrDefault true si on fixe la valeur modifiée à la main, false si valeur par défaut
+     */
+    private setContextValue(contextId: string, val: number, currentOrDefault: boolean) {
+        if (contextId != undefined) {
+            var cnt = this._contexts[contextId];
+
+            if (cnt == undefined)
+                cnt = new Context();
+
+            if (currentOrDefault)
+                cnt.current = val;
+            else
+                cnt.default = val;
+
+            this._contexts[contextId] = cnt;
+        }
+    }
+
+    /**
+     * fixe la valeur du paramètre.
+     * une notification préalable est envoyée pour laisser l'occasion aux objets liés de préciser le contexte
+     * dans lequel cette valeur existe
+     * @param sender 
+     * @param val 
+     */
+    public setValue(sender: any, val: number) {
+        // on laisse l'occasion de préciser le contexte
+        this.notifyObservers(
+            {
+                "action": "ngparamBeforeValue",
+                "param": this,
                 "value": val
-            }
+            }, sender
         )
+
+        this._paramDef.v = val;
+        this.setContextValue(this.currentContextId, val, true);
+        this.notifyValueModified(sender);
     }
 
     get isDefined(): boolean {
@@ -231,8 +340,8 @@ export class NgParameter extends InputField implements IObservable {
     }
 
     private get isValueValid(): boolean {
-        const v = this._paramDef.getValue();
         try {
+            const v = this._paramDef.getValue();
             this._paramDef.checkValue(v);
             return true;
         }
@@ -256,7 +365,7 @@ export class NgParameter extends InputField implements IObservable {
         throw "NgParameter.isValid() : valeur de ParamRadioConfig non prise en compte";
     }
 
-    public static getRadioConfig(s: string) {
+    private static getRadioConfig(s: string) {
         if (s == "fix")
             return ParamRadioConfig.FIX;
 
@@ -269,6 +378,23 @@ export class NgParameter extends InputField implements IObservable {
         throw "invalid parameter radio configuration " + s;
     }
 
+    public parseConfig(json: {}, data?: {}) {
+        const appSetupService: ApplicationSetupService = data["appSetupService"];
+        const radioConfig: string = data["radioConfig"];
+
+        this._confId = json["id"];
+        this.unit = json["unit"];
+        if (this.symbol == "Pr")
+            var val = appSetupService.computePrecision;
+        else
+            val = json["value"];
+        if (val != undefined)
+            this.setValue(this, +val);
+        this.radioConfig = NgParameter.getRadioConfig(radioConfig);
+        this.radioState = ParamRadioConfig.FIX;
+        this.isDefault = false; // malgré le fait qu'il soit initialisé dans la déclaration de la classe NgParam à false, quand on relit sa valeur, il vaut undefined (merci Microsoft)
+    }
+
     protected verifyDependency(d: Dependency): boolean {
         switch (d.masterCondition.type) {
             case DependencyConditionType.HasValue:
@@ -285,26 +411,27 @@ export class NgParameter extends InputField implements IObservable {
         }
     }
 
-    // interface IObservable
-
-    /**
-     * ajoute un observateur à la liste
-     */
-    public addObserver(o: Observer) {
-        this._observable.addObserver(o);
-    }
-
     /**
-     * supprime un observateur de la liste
+     * copie des membres
      */
-    public removeObserver(o: Observer) {
-        this._observable.removeObserver(o);
+    protected copyMembers(n: NgParameter) {
+        super.copyMembers(n);
+        n.unit = this.unit;
+        n.radioConfig = this.radioConfig;
+        n.radioState = this.radioState;
+        n.isDefault = this.isDefault;
+        n._valueMode = this.valueMode;
+        n._minValue = this._minValue;
+        n._maxValue = this._maxValue;
+        n._stepValue = this._stepValue;
+        if (this._valueList != undefined)
+            n._valueList = this._valueList.slice(0); // copie
     }
 
     /**
-     * notifie un événement aux observateurs
+     * crée une nouvelle instance
      */
-    public notifyObservers(data: any) {
-        this._observable.notifyObservers(data);
+    protected clone(): NgParameter {
+        return new NgParameter(this._paramDef.clone());
     }
 }
diff --git a/src/app/formulaire/select-field.ts b/src/app/formulaire/select-field.ts
index c0013a05ff8659cd8ceb379448e3aac219d8fdd6..65bfbfc9480416f38e37fbf7d3cbd82477819ca6 100644
--- a/src/app/formulaire/select-field.ts
+++ b/src/app/formulaire/select-field.ts
@@ -1,46 +1,43 @@
-import { ComputeNodeType } from "jalhyd";
-
 import { Field } from "./field";
 import { SelectEntry } from "./select-entry";
-import { FieldType } from "./field";
-import { Dependency } from "./dependency";
-import { DependencyConditionType } from "./dependency-condition";
-import { ValueDependencyCondition } from "./value-dependency-condition";
+import { Dependency } from "./dependency/dependency";
+import { DependencyConditionType } from "./dependency/dependency-condition";
+import { ValueDependencyCondition } from "./dependency/value-dependency-condition";
 import { StringMap } from "../stringmap";
-
+import { IObservable, Observable, Observer } from "../services/observer";
 
 export class SelectField extends Field {
     private _entries: SelectEntry[];
 
-    public selectedEntry: SelectEntry;
+    private _selectedEntry: SelectEntry;
 
-    public get entries() {
-        return this._entries;
+    constructor(isTmpl = false) {
+        super(isTmpl);
+        this._entries = [];
     }
 
-    constructor(nodeType: ComputeNodeType, id: string, formId: number) {
-        super(nodeType, id, FieldType.Select, formId);
-        this._entries = [];
+    public get entries() {
+        return this._entries;
     }
 
     public addEntry(e: SelectEntry) {
         this._entries.push(e);
-        if (this.selectedEntry == undefined)
-            this.selectedEntry = e;
+        if (this._selectedEntry == undefined)
+            this._selectedEntry = e;
     }
 
     public getValue() {
-        if (this.selectedEntry == undefined)
-            return undefined;
-        return this.selectedEntry.value;
+        return this._selectedEntry;
     }
 
-    public setValue(val: string) {
-        for (let e of this._entries)
-            if (e.value === val) {
-                this.selectedEntry = e;
-                return;
-            }
+    public setValue(v: SelectEntry) {
+        if (this._selectedEntry !== v) {
+            this._selectedEntry = v;
+            this.notifyObservers({
+                "action": "select",
+                "value": v
+            }, this);
+        }
     }
 
     public get isValid(): boolean {
@@ -48,16 +45,16 @@ export class SelectField extends Field {
     }
 
     public getLabel() {
-        if (this.selectedEntry == undefined)
+        if (this._selectedEntry == undefined)
             return undefined;
-        return this.selectedEntry.label;
+        return this._selectedEntry.label;
     }
 
     protected verifyDependency(d: Dependency): boolean {
         switch (d.masterCondition.type) {
             case DependencyConditionType.HasValue:
                 let mc: ValueDependencyCondition = <ValueDependencyCondition>d.masterCondition;
-                return this.selectedEntry.value === mc.value;
+                return this._selectedEntry.value === mc.value;
 
             default:
                 throw "SelectField.verifyDependency() : type de condition '" + DependencyConditionType[d.masterCondition.type] + "' non pris en charge";
@@ -71,4 +68,29 @@ export class SelectField extends Field {
             e.label = loc[e.value];
         }
     }
-}
\ No newline at end of file
+
+    /**
+     * copie des membres
+     */
+    protected copyMembers(n: SelectField) {
+        super.copyMembers(n);
+        for (const e of this._entries)
+            n.addEntry(e);
+    }
+
+    /**
+     * crée une nouvelle instance
+     */
+    protected clone(): SelectField {
+        return new SelectField();
+    }
+
+    public parseConfig(field: {}, data?: {}) {
+        this._confId = field["id"];
+        const values = field["select"];
+        for (const v of values) {
+            const e: SelectEntry = new SelectEntry(v["id"], undefined);
+            this.addEntry(e);
+        }
+    }
+}
diff --git a/src/app/formulaire/value-dependency.ts b/src/app/formulaire/value-dependency.ts
deleted file mode 100644
index bd02ee74ee4c3b8593e2e2495b244d8a423be8c4..0000000000000000000000000000000000000000
--- a/src/app/formulaire/value-dependency.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { Dependency } from "./dependency";
-import { FormulaireElement } from "./formulaire-element";
-import { ValueDependencyCondition } from "./value-dependency-condition";
-
-
-export class ValueDependency extends Dependency {
-    public slaveValue: any;
-
-    constructor(m: FormulaireElement, s: FormulaireElement, masterValue: any) {
-        super(m, s, new ValueDependencyCondition(masterValue));
-    }
-
-    public toString() {
-        return "valdep\n  " + super.toString() + "\n  slave val " + this.slaveValue;
-    }
-}
diff --git a/src/app/services/formulaire/formulaire.service.ts b/src/app/services/formulaire/formulaire.service.ts
index 5c313c89931a990fb48be6395c9543a0caf2cd6c..bcf25722b4aa52a6b839ec76dadd71f5f6395ad3 100644
--- a/src/app/services/formulaire/formulaire.service.ts
+++ b/src/app/services/formulaire/formulaire.service.ts
@@ -3,19 +3,27 @@ import { Response } from "@angular/http";
 import { Observable as rxObservable } from "rxjs/Observable";
 import "rxjs/add/operator/toPromise";
 
-import { EnumEx } from "jalhyd";
+import { CalculatorType, EnumEx } from "jalhyd";
 
 import { ParamService } from "../param/param.service";
 import { HttpService } from "../../services/http/http.service";
 import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
 import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
-import { FormulaireDefinition, CalculatorType } from "../../formulaire/formulaire-definition";
+import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
 import { FormulaireElement } from "../../formulaire/formulaire-element";
 import { InputField } from "../../formulaire/input-field";
 import { SelectField } from "../../formulaire/select-field";
 import { CheckField } from "../../formulaire/check-field";
 import { StringMap } from "../../stringmap";
 import { Observable } from "../observer";
+import { FormulaireConduiteDistributrice } from "../../formulaire/definition/concrete/form-cond-distri";
+import { FormulaireLechaptCalmon } from "../../formulaire/definition/concrete/form-lechapt-calmon";
+import { FormulaireSectionParametree } from "../../formulaire/definition/concrete/form-section-parametree";
+import { FormulaireCourbeRemous } from "../../formulaire/definition/concrete/form-courbe-remous";
+import { FormulaireRegimeUniforme } from "../../formulaire/definition/concrete/form-regime-uniforme";
+import { FormulairePasseBassinDimensions } from "../../formulaire/definition/concrete/form-passe-bassin-dim";
+import { FormulairePasseBassinPuissance } from "../../formulaire/definition/concrete/form-passe-bassin-puissance";
+import { FormulaireParallelStructure } from "../../formulaire/definition/concrete/form-parallel-structures";
 
 @Injectable()
 export class FormulaireService extends Observable {
@@ -105,6 +113,9 @@ export class FormulaireService extends Observable {
             case CalculatorType.PabPuissance:
                 return this.intlService.localizeText("INFO_PABPUISS_TITRE")
 
+            case CalculatorType.ParallelStructure:
+                return this.intlService.localizeText("INFO_OUVRAGEPARAL_TITRE")
+
             default:
                 return "Invalid calculator type " + type;
         }
@@ -120,10 +131,44 @@ export class FormulaireService extends Observable {
     }
 
     public createFormulaire(ct: CalculatorType): Promise<FormulaireDefinition> {
-        if (ct == undefined)
-            throw "FormulaireService.createFormulaire() : invalid undefined CalculatorType"
+        let f: FormulaireDefinition;
+        switch (ct) {
+            case CalculatorType.ConduiteDistributrice:
+                f = new FormulaireConduiteDistributrice(this.paramService, this.appSetupService);
+                break;
+
+            case CalculatorType.LechaptCalmon:
+                f = new FormulaireLechaptCalmon(this.paramService, this.appSetupService);
+                break;
+
+            case CalculatorType.SectionParametree:
+                f = new FormulaireSectionParametree(this.paramService, this.appSetupService, this.intlService);
+                break;
+
+            case CalculatorType.RegimeUniforme:
+                f = new FormulaireRegimeUniforme(this.paramService, this.appSetupService);
+                break;
+
+            case CalculatorType.CourbeRemous:
+                f = new FormulaireCourbeRemous(this.paramService, this.appSetupService);
+                break;
+
+            case CalculatorType.PabDimensions:
+                f = new FormulairePasseBassinDimensions(this.paramService, this.appSetupService);
+                break;
+
+            case CalculatorType.PabPuissance:
+                f = new FormulairePasseBassinPuissance(this.paramService, this.appSetupService);
+                break;
+
+            case CalculatorType.ParallelStructure:
+                f = new FormulaireParallelStructure(this.paramService, this.appSetupService);
+                break;
+
+            default:
+                throw new Error(`FormulaireService.createFormulaire() : type de calculette ${ct} non pris en charge`)
+        }
 
-        let f = new FormulaireDefinition(ct, this.paramService, this.intlService, this.appSetupService);
         f.calculatorName = this.getLocalisedTitleFromCalculatorType(ct) + " (" + f.uid + ")";
         this._formulaires.push(f);
         let prom: Promise<Response> = this.loadConfig(f, ct);
@@ -177,9 +222,9 @@ export class FormulaireService extends Observable {
     private getFormulaireElementById(formId: number, elemId: string): FormulaireElement {
         for (let f of this._formulaires)
             if (f.uid == formId) {
-                let s = f.getFormulaireElementById(elemId);
+                let s = f.getFormulaireNodeById(elemId);
                 if (s != undefined)
-                    return s;
+                    return s as FormulaireElement;
             }
         return undefined;
     }
@@ -210,6 +255,12 @@ export class FormulaireService extends Observable {
             case CalculatorType.PabPuissance:
                 return "app/calculators/pab-puissance/pab-puissance.";
 
+            case CalculatorType.Structure:
+                return "app/calculators/ouvrages/ouvrages.";
+
+            case CalculatorType.ParallelStructure:
+                return "app/calculators/parallel-structures/parallel-structures.";
+
             default:
                 throw "FormulaireService.getConfigPathPrefix() : valeur de CalculatorType " + ct + " non implémentée"
         }
diff --git a/src/app/services/observer.ts b/src/app/services/observer.ts
index d80be81107c3e538079381168b53802e65d1bda1..6a1e85b468eacc4e78df1f5336fc78582f465d5e 100644
--- a/src/app/services/observer.ts
+++ b/src/app/services/observer.ts
@@ -1,5 +1,5 @@
 export interface Observer {
-    update(sender: IObservable, data: any): void;
+    update(sender: any, data: any): void;
 }
 
 export interface IObservable {
@@ -16,21 +16,14 @@ export interface IObservable {
     /**
      * notifie un événement aux observateurs
      */
-    notifyObservers(data: any);
+    notifyObservers(data: any, sender?: any);
 }
 
 export class Observable implements IObservable {
     private _observers: Observer[];
 
-    private _sender: IObservable;
-
     constructor() {
         this._observers = [];
-        this._sender = this;
-    }
-
-    public set sender(s: IObservable) {
-        this._sender = s;
     }
 
     /**
@@ -51,8 +44,10 @@ export class Observable implements IObservable {
     /**
      * notifie un événement aux observateurs
      */
-    public notifyObservers(data: any) {
+    public notifyObservers(data: any, sender?: any) {
+        if (sender == undefined)
+            sender = this;
         for (let o of this._observers)
-            o.update(this._sender, data);
+            o.update(sender, data);
     }
 }
diff --git a/src/app/services/param/param.service.ts b/src/app/services/param/param.service.ts
index a56076a61b4902525e01c3c5f374ac83cece20e3..8d088ef028720437ee0c3f9feae5eaa86b2d1c12 100644
--- a/src/app/services/param/param.service.ts
+++ b/src/app/services/param/param.service.ts
@@ -1,113 +1,25 @@
-import { ParamDomain, ComputeNodeType, ComputeNodeParameters, ParamsEquation, ParamDefinition, ParamDomainValue, ParamCalculability } from "jalhyd";
+import { ParamDomain, ComputeNodeType, ComputeNodeParameters, ParamsEquation, ParamDefinition, ParamDomainValue, ParamCalculability, CalculatorType } from "jalhyd";
 
 import { NgParameter } from "../../formulaire/ngparam";
-import { logObject } from "../../util";
+import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
 
 export class ParamService {
-    private _params: ParamDefinition[];
-
-    constructor() {
-        this._params = [];
-
-        this.addParameters(ComputeNodeType.CondDistri);
-        this.addParameters(ComputeNodeType.LechaptCalmon);
-        this.addParameters(ComputeNodeType.SectionTrapeze);
-        this.addParameters(ComputeNodeType.SectionRectangle);
-        this.addParameters(ComputeNodeType.SectionCercle);
-        this.addParameters(ComputeNodeType.SectionPuissance);
-        this.addParameters(ComputeNodeType.RegimeUniformeTrapeze);
-        this.addParameters(ComputeNodeType.RegimeUniformeRectangle);
-        this.addParameters(ComputeNodeType.RegimeUniformeCercle);
-        this.addParameters(ComputeNodeType.RegimeUniformePuissance);
-        this.addParameters(ComputeNodeType.CourbeRemousCercle);
-        this.addParameters(ComputeNodeType.CourbeRemousPuissance);
-        this.addParameters(ComputeNodeType.CourbeRemousRectangle);
-        this.addParameters(ComputeNodeType.CourbeRemousTrapeze);
-        this.addParameters(ComputeNodeType.PabDimensions);
-        this.addParameters(ComputeNodeType.PabPuissance);
-
-        // précision de calcul
-
-        let d = new ParamDomain(ParamDomainValue.INTERVAL, 1e-10, 100);
-        let p = new ParamDefinition(ComputeNodeType.LechaptCalmon, 'Pr', d);
-        p.calculability = ParamCalculability.FREE;
-        this.addParameter(p);
-
-        p = new ParamDefinition(ComputeNodeType.CondDistri, 'Pr', d);
-        p.calculability = ParamCalculability.FREE;
-        this.addParameter(p);
-
-        p = new ParamDefinition(ComputeNodeType.SectionParametree, 'Pr', d);
-        p.calculability = ParamCalculability.FREE;
-        this.addParameter(p);
-
-        p = new ParamDefinition(ComputeNodeType.RegimeUniforme, 'Pr', d);
-        p.calculability = ParamCalculability.FREE;
-        this.addParameter(p);
-
-        p = new ParamDefinition(ComputeNodeType.CourbeRemous, 'Pr', d);
+    private createAccuracyParameter(): ParamDefinition {
+        const d = new ParamDomain(ParamDomainValue.INTERVAL, 1e-10, 100);
+        const p = new ParamDefinition('Pr', d);
         p.calculability = ParamCalculability.FREE;
-        this.addParameter(p);
-
-        p = new ParamDefinition(ComputeNodeType.PabPuissance, 'Pr', d);
-        p.calculability = ParamCalculability.FREE;
-        this.addParameter(p);
-
-        // logObject(this._params);
-    }
-
-    private addParameter(p: ParamDefinition) {
-        if (!this.hasParameter(p.computeNodeType, p.symbol))
-            this._params.push(p);
-    }
-
-    private addParameters(nodeType: ComputeNodeType) {
-        let cdp: ParamsEquation = ComputeNodeParameters.getInstance().getComputeNodeParameters(nodeType);
-        for (let pi in cdp.map) {
-            let p: ParamDefinition = cdp.map[pi];
-            this.addParameter(p);
-        }
+        return p;
     }
 
-    /**
-     * vérifie si un noeud possède un paramètre donné
-     * @param nodeType type de noeud
-     * @param symbol symbole à vérifier
-     */
-    private hasParameter(nodeType: ComputeNodeType, symbol: string): ParamDefinition {
-        for (let p of this._params)
-            if (p.computeNodeType == nodeType && p.symbol == symbol)
-                return p;
-
-        switch (nodeType) {
-            case ComputeNodeType.SectionCercle:
-            case ComputeNodeType.SectionPuissance:
-            case ComputeNodeType.SectionRectangle:
-            case ComputeNodeType.SectionTrapeze:
-                return this.hasParameter(ComputeNodeType.SectionParametree, symbol);
-
-            case ComputeNodeType.RegimeUniformeCercle:
-            case ComputeNodeType.RegimeUniformePuissance:
-            case ComputeNodeType.RegimeUniformeRectangle:
-            case ComputeNodeType.RegimeUniformeTrapeze:
-                return this.hasParameter(ComputeNodeType.RegimeUniforme, symbol);
-
-            case ComputeNodeType.CourbeRemousCercle:
-            case ComputeNodeType.CourbeRemousPuissance:
-            case ComputeNodeType.CourbeRemousRectangle:
-            case ComputeNodeType.CourbeRemousTrapeze:
-                return this.hasParameter(ComputeNodeType.CourbeRemous, symbol);
-
-            default:
-                return undefined;
-        }
-    }
+    public createParameter(calcType: CalculatorType, nodeType: ComputeNodeType, symbol: string, isTmpl = false): NgParameter {
+        if (symbol === "Pr")
+            var prmDef: ParamDefinition = this.createAccuracyParameter();
+        else
+            prmDef = ComputeNodeParameters.getInstance().getComputeNodeParameter(calcType, nodeType, symbol);
 
-    public createParameter(nodeType: ComputeNodeType, symbol: string, formId: number): NgParameter {
-        let prmDef: ParamDefinition = this.hasParameter(nodeType, symbol);
         if (prmDef == undefined)
-            throw "ParamService.createParameter() : pas de paramètre '" + symbol + "' pour le type de noeud " + ComputeNodeType[nodeType];
+            throw new Error(`ParamService.createParameter() : pas de paramètre '${symbol}' pour la calculette ${CalculatorType[calcType]}/type de noeud ${ComputeNodeType[nodeType]}`);
 
-        return new NgParameter(prmDef.clone(), formId);
+        return new NgParameter(prmDef.clone(), isTmpl);
     }
 }
diff --git a/src/dependencies/bootstrap.min.js b/src/dependencies/bootstrap.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..d9c72dfc1a2228e2bf64652d839a75e5e50968da
--- /dev/null
+++ b/src/dependencies/bootstrap.min.js
@@ -0,0 +1,7 @@
+/*!
+ * Bootstrap v4.0.0-alpha.6 (https://getbootstrap.com)
+ * Copyright 2011-2017 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.");+function(t){var e=t.fn.jquery.split(" ")[0].split(".");if(e[0]<2&&e[1]<9||1==e[0]&&9==e[1]&&e[2]<1||e[0]>=4)throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}(jQuery),+function(){function t(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function e(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},o=function(){function t(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}return function(e,n,i){return n&&t(e.prototype,n),i&&t(e,i),e}}(),r=function(t){function e(t){return{}.toString.call(t).match(/\s([a-zA-Z]+)/)[1].toLowerCase()}function n(t){return(t[0]||t).nodeType}function i(){return{bindType:a.end,delegateType:a.end,handle:function(e){if(t(e.target).is(this))return e.handleObj.handler.apply(this,arguments)}}}function o(){if(window.QUnit)return!1;var t=document.createElement("bootstrap");for(var e in h)if(void 0!==t.style[e])return{end:h[e]};return!1}function r(e){var n=this,i=!1;return t(this).one(c.TRANSITION_END,function(){i=!0}),setTimeout(function(){i||c.triggerTransitionEnd(n)},e),this}function s(){a=o(),t.fn.emulateTransitionEnd=r,c.supportsTransitionEnd()&&(t.event.special[c.TRANSITION_END]=i())}var a=!1,l=1e6,h={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},c={TRANSITION_END:"bsTransitionEnd",getUID:function(t){do t+=~~(Math.random()*l);while(document.getElementById(t));return t},getSelectorFromElement:function(t){var e=t.getAttribute("data-target");return e||(e=t.getAttribute("href")||"",e=/^#[a-z]/i.test(e)?e:null),e},reflow:function(t){return t.offsetHeight},triggerTransitionEnd:function(e){t(e).trigger(a.end)},supportsTransitionEnd:function(){return Boolean(a)},typeCheckConfig:function(t,i,o){for(var r in o)if(o.hasOwnProperty(r)){var s=o[r],a=i[r],l=a&&n(a)?"element":e(a);if(!new RegExp(s).test(l))throw new Error(t.toUpperCase()+": "+('Option "'+r+'" provided type "'+l+'" ')+('but expected type "'+s+'".'))}}};return s(),c}(jQuery),s=(function(t){var e="alert",i="4.0.0-alpha.6",s="bs.alert",a="."+s,l=".data-api",h=t.fn[e],c=150,u={DISMISS:'[data-dismiss="alert"]'},d={CLOSE:"close"+a,CLOSED:"closed"+a,CLICK_DATA_API:"click"+a+l},f={ALERT:"alert",FADE:"fade",SHOW:"show"},_=function(){function e(t){n(this,e),this._element=t}return e.prototype.close=function(t){t=t||this._element;var e=this._getRootElement(t),n=this._triggerCloseEvent(e);n.isDefaultPrevented()||this._removeElement(e)},e.prototype.dispose=function(){t.removeData(this._element,s),this._element=null},e.prototype._getRootElement=function(e){var n=r.getSelectorFromElement(e),i=!1;return n&&(i=t(n)[0]),i||(i=t(e).closest("."+f.ALERT)[0]),i},e.prototype._triggerCloseEvent=function(e){var n=t.Event(d.CLOSE);return t(e).trigger(n),n},e.prototype._removeElement=function(e){var n=this;return t(e).removeClass(f.SHOW),r.supportsTransitionEnd()&&t(e).hasClass(f.FADE)?void t(e).one(r.TRANSITION_END,function(t){return n._destroyElement(e,t)}).emulateTransitionEnd(c):void this._destroyElement(e)},e.prototype._destroyElement=function(e){t(e).detach().trigger(d.CLOSED).remove()},e._jQueryInterface=function(n){return this.each(function(){var i=t(this),o=i.data(s);o||(o=new e(this),i.data(s,o)),"close"===n&&o[n](this)})},e._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},o(e,null,[{key:"VERSION",get:function(){return i}}]),e}();return t(document).on(d.CLICK_DATA_API,u.DISMISS,_._handleDismiss(new _)),t.fn[e]=_._jQueryInterface,t.fn[e].Constructor=_,t.fn[e].noConflict=function(){return t.fn[e]=h,_._jQueryInterface},_}(jQuery),function(t){var e="button",i="4.0.0-alpha.6",r="bs.button",s="."+r,a=".data-api",l=t.fn[e],h={ACTIVE:"active",BUTTON:"btn",FOCUS:"focus"},c={DATA_TOGGLE_CARROT:'[data-toggle^="button"]',DATA_TOGGLE:'[data-toggle="buttons"]',INPUT:"input",ACTIVE:".active",BUTTON:".btn"},u={CLICK_DATA_API:"click"+s+a,FOCUS_BLUR_DATA_API:"focus"+s+a+" "+("blur"+s+a)},d=function(){function e(t){n(this,e),this._element=t}return e.prototype.toggle=function(){var e=!0,n=t(this._element).closest(c.DATA_TOGGLE)[0];if(n){var i=t(this._element).find(c.INPUT)[0];if(i){if("radio"===i.type)if(i.checked&&t(this._element).hasClass(h.ACTIVE))e=!1;else{var o=t(n).find(c.ACTIVE)[0];o&&t(o).removeClass(h.ACTIVE)}e&&(i.checked=!t(this._element).hasClass(h.ACTIVE),t(i).trigger("change")),i.focus()}}this._element.setAttribute("aria-pressed",!t(this._element).hasClass(h.ACTIVE)),e&&t(this._element).toggleClass(h.ACTIVE)},e.prototype.dispose=function(){t.removeData(this._element,r),this._element=null},e._jQueryInterface=function(n){return this.each(function(){var i=t(this).data(r);i||(i=new e(this),t(this).data(r,i)),"toggle"===n&&i[n]()})},o(e,null,[{key:"VERSION",get:function(){return i}}]),e}();return t(document).on(u.CLICK_DATA_API,c.DATA_TOGGLE_CARROT,function(e){e.preventDefault();var n=e.target;t(n).hasClass(h.BUTTON)||(n=t(n).closest(c.BUTTON)),d._jQueryInterface.call(t(n),"toggle")}).on(u.FOCUS_BLUR_DATA_API,c.DATA_TOGGLE_CARROT,function(e){var n=t(e.target).closest(c.BUTTON)[0];t(n).toggleClass(h.FOCUS,/^focus(in)?$/.test(e.type))}),t.fn[e]=d._jQueryInterface,t.fn[e].Constructor=d,t.fn[e].noConflict=function(){return t.fn[e]=l,d._jQueryInterface},d}(jQuery),function(t){var e="carousel",s="4.0.0-alpha.6",a="bs.carousel",l="."+a,h=".data-api",c=t.fn[e],u=600,d=37,f=39,_={interval:5e3,keyboard:!0,slide:!1,pause:"hover",wrap:!0},g={interval:"(number|boolean)",keyboard:"boolean",slide:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean"},p={NEXT:"next",PREV:"prev",LEFT:"left",RIGHT:"right"},m={SLIDE:"slide"+l,SLID:"slid"+l,KEYDOWN:"keydown"+l,MOUSEENTER:"mouseenter"+l,MOUSELEAVE:"mouseleave"+l,LOAD_DATA_API:"load"+l+h,CLICK_DATA_API:"click"+l+h},E={CAROUSEL:"carousel",ACTIVE:"active",SLIDE:"slide",RIGHT:"carousel-item-right",LEFT:"carousel-item-left",NEXT:"carousel-item-next",PREV:"carousel-item-prev",ITEM:"carousel-item"},v={ACTIVE:".active",ACTIVE_ITEM:".active.carousel-item",ITEM:".carousel-item",NEXT_PREV:".carousel-item-next, .carousel-item-prev",INDICATORS:".carousel-indicators",DATA_SLIDE:"[data-slide], [data-slide-to]",DATA_RIDE:'[data-ride="carousel"]'},T=function(){function h(e,i){n(this,h),this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this._config=this._getConfig(i),this._element=t(e)[0],this._indicatorsElement=t(this._element).find(v.INDICATORS)[0],this._addEventListeners()}return h.prototype.next=function(){if(this._isSliding)throw new Error("Carousel is sliding");this._slide(p.NEXT)},h.prototype.nextWhenVisible=function(){document.hidden||this.next()},h.prototype.prev=function(){if(this._isSliding)throw new Error("Carousel is sliding");this._slide(p.PREVIOUS)},h.prototype.pause=function(e){e||(this._isPaused=!0),t(this._element).find(v.NEXT_PREV)[0]&&r.supportsTransitionEnd()&&(r.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},h.prototype.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},h.prototype.to=function(e){var n=this;this._activeElement=t(this._element).find(v.ACTIVE_ITEM)[0];var i=this._getItemIndex(this._activeElement);if(!(e>this._items.length-1||e<0)){if(this._isSliding)return void t(this._element).one(m.SLID,function(){return n.to(e)});if(i===e)return this.pause(),void this.cycle();var o=e>i?p.NEXT:p.PREVIOUS;this._slide(o,this._items[e])}},h.prototype.dispose=function(){t(this._element).off(l),t.removeData(this._element,a),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},h.prototype._getConfig=function(n){return n=t.extend({},_,n),r.typeCheckConfig(e,n,g),n},h.prototype._addEventListeners=function(){var e=this;this._config.keyboard&&t(this._element).on(m.KEYDOWN,function(t){return e._keydown(t)}),"hover"!==this._config.pause||"ontouchstart"in document.documentElement||t(this._element).on(m.MOUSEENTER,function(t){return e.pause(t)}).on(m.MOUSELEAVE,function(t){return e.cycle(t)})},h.prototype._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.which){case d:t.preventDefault(),this.prev();break;case f:t.preventDefault(),this.next();break;default:return}},h.prototype._getItemIndex=function(e){return this._items=t.makeArray(t(e).parent().find(v.ITEM)),this._items.indexOf(e)},h.prototype._getItemByDirection=function(t,e){var n=t===p.NEXT,i=t===p.PREVIOUS,o=this._getItemIndex(e),r=this._items.length-1,s=i&&0===o||n&&o===r;if(s&&!this._config.wrap)return e;var a=t===p.PREVIOUS?-1:1,l=(o+a)%this._items.length;return l===-1?this._items[this._items.length-1]:this._items[l]},h.prototype._triggerSlideEvent=function(e,n){var i=t.Event(m.SLIDE,{relatedTarget:e,direction:n});return t(this._element).trigger(i),i},h.prototype._setActiveIndicatorElement=function(e){if(this._indicatorsElement){t(this._indicatorsElement).find(v.ACTIVE).removeClass(E.ACTIVE);var n=this._indicatorsElement.children[this._getItemIndex(e)];n&&t(n).addClass(E.ACTIVE)}},h.prototype._slide=function(e,n){var i=this,o=t(this._element).find(v.ACTIVE_ITEM)[0],s=n||o&&this._getItemByDirection(e,o),a=Boolean(this._interval),l=void 0,h=void 0,c=void 0;if(e===p.NEXT?(l=E.LEFT,h=E.NEXT,c=p.LEFT):(l=E.RIGHT,h=E.PREV,c=p.RIGHT),s&&t(s).hasClass(E.ACTIVE))return void(this._isSliding=!1);var d=this._triggerSlideEvent(s,c);if(!d.isDefaultPrevented()&&o&&s){this._isSliding=!0,a&&this.pause(),this._setActiveIndicatorElement(s);var f=t.Event(m.SLID,{relatedTarget:s,direction:c});r.supportsTransitionEnd()&&t(this._element).hasClass(E.SLIDE)?(t(s).addClass(h),r.reflow(s),t(o).addClass(l),t(s).addClass(l),t(o).one(r.TRANSITION_END,function(){t(s).removeClass(l+" "+h).addClass(E.ACTIVE),t(o).removeClass(E.ACTIVE+" "+h+" "+l),i._isSliding=!1,setTimeout(function(){return t(i._element).trigger(f)},0)}).emulateTransitionEnd(u)):(t(o).removeClass(E.ACTIVE),t(s).addClass(E.ACTIVE),this._isSliding=!1,t(this._element).trigger(f)),a&&this.cycle()}},h._jQueryInterface=function(e){return this.each(function(){var n=t(this).data(a),o=t.extend({},_,t(this).data());"object"===("undefined"==typeof e?"undefined":i(e))&&t.extend(o,e);var r="string"==typeof e?e:o.slide;if(n||(n=new h(this,o),t(this).data(a,n)),"number"==typeof e)n.to(e);else if("string"==typeof r){if(void 0===n[r])throw new Error('No method named "'+r+'"');n[r]()}else o.interval&&(n.pause(),n.cycle())})},h._dataApiClickHandler=function(e){var n=r.getSelectorFromElement(this);if(n){var i=t(n)[0];if(i&&t(i).hasClass(E.CAROUSEL)){var o=t.extend({},t(i).data(),t(this).data()),s=this.getAttribute("data-slide-to");s&&(o.interval=!1),h._jQueryInterface.call(t(i),o),s&&t(i).data(a).to(s),e.preventDefault()}}},o(h,null,[{key:"VERSION",get:function(){return s}},{key:"Default",get:function(){return _}}]),h}();return t(document).on(m.CLICK_DATA_API,v.DATA_SLIDE,T._dataApiClickHandler),t(window).on(m.LOAD_DATA_API,function(){t(v.DATA_RIDE).each(function(){var e=t(this);T._jQueryInterface.call(e,e.data())})}),t.fn[e]=T._jQueryInterface,t.fn[e].Constructor=T,t.fn[e].noConflict=function(){return t.fn[e]=c,T._jQueryInterface},T}(jQuery),function(t){var e="collapse",s="4.0.0-alpha.6",a="bs.collapse",l="."+a,h=".data-api",c=t.fn[e],u=600,d={toggle:!0,parent:""},f={toggle:"boolean",parent:"string"},_={SHOW:"show"+l,SHOWN:"shown"+l,HIDE:"hide"+l,HIDDEN:"hidden"+l,CLICK_DATA_API:"click"+l+h},g={SHOW:"show",COLLAPSE:"collapse",COLLAPSING:"collapsing",COLLAPSED:"collapsed"},p={WIDTH:"width",HEIGHT:"height"},m={ACTIVES:".card > .show, .card > .collapsing",DATA_TOGGLE:'[data-toggle="collapse"]'},E=function(){function l(e,i){n(this,l),this._isTransitioning=!1,this._element=e,this._config=this._getConfig(i),this._triggerArray=t.makeArray(t('[data-toggle="collapse"][href="#'+e.id+'"],'+('[data-toggle="collapse"][data-target="#'+e.id+'"]'))),this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}return l.prototype.toggle=function(){t(this._element).hasClass(g.SHOW)?this.hide():this.show()},l.prototype.show=function(){var e=this;if(this._isTransitioning)throw new Error("Collapse is transitioning");if(!t(this._element).hasClass(g.SHOW)){var n=void 0,i=void 0;if(this._parent&&(n=t.makeArray(t(this._parent).find(m.ACTIVES)),n.length||(n=null)),!(n&&(i=t(n).data(a),i&&i._isTransitioning))){var o=t.Event(_.SHOW);if(t(this._element).trigger(o),!o.isDefaultPrevented()){n&&(l._jQueryInterface.call(t(n),"hide"),i||t(n).data(a,null));var s=this._getDimension();t(this._element).removeClass(g.COLLAPSE).addClass(g.COLLAPSING),this._element.style[s]=0,this._element.setAttribute("aria-expanded",!0),this._triggerArray.length&&t(this._triggerArray).removeClass(g.COLLAPSED).attr("aria-expanded",!0),this.setTransitioning(!0);var h=function(){t(e._element).removeClass(g.COLLAPSING).addClass(g.COLLAPSE).addClass(g.SHOW),e._element.style[s]="",e.setTransitioning(!1),t(e._element).trigger(_.SHOWN)};if(!r.supportsTransitionEnd())return void h();var c=s[0].toUpperCase()+s.slice(1),d="scroll"+c;t(this._element).one(r.TRANSITION_END,h).emulateTransitionEnd(u),this._element.style[s]=this._element[d]+"px"}}}},l.prototype.hide=function(){var e=this;if(this._isTransitioning)throw new Error("Collapse is transitioning");if(t(this._element).hasClass(g.SHOW)){var n=t.Event(_.HIDE);if(t(this._element).trigger(n),!n.isDefaultPrevented()){var i=this._getDimension(),o=i===p.WIDTH?"offsetWidth":"offsetHeight";this._element.style[i]=this._element[o]+"px",r.reflow(this._element),t(this._element).addClass(g.COLLAPSING).removeClass(g.COLLAPSE).removeClass(g.SHOW),this._element.setAttribute("aria-expanded",!1),this._triggerArray.length&&t(this._triggerArray).addClass(g.COLLAPSED).attr("aria-expanded",!1),this.setTransitioning(!0);var s=function(){e.setTransitioning(!1),t(e._element).removeClass(g.COLLAPSING).addClass(g.COLLAPSE).trigger(_.HIDDEN)};return this._element.style[i]="",r.supportsTransitionEnd()?void t(this._element).one(r.TRANSITION_END,s).emulateTransitionEnd(u):void s()}}},l.prototype.setTransitioning=function(t){this._isTransitioning=t},l.prototype.dispose=function(){t.removeData(this._element,a),this._config=null,this._parent=null,this._element=null,this._triggerArray=null,this._isTransitioning=null},l.prototype._getConfig=function(n){return n=t.extend({},d,n),n.toggle=Boolean(n.toggle),r.typeCheckConfig(e,n,f),n},l.prototype._getDimension=function(){var e=t(this._element).hasClass(p.WIDTH);return e?p.WIDTH:p.HEIGHT},l.prototype._getParent=function(){var e=this,n=t(this._config.parent)[0],i='[data-toggle="collapse"][data-parent="'+this._config.parent+'"]';return t(n).find(i).each(function(t,n){e._addAriaAndCollapsedClass(l._getTargetFromElement(n),[n])}),n},l.prototype._addAriaAndCollapsedClass=function(e,n){if(e){var i=t(e).hasClass(g.SHOW);e.setAttribute("aria-expanded",i),n.length&&t(n).toggleClass(g.COLLAPSED,!i).attr("aria-expanded",i)}},l._getTargetFromElement=function(e){var n=r.getSelectorFromElement(e);return n?t(n)[0]:null},l._jQueryInterface=function(e){return this.each(function(){var n=t(this),o=n.data(a),r=t.extend({},d,n.data(),"object"===("undefined"==typeof e?"undefined":i(e))&&e);if(!o&&r.toggle&&/show|hide/.test(e)&&(r.toggle=!1),o||(o=new l(this,r),n.data(a,o)),"string"==typeof e){if(void 0===o[e])throw new Error('No method named "'+e+'"');o[e]()}})},o(l,null,[{key:"VERSION",get:function(){return s}},{key:"Default",get:function(){return d}}]),l}();return t(document).on(_.CLICK_DATA_API,m.DATA_TOGGLE,function(e){e.preventDefault();var n=E._getTargetFromElement(this),i=t(n).data(a),o=i?"toggle":t(this).data();E._jQueryInterface.call(t(n),o)}),t.fn[e]=E._jQueryInterface,t.fn[e].Constructor=E,t.fn[e].noConflict=function(){return t.fn[e]=c,E._jQueryInterface},E}(jQuery),function(t){var e="dropdown",i="4.0.0-alpha.6",s="bs.dropdown",a="."+s,l=".data-api",h=t.fn[e],c=27,u=38,d=40,f=3,_={HIDE:"hide"+a,HIDDEN:"hidden"+a,SHOW:"show"+a,SHOWN:"shown"+a,CLICK:"click"+a,CLICK_DATA_API:"click"+a+l,FOCUSIN_DATA_API:"focusin"+a+l,KEYDOWN_DATA_API:"keydown"+a+l},g={BACKDROP:"dropdown-backdrop",DISABLED:"disabled",SHOW:"show"},p={BACKDROP:".dropdown-backdrop",DATA_TOGGLE:'[data-toggle="dropdown"]',FORM_CHILD:".dropdown form",ROLE_MENU:'[role="menu"]',ROLE_LISTBOX:'[role="listbox"]',NAVBAR_NAV:".navbar-nav",VISIBLE_ITEMS:'[role="menu"] li:not(.disabled) a, [role="listbox"] li:not(.disabled) a'},m=function(){function e(t){n(this,e),this._element=t,this._addEventListeners()}return e.prototype.toggle=function(){if(this.disabled||t(this).hasClass(g.DISABLED))return!1;var n=e._getParentFromElement(this),i=t(n).hasClass(g.SHOW);if(e._clearMenus(),i)return!1;if("ontouchstart"in document.documentElement&&!t(n).closest(p.NAVBAR_NAV).length){var o=document.createElement("div");o.className=g.BACKDROP,t(o).insertBefore(this),t(o).on("click",e._clearMenus)}var r={relatedTarget:this},s=t.Event(_.SHOW,r);return t(n).trigger(s),!s.isDefaultPrevented()&&(this.focus(),this.setAttribute("aria-expanded",!0),t(n).toggleClass(g.SHOW),t(n).trigger(t.Event(_.SHOWN,r)),!1)},e.prototype.dispose=function(){t.removeData(this._element,s),t(this._element).off(a),this._element=null},e.prototype._addEventListeners=function(){t(this._element).on(_.CLICK,this.toggle)},e._jQueryInterface=function(n){return this.each(function(){var i=t(this).data(s);if(i||(i=new e(this),t(this).data(s,i)),"string"==typeof n){if(void 0===i[n])throw new Error('No method named "'+n+'"');i[n].call(this)}})},e._clearMenus=function(n){if(!n||n.which!==f){var i=t(p.BACKDROP)[0];i&&i.parentNode.removeChild(i);for(var o=t.makeArray(t(p.DATA_TOGGLE)),r=0;r<o.length;r++){var s=e._getParentFromElement(o[r]),a={relatedTarget:o[r]};if(t(s).hasClass(g.SHOW)&&!(n&&("click"===n.type&&/input|textarea/i.test(n.target.tagName)||"focusin"===n.type)&&t.contains(s,n.target))){var l=t.Event(_.HIDE,a);t(s).trigger(l),l.isDefaultPrevented()||(o[r].setAttribute("aria-expanded","false"),t(s).removeClass(g.SHOW).trigger(t.Event(_.HIDDEN,a)))}}}},e._getParentFromElement=function(e){var n=void 0,i=r.getSelectorFromElement(e);return i&&(n=t(i)[0]),n||e.parentNode},e._dataApiKeydownHandler=function(n){if(/(38|40|27|32)/.test(n.which)&&!/input|textarea/i.test(n.target.tagName)&&(n.preventDefault(),n.stopPropagation(),!this.disabled&&!t(this).hasClass(g.DISABLED))){var i=e._getParentFromElement(this),o=t(i).hasClass(g.SHOW);if(!o&&n.which!==c||o&&n.which===c){if(n.which===c){var r=t(i).find(p.DATA_TOGGLE)[0];t(r).trigger("focus")}return void t(this).trigger("click")}var s=t(i).find(p.VISIBLE_ITEMS).get();if(s.length){var a=s.indexOf(n.target);n.which===u&&a>0&&a--,n.which===d&&a<s.length-1&&a++,a<0&&(a=0),s[a].focus()}}},o(e,null,[{key:"VERSION",get:function(){return i}}]),e}();return t(document).on(_.KEYDOWN_DATA_API,p.DATA_TOGGLE,m._dataApiKeydownHandler).on(_.KEYDOWN_DATA_API,p.ROLE_MENU,m._dataApiKeydownHandler).on(_.KEYDOWN_DATA_API,p.ROLE_LISTBOX,m._dataApiKeydownHandler).on(_.CLICK_DATA_API+" "+_.FOCUSIN_DATA_API,m._clearMenus).on(_.CLICK_DATA_API,p.DATA_TOGGLE,m.prototype.toggle).on(_.CLICK_DATA_API,p.FORM_CHILD,function(t){t.stopPropagation()}),t.fn[e]=m._jQueryInterface,t.fn[e].Constructor=m,t.fn[e].noConflict=function(){return t.fn[e]=h,m._jQueryInterface},m}(jQuery),function(t){var e="modal",s="4.0.0-alpha.6",a="bs.modal",l="."+a,h=".data-api",c=t.fn[e],u=300,d=150,f=27,_={backdrop:!0,keyboard:!0,focus:!0,show:!0},g={backdrop:"(boolean|string)",keyboard:"boolean",focus:"boolean",show:"boolean"},p={HIDE:"hide"+l,HIDDEN:"hidden"+l,SHOW:"show"+l,SHOWN:"shown"+l,FOCUSIN:"focusin"+l,RESIZE:"resize"+l,CLICK_DISMISS:"click.dismiss"+l,KEYDOWN_DISMISS:"keydown.dismiss"+l,MOUSEUP_DISMISS:"mouseup.dismiss"+l,MOUSEDOWN_DISMISS:"mousedown.dismiss"+l,CLICK_DATA_API:"click"+l+h},m={SCROLLBAR_MEASURER:"modal-scrollbar-measure",BACKDROP:"modal-backdrop",OPEN:"modal-open",FADE:"fade",SHOW:"show"},E={DIALOG:".modal-dialog",DATA_TOGGLE:'[data-toggle="modal"]',DATA_DISMISS:'[data-dismiss="modal"]',FIXED_CONTENT:".fixed-top, .fixed-bottom, .is-fixed, .sticky-top"},v=function(){function h(e,i){n(this,h),this._config=this._getConfig(i),this._element=e,this._dialog=t(e).find(E.DIALOG)[0],this._backdrop=null,this._isShown=!1,this._isBodyOverflowing=!1,this._ignoreBackdropClick=!1,this._isTransitioning=!1,this._originalBodyPadding=0,this._scrollbarWidth=0}return h.prototype.toggle=function(t){return this._isShown?this.hide():this.show(t)},h.prototype.show=function(e){var n=this;if(this._isTransitioning)throw new Error("Modal is transitioning");r.supportsTransitionEnd()&&t(this._element).hasClass(m.FADE)&&(this._isTransitioning=!0);var i=t.Event(p.SHOW,{relatedTarget:e});t(this._element).trigger(i),this._isShown||i.isDefaultPrevented()||(this._isShown=!0,this._checkScrollbar(),this._setScrollbar(),t(document.body).addClass(m.OPEN),this._setEscapeEvent(),this._setResizeEvent(),t(this._element).on(p.CLICK_DISMISS,E.DATA_DISMISS,function(t){return n.hide(t)}),t(this._dialog).on(p.MOUSEDOWN_DISMISS,function(){t(n._element).one(p.MOUSEUP_DISMISS,function(e){t(e.target).is(n._element)&&(n._ignoreBackdropClick=!0)})}),this._showBackdrop(function(){return n._showElement(e)}))},h.prototype.hide=function(e){var n=this;if(e&&e.preventDefault(),this._isTransitioning)throw new Error("Modal is transitioning");var i=r.supportsTransitionEnd()&&t(this._element).hasClass(m.FADE);i&&(this._isTransitioning=!0);var o=t.Event(p.HIDE);t(this._element).trigger(o),this._isShown&&!o.isDefaultPrevented()&&(this._isShown=!1,this._setEscapeEvent(),this._setResizeEvent(),t(document).off(p.FOCUSIN),t(this._element).removeClass(m.SHOW),t(this._element).off(p.CLICK_DISMISS),t(this._dialog).off(p.MOUSEDOWN_DISMISS),i?t(this._element).one(r.TRANSITION_END,function(t){return n._hideModal(t)}).emulateTransitionEnd(u):this._hideModal())},h.prototype.dispose=function(){t.removeData(this._element,a),t(window,document,this._element,this._backdrop).off(l),this._config=null,this._element=null,this._dialog=null,this._backdrop=null,this._isShown=null,this._isBodyOverflowing=null,this._ignoreBackdropClick=null,this._originalBodyPadding=null,this._scrollbarWidth=null},h.prototype._getConfig=function(n){return n=t.extend({},_,n),r.typeCheckConfig(e,n,g),n},h.prototype._showElement=function(e){var n=this,i=r.supportsTransitionEnd()&&t(this._element).hasClass(m.FADE);this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.appendChild(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.scrollTop=0,i&&r.reflow(this._element),t(this._element).addClass(m.SHOW),this._config.focus&&this._enforceFocus();var o=t.Event(p.SHOWN,{relatedTarget:e}),s=function(){n._config.focus&&n._element.focus(),n._isTransitioning=!1,t(n._element).trigger(o)};i?t(this._dialog).one(r.TRANSITION_END,s).emulateTransitionEnd(u):s()},h.prototype._enforceFocus=function(){var e=this;t(document).off(p.FOCUSIN).on(p.FOCUSIN,function(n){document===n.target||e._element===n.target||t(e._element).has(n.target).length||e._element.focus()})},h.prototype._setEscapeEvent=function(){var e=this;this._isShown&&this._config.keyboard?t(this._element).on(p.KEYDOWN_DISMISS,function(t){t.which===f&&e.hide()}):this._isShown||t(this._element).off(p.KEYDOWN_DISMISS)},h.prototype._setResizeEvent=function(){var e=this;this._isShown?t(window).on(p.RESIZE,function(t){return e._handleUpdate(t)}):t(window).off(p.RESIZE)},h.prototype._hideModal=function(){var e=this;this._element.style.display="none",this._element.setAttribute("aria-hidden","true"),this._isTransitioning=!1,this._showBackdrop(function(){t(document.body).removeClass(m.OPEN),e._resetAdjustments(),e._resetScrollbar(),t(e._element).trigger(p.HIDDEN)})},h.prototype._removeBackdrop=function(){this._backdrop&&(t(this._backdrop).remove(),this._backdrop=null)},h.prototype._showBackdrop=function(e){var n=this,i=t(this._element).hasClass(m.FADE)?m.FADE:"";if(this._isShown&&this._config.backdrop){var o=r.supportsTransitionEnd()&&i;if(this._backdrop=document.createElement("div"),this._backdrop.className=m.BACKDROP,i&&t(this._backdrop).addClass(i),t(this._backdrop).appendTo(document.body),t(this._element).on(p.CLICK_DISMISS,function(t){return n._ignoreBackdropClick?void(n._ignoreBackdropClick=!1):void(t.target===t.currentTarget&&("static"===n._config.backdrop?n._element.focus():n.hide()))}),o&&r.reflow(this._backdrop),t(this._backdrop).addClass(m.SHOW),!e)return;if(!o)return void e();t(this._backdrop).one(r.TRANSITION_END,e).emulateTransitionEnd(d)}else if(!this._isShown&&this._backdrop){t(this._backdrop).removeClass(m.SHOW);var s=function(){n._removeBackdrop(),e&&e()};r.supportsTransitionEnd()&&t(this._element).hasClass(m.FADE)?t(this._backdrop).one(r.TRANSITION_END,s).emulateTransitionEnd(d):s()}else e&&e()},h.prototype._handleUpdate=function(){this._adjustDialog()},h.prototype._adjustDialog=function(){var t=this._element.scrollHeight>document.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},h.prototype._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},h.prototype._checkScrollbar=function(){this._isBodyOverflowing=document.body.clientWidth<window.innerWidth,this._scrollbarWidth=this._getScrollbarWidth()},h.prototype._setScrollbar=function(){var e=parseInt(t(E.FIXED_CONTENT).css("padding-right")||0,10);this._originalBodyPadding=document.body.style.paddingRight||"",this._isBodyOverflowing&&(document.body.style.paddingRight=e+this._scrollbarWidth+"px")},h.prototype._resetScrollbar=function(){document.body.style.paddingRight=this._originalBodyPadding},h.prototype._getScrollbarWidth=function(){var t=document.createElement("div");t.className=m.SCROLLBAR_MEASURER,document.body.appendChild(t);var e=t.offsetWidth-t.clientWidth;return document.body.removeChild(t),e},h._jQueryInterface=function(e,n){return this.each(function(){var o=t(this).data(a),r=t.extend({},h.Default,t(this).data(),"object"===("undefined"==typeof e?"undefined":i(e))&&e);if(o||(o=new h(this,r),t(this).data(a,o)),"string"==typeof e){if(void 0===o[e])throw new Error('No method named "'+e+'"');o[e](n)}else r.show&&o.show(n)})},o(h,null,[{key:"VERSION",get:function(){return s}},{key:"Default",get:function(){return _}}]),h}();return t(document).on(p.CLICK_DATA_API,E.DATA_TOGGLE,function(e){var n=this,i=void 0,o=r.getSelectorFromElement(this);o&&(i=t(o)[0]);var s=t(i).data(a)?"toggle":t.extend({},t(i).data(),t(this).data());"A"!==this.tagName&&"AREA"!==this.tagName||e.preventDefault();var l=t(i).one(p.SHOW,function(e){e.isDefaultPrevented()||l.one(p.HIDDEN,function(){t(n).is(":visible")&&n.focus()})});v._jQueryInterface.call(t(i),s,this)}),t.fn[e]=v._jQueryInterface,t.fn[e].Constructor=v,t.fn[e].noConflict=function(){return t.fn[e]=c,v._jQueryInterface},v}(jQuery),function(t){var e="scrollspy",s="4.0.0-alpha.6",a="bs.scrollspy",l="."+a,h=".data-api",c=t.fn[e],u={offset:10,method:"auto",target:""},d={offset:"number",method:"string",target:"(string|element)"},f={ACTIVATE:"activate"+l,SCROLL:"scroll"+l,LOAD_DATA_API:"load"+l+h},_={DROPDOWN_ITEM:"dropdown-item",DROPDOWN_MENU:"dropdown-menu",NAV_LINK:"nav-link",NAV:"nav",ACTIVE:"active"},g={DATA_SPY:'[data-spy="scroll"]',ACTIVE:".active",LIST_ITEM:".list-item",LI:"li",LI_DROPDOWN:"li.dropdown",NAV_LINKS:".nav-link",DROPDOWN:".dropdown",DROPDOWN_ITEMS:".dropdown-item",DROPDOWN_TOGGLE:".dropdown-toggle"},p={OFFSET:"offset",POSITION:"position"},m=function(){function h(e,i){var o=this;n(this,h),this._element=e,this._scrollElement="BODY"===e.tagName?window:e,this._config=this._getConfig(i),this._selector=this._config.target+" "+g.NAV_LINKS+","+(this._config.target+" "+g.DROPDOWN_ITEMS),this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,t(this._scrollElement).on(f.SCROLL,function(t){return o._process(t)}),this.refresh(),this._process()}return h.prototype.refresh=function(){var e=this,n=this._scrollElement!==this._scrollElement.window?p.POSITION:p.OFFSET,i="auto"===this._config.method?n:this._config.method,o=i===p.POSITION?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight();var s=t.makeArray(t(this._selector));s.map(function(e){var n=void 0,s=r.getSelectorFromElement(e);return s&&(n=t(s)[0]),n&&(n.offsetWidth||n.offsetHeight)?[t(n)[i]().top+o,s]:null}).filter(function(t){return t}).sort(function(t,e){return t[0]-e[0]}).forEach(function(t){e._offsets.push(t[0]),e._targets.push(t[1])})},h.prototype.dispose=function(){t.removeData(this._element,a),t(this._scrollElement).off(l),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},h.prototype._getConfig=function(n){if(n=t.extend({},u,n),"string"!=typeof n.target){var i=t(n.target).attr("id");i||(i=r.getUID(e),t(n.target).attr("id",i)),n.target="#"+i}return r.typeCheckConfig(e,n,d),n},h.prototype._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},h.prototype._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},h.prototype._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.offsetHeight},h.prototype._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=n){var i=this._targets[this._targets.length-1];return void(this._activeTarget!==i&&this._activate(i))}if(this._activeTarget&&t<this._offsets[0]&&this._offsets[0]>0)return this._activeTarget=null,void this._clear();for(var o=this._offsets.length;o--;){var r=this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&(void 0===this._offsets[o+1]||t<this._offsets[o+1]);r&&this._activate(this._targets[o])}},h.prototype._activate=function(e){this._activeTarget=e,this._clear();var n=this._selector.split(",");n=n.map(function(t){return t+'[data-target="'+e+'"],'+(t+'[href="'+e+'"]')});var i=t(n.join(","));i.hasClass(_.DROPDOWN_ITEM)?(i.closest(g.DROPDOWN).find(g.DROPDOWN_TOGGLE).addClass(_.ACTIVE),i.addClass(_.ACTIVE)):i.parents(g.LI).find("> "+g.NAV_LINKS).addClass(_.ACTIVE),t(this._scrollElement).trigger(f.ACTIVATE,{relatedTarget:e})},h.prototype._clear=function(){t(this._selector).filter(g.ACTIVE).removeClass(_.ACTIVE)},h._jQueryInterface=function(e){return this.each(function(){var n=t(this).data(a),o="object"===("undefined"==typeof e?"undefined":i(e))&&e;
+if(n||(n=new h(this,o),t(this).data(a,n)),"string"==typeof e){if(void 0===n[e])throw new Error('No method named "'+e+'"');n[e]()}})},o(h,null,[{key:"VERSION",get:function(){return s}},{key:"Default",get:function(){return u}}]),h}();return t(window).on(f.LOAD_DATA_API,function(){for(var e=t.makeArray(t(g.DATA_SPY)),n=e.length;n--;){var i=t(e[n]);m._jQueryInterface.call(i,i.data())}}),t.fn[e]=m._jQueryInterface,t.fn[e].Constructor=m,t.fn[e].noConflict=function(){return t.fn[e]=c,m._jQueryInterface},m}(jQuery),function(t){var e="tab",i="4.0.0-alpha.6",s="bs.tab",a="."+s,l=".data-api",h=t.fn[e],c=150,u={HIDE:"hide"+a,HIDDEN:"hidden"+a,SHOW:"show"+a,SHOWN:"shown"+a,CLICK_DATA_API:"click"+a+l},d={DROPDOWN_MENU:"dropdown-menu",ACTIVE:"active",DISABLED:"disabled",FADE:"fade",SHOW:"show"},f={A:"a",LI:"li",DROPDOWN:".dropdown",LIST:"ul:not(.dropdown-menu), ol:not(.dropdown-menu), nav:not(.dropdown-menu)",FADE_CHILD:"> .nav-item .fade, > .fade",ACTIVE:".active",ACTIVE_CHILD:"> .nav-item > .active, > .active",DATA_TOGGLE:'[data-toggle="tab"], [data-toggle="pill"]',DROPDOWN_TOGGLE:".dropdown-toggle",DROPDOWN_ACTIVE_CHILD:"> .dropdown-menu .active"},_=function(){function e(t){n(this,e),this._element=t}return e.prototype.show=function(){var e=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&t(this._element).hasClass(d.ACTIVE)||t(this._element).hasClass(d.DISABLED))){var n=void 0,i=void 0,o=t(this._element).closest(f.LIST)[0],s=r.getSelectorFromElement(this._element);o&&(i=t.makeArray(t(o).find(f.ACTIVE)),i=i[i.length-1]);var a=t.Event(u.HIDE,{relatedTarget:this._element}),l=t.Event(u.SHOW,{relatedTarget:i});if(i&&t(i).trigger(a),t(this._element).trigger(l),!l.isDefaultPrevented()&&!a.isDefaultPrevented()){s&&(n=t(s)[0]),this._activate(this._element,o);var h=function(){var n=t.Event(u.HIDDEN,{relatedTarget:e._element}),o=t.Event(u.SHOWN,{relatedTarget:i});t(i).trigger(n),t(e._element).trigger(o)};n?this._activate(n,n.parentNode,h):h()}}},e.prototype.dispose=function(){t.removeClass(this._element,s),this._element=null},e.prototype._activate=function(e,n,i){var o=this,s=t(n).find(f.ACTIVE_CHILD)[0],a=i&&r.supportsTransitionEnd()&&(s&&t(s).hasClass(d.FADE)||Boolean(t(n).find(f.FADE_CHILD)[0])),l=function(){return o._transitionComplete(e,s,a,i)};s&&a?t(s).one(r.TRANSITION_END,l).emulateTransitionEnd(c):l(),s&&t(s).removeClass(d.SHOW)},e.prototype._transitionComplete=function(e,n,i,o){if(n){t(n).removeClass(d.ACTIVE);var s=t(n.parentNode).find(f.DROPDOWN_ACTIVE_CHILD)[0];s&&t(s).removeClass(d.ACTIVE),n.setAttribute("aria-expanded",!1)}if(t(e).addClass(d.ACTIVE),e.setAttribute("aria-expanded",!0),i?(r.reflow(e),t(e).addClass(d.SHOW)):t(e).removeClass(d.FADE),e.parentNode&&t(e.parentNode).hasClass(d.DROPDOWN_MENU)){var a=t(e).closest(f.DROPDOWN)[0];a&&t(a).find(f.DROPDOWN_TOGGLE).addClass(d.ACTIVE),e.setAttribute("aria-expanded",!0)}o&&o()},e._jQueryInterface=function(n){return this.each(function(){var i=t(this),o=i.data(s);if(o||(o=new e(this),i.data(s,o)),"string"==typeof n){if(void 0===o[n])throw new Error('No method named "'+n+'"');o[n]()}})},o(e,null,[{key:"VERSION",get:function(){return i}}]),e}();return t(document).on(u.CLICK_DATA_API,f.DATA_TOGGLE,function(e){e.preventDefault(),_._jQueryInterface.call(t(this),"show")}),t.fn[e]=_._jQueryInterface,t.fn[e].Constructor=_,t.fn[e].noConflict=function(){return t.fn[e]=h,_._jQueryInterface},_}(jQuery),function(t){if("undefined"==typeof Tether)throw new Error("Bootstrap tooltips require Tether (http://tether.io/)");var e="tooltip",s="4.0.0-alpha.6",a="bs.tooltip",l="."+a,h=t.fn[e],c=150,u="bs-tether",d={animation:!0,template:'<div class="tooltip" role="tooltip"><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:"0 0",constraints:[],container:!1},f={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"string",constraints:"array",container:"(string|element|boolean)"},_={TOP:"bottom center",RIGHT:"middle left",BOTTOM:"top center",LEFT:"middle right"},g={SHOW:"show",OUT:"out"},p={HIDE:"hide"+l,HIDDEN:"hidden"+l,SHOW:"show"+l,SHOWN:"shown"+l,INSERTED:"inserted"+l,CLICK:"click"+l,FOCUSIN:"focusin"+l,FOCUSOUT:"focusout"+l,MOUSEENTER:"mouseenter"+l,MOUSELEAVE:"mouseleave"+l},m={FADE:"fade",SHOW:"show"},E={TOOLTIP:".tooltip",TOOLTIP_INNER:".tooltip-inner"},v={element:!1,enabled:!1},T={HOVER:"hover",FOCUS:"focus",CLICK:"click",MANUAL:"manual"},I=function(){function h(t,e){n(this,h),this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._isTransitioning=!1,this._tether=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}return h.prototype.enable=function(){this._isEnabled=!0},h.prototype.disable=function(){this._isEnabled=!1},h.prototype.toggleEnabled=function(){this._isEnabled=!this._isEnabled},h.prototype.toggle=function(e){if(e){var n=this.constructor.DATA_KEY,i=t(e.currentTarget).data(n);i||(i=new this.constructor(e.currentTarget,this._getDelegateConfig()),t(e.currentTarget).data(n,i)),i._activeTrigger.click=!i._activeTrigger.click,i._isWithActiveTrigger()?i._enter(null,i):i._leave(null,i)}else{if(t(this.getTipElement()).hasClass(m.SHOW))return void this._leave(null,this);this._enter(null,this)}},h.prototype.dispose=function(){clearTimeout(this._timeout),this.cleanupTether(),t.removeData(this.element,this.constructor.DATA_KEY),t(this.element).off(this.constructor.EVENT_KEY),t(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&t(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._tether=null,this.element=null,this.config=null,this.tip=null},h.prototype.show=function(){var e=this;if("none"===t(this.element).css("display"))throw new Error("Please use show on visible elements");var n=t.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){if(this._isTransitioning)throw new Error("Tooltip is transitioning");t(this.element).trigger(n);var i=t.contains(this.element.ownerDocument.documentElement,this.element);if(n.isDefaultPrevented()||!i)return;var o=this.getTipElement(),s=r.getUID(this.constructor.NAME);o.setAttribute("id",s),this.element.setAttribute("aria-describedby",s),this.setContent(),this.config.animation&&t(o).addClass(m.FADE);var a="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,l=this._getAttachment(a),c=this.config.container===!1?document.body:t(this.config.container);t(o).data(this.constructor.DATA_KEY,this).appendTo(c),t(this.element).trigger(this.constructor.Event.INSERTED),this._tether=new Tether({attachment:l,element:o,target:this.element,classes:v,classPrefix:u,offset:this.config.offset,constraints:this.config.constraints,addTargetClasses:!1}),r.reflow(o),this._tether.position(),t(o).addClass(m.SHOW);var d=function(){var n=e._hoverState;e._hoverState=null,e._isTransitioning=!1,t(e.element).trigger(e.constructor.Event.SHOWN),n===g.OUT&&e._leave(null,e)};if(r.supportsTransitionEnd()&&t(this.tip).hasClass(m.FADE))return this._isTransitioning=!0,void t(this.tip).one(r.TRANSITION_END,d).emulateTransitionEnd(h._TRANSITION_DURATION);d()}},h.prototype.hide=function(e){var n=this,i=this.getTipElement(),o=t.Event(this.constructor.Event.HIDE);if(this._isTransitioning)throw new Error("Tooltip is transitioning");var s=function(){n._hoverState!==g.SHOW&&i.parentNode&&i.parentNode.removeChild(i),n.element.removeAttribute("aria-describedby"),t(n.element).trigger(n.constructor.Event.HIDDEN),n._isTransitioning=!1,n.cleanupTether(),e&&e()};t(this.element).trigger(o),o.isDefaultPrevented()||(t(i).removeClass(m.SHOW),this._activeTrigger[T.CLICK]=!1,this._activeTrigger[T.FOCUS]=!1,this._activeTrigger[T.HOVER]=!1,r.supportsTransitionEnd()&&t(this.tip).hasClass(m.FADE)?(this._isTransitioning=!0,t(i).one(r.TRANSITION_END,s).emulateTransitionEnd(c)):s(),this._hoverState="")},h.prototype.isWithContent=function(){return Boolean(this.getTitle())},h.prototype.getTipElement=function(){return this.tip=this.tip||t(this.config.template)[0]},h.prototype.setContent=function(){var e=t(this.getTipElement());this.setElementContent(e.find(E.TOOLTIP_INNER),this.getTitle()),e.removeClass(m.FADE+" "+m.SHOW),this.cleanupTether()},h.prototype.setElementContent=function(e,n){var o=this.config.html;"object"===("undefined"==typeof n?"undefined":i(n))&&(n.nodeType||n.jquery)?o?t(n).parent().is(e)||e.empty().append(n):e.text(t(n).text()):e[o?"html":"text"](n)},h.prototype.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},h.prototype.cleanupTether=function(){this._tether&&this._tether.destroy()},h.prototype._getAttachment=function(t){return _[t.toUpperCase()]},h.prototype._setListeners=function(){var e=this,n=this.config.trigger.split(" ");n.forEach(function(n){if("click"===n)t(e.element).on(e.constructor.Event.CLICK,e.config.selector,function(t){return e.toggle(t)});else if(n!==T.MANUAL){var i=n===T.HOVER?e.constructor.Event.MOUSEENTER:e.constructor.Event.FOCUSIN,o=n===T.HOVER?e.constructor.Event.MOUSELEAVE:e.constructor.Event.FOCUSOUT;t(e.element).on(i,e.config.selector,function(t){return e._enter(t)}).on(o,e.config.selector,function(t){return e._leave(t)})}t(e.element).closest(".modal").on("hide.bs.modal",function(){return e.hide()})}),this.config.selector?this.config=t.extend({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},h.prototype._fixTitle=function(){var t=i(this.element.getAttribute("data-original-title"));(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},h.prototype._enter=function(e,n){var i=this.constructor.DATA_KEY;return n=n||t(e.currentTarget).data(i),n||(n=new this.constructor(e.currentTarget,this._getDelegateConfig()),t(e.currentTarget).data(i,n)),e&&(n._activeTrigger["focusin"===e.type?T.FOCUS:T.HOVER]=!0),t(n.getTipElement()).hasClass(m.SHOW)||n._hoverState===g.SHOW?void(n._hoverState=g.SHOW):(clearTimeout(n._timeout),n._hoverState=g.SHOW,n.config.delay&&n.config.delay.show?void(n._timeout=setTimeout(function(){n._hoverState===g.SHOW&&n.show()},n.config.delay.show)):void n.show())},h.prototype._leave=function(e,n){var i=this.constructor.DATA_KEY;if(n=n||t(e.currentTarget).data(i),n||(n=new this.constructor(e.currentTarget,this._getDelegateConfig()),t(e.currentTarget).data(i,n)),e&&(n._activeTrigger["focusout"===e.type?T.FOCUS:T.HOVER]=!1),!n._isWithActiveTrigger())return clearTimeout(n._timeout),n._hoverState=g.OUT,n.config.delay&&n.config.delay.hide?void(n._timeout=setTimeout(function(){n._hoverState===g.OUT&&n.hide()},n.config.delay.hide)):void n.hide()},h.prototype._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},h.prototype._getConfig=function(n){return n=t.extend({},this.constructor.Default,t(this.element).data(),n),n.delay&&"number"==typeof n.delay&&(n.delay={show:n.delay,hide:n.delay}),r.typeCheckConfig(e,n,this.constructor.DefaultType),n},h.prototype._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},h._jQueryInterface=function(e){return this.each(function(){var n=t(this).data(a),o="object"===("undefined"==typeof e?"undefined":i(e))&&e;if((n||!/dispose|hide/.test(e))&&(n||(n=new h(this,o),t(this).data(a,n)),"string"==typeof e)){if(void 0===n[e])throw new Error('No method named "'+e+'"');n[e]()}})},o(h,null,[{key:"VERSION",get:function(){return s}},{key:"Default",get:function(){return d}},{key:"NAME",get:function(){return e}},{key:"DATA_KEY",get:function(){return a}},{key:"Event",get:function(){return p}},{key:"EVENT_KEY",get:function(){return l}},{key:"DefaultType",get:function(){return f}}]),h}();return t.fn[e]=I._jQueryInterface,t.fn[e].Constructor=I,t.fn[e].noConflict=function(){return t.fn[e]=h,I._jQueryInterface},I}(jQuery));(function(r){var a="popover",l="4.0.0-alpha.6",h="bs.popover",c="."+h,u=r.fn[a],d=r.extend({},s.Default,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),f=r.extend({},s.DefaultType,{content:"(string|element|function)"}),_={FADE:"fade",SHOW:"show"},g={TITLE:".popover-title",CONTENT:".popover-content"},p={HIDE:"hide"+c,HIDDEN:"hidden"+c,SHOW:"show"+c,SHOWN:"shown"+c,INSERTED:"inserted"+c,CLICK:"click"+c,FOCUSIN:"focusin"+c,FOCUSOUT:"focusout"+c,MOUSEENTER:"mouseenter"+c,MOUSELEAVE:"mouseleave"+c},m=function(s){function u(){return n(this,u),t(this,s.apply(this,arguments))}return e(u,s),u.prototype.isWithContent=function(){return this.getTitle()||this._getContent()},u.prototype.getTipElement=function(){return this.tip=this.tip||r(this.config.template)[0]},u.prototype.setContent=function(){var t=r(this.getTipElement());this.setElementContent(t.find(g.TITLE),this.getTitle()),this.setElementContent(t.find(g.CONTENT),this._getContent()),t.removeClass(_.FADE+" "+_.SHOW),this.cleanupTether()},u.prototype._getContent=function(){return this.element.getAttribute("data-content")||("function"==typeof this.config.content?this.config.content.call(this.element):this.config.content)},u._jQueryInterface=function(t){return this.each(function(){var e=r(this).data(h),n="object"===("undefined"==typeof t?"undefined":i(t))?t:null;if((e||!/destroy|hide/.test(t))&&(e||(e=new u(this,n),r(this).data(h,e)),"string"==typeof t)){if(void 0===e[t])throw new Error('No method named "'+t+'"');e[t]()}})},o(u,null,[{key:"VERSION",get:function(){return l}},{key:"Default",get:function(){return d}},{key:"NAME",get:function(){return a}},{key:"DATA_KEY",get:function(){return h}},{key:"Event",get:function(){return p}},{key:"EVENT_KEY",get:function(){return c}},{key:"DefaultType",get:function(){return f}}]),u}(s);return r.fn[a]=m._jQueryInterface,r.fn[a].Constructor=m,r.fn[a].noConflict=function(){return r.fn[a]=u,m._jQueryInterface},m})(jQuery)}();
\ No newline at end of file
diff --git a/src/dependencies/jquery-3.1.1.slim.min.js b/src/dependencies/jquery-3.1.1.slim.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..a240ca9b2dc6a10c1d9f21a7cda477fd78a60f83
--- /dev/null
+++ b/src/dependencies/jquery-3.1.1.slim.min.js
@@ -0,0 +1,4 @@
+/*! jQuery v3.1.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/parseXML,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-event/ajax,-effects,-effects/animatedSelector,-effects/Tween,-deprecated | (c) jQuery Foundation | jquery.org/license */
+!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.1.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/parseXML,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-event/ajax,-effects,-effects/animatedSelector,-effects/Tween,-deprecated",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c<b?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:h,sort:c.sort,splice:c.splice},r.extend=r.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||r.isFunction(g)||(g={}),h===i&&(g=this,h--);h<i;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(r.isPlainObject(d)||(e=r.isArray(d)))?(e?(e=!1,f=c&&r.isArray(c)?c:[]):f=c&&r.isPlainObject(c)?c:{},g[b]=r.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},r.extend({expando:"jQuery"+(q+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===r.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=r.type(a);return("number"===b||"string"===b)&&!isNaN(a-parseFloat(a))},isPlainObject:function(a){var b,c;return!(!a||"[object Object]"!==k.call(a))&&(!(b=e(a))||(c=l.call(b,"constructor")&&b.constructor,"function"==typeof c&&m.call(c)===n))},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?j[k.call(a)]||"object":typeof a},globalEval:function(a){p(a)},camelCase:function(a){return a.replace(t,"ms-").replace(u,v)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(w(a)){for(c=a.length;d<c;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(s,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(w(Object(a))?r.merge(c,"string"==typeof a?[a]:a):h.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:i.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;d<c;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;f<g;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,f=0,h=[];if(w(a))for(d=a.length;f<d;f++)e=b(a[f],f,c),null!=e&&h.push(e);else for(f in a)e=b(a[f],f,c),null!=e&&h.push(e);return g.apply([],h)},guid:1,proxy:function(a,b){var c,d,e;if("string"==typeof b&&(c=a[b],b=a,a=c),r.isFunction(a))return d=f.call(arguments,2),e=function(){return a.apply(b||this,d.concat(f.call(arguments)))},e.guid=a.guid=a.guid||r.guid++,e},now:Date.now,support:o}),"function"==typeof Symbol&&(r.fn[Symbol.iterator]=c[Symbol.iterator]),r.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){j["[object "+b+"]"]=b.toLowerCase()});function w(a){var b=!!a&&"length"in a&&a.length,c=r.type(a);return"function"!==c&&!r.isWindow(a)&&("array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",M="\\["+K+"*("+L+")(?:"+K+"*([*^$|!~]?=)"+K+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+L+"))|)"+K+"*\\]",N=":("+L+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+M+")*)|.*)\\)|)",O=new RegExp(K+"+","g"),P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\r\\' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c<b;c+=2)a.push(c);return a}),odd:pa(function(a,b){for(var c=1;c<b;c+=2)a.push(c);return a}),lt:pa(function(a,b,c){for(var d=c<0?c+b:c;--d>=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=ma(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=na(b);function ra(){}ra.prototype=d.filters=d.pseudos,d.setFilters=new ra,g=ga.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){c&&!(e=Q.exec(h))||(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=R.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(P," ")}),h=h.slice(c.length));for(g in d.filter)!(e=V[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?ga.error(a):z(a,i).slice(0)};function sa(a){for(var b=0,c=a.length,d="";b<c;b++)d+=a[b].value;return d}function ta(a,b,c){var d=b.dir,e=b.next,f=e||d,g=c&&"parentNode"===f,h=x++;return b.first?function(b,c,e){while(b=b[d])if(1===b.nodeType||g)return a(b,c,e);return!1}:function(b,c,i){var j,k,l,m=[w,h];if(i){while(b=b[d])if((1===b.nodeType||g)&&a(b,c,i))return!0}else while(b=b[d])if(1===b.nodeType||g)if(l=b[u]||(b[u]={}),k=l[b.uniqueID]||(l[b.uniqueID]={}),e&&e===b.nodeName.toLowerCase())b=b[d]||b;else{if((j=k[f])&&j[0]===w&&j[1]===h)return m[2]=j[2];if(k[f]=m,m[2]=a(b,c,i))return!0}return!1}}function ua(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d<e;d++)ga(a,b[d],c);return c}function wa(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;h<i;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function xa(a,b,c,d,e,f){return d&&!d[u]&&(d=xa(d)),e&&!e[u]&&(e=xa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||va(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:wa(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=wa(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i<f;i++)if(c=d.relative[a[i].type])m=[ta(ua(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;e<f;e++)if(d.relative[a[e].type])break;return xa(i>1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i<e&&ya(a.slice(i,e)),e<f&&ya(a=a.slice(e)),e<f&&sa(a))}m.push(c)}return ua(m)}function za(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):C.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b<d;b++)if(r.contains(e[b],this))return!0}));for(c=this.pushStack([]),b=0;b<d;b++)r.find(a,e[b],c);return d>1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a<c;a++)if(r.contains(this,b[a]))return!0})},closest:function(a,b){var c,d=0,e=this.length,f=[],g="string"!=typeof a&&r(a);if(!A.test(a))for(;d<e;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/[^\x20\t\r\n\f]+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h<f.length)f[h].apply(c[0],c[1])===!1&&a.stopOnFalse&&(h=f.length,c=!1)}a.memory||(c=!1),b=!1,e&&(f=c?[]:"")},j={add:function(){return f&&(c&&!b&&(h=f.length-1,g.push(c)),function d(b){r.each(b,function(b,c){r.isFunction(c)?a.unique&&j.has(c)||f.push(c):c&&c.length&&"string"!==r.type(c)&&d(c)})}(arguments),c&&!b&&i()),this},remove:function(){return r.each(arguments,function(a,b){var c;while((c=r.inArray(b,f,c))>-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b<f)){if(a=d.apply(h,i),a===c.promise())throw new TypeError("Thenable self-resolution");j=a&&("object"==typeof a||"function"==typeof a)&&a.then,r.isFunction(j)?e?j.call(a,g(f,c,M,e),g(f,c,N,e)):(f++,j.call(a,g(f,c,M,e),g(f,c,N,e),g(f,c,M,c.notifyWith))):(d!==M&&(h=void 0,i=[a]),(e||c.resolveWith)(h,i))}},k=e?j:function(){try{j()}catch(a){r.Deferred.exceptionHook&&r.Deferred.exceptionHook(a,k.stackTrace),b+1>=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,
+holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R),a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h<i;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},T=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function U(){this.expando=r.expando+U.uid++}U.uid=1,U.prototype={cache:function(a){var b=a[this.expando];return b||(b={},T(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[r.camelCase(b)]=c;else for(d in b)e[r.camelCase(d)]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][r.camelCase(b)]},access:function(a,b,c){return void 0===b||b&&"string"==typeof b&&void 0===c?this.get(a,b):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d=a[this.expando];if(void 0!==d){if(void 0!==b){r.isArray(b)?b=b.map(r.camelCase):(b=r.camelCase(b),b=b in d?[b]:b.match(K)||[]),c=b.length;while(c--)delete d[b[c]]}(void 0===b||r.isEmptyObject(d))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!r.isEmptyObject(b)}};var V=new U,W=new U,X=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Y=/[A-Z]/g;function Z(a){return"true"===a||"false"!==a&&("null"===a?null:a===+a+""?+a:X.test(a)?JSON.parse(a):a)}function $(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Y,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c=Z(c)}catch(e){}W.set(a,b,c)}else c=void 0;return c}r.extend({hasData:function(a){return W.hasData(a)||V.hasData(a)},data:function(a,b,c){return W.access(a,b,c)},removeData:function(a,b){W.remove(a,b)},_data:function(a,b,c){return V.access(a,b,c)},_removeData:function(a,b){V.remove(a,b)}}),r.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=W.get(f),1===f.nodeType&&!V.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=r.camelCase(d.slice(5)),$(f,d,e[d])));V.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){W.set(this,a)}):S(this,function(b){var c;if(f&&void 0===b){if(c=W.get(f,a),void 0!==c)return c;if(c=$(f,a),void 0!==c)return c}else this.each(function(){W.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?r.queue(this[0],a):void 0===b?this:this.each(function(){var c=r.queue(this,a,b);r._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&r.dequeue(this,a)})},dequeue:function(a){return this.each(function(){r.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=r.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=V.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var _=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,aa=new RegExp("^(?:([+-])=|)("+_+")([a-z%]*)$","i"),ba=["Top","Right","Bottom","Left"],ca=function(a,b){return a=b||a,"none"===a.style.display||""===a.style.display&&r.contains(a.ownerDocument,a)&&"none"===r.css(a,"display")},da=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};function ea(a,b,c,d){var e,f=1,g=20,h=d?function(){return d.cur()}:function(){return r.css(a,b,"")},i=h(),j=c&&c[3]||(r.cssNumber[b]?"":"px"),k=(r.cssNumber[b]||"px"!==j&&+i)&&aa.exec(r.css(a,b));if(k&&k[3]!==j){j=j||k[3],c=c||[],k=+i||1;do f=f||".5",k/=f,r.style(a,b,k+j);while(f!==(f=h()/i)&&1!==f&&--g)}return c&&(k=+k||+i||0,e=c[1]?k+(c[1]+1)*c[2]:+c[2],d&&(d.unit=j,d.start=k,d.end=e)),e}var fa={};function ga(a){var b,c=a.ownerDocument,d=a.nodeName,e=fa[d];return e?e:(b=c.body.appendChild(c.createElement(d)),e=r.css(b,"display"),b.parentNode.removeChild(b),"none"===e&&(e="block"),fa[d]=e,e)}function ha(a,b){for(var c,d,e=[],f=0,g=a.length;f<g;f++)d=a[f],d.style&&(c=d.style.display,b?("none"===c&&(e[f]=V.get(d,"display")||null,e[f]||(d.style.display="")),""===d.style.display&&ca(d)&&(e[f]=ga(d))):"none"!==c&&(e[f]="none",V.set(d,"display",c)));for(f=0;f<g;f++)null!=e[f]&&(a[f].style.display=e[f]);return a}r.fn.extend({show:function(){return ha(this,!0)},hide:function(){return ha(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){ca(this)?r(this).show():r(this).hide()})}});var ia=/^(?:checkbox|radio)$/i,ja=/<([a-z][^\/\0>\x20\t\r\n\f]+)/i,ka=/^$|\/(?:java|ecma)script/i,la={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};la.optgroup=la.option,la.tbody=la.tfoot=la.colgroup=la.caption=la.thead,la.th=la.td;function ma(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function na(a,b){for(var c=0,d=a.length;c<d;c++)V.set(a[c],"globalEval",!b||V.get(b[c],"globalEval"))}var oa=/<|&#?\w+;/;function pa(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],n=0,o=a.length;n<o;n++)if(f=a[n],f||0===f)if("object"===r.type(f))r.merge(m,f.nodeType?[f]:f);else if(oa.test(f)){g=g||l.appendChild(b.createElement("div")),h=(ja.exec(f)||["",""])[1].toLowerCase(),i=la[h]||la._default,g.innerHTML=i[1]+r.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;r.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",n=0;while(f=m[n++])if(d&&r.inArray(f,d)>-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=ma(l.appendChild(f),"script"),j&&na(g),c){k=0;while(f=g[k++])ka.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var qa=d.documentElement,ra=/^key/,sa=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ta=/^([^.]*)(?:\.(.+)|)/;function ua(){return!0}function va(){return!1}function wa(){try{return d.activeElement}catch(a){}}function xa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)xa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=va;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(qa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c<arguments.length;c++)i[c]=arguments[c];if(b.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,b)!==!1){h=r.event.handlers.call(this,b,j),c=0;while((f=h[c++])&&!b.isPropagationStopped()){b.currentTarget=f.elem,d=0;while((g=f.handlers[d++])&&!b.isImmediatePropagationStopped())b.rnamespace&&!b.rnamespace.test(g.namespace)||(b.handleObj=g,b.data=g.data,e=((r.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(b.result=e)===!1&&(b.preventDefault(),b.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,b),b.result}},handlers:function(a,b){var c,d,e,f,g,h=[],i=b.delegateCount,j=a.target;if(i&&j.nodeType&&!("click"===a.type&&a.button>=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c<i;c++)d=b[c],e=d.selector+" ",void 0===g[e]&&(g[e]=d.needsContext?r(e,this).index(j)>-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i<b.length&&h.push({elem:j,handlers:b.slice(i)}),h},addProp:function(a,b){Object.defineProperty(r.Event.prototype,a,{enumerable:!0,configurable:!0,get:r.isFunction(b)?function(){if(this.originalEvent)return b(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[a]},set:function(b){Object.defineProperty(this,a,{enumerable:!0,configurable:!0,writable:!0,value:b})}})},fix:function(a){return a[r.expando]?a:new r.Event(a)},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==wa()&&this.focus)return this.focus(),!1},delegateType:"focusin"},blur:{trigger:function(){if(this===wa()&&this.blur)return this.blur(),!1},delegateType:"focusout"},click:{trigger:function(){if("checkbox"===this.type&&this.click&&r.nodeName(this,"input"))return this.click(),!1},_default:function(a){return r.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}}},r.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c)},r.Event=function(a,b){return this instanceof r.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?ua:va,this.target=a.target&&3===a.target.nodeType?a.target.parentNode:a.target,this.currentTarget=a.currentTarget,this.relatedTarget=a.relatedTarget):this.type=a,b&&r.extend(this,b),this.timeStamp=a&&a.timeStamp||r.now(),void(this[r.expando]=!0)):new r.Event(a,b)},r.Event.prototype={constructor:r.Event,isDefaultPrevented:va,isPropagationStopped:va,isImmediatePropagationStopped:va,isSimulated:!1,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=ua,a&&!this.isSimulated&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=ua,a&&!this.isSimulated&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=ua,a&&!this.isSimulated&&a.stopImmediatePropagation(),this.stopPropagation()}},r.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(a){var b=a.button;return null==a.which&&ra.test(a.type)?null!=a.charCode?a.charCode:a.keyCode:!a.which&&void 0!==b&&sa.test(a.type)?1&b?1:2&b?3:4&b?2:0:a.which}},r.event.addProp),r.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){r.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return e&&(e===d||r.contains(d,e))||(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),r.fn.extend({on:function(a,b,c,d){return xa(this,a,b,c,d)},one:function(a,b,c,d){return xa(this,a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,r(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return b!==!1&&"function"!=typeof b||(c=b,b=void 0),c===!1&&(c=va),this.each(function(){r.event.remove(this,a,c,b)})}});var ya=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,za=/<script|<style|<link/i,Aa=/checked\s*(?:[^=]|=\s*.checked.)/i,Ba=/^true\/(.*)/,Ca=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function Da(a,b){return r.nodeName(a,"table")&&r.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a:a}function Ea(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Fa(a){var b=Ba.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ga(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(V.hasData(a)&&(f=V.access(a),g=V.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c<d;c++)r.event.add(b,e,j[e][c])}W.hasData(a)&&(h=W.access(a),i=r.extend({},h),W.set(b,i))}}function Ha(a,b){var c=b.nodeName.toLowerCase();"input"===c&&ia.test(a.type)?b.checked=a.checked:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}function Ia(a,b,c,d){b=g.apply([],b);var e,f,h,i,j,k,l=0,m=a.length,n=m-1,q=b[0],s=r.isFunction(q);if(s||m>1&&"string"==typeof q&&!o.checkClone&&Aa.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ia(f,b,c,d)});if(m&&(e=pa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(ma(e,"script"),Ea),i=h.length;l<m;l++)j=e,l!==n&&(j=r.clone(j,!0,!0),i&&r.merge(h,ma(j,"script"))),c.call(a[l],j,l);if(i)for(k=h[h.length-1].ownerDocument,r.map(h,Fa),l=0;l<i;l++)j=h[l],ka.test(j.type||"")&&!V.access(j,"globalEval")&&r.contains(k,j)&&(j.src?r._evalUrl&&r._evalUrl(j.src):p(j.textContent.replace(Ca,""),k))}return a}function Ja(a,b,c){for(var d,e=b?r.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||r.cleanData(ma(d)),d.parentNode&&(c&&r.contains(d.ownerDocument,d)&&na(ma(d,"script")),d.parentNode.removeChild(d));return a}r.extend({htmlPrefilter:function(a){return a.replace(ya,"<$1></$2>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=ma(h),f=ma(a),d=0,e=f.length;d<e;d++)Ha(f[d],g[d]);if(b)if(c)for(f=f||ma(a),g=g||ma(h),d=0,e=f.length;d<e;d++)Ga(f[d],g[d]);else Ga(a,h);return g=ma(h,"script"),g.length>0&&na(g,!i&&ma(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(T(c)){if(b=c[V.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[V.expando]=void 0}c[W.expando]&&(c[W.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ja(this,a,!0)},remove:function(a){return Ja(this,a)},text:function(a){return S(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.appendChild(a)}})},prepend:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(ma(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return S(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!za.test(a)&&!la[(ja.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c<d;c++)b=this[c]||{},1===b.nodeType&&(r.cleanData(ma(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ia(this,arguments,function(b){var c=this.parentNode;r.inArray(this,a)<0&&(r.cleanData(ma(this)),c&&c.replaceChild(b,this))},a)}}),r.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){r.fn[a]=function(a){for(var c,d=[],e=r(a),f=e.length-1,g=0;g<=f;g++)c=g===f?this:this.clone(!0),r(e[g])[b](c),h.apply(d,c.get());return this.pushStack(d)}});var Ka=/^margin/,La=new RegExp("^("+_+")(?!px)[a-z%]+$","i"),Ma=function(b){var c=b.ownerDocument.defaultView;return c&&c.opener||(c=a),c.getComputedStyle(b)};!function(){function b(){if(i){i.style.cssText="box-sizing:border-box;position:relative;display:block;margin:auto;border:1px;padding:1px;top:1%;width:50%",i.innerHTML="",qa.appendChild(h);var b=a.getComputedStyle(i);c="1%"!==b.top,g="2px"===b.marginLeft,e="4px"===b.width,i.style.marginRight="50%",f="4px"===b.marginRight,qa.removeChild(h),i=null}}var c,e,f,g,h=d.createElement("div"),i=d.createElement("div");i.style&&(i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",o.clearCloneStyle="content-box"===i.style.backgroundClip,h.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",h.appendChild(i),r.extend(o,{pixelPosition:function(){return b(),c},boxSizingReliable:function(){return b(),e},pixelMarginRight:function(){return b(),f},reliableMarginLeft:function(){return b(),g}}))}();function Na(a,b,c){var d,e,f,g,h=a.style;return c=c||Ma(a),c&&(g=c.getPropertyValue(b)||c[b],""!==g||r.contains(a.ownerDocument,a)||(g=r.style(a,b)),!o.pixelMarginRight()&&La.test(g)&&Ka.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function Oa(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}var Pa=/^(none|table(?!-c[ea]).+)/,Qa={position:"absolute",visibility:"hidden",display:"block"},Ra={letterSpacing:"0",fontWeight:"400"},Sa=["Webkit","Moz","ms"],Ta=d.createElement("div").style;function Ua(a){if(a in Ta)return a;var b=a[0].toUpperCase()+a.slice(1),c=Sa.length;while(c--)if(a=Sa[c]+b,a in Ta)return a}function Va(a,b,c){var d=aa.exec(b);return d?Math.max(0,d[2]-(c||0))+(d[3]||"px"):b}function Wa(a,b,c,d,e){var f,g=0;for(f=c===(d?"border":"content")?4:"width"===b?1:0;f<4;f+=2)"margin"===c&&(g+=r.css(a,c+ba[f],!0,e)),d?("content"===c&&(g-=r.css(a,"padding"+ba[f],!0,e)),"margin"!==c&&(g-=r.css(a,"border"+ba[f]+"Width",!0,e))):(g+=r.css(a,"padding"+ba[f],!0,e),"padding"!==c&&(g+=r.css(a,"border"+ba[f]+"Width",!0,e)));return g}function Xa(a,b,c){var d,e=!0,f=Ma(a),g="border-box"===r.css(a,"boxSizing",!1,f);if(a.getClientRects().length&&(d=a.getBoundingClientRect()[b]),d<=0||null==d){if(d=Na(a,b,f),(d<0||null==d)&&(d=a.style[b]),La.test(d))return d;e=g&&(o.boxSizingReliable()||d===a.style[b]),d=parseFloat(d)||0}return d+Wa(a,b,c||(g?"border":"content"),e,f)+"px"}r.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Na(a,"opacity");return""===c?"1":c}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=r.camelCase(b),i=a.style;return b=r.cssProps[h]||(r.cssProps[h]=Ua(h)||h),g=r.cssHooks[b]||r.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b]:(f=typeof c,"string"===f&&(e=aa.exec(c))&&e[1]&&(c=ea(a,b,e),f="number"),null!=c&&c===c&&("number"===f&&(c+=e&&e[3]||(r.cssNumber[h]?"":"px")),o.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=r.camelCase(b);return b=r.cssProps[h]||(r.cssProps[h]=Ua(h)||h),g=r.cssHooks[b]||r.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=Na(a,b,d)),"normal"===e&&b in Ra&&(e=Ra[b]),""===c||c?(f=parseFloat(e),c===!0||isFinite(f)?f||0:e):e}}),r.each(["height","width"],function(a,b){r.cssHooks[b]={get:function(a,c,d){if(c)return!Pa.test(r.css(a,"display"))||a.getClientRects().length&&a.getBoundingClientRect().width?Xa(a,b,d):da(a,Qa,function(){return Xa(a,b,d)})},set:function(a,c,d){var e,f=d&&Ma(a),g=d&&Wa(a,b,d,"border-box"===r.css(a,"boxSizing",!1,f),f);return g&&(e=aa.exec(c))&&"px"!==(e[3]||"px")&&(a.style[b]=c,c=r.css(a,b)),Va(a,c,g)}}}),r.cssHooks.marginLeft=Oa(o.reliableMarginLeft,function(a,b){if(b)return(parseFloat(Na(a,"marginLeft"))||a.getBoundingClientRect().left-da(a,{marginLeft:0},function(){return a.getBoundingClientRect().left}))+"px"}),r.each({margin:"",padding:"",border:"Width"},function(a,b){r.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];d<4;d++)e[a+ba[d]+b]=f[d]||f[d-2]||f[0];return e}},Ka.test(a)||(r.cssHooks[a+b].set=Va)}),r.fn.extend({css:function(a,b){return S(this,function(a,b,c){var d,e,f={},g=0;if(r.isArray(b)){for(d=Ma(a),e=b.length;g<e;g++)f[b[g]]=r.css(a,b[g],!1,d);return f}return void 0!==c?r.style(a,b,c):r.css(a,b)},a,b,arguments.length>1)}}),r.fn.delay=function(b,c){return b=r.fx?r.fx.speeds[b]||b:b,c=c||"fx",this.queue(c,function(c,d){var e=a.setTimeout(c,b);d.stop=function(){a.clearTimeout(e)}})},function(){var a=d.createElement("input"),b=d.createElement("select"),c=b.appendChild(d.createElement("option"));a.type="checkbox",o.checkOn=""!==a.value,o.optSelected=c.selected,a=d.createElement("input"),a.value="t",a.type="radio",o.radioValue="t"===a.value}();var Ya,Za=r.expr.attrHandle;r.fn.extend({attr:function(a,b){return S(this,r.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?Ya:void 0)),void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&r.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(K);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),Ya={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=Za[b]||r.find.attr;Za[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=Za[g],Za[g]=e,e=null!=c(a,b,d)?g:null,Za[g]=f),e}});var $a=/^(?:input|select|textarea|button)$/i,_a=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return S(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):$a.test(a.nodeName)||_a.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function ab(a){var b=a.match(K)||[];return b.join(" ")}function bb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,bb(this)))});if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=bb(c),d=1===c.nodeType&&" "+ab(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=ab(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,bb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=bb(c),d=1===c.nodeType&&" "+ab(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=ab(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,bb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(K)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=bb(this),b&&V.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":V.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+ab(bb(c))+" ").indexOf(b)>-1)return!0;return!1}});var cb=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":r.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(cb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:ab(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d<i;d++)if(c=e[d],(c.selected||d===f)&&!c.disabled&&(!c.parentNode.disabled||!r.nodeName(c.parentNode,"optgroup"))){if(b=r(c).val(),g)return b;h.push(b)}return h},set:function(a,b){var c,d,e=a.options,f=r.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=r.inArray(r.valHooks.option.get(d),f)>-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(r.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var db=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!db.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,db.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(V.get(h,"events")||{})[b.type]&&V.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&T(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!T(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=V.access(d,b);e||d.addEventListener(a,c,!0),V.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=V.access(d,b)-1;e?V.access(d,b,e):(d.removeEventListener(a,c,!0),V.remove(d,b))}}});var eb=/\[\]$/,fb=/\r?\n/g,gb=/^(?:submit|button|image|reset|file)$/i,hb=/^(?:input|select|textarea|keygen)/i;function ib(a,b,c,d){var e;if(r.isArray(b))r.each(b,function(b,e){c||eb.test(a)?d(a,e):ib(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d);
+});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)ib(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(r.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)ib(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&hb.test(this.nodeName)&&!gb.test(a)&&(this.checked||!ia.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:r.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(fb,"\r\n")}}):{name:b.name,value:c.replace(fb,"\r\n")}}).get()}}),r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},o.createHTMLDocument=function(){var a=d.implementation.createHTMLDocument("").body;return a.innerHTML="<form></form><form></form>",2===a.childNodes.length}(),r.parseHTML=function(a,b,c){if("string"!=typeof a)return[];"boolean"==typeof b&&(c=b,b=!1);var e,f,g;return b||(o.createHTMLDocument?(b=d.implementation.createHTMLDocument(""),e=b.createElement("base"),e.href=d.location.href,b.head.appendChild(e)):b=d),f=B.exec(a),g=!c&&[],f?[b.createElement(f[1])]:(f=pa([a],b,g),g&&g.length&&r(g).remove(),r.merge([],f.childNodes))};function jb(a){return r.isWindow(a)?a:9===a.nodeType&&a.defaultView}r.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=r.css(a,"position"),l=r(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=r.css(a,"top"),i=r.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),r.isFunction(b)&&(b=b.call(a,c,r.extend({},h))),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},r.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){r.offset.setOffset(this,a,b)});var b,c,d,e,f=this[0];if(f)return f.getClientRects().length?(d=f.getBoundingClientRect(),d.width||d.height?(e=f.ownerDocument,c=jb(e),b=e.documentElement,{top:d.top+c.pageYOffset-b.clientTop,left:d.left+c.pageXOffset-b.clientLeft}):d):{top:0,left:0}},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===r.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),r.nodeName(a[0],"html")||(d=a.offset()),d={top:d.top+r.css(a[0],"borderTopWidth",!0),left:d.left+r.css(a[0],"borderLeftWidth",!0)}),{top:b.top-d.top-r.css(c,"marginTop",!0),left:b.left-d.left-r.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent;while(a&&"static"===r.css(a,"position"))a=a.offsetParent;return a||qa})}}),r.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c="pageYOffset"===b;r.fn[a]=function(d){return S(this,function(a,d,e){var f=jb(a);return void 0===e?f?f[b]:a[d]:void(f?f.scrollTo(c?f.pageXOffset:e,c?e:f.pageYOffset):a[d]=e)},a,d,arguments.length)}}),r.each(["top","left"],function(a,b){r.cssHooks[b]=Oa(o.pixelPosition,function(a,c){if(c)return c=Na(a,b),La.test(c)?r(a).position()[b]+"px":c})}),r.each({Height:"height",Width:"width"},function(a,b){r.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){r.fn[d]=function(e,f){var g=arguments.length&&(c||"boolean"!=typeof e),h=c||(e===!0||f===!0?"margin":"border");return S(this,function(b,c,e){var f;return r.isWindow(b)?0===d.indexOf("outer")?b["inner"+a]:b.document.documentElement["client"+a]:9===b.nodeType?(f=b.documentElement,Math.max(b.body["scroll"+a],f["scroll"+a],b.body["offset"+a],f["offset"+a],f["client"+a])):void 0===e?r.css(b,c,h):r.style(b,c,e,h)},b,g?e:void 0,g)}})}),"function"==typeof define&&define.amd&&define("jquery",[],function(){return r});var kb=a.jQuery,lb=a.$;return r.noConflict=function(b){return a.$===r&&(a.$=lb),b&&a.jQuery===r&&(a.jQuery=kb),r},b||(a.jQuery=a.$=r),r});
diff --git a/src/dependencies/tether.min.js b/src/dependencies/tether.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..d16b9b14eb8151c45f77b47eb73c27568248b3a6
--- /dev/null
+++ b/src/dependencies/tether.min.js
@@ -0,0 +1 @@
+!function(t,e){"function"==typeof define&&define.amd?define(e):"object"==typeof exports?module.exports=e(require,exports,module):t.Tether=e()}(this,function(t,e,o){"use strict";function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function n(t){var e=t.getBoundingClientRect(),o={};for(var i in e)o[i]=e[i];if(t.ownerDocument!==document){var r=t.ownerDocument.defaultView.frameElement;if(r){var s=n(r);o.top+=s.top,o.bottom+=s.top,o.left+=s.left,o.right+=s.left}}return o}function r(t){var e=getComputedStyle(t)||{},o=e.position,i=[];if("fixed"===o)return[t];for(var n=t;(n=n.parentNode)&&n&&1===n.nodeType;){var r=void 0;try{r=getComputedStyle(n)}catch(s){}if("undefined"==typeof r||null===r)return i.push(n),i;var a=r,f=a.overflow,l=a.overflowX,h=a.overflowY;/(auto|scroll)/.test(f+h+l)&&("absolute"!==o||["relative","absolute","fixed"].indexOf(r.position)>=0)&&i.push(n)}return i.push(t.ownerDocument.body),t.ownerDocument!==document&&i.push(t.ownerDocument.defaultView),i}function s(){A&&document.body.removeChild(A),A=null}function a(t){var e=void 0;t===document?(e=document,t=document.documentElement):e=t.ownerDocument;var o=e.documentElement,i=n(t),r=P();return i.top-=r.top,i.left-=r.left,"undefined"==typeof i.width&&(i.width=document.body.scrollWidth-i.left-i.right),"undefined"==typeof i.height&&(i.height=document.body.scrollHeight-i.top-i.bottom),i.top=i.top-o.clientTop,i.left=i.left-o.clientLeft,i.right=e.body.clientWidth-i.width-i.left,i.bottom=e.body.clientHeight-i.height-i.top,i}function f(t){return t.offsetParent||document.documentElement}function l(){if(M)return M;var t=document.createElement("div");t.style.width="100%",t.style.height="200px";var e=document.createElement("div");h(e.style,{position:"absolute",top:0,left:0,pointerEvents:"none",visibility:"hidden",width:"200px",height:"150px",overflow:"hidden"}),e.appendChild(t),document.body.appendChild(e);var o=t.offsetWidth;e.style.overflow="scroll";var i=t.offsetWidth;o===i&&(i=e.clientWidth),document.body.removeChild(e);var n=o-i;return M={width:n,height:n}}function h(){var t=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],e=[];return Array.prototype.push.apply(e,arguments),e.slice(1).forEach(function(e){if(e)for(var o in e)({}).hasOwnProperty.call(e,o)&&(t[o]=e[o])}),t}function d(t,e){if("undefined"!=typeof t.classList)e.split(" ").forEach(function(e){e.trim()&&t.classList.remove(e)});else{var o=new RegExp("(^| )"+e.split(" ").join("|")+"( |$)","gi"),i=c(t).replace(o," ");g(t,i)}}function p(t,e){if("undefined"!=typeof t.classList)e.split(" ").forEach(function(e){e.trim()&&t.classList.add(e)});else{d(t,e);var o=c(t)+(" "+e);g(t,o)}}function u(t,e){if("undefined"!=typeof t.classList)return t.classList.contains(e);var o=c(t);return new RegExp("(^| )"+e+"( |$)","gi").test(o)}function c(t){return t.className instanceof t.ownerDocument.defaultView.SVGAnimatedString?t.className.baseVal:t.className}function g(t,e){t.setAttribute("class",e)}function m(t,e,o){o.forEach(function(o){e.indexOf(o)===-1&&u(t,o)&&d(t,o)}),e.forEach(function(e){u(t,e)||p(t,e)})}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function v(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function y(t,e){var o=arguments.length<=2||void 0===arguments[2]?1:arguments[2];return t+o>=e&&e>=t-o}function b(){return"undefined"!=typeof performance&&"undefined"!=typeof performance.now?performance.now():+new Date}function w(){for(var t={top:0,left:0},e=arguments.length,o=Array(e),i=0;i<e;i++)o[i]=arguments[i];return o.forEach(function(e){var o=e.top,i=e.left;"string"==typeof o&&(o=parseFloat(o,10)),"string"==typeof i&&(i=parseFloat(i,10)),t.top+=o,t.left+=i}),t}function C(t,e){return"string"==typeof t.left&&t.left.indexOf("%")!==-1&&(t.left=parseFloat(t.left,10)/100*e.width),"string"==typeof t.top&&t.top.indexOf("%")!==-1&&(t.top=parseFloat(t.top,10)/100*e.height),t}function O(t,e){return"scrollParent"===e?e=t.scrollParents[0]:"window"===e&&(e=[pageXOffset,pageYOffset,innerWidth+pageXOffset,innerHeight+pageYOffset]),e===document&&(e=e.documentElement),"undefined"!=typeof e.nodeType&&!function(){var t=e,o=a(e),i=o,n=getComputedStyle(e);if(e=[i.left,i.top,o.width+i.left,o.height+i.top],t.ownerDocument!==document){var r=t.ownerDocument.defaultView;e[0]+=r.pageXOffset,e[1]+=r.pageYOffset,e[2]+=r.pageXOffset,e[3]+=r.pageYOffset}G.forEach(function(t,o){t=t[0].toUpperCase()+t.substr(1),"Top"===t||"Left"===t?e[o]+=parseFloat(n["border"+t+"Width"]):e[o]-=parseFloat(n["border"+t+"Width"])})}(),e}var E=function(){function t(t,e){for(var o=0;o<e.length;o++){var i=e[o];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}return function(e,o,i){return o&&t(e.prototype,o),i&&t(e,i),e}}(),x=void 0;"undefined"==typeof x&&(x={modules:[]});var A=null,T=function(){var t=0;return function(){return++t}}(),S={},P=function(){var t=A;t&&document.body.contains(t)||(t=document.createElement("div"),t.setAttribute("data-tether-id",T()),h(t.style,{top:0,left:0,position:"absolute"}),document.body.appendChild(t),A=t);var e=t.getAttribute("data-tether-id");return"undefined"==typeof S[e]&&(S[e]=n(t),k(function(){delete S[e]})),S[e]},M=null,W=[],k=function(t){W.push(t)},_=function(){for(var t=void 0;t=W.pop();)t()},B=function(){function t(){i(this,t)}return E(t,[{key:"on",value:function(t,e,o){var i=!(arguments.length<=3||void 0===arguments[3])&&arguments[3];"undefined"==typeof this.bindings&&(this.bindings={}),"undefined"==typeof this.bindings[t]&&(this.bindings[t]=[]),this.bindings[t].push({handler:e,ctx:o,once:i})}},{key:"once",value:function(t,e,o){this.on(t,e,o,!0)}},{key:"off",value:function(t,e){if("undefined"!=typeof this.bindings&&"undefined"!=typeof this.bindings[t])if("undefined"==typeof e)delete this.bindings[t];else for(var o=0;o<this.bindings[t].length;)this.bindings[t][o].handler===e?this.bindings[t].splice(o,1):++o}},{key:"trigger",value:function(t){if("undefined"!=typeof this.bindings&&this.bindings[t]){for(var e=0,o=arguments.length,i=Array(o>1?o-1:0),n=1;n<o;n++)i[n-1]=arguments[n];for(;e<this.bindings[t].length;){var r=this.bindings[t][e],s=r.handler,a=r.ctx,f=r.once,l=a;"undefined"==typeof l&&(l=this),s.apply(l,i),f?this.bindings[t].splice(e,1):++e}}}}]),t}();x.Utils={getActualBoundingClientRect:n,getScrollParents:r,getBounds:a,getOffsetParent:f,extend:h,addClass:p,removeClass:d,hasClass:u,updateClasses:m,defer:k,flush:_,uniqueId:T,Evented:B,getScrollBarSize:l,removeUtilElements:s};var z=function(){function t(t,e){var o=[],i=!0,n=!1,r=void 0;try{for(var s,a=t[Symbol.iterator]();!(i=(s=a.next()).done)&&(o.push(s.value),!e||o.length!==e);i=!0);}catch(f){n=!0,r=f}finally{try{!i&&a["return"]&&a["return"]()}finally{if(n)throw r}}return o}return function(e,o){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,o);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),E=function(){function t(t,e){for(var o=0;o<e.length;o++){var i=e[o];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}return function(e,o,i){return o&&t(e.prototype,o),i&&t(e,i),e}}(),j=function(t,e,o){for(var i=!0;i;){var n=t,r=e,s=o;i=!1,null===n&&(n=Function.prototype);var a=Object.getOwnPropertyDescriptor(n,r);if(void 0!==a){if("value"in a)return a.value;var f=a.get;if(void 0===f)return;return f.call(s)}var l=Object.getPrototypeOf(n);if(null===l)return;t=l,e=r,o=s,i=!0,a=l=void 0}};if("undefined"==typeof x)throw new Error("You must include the utils.js file before tether.js");var Y=x.Utils,r=Y.getScrollParents,a=Y.getBounds,f=Y.getOffsetParent,h=Y.extend,p=Y.addClass,d=Y.removeClass,m=Y.updateClasses,k=Y.defer,_=Y.flush,l=Y.getScrollBarSize,s=Y.removeUtilElements,L=function(){if("undefined"==typeof document)return"";for(var t=document.createElement("div"),e=["transform","WebkitTransform","OTransform","MozTransform","msTransform"],o=0;o<e.length;++o){var i=e[o];if(void 0!==t.style[i])return i}}(),D=[],X=function(){D.forEach(function(t){t.position(!1)}),_()};!function(){var t=null,e=null,o=null,i=function n(){return"undefined"!=typeof e&&e>16?(e=Math.min(e-16,250),void(o=setTimeout(n,250))):void("undefined"!=typeof t&&b()-t<10||(null!=o&&(clearTimeout(o),o=null),t=b(),X(),e=b()-t))};"undefined"!=typeof window&&"undefined"!=typeof window.addEventListener&&["resize","scroll","touchmove"].forEach(function(t){window.addEventListener(t,i)})}();var F={center:"center",left:"right",right:"left"},H={middle:"middle",top:"bottom",bottom:"top"},N={top:0,left:0,middle:"50%",center:"50%",bottom:"100%",right:"100%"},U=function(t,e){var o=t.left,i=t.top;return"auto"===o&&(o=F[e.left]),"auto"===i&&(i=H[e.top]),{left:o,top:i}},V=function(t){var e=t.left,o=t.top;return"undefined"!=typeof N[t.left]&&(e=N[t.left]),"undefined"!=typeof N[t.top]&&(o=N[t.top]),{left:e,top:o}},R=function(t){var e=t.split(" "),o=z(e,2),i=o[0],n=o[1];return{top:i,left:n}},q=R,I=function(t){function e(t){var o=this;i(this,e),j(Object.getPrototypeOf(e.prototype),"constructor",this).call(this),this.position=this.position.bind(this),D.push(this),this.history=[],this.setOptions(t,!1),x.modules.forEach(function(t){"undefined"!=typeof t.initialize&&t.initialize.call(o)}),this.position()}return v(e,t),E(e,[{key:"getClass",value:function(){var t=arguments.length<=0||void 0===arguments[0]?"":arguments[0],e=this.options.classes;return"undefined"!=typeof e&&e[t]?this.options.classes[t]:this.options.classPrefix?this.options.classPrefix+"-"+t:t}},{key:"setOptions",value:function(t){var e=this,o=arguments.length<=1||void 0===arguments[1]||arguments[1],i={offset:"0 0",targetOffset:"0 0",targetAttachment:"auto auto",classPrefix:"tether"};this.options=h(i,t);var n=this.options,s=n.element,a=n.target,f=n.targetModifier;if(this.element=s,this.target=a,this.targetModifier=f,"viewport"===this.target?(this.target=document.body,this.targetModifier="visible"):"scroll-handle"===this.target&&(this.target=document.body,this.targetModifier="scroll-handle"),["element","target"].forEach(function(t){if("undefined"==typeof e[t])throw new Error("Tether Error: Both element and target must be defined");"undefined"!=typeof e[t].jquery?e[t]=e[t][0]:"string"==typeof e[t]&&(e[t]=document.querySelector(e[t]))}),p(this.element,this.getClass("element")),this.options.addTargetClasses!==!1&&p(this.target,this.getClass("target")),!this.options.attachment)throw new Error("Tether Error: You must provide an attachment");this.targetAttachment=q(this.options.targetAttachment),this.attachment=q(this.options.attachment),this.offset=R(this.options.offset),this.targetOffset=R(this.options.targetOffset),"undefined"!=typeof this.scrollParents&&this.disable(),"scroll-handle"===this.targetModifier?this.scrollParents=[this.target]:this.scrollParents=r(this.target),this.options.enabled!==!1&&this.enable(o)}},{key:"getTargetBounds",value:function(){if("undefined"==typeof this.targetModifier)return a(this.target);if("visible"===this.targetModifier){if(this.target===document.body)return{top:pageYOffset,left:pageXOffset,height:innerHeight,width:innerWidth};var t=a(this.target),e={height:t.height,width:t.width,top:t.top,left:t.left};return e.height=Math.min(e.height,t.height-(pageYOffset-t.top)),e.height=Math.min(e.height,t.height-(t.top+t.height-(pageYOffset+innerHeight))),e.height=Math.min(innerHeight,e.height),e.height-=2,e.width=Math.min(e.width,t.width-(pageXOffset-t.left)),e.width=Math.min(e.width,t.width-(t.left+t.width-(pageXOffset+innerWidth))),e.width=Math.min(innerWidth,e.width),e.width-=2,e.top<pageYOffset&&(e.top=pageYOffset),e.left<pageXOffset&&(e.left=pageXOffset),e}if("scroll-handle"===this.targetModifier){var t=void 0,o=this.target;o===document.body?(o=document.documentElement,t={left:pageXOffset,top:pageYOffset,height:innerHeight,width:innerWidth}):t=a(o);var i=getComputedStyle(o),n=o.scrollWidth>o.clientWidth||[i.overflow,i.overflowX].indexOf("scroll")>=0||this.target!==document.body,r=0;n&&(r=15);var s=t.height-parseFloat(i.borderTopWidth)-parseFloat(i.borderBottomWidth)-r,e={width:15,height:.975*s*(s/o.scrollHeight),left:t.left+t.width-parseFloat(i.borderLeftWidth)-15},f=0;s<408&&this.target===document.body&&(f=-11e-5*Math.pow(s,2)-.00727*s+22.58),this.target!==document.body&&(e.height=Math.max(e.height,24));var l=this.target.scrollTop/(o.scrollHeight-s);return e.top=l*(s-e.height-f)+t.top+parseFloat(i.borderTopWidth),this.target===document.body&&(e.height=Math.max(e.height,24)),e}}},{key:"clearCache",value:function(){this._cache={}}},{key:"cache",value:function(t,e){return"undefined"==typeof this._cache&&(this._cache={}),"undefined"==typeof this._cache[t]&&(this._cache[t]=e.call(this)),this._cache[t]}},{key:"enable",value:function(){var t=this,e=arguments.length<=0||void 0===arguments[0]||arguments[0];this.options.addTargetClasses!==!1&&p(this.target,this.getClass("enabled")),p(this.element,this.getClass("enabled")),this.enabled=!0,this.scrollParents.forEach(function(e){e!==t.target.ownerDocument&&e.addEventListener("scroll",t.position)}),e&&this.position()}},{key:"disable",value:function(){var t=this;d(this.target,this.getClass("enabled")),d(this.element,this.getClass("enabled")),this.enabled=!1,"undefined"!=typeof this.scrollParents&&this.scrollParents.forEach(function(e){e.removeEventListener("scroll",t.position)})}},{key:"destroy",value:function(){var t=this;this.disable(),D.forEach(function(e,o){e===t&&D.splice(o,1)}),0===D.length&&s()}},{key:"updateAttachClasses",value:function(t,e){var o=this;t=t||this.attachment,e=e||this.targetAttachment;var i=["left","top","bottom","right","middle","center"];"undefined"!=typeof this._addAttachClasses&&this._addAttachClasses.length&&this._addAttachClasses.splice(0,this._addAttachClasses.length),"undefined"==typeof this._addAttachClasses&&(this._addAttachClasses=[]);var n=this._addAttachClasses;t.top&&n.push(this.getClass("element-attached")+"-"+t.top),t.left&&n.push(this.getClass("element-attached")+"-"+t.left),e.top&&n.push(this.getClass("target-attached")+"-"+e.top),e.left&&n.push(this.getClass("target-attached")+"-"+e.left);var r=[];i.forEach(function(t){r.push(o.getClass("element-attached")+"-"+t),r.push(o.getClass("target-attached")+"-"+t)}),k(function(){"undefined"!=typeof o._addAttachClasses&&(m(o.element,o._addAttachClasses,r),o.options.addTargetClasses!==!1&&m(o.target,o._addAttachClasses,r),delete o._addAttachClasses)})}},{key:"position",value:function(){var t=this,e=arguments.length<=0||void 0===arguments[0]||arguments[0];if(this.enabled){this.clearCache();var o=U(this.targetAttachment,this.attachment);this.updateAttachClasses(this.attachment,o);var i=this.cache("element-bounds",function(){return a(t.element)}),n=i.width,r=i.height;if(0===n&&0===r&&"undefined"!=typeof this.lastSize){var s=this.lastSize;n=s.width,r=s.height}else this.lastSize={width:n,height:r};var h=this.cache("target-bounds",function(){return t.getTargetBounds()}),d=h,p=C(V(this.attachment),{width:n,height:r}),u=C(V(o),d),c=C(this.offset,{width:n,height:r}),g=C(this.targetOffset,d);p=w(p,c),u=w(u,g);for(var m=h.left+u.left-p.left,v=h.top+u.top-p.top,y=0;y<x.modules.length;++y){var b=x.modules[y],O=b.position.call(this,{left:m,top:v,targetAttachment:o,targetPos:h,elementPos:i,offset:p,targetOffset:u,manualOffset:c,manualTargetOffset:g,scrollbarSize:S,attachment:this.attachment});if(O===!1)return!1;"undefined"!=typeof O&&"object"==typeof O&&(v=O.top,m=O.left)}var E={page:{top:v,left:m},viewport:{top:v-pageYOffset,bottom:pageYOffset-v-r+innerHeight,left:m-pageXOffset,right:pageXOffset-m-n+innerWidth}},A=this.target.ownerDocument,T=A.defaultView,S=void 0;return T.innerHeight>A.documentElement.clientHeight&&(S=this.cache("scrollbar-size",l),E.viewport.bottom-=S.height),T.innerWidth>A.documentElement.clientWidth&&(S=this.cache("scrollbar-size",l),E.viewport.right-=S.width),["","static"].indexOf(A.body.style.position)!==-1&&["","static"].indexOf(A.body.parentElement.style.position)!==-1||(E.page.bottom=A.body.scrollHeight-v-r,E.page.right=A.body.scrollWidth-m-n),"undefined"!=typeof this.options.optimizations&&this.options.optimizations.moveElement!==!1&&"undefined"==typeof this.targetModifier&&!function(){var e=t.cache("target-offsetparent",function(){return f(t.target)}),o=t.cache("target-offsetparent-bounds",function(){return a(e)}),i=getComputedStyle(e),n=o,r={};if(["Top","Left","Bottom","Right"].forEach(function(t){r[t.toLowerCase()]=parseFloat(i["border"+t+"Width"])}),o.right=A.body.scrollWidth-o.left-n.width+r.right,o.bottom=A.body.scrollHeight-o.top-n.height+r.bottom,E.page.top>=o.top+r.top&&E.page.bottom>=o.bottom&&E.page.left>=o.left+r.left&&E.page.right>=o.right){var s=e.scrollTop,l=e.scrollLeft;E.offset={top:E.page.top-o.top+s-r.top,left:E.page.left-o.left+l-r.left}}}(),this.move(E),this.history.unshift(E),this.history.length>3&&this.history.pop(),e&&_(),!0}}},{key:"move",value:function(t){var e=this;if("undefined"!=typeof this.element.parentNode){var o={};for(var i in t){o[i]={};for(var n in t[i]){for(var r=!1,s=0;s<this.history.length;++s){var a=this.history[s];if("undefined"!=typeof a[i]&&!y(a[i][n],t[i][n])){r=!0;break}}r||(o[i][n]=!0)}}var l={top:"",left:"",right:"",bottom:""},d=function(t,o){var i="undefined"!=typeof e.options.optimizations,n=i?e.options.optimizations.gpu:null;if(n!==!1){var r=void 0,s=void 0;if(t.top?(l.top=0,r=o.top):(l.bottom=0,r=-o.bottom),t.left?(l.left=0,s=o.left):(l.right=0,s=-o.right),window.matchMedia){var a=window.matchMedia("only screen and (min-resolution: 1.3dppx)").matches||window.matchMedia("only screen and (-webkit-min-device-pixel-ratio: 1.3)").matches;a||(s=Math.round(s),r=Math.round(r))}l[L]="translateX("+s+"px) translateY("+r+"px)","msTransform"!==L&&(l[L]+=" translateZ(0)")}else t.top?l.top=o.top+"px":l.bottom=o.bottom+"px",t.left?l.left=o.left+"px":l.right=o.right+"px"},p=!1;if((o.page.top||o.page.bottom)&&(o.page.left||o.page.right)?(l.position="absolute",d(o.page,t.page)):(o.viewport.top||o.viewport.bottom)&&(o.viewport.left||o.viewport.right)?(l.position="fixed",d(o.viewport,t.viewport)):"undefined"!=typeof o.offset&&o.offset.top&&o.offset.left?!function(){l.position="absolute";var i=e.cache("target-offsetparent",function(){return f(e.target)});f(e.element)!==i&&k(function(){e.element.parentNode.removeChild(e.element),i.appendChild(e.element)}),d(o.offset,t.offset),p=!0}():(l.position="absolute",d({top:!0,left:!0},t.page)),!p)if(this.options.bodyElement)this.options.bodyElement.appendChild(this.element);else{for(var u=!0,c=this.element.parentNode;c&&1===c.nodeType&&"BODY"!==c.tagName;){if("static"!==getComputedStyle(c).position){u=!1;break}c=c.parentNode}u||(this.element.parentNode.removeChild(this.element),this.element.ownerDocument.body.appendChild(this.element))}var g={},m=!1;for(var n in l){var v=l[n],b=this.element.style[n];b!==v&&(m=!0,g[n]=v)}m&&k(function(){h(e.element.style,g),e.trigger("repositioned")})}}}]),e}(B);I.modules=[],x.position=X;var $=h(I,x),z=function(){function t(t,e){var o=[],i=!0,n=!1,r=void 0;try{for(var s,a=t[Symbol.iterator]();!(i=(s=a.next()).done)&&(o.push(s.value),!e||o.length!==e);i=!0);}catch(f){n=!0,r=f}finally{try{!i&&a["return"]&&a["return"]()}finally{if(n)throw r}}return o}return function(e,o){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,o);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),Y=x.Utils,a=Y.getBounds,h=Y.extend,m=Y.updateClasses,k=Y.defer,G=["left","top","right","bottom"];x.modules.push({position:function(t){var e=this,o=t.top,i=t.left,n=t.targetAttachment;if(!this.options.constraints)return!0;var r=this.cache("element-bounds",function(){return a(e.element)}),s=r.height,f=r.width;if(0===f&&0===s&&"undefined"!=typeof this.lastSize){var l=this.lastSize;f=l.width,s=l.height}var d=this.cache("target-bounds",function(){return e.getTargetBounds()}),p=d.height,u=d.width,c=[this.getClass("pinned"),this.getClass("out-of-bounds")];this.options.constraints.forEach(function(t){var e=t.outOfBoundsClass,o=t.pinnedClass;e&&c.push(e),o&&c.push(o)}),c.forEach(function(t){["left","top","right","bottom"].forEach(function(e){c.push(t+"-"+e)})});var g=[],v=h({},n),y=h({},this.attachment);return this.options.constraints.forEach(function(t){var r=t.to,a=t.attachment,l=t.pin;"undefined"==typeof a&&(a="");var h=void 0,d=void 0;if(a.indexOf(" ")>=0){var c=a.split(" "),m=z(c,2);d=m[0],h=m[1]}else h=d=a;var b=O(e,r);"target"!==d&&"both"!==d||(o<b[1]&&"top"===v.top&&(o+=p,v.top="bottom"),o+s>b[3]&&"bottom"===v.top&&(o-=p,v.top="top")),"together"===d&&("top"===v.top&&("bottom"===y.top&&o<b[1]?(o+=p,v.top="bottom",o+=s,y.top="top"):"top"===y.top&&o+s>b[3]&&o-(s-p)>=b[1]&&(o-=s-p,v.top="bottom",y.top="bottom")),"bottom"===v.top&&("top"===y.top&&o+s>b[3]?(o-=p,v.top="top",o-=s,y.top="bottom"):"bottom"===y.top&&o<b[1]&&o+(2*s-p)<=b[3]&&(o+=s-p,v.top="top",y.top="top")),"middle"===v.top&&(o+s>b[3]&&"top"===y.top?(o-=s,y.top="bottom"):o<b[1]&&"bottom"===y.top&&(o+=s,y.top="top"))),"target"!==h&&"both"!==h||(i<b[0]&&"left"===v.left&&(i+=u,v.left="right"),i+f>b[2]&&"right"===v.left&&(i-=u,v.left="left")),"together"===h&&(i<b[0]&&"left"===v.left?"right"===y.left?(i+=u,v.left="right",i+=f,y.left="left"):"left"===y.left&&(i+=u,v.left="right",i-=f,y.left="right"):i+f>b[2]&&"right"===v.left?"left"===y.left?(i-=u,v.left="left",i-=f,y.left="right"):"right"===y.left&&(i-=u,v.left="left",i+=f,y.left="left"):"center"===v.left&&(i+f>b[2]&&"left"===y.left?(i-=f,y.left="right"):i<b[0]&&"right"===y.left&&(i+=f,y.left="left"))),"element"!==d&&"both"!==d||(o<b[1]&&"bottom"===y.top&&(o+=s,y.top="top"),o+s>b[3]&&"top"===y.top&&(o-=s,y.top="bottom")),"element"!==h&&"both"!==h||(i<b[0]&&("right"===y.left?(i+=f,y.left="left"):"center"===y.left&&(i+=f/2,y.left="left")),i+f>b[2]&&("left"===y.left?(i-=f,y.left="right"):"center"===y.left&&(i-=f/2,y.left="right"))),"string"==typeof l?l=l.split(",").map(function(t){return t.trim()}):l===!0&&(l=["top","left","right","bottom"]),l=l||[];var w=[],C=[];o<b[1]&&(l.indexOf("top")>=0?(o=b[1],w.push("top")):C.push("top")),o+s>b[3]&&(l.indexOf("bottom")>=0?(o=b[3]-s,w.push("bottom")):C.push("bottom")),i<b[0]&&(l.indexOf("left")>=0?(i=b[0],w.push("left")):C.push("left")),i+f>b[2]&&(l.indexOf("right")>=0?(i=b[2]-f,w.push("right")):C.push("right")),w.length&&!function(){var t=void 0;t="undefined"!=typeof e.options.pinnedClass?e.options.pinnedClass:e.getClass("pinned"),g.push(t),w.forEach(function(e){g.push(t+"-"+e)})}(),C.length&&!function(){var t=void 0;t="undefined"!=typeof e.options.outOfBoundsClass?e.options.outOfBoundsClass:e.getClass("out-of-bounds"),g.push(t),C.forEach(function(e){g.push(t+"-"+e)})}(),(w.indexOf("left")>=0||w.indexOf("right")>=0)&&(y.left=v.left=!1),(w.indexOf("top")>=0||w.indexOf("bottom")>=0)&&(y.top=v.top=!1),v.top===n.top&&v.left===n.left&&y.top===e.attachment.top&&y.left===e.attachment.left||(e.updateAttachClasses(y,v),e.trigger("update",{attachment:y,targetAttachment:v}))}),k(function(){e.options.addTargetClasses!==!1&&m(e.target,g,c),m(e.element,g,c)}),{top:o,left:i}}});var Y=x.Utils,a=Y.getBounds,m=Y.updateClasses,k=Y.defer;x.modules.push({position:function(t){var e=this,o=t.top,i=t.left,n=this.cache("element-bounds",function(){return a(e.element)}),r=n.height,s=n.width,f=this.getTargetBounds(),l=o+r,h=i+s,d=[];o<=f.bottom&&l>=f.top&&["left","right"].forEach(function(t){var e=f[t];e!==i&&e!==h||d.push(t)}),i<=f.right&&h>=f.left&&["top","bottom"].forEach(function(t){var e=f[t];e!==o&&e!==l||d.push(t)});var p=[],u=[],c=["left","top","right","bottom"];return p.push(this.getClass("abutted")),c.forEach(function(t){p.push(e.getClass("abutted")+"-"+t)}),d.length&&u.push(this.getClass("abutted")),d.forEach(function(t){u.push(e.getClass("abutted")+"-"+t)}),k(function(){e.options.addTargetClasses!==!1&&m(e.target,u,p),m(e.element,u,p)}),!0}});var z=function(){function t(t,e){var o=[],i=!0,n=!1,r=void 0;try{for(var s,a=t[Symbol.iterator]();!(i=(s=a.next()).done)&&(o.push(s.value),!e||o.length!==e);i=!0);}catch(f){n=!0,r=f}finally{try{!i&&a["return"]&&a["return"]()}finally{if(n)throw r}}return o}return function(e,o){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,o);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();return x.modules.push({position:function(t){var e=t.top,o=t.left;if(this.options.shift){var i=this.options.shift;"function"==typeof this.options.shift&&(i=this.options.shift.call(this,{top:e,left:o}));var n=void 0,r=void 0;if("string"==typeof i){i=i.split(" "),i[1]=i[1]||i[0];var s=i,a=z(s,2);n=a[0],r=a[1],n=parseFloat(n,10),r=parseFloat(r,10)}else n=i.top,r=i.left;return e+=n,o+=r,{top:e,left:o}}}}),$});
\ No newline at end of file
diff --git a/src/index.html b/src/index.html
index ec92ef2fa5dbe578010c48a682fe3ff3388e0611..27c8202c3c313c5a6467c9901d970f25a83c7bc7 100644
--- a/src/index.html
+++ b/src/index.html
@@ -12,11 +12,16 @@
   <link rel="stylesheet" href="assets/font-awesome-4.7.0/css/font-awesome.css">
 
   <!-- 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 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 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 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 -->
diff --git a/src/locale/error_messages.en.json b/src/locale/error_messages.en.json
index 90b1704b7e51798c7f5b25b516a97e04587db46f..9523f6897bfa88d46dfd85abdbd98606ff2fb35b 100644
--- a/src/locale/error_messages.en.json
+++ b/src/locale/error_messages.en.json
@@ -79,5 +79,6 @@
     "INFO_OPTION_YES": "Yes",
     "INFO_OPTION_NO": "No",
     "INFO_PABDIM_TITRE": "Pool pass: dimensions",
-    "INFO_PABPUISS_TITRE": "Pool pass: dissipated power"
+    "INFO_PABPUISS_TITRE": "Pool pass: dissipated power",
+    "INFO_OUVRAGEPARAL_TITRE": "Parallel structures"
 }
\ No newline at end of file
diff --git a/src/locale/error_messages.fr.json b/src/locale/error_messages.fr.json
index 0bbc5c5cab8c715ad9de510707ee27c4bc0961de..19f661f1b5e0ec800e7b9e9e00f1496746574afd 100644
--- a/src/locale/error_messages.fr.json
+++ b/src/locale/error_messages.fr.json
@@ -85,5 +85,6 @@
     "INFO_OPTION_YES": "Oui",
     "INFO_OPTION_NO": "Non",
     "INFO_PABDIM_TITRE": "Passe à bassin&nbsp;: dimensions",
-    "INFO_PABPUISS_TITRE": "Passe à bassin&nbsp;: puissance dissipée"
+    "INFO_PABPUISS_TITRE": "Passe à bassin&nbsp;: puissance dissipée",
+    "INFO_OUVRAGEPARAL_TITRE": "Ouvrages en parallèle"
 }
\ No newline at end of file