<!--

Modified by Genna BEN HASSEN, Camille SIHARATH and Grégoire ALIZADEH NOIRET, 16 December 2021
Modified by Grégoire ALIZADEH NOIRET, 15 March 2023

-->

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Data visualization</title>
  <script src="d3.js" charset="urf-8"></script>
  <%- include('../partials/head') %>
</head>

<body>
  <header>
    <nav id="main-nav" class="navbar navbar-default navbar-fixed-top">
      <%- include('../partials/header') %>

      <!-- Bouton Action -->
      <div id="action" class="dropdown">
        <button class="btn dropdown-toggle btn-sm btn-outline-secondary" type="button" data-bs-toggle="dropdown" aria-expanded="false" title="Choose action on node">
          On click
        </button>
        <ul class="dropdown-menu" aria-labelledby="action">
          <li><a id="branch-res" class="dropdown-item" href="#">View Branch Results</a></li>
          <li><a id="switch" class="dropdown-item" href="#">Switch children</a></li>
          <li><a id="collapse" class="dropdown-item" href="#">Collapse/Expand</a></li>
          <li><a id="subtree" class="dropdown-item" href="#">Sub Tree/Upper Tree</a></li>
        </ul>
      </div>

      <!-- Bouton Collapse -->
      <!-- <button type="button" class="btn btn-default btn-sm" aria-label="Collapse" data-toggle="tooltip" data-placement="top" title="Collapse the tree" id="globalcollapse">
        <span>Compact tree</span>
      </button> -->

      <!-- Bouton Expand -->
      <div class="btn-group" role="group" aria-label="">
        <button id="globalexpand" class="btn btn-sm btn-outline-secondary" type="button" aria-label="Expand" data-toggle="tooltip" data-placement="top" title="Expand the tree (takes time on big trees!)">
          <span>Expand tree</span>
        </button>
      </div>

      <!-- Boutons Width / Height -->
      <div class="btn-group" role="group" aria-label="Tree size buttons">
        <div class="btn-group" role="group" aria-label="Width buttons">
          <!-- Bouton Reduce width -->
          </button>
          <button id="lessWidth" class="btn btn-sm btn-outline-secondary" type="button" aria-label="Less width" data-toggle="tooltip" data-placement="top" title="Reduce tree width">
            <span>Width -</span>
            <span class="glyphicon glyphicon-minus" aria-hidden="true"></span>
          </button>
          <!-- Bouton Increase width -->
          <button id="moreWidth" class="btn btn-sm btn-outline-secondary" type="button" aria-label="More width" data-toggle="tooltip" data-placement="top" title="Increase tree width">
            <span>Width +</span>
            <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
          </button>
          <!-- Bouton Increase width -->
          <button id="loga" class="btn btn-sm btn-outline-secondary" type="button" aria-label="Logarithmic scale" data-toggle="tooltip" data-placement="top" title="Logarithmic scale">
            <span>Logarithmic</span>
            <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
          </button>          
        </div>
        <div class="btn-group" role="group" aria-label="Height buttons">
          <!-- Bouton Reduce height -->
          <button id="lessHeigth" class="btn btn-sm btn-outline-secondary" type="button" aria-label="Left Align" data-toggle="tooltip" data-placement="top" title="Reduce tree height">
            <span>Height -</span>
            <span class="glyphicon glyphicon-minus" aria-hidden="true"></span>
          </button>
          <!-- Bouton Increase height -->
          <button id="moreHeigth" class="btn btn-sm btn-outline-secondary" type="button" aria-label="Left Align" data-toggle="tooltip" data-placement="top" title="Increase tree height">
            <span>Height +</span>
            <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
          </button>
        </div>
      </div>

      <!-- Bouton Alignement -->
      <div id="alignment-type" class="dropdown">
        <button class="btn dropdown-toggle btn-sm btn-outline-secondary" type="button" data-bs-toggle="dropdown" aria-expanded="false" title="Choose alignment type">
          Alignment type
        </button>
        <ul class="dropdown-menu" aria-labelledby="alignment-type">
          <li><a id="aminoacid-display" class="dropdown-item" href="#">Amino Acids</a></li>
          <li><a id="codons-display" class="dropdown-item" href="#">Codons</a></li>
        </ul>
      </div>


        <div class="btn-group" role="group" aria-label="Redundance">
          <!-- Bouton redondance -->
          <button id="redondance" class="btn btn-sm btn-outline-secondary" type="button" aria-label="Redundancy" data-toggle="tooltip" data-placement="top" title="Reduce tree height">
            <span>Redundancy</span>
          </button>
        </div>


      <!-- Bouton Export -->
      <div id="export" class="dropdown">
        <button class="btn dropdown-toggle btn-sm btn-outline-secondary" type="button" data-bs-toggle="dropdown" aria-expanded="false" title="Choose Export">
          Export
        </button>
        <ul class="dropdown-menu" aria-labelledby="export">
          <li><a id="tree" class="dropdown-item" href="#">Tree</a></li>
          <li><a id="selecp" class="dropdown-item" href="#">Alignment</a></li>
        </ul>
      </div>

      <!-- Champs Seuils sup. et inf. -->
      <div id="thresholds-group" class="btn-group" role="group" aria-label="">
        <button class="btn btn-sm btn-outline-secondary">
          <div class="input-group align-items-center">
            <label for="ps-threshold-low">Thresholds</label>
            <input id="ps-threshold-low"  placeholder="PS Threshold" value="0.9" style="width: 5em;"/>
            <input id="ps-threshold-high" placeholder="PS Threshold" value="1.0" style="width: 5em;"/>
          </div>
        </button>
        <div>
          <div class="input-group">
            <input type="radio" class="btn-check" name="selectedThreshold" id="lowerSelected" value="lowerSelected">
            <label class="btn btn-outline-secondary" for="lowerSelected" style="margin-left: 0.5rem;"><</label>
            <input type="radio" class="btn-check" name="selectedThreshold" id="upperSelected" value="upperSelected" checked>
            <label class="btn btn-outline-secondary" for="upperSelected" style="margin-left: -0.25rem;">></label>
          </div>
        </div>
      </div>
    </nav>
    <div id="navigator-container" style="border:2px solid lightgrey; overflow-y: hidden; width: 100%;">
      <svg id="svg0"></svg>
    </div>
    <div>
      <div id="bidon" style="border:2px solid lightgrey; float: left; overflow-y: auto; width: 40%;">
        <svg id="svgbidon"></svg>
      </div>
    <div id="selpos" style="border:2px solid lightgrey; overflow-x: auto; overflow-y: auto;">
        <svg id="svg3"></svg>
    </div>
    </div>
  </header>
  <main>
    <div id="tree-container" style="border:2px solid lightgrey; float: left; overflow-y: auto; width: 40%;">
      <svg id="svg1"></svg>
    </div>
    <div id="alignment-container" style="border:2px solid lightgrey; overflow-x: auto; overflow-y: auto;">
      <svg id="svg2"></svg>
    </div>
  </main>
</body>

<script src="xmltree.js" charset="utf-8"></script>
<script src="layout.js" charset="utf-8"></script>
<script src="links.js" charset="utf-8"></script>
<script src="svgEvents.js" charset="utf-8"></script>

<!-- Code JS pour l'affichage de l'arbre -->
<script type="text/javascript">
// Recuperation des variables envoyees via Express
// ----------------------------------------------
var recTree = <%- arbre %>;
var pattern = <%- pattern %>;
pattern = '';
var branchSite = <%- branchSite %>;
var logBranchLength = <%- logBranchLength %>;

  // Variables globales
// ------------------
var treeRoot;
var cladeRoot;
var layout = d3.layout.cladogram();   // Fonction  D3 qui transforme les donnees en cladograme
var nodeWidth = 80;                  // Largeur
var nodeHeight = 30;                  // Hauteur
// var x0Align = 350;                  // Cordonee horizonatle de l'alignement
var stringWidth;                      // Largeur affichage arbre
var tagSimpleGene = false;            // True si arbre de gene de type PhyloXML, False si arbre de gene de type RecPhyloXML
var svg;                              // La div SVG
var g;                                // Conteneur principal
var diagonal = svgLinks.shoulder;     // cf links.js
var symbol = d3.symbol().size(128);   // Symbol des noeuds
var color = d3.scaleOrdinal(d3.schemeCategory20); // Echelle des couleurs
var options ;                         // Options d'affichage
var action;                           // Action associee au clic
var selectedCrossref ;                // Reference croisée choisie (nom de la sequence)
var selectedCrossrefURL ;             // URL associe
var crossref;                         // Objet decrivant les differentes ref croisees associees a la famille
var spattern=pattern.split('\n');     // pattern a chercher et a afficher dans l'arbre
// Exemple getfam?fam=CLU_005912_4_2_C&pattern=0:PETMA_GL476433.PE1+0:ANOCA_6.PE486+1:XENTR_GL172641_1.PE38+1:XENTR_GL172642_1.PE97
pattern=spattern;
var patterns= [];
var div;
var myStorage = window.localStorage;    // utilisation de localStorage pour consrever les preferences
var displaySeqType = 'AA';
var hStepBase = 12;
var hStep;
var alignmentLetterSpacing = hStepBase - 10;
var psThresholdHigh = parseFloat($('#ps-threshold-high').val()) || parseFloat(1.0);
var psThresholdLow = parseFloat($('#ps-threshold-low').val()) || parseFloat(0.9);
var psThresholdHighBgColor = "#FF0000";
var psThresholdLowBgColor = "#FFFF00";
var psBelowThresholdBgColor = "#DDDDDD";
var psMissingBgColor = "#FFFFFF";
var seq_lg = xmlparser.flatTree(recTree.phyloxml.phylogeny.maxSeqIdLength)*10;
var isCodon = (xmlparser.flatTree(recTree.phyloxml.phylogeny.size.isCodon)=="true");
var collapseMode = 'collapseOff';
var navWindowColor = '#000000';
var alignmentWindow = $('#alignment-container');
var selposWindow = $('#selpos');
var compactWindow = $('#navigator-container');
var resultsJSON = null;
var resultsShown;
var maxSeqLen = 0;
var updateNodesTimeout = null;
var selectedNode = null;
var globalI = 0;
var leftBoundary = 0;
var rightBoundary = 1;
var firstLoad = 1;
var blackWhite = false; // Light grey -> dark grey instead of grey -> light red -> dark red
var automaticBoundaries = true;
var siteOffset = 0;
var nbBranches = 0;
var nbLeaves = 0;
var nbSites = 0;
var upperThresholdMode = true;

