]> SALOME platform Git repositories - tools/eficas.git/commitdiff
Salome HOME
Première implémentation des optionnels
authorEric Fayolle <eric.fayolle@edf.fr>
Mon, 24 Jan 2022 17:51:13 +0000 (18:51 +0100)
committerEric Fayolle <eric.fayolle@edf.fr>
Mon, 24 Jan 2022 17:51:13 +0000 (18:51 +0100)
testFlask/mdm.py
testFlask/templates/base.html
testFlask/templates/commandes_2.html

index 91717df2de98c9ffa89bfaedba03a78d39d70783..eb32e1d5714825acfe22f5e8665f0e9033bd3003 100755 (executable)
@@ -3,14 +3,15 @@
 import sys
 sys.path.append('/home/eric/FLASK/eficas.eecj.git/testFlask')
 from connectEficas import accasConnecteur
+
 code='Essai'
 
 from flask import Flask, request, render_template, url_for, jsonify, make_response, session, g, Response
 # from flask import Flask, request, render_template, url_for, json, jsonify
 import json
-import pprint
+from   pprint           import pprint
 from   forms           import BasicForm
-from   collections   import OrderedDict
+from   collections    import OrderedDict
 from   markupsafe  import escape
 
 from flask_sse import sse
@@ -49,7 +50,7 @@ def fromConnecteur(maFonction,*args,**kwargs):
 #         gérer un appel register callback
 app.fromConnecteur=fromConnecteur
 
-## SSE from Eficas signals :
+## ServerSideEvent from Eficas signals :
 #    - Validite
 #    - Ajouter un noeud   (et ses enfants)
 #    - Supprimer un noeud (et ses enfants),
@@ -169,14 +170,16 @@ def index():
     
     # myNewTreeDico=OrderedDict([('text', 'MonProc2'), ('nodes', OrderedDict([('text', 'MonProc22'), ('nodes', [{'text': 'param1 1.0'}, [OrderedDict([('text', 'Fact1'), ('nodes', [{'text': 'param3 43.0'}])]), OrderedDict([('text', 'Fact1'), ('nodes', [{'text': 'param3 44.0'}])])]])]))])
     # myNewTreeJS=json.dumps([myNewTreeDico])
-    print("---- myTreeDico : ")
-    pprint.pprint(myTreeDico)
-    print("---- myTreeJS : ", myTreeJS)
-    print("---- tree4Fancy     : ", tree4Fancy)
+    print("---- myTreeDico : ")
+    pprint.pprint(myTreeDico)
+    print("---- myTreeJS : ", myTreeJS)
+    print("---- tree4Fancy     : ", tree4Fancy)
     # print("---- myNewTreeDico : ", myNewTreeDico)
     # print("---- myNewTreeJS : ", myNewTreeJS)