// Simon
// Dico des aa ( a ameliorer)

/*var coloraa = new Object();

coloraa["K"]= "rgb(230,51,25)"
coloraa["R"]= "rgb(230,51,25)"

coloraa["A"]= "rgb(25,128,230)"
coloraa["F"]= "rgb(25,128,230)"
coloraa["I"]= "rgb(25,128,230)"
coloraa["L"]= "rgb(25,128,230)"
coloraa["M"]= "rgb(25,128,230)"
coloraa["V"]= "rgb(25,128,230)"
coloraa["W"]= "rgb(25,128,230)"

coloraa["N"]= "rgb(25,204,25)"
coloraa["Q"]= "rgb(25,204,25)"
coloraa["S"]= "rgb(25,204,25)"
coloraa["T"]= "rgb(25,204,25)"

coloraa["H"]= "rgb(25,179,179)"
coloraa["Y"]= "rgb(25,179,179)"

coloraa["C"]= "rgb(230,128,128)"

coloraa["D"]= "rgb(204,77,204)"
coloraa["E"]= "rgb(204,77,204)"

coloraa["P"]= "rgb(204,204,0)"

coloraa["G"]= "rgb(230,153,77)"

coloraa["-"] = "white";
  */

// var rgbaa = new Object();
//
// rgbaa["K"]= [230,51,25];
// rgbaa["R"]= [230,51,25];
//
// rgbaa["A"]= [25,128,230];
// rgbaa["F"]= [25,128,230];
// rgbaa["I"]= [25,128,230];
// rgbaa["L"]= [25,128,230];
// rgbaa["M"]= [25,128,230];
// rgbaa["V"]= [25,128,230];
// rgbaa["W"]= [25,128,230];
//
// rgbaa["N"]= [25,204,25];
// rgbaa["Q"]= [25,204,25];
// rgbaa["S"]= [25,204,25];
// rgbaa["T"]= [25,204,25];
//
// rgbaa["H"]= [25,179,179];
// rgbaa["Y"]= [25,179,179];
//
// rgbaa["C"]= [230,128,128];
//
// rgbaa["D"]= [204,77,204];
// rgbaa["E"]= [204,77,204];
//
// rgbaa["P"]= [204,204,0];
//
// rgbaa["G"]= [230,153,77];
//
// rgbaa["-"] = [255,255,255];
// rgbaa["*"] = [0,0,0];


var hslaa = new Object();

hslaa["K"]= [8,80,50];
hslaa["R"]= [8,80,50];

hslaa["A"]= [210,80,50];
hslaa["F"]= [210,80,50];
hslaa["I"]= [210,80,50];
hslaa["L"]= [210,80,50];
hslaa["M"]= [210,80,50];
hslaa["V"]= [210,80,50];
hslaa["W"]= [210,80,50];

hslaa["N"]= [120,78,45];
hslaa["Q"]= [120,78,45];
hslaa["S"]= [120,78,45];
hslaa["T"]= [120,78,45];

hslaa["H"]= [180,75,40];
hslaa["Y"]= [180,75,40];

hslaa["C"]= [0,67,70];

hslaa["D"]= [300,55,55];
hslaa["E"]= [300,55,55];

hslaa["P"]= [60,100,40];

hslaa["G"]= [30,75,60];

hslaa["-"] = [0,0,100];
hslaa["*"] = [0,0,0];

var flag_redond = 1;

// Fonction rgb ->cold
// ---------------------------------------
// function  rgb2col(rgb) {
//   var col = "rgb("+rgb[0]+","+rgb[1]+","+rgb[2]+")";
//   return col
// }

// Fonction rgb ->cold
// ---------------------------------------
function  hsl2col(rgb) {
  var col = "hsl("+rgb[0]+","+rgb[1]+"%,"+rgb[2]+"%)";
  return col
}
var colordna = new Object();
var colordna_red = new Object();
var geneticcode = xmlparser.flatTree(recTree.phyloxml.phylogeny.geneticCode);
geneticcode["---"] = "-";

  for (d in geneticcode){
    var col = "grey";
    var _aa = geneticcode[d];
    if (_aa != undefined)  {
      var _hsl = hslaa[_aa];
      if (_hsl != undefined) {
        col = hsl2col(_hsl)
      }

    }
    colordna[d] = col;
    //colordna[d] = rgb2col(rgbaa[geneticcode[d]]);
  }

var redond_dna = new Object();

for (d in geneticcode){
    var _aa = geneticcode[d];
    if (_aa in redond_dna) {
      redond_dna[_aa].push(d);
    }
    else {
      redond_dna[_aa] = [d];
    }
  }


/*Redefinit les couleurs des codons*/
console.log("REDOND = ");
console.log(redond_dna);
var step_satur = 50;

for (d in redond_dna){
  var _codons =  redond_dna[d];
  if (d != "*" && d!= "-") {
    for (i = 0;i < _codons.length;i++) {
      var _hsl =  hslaa[d];
      var new_hsl = [_hsl[0],_hsl[1],_hsl[2] + Math.floor(i* step_satur / _codons.length)];
      console.log(_codons[i]+ " avant "+ colordna[_codons[i]]+ " apres "+ hsl2col(new_hsl));
      colordna_red[_codons[i]] =    hsl2col(new_hsl);
    }
  }
}

var  dico_aa = new Object();
var  dico_dna = new Object();
console.log("isCodon",isCodon);

if (!isStorageSupported(myStorage)) {
  console.log("window.localStorage problem");
  $(testnav).append('It seems that <b>window.localStorage</b> is full or is not supported by your browser<br>Please check this <a href="/clear/">here</a> ');
}
else {
  main_display();
}

// Fonction d'affichage principale
// ---------------------------------------
function  main_display() {
  $('#redondance').hide();
  // Arbre d'especes (lu dans monarbre.js)
  // var speciesTreeRoot = d3.hierarchy(cladeRootSpecies, function(d) {
    // return d.clade;
  // });
  // Transforme le tableau de [index:sequences] en tableau de tableau de [sequences].
  // a ameliorer, pas tres propre
  pattern.forEach(function(d,i){
    var buf = d.split(':');
    if (buf.length > 1){
      var indexPatt = buf[0];
      if (!patterns[indexPatt]){
        patterns[indexPatt]  = [];
      }
      patterns[indexPatt].push(buf[1]);
    }
    else {
      // console.log("PATTERN ERROR : "+buf);
    }
  });
  // Test si l'arbre est du type
  // -recPhymloXML ( pas de distance, evenements de perte et transfert)
  // -phyloxml ()
  if (recTree.recGeneTree) {
    cladeRoot = recTree.recGeneTree.phylogeny.clade;
    // treeRoot = recTree.recGeneTree.phylogeny;
  }
  if (recTree.recPhylo) {
    cladeRoot = recTree.recPhylo.recGeneTree.phylogeny.clade;
    // treeRoot = recTree.recPhylo.recGeneTree.phylogeny;
  }
  if (recTree.phyloxml) {
    cladeRoot = recTree.phyloxml.phylogeny.clade;
    // treeRoot = recTree.phyloxml.phylogeny;
    tagSimpleGene = true;
  }

  // Creation du SVG
  // ===============
  // Definit  la div tooltip
  div = d3.select("body").append("div")
    .attr("class", "tooltip")
    .style("opacity", 1)
    .style('position', 'absolute');
  svg1 = d3.select("#svg1");   // selectionne la div svg1
  g = svg1.append("g")      // ajoute element au svg1
    .attr("id", "treeAlign");
  var transition = d3.transition();
  margin = {
    top: 100,
    down: 20,
    left: 200,
    right: 300
  }

  // Option d'affichage
  stringOptions = myStorage.getItem("options");
  if (stringOptions === null || stringOptions === undefined || stringOptions === "undefined"){
    stringOptions = "name,species";
  }
  options = stringOptions.split(",");

  // Action associee au click
  // action = svgEvents.switchChildren;
  action = svgEvents.showBranchResults;
  var stringAction = myStorage.getItem("action");
  if ((stringAction === null )||(stringAction === undefined)||(stringAction === "undefined")){
    stringAction = 'View Branch Results';
    myStorage.setItem("action",stringAction);
  }
  switch (myStorage.getItem("action")) {
    case 'View Branch Results':
      action = svgEvents.showBranchResults;
      break;
    case "Switch children":
      action = svgEvents.switchChildren;
      break;
    case "Collapse/Expand":
      action = svgEvents.collapse;
      break;
    case "Sub Tree/Upper Tree":
      action = svgEvents.focus;
      break;
    default:
      console.log(action);
      break;
  }

  // Largeur de l'arbre
  if (tagSimpleGene === false) {
    nodeWidth = nodeWidth / 5 ;
  }
  stringWidth = myStorage.getItem("width");
  if ((stringWidth === null )||(stringWidth === undefined)||(stringWidth === "undefined")){
    stringWidth = nodeWidth;
    myStorage.setItem("width",stringWidth);
  }

  // Concerne les arbres recphyloxml
  var flatTreeConfig = {
    transferBack :false,
    speciationLoss : false,
    speciationOutLoss : false,
  }

  // Operation effectuee si il s'agt d'un arbre RecPhyloXML
  if (tagSimpleGene === false) {
    // cladeRoot est la racine hierarchisee en clade avec des modifs
    // specifiques aux arbres recPhyloXML
    cladeRoot =  xmlparser.flatTree(cladeRoot,flatTreeConfig);
  }
  var _cladeRoot = null;
  var _Parent = null;
  treeRoot = d3.hierarchy(cladeRoot, function(d) {
    return d.clade;
  });
  addNumberSeqSpec(treeRoot);
  updateLayout(cladeRoot,1);
}

// Definitions des autres fonctions
// --------------------------------
// Fonction de mise a jour de l'affichage
// ---------------------------------------
function updateLayout(cRoot,firstLoad=0) {
  var treeRoot = d3.hierarchy(cRoot, function(d) {
    return d.clade;
  });
  treeRoot.each(function (d) {
    var eventsRec = d.data.eventsRec;
    // Test si on a un noeud enrichi avec l'info eventsRec (type RecPhyloXML) ou non (type phyloxml)
    if (eventsRec){
      d.data.lastEvent = eventsRec[eventsRec.length - 1];
    }
    else {
      // Lors de la premiere lecture de l'arbre phyloxml, il n'y a pas de data.lastEvent defini aux noeuds
      // (Sauf les noeuds ecrases par la monophylie)
      // Mais ensuite ils sont definis : on choisit "speciation" meme si il ne s'agit pas d'une speciation en
      // realité, mais cela permet d'utiliser les code graphiques de speciation. eventType peut etre "speciation".
      // Par contre il ne doit plus etre   "collapsed" car c'est lie a la representation dans le DOM. Utiliser nodeinfo.status
      if (d.children) {
        if (!d.data.lastEvent) {
          d.data.lastEvent =  {eventType : "speciation"};
        }
      }
    }
  });
  layout.nodeSize([nodeWidth, nodeHeight]); // *i*
  layout(treeRoot);
  // Traitement des distances dans le cas d'un arbre de gene de type phyloxml
  if (tagSimpleGene) {
      if (firstLoad == 1) {
        var max_tree_length =  getmaxlength(treeRoot,0);
        if (max_tree_length > 0) {
          nodeWidth = Math.floor(nodeWidth / max_tree_length) * 3.0;
        }
    };
    phylogeny(treeRoot,nodeWidth);
  } else {
      if (firstLoad == 1) {
        var max_tree_length =  getmaxlength(treeRoot,0);
        if (max_tree_length > 0) {
        nodeWidth = Math.floor(nodeWidth / max_tree_length) * 3.0;
      }
    };
    phylogeny_setloss(treeRoot,nodeWidth);
  }

  // Remplissage du dico nom de sequence => tableau de couleurs
  if ((firstLoad == 1) ||  (firstLoad == 2)) {

    var nodes_ini = treeRoot.descendants()
    var leaves_ini = nodes_ini.filter(function (e) {
      return !e.children;
    });
    var leaves_sequences = leaves_ini.filter(function(d) {return (d.data.lastEvent.aaAlign);});
    leaves_sequences.forEach (function (d,i) {
      var leaf_name = d.data.name;
      var col_aas = [];
      var col_dnas = [];
      console.log("SEQUENCE NAME = "+leaf_name);
      var dna_sequence = d.data.lastEvent.dnaAlign;
      var aa_sequence = d.data.lastEvent.aaAlign;
      var aas = aa_sequence.split("");
      aas.forEach (function (aa,j) {
        var col = hslaa[aa];
        if (col == undefined) {
          col  = "grey"
        }
        else {
          col = hsl2col(col);
        };
        col_aas.push(col);
      })
      dico_aa[leaf_name] = col_aas;

      if (dna_sequence != undefined) {
        var dnas = dna_sequence.split("");
        for (i = 0; i <= dnas.length-3; i+=3) {
          var codon  = dnas[i]+dnas[i+1]+dnas[i+2];
          var col = "grey";
          if (flag_redond  == -1) {
            col = colordna_red[codon];
              console.log("redundant color "+ col);
          } else {
             col = colordna[codon];
              console.log("classical color "+ col);
          }
          if (col == undefined) {col  = "grey"};
          col_dnas.push(col);
        }
      }
      dico_dna[leaf_name] = col_dnas;

    })
  }

  updateSvg(treeRoot,firstLoad);
  updateScroll();
}