-    print("---- myFancyTreeDico : ", myFancyTreeDico)
-    print("---- myFancyTreeJS : ", myFancyTreeJS)
+    print("---- myFancyTreeDico ----")
+    pprint(myFancyTreeDico)
+    print("---- myFancyTreeJS ----")
+    pprint( myFancyTreeJS)
 
     return render_template('commandes_2.html',
       titre=code,
@@ -228,6 +231,24 @@ def updateSimp():
         #return make_response(jsonify({"message": "Request body must be JSON"}), 400)
     
     
+@app.route("/removeNode", methods=['POST'])
+def removeNode():
+    # Validate the request body contains JSON
+    if request.is_json:
+        # Parse the JSON into a Python dictionary
+        req = request.get_json()
+        # Print the dictionary
+        print("/removeNode ",req);print("/removeNode ",req['id']);
+        id   =req['id'];
+        ret,message = monConnecteur.suppNode(id);
+        print ("/removeNode : ret : ",ret," message : ",message)
+        
+        return make_response(json.dumps( {'ret':ret} ))
+    else:
+        # The request body wasn't JSON so return a 400 HTTP status code
+        return "Request was not JSON", 400
+        #return make_response(jsonify({"message": "Request body must be JSON"}), 400)
+
 @app.route("/appendChild", methods=['POST'])
 def appendChild():
     # Validate the request body contains JSON
@@ -235,7 +256,7 @@ def appendChild():
         # Parse the JSON into a Python dictionary
         req = request.get_json()
         # Print the dictionary
-        print(req);print(req['id'])
+        print(req);print(req['id']);print(req['name']);print(req['pos'])
         id=req['id'];name=req['name'];pos=req['pos'];
         # id, value = req.values() # Dangereux correspondance implicite
         #rId,message,changeDone  = monConnecteur.appendChild(id,name,pos);
index 61bb3c9eacf7bb0bcf947dcc37ee01307d68154b..c6f5262f9aee73b9ea648e7fb6e23a6e6e8ac147 100644 (file)
@@ -5,7 +5,10 @@
     <meta charset="utf-8">
 
     <meta name="viewport" content="width=device-width, initial-scale=1">
-    
+
+    <!-- Include glyph font definitions, for example matching `preset: "awesome5"` -->
+    <!-- <link href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" rel="stylesheet"> -->
+      
     <!-- <script src="{{ url_for('static', filename='jquery/dist/jquery.js') }}"></script> -->
 
     <!-- jQuery library -->
index b1f06e9dafeca056b736ad3300a697f71dbb0995..934c9e0d7b416399ee08f4fe6563aa3460e3f5ab 100644 (file)
@@ -7,7 +7,7 @@
 <!--     <button id="bt1">Get External Content</button> -->
 
 
-  
+
 
 <!--     {%- for key, value in mcTraite.items() recursive %} -->
 <!--   <h3>{{ key }} </h3> -->
 
 <!-- <div id="tree2"></div> -->
 
-       <!-- <div class="row"> -->
-        <!-- <hr> -->
-       <!-- <h2>MyDataModel : Testing the WebAPP approach</h2> -->
+<!-- <div class="row"> -->
+<!-- <hr> -->
+<!-- <h2>MyDataModel : Testing the WebAPP approach</h2> -->
+
+<!--  <div class="col-sm-4"> -->
+<!--    <h2>Searching</h2> -->
+<!--    <\!-- <form> -\-> -->
+<!--      <div class="form-group"> -->
+<!--        <label for="input-search" class="sr-only">Search Tree:</label> -->
+<!--        <input type="input" class="form-control" id="input-search" placeholder="Type to search..." value=""> -->
+<!--      </div> -->
+<!--      <div class="checkbox"> -->
+<!--        <label> -->
+<!--          <input type="checkbox" class="checkbox" id="chk-ignore-case" value="false"> -->
+<!--          Ignore Case -->
+<!--        </label> -->
+<!--      </div> -->
+<!--      <div class="checkbox"> -->
+<!--        <label> -->
+<!--          <input type="checkbox" class="checkbox" id="chk-exact-match" value="false"> -->
+<!--          Exact Match -->
+<!--        </label> -->
+<!--      </div> -->
+<!--      <div class="checkbox"> -->
+<!--        <label> -->
+<!--          <input type="checkbox" class="checkbox" id="chk-reveal-results" value="false"> -->
+<!--          Reveal Results -->
+<!--        </label> -->
+<!--      </div> -->
+<!--      <button type="button" class="btn btn-success" id="btn-search">Search</button> -->
+<!--      <button type="button" class="btn btn-default" id="btn-clear-search">Clear</button> -->
+<!--    <\!-- </form> -\-> -->
+<!--  </div> -->
+<!--  <div class="col-sm-4"> -->
+<!--    <h2>Results</h2> -->
+<!--    <div id="search-output"></div> -->
+<!-- </div> -->
+<!-- </div> -->
+
+<div class="row">
+  <div class="col-sm-12 panel panel-default">
+    <!-- <h2>Choose Object to add</h2> -->
     
-       <!--  <div class="col-sm-4"> -->
-       <!--    <h2>Searching</h2> -->
-       <!--    <\!-- <form> -\-> -->
-       <!--      <div class="form-group"> -->
-       <!--        <label for="input-search" class="sr-only">Search Tree:</label> -->
-       <!--        <input type="input" class="form-control" id="input-search" placeholder="Type to search..." value=""> -->
-       <!--      </div> -->
-       <!--      <div class="checkbox"> -->
-       <!--        <label> -->
-       <!--          <input type="checkbox" class="checkbox" id="chk-ignore-case" value="false"> -->
-       <!--          Ignore Case -->
-       <!--        </label> -->
-       <!--      </div> -->
-       <!--      <div class="checkbox"> -->
-       <!--        <label> -->
-       <!--          <input type="checkbox" class="checkbox" id="chk-exact-match" value="false"> -->
-       <!--          Exact Match -->
-       <!--        </label> -->
-       <!--      </div> -->
-       <!--      <div class="checkbox"> -->
-       <!--        <label> -->
-       <!--          <input type="checkbox" class="checkbox" id="chk-reveal-results" value="false"> -->
-       <!--          Reveal Results -->
-       <!--        </label> -->
-       <!--      </div> -->
-       <!--      <button type="button" class="btn btn-success" id="btn-search">Search</button> -->
-       <!--      <button type="button" class="btn btn-default" id="btn-clear-search">Clear</button> -->
-       <!--    <\!-- </form> -\-> -->
-       <!--  </div> -->
-       <!--  <div class="col-sm-4"> -->
-       <!--    <h2>Results</h2> -->
-       <!--    <div id="search-output"></div> -->
-        <!-- </div> -->
-       <!-- </div> -->
-       <div class="row">
-         <div class="col-sm-12 panel panel-default">
-           <!-- <h2>Choose Object to add</h2> -->
-          
-          <!--     <h1>Choose command to add </h1> -->
-          <!-- <h2>Commandes en ligne </h2>  -->
-          <!-- <ul> -->
-          <!--   {% for commande in listeCommandes %} -->
-           <!--   <li>{{ commande|e }}</li> -->
-           <!--   {% endfor %} -->
-          <!-- </ul> -->
-          <!-- <h2>Commandes en checkbutton </h2> -->
-          
-          {% for commande in listeCommandes %}
-             <!-- PN : pourquoi  peut-il  avoir plusieurs cheched -> à cause de name -->
-             <div class="btn-group" data-toggle="buttons" onclick="clickOnCommand( '#tree1','#tree1-messages', '{{ commande }}' )">
-               <!-- <input type="radio" name={{ commande }} id={{ commande }} > -->
-               <!-- <input type="radio" class="form-check-input" name='Etapes' id={{ commande }} > -->
-               <label class="btn btn-primary activate" for={{ commande }}> {{ commande }}</label><!-- <br> -->
-
-              <!-- <label class="btn btn-primary" for={{ commande }}> -->
-               <!--   <input class="form_check_input" type="radio" name='Etapes' id={{ commande }} > {{ commande }} -->
-              <!-- </label> -->
-              
-             </div>
-          {% endfor %}
-        </div>
-       </div>
-        <!-- <div class="row panel panel-default"> -->
-         <!--     <h2> </h2> -->
-         <!--     <\!-- <div id="treeview-searchable" class=""></div> -\-> -->
-        <!-- </div> -->
-        <div class="row">         
-            <div class="col-sm-8 panel panel-default">
-             <div class="panel-heading">DatasetName : {{titre}}</div>
+    <!--     <h1>Choose command to add </h1> -->
+    <!-- <h2>Commandes en ligne </h2>  -->
+    <!-- <ul> -->
+    <!--   {% for commande in listeCommandes %} -->
+    <!--   <li>{{ commande|e }}</li> -->
+    <!--   {% endfor %} -->
+    <!-- </ul> -->
+    <!-- <h2>Commandes en checkbutton </h2> -->
     
-             <!-- <div id="tree1" class="table table-condensed table-hover table-striped fancytree-fade-expander"> -->
-             <table id="tree1" class="table table-condensed table-hover table-striped fancytree-fade-expander">
-              <!-- TODO : Manage the space between Title and Value while choosing a Value size adapted to the value -->
-               <colgroup>
-                <col width="80px"></col>
-                <col width="30px"></col>
-                <col width="300px"></col>
-                <col width="300px"></col>
-                <col width="60px"></col>
-                <col width="*"></col>
-               </colgroup>
-               <thead>
-                <tr> <th></th> <th></th> <th>Mot Clé</th> <th>Value</th> <th></th> <th></th> </tr>
-               </thead>
-               <tbody>
-                <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> </tr>
-               </tbody>
-              <!-- </div> -->
-            </table>
-           </div>
-          <!-- <div class="col-sm-4" ><table class="fancytree-ext-table"><tr>Optionals :</tr></table> -->
-          <div class="col-sm-4 panel panel-default" >
-            <div class="panel-heading">Optionals</div>
-            <div class="panel-body">Essai</div>
-          </div>
-        </div>
-        <div class="row">
-          <!-- <footer class="footer"> -->
-        <!-- <nav class="navbar navbar-inverse navbar-fixed-bottom"> -->
-        <!-- <nav class="navbar navbar-inverse"> -->
-        <!--   <div class="container-fluid"> -->
-            <!-- <div class="navbar-header" > -->
-            <!--   <a class="navbar-brand" href="#">Messages</a> -->
-            <!-- </div> -->
-            <!-- <h2>Messages</h2> -->
-             <!-- <span id="tree1-messages" class="nav navbar-nav col-sm-12 navbar-right alert alert-success"  >....</span> -->
-             <footer id="tree1-messages" class="container-fluid footer col-sm-12 alert alert-success"  >....</footer>
-             <!-- alert alert-success alert-info alert-warning alert-danger -->
-        <!--   </div> -->
-        <!-- </nav> -->
-        <!-- </footer> -->
-       </div>
-    </div> <!-- fin container-fluid principal-->
-
-    <script>
+    {% for commande in listeCommandes %}
+    <!-- PN : pourquoi  peut-il  avoir plusieurs cheched -> à cause de name -->
+    <div class="btn-group" data-toggle="buttons" onclick="clickOnCommand( '#tree1','#tree1-messages', '{{ commande }}' , '' )">
+      <!-- <input type="radio" name={{ commande }} id={{ commande }} > -->
+      <!-- <input type="radio" class="form-check-input" name='Etapes' id={{ commande }} > -->
+      <label class="btn btn-primary activate" for={{ commande }}> {{ commande }}</label><!-- <br> -->
+      
+      <!-- <label class="btn btn-primary" for={{ commande }}> -->
+      <!--   <input class="form_check_input" type="radio" name='Etapes' id={{ commande }} > {{ commande }} -->
+      <!-- </label> -->
+      
+    </div>
+    {% endfor %}
+  </div>
+</div>
+
+<!-- <div class="row panel panel-default"> -->
+<!--     <h2> </h2> -->
+<!--     <\!-- <div id="treeview-searchable" class=""></div> -\-> -->
+<!-- </div> -->
+<div class="row">         
+  <div class="col-sm-8 panel panel-default"> <!-- Panel Arbre Principal -->
+    <div class="panel-heading">DatasetName : {{titre}}</div>
+    
+    <!-- <div id="tree1" class="table table-condensed table-hover table-striped fancytree-fade-expander"> -->
+    <table id="tree1" class="table table-condensed table-hover table-striped fancytree-fade-expander">
+      <!-- TODO : Manage the space between Title and Value while choosing a Value size adapted to the value -->
+      <colgroup>
+       <col width="80px"></col>
+       <col width="30px"></col>
+       <col width="300px"></col>
+       <col width="300px"></col>
+       <col width="20px"></col>
+       <col width="20px"></col>
+       <col width="20px"></col>
+       <col width="*"></col>
+      </colgroup>
+      <thead>
+       <tr> <th></th> <th></th> <th>Mot Clé</th> <th>Value</th> <th></th> <th></th> <th></th> <th></th> </tr>
+      </thead>
+      <tbody>
+       <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> </tr>
+      </tbody>
+      <!-- </div> -->
+    </table>
+  </div>
+  
+  <!-- <div class="col-sm-4" ><table class="fancytree-ext-table"><tr>Optionals :</tr></table> -->
+  <div class="col-sm-4 panel panel-default" > <!-- Panel Arbre Options -->
+    <div class="panel-heading">Optionals</div>
+    <div class="panel-body">
+      <div id="Optionals"></div>
+      <div id="bebe"></div>
+    </div>
+  </div>
+  
+  <div class="row">  
+    <!-- <footer class="footer"> -->
+    <!-- <nav class="navbar navbar-inverse navbar-fixed-bottom"> -->
+    <!-- <nav class="navbar navbar-inverse"> -->
+    <!--   <div class="container-fluid"> -->
+    <!-- <div class="navbar-header" > -->
+    <!--   <a class="navbar-brand" href="#">Messages</a> -->
+    <!-- </div> -->
+    <!-- <h2>Messages</h2> -->
+    <!-- <span id="tree1-messages" class="nav navbar-nav col-sm-12 navbar-right alert alert-success"  >....</span> -->
+    <footer id="tree1-messages" class="container-fluid footer col-sm-12 alert alert-success"  >....</footer>
+    <!-- alert alert-success alert-info alert-warning alert-danger -->
+    <!--   </div> -->
+    <!-- </nav> -->
+    <!-- </footer> -->
+  </div>
+</div> <!-- fin container-fluid principal-->
+
+       <script>
+
+//$.ui.fancytree.debugLevel=4; // Set the fancyTree debug level
+
+//Créer une fonction pour désactiver toutes les classes d'alert pour positionner une seule 
+let treeMessage=function(msgCssSelStr,messageClass,message) {
+    const messageClasses = new Set(["alert-success","alert-info","alert-warning","alert-danger"]);
+    if (messageClasses.has(messageClass)) {
+       $(msgCssSelStr).removeClass("alert-success alert-info alert-warning alert-danger");
+        $(msgCssSelStr).addClass(messageClass);
+       if ( messageClass == "alert-danger" ) {
+           $.ui.fancytree.error(message);    // debugLevel >= 1
+       } else if ( messageClass == "alert-warning" ) {
+           $.ui.fancytree.warn(message);     // debugLevel >= 2
+       } else if ( messageClass == "alert-info" ) {
+           $.ui.fancytree.info(message);     // debugLevel >= 3
+       } else {
+            console.debug(message);           // debugLevel >= 4
+       };
+        $(msgCssSelStr).text(message);
+    } else {
+       alert("The messageClass : |"+messageClass+"| is not in "+messageClasses); //TODO afficher la chaine messageClasses
+    }; 
+}
+
+// APPELS EXPLICITES AU SERVER
+// TODO : Il faudrait s'assurer que si une requête de ce type est envoyée au serveur
+//        on récupère bien l'evenement appendChild --> validation, promise...
+// TODO : ?async?
+function callService(serviceName, obj, callBack) {
+    let msgCssSelStr  =  '#tree1-messages';
+    var success = function(data, status) {
+        message="Successfull "+serviceName+" call with status : " + status;
+        treeMessage(msgCssSelStr,"alert-success",message) ;
+       callBack(data);
+    };
 
-    //Créer une fonction pour désactiver toutes les classes d'alert pour positionner une seule 
-    let treeMessage=function(msgCssSelStr,messageClass,message) {
-        const messageClasses = new Set(["alert-success","alert-info","alert-warning","alert-danger"]);
-       if (messageClasses.has(messageClass)) {
-          $(msgCssSelStr).text(message);
-         $(msgCssSelStr).removeClass("alert-success alert-info alert-warning alert-danger");
-         $(msgCssSelStr).addClass(messageClass);
+    let url    = "{{ url_for('appendChild') }}";
+    let newurl = url.replace("appendChild", serviceName); //TODO : Trouver une meilleure solution !
+    $.ajax({
+        type           : "POST",
+        url            : newurl,
+        data           : JSON.stringify(obj),
+        contentType    : "application/json; charset=utf-8",
+        dataType       : "json", 
+        success                : success,
+        failure                : function(errMsg) { alert(errMsg); }
+    });
+};
+
+// APPELS EXPLICITES AU SERVER
+function clickOnRemove(treeCssSelStr, msgCssSelStr, key ) {
+    var tree  = $.ui.fancytree.getTree(treeCssSelStr);
+    var _key  = key; 
+    if ( key ==  "") {
+       activeNode = tree.activeNode;
+       if ( activeNode !=  null) {
+           _key = activeNode.key;
        } else {
-           alert("The messageClass : |"+messageClass+"| is not in "+messageClasses);
-       }; 
-    }
+           return;
+       };
+    };
 
-    // APPELS EXPLICITES AU SERVER
-    //TODO : Il faudrai s'assurer que si une requête de ce type est envoyée au serveur
-    //       on récupère bien l'evenement appendChild --> validation, promise... 
-    function clickOnCommand(treeCssSelStr, msgCssSelStr, cmdName ) {
-      tree             = $.ui.fancytree.getTree("#tree1");
-      activeNode       = tree.activeNode;
-      if ( activeNode ==  null) {
-          index=0; pos=0;
-      } else {
-        parentList = activeNode.getParentList();
+    let checkCallBack = function(data) {
+       // Tester data == NULL !
+       ret = data['ret'];
+       if ( ret == null ) {
+           let message = "Unsuccessfull removeNode processing for key " + _key + ". removeNode has returned a null id.";
+           treeMessage(msgCssSelStr,"alert-danger",message) ;
+       };
+    };
+    callService('removeNode', {id: _key}, checkCallBack);
+};
+
+// TODO : Il faudrait s'assurer que si une requête de ce type est envoyée au serveur
+//        on récupère bien l'evenement appendChild --> validation, promise... 
+function sendAppendChild( key, name, pos) {
+    let msgCssSelStr  =  '#tree1-messages';
+
+    let checkCallBack = function(data) {
+       // Tester data == NULL !
+       rId = data['id'];
+       if ( rId == null ) {
+           let message = "Unsuccessfull appendChild processing for command " + name + ". appendChild has returned a null id.";
+           treeMessage(msgCssSelStr,"alert-danger",message) ;
+       };
+    };
+    callService('appendChild', {id: key, name:name, pos:pos }, checkCallBack);
+};
+
+function getParentCommandNode(node) {
+    if ( node == null ) return null;
+    let parentList = node.getParentList(false,true); // default : (false,false)==(rootNode exclus, not self)
+    return parentList[1];                            // parentList()[0] is always the efficas <code> name
+};
+
+// TODO Vérif assertion : Les functions clickOn ont des arguments en chaîne de caractères pour être facilement
+//                         utilsées dans les attributs des elements html  on="clickOn('arg1','arg2',)"
+function clickOnCommand(treeCssSelStr, msgCssSelStr, cmdName, key ) {
+    tree = $.ui.fancytree.getTree(treeCssSelStr);
+    if ( key !=  "") {
+       activeNode=tree.getNodeByKey(key);
+    } else {    
+       activeNode = tree.activeNode;
+    };
+    if ( activeNode ==  null) {
+        index=0; pos=0;
+    } else {
+        parentList = activeNode.getParentList(); // default : (false,false)==(rootNode exclus, not self)
        console.log("clickOnCommand : parentList "+parentList)
-       if ( parentList.length  == 0 ) {
-         console.log("clickOnCommand : index is forced = 0");
-          index=0; pos=0;                                        // le root node est actuellement le nom de code !
+       if ( parentList.length  == 0 ) {                           // TODO : Verif, ne devrait pas être possible 
+           console.log("clickOnCommand : index is forced = 0");
+            index=0; pos=0;                                        // The first node is always the code name
         } else {
            if ( parentList.length  == 1 ) {
                activeCommandKey  = activeNode.key;
            } else {
-                activeCommandKey  = parentList[1].key;             //activeNode.getParentList()[0] is always the root node so [1] is always the first level, the eficas command level (the code name by now)
+                activeCommandKey  = parentList[1].key;             //activeNode.getParentList()[0] is always the <efficas code> node so [1] is always the first level, the eficas command level
            };
            console.log("clickOnCommand : activeCommandKey "+activeCommandKey)
-            // commandList    = tree.rootNode.getChildren()    // le root node est actuellement le nom de code !
-            commandList            = tree.rootNode.getChildren()[0].getChildren(); // le root node est actuellement le nom de code !
-           index             = commandList.findIndex(function(n){return n.key == activeCommandKey;});                                   pos=index+1;
+            commandList            = tree.rootNode.getChildren()[0].getChildren(); // le premier node est actuellement le nom de code 
+           index           = commandList.findIndex(function(n){return n.key == activeCommandKey;});                                     pos=index+1;
         }
-      }
-      console.log("clickOnCommand : index = "+index);
-
-   
-      let success=function(data, status) {
-           console.log("Successfully appendChild message for command " + cmdName + " sent: "  + "\nStatus: " + status);
-           // rChangeIsAccepted = data.changeIsAccepted;
-           rId            = data['id'];
-           // rValidite         = data['validite'];
-           console.log( {'data': data } );
-           console.log( {'rId': rId } );
-           //console.log( {'rValidite': rValidite } );
-           //console.log( {'rChangeIsAccepted': rChangeIsAccepted } );
-          if ( rId == null ) {
-             let message = "Unsuccessfull appendChild for command " + cmdName + ". appendChild has returned a null id."
-              console.log(message);
-               $(msgCssSelStr).text(message);
-              //Créer une fonction pour désactiver toutes les classes d'alert pour positionner une seule 
-              $(msgCssSelStr).removeClass("alert-success alert-info alert-warning alert-danger");
-              $(msgCssSelStr).addClass("alert-danger");
-          }
-        // if ( rChangeIsAccepted ) {
-        //   node.data.wValue   = rValue
-        //   node.data.validite = rValidite
-        //   //node.data        = data.source ?? Essayer de remplacer la source complète du noeud  
-        //   console.log('rValue    : '+rValue);
-        //   console.log('rValidite : '+rValidite);
-        // } else {
-        //   // Gérer le input pour laisser le focus et passer en rouge
-        // };
-        // node.render(true,false); //force rendering the node (not parents nor descendants)
-        // //node.renderStatus();      //CSS element updates
-      };
-                  
-      $.ajax({
-              type             : "POST",
-              url              : "{{ url_for('appendChild') }}",
-              data             : JSON.stringify({id: tree.rootNode.children[0].key, name:cmdName, pos:pos }),
-              contentType      : "application/json; charset=utf-8",
-              dataType         : "json", 
-              success          : success,
-              failure          : function(errMsg) { alert(errMsg); }
-      })
-                                
-    };
-      
-   //GESTION DES EVENEMENTS PROVENANT DU SERVEUR
-   var source = new EventSource("{{ url_for('sse.stream') }}");
-
-   // --- propageValid ---
-   source.addEventListener('propageValide', function(event) {
-        const data     = JSON.parse(event.data);
-        var   id        = data.id;
-        var   valid     = data.valid;
-        var   message  = data.message;
-        // alert("The server says " + data.message);
-        console.log("The server says " + data.message +" , id: "+id);
-        $("#tree1-messages").text(message);
-        var tree=$.ui.fancytree.getTree("#tree1");
-        //var activeNode = tree.getActiveNode();
+    }
+    console.log("clickOnCommand : index = "+index);
+    sendAppendChild( tree.rootNode.children[0].key, cmdName, pos);                              
+};
+
+
+
+//GESTION DES EVENEMENTS PROVENANT DU SERVEUR
+var source = new EventSource("{{ url_for('sse.stream') }}");
+
+// --- propageValid ---
+(function (treeCssSelStr, msgCssSelStr) {
+    let _treeCssSelStr  = treeCssSelStr; //inutile
+    let _msgCssSelStr   = msgCssSelStr;  //inutile
+    // var  _tree          = $.ui.fancytree.getTree(_treeCssSelStr);
+    source.addEventListener('propageValide', function(event) {
+        const data    = JSON.parse(event.data);
+        var   id      = data.id;
+        var   valid   = data.valid;
+        var   message = "The server says " + data.message +" , id: "+id;
+        var   tree    = $.ui.fancytree.getTree(_treeCssSelStr);
+        
+        treeMessage(_msgCssSelStr,"alert-info",message);
+        // console.log("_tree : "+ _tree); //?Expliquer pourquoi null: à cause de $.?
         node=tree.getNodeByKey(id);
-        node.data.validite=valid;  //Pas encore réussi à tester ...
+        node.data.validite=valid;  //TODO : ?? Pas encore réussi à tester ... ??
         node.render(true,false);   //force rendering the node (not parents nor descendants)
-        //node.renderStatus();     //CSS element updates
-  
-        //activeNode.addChildren({
-        //  title: "Document using a custom icon",
-        //  icon: "customdoc1.gif"
-        //});
-   }, false);
-
-   // --- appendChildren ---
-   (function (treeCssSelStr, msgCssSelStr) {
-     let _treeCssSelStr  = treeCssSelStr; //inutile
-     let _msgCssSelStr   = msgCssSelStr;  //inutile
-     var  _tree          = $.ui.fancytree.getTree(_treeCssSelStr);
-     source.addEventListener('appendChildren', function(event) {
+        //node.renderStatus();     //CSS element updates  
+    }, false);
+})('#tree1','#tree1-messages');
+
+// --- appendChildren ---
+(function (treeCssSelStr, msgCssSelStr) {
+    let _treeCssSelStr  = treeCssSelStr; //inutile
+    let _msgCssSelStr   = msgCssSelStr;  //inutile
+    var  _tree          = $.ui.fancytree.getTree(_treeCssSelStr);
+    source.addEventListener('appendChildren', function(event) {
         const data      = JSON.parse(event.data);
-        var   id       = data.id;
-        const source   = data.fcyTreeSrc;
-        var   pos      = data.pos;
-        var   message  = data.message;
-   
-        console.log("The server says " + data.message);
-        //$(_msgCssSelStr).text(message);
-        treeMessage(_msgCssSelStr,"alert-success",message);
-        //var activeNode = tree.getActiveNode();
+        var   id        = data.id;
+        const source    = data.fcyTreeSrc;
+        var   pos       = data.pos;
+        //var   message = data.message;
+        var   message   = "The server says " + data.message +" , id: "+id;
+       
+        treeMessage(_msgCssSelStr,"alert-info",message);
         console.log("_tree : "+ _tree); //?Expliquer pourquoi null: à cause de $.?
-        var  tree     = $.ui.fancytree.getTree(_treeCssSelStr);
-        node          = tree.getNodeByKey(id);
-       countChildren = node.countChildren(false);
+        var  tree          = $.ui.fancytree.getTree(_treeCssSelStr);
+        var  node          = tree.getNodeByKey(id);
+        var countChildren  = node.countChildren(false);
         console.log("countChildren : "+ countChildren); 
         console.log("pos           : "+ pos); 
-       if ( pos >= countChildren) {
+        if ( pos >= countChildren) {
             node.addChildren(source);
-       } else {
+        } else {
             node.addChildren(source,pos);
-       }
-        <!-- node.addChildren({ -->
-        <!--   title: "Document using a custom icon", -->
-        <!--   icon: "customdoc1.gif" -->
-        <!-- }); -->
-     }, false);
-   })('#tree1','#tree1-messages');
-
-   // --- deleteChildren ---   
-   source.addEventListener('deleteChildren', function(event) {
-     const data                = JSON.parse(event.data);
-     <!-- const keySet         = new Set(data.idList); -->
-     const keyList             = data.idList;
-     var   message           = data.message;
-     <!-- const nodeAsKey = function (node,keys) { -->
-     <!--   //?keySet = _isFunction(match) ? keys : _makeNodeTitleMatcher(match); -->
-     <!--   if (node.key === key ) return true; -->
-     <!--   return false; -->
-     <!-- }; -->
-     <!-- var myNodeAsKey= function(n) {return nodeAsKey(n,'efbab07c5d8e11ecbd14ac220bca9aa6');} -->
-     //alert("The server says " + data.message);
-     console.log("The server says " + data.message);
-     $("#tree1-messages").text(message);
-     var tree       = $.ui.fancytree.getTree("#tree1");
-     nodeList       = keyList.map( (x) => { return tree.getNodeByKey(x) } );
-     nodeList.map( (x) => { x.remove()} );
-
-     //tree.findAll(match);
-     //var activeNode = tree.getActiveNode();
-     //activeNode.addChildren({
-     //  title: "Document using a custom icon",
-     //  icon: "customdoc1.gif"
-     //});
-   }, false);
-
-   
-    source.addEventListener('error', function(event) {
-        alert("Failed to connect to event stream. Is Redis running?");
+        }
+            <!-- node.addChildren({ -->
+           <!--   title: "Document using a custom icon", -->
+           <!--   icon: "customdoc1.gif" -->
+           <!-- }); -->
+    }, false);
+})('#tree1','#tree1-messages');
+
+// --- deleteChildren ---    
+(function (treeCssSelStr, msgCssSelStr) {
+    let _treeCssSelStr  = treeCssSelStr; //inutile
+    let _msgCssSelStr   = msgCssSelStr;  //inutile
+    var  _tree          = $.ui.fancytree.getTree(_treeCssSelStr);
+    source.addEventListener('deleteChildren', function(event) {
+       const data        = JSON.parse(event.data);
+       // const keySet = new Set(data.idList); 
+       const keyList     = data.idList;
+       var   tree        = $.ui.fancytree.getTree("#tree1");
+       var   message     = "The server says " + data.message +" , keyList: "+keyList;
+        treeMessage(_msgCssSelStr,"alert-info",message);
+        // const nodeAsKey = function (node,keys) {
+        //   //?keySet = _isFunction(match) ? keys : _makeNodeTitleMatcher(match);
+        //   if (node.key === key ) return true;
+        //   return false;
+        // };
+       // var myNodeAsKey= function(n) {return nodeAsKey(n,'efbab07c5d8e11ecbd14ac220bca9aa6');}
+       //alert("The server says " + data.message);
+       //TODO : Vérifier les keys doublons demandées en supression !    
+       nodeList       = keyList.map( (x) => { return tree.getNodeByKey(x) } );
+       nodeList.map( (x) => { x.remove()} );
+       
+       //tree.findAll(match);
+       //var activeNode = tree.getActiveNode();
+       //activeNode.addChildren({
+       //  title: "Document using a custom icon",
+       //  icon: "customdoc1.gif"
+       //});
     }, false);
+})('#tree1','#tree1-messages');
+
+
+source.addEventListener('error', function(event) {
+    alert("Failed to connect to event stream. Is Redis running?");
+}, false);
     
-    function getTree1() {
+function getTree1() {
     // Some logic to retrieve, or generate tree structure
     return  {{ tree|tojson }};
-    }
+}
+
+
 
-    <!-- function getTree2() { -->
-    <!-- // Some logic to retrieve, or generate tree structure -->
-    <!-- return  {{ mcTraiteJson|tojson }}; -->
-    <!-- } -->
+    // function getTree2() {
+    // // Some logic to retrieve, or generate tree structure
+    // return  {{ mcTraiteJson|tojson }};
+    // }
+
+    // $("#tree1").fancytree({ source: [ {title: "Node 1", key: "1000"},{title: "Folder 2", key: "2", folder: true, children: [ {title: "Node 2.1", key: "3000"},{title: "Node 2.2", key: "44"} ]}    ], })
+
+    // $("#tree1").fancytree({ source: JSON.parse(getTree1()), })
 
-<!-- $("#tree1").fancytree({ source: [ {title: "Node 1", key: "1000"},{title: "Folder 2", key: "2", folder: true, children: [ {title: "Node 2.1", key: "3000"},{title: "Node 2.2", key: "44"} ]}    ], })  -->
 
-<!-- $("#tree1").fancytree({ source: JSON.parse(getTree1()), }) -->
 
     //Doublon cf. base.html
-    var glyph_opts = {
-      preset: "bootstrap3",
-      map: {
-      }
-    };
+var glyph_opts = {
+    preset: "bootstrap3",
+    // preset: "awesome3",
+    // preset: "awesome4",
+    // preset: "awesome5",
+    // preset: "material",
+    map: {
+    }
+};
    
-   $(function(){
-     $("#tree1").fancytree({
-      //focusOnSelect:true,
-      debug:4,
-      //extensions: ["dnd5", "edit", "glyph", "wide", "table", "gridnav"],
-      extensions: [ "glyph", "table", "ariagrid"],
-      //extensions: [ "glyph", "table", "gridnav"],
-      checkbox: true, // Activate the use of checkboxes next to the nodes
-
-      // selectMode: 1: single selection
-      //   Only one node is selected at any time.
-      // selectMode: 2: multiple selection (default)
-      //   Every node may be selected independently.
-      // selectMode: 3: hierarchical selection
-      //   (De)selecting a node will propagate to all descendants. Mixed states will be displayed as partially selected using a tri-state checkbox.
-
-      selectMode: 1,
-      dnd5: { // unused drag&drop module
-           dragStart: function(node, data) { return true; },
-           dragEnter: function(node, data) { return true; },
-           dragDrop : function(node, data) { data.otherNode.copyTo(node, data.hitMode); }
-      },
-      glyph: glyph_opts,
-      source: JSON.parse(getTree1()),
-      //wide: {
-      //  iconWidth: "1em",       // Adjust this if @fancy-icon-width != "16px"
-      //  iconSpacing: "0.5em",   // Adjust this if @fancy-icon-spacing != "3px"
-      //  labelSpacing: "0.1em",  // Adjust this if padding between icon and label != "3px"
-      //  levelOfs: "1.5em"       // Adjust this if ul padding != "16px"
-      //},
-      icon: function(event, data) {
-        <!-- if( data.node.isFolder() ) { -->
-        <!--    return "glyphicon glyphicon-book"; -->
-        <!-- } -->
-        if( data.node.data.classeAccas ==  "MCSIMP") {
-           return "glyphicon glyphicon-leaf";
-        } else {
-           return "glyphicon glyphicon-list-alt";
-       }
-        <!-- return "glyphicon glyphicon-list-alt"; -->
-      },
-      table: {                // Options for the table extension
-        checkboxColumnIdx: 1, // Tree Nodes Checkboxes are rendered at column #1 (numbered from #0)
-        nodeColumnIdx: 2,     // Tree Nodes are rendered at column #2
-       indentation : 10      // Tree Nodes indentation between levels
-      },
-      gridnav: {
-               autofocusInput:   true, // Focus first embedded input if node gets activated
-               handleCursorKeys: true   // Allow UP/DOWN in inputs to move to prev/next node
-      },
-      ariagrid:        {
-        cellFocus: 'allow'     
-      },       
-      //lazyLoad: function(event, data) {
-      //  data.result = {url: "ajax-sub2.json", debugDelay: 1000};
-      //}
-      // renderStatusColumns: Pour le CSS
-      renderColumns: function(event, data) {
-
-        // Pour tester ds la console :
-        //var tree=$.ui.fancytree.getTree("#tree1")
-        //p1=tree.getNodeByKey('8304986a334211ec853cac220bca9aa6')
-        //$(p1.tr).find(">td")
-
-        //import {createTree, version} from 'jquery.fancytree'
-        //const tree = createTree('#tree', { ... });
-        //var node = tree.getActiveNode();      
-
-        var node = data.node,
-        $tdList = $(node.tr).find(">td");
-        var _attr=''
-      
-        //Render Column #0
-        $tdList.eq(0).text(node.getIndexHier());
-      
-        //Render Column #3
-        // Jaune Eficas #ffff00
-        // Rouge Eficas #ff0000
-        // Vert  Eficas #00ff00
-        if (node.data.classeAccas ==  "MCSIMP") {
-          //VALIDATION STATES - Bootstrap includes validation styles for error, warning, and success messages.
-          //To use, add .has-warning, .has-error, or .has-success to the parent element
-          if( node.data.validite ) {
-            _attr='has-success has-feedback';
-            // $(".fancytree-exp-n > td:nth-child(2) span").filter(".fancytree-checkbox").css('background-color','blue')
-            $tdList.eq(1).find("span").filter(".fancytree-checkbox").css('background-color','#00ff00'); // Update the checkbox validation state TODO: create a CSS class
-          } else {
-            _attr='has-error has-feedback';
-            //$tdList.eq(1).css('background-color','red'); // Update the checkbox validation state TODO: create a CSS class
-            $tdList.eq(1).find("span").filter(".fancytree-checkbox").css('background-color','#ff0000'); // Update the checkbox validation state TODO: create a CSS class
-          } //TODO: paramétrer l'attribut/framework
-          console.log("node.data.validite, _attr :"+node.data.validite+" , "+_attr);
-         //    $tdList.eq(3).html("<simp><input type='input' class='form-control-sm' id='simp1' placeholder='"+node.data.wValue+"' value='"+node.data.wValue+"'></simp>");
-          $tdList.eq(3)
-            .html(
-             "<form class='was-validated'>"+ //'was-validated' ??
-               "<div class='form-group'>" +
-             //  "<div class='form-group "+ _attr + "'>" +
-             //  "<div class='form-group has-error'>" + 
-                 "<input type='input' class='form-control input-sm' id='simp1' placeholder='" + node.data.wValue + //TODO:intosugg
-                                                                                 "' value='"+node.data.wValue+"' required>"+
-                 //"<div class='valid-feedback'>Valid.</div>"+
-                 //"<div class='invalid-feedback'>Please fill out this field.</div>"+
-               "</div>"+
-              "</form>"
-              )
-            .addClass(_attr)
-            .find("input").css('color','black'); //TODO : A placer ds le CSS
-          $tdList.eq(4)
-               .html("<span class='glyphicon glyphicon-plus' onclick='alert(\"glyphicon-plus : "+node.key+"\")'></span>"+
-                     "<span class='glyphicon glyphicon-trash'></span>"+
-                     "<span class='glyphicon glyphicon-book''></span>");
-       
-        } else { // Not an SIMP 
-         $tdList.eq(2).prop("colspan", 4).nextAll().remove(); //Merge unused columns for easy keyboard navigation
-         return;
-       }
-      },
-
-      defaultGridAction: function( event, data ) { //(used by ext-aria) The user hit enter on the active row or cell.
-          var node = data.node
-          var rValue,rValidite,rChangeIsAccepted
-
-          if (node.data.classeAccas ==  "MCSIMP") {
-            $input=$(node.tr).find('input')
-            value=$input.val()
-
-            // Called when ENTER is pressed in cell-mode.
-            // Return false to prevent default
-            //data.activeTd contains the currently active <td> element or null
-            //data.colIdx   contains the 0-based column index or -1
+$(function(){
+    $("#tree1").fancytree({
+       //focusOnSelect:true,
+       debug:4,
+       //extensions: ["dnd5", "edit", "glyph", "wide", "table", "gridnav"],
+       extensions: [ "glyph", "table", "ariagrid"],
+       //extensions: [ "glyph", "table", "gridnav"],
+       checkbox: true, // Activate the use of checkboxes next to the nodes
+
+       // selectMode: 1: single selection
+       //   Only one node is selected at any time.
+       // selectMode: 2: multiple selection (default)
+       //   Every node may be selected independently.
+       // selectMode: 3: hierarchical selection
+       //   (De)selecting a node will propagate to all descendants. Mixed states will be displayed as partially selected using a tri-state checkbox.
+
+       selectMode: 1,
+       escapeTitles: true,
+       dnd5: { // unused drag&drop module
+            dragStart: function(node, data) { return true; },
+            dragEnter: function(node, data) { return true; },
+            dragDrop : function(node, data) { data.otherNode.copyTo(node, data.hitMode); }
+       },
+       glyph: glyph_opts,
+       source: JSON.parse(getTree1()), //TODO: Vérifier que la transformation JSON n'est pas double avec Flask
+       //wide: {
+       //  iconWidth: "1em",       // Adjust this if @fancy-icon-width != "16px"
+       //  iconSpacing: "0.5em",   // Adjust this if @fancy-icon-spacing != "3px"
+       //  labelSpacing: "0.1em",  // Adjust this if padding between icon and label != "3px"
+       //  levelOfs: "1.5em"       // Adjust this if ul padding != "16px"
+       //},
+       icon: function(event, data) {
+            // if( data.node.isFolder() ) {
+            //    return "glyphicon glyphicon-book";
+            // }
+            if( data.node.data.classeAccas ==  "MCSIMP") {
+               return "glyphicon glyphicon-leaf";
+            } else if( data.node.data.classeAccas ==  "PROCEDURE") {
+               return "glyphicon glyphicon-tree-deciduous";
+            } else if( data.node.data.classeAccas ==  "MCFACT") {
+               return "glyphicon glyphicon-grain";
+           } else {
+               return "glyphicon glyphicon-tree-deciduous";
+           };
+       },
+       // icon: function(event, data) {
+       //  if( data.node.type ) {
+       //    return "ft-ico-" + data.node.type;
+       //  }
+       // },
+       tooltip: function(event, data) {
+            // Create dynamic tooltips
+            return data.node.title + " (" + data.node.key + ")";
+       },      
+       table: {                // Options for the table extension
+            checkboxColumnIdx: 1, // Tree Nodes Checkboxes are rendered at column #1 (numbered from #0)
+            nodeColumnIdx: 2,     // Tree Nodes are rendered at column #2
+           indentation : 10      // Tree Nodes indentation between levels
+       },
+       gridnav: {
+           autofocusInput:   true, // Focus first embedded input if node gets activated
+           handleCursorKeys: true  // Allow UP/DOWN in inputs to move to prev/next node
+       },
+       ariagrid:       {
+            cellFocus: 'allow'
+       },
+       // viewport: {
+        //     enabled: true,
+        //     count: 15,
+       // },
+       //TODO : Mettre en place le lazyload
+       //lazyLoad: function(event, data) {
+       //  data.result = {url: "ajax-sub2.json", debugDelay: 1000};
+       //}
+       // renderStatusColumns: Pour le CSS
+       renderColumns: function(event, data) {
+
+            // Pour tester ds la console :
+            //var tree=$.ui.fancytree.getTree("#tree1")
+            //p1=tree.getNodeByKey('8304986a334211ec853cac220bca9aa6')
+            //$(p1.tr).find(">td")
+
+            //import {createTree, version} from 'jquery.fancytree'
+            //const tree = createTree('#tree', { ... });
+            //var node = tree.getActiveNode();      
+
+            var node           = data.node,
+               $tdList         = $(node.tr).find(">td");
+            let classeAccas    = node.data.classeAccas;
+            let validite       = node.data.validite;
+            let wValue         = node.data.wValue;
+            let statut         = node.data.statut;
+           let cmdName         = node.data.nomCommande; // undefined if classeAccas != 'MCFACT'
+           let repeatable      = node.data.repetable;          // ?? undefined if classeAccas != 'MCFACT' ??
+           let infoOptionnels  = node.data.infoOptionnels;
+            let key            = node.key;
+            let name           = node.title;
+            var _attr          = ''
+           
+            //Render Column #0
+            $tdList.eq(0).text(node.getIndexHier());
+           
+            //Render Column #2 (icon color)
+            if( validite ) { //TODO : les deux attributs crées un dépacement du input du row
+               _attr='has-success has-feedback';
+               // $(".fancytree-exp-n > td:nth-child(2) span").filter(".fancytree-checkbox").css('background-color','blue')
+               //$tdList.eq(1).find("span").filter(".fancytree-checkbox").css('background-color','#00ff00'); // Update the checkbox validation state TODO: create a CSS class
+               $tdList.eq(2).find("span").filter(".fancytree-custom-icon").css('color','#00ff00'); // Update the checkbox validation state TODO: create a CSS class
+            } else {
+               _attr='has-error has-feedback';
+               //$tdList.eq(1).css('background-color','red'); // Update the checkbox validation state TODO: create a CSS class
+               // $tdList.eq(1).find("span").filter(".fancytree-checkbox").css('background-color','#ff0000'); // Update the checkbox validation state TODO: create a CSS class
+               $tdList.eq(2).find("span").filter(".fancytree-custom-icon").css('color','#ff0000'); // Update the checkbox validation state TODO: create a CSS class
+            } //TODO: paramétrer l'attribut/framework
+            console.log("node.data.validite, _attr :"+validite+" , "+_attr);
+
+            //Render Column #3 (input field)
+            // Jaune Eficas #ffff00, Rouge Eficas #ff0000, Vert  Eficas #00ff00
+            if ( classeAccas ==  "MCSIMP" ) {
+               //VALIDATION STATES - Bootstrap includes validation styles for error, warning, and success messages.
+               //To use, add .has-warning, .has-error, or .has-success to the parent element
+               //      $tdList.eq(3).html("<simp><input type='input' class='form-control-sm' id='simp1' placeholder='"+wValue+"' value='"+wValue+"'></simp>");
+               $tdList.eq(3)
+                   .html(
+                       "<form>"+ 
+                           "<div class='form-group'>" +
+                           "<input type='input' class='form-control input-sm' id='"+key+"' placeholder='"
+                           + wValue + //TODO:intosugg
+                       "' value='"+wValue+"' required>"+
+                           "</div>"+
+                           "</form>"
+                   )
+                   .addClass(_attr)
+                   .find("input").css('color','black'); //TODO : A placer ds le CSS
+               actionListIndex=4;
+               // $tdList.eq(actionListIndex)
+               //    .html("<span class='glyphicon glyphicon-plus-sign' onclick='alert(\"glyphicon-plus-sign : "+key+"\")'></span>");
+               if (statut == 'f') {
+                   $tdList.eq(actionListIndex+1)
+                       .html("<span class='glyphicon glyphicon-trash' onclick='alert(\"glyphicon-trash : "+key+"\")'></span>");
+               };
+               $tdList.eq(actionListIndex+2)
+                   .html("<span class='glyphicon glyphicon-question-sign' onclick='alert(\"glyphicon-question-sign : "+key+"\")'></span>");
+               
+            } else { // Not classeAccas ==  "MCSIMP"
+               
+               //$tdList.eq(2).prop("colspan", 2).nextAll().remove(); //Merge unused columns for easy keyboard navigation
+               if ( classeAccas ==  "PROCEDURE" || classeAccas ==  "OPER" ) {
+                   plusFunction  = "clickOnCommand( \"#tree1\", \"#tree1-messages\", \""+name+"\", \""+key+"\" )";
+                   trashFunction = "clickOnRemove( \"#tree1\", \"#tree1-messages\", \""+key+"\" )";
+               } else if ( classeAccas ==  "MCFACT"  && repeatable ) {
+                   plusFunction = "sendAppendChild( \""+key+"\", \""+cmdName+"\",null)";
+               } else {
+                   plusFunction  = undefined;
+                   // plusFunction  = "alert(\"glyphicon-plus-sign : "+key+"\")";
+                   trashFunction = undefined;
+                   trashFunction = "alert(\"glyphicon-trash : "+key+"\")";
+               };
+               
+               $tdList.eq(2).prop("colspan", 2); //Merge unused columns for easy keyboard navigation
+               actionListIndex=3;
+               if ( plusFunction != undefined ) {
+                   $tdList.eq(actionListIndex)
+                       .html("<span class='glyphicon glyphicon-plus-sign' onclick='"+plusFunction+"'></span>");
+               };
+               if ( trashFunction != undefined ) {
+                   if (statut == 'f') {
+                       $tdList.eq(actionListIndex+1)
+                           .html("<span class='glyphicon glyphicon-trash' onclick='"+trashFunction+"'></span>");
+                   };
+               };
+               $tdList.eq(actionListIndex+2)
+                   .html("<span class='glyphicon glyphicon-question-sign' onclick='alert(\"glyphicon-question-sign : "+key+"\")'></span>");
+
+           };
+
+           // La souris entre dans un row :
+           // cmdHasOptional:   Si mon topLevelParent (ma Commande) a       des facultatifs, le optTopLevelParent doit être le mien   // topLevelParent peut être moi
+           // cmdHasNoOptional: Si mon topLevelParent (ma Commande) n'a pas de  facultatifs, le optTopLevelParent doit être vide      // topLevelParent peut être moi
+           // If (cmdHasOptional): { displayAllOptUntilMyParent: L'arbre des facultatifs partant de mon topLevelParent jusqu'à mon parent doit d'afficher }
+
+           //let optTree             = $.ui.fancytree.getTree("#Optionals");
+           // let optTreeCommandName  = optTree.getFirstChild().title;
+           // let parentCommandNode   = getParentCommandNode(node);
+           // console.debug(parentCommandNode);
+           //let parentCommandName   = parentCommandNode.name;
+
+           let hasOpt=function(node) {
+               if ( node == null )                         return false;
+               if ( node.data.infoOptionnels != undefined) return true ;
+           };
+
+           $(node.tr).mouseenter(function(){
+               // if ( parentCommandNode.hasOpt() ) {
+               // BUG: !!!!!! Ts les noeuds du chemin
+               // if ( hasOpt(parentCommandNode) ) {
+               //     // TODO : Optimiser un arbre complet avec masquage/demasquage en fction du row
+               //     // if ( parentCommandName != optTreeCommandName ) {
+               //     //       buildOptionalTree(parentCommandNode);
+               //     // } else {
+               //     //       // expand first parent optionals node 
+               //     // }
+               //     buildOptionalTree(node)
+               // } else {
+               //     if ( optTree != null) { optTree.destroy();}; // Supress optional keywords 
+               // };
+               optTreeSrc=buildOptionalTreeSrc(node);
+               optFunction = "sendAppendChild( \""+key+"\", \""+cmdName+"\",null)";
+               optTreeOptions = {};
+               //$("#bebe").text("---------"+optTreeSrc);
+               //console.debug('optTreeSrc :'+optTreeSrc);
+               // if ( optTreeSrc.length == 0 ) {
+                   let optTree     = $.ui.fancytree.getTree("#Optionals");
+                   if (optTree != null) { optTree.destroy();};
+               // } else {
+                   let $optTree   = $("#Optionals").fancytree( { source: optTreeSrc } );
+                   //let optTree    = $optTree.fancytree;
+                   optTree    = $.ui.fancytree.getTree("#Optionals");
+                   optTree.expandAll();
+               // };
+               
+           });
+           
+           let buildOptionalTreeSrc=function(node) {
+               if ( node == null ) return [];
+               let parentList = node.getParentList(false,false); // default : (false,false)==(rootNode exclus, not self)
+               if ( parentList.length > 0 ) { parentList=parentList.slice(1);}; // The first node is the code node
+               // La construction des optionals en suivant les parents fonctionnent car les SIMP ne portent pas les optionnals
+               // [ {title: "Node 1"}, {title: "Folder 2", children: [ {title: "Node 2.1"}, {title: "Node 2.2"} ]} ]
+               // [ {title: "Node 1"} ] + [ {title: "Folder 2", children: [ {title: "Node 2.1"}, {title: "Node 2.2"} ]} ]
+
+               let _optToChild=function(optKeywords) {
+                   return  optKeywords.map((o)=>{ return {'title':o} });
+               };
+
+               let _build=function(parentList) {
+                   if ( parentList.length == 0 ) return [];
+                   node = parentList[0];
+                   if (hasOpt(node))  {
+                       optKeywords = node.data.infoOptionnels[0];
+                   } else {    
+                       optKeywords = [];
+                   };
+                   return [{"title": node.title,
+                            "children": _optToChild(optKeywords).concat(_build(parentList.slice(1))) }];
+               };
+               return _build(parentList);
+           };
+           
+           // // let findName(treeObj,name){ return treeObj.findFirst((n)=>{if (n.title==name) {return n}}) };
+           // $(node.tr).mouseenter(function(){
+           //  if ( optTree != null) {
+           //      if (  name != optTree.getFirstChild().title ) {
+           //          if ( infoOptionnels != undefined) {
+           //              console.log("You entered p1! : "+node.title+" , "+infoOptionnels.toString());
+           //              let opt        = node.data.infoOptionnels[0];
+           //              if ( opt != [] ) {
+           //                  let optNodes   = opt.map((o)=>{return {'title':o} });
+           //                  let optTreeSrc = [{'title': node.title, 'children': optNodes}];
+           //                  let $optTree   = $("#Optionals").fancytree( { source: optTreeSrc} );
+           //                  //let optTree    = $optTree.fancytree;
+           //                  let optTree    = $.ui.fancytree.getTree("#Optionals");
+           //                  optTree.expandAll();
+           //              };
+           //          } else {}; // infoOptionnels == undefined
+           //      } else { 
+           //      };
+           //  } else { //optTree not null
+           //  };
+           // });
+           // // $(node.tr).mouseleave(function(){
+           // //       console.log("You leave   p1! : "+node.title+" , infoOptionnels : ",infoOptionnels.toString());
+           // // });
+           
+       },
+
+       defaultGridAction: function( event, data ) { //(used by ext-aria) The user hit enter on the active row or cell.
+            var node = data.node
+            var rValue,rValidite,rChangeIsAccepted
+            let _msgCssSelStr='#tree1-messages'; //TODO: A déplacer
+         
+            if (node.data.classeAccas == "MCSIMP") {
+               $input=$(node.tr).find('input');
+               value=$input.val();
+
+               // Called when ENTER is pressed in cell-mode.
+               // Return false to prevent default
+               // data.activeTd contains the currently active <td> element or null
+               // data.colIdx   contains the 0-based column index or -1
+               console.log("event.type, data :"+event.type+" , "+data);
+               if( !data.activeTd ) {
+                   alert( "Custom default action for row: " + data.node.title );
+                   // we don't return false, so default action is applied:
+               } else if ( data.colIdx != 3 ) {
+                   // eslint-disable-next-line no-alert
+                   alert( "Custom default action: " + data.node.title );
+                   return false;
+               };
+             
+               let checkCallBack = function(data) {
+                   // Tester data == NULL !
+                    var changeIsAccepted = data.changeIsAccepted;
+                    var wValue           = data.source['wValue'];
+                    var validite         = data.source['validite'];
+                   //node.fromDict()
+                   //source=JSON.parse(getTree1())
+                   if ( changeIsAccepted ) {
+                       // $.ui.fancytree.assert(value == wValue); il faut gérer les représentations des types pour activer cet assert
+                       node.data.wValue   = wValue
+                       node.data.validite = validite
+                       treeMessage(_msgCssSelStr,"alert-success","Changing SIMP "+node.title+" value to "+value+" has been accepted.");
+                       //node.data        = data.source ?? A priori, il est inutile d'essayer de remplacer la source complète du noeud  
+                       console.log('wValue    : '+wValue);
+                       console.log('validite  : '+validite);
+                   } else {
+                       // Gérer le input pour laisser le focus et passer en rouge
+                       let message = "Unsuccessfull SIMP processing for |" + node.getPath() + "|, value |"+value+"| has not been accepted.";
+                       treeMessage(_msgCssSelStr,"alert-warning",message) ;
+                   };
+                   node.render(true,false);  //force rendering the node (not parents nor descendants)
+                   //node.renderStatus();    //CSS element updates
+               };
+               
+               callService('updateSimp', {id: node.key, value:value }, checkCallBack);
+               
+            } // MCSIMP
+       },
+       lazyLoad: function(event, data) {
+            var node = data.node;
             console.log("event.type, data :"+event.type+" , "+data);
-            if( !data.activeTd ) {
-              alert( "Custom default action for row: " + data.node.title );
-              // we don't return false, so default action is applied:
-            } else if ( data.colIdx != 3 ) {
-              // eslint-disable-next-line no-alert
-              alert( "Custom default action: " + data.node.title );
-              return false;
-            };
-
-           let success=function(data, status) { 
-                   <!-- alert("Successfully Value Sent: " + value + "\nStatus: " + status +"\ndata.changeIsAccepted :" +data.changeIsAccepted ); -->
-                   console.log("Successfully Value Sent: " + value + "\nStatus: " + status +"\ndata.changeIsAccepted :" +data.changeIsAccepted );
-                   rChangeIsAccepted = data.changeIsAccepted;
-                   rValue            = data.source['wValue'];
-                   rValidite         = data.source['validite'];
-                   console.log( {'data': data } );
-                   console.log( {'rValue': rValue } );
-                   console.log( {'rValidite': rValidite } );
-                   console.log( {'rChangeIsAccepted': rChangeIsAccepted } );
-//node.fromDict()
-//source=JSON.parse(getTree1())
-      if ( rChangeIsAccepted ) {
-node.data.wValue   = rValue
-node.data.validite = rValidite
-//node.data        = data.source ?? Essayer de remplacer la source complète du noeud  
-console.log('rValue    : '+rValue);
-console.log('rValidite : '+rValidite);
-      } else {
-// Gérer le input pour laisser le focus et passer en rouge
-      };
-node.render(true,false); //force rendering the node (not parents nor descendants)
-//node.renderStatus();      //CSS element updates
-            };
-                  
-            $.ajax({
-                 type          : "POST",
-                 url           : "{{ url_for('updateSimp') }}",
-                 data          : JSON.stringify({id: node.key, value:value }),
-                 contentType   : "application/json; charset=utf-8",
-                 dataType      : "json", 
-                 // The callback function when the web service return success.
-                 success: success,
-                 // The callback function when the web service return fail.
-                 failure: function(errMsg) {
-                    alert(errMsg);
-                 }
-            })
-          }
-      },
-      lazyLoad: function(event, data) {
-         var node = data.node;
-         console.log("event.type, data :"+event.type+" , "+data);
-        // Issue an Ajax request to load child nodes
-        data.result = {
-          url: "/getBranchData",
-          data: {key: node.key}
-        }
-      },
-      icon: function(event, data) {
-        if( data.node.type ) {
-          return "ft-ico-" + data.node.type;
-        }
-      },
-      activate: function(event, data) {
-          console.log("event.type, data :"+event.type+" , "+data);
-      },
-      deactivate: function(event, data) {
-          console.log("event.type, data :"+event.type+" , "+data);
-      },
-      beforeActivate: function(event, data) {
-          console.log("event.type, data :"+event.type+" , "+data);
-      },
-      beforeSelect: function(event, data) {
-          console.log("event.type, data :"+event.type+" , "+data);
-      },
-      activateCell: function(event, data) {
-          console.log("event.type, data :"+event.type+" , "+data);
-      },
-      select: function(event, data) {
-          console.log("event.type, data :"+event.type+" , "+data);
-      },
-      beforeUpdateViewport: function(event, data) {
-          console.log("event.type, data :"+event.type+" , "+data);
-      },
-      click: function(event, data) {
-          console.log("event.type, data :"+event.type+" , "+data);
-      },
-      blur: function(event, data) {
-          console.log("event.type, data :"+event.type+" , "+data);
-      },
-      blurTree: function(event, data) {
-          console.log("event.type, data :"+event.type+" , "+data);
-      },
-      focusTree: function(event, data) {
-          console.log("event.type, data :"+event.type+" , "+data);
-      },
-      keydown: function(event, data) {
-          console.log("event.type, data :"+event.type+" , "+data);
-      },
+            // Issue an Ajax request to load child nodes
+            data.result = {
+               url: "/getBranchData",
+               data: {key: node.key}
+            }
+       },
+       activate: function(event, data) {
+            console.log("event.type, data :"+event.type+" , "+data);
+       },
+       deactivate: function(event, data) {
+            console.log("event.type, data :"+event.type+" , "+data);
+       },
+       beforeActivate: function(event, data) {
+            console.log("event.type, data :"+event.type+" , "+data);
+       },
+       beforeSelect: function(event, data) {
+            console.log("event.type, data :"+event.type+" , "+data);
+       },
+       activateCell: function(event, data) {
+            console.log("event.type, data :"+event.type+" , "+data);
+       },
+       select: function(event, data) {
+            console.log("event.type, data :"+event.type+" , "+data);
+       },
+       beforeUpdateViewport: function(event, data) {
+            console.log("event.type, data :"+event.type+" , "+data);
+       },
+       click: function(event, data) {
+            console.log("event.type, data :"+event.type+" , "+data);
+       },
+       blur: function(event, data) {
+            console.log("event.type, data :"+event.type+" , "+data);
+       },
+       blurTree: function(event, data) {
+            console.log("event.type, data :"+event.type+" , "+data);
+       },
+       focusTree: function(event, data) {
+            console.log("event.type, data :"+event.type+" , "+data);
+       },
+       keydown: function(event, data) {
+            console.log("event.type, data :"+event.type+" , "+data);
+       },
        
-     });
     });
+});
 
          <!-- var $searchableTree = $('#tree1').treeview({ -->
          <!-- data: getTree1() -->
@@ -647,3 +850,32 @@ node.render(true,false); //force rendering the node (not parents nor descendants
 
     </script>
 {% endblock %}
+
+// # Différents tests :
+
+var tree1=$.ui.fancytree.getTree("#tree1")
+a={ source: [ {title: "Node 1", key: "1000"},{title: "Folder 2", key: "2", folder: true, children: [ {title: "Node 2.1", key: "3000"},{title: "Node 2.2", key: "44"} ]}    ], }
+$("#Optionals").fancytree( a )
+ou
+$.ui.fancytree.createTree('#Optionals', a)
+
+var tree2=$.ui.fancytree.getTree("#Optionals")
+b=tree2.toDict()
+$("#bebe").fancytree( { source: b} )
+$("#bebe").fancytree( { source: tree1.toDict()} )
+classeAccas ==  "MCSIMP"
+p1=tree1.getNodeByKey("54930c427ad911ec90fb64c901d49bcd")
+let myOpt=[}
+p1.visit( (n)=>{if (n.data.classeAccas ==  "MCSIMP") {myOpt.push(n)}}, true)
+------
+
+var tree1=$.ui.fancytree.getTree("#tree1")
+p1=tree1.getNodeByKey("721bcb347adc11ec8c2a64c901d49bcd")
+opt=p1.data.infoOptionnels[0]
+
+var optNodes= opt.map((o)=>{return {'title':o} })
+optTreeSrc=[{'title': p1.title+' (optionals)', 'children': optNodes}];
+$optTree=$("#bebe").fancytree( { source: optTreeSrc} )
+optTree=$optTree.fancytree()
+#var optTree=$.ui.fancytree.getTree("#bebe")
+optTree.expandAll()