// Fonction principale : mise a jour du svg
// ----------------------------------------
function updateSvg(treeRoot,firstLoad ,config = {}) {
  console.log('Start updatesvg');

  // Suppression de l'ancien affichage de l'alignement et du graphe
  d3.selectAll('#ps-graph, #sequences-container').remove();

  // ====================================================
  // | Début modif : variables pour graphe et séquences |
  // ====================================================
  // Ajustement du pas selon le type de séquences affichées
  hStep = displaySeqType == 'AA' ? hStepBase : hStepBase*3;
  var seqblocks = d3.selectAll('.seqblock');
  if (!resultsJSON && branchSite) {
    showTooltip();
    writeTooltip('Please click a node to display branch data');
    resultsJSON = new Array(maxSeqLen).fill(-1);
  } else if (!resultsJSON && !branchSite) {
    hideTooltip();
    resultsJSON = JSON.parse(xmlparser.flatTree(recTree.phyloxml.phylogeny.global_results.results));
    // console.log('Results undefined, not branch-site');
  } else {
    maxSeqLen = resultsJSON.length;
  }

  resultsJSON = resultsJSON;
  // console.log(resultsJSON);
  var psGraphMargins = {top: 20, right: 50, bottom: 50, left: 50+seq_lg};
  var psGraphWidth = resultsJSON.length*hStep;
  var psGraphHeight = 100;
  // ==============================================================
  // Fin modif : variables pour graphe et séquences
  // ==============================================================
  var scrollref=0;
  var configLayout = {
    layout : config.layout || "cladogramSpecial",
    links : config.links || "shoulder",
    symbolSize : config.symbolSize || 128,
    lengthLinkLoss : config.lengthLinkLoss || 10,
    linkStrokeSize : config.linkStrokeSize || 3,
    nodeWidth : config.nodeWidth || 30,
    nodeHeight : config.nodeHeight || 30,
    margin : config.margin || { top: 10, down: 20, left: 500  , right: 50},
    color : config.color || {
      speciation : "#2F4F4F",
      speciationOutLoss : "#2CA02C",
      branchingOut : "#D62728",
      speciationOut : "#2CA02C",
      bifurcationOut : "#000000",
      transferBack : "#D62728",
      duplication : "#9467BD",
      speciationLoss : "#1F77B4",
      leaf : "#FF7F0E",
      loss : "#000000"
    },
    symbols : config.symbols || {
      speciation : "symbolCircle",
      collapsed : "symbolCircle",
      speciationOutLoss : "symbolCircle",
      branchingOut : "symbolSquare",
      speciationOut : "symbolCircle",
      bifurcationOut : "symbolCircle",
      transferBack : "symbolDiamond",
      duplication : "symbolSquare",
      speciationLoss : "symbolCircle"
    }
  }

  var nodes = treeRoot.descendants();
  var links = treeRoot.links();


  // Coloration du pattern si il existe
  if ((pattern != "none") && (pattern !== ' ') && (pattern !== undefined)) {
    // Boucle sur les differents patterns
    patterns.forEach(function (d,i) {
      // Traite le pattern d avec comme index i
      // var subNodes = subnode(nodes,d,i);
      scrollref = subnode(nodes,d,i);
    });
  }

  var minX = d3.min(nodes, function(d) {
    return d.x;
  });
  var maxX = d3.max(nodes, function(d) {
    return d.x;
  });
  var minY = d3.min(nodes, function(d) {
    return d.y;
  });
  var maxY = d3.max(nodes, function(d) {
    return d.y;
  });
  var widthSVG = maxX - minX;
  var heightSVG = maxY - minY;
  var ajustementX = resultsJSON.length*hStep - 120;
  var ajustementY = -200;
  //  var decalageY = -90;
  var decalageY = -150;

  svg1.attr("width", widthSVG + margin.right + margin.left);
  svg1.attr("height", heightSVG + margin.top + margin.down+300      + ajustementY);
  g.attr("transform", "translate(" + (margin.right - minX - 250) + "," + (margin.top - minY + 50 + psGraphHeight + decalageY) + ")");

  // console.log(links);

  // ====================
  //  D3: Objets LINK
  // ====================
  var link = g.selectAll(".link").data(links);  // associe l'element link a la donnee links
  //EXIT gere les elements qui ont disparu
  link.exit().remove();
  //ENTER gere les elements nouveaux
  var linkEnter =
    link
    .enter()
    .append("path")
    .attr("class", "link");
  // affichage des branches
  linkEnter
    .merge(link)  // fusionne les nouveaux elements avec les anciens
    .attr("fill","none")
    .attr("stroke-width",configLayout.linkStrokeSize)
    .attr("stroke",function (d) {
      if (d.target.data.lastEvent !== undefined) {
        var pa = d.target.data.lastEvent.pattern;
        if (pa !== undefined) {
          return "red";
        }
      }
      if (d.target.data.deadSpecies)
        {
          return "#ffffff";
        }
      else {
        return "#0e2e2e";
      }
    })
    .on('mouseover',function (d) {
      var pattIndex =  d.target.data.lastEvent.pattern;
      if (pattIndex !== undefined) {
        var sel1 = g.selectAll(".link").filter(function(e) {
          var pa = e.target.data.lastEvent.pattern;
          if (pa !== undefined) {
            // pa est le tableau des index des pattern  associee au noeud
            // pattIndex[0] est l'index de la pattern  associee au noeud survole
            return (pa.indexOf(pattIndex[0]) > -1)
          }
          return (false)
        });
      }
    })
    .on('mouseout',function (d){
      var pattIndex =  d.target.data.lastEvent.pattern;
      if (pattIndex !== undefined) {
        var sel1 = g.selectAll(".link").filter(function(e) {
          var pa = e.target.data.lastEvent.pattern;
          if (pa !== undefined) {
            // pa est le tableau des index des pattern  associee au noeud
            // pattIndex[0] est l'index de la pattern  associee au noeud survole
            return (pa.indexOf(pattIndex[0]) > -1)
          }
          return (false)
        });
        sel1.style("stroke-width",configLayout.linkStrokeSize)
      }
    })
    .attr("d", diagonal)
    .attr('stroke-dasharray',function(d){
      var et = d.target.data.lastEvent.eventType;
      // var sl = d.target.data.lastEvent.speciesLocation;
      // if (et === "transferBack" || et === "bifurcationOut" || sl == "Out") return '5,5';
      if (et === "transferBack" || et === "bifurcationOut") return '5,5';
    });
  // ====================
  // D3:  Objets NODE
  // ====================
  var node = g.selectAll(".node").data(nodes);
  //EXIT
  node.exit().remove();
  //ENTER
  var nodeEnter =
    node
    .enter()
    .append("g")
    .attr("class", "node");

  nodeEnter
    .append("g")
      .attr("class", "gsymbol")
      .append("path")
        .attr("class", "symbol")
        .style('stroke-width', '0')
        .style('stroke', '#000000')
        .on('mouseover', function(d) {
          if (branchSite && selectedNode) {
            showTooltip();
            // Display a tooltip when hovering the cursor over a node
            if (d.data.branch_info) {
              writeTooltip(getPositiveRateJSON(d.data.branch_info.results, leftBoundary, rightBoundary).toFixed(3));
            } else {
              writeTooltip('');
            }
          }
        })
        .on('mouseout', function() {
          if (selectedNode) {
            hideTooltip();
          }
        })
        .on('click', function(d) {
          if (action == svgEvents.showBranchResults && d.data.branch_info) {
            resultsJSON = JSON.parse(d.data.branch_info.results);
            selectedNode = d3.select(this);
          }
        });
  if (firstLoad) {
    d3.selectAll('path.symbol')
      .style('stroke-width', '0')
      .style('stroke', '#888888')
      .style('fill', '#000000')
      ;
  } else {
    d3.selectAll('path.symbol')
      .style('stroke-width', '0')
      .style('stroke', '#000000')
      .style('fill', '#000000')
      ;
    if (selectedNode) {
      // selected node color and outline
      selectedNode
        .style('stroke-width', '2')
        .style('stroke', '#000000')
        .style('fill', '#FFFFFF')
        ;
    }
  }
  nodeEnter
    .append("text")
    .attr("class", "label")
    .on("click", function(d) {
    });
  nodeEnter
    .append("text")
    .attr("class", "internal");
  //ENTER + UPDATE
  var allNodes =
    nodeEnter
    .merge(node)
    .attr("transform", function(d) {
      if (d.data.lastEvent && d.data.lastEvent.eventType === "speciation") {
        return "translate(" + [d.x, d.y] + ")";
      }
      else {
        return "translate(" + [d.x + 4, d.y] + ")";
      }
    })
    .on("click",action);
  allNodes
    .select(".gsymbol")
    .attr("transform", function(d) {
      var str = "";
      if (d.data.lastEvent && d.data.lastEvent.eventType === "loss") {
        str += "rotate(45)";
      }
      if (d.data.lastEvent && d.data.lastEvent.eventType === "leaf")  {
        str += "rotate(90)";
      } else {
        str += "";
      }
      return str;
    });
  allNodes
    .select(".symbol")
    .attr("d", function(d) { //Type de symbol
       // Verification si le noeud est collapse
      if (d.data.nodeinfo && d.data.lastEvent) {
          if (d.data.nodeinfo.status === "collapsed") {
            return symbol.type(d3.symbolCircle)()
          }
      }
      if (d.data.lastEvent) {
        switch (d.data.lastEvent.eventType) {
          case "monophyletic":          // cas ou le noeud monophyletic n'est pas pas collapse
            return symbol.type(d3[configLayout.symbols.speciation])();
            break;
          case "speciation":
            return symbol.type(d3[configLayout.symbols.speciation])();
            break;
          case "speciationOutLoss":
            return symbol.type(d3[configLayout.symbols.speciationOutLoss])();
            break;
          case "branchingOut":
            return symbol.type(d3[configLayout.symbols.branchingOut])();
            break;
          case "speciationOut":
            return symbol.type(d3[configLayout.symbols.speciationOut])();
            break;
          case "bifurcationOut":
            return symbol.type(d3[configLayout.symbols.bifurcationOut])();
            break;
          case "transferBack":
            return symbol.type(d3[configLayout.symbols.transferBack])();
            break;
          case "duplication":
            return symbol.type(d3[configLayout.symbols.duplication])();
            break;
          case "speciationLoss":
            return symbol.type(d3[configLayout.symbols.speciationLoss])();
            break;
          case "leaf":
            return symbol.type(d3.symbolTriangle)();
            break;
          case "loss":
            return symbol.type(d3.symbolCross)();
            break;
          default:
            return symbol.type(d3.symbolCross)();
            break;
        }
      } else {
        return symbol.type(d3.symbolCross)();
      }
    });
  allNodes
    .select(".label")
    .transition()
    .attr("y", function(d) {
      // return d.children ? -8 : 3;
      return d.children ? 0 : 4;
    })
    .attr("x", function(d) {
      // return d.children ? -8 : 8;
      return d.children ? 0 : 12;
    })
    .style("text-anchor", function(d) {
      return d.children ? "end" : "start";
    })
    .text(function(d) {
      var name = "";
      if (d.data.nodeinfo) {
        if (d.data.nodeinfo.status === "collapsed") {
            name += "seq:"+d.data.nbseqspe.nbSeq + " spe:"+d.data.nbseqspe.nbSpe;
        }
      }
      if (d.data.name && options.includes("name") && d.data.lastEvent.eventType === "leaf") {
        if (d.data.lastEvent && d.data.lastEvent.crossref) {
          var cross=d.data.lastEvent.crossref;
          if (selectedCrossref !== null) {
            name += cross[selectedCrossref];
          }
        }
        else {
          name += (d.data.name || d.data.lastEvent.geneName);
        }
        if (d.data.lastEvent && d.data.lastEvent.pattern !==undefined) {
          name = ">>>>"+name+"<<<<";
        }
      }

      return name;
    })
    .style("fill",function (d) {
      // if (d.data.name && d.data.lastEvent.eventType === "leaf") {
      //   return setLeafColour(d.data.lastEvent.speciesLocation);
      // }
      // if (d.data.name && d.data.lastEvent.eventType === "monophyletic") {
      //   return setLeafColour(d.data.lastEvent.speciesLocation);
      // }
    })
    .style("font-family","sans-serif")
    .style("font-size","12px")
    .style("font-weight","bold")
    .style("text-decoration",function (d) {
      if (d.data.lastEvent) {
        var pa = d.data.lastEvent.pattern;
      } else {
        var pa = undefined;
      }
      if (pa != undefined) {
        return "wavy underline overline";
      }
      else return "none";
    });
  allNodes
    .select(".internal")
    .transition()
    .attr("y", 11)
    .attr("x", -10)
    .style("text-anchor","end")
    .text(function(d) {
      var name = "";
      if (options.includes("intname")) {
        if (d.data.confidence) {
          // Display confidence below branches
          var name = d.data.confidence._text;
        }
      }
      return name
    });

  // Get tree characteristics (do this only once)
  if (firstLoad) {
    nbBranches = g.selectAll('path.symbol').size();
    g.selectAll('path.symbol').each(function(elem) {
      if (elem.data.lastEvent.aaAlign) {
        nbLeaves ++;
        if (elem.data.lastEvent.aaAlign.length > nbSites) {
          nbSites = elem.data.lastEvent.aaAlign.length;
        }
      }
    });
  }

  // =======================
  // Selection des feuilles
  // =======================
  var leaves = nodes.filter(function (e) {
    return !e.children;
  });

  var widthSVG0 = resultsJSON.length;
  var heightSVG0 = 60;
  var psGraphGlobalWidth = resultsJSON.length;
  var psGraphGlobalHeight = 60;
  var psGraphGlobalMargins = {top:0, right:0, bottom:0, left:0};
  var svg0 = d3.select('#svg0');
  svg0.attr('width', widthSVG0 + 15)
      .attr("height",psGraphGlobalHeight);
  var svg3 = d3.select('#svg3');
  svg3
    // .attr('width', widthSVG0 + 15)
    // .attr('height',  heightSVG0*3)
    // .attr("position","fixed")
    .attr("width",  50 + seq_lg + 10 + resultsJSON.length*hStep + 10 + seq_lg + 50);
    // .attr("height",   10);

  d3.select('#ps-graph-global').remove();

  if (!document.getElementById('ps-graph-global')) {
    // Ajout du graphe des résultats au SVG
    svg0.append('g')
      .attr('id', 'ps-graph-global')
      .attr('transform', 'translate('+psGraphGlobalMargins.left+', '+psGraphGlobalMargins.top+')');
  }

  var psGraphGlobal = d3.select('#ps-graph-global');

  // Calcul du décalage des sites pour le positionnement des rectangles du graphe
  // Association des données aux futurs rectangles
  var globalRects = psGraphGlobal
    .selectAll('.ps-rects-global')
    .data(function() {
      return resultsJSON;
    });
  globalRects.exit().remove();
  // Affichage des rectangles
  var i = 0;
  globalRects.enter()
    .append('rect')
      .attr('class', 'ps-rects-global')
      .attr('width', function(d) { return 1; })
      .attr('height', function(d) { return psGraphGlobalHeight; })
      .attr('x', function(d) { i ++; return i - siteOffset; })
      .attr('y', function(d) { return 0; })
      .style('fill', function(d) {
        return getColorFromResult(d);
      });

  // Handle click-drag events on the small graph
  document.getElementById(compactWindow.attr('id')).onmousedown = function(e) {
    scrollAlignment(e);
    compactWindow.on('mousemove', function (e) {
      scrollAlignment(e);
    });
    compactWindow.on('mouseup', function (e) {
      // scrollAlignment(e);
      compactWindow.off('mousemove mouseup');
    });
  };

  document.getElementById(alignmentWindow.attr('id')).onscroll = function() {
    // Generic scrolling (even without the small graph)
    // Draw rectangle at currently visualized site
    $('.navigation-window').remove();
    var siteNumber = - Math.trunc((60 + seq_lg - alignmentWindow.scrollLeft() - alignmentWindow.innerWidth()/2)/hStep) - 2;
    var navWindowWidth = alignmentWindow.innerWidth()/hStep;
    var siteStart = Math.trunc(siteNumber - navWindowWidth/2) + 5;
    var siteEnd = Math.trunc(siteNumber + navWindowWidth/2) + 5;
    var navWindowRect = psGraphGlobal
      .append('rect')
        .attr('class', 'navigation-window')
        .attr('x', siteStart)
        .attr('y', 0)
        .attr('width', navWindowWidth)
        .attr('height', psGraphGlobalHeight)
        .style('fill', 'rgba(0,0,0,0.17)')
        .style('stroke', navWindowColor)
        .style('stroke-width', '0');

    // autre scroll
    var ss =  alignmentWindow.scrollLeft();
    selposWindow.scrollLeft(ss, 0);
    if (automaticBoundaries) {
      leftBoundary = siteStart;
      rightBoundary = siteEnd;
    }
    updateNodeColors();
  };

  document.getElementById(selposWindow.attr('id')).onscroll = function() {
    // Generic scrolling (even without the small graph)
    // Draw rectangle at currently visualized site
    $('.navigation-window').remove();
    var siteNumber = - Math.trunc((60 + seq_lg - selposWindow.scrollLeft() - selposWindow.innerWidth()/2)/hStep) - 2;
    var navWindowWidth = selposWindow.innerWidth()/hStep;
    var siteStart = Math.trunc(siteNumber - navWindowWidth/2) + 5;
    var siteEnd = Math.trunc(siteNumber + navWindowWidth/2) + 5;
    var navWindowRect = psGraphGlobal
      .append('rect')
        .attr('class', 'navigation-window')
        .attr('x', siteStart)
        .attr('y', 0)
        .attr('width', navWindowWidth)
        .attr('height', psGraphGlobalHeight)
        .style('fill', 'rgba(0,0,0,0.17)')
        .style('stroke', navWindowColor)
        .style('stroke-width', '0');

      var ss =  selposWindow.scrollLeft();
      alignmentWindow.scrollLeft(ss, 0);
        // scroll l'autre


    if (automaticBoundaries) {
      leftBoundary = siteStart;
      rightBoundary = siteEnd;
    }
    updateNodeColors();
  };

  // ======================================================
  // | Début : graphe des résultats de sélection positive |
  // ======================================================
  var  svg2 = d3.select("#svg2");
  svg2
    .attr("width",  50 + seq_lg + 10 + resultsJSON.length*hStep + 10 + seq_lg + 50)
    .attr("height",  heightSVG + margin.top + margin.down + 100);
  // === Définition de l'échelle pour l'axe x ===
  var x = d3.scaleLinear()
    .domain([1, resultsJSON.length+1])
    .range([0, psGraphWidth]);
  // === Définition de l'échelle pour l'axe y ===
  var y = d3.scaleLinear()
    .domain([0, maxStatJSON(resultsJSON)])
    .range([psGraphHeight, 0]);
  // === Ajout du graphe des résultats au SVG ===
  var psGraph = svg3.append('g')
    .attr('id', 'ps-graph')
    .attr('transform', 'translate('+(50 + seq_lg)+', '+psGraphMargins.top+')');
  // === Ajout de l'axe x au graphe ===
  psGraph.append('g')
    .attr('transform', 'translate(0,' + psGraphHeight + ')')
    .call(d3.axisBottom(x).ticks(resultsJSON.length/5)); // graduations tous les 5 codons / AA
  // === Ajout de l'axe y au graphe ===
  psGraph.append('g')
    .call(d3.axisLeft(y));

  // === Calcul du décalage des sites pour le positionnement des rectangles du graphe ===
  // === Association des données aux futurs rectangles ===
  var rects = psGraph
    .selectAll('.ps-rects')
    .data(function() {
      // console.log('big graph: attaching resultsJSON', resultsJSON);
      return resultsJSON;
    });
  // === Affichage des rectangles ===
  var i = 0;
  rects.enter()
    .append('rect')
      // .attr('id', function(d) {
      //   return 'ps-rect-'+d.site;
      // })
      .attr('class', 'ps-rects')
      // .attr('title', function(d) {
      //   return d.site;
      // })
      .attr('width', function(d) { return hStep; })
      .attr('height', function(d) { return y(0) - y(d); })
      .attr('x', function(d) { i ++; return x(i - siteOffset); })
      .attr('y', function(d) { return y(d); })
      .style('fill', function(d) {
        return getColorFromResult(d);
      })
      .style('stroke', '#000000')
      .style('stroke-width', '0')
      .on('mouseover', function(d) {
        // console.log(d);
        writeTooltip(d);
        showTooltip();
      })
      .on('mouseout', hideTooltip);

  // Ajout du seuil supérieur au graphe
  psGraph.append('line')
    .attr('x1', x(1))
    .attr('y1', y(psThresholdHigh))
    .attr('x2', x(resultsJSON.length+1))
    .attr('y2', y(psThresholdHigh))
    .style('stroke', '#000000')
    // .style('stroke', psThresholdHighBgColor)
    .style('stroke-width', '2')
    .style('stroke-dasharray', ('4, 4'));
  // Ajout du seuil inférieur au graphe
  psGraph.append('line')
    .attr('x1', x(1))
    .attr('y1', y(psThresholdLow))
    .attr('x2', x(resultsJSON.length+1))
    .attr('y2', y(psThresholdLow))
    .style('stroke', '#000000')
    // .style('stroke', psThresholdLowBgColor)
    .style('stroke-width', '1')
    .style('stroke-dasharray', ('4, 4'));

  psGraph.append('br');

  // ====================================================
  // | Fin : graphe des résultats de sélection positive |
  // ====================================================







  // ==============================
  // | Début : blocs de séquences |
  // ==============================
  seq_dataset = [];
  // var leaves_sequences = leaves.filter(function(d) {return (isNuc ? d.data.lastEvent.dnaAlign : d.data.lastEvent.aaAlign);});
  var leaves_sequences = leaves.filter(function(d) {return (d.data.lastEvent.aaAlign);});
    leaves_sequences.forEach (function (d,i) {
      var leaf_name = d.data.lastEvent.name;
      var dna_sequence = d.data.lastEvent.dnaAlign;
      var aa_sequence = d.data.lastEvent.aaAlign;
      var lengthmax=d.data.lastEvent.lengthMax;
      var locnode = {
        name:d.data.name,
        y:d.y,
        dna:d.data.lastEvent.dnaAlign,
        aa:d.data.lastEvent.aaAlign,
      };
      seq_dataset.push(locnode);
    });
  var seqDiv = d3.select('#svg2')
    .append('g')
    .attr('id', 'sequences-container')
    .attr("transform", "translate(" + (margin.right - minX - 300) + "," + (margin.top - minY + 50 + psGraphHeight  + decalageY) + ")")
  // Association des données aux futurs blocs de séquences
  var seqblocks = seqDiv
    .selectAll('.seqblock')
    .data(seq_dataset);
  // Création et positionnement des blocs de séquences
  var seqblocksEnter = seqblocks.enter().append('g')
    .attr('class', 'node seqblock')
    .attr('transform', function(d) {
      var transx = psGraphMargins.left;
      var transy = d.y ; // fonctionne seulement avec nodeHeight = 15 *amod*
      return 'translate('+transx+', '+transy+')';
    });
    seqblocksEnter.append("text")
      .attr('class', 'sequence-name')
      .text(function(d) {
        return d.name;
      })
      .attr('transform', 'translate('+'-'+(Math.floor(seq_lg))+','+5+')');
  var maxYSequences = function(seqDataset) {
    var currentmax = seqDataset[0].y;
    seqDataset.forEach(function(seq) {
      currentmax = seq.y > currentmax ? seq.y : currentmax;
    });
    return currentmax;
  }
  var maxYSeq = maxYSequences(seq_dataset);
  var minYSequences = function(seqDataset) {
    var currentmin = seqDataset[0].y;
    seqDataset.forEach(function(seq) {
      currentmin = seq.y < currentmin ? seq.y : currentmin;
    });
    return currentmin;
  }
  var minYSeq = minYSequences(seq_dataset);
  // Alignement : ajout des conteneurs au SVG
  var seqTextAA = seqblocksEnter.append('g')
      .attr('class', 'aa-sequence')
      .style('display', function() {
        return displaySeqType == 'AA' ? 'block' : 'none';
      });
  var seqTextNuc = seqblocksEnter.append('g')
      .attr('class', 'dna-sequence')
      .style('display', function() {
        return displaySeqType == 'Nuc' ? 'block' : 'none';
      });

  // Alignement : ajout des rectangles
  var aaSequence = d3.select('.aa-sequence');
  var dnaSequence = d3.select('.dna-sequence');
  var targetSequence = displaySeqType == 'AA' ? aaSequence : dnaSequence;


// Simon : Alignement : ajout des coelurs AA
  console.log("Genere aa_dataset and dna_dataset");
  aa_dataset = [];
  dna_dataset = [];
/*  d3.selectAll(".aa_block").remove(); // on vire  tout
  d3.selectAll(".dna_block").remove();*/

  d3.select("#aa-container").remove(); // on vire  tout
  d3.select("#dna-container").remove();

  var leaves_sequences = leaves.filter(function(d) {return (d.data.lastEvent.aaAlign);});

  leaves_sequences.forEach (function (d,i) {
    var index = i;
    var seqname =  d.data.name;
    var aas = dico_aa[seqname];
    aas.forEach (function (aa,j) {
      var opac =  getOpacFromResult(resultsJSON[j]);
      var locnode = {
        index:i,
        name:d.data.name,
        y:d.y,
        pos:j,
        col:aa,
        opac:opac,
      };
      aa_dataset.push(locnode);
      })
      var dnas = dico_dna[seqname];
      dnas.forEach (function (dna,j) {
        var opac =  getOpacFromResult(resultsJSON[j]);
        var locnode = {
        index:i,
        name:d.data.name,
        y:d.y,
        pos:j,
        col:dna,
        opac:opac,
      };
      dna_dataset.push(locnode);
      })
    });

  console.log("aa_dataset and dna_dataset OK");

seqTextAA.append('text')
    .style('letter-spacing', (alignmentLetterSpacing)+'px') // Espacement des lettres pour correpondre au graphe
    .attr('x', function(d) {return 1;})
    .attr('y', function(d) {return Math.floor(hStepBase/3);})
    .text(function(d) {return d.aa;});

  seqTextNuc.append('text')
    .style('letter-spacing', (alignmentLetterSpacing)+'px') // Espacement des lettres pour correpondre au graphe
    .attr('x', function(d) {return 1;})
    .attr('y', function(d) {return Math.floor(hStepBase/3);})
    .text(function(d) {return d.dna;});

    var aaDiv = d3.select('#svg2')
      .append('g')
      .attr('id', 'aa-container')
      .attr("transform", "translate(" + (margin.right - minX - 300) + "," + (margin.top - minY + 50 + psGraphHeight  + decalageY) + ")")


    var aablock =
      aaDiv.selectAll(".aa_block")
      .data(aa_dataset);
      //EXIT
      aablock.exit().remove();
      //ENTER
      var aablockEnter =
      aablock
      .enter()
      .append("g")
      .attr("class", "aa_block");
      //ENTER + updatesATE
      aablockEnter
      .merge(aablock)
      .append("rect")
      .attr('width', function(d) { return hStep; })
      .attr('height', function(d) { return 15 })
      .style("fill", function(d){ return (d.col)})
      .style("opacity", function(d){
        if (displaySeqType != "AA") {
          return 0
        } else {
/*          return d.opac*/
          return 0.5
        }
      });


      //ENTER + updatesATE
      aablockEnter
      .merge(aablock)
      .attr("transform",function(d,i) {
        var transx = (d.pos)*hStep + margin.left + 30 ;
         if (branchSite) {
         transx = (d.pos)*hStep + seq_lg + 50 ;
        }
        if (isCodon == 0 ) {
          transx = transx - 80 // ???
        }
        var transy = d.y - 7;
        return "translate("+transx+", "+transy+")";
      })
      .append("rect")
      .attr("class","aa_rect")
      .attr("transform","translate(0,15)" )
      .attr('width', function(d) { return hStep; })
      .attr('height', function(d) { return 2 })
      .attr("stroke","black")
      .attr("stroke-opacity", 1)
      .style("fill", "black")
      .style("opacity", function(d){
        if (displaySeqType != "AA") {
          return 0
        } else {
        return d.opac

        }
      });

      var dnaDiv = d3.select('#svg2')
        .append('g')
        .attr('id', 'dna-container')
        .attr("transform", "translate(" + (margin.right - minX - 300) + "," + (margin.top - minY + 50 + psGraphHeight  + decalageY) + ")")


      var dnablock =
        dnaDiv.selectAll(".dna_block")
        .data(dna_dataset);
        //EXIT
        dnablock.exit().remove();
        //ENTER
        var dnablockEnter =
        dnablock
        .enter()
        .append("g")
        .attr("class", "dna_block");
        //ENTER + updatesATE
        dnablockEnter
        .merge(dnablock)
        .append("rect")
        .attr('width', function(d) { return hStep; })
        .attr('height', function(d) { return 10 })
        .style("fill", function(d){ return (d.col)})
        .style("opacity", function(d){
          if (displaySeqType != "Nuc") {
            return 0
          } else {
            return 0.5
          }
        });
        // Rectangles qui soulignent les position sel pos
        //ENTER + updates
        dnablockEnter
        .merge(dnablock)
        .attr("transform",function(d,i) {
          var transx = (d.pos)*hStep + margin.left + 30 ;
          if (branchSite) {
            transx = (d.pos)*hStep + seq_lg + 50 ;
          }         
          var transy = d.y - 7;
          return "translate("+transx+", "+transy+")";
        })
        .append("rect")
        .attr("class","dna_rect")
        .attr("transform","translate(0,15)" )
        .attr('width', function(d) { return hStep; })
        .attr('height', function(d) { return  4 })
        .attr("stroke","black")
        .attr("stroke-opacity", 1)
        .style("fill", "black")
        .style("opacity", function(d){
          if (displaySeqType != "Nuc") {
            return 0
          } else {
            return d.opac
          }
        });


  // ==============================================================
  // Fin modif : blocs de séquences
  // ==============================================================
  firstLoad = 0;
    console.log('Fin updatesvg');
}
// Fin de la fonction updateSvg
// ----------------------------

// Autres fonctions utilitaires
// ----------------------------

function getColorFromResult(res) {
  if (upperThresholdMode) {
    if (res >= psThresholdHigh) {
      return psThresholdHighBgColor;
    } else if (res >= psThresholdLow) {
      return psThresholdLowBgColor;
    } else if (res >= 0) {
      return psBelowThresholdBgColor;
    } else {
      return psMissingBgColor;
    }
  } else {
    if (res >= 0) {
      if (res <= psThresholdLow) {
        return psThresholdHighBgColor;
      } else if (res <= psThresholdHigh) {
        return psThresholdLowBgColor;
      } else {
        return psBelowThresholdBgColor;
      }
    } else {
      return psMissingBgColor;
    }
  }
}


// Simon
function getOpacFromResult(res) {
  if (upperThresholdMode) {
    if (res >= psThresholdHigh) {
      //return psThresholdHighBgColor;
      return 1.0;
    } else if (res >= psThresholdLow) {
//      return psThresholdLowBgColor;
      return 0.5;
    } else if (res >= 0) {
      //return psBelowThresholdBgColor;
      return 0.0;
    } else {
      //return psMissingBgColor;
      return 0.0;
    }
  } else {
    if (res >= 0) {
      if (res <= psThresholdLow) {
        //return psThresholdHighBgColor;
        return 1.0;
      } else if (res <= psThresholdHigh) {
        //return psThresholdLowBgColor;
        return 0.0;
      } else {
        //return psBelowThresholdBgColor;
        return 0.0;
      }
    } else {
      // return psMissingBgColor;
      return 0.0;
    }
  }
}

// Fonction de calcul de la statistique maximale dans les résultats
function maxStatJSON(jsonResultsList) {
    var currentmax = jsonResultsList[0];
    jsonResultsList.forEach(function(res) {
      if (res > currentmax) {
        currentmax = res;
      }
    });
    if (currentmax >= 2) {
      return 2;
    } else {
      return currentmax;
    }
    return currentmax;
  }
  function minStatJSON(jsonResultsList) {
    var currentmin = jsonResultsList[0];
    jsonResultsList.forEach(function(res) {
      if (res < currentmin) {
        currentmin = res;
      }
    });
    if (currentmin <= 0) {
      return currentmin;
    } else {
      return 0;
    }
  }

function colorByRate(rate) {
  if (blackWhite) {
    return 'hsl(0, 0%, '+(80-2*rate*100)+'%)';
  } else {
    if (rate > 0) {
      return 'hsl(0, 100%, '+(85 - 2*rate*100)+'%)';
    } else {
      return 'hsl(0, 0%, '+(80-2*rate*100)+'%)';
    }
  }
}

function updateNodeColors() {
  // The color of a node depends on the number of positive sites
  g.selectAll('path.symbol')
    .style('fill', function(d) {
      if (d.data.branch_info) {
        var positiveRate = getPositiveRateJSON(d.data.branch_info.results, leftBoundary, rightBoundary).toFixed(3);
        return colorByRate(positiveRate);
      }
    });

  // The color of a branch depends on the number of positive sites in its node
  g.selectAll('path.link')
    .style('stroke', function(d) {
      if (d.target.data.branch_info) {
        var positiveRate = getPositiveRateJSON(d.target.data.branch_info.results, leftBoundary, rightBoundary).toFixed(3);
        return colorByRate(positiveRate);
      }
    });

  // Selected node color and outline
  if (action == svgEvents.showBranchResults && selectedNode) {
    selectedNode
      .style('stroke-width', '2')
      .style('stroke', '#000000')
      .style('fill', '#FFFFFF')
      ;
  }
}

function scrollAlignment(event) {
  // Navigation on the small graph
  // Get the site's number (mouse position in pixels) and navigate to this site in the large window
  var siteNumber = event.pageX + compactWindow.scrollLeft();
  var leftScroll = 50 + seq_lg + 10 + siteNumber*hStep - alignmentWindow.innerWidth()/2;
  var rightScroll = 50 + seq_lg + 10 + siteNumber*hStep + alignmentWindow.innerWidth()/2;
  var siteStart = Math.trunc((leftScroll - (60 + seq_lg))/hStep)+2;
  var siteEnd = Math.trunc((rightScroll - (60 + seq_lg))/hStep)+2;
  alignmentWindow.scrollLeft(leftScroll, 0);
  selposWindow.scrollLeft(leftScroll, 0);
  if (automaticBoundaries) {
    leftBoundary = siteStart;
    rightBoundary = siteEnd;
  }
}

function updateScroll() {
  // Keep the navigation window visible after a branch change
  alignmentWindow
    .scrollLeft(alignmentWindow.scrollLeft()+1)
    .scrollLeft(alignmentWindow.scrollLeft()-1);
  selposWindow
      .scrollLeft(selposWindow.scrollLeft()+1)
      .scrollLeft(selposWindow.scrollLeft()-1);
}

function reloadGlobalResults() {
  resultsJSON = JSON.parse(xmlparser.flatTree(recTree.phyloxml.phylogeny.global_results.results));
  updateLayout(cladeRoot);
}

function hideTooltip() { $('.tooltip').hide(); }
function showTooltip() { $('.tooltip').show(); }
function writeTooltip(text) { $('.tooltip').text(text); }

// Get the rate of positive sites within the indicated boundaries
function getPositiveRateJSON(textResultsList, leftB, rightB) {
  var jsonResultsList = JSON.parse(textResultsList);
  var nbPos = 0;
  jsonResultsList.forEach((value, site) => {
    if (upperThresholdMode) {
      if (value >= psThresholdHigh && site >= leftB && site <= rightB) {
        nbPos ++;
      }
    } else {
      if (value >= 0 && value <= psThresholdLow && site >= leftB && site <= rightB) {
        nbPos ++;
      }
    }
  });
  return nbPos/(rightBoundary - leftBoundary + 1);
}

// Fonction de test du support local.storage
// ------------------------------------------
// https://michalzalecki.com/why-using-localStorage-directly-is-a-bad-idea/
function isStorageSupported(storage) {
  try {
    const key = "__some_random_key_you_are_not_going_to_use__";
    storage.setItem(key, key);
    storage.removeItem(key);
    return true;
  } catch (e) {
    return false;
  }
}

function saveSVGTree(){
  $("a#tree").css('color', '#FF0000');
  $("a#selecp").css('color', '#000000');
  var style = "\n";
  var img = new Image();
  // prepend style to svg
  const treeSvg = d3.select('#svg1');
  treeSvg.insert('defs',":first-child");
  d3.select("#svg1 defs")
      .append('style')
      .attr('type','text/css')
      .html(style);
  var as_text = new XMLSerializer().serializeToString(treeSvg.node());
  img.src = 'data:image/svg+xml;base64,'+window.btoa(unescape(encodeURIComponent(as_text)));
  // window.open().document.write('<p>Please copy or save the the image (larger images may not display properly on the page) <img src="' + img.src + '"/>');
  window.open().document.write('<p>Please copy or save the the image (larger images may not display properly on the page)</p> <img src="' + img.src + '"/>');
};

function saveSVGAlignment(){
  $("a#tree").css('color', '#000000');
  $("a#selecp").css('color', '#FF0000');
  // Application de styles pour l'export
  $('.seqblock text').css({
    'font-family':'Courier',
    'font-size': '16px',
  });
  $('.seqblock g text').css({
    'letter-spacing':(alignmentLetterSpacing)+'px',
    'font-size': '16.667px',
  });
  var style = "\n";
  var img = new Image();
  // prepend style to svg
  const alignmentSvg = d3.select('#svg2');
  alignmentSvg.insert('defs',":first-child");
  d3.select("#svg2 defs")
      .append('style')
      .attr('type','text/css')
      .html(style);
  var as_text = new XMLSerializer().serializeToString(alignmentSvg.node());
  img.src = 'data:image/svg+xml;base64,'+window.btoa(unescape(encodeURIComponent(as_text)));
  window.open().document.write('<p>Please copy or save the the image (larger images may not display properly on the page)</p> <img src="' + img.src + '"/>');
  // var as_text = new XMLSerializer().serializeToString(alignmentSvg.node());
  // img.src = 'data:image/svg+xml;base64,'+window.btoa(unescape(encodeURIComponent(as_text)));
  // window.open().document.write('<p>Please copy or save the the image (it may not display if it is too large) <img src="' + img.src + '"/>');
};

// Fonction recursive qui renvoie les noeuds ancestraux communs a
// une liste de feuilles
// ---------------------------------------------------------------
function recuAncestors (todo, done) {
  if (todo.length > 1) {
    var n1 = todo.shift();
    var n2 = todo.shift();
    var anc = n1.path(n2);
    var maxHeight=0;
    var n3=n1;
    anc.forEach(function (d ){
      if (d.height > maxHeight) {
        maxHeight = d.height;
        n3=d;
      }
    });
    todo.splice(0, 0,n3);
    done.push(n1);
    done.push(n2);
    anc = n1.path(n3);
    anc.forEach(function (d ){
      done.push(d)
    });
    anc = n2.path(n3);
    anc.forEach(function (d ){
      done.push(d)
    });
    return recuAncestors (todo, done)
  }
  else {
    var n1  = todo.shift();
    done.push(n1);
    return (done, [])
  }
}

// Fonction de coloration du resultat d'une recherche de  pattern
// La variable pattern est ici un tableau du nom des feuilles qui ont matchés
// --------------------------------------------------------------------------
function subnode(nodes, pattern, colPattern) {
  var ancs;
  var n1;
  var n2;
  var n3;
  var selection = [];
  var racine = null;
  // Selection des feuilles
  var scrollref = 0;
  nodes.forEach(function (d, i){
    if (d.depth == 0) {
      racine = d;
    }
    if (d.data.lastEvent && d.data.lastEvent.crossref) {
      // Si la balise crossref est presente on
      // l'utilise plutot que le nom du gene
      var cross=d.data.lastEvent.crossref;
      for(var exKey in cross) {
        var nomFeuille = cross[exKey];
        pattern.forEach(function (p){
          var nomPattern = p.replace(/[\n\r]/g, '');
          if (nomFeuille === nomPattern) {
            selection.push(d);
            scrollref=d.y;
          }
        })
      }
    }
    if (d.data.name){
      var nomFeuille = d.data.name;
      pattern.forEach(function (p){
        var nomPattern = p.replace(/[\n\r]/g, '');
        if (nomFeuille === nomPattern) {
          selection.push(d);
          scrollref=d.y;
          if (!d.data.lastEvent["pattern"]) {
            d.data.lastEvent["pattern"] = [];
            }
            d.data.lastEvent["pattern"].push(colPattern); // un noeud peut etre associe a plusieur palettes
        }
      })
    }
    var tmp =   removeDuplicates(selection);
    selection = tmp;
  });
  var processed = [];
  if (selection.length == 1) {
    selection.push(racine);
  }
  var toprocess = selection;
  recuAncestors(toprocess,processed);
  var ancestors = processed;
  nodes.forEach(function (d ){
    if (ancestors.includes(d)) {
      if (!d.data.lastEvent["pattern"]) {
        d.data.lastEvent["pattern"] = [];
      }
      d.data.lastEvent["pattern"].push(colPattern); // un noeud peut etre associe a plusieur palettes
    }
  });
  return scrollref;
}

// Fonction de calcul de la position  d'un noeud a partir des longueurs
// de branch_length
// --------------------------------------------------------------------
function phylo(n) {
  var dist = 0.0;
  if (n && n.data.branch_length != null) {
    var p = n.parent;
    if (logBranchLength) {
      dist = Math.log(parseFloat(n.data.branch_length)+1.1) + phylo(p);
    } else {
      dist = parseFloat(n.data.branch_length) + phylo(p);
    }
    return dist;
  }
  return dist;
}


function getmaxlength(treeRoot,max) {
  treeRoot.each(function (d) {
    var phylodist = phylo(d);
    if (phylodist > max) {
      max = phylodist;
    }
  });
  return max;
}


// Fonction pour utiliser les longueurs de branches lors de l'affichage
// d'un arbre
// --------------------------------------------------------------------
function phylogeny(treeRoot,offset) {
  treeRoot.each(function (d) {
    var phylodist = phylo(d);
    d.x = phylodist*offset;
  });
}

// Fonction qui decale la position x d'un "loss"
// ---------------------------------------------
function phylogeny_setloss(treeRoot,offset) {
  treeRoot.each(function (d) {
    if (d.data.lastEvent && d.data.lastEvent.eventType === "loss") {
     var lospar = d.parent;
     d.x = (d.x+lospar.x)/2
    }
  });
}

// Fonction ajoute nb sequences et especes
// ---------------------------------------
function  addNumberSeqSpec(treeRoot) {
  treeRoot.eachAfter(function (d) {
    var specs = [];
    if (!d.children) {
      d.data.nbseqspe =  {nbSeq : 1,nbSpe : 1};
    }
    else {
      var nbseq = 0;
      var nbspe = 0;
      var fils = d.children;
      fils.forEach(function (d){
        nbseq = nbseq + d.data.nbseqspe.nbSeq;
        nbspe = nbspe + d.data.nbseqspe.nbSpe;
        var spec = getSpec(d);
        specs = specs.concat(spec);
      })
      var tmp = specs;
      uspecs=removeDuplicates(tmp);
      nbspe=uspecs.length;
      d.data.nbseqspe =  {nbSeq :nbseq,nbSpe :nbspe};
    }
  });
}

// Fonction qui collapse les noeuds d'une certaine profondeur
// ----------------------------------------------------------
function expandTree(treeRoot) {
  treeRoot.each(function (d) {
    if (d.data.nodeinfo) {
      if (d.data.nodeinfo.status === "collapsed") {
        if (d.data._clade) {
          d.data.clade = d.data._clade;
          d.data._clade = null;
          d.data.nodeinfo = {status : "extended"};
        }
      }
    }
  });
}

// Fonction recupere especes
// -------------------------
function getSpec (noeud) {
  if (!noeud.children) {
    var specs = [];
    var eventsRec = noeud.data.eventsRec;
    if (eventsRec) {
      var lastEvent = eventsRec[eventsRec.length - 1];
      specs.push(lastEvent.speciesLocation)
    }
    else {
      console.log(" ERROR : NO EVENTS FOR ");
      console.log(noeud.data);
    }
    return specs;
  }
  else {
    var specs = [];
    var fils = noeud.children;
      fils.forEach(function (d, i){
        var spec = getSpec(d);
        var tmp = specs.concat(spec);
        specs = tmp;
      });
    return specs;
  }
}

// Fonction qui collapse les noeuds d'une certaine profondeur
// ----------------------------------------------------------
function expandTree(treeRoot) {
  treeRoot.each(function (d) {
    if (d.data.nodeinfo) {
      if (d.data.nodeinfo.status === "collapsed") {
        if (d.data._clade) {
          d.data.clade = d.data._clade;
          d.data._clade = null;
          d.data.nodeinfo = {status : "extended"};
        }
      }
    }
  });
}

// Fonction qui collapse les noeuds d'une certaine profondeur
// ----------------------------------------------------------
function collapseVeryBigTree(treeRoot,patterns) {
  var acollapser = [];
  var aouvrir = [];
  if (patterns.length > 0)  {
    //Ecrase tous les noeuds
    treeRoot.each(function (d) {
      if ((d.depth == 5) || (d.depth == 10) ||(d.depth == 20)|| (d.depth == 30)|| (d.depth == 40) ) {
      acollapser.push(d)
      }
    });
    // Boucle sur les patterns
    patterns.forEach(function(p) {
      // Selection des feuilles de la pattern
      p.forEach(function(d,i) {
        var nomPattern= d.replace(/[\n\r]/g, '');
        var focusNode = null;
        // Recupere la feuille focus
        treeRoot.each(function (d,i ){
          if (d.data.name){
            var nomFeuille = d.data.name;
            focusNode = d;
          }
          // cas des crossref
          if (d.data.eventsRec) {
            if (d.data.eventsRec[0].crossref) {
              // Si la balise crossref est presente on
              // l'utilise plutot que le nom du gene
              var cross=d.data.eventsRec[0].crossref;
              for(var exKey in cross) {
                var nomFeuille = cross[exKey];
                if (nomFeuille === nomPattern){
                  console.log("FIND PATTERN "+nomFeuille);
                  focusNode = d;
                }
              }
            }
          }
        });
        if (focusNode) {
          var parent = focusNode.parent;
          //Ouvre les freres de la feuille
          parent.each(function (d) {
            // aouvrir.push(d)
          });
          //ouvre les noeuds entre la feuille et la racine
          while (parent != null) {
            aouvrir.push(parent)
            parent = parent.parent;
          }
        }
        else {
          console.log("PATTERN NOT FOUND : "+nomPattern);
        }
      });
    });
  }
  else {
    console.log("NO PATTERN")
    treeRoot.each(function (d) {
      if ((d.depth == 5) || (d.depth == 10) ||(d.depth == 30)|| (d.depth == 60)|| (d.depth == 90) ) {
        acollapser.push(d)
      }
    });
  }
  acollapser.forEach(function (d) {
    console.log('collapseVeryBigTree: acollapser');
    if (d.data.clade) {
      // if (collapseMode == 'collapseOn') {
      d.data._clade = d.data.clade;
      d.data.clade = null;
      d.data.nodeinfo = {status : "collapsed"};
      // }
    }
  });
  aouvrir.forEach(function (d) {
    if (d.data._clade) {
      d.data.clade = d.data._clade;
      d.data._clade = null;
      d.data.nodeinfo = {status : "extended"};
    }
  });
}
// Fonction de suppression d'un element unique dans un tableau
// -----------------------------------------------------------
function removeSingle(target_array) {
  target_array.sort();
  var i = 0;
  while(i < target_array.length) {
    if(target_array[i] !== target_array[i+1]) {
      target_array.splice(i,1);
    }
    else {
      i += 1;
    }
  }
  return target_array;
}
// Fonction de suppression des  elements multiples dans un tableau
// ---------------------------------------------------------------
function removeDuplicates(target_array) {
  target_array.sort();
  var i = 0;
  while(i < target_array.length) {
    if(target_array[i] === target_array[i+1]) {
      target_array.splice(i+1,1);
    }
    else {
      i += 1;
    }
  }
  return target_array;
}
// Fonction  suppression des  elements multiples dans un tableau et classe par occurence
// -------------------------------------------------------------------------------------
function removesortDuplicates(target_array) {
  target_array.sort();
  var sorted_array = [];
  var i = 0;
  var j = 0;
  var vscore  = {score: 0, fam: "none"};
  var scored_array = [];
  var score = [];
  score[j] = 0;
  sorted_array[i] = target_array[i];
  i=1;
  while(i < target_array.length) {
    if(target_array[i] !==  sorted_array[j]) {
      j = j + 1;
      sorted_array[j] = target_array[i];
      score[j] = 0;
    } else {
        score[j] = score[j] + 1;
    }
      i = i + 1;
  }
  i = 0;
  while(i <= j){
    vscore  = {score: score[i], fam: sorted_array[i]};
    scored_array.push(vscore);
    i = i + 1
  }
  scored_array.sort(function(a,b){
        scA=a.score;
        scB=b.score;
        return scB - scA;
      });
  i = 0;
  while(i <= j){
    sorted_array[i] = scored_array[i].fam;
    i = i + 1
  }
  return sorted_array;
}


// Actions sur les boutons
// -----------------------
$('#selectCrossref').on('click', function(event) {
  selectedCrossref=$( "#selectCrossref option:selected" ).text();
  selectedCrossrefURL=crossref[selectedCrossref].url;
  myStorage.setItem("crossref",selectedCrossref);
  //updateLayout(cladeRoot);
});
$('#selectCrossref').ready(function() {
    // selectedCrossref="HOGENOM";
  });
$('#action').ready(function() {
  switch (myStorage.getItem("action")) {
    case 'View Branch Results':
      $('#branch-res').css('color','#FF0000');
      break;
    case "Switch children":
      $('#switch').css('color','#FF0000');
      break;
    case "Collapse/Expand":
      $('#collapse').css('color','#FF0000');
      break;
    case "Sub Tree/Upper Tree":
      $('#subtree').css('color','#FF0000');
      break;
    default:
  }
});
$('#action').on('click', function(event, dropdownData) {
  var target = event.toElement || event.relatedTarget || event.target || function () { throw "Failed to attach an event target!"; }
  myStorage.setItem("action",target.text);
  switch (target.text) {
    case 'View Branch Results':
      action = svgEvents.showBranchResults;
      $('#branch-res').css('color','#FF0000');
      $('#switch').css('color','#000000');
      $('#collapse').css('color','#000000');
      $('#subtree').css('color','#000000');
/*      updateLayout(cladeRoot);*/
      break;
    case "Switch children":
      action = svgEvents.switchChildren;
      $('#branch-res').css('color','#000000');
      $('#switch').css('color','#FF0000');
      $('#collapse').css('color','#000000');
      $('#subtree').css('color','#000000');
/*      updateLayout(cladeRoot);*/
      break;
    case "Collapse/Expand":
      action = svgEvents.collapse;
      $('#branch-res').css('color','#000000');
      $('#switch').css('color','#000000');
      $('#collapse').css('color','#FF0000');
      $('#subtree').css('color','#000000');
/*      updateLayout(cladeRoot);*/
      break;
    case "Sub Tree/Upper Tree":
      action = svgEvents.focus;
      $('#branch-res').css('color','#000000');
      $('#switch').css('color','#000000');
      $('#collapse').css('color','#000000');
      $('#subtree').css('color','#FF0000');
/*      updateLayout(cladeRoot);*/
      break;
    default:
  }
});
$('#globalcollapse').on('click', function(event, dropdownData) {
  treeRoot = d3.hierarchy(cladeRoot, function(d) {
    return d.clade;
  });
  collapseVeryBigTree(treeRoot,patterns);
  updateLayout(cladeRoot);
});
$('#globalexpand').on('click', function(event, dropdownData) {
  treeRoot = d3.hierarchy(cladeRoot, function(d) {
    return d.clade;
  });
  expandTree(treeRoot);
  updateLayout(cladeRoot);
});
var modStepX = 150;
var modStepY = 5;
$('#moreWidth').on('click', function(event, dropdownData) {
  nodeWidth += modStepX;
  myStorage.setItem("width",nodeWidth);
  updateLayout(cladeRoot);
});
$('#lessWidth').on('click', function(event, dropdownData) {
  nodeWidth -= modStepX;
  if  (nodeWidth < modStepX ) {
    nodeWidth = modStepX;
  }
  myStorage.setItem("width",nodeWidth);
  updateLayout(cladeRoot);
});
$('#loga').on('click', function(event, dropdownData) {
  if (logBranchLength) {
    logBranchLength = false;
  }else {
    logBranchLength = true;
  }
  updateLayout(cladeRoot);
});
$('#moreHeigth').on('click', function(event, dropdownData) {
  nodeHeight += modStepY;
  updateLayout(cladeRoot);
});
$('#lessHeigth').on('click', function(event, dropdownData) {
  if (nodeHeight - modStepY >= 30 ) {
  // if (true) {
    nodeHeight -= modStepY;
    updateLayout(cladeRoot);
  }
});
$('#redondance').on('click', function(event, dropdownData) {
    flag_redond = - flag_redond;
    console.log("REDOND "+flag_redond);
    updateLayout(cladeRoot,2);
});
$('#export').on('click', function(event, dropdownData) {
  var target = event.toElement || event.relatedTarget || event.target || function () { throw "Failed to attach an event target!"; }
  myStorage.setItem("export",target.text);
  switch (target.text) {
    case "Tree":
      $(".internal").css('font-size', '0.8rem');
      saveSVGTree();
      break;
    case "Alignment":
      saveSVGAlignment();
      // Correction des styles de la page après l'export
      $('.seqblock text').css({
        'font-family':'Courier',
      });
      $('.seqblock g text').css({
        'letter-spacing': (alignmentLetterSpacing)+'px',
        'font-size': '16.667px',
      });
      break;
    default:
  }
});

// The page has loaded
$(document).ready(function() {
  $('#upperSelected').change(function() {
    upperThresholdMode = true;
    updateLayout(cladeRoot);
  });
  $('#lowerSelected').change(function() {
    upperThresholdMode = false;
    updateLayout(cladeRoot);
  });

  if (!isCodon) {
      $('#alignment-type').hide();
  }

  if (!branchSite) {
    $('#left-boundary, #right-boundary').prop('disabled', true);
  }
  $('input#automatic-boundaries').change(function (e) {
    if ($(this).is(':checked')) {
      automaticBoundaries = true;
    } else {
      automaticBoundaries = false;
    }
  });
  var tooltips = document.querySelectorAll('.tooltip');
  window.onmousemove = function (e) {
      var x = (e.clientX + 16) + 'px';
      var y = (e.clientY + 16 + window.scrollY) + 'px';
      for (var i = 0; i < tooltips.length; i++) {
          tooltips[i].style.top = y;
          tooltips[i].style.left = x;
      }
  };
  $('button#reload-global-results').click(reloadGlobalResults);
  switch (displaySeqType) {
    case 'AA':
      $('a#aminoacid-display').css('color', '#FF0000');
      $('a#codons-display').css('color', '#000000');
      break;
    case 'Nuc':
      $('a#aminoacid-display').css('color', '#000000');
      $('a#codons-display').css('color', '#FF0000');
      break;
    default:
      $('a#aminoacid-display').css('color', '#000000');
      $('a#codons-display').css('color', '#000000');
      break;
  }
  $('main').css('margin-top', $('header').outerHeight());
  // Gestion du redimensionnement de la fenêtre
  $(window).on('ready resize', function() {
    $('main').css('margin-top', $('header').outerHeight());
  });

  // Gestion du bouton pour le type d'alignement
  $('#alignment-type').on('click', function(event, dropdownData) {
    var target = event.toElement || event.relatedTarget || event.target || function () { throw "Failed to attach an event target!"; }
    myStorage.setItem("alignment-type",target.text);
    switch (target.text) {
      case "Codons": // Codons to Amino Acids
        if (displaySeqType != 'Nuc') {
          displaySeqType = "Nuc";
          $(".aa-sequence").css("display", "none");
          $(".dna-sequence").css("display", "block");
          $("#toggle-seqtype").html("Current display: "+displaySeqType);
          $("#codons-display").css('color', '#FF0000');
          $("#aminoacid-display").css('color', '#000000');
          $('#redondance').show();

          updateLayout(cladeRoot);
          alignmentWindow.scrollLeft(alignmentWindow.scrollLeft()*3);
          selposWindow.scrollLeft(selposWindow.scrollLeft()*3);
        }
        break;
      case "Amino Acids": // Amino Acids to Codons
        if (displaySeqType != 'AA') {
          displaySeqType = "AA";
          $(".dna-sequence").css("display", "none");
          $(".aa-sequence").css("display", "block");
          $("#codons-display").css('color', '#000000');
          $("#aminoacid-display").css('color', '#FF0000');
          $('#redondance').hide();
          alignmentWindow.scrollLeft(alignmentWindow.scrollLeft()/3);
          selposWindow.scrollLeft(selposWindow.scrollLeft()/3);
          updateLayout(cladeRoot);
        }
        break;
      default:
    }
  });

  // Gestion de la modification des seuils
  $('#ps-threshold-high, #ps-threshold-low').on('change', function(event) {
    // On a modifié un des seuils
    // psThresholdsChanged = true;
    // Correction des valeurs en présence d'incohérences
    var psHighVal = parseFloat($('#ps-threshold-high').val());
    var psLowVal = parseFloat($('#ps-threshold-low').val());
    if (psHighVal < psLowVal) {
      if (event.currentTarget.id == 'ps-threshold-high') {
        // On a descendu le seuil supérieur plus bas que le seuil inférieur
        // ex : psHighVal = 0.95 | psLowVal = 1.0
        // On veut donc redecsendre le seuil inférieur en conséquence
        $('#ps-threshold-low').val(psHighVal);
        psLowVal = psHighVal;
      } else {
        // On a monté le seuil inférieur plus haut que le seuil supérieur
        // ex : psHighVal = 1.0 | psLowVal = 1.05
        // On veut donc remonter le seuil supérieur en conséquence
        $('#ps-threshold-high').val(psLowVal);
        psHighVal = psLowVal;
      }
    }
    // Réaffectation des variables globales
    psThresholdHigh = psHighVal;
    psThresholdLow = psLowVal;
    updateLayout(cladeRoot);
  });
});
</script>
</html>
