changeset 0:b58b229c4cbf draft

planemo upload commit 523a9c8df173302ad38e9f15e7d82eab01736551-dirty
author lain
date Fri, 03 Mar 2023 14:10:24 +0000
parents
children 7e3085fc60c1
files README.md add-one-spectrum-index.js common.js compound-mix.html compound-ref.html config.yml macros.xml meta-compound-mix.html meta-compound-ref.html ms2pf_it.xml server.py tab_list.html
diffstat 12 files changed, 6645 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.md	Fri Mar 03 14:10:24 2023 +0000
@@ -0,0 +1,56 @@
+# MS to Peakforest
+
+
+Metadata
+--------
+
+ * **@name**: MS2PF
+ * **@version**: 1.1.0
+ * **@authors**: Lain Pavot (PFEM - INRAE - MetaboHUB)
+ * **@maintainers**: Lain Pavot (PFEM - INRAE - MetaboHUB)
+ * **@init date**: 2022, November
+ * **@main usage**:
+Generates peakforest forms, auto-filled with data and metadata provided
+with files or parametres.
+Lets you check data and metadata, correct, and send them to the peakforest
+instance of you choice.
+
+test
+----
+
+Test with FragNot data
+```sh
+input='Galaxy10-[Cmpd1-1__INCHIKEY___GIAZPLMMQOERPN-UHFFFAOYSA-N__RT___0..ABINITIOFRAGNOT_PForest.tabular].tabular'
+input="test-data/${input}"
+./server.py \
+  --input ''${input}'',''${input}'',''${input}'' \
+  --raw-metadata \'${input},${input},${input}\' \
+  --method cf_pfem_urine_qtof \
+  --peakforest-url https://metabohub.peakforest.org \
+  --scan-type ms \
+  --polarity positive \
+  --name 'test1,test2,test3'
+```
+
+Test with MS2Snoop data
+```sh
+input='out-smol-base.tsv'
+input="test-data/${input}"
+./server.py \
+  --input ''${input}'' \
+  --method cf_pfem_urine_qtof \
+  --pf_url https://nightly.peakforest.org \
+  --scan_type ms \
+  --polarity positive
+```
+
+Services provided
+-----------------
+
+ * Help and support: support@workflow4metabolomics.org
+
+
+License
+-------
+
+ * Cea Cnrs Inria Logiciel Libre License, version 2.1 (CECILL-2.1)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/add-one-spectrum-index.js	Fri Mar 03 14:10:24 2023 +0000
@@ -0,0 +1,2584 @@
+
+var token = "{{ PF_TOKEN_PLACEHOLDER }}" ;
+if ({{ TAB_INDEX_PLACEHOLDER }} == 1) {
+}
+
+context_{{ TAB_INDEX_PLACEHOLDER }} = {
+  prefix: "#accordion-{{ TAB_INDEX_PLACEHOLDER }} ",
+  name: "Context[{{ TAB_INDEX_PLACEHOLDER }}]",
+  produce_json: {{ PRODUCE_JSON_PLACEHOLDER }},
+  DEFAULT_MS_PEAK_VALUES: {{ MS_PEAK_VALUES_PLACEHOLDER }},
+  DEFAULT_DATA: {{ DEFAULT_DATA }},
+  modeEditSpectrum: false,
+  isSeparationFlowRateInit: false,
+  isMSpeaksInit: false,
+  isLC: false,
+  isGC: false,
+  isIC: false,
+  hot_LC_SFG: null ,
+  hot_MS_Peaks: null,
+  hot_RCC_ADDED: null,
+  jsonSpectrumType: null,
+  isJsonSpectrumTypeComplete: false,
+  jsonSample: null,
+  isJsonSampleComplete: false,
+  isJsonRCCaddedComplete: false,
+  jsonChromato: null,
+  isJsonChromatoComplete: false,
+  jsonAnalyzer: null,
+  isJsonAnalyzerComplete: false,
+  jsonPeaksList: [],
+  isJsonPeaksListComplete: false,
+  jsonOtherMetadata: null,
+  isJsonOtherMetadataComplete: false,
+  jsonMolIonization: null,
+  jsonMolIonBeam: null,
+  cptPeakListTab: 0,
+  jsonAnalyzerAcquisition: [],
+  idMetadataMap: {},
+  listOfViewableSpectra: [],
+  singlePick: true,
+  multiPickLine: -1,
+  subjects: [],
+  fitlerSearchLoadlCpd: 5,
+  inchikey: null,
+  initialized: false,
+  selected_ion_index: null,
+  lock: {
+    precursor_ion: false
+  },
+
+  sent_json: null,
+
+  init: function() {
+
+    var self = this ;
+
+    $(document).ready(() => self.on_ready());
+    $(document).ready(function() {
+      console.log("adding click on open_tab_{{ TAB_INDEX_PLACEHOLDER }}.") ;
+      var activate_tab = function() {
+        if (self.initialized) {
+          return ;
+        }
+        self.auto_set_spec_type() ;
+      }
+      if (self.is_ref()) {
+        $("#open_tab_{{ TAB_INDEX_PLACEHOLDER }}").click(activate_tab) ;
+      }
+      if ({{ TAB_INDEX_PLACEHOLDER }} == 1) {
+        activate_tab() ;
+      }
+    }) ;
+
+    return (self) ;
+
+  },
+
+  on_ready: function() {
+    var self = this ;
+
+    $(self.prefix+".pickChemicalCompound").click(self.pickChemicalCompound);
+    $(self.prefix+".add1spectrum").change(
+      function(){self.add1spectrum_change_handler(this)}
+    )
+    self.attach_search() ;
+    self.add_date_check() ;
+    self.populate_selects() ;
+    self.add_change_handlers() ;
+  },
+
+  populate_selects: function() {
+    var self = this ;
+    var choose_in_list ;
+
+    choose_in_list = `
+      <option value="" selected="selected" disabled="disabled">
+        choose in list&hellip;
+      </option>
+    ` ;
+    $("#add1spectrum-chromatoLC-method-{{ TAB_INDEX_PLACEHOLDER }}").append(choose_in_list) ;
+    $("#add1spectrum-chromatoLC-colConstructor-{{ TAB_INDEX_PLACEHOLDER }}").append(choose_in_list) ;
+    $("#add1spectrum-chromatoLC-separationSolvA-{{ TAB_INDEX_PLACEHOLDER }}").append(choose_in_list) ;
+    $("#add1spectrum-chromatoLC-separationSolvB-{{ TAB_INDEX_PLACEHOLDER }}").append(choose_in_list) ;
+    $("#add1spectrum-sample-lcmsSolvent-{{ TAB_INDEX_PLACEHOLDER }}").append(choose_in_list) ;
+    $.getJSON("{{ PF_URL_PLACEHOLDER }}/webapp/resources/json/list-lc-methods.json", self.populate_lc_methods) ;
+    $.getJSON("{{ PF_URL_PLACEHOLDER }}/webapp/resources/json/list-lc-columns.json", self.populate_lc_columns) ;
+    $.getJSON("{{ PF_URL_PLACEHOLDER }}/webapp/resources/json/list-lc-solvents.json", self.populate_lc_solvents) ;
+    $.getJSON("{{ PF_URL_PLACEHOLDER }}/webapp/resources/json/list-ms-ionization-methods.json", self.populate_ms_ionization) ;
+    $.getJSON("{{ PF_URL_PLACEHOLDER }}/webapp/resources/json/list-lcms-solvents.json", self.populate_lcms_solvents) ;
+    self.resetFromColors();
+  },
+
+  attach_search: function() {
+    var self = this ;
+
+    $("#add-one-cc-s1-value-{{ TAB_INDEX_PLACEHOLDER }}").bind("keypress", function(e) {
+      var code = e.keyCode || e.which;
+      if (code == 13) {
+        self.searchLocalCompound();
+      }
+    });
+    $("#add-one-cc-s1-value-{{ TAB_INDEX_PLACEHOLDER }}").typeahead({
+      source: function(query, process) {
+        return self.searchAjax();
+      }
+    });
+  },
+
+  add_date_check: function() {
+    var self = this ;
+
+    $("#add1spectrum-other-date-{{ TAB_INDEX_PLACEHOLDER }}").focusout(function() {
+      var element = $(this) ;
+      self.rm_warning(element);
+      self.rm_success(element) ;
+      if (element.val() == "") {
+        element.parent().addClass("has-warning");
+      } else {
+        element.parent().addClass("has-success");
+      }
+    });
+  },
+
+  populate_lc_methods: function(data) {
+    // load data from json
+    $.each(data.methods,function() {
+      if (this.name !== undefined) {
+        if (this.value !== undefined) {
+          $("#add1spectrum-chromatoLC-method-{{ TAB_INDEX_PLACEHOLDER }}").append(
+            `<option value="${this.value}">${this.name}</option>`
+          );
+        } else {
+          $("#add1spectrum-chromatoLC-method-{{ TAB_INDEX_PLACEHOLDER }}").append(
+            `<option disabled>${this.name}</option>`
+          );
+        }
+      }
+    });
+  },
+
+  populate_lc_columns: function(data) {
+    // load data from json
+    $.each(data.columns, function() {
+      $("#add1spectrum-chromatoLC-colConstructor-{{ TAB_INDEX_PLACEHOLDER }}").append(
+        `<option value="${this.value}">${this.name}</option>`
+      );
+    });
+    $("#add1spectrum-chromatoLC-colConstructor-{{ TAB_INDEX_PLACEHOLDER }}").append(
+      '<option value="other" >Other</option>'
+    );
+  },
+
+  populate_lc_solvents: function(data) {
+    // load data from json
+    $.each(data.solvents, function() {
+      $("#add1spectrum-chromatoLC-separationSolvA-{{ TAB_INDEX_PLACEHOLDER }}").append(
+        `<option value="${this.value}">${this.name}</option>`
+      );
+      $("#add1spectrum-chromatoLC-separationSolvB-{{ TAB_INDEX_PLACEHOLDER }}").append(
+        `<option value="${this.value}">${this.name}</option>`
+      );
+    });
+  },
+
+  populate_ms_ionization: function(data) {
+    // load data from json
+    $.each(data.methods,function() {
+      if (this.name !==undefined) {
+        if (this.value !==undefined) {
+          $("#add1spectrum-analyzserMS-ionizationMethod-pos-{{ TAB_INDEX_PLACEHOLDER }}").append(
+            `<option value="${this.value}">${this.name}</option>`
+          );
+          $("#add1spectrum-analyzserMS-ionizationMethod-neg-{{ TAB_INDEX_PLACEHOLDER }}").append(
+            `<option value="${this.value}">${this.name}</option>`
+          );
+        } else {
+          $("#add1spectrum-analyzserMS-ionizationMethod-pos-{{ TAB_INDEX_PLACEHOLDER }}").append(
+            `<option disabled>${this.name}</option>`
+          );
+          $("#add1spectrum-analyzserMS-ionizationMethod-neg-{{ TAB_INDEX_PLACEHOLDER }}").append(
+            `<option disabled>${this.name}</option>`
+          );
+        }
+      }
+    });
+  },
+
+  populate_lcms_solvents: function(data) {
+    // load data from json
+    $.each(data.solvents, function () {
+      $("#add1spectrum-sample-lcmsSolvent-{{ TAB_INDEX_PLACEHOLDER }}").append(
+        `<option value="${this.value}" class="${this.classD}">${this.name}</option>`
+      );
+    });
+  },
+
+  add_change_handlers: function() {
+    var self = this ;
+
+    $("#add1spectrum-ionTrapBeam-type-{{ TAB_INDEX_PLACEHOLDER }}").on("change", function() {
+      var v = $("#add1spectrum-ionTrapBeam-type-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      $(self.prefix + ".add1spectrum-ionTrap").hide();
+      if (v == "beam") {
+      } else if (v == "trap") {
+        $(self.prefix + ".add1spectrum-ionTrap").show();
+      }
+    }) ;
+    $("#add1spectrum-peaksMS-msLevel-{{ TAB_INDEX_PLACEHOLDER }}").on("change", function() {
+      var v = $("#add1spectrum-peaksMS-msLevel-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      $(self.prefix + ".disabled-if-ms-in-msms").attr(
+        "disabled",
+        (v == "ms2" || v == "ms3")
+      ) ;
+      $(self.prefix + ".enabled-if-ms-in-msms").attr(
+        "disabled",
+        !(v == "ms2" || v == "ms3")
+      ) ;
+      $(self.prefix + ".hidden-if-ms-in-msms").css(
+        "display",
+        !(v == "ms2" || v == "ms3") ? "none" : ""
+      ) ;
+    }) ;
+  },
+
+  is_mix: function() {
+    return this.DEFAULT_DATA["sample_type"] == "compound-mix" ;
+  },
+
+  is_ref: function() {
+    return this.DEFAULT_DATA["sample_type"] == "compound-ref" ;
+  },
+
+  is_ms: function() {
+    return this.DEFAULT_DATA["spectrum_type"] == "LC_MS" ;
+  },
+
+  is_ms2: function() {
+    return this.DEFAULT_DATA["spectrum_type"] == "LC_MSMS" ;
+  },
+
+  is_other_in_mix() {
+    return this.is_mix() && {{ TAB_INDEX_PLACEHOLDER }} > 1 ;
+  },
+
+  hide: function(id) {
+    if (typeof id == Array) {
+      return (id.forEach(hide)) ;
+    }
+    $(id).hide()
+  },
+
+  rm_success: function(element) {
+    return (this.rm_parent_class(element, "has-success"))
+  },
+  rm_error: function(element) {
+    return (this.rm_parent_class(element, "has-error"))
+  },
+  rm_warning: function(element) {
+    return (this.rm_parent_class(element, "has-warning"))
+  },
+  set_success: function(element) {
+    return (this.set_parent_class(element, "has-success"))
+  },
+  set_error: function(element) {
+    return (this.set_parent_class(element, "has-error"))
+  },
+  set_warning: function(element) {
+    return (this.set_parent_class(element, "has-warning"))
+  },
+  rm_parent_class: function(element, cls) {
+    var parent ;
+
+    if ((parent = element.parent()) == null) {
+      return false ;
+    }
+    return this.rm_class(parent, cls) ;
+  },
+  set_parent_class: function(element, cls) {
+    var parent ;
+
+    if ((parent = element.parent()) == null) {
+      return false ;
+    }
+    return this.set_class(parent, cls) ;
+  },
+  rm_class: function(element, cls) {
+    if (element.hasClass(cls)) {
+      element.removeClass(cls) ;
+      return true ;
+    }
+    return false ;
+  },
+  set_class: function(element, cls) {
+    if (element.hasClass(cls)) {
+      return false ;
+    }
+    element.addClass(cls) ;
+    return true ;
+  },
+  is_success: function(element) {
+    return (this.parent_has_class(element, "has-success"))
+  },
+  is_warning: function(element) {
+    return (this.parent_has_class(element, "has-warning"))
+  },
+  is_error: function(element) {
+    return (this.parent_has_class(element, "has-error"))
+  },
+  is_optional: function(element) {
+    return (element.hasClass("is-optional"))
+  },
+  is_mandatory: function(element) {
+    return (element.hasClass("is-mandatory"))
+  },
+  parent_has_class: function(element, cls) {
+    var parent ;
+
+    if ((parent = element.parent()) == null) {
+      return false ;
+    }
+    return (parent.hasClass(cls)) ;
+  },
+
+  add1spectrum_change_handler: function(element) {
+    var self = this ;
+    var idElem ;
+    var valElem ;
+    var isSuccess ;
+    var isWarning ;
+    var isError ;
+    var isOptional ;
+    var isMandatory ;
+
+    element = $(element) ;
+    idElem = element.attr("id") ;
+    valElem = element.val();
+    console.log(`Change handler called for [${idElem}]=${valElem}`) ;
+    if (idElem.split("-").slice(0, -1).join("-") == "add1spectrum-peaksMS-msPrecursorIon") {
+      var index = idElem.split("-")[3] - 1 ;
+      all_contexts[index].sync_precursor_ion() ;
+      if (all_contexts[index].hot_MS_Peaks != null) {
+        all_contexts[index].hot_MS_Peaks.render()
+      }
+    }
+    isSuccess = self.is_success(element);
+    isWarning = self.is_warning(element);
+    isError = self.is_error(element);
+    isOptional = self.is_optional(element);
+    isMandatory = self.is_mandatory(element);
+
+    switch(idElem) {
+    case "add1spectrum-sample-type-{{ TAB_INDEX_PLACEHOLDER }}":
+      if (valElem == "compound-ref") {
+        $("#add1spectrum-sample-type-compound-ref-{{ TAB_INDEX_PLACEHOLDER }}").show();
+        $("#add1spectrum-sample-type-compound-mix-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+        $("#add1spectrum-sample-type-rcc-added-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+      } else if (valElem == "compound-mix") {
+        $("#add1spectrum-sample-type-compound-mix-{{ TAB_INDEX_PLACEHOLDER }}").show();
+        $("#add1spectrum-sample-type-compound-ref-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+        $("#add1spectrum-sample-type-rcc-added-{{ TAB_INDEX_PLACEHOLDER }}").show();
+        self.handsontableRefChemCpdAdded(null);
+      }
+      break;
+    case "add1spectrum-chromatoLC-colConstructor-{{ TAB_INDEX_PLACEHOLDER }}":
+      self.resetElemColor("add1spectrum-chromatoLC-colConstructorOther-{{ TAB_INDEX_PLACEHOLDER }}");
+      if (valElem == "" || valElem == null || valElem != "other" ) {
+        self.disableElem("add1spectrum-chromatoLC-colConstructorOther-{{ TAB_INDEX_PLACEHOLDER }}");
+      } else {
+        self.enableElem("add1spectrum-chromatoLC-colConstructorOther-{{ TAB_INDEX_PLACEHOLDER }}");
+      }
+      break;
+    case "add1spectrum-chromatoLC-method-{{ TAB_INDEX_PLACEHOLDER }}":
+      self.fulfillLCdata(valElem);
+      break;
+    case "add1spectrum-analyzserMS-ionizationMethod-pos-{{ TAB_INDEX_PLACEHOLDER }}":
+      if ($("#add1spectrum-analyzserMS-ionizationMethod-pos-{{ TAB_INDEX_PLACEHOLDER }}").val() != null) {
+        $($("#add1spectrum-peaksMS-polarity-{{ TAB_INDEX_PLACEHOLDER }} option")[1]).attr("disabled", false);
+      }
+      break;
+    case "add1spectrum-analyzserMS-ionizationMethod-neg-{{ TAB_INDEX_PLACEHOLDER }}":
+      if ($("#add1spectrum-analyzserMS-ionizationMethod-neg-{{ TAB_INDEX_PLACEHOLDER }}").val() != null) {
+        $($("#add1spectrum-peaksMS-polarity-{{ TAB_INDEX_PLACEHOLDER }} option")[2]).attr("disabled", false);
+      }
+      break;
+    }
+    if ((isMandatory || isOptional) && (valElem == "" || valElem == null)) {
+      if (isSuccess) {
+        self.rm_success(element) ;
+      } else if (isWarning) {
+        self.rm_warning(element) ;
+      } else if (isError) {
+        self.rm_error(element) ;
+      }
+      if (isMandatory) {
+        self.set_error(element) ;
+      } else {
+        self.set_warning(element) ;
+      }
+    }
+    if (isMandatory && (valElem != "" && valElem != null)) {
+      if (isError) {
+        self.rm_error(element) ;
+      }
+      self.set_success(element) ;
+    } else if (isOptional && valElem != "") {
+      if (isWarning) {
+        self.rm_warning(element) ;
+      }
+      self.set_success(element) ;
+    }
+    if (element.parent().children("input").size() == 2) {
+      if (isMandatory) {
+        if (isSuccess) {
+          self.rm_success(element) ;
+        } else if (isWarning) {
+          self.rm_warning(element) ;
+        } else if (isError) {
+          self.rm_error(element) ;
+        }
+        if (element.parent().children("input").toArray().some(
+          (el) => $(el).val() === null || $(el).val() === ""
+        )) {
+          self.set_error(element) ;
+        } else {
+          eslf.set_success(element) ;
+        }
+      } else if (isOptional) {
+        isSuccess = self.is_success(element);
+        isWarning = self.is_warning(element);
+        isError = self.is_error(element);
+        if (isSuccess) {
+          self.rm_success(element)
+        } else if (isWarning) {
+          self.rm_warning(element) ;
+        } else if (isError) {
+          self.rm_error(element) ;
+        }
+        if (element.parent().children("input").toArray().some(
+          (el) => ($(el).val() == null || $(el).val() == "")
+        )) {
+          self.set_warning(element) ;
+        } else {
+          self.set_success(element) ;
+        }
+      }
+    }
+    if (element.hasClass("one-or-more")) {
+      // get parent class
+      isSuccess = self.is_success(element);
+      isWarning = self.is_warning(element);
+      isError = self.is_error(element);
+      // reset class
+      if (isSuccess) {
+        self.rm_success(element) ;
+      } else if (isWarning) {
+        self.rm_warning(element) ;
+      } else if (isError) {
+        self.rm_error(element) ;
+      }
+      isTmpSuccess = false;
+      $.each(element.parent().children("input"), function(id, child){
+        if ($(child).val() != null && $(child).val() != "") {
+          isTmpSuccess = true;
+        }
+      });
+      $.each(element.parent().children("select"), function(id, child){
+        if ($(child).val() != null && $(child).val() != "") {
+          isTmpSuccess = true;
+        }
+      });
+      // end parkour
+      element.parent().addClass(
+        isTmpSuccess ? "has-success" : "has-error"
+      );
+    }
+    // CHECK IF OK STEP 2
+    if (element.hasClass("add1spectrum-sampleForm")) {
+      var isBtnStep2OK = true;
+      $.each($(self.prefix + ".add1spectrum-sampleForm"), (id, elem) => {
+        if (self.is_error($(elem)) && $(elem).is(":visible")) {
+          isBtnStep2OK = false;
+        }
+      });
+      if (isBtnStep2OK) {
+        self.rm_class($("#btnSwitch-gotoStep2-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-disabled") ;
+        self.set_class($("#btnSwitch-gotoStep2-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-primary") ;
+        $("#btnSwitch-gotoStep2-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", false);
+      } else {
+        self.set_class($("#btnSwitch-gotoStep2-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-disabled") ;
+        self.rm_class($("#btnSwitch-gotoStep2-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-primary") ;
+        $("#btnSwitch-gotoStep2-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", true);
+      }
+    }
+    // CHECK IF OK STEP 3 - LC
+    if (
+      element.hasClass("add1spectrum-chromatoLCForm")
+      && $("#btnSwitch-gotoStep3-lc-{{ TAB_INDEX_PLACEHOLDER }}").is(":visible")
+    ) {
+      var isBtnStep3OK = true;
+      $.each($(self.prefix+".add1spectrum-chromatoLCForm"), (id, elem) => {
+        if (self.is_error($(elem)) && $(elem).is(":visible"))
+          isBtnStep3OK = false;
+      });
+      if (isBtnStep3OK) {
+        self.rm_class($("#btnSwitch-gotoStep3-lc-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-disabled") ;
+        self.set_class($("#btnSwitch-gotoStep3-lc-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-primary") ;
+        $("#btnSwitch-gotoStep3-lc-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", false);
+      } else {
+        self.set_class($("#btnSwitch-gotoStep3-lc-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-disabled") ;
+        self.rm_class($("#btnSwitch-gotoStep3-lc-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-primary") ;
+        $("#btnSwitch-gotoStep3-lc-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", true);
+      }
+    }
+    // TODO CHECK IF OK STEP 3 - GC
+    // CHECK IF OK STEP 4 - MS ANALYZER
+    if (
+      element.hasClass("add1spectrum-analyzerMSForm")
+      && $("#btnSwitch-gotoStep4-ms-{{ TAB_INDEX_PLACEHOLDER }}").is(":visible")
+    ) {
+      var isBtnStep4OK = true;
+      $.each($(self.prefix+".add1spectrum-analyzerMSForm"), (id, elem) => {
+        if (self.is_error($(elem)) && $(elem).is(":visible"))
+          isBtnStep4OK = false;
+      });
+      if (isBtnStep4OK) {
+        self.rm_class($("#btnSwitch-gotoStep4-ms-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-disabled") ;
+        self.set_class($("#btnSwitch-gotoStep4-ms-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-primary") ;
+        $("#btnSwitch-gotoStep4-ms-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", false);
+      } else {
+        self.set_class($("#btnSwitch-gotoStep4-ms-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-disabled") ;
+        self.rm_class($("#btnSwitch-gotoStep4-ms-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-primary") ;
+        $("#btnSwitch-gotoStep4-ms-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", true);
+      }
+    }
+    // CHECK IF OK STEP 5 - PEAKS
+    if (
+      element.hasClass("add1spectrum-peaksMSForm-peaklist")
+      && $("#btnSwitch-gotoStep5-ms-{{ TAB_INDEX_PLACEHOLDER }}").is(":visible")
+    ) {
+      var isBtnStep5OK = true;
+      $.each($(self.prefix+".add1spectrum-peaksMSForm-peaklist"), (id, elem) => {
+        if (self.is_error($(elem)) && $(elem).is(":visible"))
+          isBtnStep5OK = false;
+      });
+      if (isBtnStep5OK && (self.is_ms() || self.a_ion_parent_is_selected())) {
+        self.rm_class($("#btnSwitch-gotoStep5-ms-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-disabled") ;
+        self.set_class($("#btnSwitch-gotoStep5-ms-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-primary") ;
+        $("#btnSwitch-gotoStep5-ms-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", false);
+      } else {
+        self.set_class($("#btnSwitch-gotoStep5-ms-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-disabled") ;
+        self.rm_class($("#btnSwitch-gotoStep5-ms-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-primary") ;
+        $("#btnSwitch-gotoStep5-ms-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", true);
+      }
+    }
+    // CHECK IF OK STEP 6 - OTHER DATA
+    if (
+      element.hasClass("add1spectrum-otherForm")
+      && $("#btnSwitch-gotoStep6-{{ TAB_INDEX_PLACEHOLDER }}").is(":visible")
+    ) {
+      self.checkIfEnableSubmit() ;
+    }
+  },
+
+  a_ion_parent_is_selected: function() {
+    return (this.get_selected_parent_ion_index() !== null) ;
+  },
+
+  get_selected_parent_ion_index: function() {
+    if (this.hot_MS_Peaks == undefined) {
+      return null ;
+    }
+    var data = this.hot_MS_Peaks.getData() ;
+    for (var i = 0 ; i < data.length ; i += 1) {
+      if (data[i][7] === "true" || data[i][7] === true) {
+        return i ;
+      }
+    }
+    return null ;
+  },
+
+  auto_set_spec_type: function() {
+    console.log("auto_set_spec_type...") ;
+    var self = this ;
+    var id = `#set_${self.DEFAULT_DATA["spectrum_type"]}_spectrum_button` ;
+    var element = $(self.prefix + id) ;
+    if (element.length) {
+      element.click() ;
+      console.log(`auto_set_spec_type to ${self.DEFAULT_DATA["spectrum_type"]} ok.`)
+    } else {
+      console.log(element)
+      console.log("Failed!")
+    }
+    if (self.DEFAULT_DATA["sample_type"]) {
+      self.auto_set_sample_type(self) ;
+    }
+  },
+
+  auto_set_sample_type: function() {
+    console.log("auto_set_sample_type...") ;
+    var self = this ;
+    var id = "#add1spectrum-sample-type-{{ TAB_INDEX_PLACEHOLDER }}" ;
+    var element = $(id) ;
+    if (element.length) {
+      element.val(self.DEFAULT_DATA["sample_type"]).change() ;
+      console.log(`auto_set_sample_type to ${self.DEFAULT_DATA["sample_type"]} ok.`) ;
+      if (self.DEFAULT_DATA["sample_type"] == "compound-ref") {
+        $("#add1spectrum-sample-mixSolvent-{{ TAB_INDEX_PLACEHOLDER }}").val(
+          "H2O/ethanol (75/25)"
+        ).change() ;
+      }
+    } else {
+      console.log(element)
+      console.log("Failed!")
+    }
+    if (self.DEFAULT_DATA["inchikey"]) {
+      setTimeout(() => self.auto_set_inchikey(self), 1000) ;
+    }
+  },
+
+  auto_set_inchikey: function() {
+    console.log("auto_set_inchikey...") ;
+    var self = this ;
+    var id ;
+    var element ;
+    if (self.is_ref()) {
+      id = "#add1spectrum-sample-inchikey-{{ TAB_INDEX_PLACEHOLDER }}:text" ;
+      element = $(id) ;
+      if (element.length) {
+        console.log(`auto_set_inchikey to ${self.DEFAULT_DATA["inchikey"]} ok.`)
+        element.val(self.DEFAULT_DATA["inchikey"]).change() ;
+        console.log(`Inchikey set!`) ;
+      } else {
+        console.log(element)
+        console.log("Failed!")
+      }
+    } else {
+      for (var index = 0 ; index < all_contexts.length ; index += 1) {
+        var inchikey = all_contexts[index].DEFAULT_DATA["inchikey"] ;
+        id = `#container_RCC_ADDED-1 tbody:nth(0) tr:nth(${index}) td:nth(1)` ;
+        element = $(id) ;
+        if (element.length) {
+          console.log(`auto_set_inchikey to ${inchikey} ok.`)
+          self.hot_RCC_ADDED.setDataAtCell(index, 1, inchikey) ;
+          element.innerHTML = inchikey ;
+          element.change() ;
+          console.log(element) ;
+          console.log(`Inchikey set!`) ;
+        } else {
+          console.log(element)
+          console.log("Failed!")
+        }
+      }
+      console.log(self.hot_RCC_ADDED)
+      self.hot_RCC_ADDED.render() ;
+    }
+    $("#btnSwitch-gotoStep2-{{ TAB_INDEX_PLACEHOLDER }}").click() ;
+    if (self.DEFAULT_DATA["method"]) {
+      setTimeout(() => self.auto_set_method(self), 1000) ;
+    }
+  },
+
+  auto_set_method: function() {
+    console.log("auto_set_method...") ;
+    var self = this ;
+    var id = "#add1spectrum-chromatoLC-method-{{ TAB_INDEX_PLACEHOLDER }}" ;
+    var element = $(id) ;
+    element.length && element.val(self.DEFAULT_DATA["method"]).change() ;
+    $("#btnSwitch-gotoStep3-lc-{{ TAB_INDEX_PLACEHOLDER }}").click() ;
+    setTimeout(() => {
+      (() => {
+        $("#btnSwitch-gotoStep4-ms-{{ TAB_INDEX_PLACEHOLDER }}").click() ;
+        console.log(`auto_set_method to ${self.DEFAULT_DATA["method"]} ok.`)
+        if (self.DEFAULT_DATA["scan_type"]) {
+          setTimeout(() => self.auto_set_scan_type(self), 1000) ;
+        }
+      })(self)
+    }, 1000) ;
+  },
+
+  auto_set_scan_type: function() {
+    console.log("auto_set_scan_type...") ;
+    var self = this ;
+    var id = "#add1spectrum-peaksMS-msLevel-{{ TAB_INDEX_PLACEHOLDER }}" ;
+    var element = $(id) ;
+    if (element.length) {
+      element.val(self.DEFAULT_DATA["scan_type"]).change() ;
+      console.log(`auto_set_scan_type to ${self.DEFAULT_DATA["scan_type"]} ok.`)
+    } else {
+      console.log("Failed!")
+    }
+    if (self.DEFAULT_DATA["polarity"]) {
+      self.auto_set_polarity(self) ;
+    }
+  },
+
+  auto_set_polarity: function() {
+    console.log("auto_set_polarity...") ;
+    var self = this ;
+    var id = "#add1spectrum-peaksMS-polarity-{{ TAB_INDEX_PLACEHOLDER }}" ;
+    var element = $(id) ;
+    if (element.length) {
+      element.val(self.DEFAULT_DATA["polarity"]).change() ;
+      console.log(`auto_set_polarity to ${self.DEFAULT_DATA["polarity"]} ok.`)
+    } else {
+      console.log("Failed!")
+    }
+    if (self.DEFAULT_DATA["resolution"]) {
+      self.auto_set_resolution(self) ;
+    }
+  },
+
+  auto_set_resolution: function() {
+    console.log("auto_set_resolution...") ;
+    var self = this ;
+    var id = "#add1spectrum-peaksMS-resolution-{{ TAB_INDEX_PLACEHOLDER }}" ;
+    var element = $(id) ;
+    if (element.length) {
+      element.val(self.DEFAULT_DATA["resolution"]).change() ;
+      console.log(`auto_set_resolution to ${self.DEFAULT_DATA["resolution"]} ok.`)
+    } else {
+      console.log("Failed!")
+    }
+    if (self.DEFAULT_DATA["next"] || true) {
+      setTimeout(() => self.finish_initialized(self), 1000) ;
+    }
+  },
+
+  finish_initialized: function() {
+    var self = this ;
+    if (ctx() === self) {
+      self.initialized = true ;
+    }
+  },
+
+  //USED IN HTML
+  updateLCMSspectraViewer: function() {
+    var self = this ;
+    var tab_no ;
+    console.log(`Updating spectral view for tab {{ TAB_INDEX_PLACEHOLDER }}`) ;
+    // reset current viewer
+    $("#containter-lcms-spectrum-preview-{{ TAB_INDEX_PLACEHOLDER }}").empty();
+    // reset data.
+
+    if (self.is_mix()) {
+      tab_no = 1 ;
+    } else {
+      tab_no = {{ TAB_INDEX_PLACEHOLDER }} ;
+    }
+    var range_from = $(`#add1spectrum-peaksMS-rangeFrom-${tab_no}`) ;
+    if ((spectrumMinPPM = Number(range_from.val())) == "") {
+      spectrumMinPPM = 10000;
+    }
+    var range_to = $(`#add1spectrum-peaksMS-rangeTo-${tab_no}`) ;
+    if ((spectrumMaxPPM = Number(range_to.val())) == "") {
+      spectrumMaxPPM = 0;
+    }
+    maxGraph = 0;
+    var localData = [];
+    var localDataAnnot = [];
+    // gather new data
+    // TODO switch tab in function of technic
+    $.each(self.hot_MS_Peaks.getData(), function() {
+      if(this[0] != undefined && this[0] != "") {
+        var x = (Number(this[0]));
+        var y = Number(this[2]);
+        var a = this[6];
+        localData.push([(x-0.000001),-150]);
+        localData.push([(x+0.000001),-150]);
+        localData.push([x,y]);
+        localDataAnnot[x] = a;
+        if (x < spectrumMinPPM) {
+          spectrumMinPPM = x;
+        }
+        if (x > spectrumMaxPPM) {
+          spectrumMaxPPM = x;
+        }
+        if (y > maxGraph) {
+          maxGraph = y;
+        }
+      }
+    });
+    
+    // build new one
+    spectrumMinPPM = spectrumMinPPM - (0.1 * spectrumMinPPM);
+    spectrumMaxPPM = spectrumMaxPPM + (0.1 * spectrumMaxPPM);
+    maxGraph = maxGraph + (0.1 * maxGraph);
+    localData.sort();
+    console.log(localData)
+    // build graph
+    $("#containter-lcms-spectrum-preview-{{ TAB_INDEX_PLACEHOLDER }}").highcharts({
+      chart: {
+        zoomType: "x",
+        spacingRight: 10,
+        spacingLeft: 10,
+        type: "scatter"
+      },
+      title: {
+        text: "Spectrum Preview",
+        useHTML: true
+      },
+      subtitle: {
+        text: (
+          document.ontouchstart === undefined
+          ? "Select area"
+          : "Pinch the chart to zoom in"
+        )
+      },
+      xAxis: {
+        type: "number",
+        title: {text: "m/z"},
+        min: spectrumMinPPM,
+        max: spectrumMaxPPM,
+        labels: {
+          formatter: function() {
+            return (Math.abs(this.value) + "");
+          }
+        }
+      },
+      yAxis: {
+        title: {text: "Relative Intensity (%)"},
+        min: 0,
+        max: maxGraph
+      },
+      tooltip: {
+        crosshairs: true,
+        formatter: function() {
+          var compo = "";
+          return (
+            "<b>" + this.series.name
+            + "</b><br/>m/z:" + Math.abs(this.x) + ""
+            + ";<br/>Relative Intensity: " + this.y
+            + "%;<br/>Annotation: " + localDataAnnot[ this.x]
+            + ""
+          );
+        }
+      },
+      legend: {enabled: false},
+      plotOptions: {scatter: {}},
+      series: [{
+        name: "preview",
+        showInLegend: true,
+        color: "#f00",
+        lineColor: "#f00",
+        pointInterval: 10,
+        pointStart: 100,
+        lineWidth : 2,
+        marker: {
+          enabled: true,
+          radius: 2,
+          lineColor: "#f00"
+        },
+        data: localData,
+        zIndex: 10
+      }]
+    });
+  },
+
+  //USED HERE
+  fulfillLCdata: function(jsonFileName) {
+    var self = this ;
+    var url = `{{ PF_URL_PLACEHOLDER }}/webapp/resources/json/lc-methods/${jsonFileName}.json` ;
+    $.getJSON(url, function(json) {
+      // $.POST
+      console.log(json);
+      // lc chromato
+      if (json.lc_chromatography != null) {
+        [
+          ["colConstructor", "column_constructor"],
+          ["colConstructorOther", "column_constructor_other"],
+          ["colName", "column_name"],
+          ["colLength", "column_length"],
+          ["colDiameter", "column_diameter"],
+          ["colParticuleSize", "particule_size"],
+          ["colTemperature", "column_temperature"],
+          ["separationFlowRate", "separation_flow_rate"],
+          ["separationSolvA", "separation_solvent_a"],
+          ["separationSolvApH", "ph_solvent_a"],
+          ["separationSolvB", "separation_solvent_b"],
+          ["separationSolvBpH", "ph_solvent_b"]
+        ].forEach((arr) => {
+          var el ;
+          console.log(arr)
+          el = $(`#add1spectrum-chromatoLC-${arr[0]}-{{ TAB_INDEX_PLACEHOLDER }}`)
+          el.val(json.lc_chromatography[arr[1]]) ;
+          el.change()
+        })
+        try {
+          $("#add1spectrum-chromatoLC-LCMode-{{ TAB_INDEX_PLACEHOLDER }}").val(json.lc_chromatography.LC_mode);
+          $("#add1spectrum-chromatoLC-LCMode-{{ TAB_INDEX_PLACEHOLDER }}").change();
+        } catch (e) {}
+        var handsontableSeparationFlowRateData = [];
+        if (json.lc_chromatography.separation_flow_gradient != null) {
+          $.each(json.lc_chromatography.separation_flow_gradient, function() {
+            var e = [
+              "" + this.time,
+              "" + this.solvA,
+              "" + this.solvB
+            ];
+            handsontableSeparationFlowRateData.push(e);
+          });
+        } else {
+          handsontableSeparationFlowRateData = null;
+        }
+        self.handsontableSeparationFlowRate(handsontableSeparationFlowRateData);
+      }
+      // ms_analyzer
+      if (json.ms_analyzer != null) {
+        $("#add1spectrum-analyzer-ms-instrument-{{ TAB_INDEX_PLACEHOLDER }}").val(json.ms_analyzer.instrument);
+        $("#add1spectrum-analyzer-ms-instrument-{{ TAB_INDEX_PLACEHOLDER }}").change();
+        $("#add1spectrum-analyzer-ms-model-{{ TAB_INDEX_PLACEHOLDER }}").val(json.ms_analyzer.model);
+        $("#add1spectrum-analyzer-ms-model-{{ TAB_INDEX_PLACEHOLDER }}").change();
+        $("#add1spectrum-analyzer-ms-resolutionFWHM-{{ TAB_INDEX_PLACEHOLDER }}").val(json.ms_analyzer.resolution_FWHM);
+        $("#add1spectrum-analyzer-ms-resolutionFWHM-{{ TAB_INDEX_PLACEHOLDER }}").change();
+        $("#add1spectrum-analyzer-ms-ionAnalyzerType-{{ TAB_INDEX_PLACEHOLDER }}").val(json.ms_analyzer.ion_analyzer_type);
+        $("#add1spectrum-analyzer-ms-ionAnalyzerType-{{ TAB_INDEX_PLACEHOLDER }}").change();
+      }
+      if (json.molecule_ionization != null) {
+        ["pos", "neg"].forEach((mode) => {
+          if (json.molecule_ionization[`mode_${mode}`] != null) {
+            [
+              ["ionizationMethod", "ionisation_method"],
+              ["sprayGazFlow", "spray_gaz_flow"],
+              ["vaporizerGazFlow", "vaporizer_gaz_flow"],
+              ["vaporizerTemperature", "vaporizer_temperature"],
+              ["sourceGazFlow", "source_gaz_flow"],
+              ["ionTransferTubeTemperatureOrTransferCapillaryTemperature", "transfer_tube_or_capillary_temperature"],
+              ["highVoltageOrCoronaVoltage", "voltage"],
+            ].forEach((tuple) => {
+              var el ;
+              var id = tuple[0] ;
+              var key = tuple[1] ;
+              el = $(`#add1spectrum-analyzserMS-${id}-${mode}-{{ TAB_INDEX_PLACEHOLDER }}`) ;
+              console.log(el)
+              el.val(json.molecule_ionization[`mode_${mode}`][key]);
+              el.change() ;
+            })
+          }
+        })
+      }
+
+      if (json.molecule_beamOrTrap != null) {
+        if (json.molecule_beamOrTrap.ion_beam != null) {
+          $("#add1spectrum-ionTrapBeam-type-{{ TAB_INDEX_PLACEHOLDER }}").val("beam").change();
+          $("#add1spectrum-ionTrapBeam-ionGas-{{ TAB_INDEX_PLACEHOLDER }}").val(json.molecule_beamOrTrap.ion_beam.ion_gas).change();
+          $("#add1spectrum-ionTrapBeam-ionGasPressureValue-{{ TAB_INDEX_PLACEHOLDER }}").val(json.molecule_beamOrTrap.ion_beam.ion_pressure_value).change();
+          $("#add1spectrum-ionTrapBeam-ionGasPressureUnit-{{ TAB_INDEX_PLACEHOLDER }}").val(json.molecule_beamOrTrap.ion_beam.ion_pressure_unit).change();
+        }
+        if (json.molecule_beamOrTrap.ion_trap != null) {
+          $("#add1spectrum-ionTrapBeam-type-{{ TAB_INDEX_PLACEHOLDER }}").val("trap").change();
+          $("#add1spectrum-ionTrapBeam-ionGas-{{ TAB_INDEX_PLACEHOLDER }}").val(json.molecule_beamOrTrap.ion_trap.ion_gas).change();
+          $("#add1spectrum-ionTrapBeam-ionGasPressureValue-{{ TAB_INDEX_PLACEHOLDER }}").val(json.molecule_beamOrTrap.ion_trap.ion_pressure_value).change();
+          $("#add1spectrum-ionTrapBeam-ionGasPressureUnit-{{ TAB_INDEX_PLACEHOLDER }}").val(json.molecule_beamOrTrap.ion_trap.ion_pressure_unit).change();
+          $("#add1spectrum-ionTrapBeam-ionNumber-{{ TAB_INDEX_PLACEHOLDER }}").val(json.molecule_beamOrTrap.ion_trap.ion_number).change();
+          $("#add1spectrum-ionTrapBeam-ionFrequencyShift-{{ TAB_INDEX_PLACEHOLDER }}").val(json.molecule_beamOrTrap.ion_trap.ion_freq_shift).change();
+        }
+      }
+      // other
+      if (json.other != null) {
+        $("#add1spectrum-other-author-{{ TAB_INDEX_PLACEHOLDER }}").val(json.other.data_authors);
+        $("#add1spectrum-other-author-{{ TAB_INDEX_PLACEHOLDER }}").change();
+        $("#add1spectrum-other-validator-{{ TAB_INDEX_PLACEHOLDER }}").val(json.other.data_validator);
+        $("#add1spectrum-other-validator-{{ TAB_INDEX_PLACEHOLDER }}").change();
+        $("#add1spectrum-other-date-{{ TAB_INDEX_PLACEHOLDER }}").val(json.other.acquisition_date);
+        $("#add1spectrum-other-date-{{ TAB_INDEX_PLACEHOLDER }}").change();
+        $("#add1spectrum-other-owner-{{ TAB_INDEX_PLACEHOLDER }}").val(json.other.data_ownership);
+        $("#add1spectrum-other-owner-{{ TAB_INDEX_PLACEHOLDER }}").change();
+      }
+    }).error(function(event, jqxhr, exception) {
+      if (event.status == 404) {
+        $("#alertBoxSelectTemplate-{{ TAB_INDEX_PLACEHOLDER }}").html(`
+          <div class="alert alert-danger alert-dismissible" role="alert">
+            <button type="button" class="close" data-dismiss="alert">
+              <span aria-hidden="true">&times;</span>
+              <span class="sr-only"><spring:message code="alert.close" text="Close" /></span>
+            </button>
+            <strong>
+              <spring:message code="alert.strong.error" text="Error!" />
+            </strong> unable to load pre-filled data!
+          </div>
+        `);
+      }
+    });
+  },
+
+  //USED HERE
+  resetFromColors: function() {
+
+    var self = this ;
+    $.each($(self.prefix+".add1spectrum"), function(id, elem) {
+      element = $(elem)
+      self.rm_success(element) ;
+      self.rm_warning(element) ;
+      self.rm_error(element) ;
+      if ($(elem).hasClass("is-mandatory") &&  ($(elem).val() == "" || $(elem).val() == null)) {
+        self.set_error($(elem)) ;
+      }
+      if ($(elem).hasClass("is-optional") &&  ($(elem).val() == "" || $(elem).val() == null)) {
+        self.set_warning($(elem)) ;
+      }
+      if ($(elem).val() != "" && $(elem).val() != null) {
+        self.set_success($(elem)) ;
+      }
+    });
+    $.each($(self.prefix+"button.switchStep"), function(id, elem) {
+      if (!$(this).hasClass("btn-disabled")) {
+        $(this).addClass("btn-disabled");
+      }
+      if ($(this).hasClass("btn-primary")) {
+        $(this).removeClass("btn-primary");
+      }
+      $(this).prop("disabled", true);
+    });
+
+    // peak list: no data to check
+    $("#btnSwitch-gotoStep5-ms-{{ TAB_INDEX_PLACEHOLDER }}").removeClass("btn-disabled");
+    $("#btnSwitch-gotoStep5-ms-{{ TAB_INDEX_PLACEHOLDER }}").addClass("btn-primary");
+    $("#btnSwitch-gotoStep5-ms-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", false);
+
+    $("#add1spectrum-other-author-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", false);
+    $("#add1spectrum-other-validator-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", false);
+    $("#add1spectrum-other-date-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", false);
+    $("#add1spectrum-other-owner-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", false);
+    $("#add1spectrum-other-fileName-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", false);
+    $("#add1spectrum-other-fileSize-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", false);
+  },
+
+  //USED IN HTML
+  switchToStep: function(step) {
+
+    var self = this ;
+    switch(step) {
+      case 2:
+        // hide after step 2 / alt step 2
+        $("#add1spectrum-chromatographyData-LC-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+        $("#add1spectrum-chromatographyData-GC-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+        $("#add1spectrum-analyserData-MS-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+        $("#add1spectrum-peaksData-MS-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+        $("#add1spectrum-otherData-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+        if ($("#step1sign-{{ TAB_INDEX_PLACEHOLDER }}").hasClass("fa-question-circle")) {
+          $("#step1sign-{{ TAB_INDEX_PLACEHOLDER }}").removeClass("fa-question-circle").addClass("fa-check-circle");
+        }
+        if (self.is_ms()) {
+          $("#add1spectrum-chromatoLC-method-{{ TAB_INDEX_PLACEHOLDER }}").empty();
+          $("#add1spectrum-chromatoLC-method-{{ TAB_INDEX_PLACEHOLDER }}").append(
+            '<option value="" selected="selected" disabled="disabled">choose in list&hellip;</option>'
+          );
+          $.getJSON("{{ PF_URL_PLACEHOLDER }}/webapp/resources/json/list-lc-methods.json", function(data) {
+            // load data from json
+            $.each(data.methods,function() {
+              if (this.name !== undefined) {
+                if (this.value !== undefined) {
+                  $("#add1spectrum-chromatoLC-method-{{ TAB_INDEX_PLACEHOLDER }}").append(
+                    `<option value="${this.value}">${this.name}</option>`
+                  );
+                } else {
+                  $("#add1spectrum-chromatoLC-method-{{ TAB_INDEX_PLACEHOLDER }}").append(
+                    `<option disabled>${this.name}</option>`
+                  );
+                }
+              }
+            });
+          });
+        } else if (self.is_ms2()) {
+          $("#add1spectrum-chromatoLC-method-{{ TAB_INDEX_PLACEHOLDER }}").empty();
+          $("#add1spectrum-chromatoLC-method-{{ TAB_INDEX_PLACEHOLDER }}").append(
+            '<option value="" selected="selected" disabled="disabled">choose in list&hellip;</option>'
+          );
+          $.getJSON(
+            "{{ PF_URL_PLACEHOLDER }}/webapp/resources/json/list-lc-msms-methods.json",
+            function(data) {
+              // load data from json
+              $.each(data.methods, function() {
+                if (this.name !== undefined) {
+                  if (this.value !== undefined) {
+                    $("#add1spectrum-chromatoLC-method-{{ TAB_INDEX_PLACEHOLDER }}").append(
+                      `<option value="${this.value}">${this.name}</option>`
+                    );
+                  } else {
+                    $("#add1spectrum-chromatoLC-method-{{ TAB_INDEX_PLACEHOLDER }}").append(
+                      `<option disabled>${this.name}</option>`
+                    );
+                  }
+                }
+              });
+            }
+          );
+        }
+        // check panel to show
+        if (self.isLC) {
+          $("#add1spectrum-chromatographyData-LC-{{ TAB_INDEX_PLACEHOLDER }}").show();
+          $("#linkActivateStep2-lc-{{ TAB_INDEX_PLACEHOLDER }}").trigger("click");
+          var sign = $("#step2-lc-sign-{{ TAB_INDEX_PLACEHOLDER }}") ;
+          self.rm_class(sign, "fa-check-circle") && self.set_class(sign, "fa-question-circle") ;
+          if (!self.isSeparationFlowRateInit) {
+            self.handsontableSeparationFlowRate(null);
+            self.isSeparationFlowRateInit = true;
+          }
+        } else if (self.isGC) {
+          $("#add1spectrum-chromatographyData-GC-{{ TAB_INDEX_PLACEHOLDER }}").show();
+          $("#linkActivateStep2-gc-{{ TAB_INDEX_PLACEHOLDER }}").trigger("click");
+          var sign = $("#step2-gc-sign-{{ TAB_INDEX_PLACEHOLDER }}") ;
+          sign.removeClass("fa-check-circle").addClass("fa-question-circle");
+          if ($("#step2-gc-sign-{{ TAB_INDEX_PLACEHOLDER }}").hasClass("fa-check-circle")) {
+          }
+        } else if (self.isIC) {
+          $("#add1spectrum-chromatographyData-IC-{{ TAB_INDEX_PLACEHOLDER }}").show();
+          $("#linkActivateStep2-ic-{{ TAB_INDEX_PLACEHOLDER }}").trigger("click");
+          if ($("#step2-ic-sign-{{ TAB_INDEX_PLACEHOLDER }}").hasClass("fa-check-circle")) {
+            $("#step2-ic-sign-{{ TAB_INDEX_PLACEHOLDER }}").removeClass("fa-check-circle").addClass("fa-question-circle");
+          }
+        }
+        break;
+      case 3:
+        // hide after step 3 / alt step 3
+        $("#add1spectrum-analyserData-MS-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+        $("#add1spectrum-peaksData-MS-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+        $("#add1spectrum-otherData-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+        // step 2 ok 
+        if ($("#step2-lc-sign-{{ TAB_INDEX_PLACEHOLDER }}").hasClass("fa-question-circle")) {
+          $("#step2-lc-sign-{{ TAB_INDEX_PLACEHOLDER }}").removeClass("fa-question-circle").addClass("fa-check-circle");
+        }
+        if ($("#step2-gc-sign-{{ TAB_INDEX_PLACEHOLDER }}").hasClass("fa-question-circle")) {
+          $("#step2-gc-sign-{{ TAB_INDEX_PLACEHOLDER }}").removeClass("fa-question-circle").addClass("fa-check-circle");
+        }
+        // check panel to show
+        if (self.is_ms() || self.is_ms2()) {
+          $("#add1spectrum-analyserData-MS-{{ TAB_INDEX_PLACEHOLDER }}").show();
+          $("#linkActivateStep3-ms-{{ TAB_INDEX_PLACEHOLDER }}").trigger('click');
+          // debug display
+          [
+            "add1spectrum-analyzserMS-sprayGazFlow-pos",
+            "add1spectrum-analyzserMS-sprayGazFlow-neg",
+            "add1spectrum-analyzserMS-vaporizerGazFlow-pos",
+            "add1spectrum-analyzserMS-vaporizerGazFlow-neg",
+            "add1spectrum-analyzserMS-vaporizerTemperature-pos",
+            "add1spectrum-analyzserMS-vaporizerTemperature-neg",
+            "add1spectrum-analyzserMS-sourceGazFlow-pos",
+            "add1spectrum-analyzserMS-sourceGazFlow-neg",
+            "add1spectrum-analyzserMS-ionTransferTubeTemperatureOrTransferCapillaryTemperature-pos",
+            "add1spectrum-analyzserMS-ionTransferTubeTemperatureOrTransferCapillaryTemperature-neg",
+            "add1spectrum-analyzserMS-highVoltageOrCoronaVoltage-pos",
+            "add1spectrum-analyzserMS-highVoltageOrCoronaVoltage-neg"
+          ].forEach((id) => {
+            var element = $(`#id-{{ TAB_INDEX_PLACEHOLDER }}`)
+            element.height(element.parent().children("span").height())
+          })
+          if ($("#step3-ms-sign-{{ TAB_INDEX_PLACEHOLDER }}").hasClass("fa-check-circle")) {
+            $("#step3-ms-sign-{{ TAB_INDEX_PLACEHOLDER }}").removeClass("fa-check-circle").addClass("fa-question-circle");
+          }
+          // MSMS only
+          $(self.prefix+".enable-if-ms").attr("disabled", !self.is_ms());
+          $(self.prefix+".enable-if-msms").attr("disabled", !self.is_ms2());
+        }
+        // avoid display bug
+        $("#add1spectrum-analyzer-ms-instrument-{{ TAB_INDEX_PLACEHOLDER }}").change();
+        break;
+      case 4:
+        // hide after step 4 / alt step 4
+        $("#add1spectrum-peaksData-MS-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+        $("#add1spectrum-otherData-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+        // step 3 ok 
+        if ($("#step3-ms-sign-{{ TAB_INDEX_PLACEHOLDER }}").hasClass("fa-question-circle")) {
+          $("#step3-ms-sign-{{ TAB_INDEX_PLACEHOLDER }}").removeClass("fa-question-circle").addClass("fa-check-circle");
+        }
+        if (self.is_ms() || self.is_ms2()) {
+          $("#add1spectrum-peaksData-MS-{{ TAB_INDEX_PLACEHOLDER }}").show();
+          $("#linkActivateStep4-ms-{{ TAB_INDEX_PLACEHOLDER }}").trigger("click");
+          if ($("#step4-ms-sign-{{ TAB_INDEX_PLACEHOLDER }}").hasClass("fa-check-circle")) {
+            $("#step4-ms-sign-{{ TAB_INDEX_PLACEHOLDER }}").removeClass("fa-check-circle").addClass("fa-question-circle");
+          }
+          // LC MS
+          if (!self.isMSpeaksInit) {
+            console.log(`self.is_mix(): ${self.is_mix()}`)
+            if (self.is_mix()) {
+              all_contexts.forEach((context, index) => {
+                context.handsontableMSpeaks(null) ;
+                context.updateLCMSspectraViewer() ;
+                context.isMSpeaksInit = true;
+                $(context.prefix+".add1spectrum-peaksMSForm-peaklist-reset").val("").change();
+              })
+            } else {
+              self.handsontableMSpeaks(null);
+              self.isMSpeaksInit = true;
+              $(self.prefix+".add1spectrum-peaksMSForm-peaklist-reset").val("").change();
+            }
+          }
+        }
+        // show ms tab
+        setTimeout(() => $("#container_MS_Peaks-{{ TAB_INDEX_PLACEHOLDER }}").trigger("click"), 250);
+        $("#add1spectrum-peaksMS-msLevel-{{ TAB_INDEX_PLACEHOLDER }}").change();
+        break;
+      case 5:
+        // hide after step 5 / alt step 5
+        $("#add1spectrum-otherData-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+        // step 4 ok 
+        if ($("#step4-ms-sign-{{ TAB_INDEX_PLACEHOLDER }}").hasClass("fa-question-circle")) {
+          $("#step4-ms-sign-{{ TAB_INDEX_PLACEHOLDER }}").removeClass("fa-question-circle").addClass("fa-check-circle");
+        }
+        if ($("#step5sign-{{ TAB_INDEX_PLACEHOLDER }}").hasClass("fa-check-circle")) {
+          $("#step5sign-{{ TAB_INDEX_PLACEHOLDER }}").removeClass("fa-check-circle").addClass("fa-question-circle");
+        }
+        $("#add1spectrum-otherData-{{ TAB_INDEX_PLACEHOLDER }}").show();
+        $("#linkActivateStep5-{{ TAB_INDEX_PLACEHOLDER }}").trigger("click");
+        // reset step 6 button
+        self.checkIfEnableSubmit();
+        $("#import1SpectrumLoadingBare-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+        $("#import1SpectrumResults-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+        break;
+      case 6:
+        self.postOneSpectrumFrom();
+        self.cptPeakListTab++;
+        break;
+      case 7:
+      self.dumpOneSpectrumFrom();
+      break;
+    }
+  },
+
+  //USED HERE
+  resetElemColor: function(idElem) {
+
+    var self = this ;
+    if ($(`#${idElem}`).parent().hasClass("has-success")) {
+      $(`#${idElem}`).parent().removeClass("has-success") ;
+    }
+    if ($(`#${idElem}`).parent().hasClass("has-warning")) {
+      $(`#${idElem}`).parent().removeClass("has-warning") ;
+    }
+    if ($(`#${idElem}`).parent().hasClass("has-error")) {
+      $(`#${idElem}`).parent().removeClass("has-error") ;
+    }
+  },
+
+  //USED HERE
+  disableElem: function(idElem) {
+
+    var self = this ;
+    $(`#${idElem}`).prop("disabled", true) ;
+    $(`#${idElem}`).val("") ;
+    if ($(`#${idElem}`).hasClass("is-mandatory")) {
+      $(`#${idElem}`).removeClass("is-mandatory") ;
+    }
+  },
+
+  //USED HERE
+  enableElem: function(idElem) {
+
+    var self = this ;
+    $(`#${idElem}`).prop("disabled", false) ;
+    $(`#${idElem}`).parent().addClass("has-error") ;
+    if (!$(`#${idElem}`).hasClass("is-mandatory")) {
+      $(`#${idElem}`).addClass("is-mandatory") ;
+    }
+  },
+
+  //USED HERE
+  checkIfEnableSubmit: function() {
+
+    var self = this ;
+    var isBtnStep6OK = true;
+    $.each($(self.prefix+".add1spectrum-otherForm"), (id, elem) => {
+      if (self.is_error($(elem)) && $(elem).is(":visible")) {
+        isBtnStep6OK = false;
+      }
+    });
+    if (isBtnStep6OK) {
+      self.rm_class($("#btnSwitch-gotoStep6-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-disabled") ;
+      self.set_class($("#btnSwitch-gotoStep6-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-primary") ;
+      $("#btnSwitch-gotoStep6-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", false);
+      self.rm_class($("#btnSwitch-gotoStep7-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-disabled") ;
+      self.set_class($("#btnSwitch-gotoStep7-{{ TAB_INDEX_PLACEHOLDER }}"), "btn-primary") ;
+      $("#btnSwitch-gotoStep7-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", false);
+    } else {
+      if (!$("#btnSwitch-gotoStep6-{{ TAB_INDEX_PLACEHOLDER }}").hasClass("btn-disabled")) {
+        $("#btnSwitch-gotoStep6-{{ TAB_INDEX_PLACEHOLDER }}").addClass("btn-disabled");
+      }
+      if ($("#btnSwitch-gotoStep6-{{ TAB_INDEX_PLACEHOLDER }}").hasClass("btn-primary")) {
+        $("#btnSwitch-gotoStep6-{{ TAB_INDEX_PLACEHOLDER }}").removeClass("btn-primary");
+      }
+      $("#btnSwitch-gotoStep6-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", true);
+      if (!$("#btnSwitch-gotoStep7-{{ TAB_INDEX_PLACEHOLDER }}").hasClass("btn-disabled")) {
+        $("#btnSwitch-gotoStep7-{{ TAB_INDEX_PLACEHOLDER }}").addClass("btn-disabled");
+      }
+      if ($("#btnSwitch-gotoStep7-{{ TAB_INDEX_PLACEHOLDER }}").hasClass("btn-primary")) {
+        $("#btnSwitch-gotoStep7-{{ TAB_INDEX_PLACEHOLDER }}").removeClass("btn-primary");
+      }
+      $("#btnSwitch-gotoStep7-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", true);
+    }
+  },
+
+  //USED IN HTML
+  addOneSpectrum: function(type) {
+
+    var self = this ;
+     // unlock
+    $(self.prefix+".add1spectrum-sampleForm").prop("disabled", false);
+    $(self.prefix+".add1spectrum-chromatoLCForm").prop("disabled", false);
+    $(self.prefix+".add1spectrum-analyzerMSForm").prop("disabled", false);
+    $(self.prefix+".add1spectrum-otherForm").prop("disabled", false);
+    $("#add1spectrum-chromatoLC-colConstructor-{{ TAB_INDEX_PLACEHOLDER }}").change();
+    // reset
+    self.isLC = false;
+    self.isGC = false;
+    self.isIC = false;
+    $("#alertBoxSubmitSpectrum-{{ TAB_INDEX_PLACEHOLDER }}").html("");
+    // hide in all steps
+    $(self.prefix+".opt-ms").hide();
+    $(self.prefix+".opt-msms").hide();
+    // hide step 2
+    $("#add1spectrum-chromatographyData-LC-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+    $("#add1spectrum-chromatographyData-GC-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+    // hide step 3
+    $("#add1spectrum-analyserData-MS-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+    $(self.prefix+".add1spectrum-ionTrap").hide();
+    // hide step 4
+    $("#add1spectrum-peaksData-MS-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+    // hide step 5
+    $("#add1spectrum-otherData-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+    // reset field step 1
+    $("#add1spectrum-sample-type-{{ TAB_INDEX_PLACEHOLDER }}").val("");
+    $(self.prefix+".add1spectrum-sample-type-panel").hide();
+    $(self.prefix+".add1spectrum-sampleForm").val("");
+    $("#sample-bonus-display-{{ TAB_INDEX_PLACEHOLDER }}").html("");
+    // reset field step 2
+    $(self.prefix+".add1spectrum-chromatoLCForm").val("");
+    // reset field step 3
+    $(self.prefix+".add1spectrum-analyzerMSForm").val("");
+    // reset field step 4
+    // reset peak lists / all tabs
+    self.isSeparationFlowRateInit = false;
+    self.isMSpeaksInit = false;
+    $(self.prefix+".handsontable").html("");
+    // reset field step 5 => NO!
+    // set icon
+    $("#step0sign-{{ TAB_INDEX_PLACEHOLDER }}").removeClass("fa-question-circle").addClass("fa-check-circle");
+    if ($("#step1sign-{{ TAB_INDEX_PLACEHOLDER }}").hasClass("fa-check-circle")) {
+      $("#step1sign-{{ TAB_INDEX_PLACEHOLDER }}").removeClass("fa-check-circle").addClass("fa-question-circle");
+    }
+    // collapse step 0 / uncollaspe step 1
+    $("#linkActivateStep1-{{ TAB_INDEX_PLACEHOLDER }}").trigger("click");
+    // show step 1 content
+    $("#add1spectrum-sampleData-{{ TAB_INDEX_PLACEHOLDER }}").show();
+    switch(type) {
+    case 1:
+      // TODO GC-MS stuff
+      self.isGC = true;
+      break;
+    case 2:
+      // LC-MS stuff
+      self.isLC = true;
+      $(self.prefix+".opt-ms").show();
+      break;
+    case 5:
+      // LC-MSMS stuff
+      self.isLC = true;
+      $(self.prefix+".opt-ms").show();
+      $(self.prefix+".opt-msms").show();
+      break;
+    case 6:
+      // IC-MS stuff
+      self.isIC = true;
+      $(self.prefix+".opt-ms").show();
+      break;
+    case 7:
+      // IC-MSMS stuff
+      self.isIC = true;
+      $(self.prefix+".opt-ms").show();
+      $(self.prefix+".opt-msms").show();
+      break;
+    }
+    self.resetFromColors();
+    // reset json obj to submit form
+    self.jsonSpectrumType = null;
+    self.isJsonSpectrumTypeComplete = false;
+    self.jsonSample = null;
+    self.isJsonSampleComplete = false;
+    self.isJsonRCCaddedComplete = false;
+    self.jsonChromato = null;
+    self.isJsonChromatoComplete = false;
+    self.jsonAnalyzer = null;
+    self.isJsonAnalyzerComplete = false;
+    self.jsonPeaksList = [];
+    self.isJsonPeaksListComplete = false;
+    self.jsonOtherMetadata = null;
+    self.isJsonOtherMetadataComplete = false;
+    
+    self.cptPeakListTab = 0;
+    self.jsonAnalyzerAcquisition = [];
+    self.idMetadataMap = {}
+    self.listOfViewableSpectra = [];
+    
+    // spec MS
+    self.jsonMolIonization = null;
+    
+    // spec MSMS
+    self.jsonMolIonBeam = null;
+    
+    // try load cpd
+    if (self.inchikey !== null) {
+      self.loadJSCompound(self.inchikey);
+    }
+  },
+
+  //USED HERE
+  loadJSCompound: function(inchikey) {
+    var self = this ;
+    
+    $("#add1spectrum-sample-type-{{ TAB_INDEX_PLACEHOLDER }}").val("compound-ref");
+    $("#add1spectrum-sample-type-{{ TAB_INDEX_PLACEHOLDER }}").change();
+    $("#add1spectrum-sample-inchikey-{{ TAB_INDEX_PLACEHOLDER }}").val(inchikey);
+    $("#add1spectrum-sample-inchikey-{{ TAB_INDEX_PLACEHOLDER }}").change();
+    if (inchidata[inchikey] !== null) {
+      return (set_inchi_data(inchidata[inchikey]), {{ TAB_INDEX_PLACEHOLDER }}) ;
+    }
+    $.ajax({
+      type: "get",
+      url: "{{ PF_URL_PLACEHOLDER }}/webapp/get-cpd-data",
+      data: "inchikey="+inchikey,
+      dataType: "json",
+      success: function(data) {
+        if (data.success) {
+          set_inchi_data(data) ;
+        }
+      },
+      error : function(data) {
+      }
+    }).always(function() {
+    });
+  },
+
+  //USED HERE
+  handsontableSeparationFlowRate: function(data) {
+    var self = this ;
+
+    // reset
+    $("#container_LC_SFG-{{ TAB_INDEX_PLACEHOLDER }}").html("");
+    // init
+    var data_LC_SFG;
+    if (data==null) {
+      data_LC_SFG = [
+       [ "", "", "" ],
+       [ "", "", "" ],
+       [ "", "", "" ],
+       [ "", "", "" ],
+       [ "", "", "" ],
+       [ "", "", "" ],
+       [ "", "", "" ],
+       [ "", "", "" ],
+       [ "", "", "" ],
+     ];
+    } else {
+      data_LC_SFG = data;
+    }
+    
+    var container_LC_SFG = document.getElementById("container_LC_SFG-{{ TAB_INDEX_PLACEHOLDER }}");
+    self.hot_LC_SFG = new Handsontable(container_LC_SFG, {
+      data : data_LC_SFG,
+      minSpareRows : 1,
+      colHeaders : true,
+      colHeaders: ["time (min)", "solv. A (%)", "solv. B (%)"],
+      contextMenu : false
+    });
+    Handsontable.Dom.addEvent(document.body, "click", function(e) {
+      var element = e.target || e.srcElement;
+      if (element.nodeName == "BUTTON"&& element.name == "dump") {
+        var name = element.getAttribute("data-dump");
+        var instance = element.getAttribute("data-instance");
+        self.hot_LC_SFG = window[instance];
+        console.log("data of " + name, self.hot_LC_SFG.getData());
+      }
+    });
+    $("#container_LC_SFG table.htCore-{{ TAB_INDEX_PLACEHOLDER }}").css("width","100%");
+  },
+
+  //USED HERE
+  handsontableMSpeaks: function(data) {
+    console.log("handsontableMSpeaks")
+    console.log(data)
+    var self = this ;
+    var data_MS_Peaks ;
+    var container_MS_Peaks ;
+
+    if (data != null) {
+      data_MS_Peaks = data;
+    } else {
+      data_MS_Peaks = JSON.parse(JSON.stringify(self.DEFAULT_MS_PEAK_VALUES));
+    }
+    $("#container_MS_Peaks-{{ TAB_INDEX_PLACEHOLDER }}").html("");
+    container_MS_Peaks = document.getElementById(
+      "container_MS_Peaks-{{ TAB_INDEX_PLACEHOLDER }}"
+    );
+    var colHeaders = [
+      "m/z", "absolute intensity", "relative intensity (%)", "delta (ppm)",
+      "composition", "attribution", "Validated"
+    ]
+    var columns = [
+      {type: "numeric", format: "0.0000"},
+      {type: "numeric", format: "0.00"},
+      {type: "numeric", format: "0.00"},
+      {type: "numeric", format: "0.0000"},
+      {type: "text"},
+      {type: "text"},
+      {type: "checkbox"}
+    ] ;
+    if (self.is_ms2()) {
+      colHeaders.push("Parent ion") ;
+      columns.push({type: "checkbox"}) ;
+    }
+    self.hot_MS_Peaks = new Handsontable(container_MS_Peaks, {
+      data: data_MS_Peaks,
+      minSpareRows: 1,
+      colHeaders: true,
+      colHeaders: colHeaders,
+      contextMenu: false,
+      maxRows: data_MS_Peaks.length,
+      minRows: data_MS_Peaks.length,
+      columns: columns,
+      afterChange: function(changes) {
+        if (self.lock.precursor_ion) {
+          return ;
+        }
+        self.lock.precursor_ion = true ;
+        try {
+          changes?.forEach(([row, prop, old, value]) => {
+            var data ;
+            var precursor_ion ;
+
+            var data = self.hot_MS_Peaks.getData() ;
+            if (prop !== 7 || value !== true || data === undefined) {
+              return ;
+            }
+            for (var i = 0; i < data.length ; i += 1) {
+              data[i][7] = false ;
+            }
+            data[row][7] = true ;
+            precursor_ion = $(`#add1spectrum-peaksMS-msPrecursorIon-{{ TAB_INDEX_PLACEHOLDER }}`) ;
+            console.log(`setting preco ion to ${data[row][0]}`) ;
+            precursor_ion.val(data[row][0]) ;
+            precursor_ion.change() ;
+            self.hot_MS_Peaks.render() ;
+          })
+        } finally {
+          self.lock.precursor_ion = false ;
+        }
+      }
+    });
+    Handsontable.Dom.addEvent(document.body, "click", (e) => {
+      var element ;
+      var name ;
+      var instance ;
+
+      element = e.target || e.srcElement ;
+      if (element.nodeName == "BUTTON" && element.name == "dump") {
+        name = element.getAttribute("data-dump") ;
+        instance = element.getAttribute("data-instance") ;
+        self.hot_MS_Peaks = window[instance] ;
+        console.log("data of " + name, self.hot_MS_Peaks.getData()) ;
+      }
+    }) ;
+
+    $("#container_MS_Peaks-{{ TAB_INDEX_PLACEHOLDER }} table.htCore").css(
+      "width","100%"
+    ) ;
+    if (self.is_ms2()) {
+      setTimeout(() => self.select_precursor_ion(), 200) ;
+    }
+  },
+
+  change_tab: function() {
+    var self = this ;
+    setTimeout(() => self.auto_select_ion_parent(), 1000) ;
+  },
+
+  auto_select_ion_parent: function() {
+    var self = this ;
+    if (!self.a_ion_parent_is_selected()) {
+      setTimeout(() => self.select_precursor_ion(), 1000) ;
+    }
+  },
+
+  select_precursor_ion: function() {
+    var self = this ;
+    var data ;
+    var precuirsor_ion ;
+
+    if (self.hot_MS_Peaks == null) {
+      return setTimeout(() => self.select_precursor_ion(), 200) ;
+    }
+    console.log("Selecting ion preco for tab {{ TAB_INDEX_PLACEHOLDER }}")
+    data = self.hot_MS_Peaks.getData() ;
+    for (var i = 0 ; i < data.length ; i += 1) {
+      if (data[i][5] === "[M+H]+" || data[i][5] === "[M+H]-") {
+          precursor_ion = $("#add1spectrum-peaksMS-msPrecursorIon-{{ TAB_INDEX_PLACEHOLDER }}") ;
+          console.log(precursor_ion)
+          precursor_ion.val(data[i][0]) ;
+          precursor_ion.change() ;
+          return ;
+      }
+    }
+  },
+
+  sync_precursor_ion: function() {
+    var self = this ;
+    var data ;
+
+    if (self.lock.precursor_ion || self.hot_MS_Peaks == undefined) {
+      return ;
+    }
+    self.lock.precursor_ion = true ;
+    console.log("Syncing precursor ion")
+    var val = $("#add1spectrum-peaksMS-msPrecursorIon-{{ TAB_INDEX_PLACEHOLDER }}").val() ;
+    var data = self.hot_MS_Peaks.getData() ;
+    for (var i = 0; i < data.length ; i += 1) {
+      if (Math.abs(data[i][0] - val) < 0.0001) {
+        for (var j = 0; j < data.length ; j += 1) {
+          data[j][7] = false ;
+        }
+        data[i][7] = true ;
+        self.hot_MS_Peaks.render() ;
+        return ;
+      }
+    }
+    self.lock.precursor_ion = false ;
+  },
+
+  // USED HERE
+  handsontableRefChemCpdAdded: function(data) {
+    var self = this ;
+    // reset
+    $("#container_RCC_ADDED-{{ TAB_INDEX_PLACEHOLDER }}").html("");
+    $("#sample-bonus-display-{{ TAB_INDEX_PLACEHOLDER }}").html("");
+    // init
+    var data_RCC_ADDED, colHeaderData, container_RCC_ADDED;
+
+    colHeaderData = [
+      {data: "common name", type: "text"},
+      {data: "<b>InChIKey</b>", type: "text"},
+      {data: "composition", renderer: lightgrayRenderer},
+      {data: "<b>concentration (&micro;g/ml)</b>", type: "text"},
+      {data: "exact mass", renderer: lightgrayRenderer},
+      {data: "(M+H)+ or (M-H)-", renderer: lightgrayRenderer}
+    ] ;
+    
+    if (data == null) {
+      data_RCC_ADDED = [
+        [ "", "", "","", "", "" ],
+        [ "", "", "","", "", "" ],
+        [ "", "", "","", "", "" ],
+        [ "", "", "","", "", "" ],
+        [ "", "", "","", "", "" ],
+        [ "", "", "","", "", "" ],
+        [ "", "", "","", "", "" ],
+        [ "", "", "","", "", "" ],
+        [ "", "", "","", "", "" ],
+        [ "", "", "","", "", "" ],
+        [ "", "", "","", "", "" ],
+        [ "", "", "","", "", "" ],
+      ];
+    } else {
+      container_RCC_ADDED = data;
+    }
+
+    container_RCC_ADDED = document.getElementById("container_RCC_ADDED-{{ TAB_INDEX_PLACEHOLDER }}");
+    self.hot_RCC_ADDED = new Handsontable(container_RCC_ADDED, {
+      data: data_RCC_ADDED,
+      minSpareRows : 1,
+      colHeaders : true,
+      colHeaders: [
+        "common name",
+        "<b>InChIKey</b>",
+        "composition",
+        "<b>concentration (&micro;g/ml)</b>",
+        "exact mass",
+        "(M+H)+ or (M-H)-"
+      ],
+      contextMenu : false,
+      columns: colHeaderData
+    });
+    function bindDumpButton_RCC_ADDED() {
+      Handsontable.Dom.addEvent(document.body, "click", (e) => {
+        var element = e.target || e.srcElement;
+        if (element.nodeName == "BUTTON"&& element.name == "dump") {
+          var name = element.getAttribute("data-dump");
+          var instance = element.getAttribute("data-instance");
+          self.hot_RCC_ADDED = window[instance];
+          console.log("data of " + name, self.hot_RCC_ADDED.getData());
+        }
+      });
+    }
+    bindDumpButton_RCC_ADDED();
+    $("#container_RCC_ADDED-{{ TAB_INDEX_PLACEHOLDER }} table.htCore").css("width","100%");
+    // celect cell
+    self.hot_RCC_ADDED.selectCell(0, 0);
+    // add select listener
+    self.hot_RCC_ADDED.addHook("afterSelection", (r, c) => self.hookSelection(r, c));
+  },
+
+  //USED HERE
+  hookSelection: function(r, c) {
+    var self = this ;
+    if (c == 0 || c == 1 || c == 2) 
+      self.pickChemicalCompound4Mix(r);
+  },
+
+  //USED HERE
+  postOneSpectrumFrom: function() {
+    var self = this ;
+    $("#import1SpectrumLoadingBare-{{ TAB_INDEX_PLACEHOLDER }}").show();
+
+    // II - form data -> json object
+    self.loadFomDataIntoJsonObjects();
+    // II.A - check if json object complete
+    var alertMsg = self.getFormErrorMessage();
+    
+    if (alertMsg != "") {
+      $("#alertBoxSubmitSpectrum-{{ TAB_INDEX_PLACEHOLDER }}").html(alertMsg);
+      $("#import1SpectrumLoadingBare-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+      return false;
+    }
+    // all OK: lock!
+    // lock sample
+    $(self.prefix+".add1spectrum-sampleForm").prop("disabled", true);
+    // lock chromato
+    $(self.prefix+".add1spectrum-chromatoLCForm").prop("disabled", true);
+    if (!$("#container_LC_SFG-{{ TAB_INDEX_PLACEHOLDER }}").is(":empty")) {
+      self.hot_LC_SFG.updateSettings({
+        cells: function(row, col, prop) {
+          var cellProperties = {};
+          cellProperties.readOnly = true;
+          return cellProperties;
+        }
+      });
+    }
+    if (!$("#container_MS_Peaks-{{ TAB_INDEX_PLACEHOLDER }}").is(":empty")) {
+      self.hot_MS_Peaks.updateSettings({
+        cells: function(row, col, prop) {
+          var cellProperties = {};
+          cellProperties.readOnly = true;
+          return cellProperties;
+        }
+      });
+    }
+    if (!$("#container_RCC_ADDED-{{ TAB_INDEX_PLACEHOLDER }}").is(":empty")) {
+      self.hot_RCC_ADDED.updateSettings({
+        cells: function(row, col, prop) {
+          var cellProperties = {};
+          cellProperties.readOnly = true;
+          return cellProperties;
+        }
+      });
+      self.hot_RCC_ADDED.removeHook(
+        "afterSelection",
+        (r, c) => self.hookSelection(r, c)
+      );
+    }
+
+    // lock analyzer
+    $(self.prefix+".add1spectrum-analyzerMSForm").prop("disabled", true);
+    // lock other metadata field
+    $(self.prefix+".add1spectrum-otherForm").prop("disabled", true);
+    // lock switch btn
+    $("#btnSwitch-gotoStep2-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", true);
+    $("#btnSwitch-gotoStep3-lc-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", true);
+    $("#btnSwitch-gotoStep4-ms-{{ TAB_INDEX_PLACEHOLDER }}").prop("disabled", true);
+    self.postSpectrumToServer() ;
+  },
+
+  postSpectrumToServer: function(jsonData, tab_index) {
+    var self = this ;
+    var base_peakdata ;
+    var new_peakdata ;
+    var is_ms2 = self.is_ms2() ;
+
+    if (self.is_mix()) {
+      if ({{ TAB_INDEX_PLACEHOLDER }} != 1) {
+        return ;
+      }
+      jsonData = self.gatherJsonObjects() ;
+      all_mix_data = Object.assign({}, jsonData) ;
+      all_mix_data["peaklists"] = [] ;
+      base_peakdata = Object.assign({}, jsonData["peaklists"][0]) ;
+      base_peakdata["msms_ms_precursor_ion"] = undefined ;
+      all_contexts.forEach((context, index) => {
+        new_peakdata = Object.assign({}, base_peakdata) ;
+        context.loadFomDataIntoJsonObjects()
+        if (is_ms2) {
+          new_peakdata["precursor_ion"] = context.jsonPeaksList["msms_ms_precursor_ion"]["m/z"] ;
+        }
+        new_peakdata["peaklist"] = context.jsonPeaksList["peaklist"] ;
+        all_mix_data["peaklists"].push(new_peakdata)
+      })
+    }
+    // II.B - rebuild json full object (with all metadata or just id if already in base)
+    if (jsonData == null) {
+      jsonData = self.gatherJsonObjects();
+    }
+    // II.C - add id metadata (if exist)
+    var json_array = [] ;
+    if (!tab_index) {
+      tab_index = {{ TAB_INDEX_PLACEHOLDER }} ;
+    }
+    json_array[tab_index-1] = jsonData ;
+    
+    // III - post json object
+    // III.A - success
+    self.sent_json = jsonData ;
+    console.warn(
+      "This data has been validated without going through the internal server!"
+    )
+    if ($("#step5sign-{{ TAB_INDEX_PLACEHOLDER }}").hasClass("fa-question-circle")) {
+      $("#step5sign-{{ TAB_INDEX_PLACEHOLDER }}").removeClass("fa-question-circle").addClass("fa-check-circle");
+    }
+    if ($("#step5sign-{{ TAB_INDEX_PLACEHOLDER }}").hasClass("fa-spinner")) {
+      $("#step5sign-{{ TAB_INDEX_PLACEHOLDER }}").removeClass("fa-spinner fa-spin").addClass("fa-check-circle");
+    }
+    $("#import1SpectrumLoadingBare-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+    $("#alertBoxSubmitSpectrum-{{ TAB_INDEX_PLACEHOLDER }}").html(`
+      <div class="alert alert-success alert-dismissible" role="alert">
+        <button type="button" class="close" data-dismiss="alert">
+          <span aria-hidden="true">&times;</span>
+          <span class="sr-only">
+            <spring:message code="alert.close" text="Close" />
+          </span>
+        </button>
+        <strong>
+          <spring:message code="alert.strong.info" text="Success!" />
+        </strong> Spectrum ready to be sent!
+      </div>
+    `);
+    return true;
+  },
+
+  //USED HERE
+  loadFomDataIntoJsonObjects: function() {
+    var self = this ;
+    // I - Spectrum type
+    self.isJsonSpectrumTypeComplete = false ;
+    if (self.isGC && self.is_ms()) {
+      self.jsonSpectrumType = "gc-ms" ;
+    } else if (self.isLC && self.is_ms()) {
+      self.jsonSpectrumType = "lc-ms" ;
+    } else if (self.isLC && self.is_ms2()) {
+      self.jsonSpectrumType = "lc-msms" ;
+    } else if (self.isGC && self.is_ms()) {
+      self.jsonSpectrumType = "ic-ms" ;
+    } else if (self.isLC && self.is_ms()) {
+      self.jsonSpectrumType = "ic-msms" ;
+    }
+    if (self.jsonSpectrumType != null && self.jsonSpectrumType != "") {
+      self.isJsonSpectrumTypeComplete = true;
+    }
+    
+    // II - Sample
+    self.isJsonSampleComplete = true;
+    self.isJsonRCCaddedComplete = true;
+    self.jsonSample = {};
+
+    switch($("#add1spectrum-sample-type-{{ TAB_INDEX_PLACEHOLDER }}").val()) {
+    // II.A - chemical lib. compound
+    case "compound-ref":
+      if ($("#add1spectrum-sample-inchikey-{{ TAB_INDEX_PLACEHOLDER }}").val() == "") {
+        return false;
+      }
+      self.jsonSample["sample_type"] = "reference-chemical-compound";
+      self.jsonSample["inchikey"] = $("#add1spectrum-sample-inchikey-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      self.jsonSample["concentration"] = $("#add1spectrum-sample-concentration-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      if (self.is_ms()) {
+        self.jsonSample["solvent"] = $("#add1spectrum-sample-lcmsSolvent-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      }
+      self.isJsonSampleComplete = true;
+      break;
+      // II.B - chemical lib. compound mix
+    case "compound-mix": 
+      // solvent
+      self.jsonSample["solvent"] = $("#add1spectrum-sample-mixSolvent-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      // cpd added
+      self.jsonSample.inchikeys_list = self.getRCCADDED();
+      self.jsonSample.concentrations_list = self.getRCCADDEDConcentration();
+      if (self.jsonSample.inchikeys_list.length == 0 && !self.is_other_in_mix()) {
+        self.isJsonRCCaddedComplete = false;
+        console.log(`isJsonRCCaddedComplete: false`)
+        return false;
+      }
+      self.isJsonSampleComplete = true;
+      break;
+    default:
+      if (!self.is_other_in_mix()) {
+        return false;
+      } 
+    }
+
+    // III - Chromato
+    self.isJsonChromatoComplete = true;
+    self.jsonChromato = {};
+    
+    // III.A - no chromato
+    // nope?
+
+    // III.B - GC  
+    if (self.isGC) {
+      self.isJsonChromatoComplete = false;
+      // TODO
+    }
+
+    // III.C - LC
+    if (self.isLC) {
+      self.isJsonChromatoComplete = true;
+      self.jsonChromato = {};
+      // check error
+      $.each($(self.prefix+".add1spectrum-chromatoLCForm").parent(), function() {
+        if ($(this).hasClass("has-error"))
+          self.isJsonChromatoComplete = false;
+      });
+      if (
+        !self.isJsonChromatoComplete
+        && !self.is_other_in_mix()
+      ) {
+        console.log(`isJsonChromatoComplete: false`)
+        return false;
+      }
+      var id = `#add1spectrum-chromatoLC-method-{{ TAB_INDEX_PLACEHOLDER }}` ;
+      self.jsonChromato["method"] = $(`${id} option:selected`).text();
+      [
+        ["column_constructor", "colConstructor"],
+        ["column_constructor_other", "colConstructorOther"],
+        ["column_name", "colName"],
+        ["column_length", "colLength"],
+        ["column_diameter", "colDiameter"],
+        ["particule_size", "colParticuleSize"],
+        ["column_temperature", "colTemperature"],
+        ["mode", "LCMode"],
+        ["separation_flow_rate", "separationFlowRate"],
+        ["solvent_a", "separationSolvA"],
+        ["solvent_a_ph", "separationSolvApH"],
+        ["solvent_b", "separationSolvB"],
+        ["solvent_b_ph", "separationSolvBApH"]
+      ].forEach((field) => {
+        id = `#add1spectrum-chromatoLC-${field[1]}-{{ TAB_INDEX_PLACEHOLDER }}` ;
+        self.jsonChromato[field[0]] = $(id).val();
+      })
+      // fulfill json object
+      
+      jsonSFG = [];
+      var formatData = {
+        time: [],
+        solvent_a_percent: [],
+        solvent_b_percent: []
+      };
+      $.each(self.hot_LC_SFG.getData(), function(){
+        if (this[0]!="") {
+          if (!isNaN(this[0]) && !isNaN(this[1]) && !isNaN(this[2])) {
+            formatData["time"].push(Number(this[0]));
+            formatData["solvent_a_percent"].push(Number(this[1]));
+            formatData["solvent_b_percent"].push(Number(this[2]));
+          }
+        }
+      });
+      self.jsonChromato.separation_flow_gradient = formatData;
+    }
+
+    // III.D - IC  
+    if (self.isIC) {
+      self.isJsonChromatoComplete = false;
+      // TODO
+    }
+
+    // IV - Analyzer
+    self.isJsonAnalyzerComplete = true;
+    self.isJsonAnalyzer = {};
+    // IV.A - MS
+    if (self.is_ms() || self.is_ms2()) {
+      self.isJsonAnalyzerComplete = true;
+      self.jsonAnalyzer = {};
+      // check error
+      $.each($(self.prefix+".add1spectrum-analyzerMSForm").parent(), function(){
+        if ($(this).hasClass("has-error"))
+          self.isJsonAnalyzerComplete = false;
+      });
+      if (
+        !self.isJsonAnalyzerComplete
+        && !self.is_other_in_mix()
+      ) {
+        console.log(`isJsonAnalyzerComplete: false`)
+        return false;
+      }
+      
+      // fulfill json object
+      self.jsonAnalyzer["instrument_name"] = $("#add1spectrum-analyzer-ms-instrument-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      self.jsonAnalyzer["instrument_model"] = $("#add1spectrum-analyzer-ms-model-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      self.jsonAnalyzer["instrument_brand"] = $("#add1spectrum-analyzer-ms-model-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      self.jsonAnalyzer["ion_analyzer_type"] = $("#add1spectrum-analyzer-ms-ionAnalyzerType-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      
+      if (self.is_ms2()) {
+        // MSMS ion BEAM or TRAP
+        self.jsonMolIonBeam = {}
+        var jsonIonTrapBeam = {
+          ion_gas: $("#add1spectrum-ionTrapBeam-ionGas-{{ TAB_INDEX_PLACEHOLDER }}").val(),
+          ion_pressure_value: $("#add1spectrum-ionTrapBeam-ionGasPressureValue-{{ TAB_INDEX_PLACEHOLDER }}").val(),
+          ion_pressure_unit: $("#add1spectrum-ionTrapBeam-ionGasPressureUnit-{{ TAB_INDEX_PLACEHOLDER }}").val()
+        } ;
+        var key = null ;
+        if ($("#add1spectrum-ionTrapBeam-type-{{ TAB_INDEX_PLACEHOLDER }}").val() == "trap") {
+          // sp
+          jsonIonTrapBeam["ion_freq_shift"] = $("#add1spectrum-ionTrapBeam-ionFrequencyShift-{{ TAB_INDEX_PLACEHOLDER }}").val();
+          jsonIonTrapBeam["ion_number"] = $("#add1spectrum-ionTrapBeam-ionNumber-{{ TAB_INDEX_PLACEHOLDER }}").val();
+          key = "ion_trap" ;
+        } else if ($("#add1spectrum-ionTrapBeam-type-{{ TAB_INDEX_PLACEHOLDER }}").val() == "beam") {
+          key = "ion_beam_storage" ;
+        }
+        if (key !== null) {
+          self.jsonMolIonBeam[key] = jsonIonTrapBeam;
+        }
+      }
+      
+      self.jsonMolIonization = {};
+      var json_mode ;
+      ["pos", "neg"].forEach((kind) => {
+        json_mode = {} ;
+        [
+          ["ionisation_method", "ionizationMethod"],
+          ["spray_gaz_flow", "sprayGazFlow"],
+          ["vaporizer_gaz_flow", "vaporizerGazFlow"],
+          ["vaporizer_temperature", "vaporizerTemperature"],
+          ["source_gaz_flow", "sourceGazFlow"],
+          ["transfer_tube_or_capillary_temperature", "ionTransferTubeTemperatureOrTransferCapillaryTemperature"],
+          ["high_voltage", "highVoltageOrCoronaVoltage"]
+        ].forEach((field) => {
+          var id = `#add1spectrum-analyzserMS-${field[1]}-${kind}-{{ TAB_INDEX_PLACEHOLDER }}` ;
+          json_mode[field[0]] = $(id).val() ;
+        })
+        self.jsonMolIonization[`mode_${kind}`] = json_mode ;
+      })
+    }
+
+    // IV.B - NMR
+    if (self.is_ms() || self.is_ms2()) {
+      self.isJsonPeaksListComplete = false;
+      // init
+      var peaklist = [];
+      var peakdata = {};
+      var spectrumData = {};
+      // peaklist
+      var ms_peak_data = self.hot_MS_Peaks.getData();
+      $.each(ms_peak_data, function(index) {
+        var data = ms_peak_data[index] ;
+        var formatData = {};
+        if (
+          data[0] != ""
+          && !isNaN(data[0])
+            // && !isNaN(data[2])
+          && (data[6] === "true" || data[6] === true)
+        ) {
+          peaklist.push({
+            mz: Number(data[0]),
+            RI: Number(data[2]),
+            deltaPPM: Number(data[3]),
+            composition: (data[4]),
+            attribution: (data[5])
+          });
+          self.isJsonPeaksListComplete = true;
+        }
+      });
+      // peak list data
+      peakdata["ms_lvl"] = $("#add1spectrum-peaksMS-msLevel-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      peakdata["polarity"] = $("#add1spectrum-peaksMS-polarity-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      peakdata["resolution"] = $("#add1spectrum-peaksMS-resolution-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      peakdata["curation"] = $("#add1spectrum-peaksMS-curation-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      peakdata["mz_range_from"] = $("#add1spectrum-peaksMS-rangeFrom-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      peakdata["mz_range_to"] = $("#add1spectrum-peaksMS-rangeTo-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      peakdata["rt_abs_from"] = $("#add1spectrum-peaksMS-rtMinFrom-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      peakdata["rt_abs_to"] = $("#add1spectrum-peaksMS-rtMinTo-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      peakdata["rt_solv_from"] = $("#add1spectrum-peaksMS-rtSolvFrom-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      peakdata["rt_solv_to"] = $("#add1spectrum-peaksMS-rtSolvTo-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      peakdata["resolution_FWHM"] = $("#add1spectrum-analyzer-ms-resolutionFWHM-{{ TAB_INDEX_PLACEHOLDER }}").val();
+      if (self.is_ms2()) {
+        var data = self.hot_MS_Peaks.getData() ;
+        if (data.length != 0) {
+          var selected_ion = self.get_selected_parent_ion_index() ;
+          peakdata["msms_ms_precursor_ion"] = {
+            "m/z": data[selected_ion][0],
+            "abs_intensity": data[selected_ion][1],
+            "rel_intensity": data[selected_ion][2],
+            "attribution": data[selected_ion][5]
+          } ;
+        }
+      }
+      peakdata["peaklist"] = peaklist;
+      self.jsonPeaksList = peakdata;
+    }
+    
+    self.isJsonOtherMetadataComplete = true;
+    self.jsonOtherMetadata = {};
+    // check error
+    $.each($(self.prefix+".add1spectrum-otherForm").parent(), function(){
+      if ($(this).hasClass("has-error"))
+        self.isJsonOtherMetadataComplete = false;
+    });
+    if (!self.isJsonOtherMetadataComplete && !self.is_other_in_mix()) {
+      console.log(`isJsonOtherMetadataComplete: false`)
+      return false;
+    }
+    self.jsonOtherMetadata["data_authors"] =  $("#add1spectrum-other-author-{{ TAB_INDEX_PLACEHOLDER }}").val();
+    self.jsonOtherMetadata["data_validator"] =  $("#add1spectrum-other-validator-{{ TAB_INDEX_PLACEHOLDER }}").val();;
+    self.jsonOtherMetadata["acquisition_date"] =  $("#add1spectrum-other-date-{{ TAB_INDEX_PLACEHOLDER }}").val();
+    if (!self.jsonOtherMetadata["acquisition_date"]) {
+      self.jsonOtherMetadata["acquisition_date"] = null ;
+    }
+    self.jsonOtherMetadata["data_ownership"] =  $("#add1spectrum-other-owner-{{ TAB_INDEX_PLACEHOLDER }}").val();
+    self.jsonOtherMetadata["raw_file_name"] =  $("#add1spectrum-other-fileName-{{ TAB_INDEX_PLACEHOLDER }}").val();
+    self.jsonOtherMetadata["raw_file_size"] =  $("#add1spectrum-other-fileSize-{{ TAB_INDEX_PLACEHOLDER }}").val();
+    return true;
+  },
+
+  //USED HERE
+  getFormErrorMessage: function() {
+    var self = this ;
+    var alertMsg = "";
+    if (!self.isJsonSpectrumTypeComplete) {
+      alertMsg = `
+      <div class="alert alert-danger alert-dismissible" role="alert">
+        <button type="button" class="close" data-dismiss="alert">
+          <span aria-hidden="true">&times;</span>
+          <span class="sr-only">
+            <spring:message code="alert.close" text="Close" />
+          </span>
+        </button>
+        <strong>
+          <spring:message code="alert.strong.error" text="Error!" />
+        </strong> Error processing spectrum type!
+      </div>` ;
+    } else if (!self.isJsonSampleComplete) {
+      alertMsg = `
+      <div class="alert alert-danger alert-dismissible" role="alert">
+        <button type="button" class="close" data-dismiss="alert">
+          <span aria-hidden="true">&times;</span>
+          <span class="sr-only">
+            <spring:message code="alert.close" text="Close" />
+          </span>
+        </button>
+        <strong>
+          <spring:message code="alert.strong.error" text="Error!" />
+        </strong> Missing mandatory data into sample section!
+        <br />
+        <a
+          href="#"
+          onclick="$('#linkActivateStep1-{{ TAB_INDEX_PLACEHOLDER }}').trigger('click');"
+        >Go to this section</a>
+      </div>` ;
+    } else if (!self.isJsonRCCaddedComplete) {
+      alertMsg = `
+      <div class="alert alert-danger alert-dismissible" role="alert">
+        <button type="button" class="close" data-dismiss="alert">
+          <span aria-hidden="true">&times;</span>
+          <span class="sr-only">
+            <spring:message code="alert.close" text="Close" />
+          </span>
+        </button>
+        <strong>
+          <spring:message code="alert.strong.error" text="Error!" />
+        </strong> Pease enter at least ONE compound in mix into sample section!
+        <br />
+        <a
+          href="#"
+          onclick="$('#linkActivateStep1-{{ TAB_INDEX_PLACEHOLDER }}').trigger('click');"
+        >Go to this section</a>
+      </div>`;
+    } else if (!self.isJsonChromatoComplete) {
+      alertMsg = `
+      <div class="alert alert-danger alert-dismissible" role="alert">
+        <button type="button" class="close" data-dismiss="alert">
+          <span aria-hidden="true">&times;</span>
+          <span class="sr-only">
+            <spring:message code="alert.close" text="Close" />
+          </span>
+        </button>
+        <strong>
+          <spring:message code="alert.strong.error" text="Error!" />
+        </strong> Missing data in chromatography section!
+      ` ;
+      if (self.is_ms()) {
+        alertMsg += `
+        <br />
+        <a
+          href="#"
+          onclick="$('#linkActivateStep4-ms-{{ TAB_INDEX_PLACEHOLDER }}').trigger('click');"
+        >Go to this section</a>`;
+      }
+      alertMsg += ' </div>';
+    } else if (!self.isJsonPeaksListComplete) {
+      alertMsg = `
+      <div class="alert alert-danger alert-dismissible" role="alert">
+        <button type="button" class="close" data-dismiss="alert">
+          <span aria-hidden="true">&times;</span>
+          <span class="sr-only">
+            <spring:message code="alert.close" text="Close" />
+          </span>
+        </button>
+        <strong>
+          <spring:message code="alert.strong.error" text="Error!" />
+        </strong> Missing peaklist!
+      ` ;
+      if (self.is_ms()) {
+        alertMsg += `
+        <br />
+        <a
+          href="#"
+          onclick="$('#linkActivateStep4-ms-{{ TAB_INDEX_PLACEHOLDER }}').trigger('click');"
+        >Go to this section</a>`;
+      }
+      alertMsg += ' </div>';
+    } else if (self.is_mix() && self.is_ms2()) {
+      for (var i = 0 ; i < all_contexts.length ; i += 1) {
+        if (all_contexts[i].get_selected_parent_ion_index() === null) {
+          return alertMsg + `
+            <div class="alert alert-danger alert-dismissible" role="alert">
+              <button type="button" class="close" data-dismiss="alert">
+                <span aria-hidden="true">&times;</span>
+                <span class="sr-only">
+                  <spring:message code="alert.close" text="Close" />
+                </span>
+              </button>
+              <strong>
+                <spring:message code="alert.strong.error" text="Error!" />
+              </strong> Select precursor ion in all peak lists!
+            </div>
+          `
+        }
+      }
+    }
+    // TODO other
+    return alertMsg;
+  },
+
+  //USED HERE
+  gatherJsonObjects: function() {
+    var self = this ;
+    var jsonData = {};
+    if (self.is_mix()) {
+      jsonData["sample_type"] = "mix_of_reference_chemical_compounds" ;
+    } else {
+      jsonData["sample_type"] = "reference_chemical_compound" ;
+    }
+    jsonData["spectrum_type"] = self.jsonSpectrumType.toUpperCase().replaceAll("-", "");
+    jsonData["sample"] = self.jsonSample;
+    if (self.isLC) {
+      jsonData["chromatography"] = self.jsonChromato;
+    } else if (self.isGC) {
+      jsonData["chromatography"] = self.jsonChromato;
+    } else if (self.isIC) {
+      jsonData["chromatography"] = self.jsonChromato;
+    } 
+    if (self.is_ms()) {
+      jsonData["analyzer"] = self.jsonAnalyzer;
+      var molecule_ionization = self.jsonMolIonization;
+      jsonData["ionization_mode_positive"] = molecule_ionization["mode_pos"] ;
+      jsonData["ionization_mode_negative"] = molecule_ionization["mode_neg"] ;
+      jsonData["peaklists"] = [self.jsonPeaksList] ;
+    } else if (self.is_ms2()) {
+      jsonData["analyzer"] = self.jsonAnalyzer;
+      var molecule_ionization = self.jsonMolIonization;
+      jsonData["ionization_mode_positive"] = molecule_ionization["mode_pos"] ;
+      jsonData["ionization_mode_negative"] = molecule_ionization["mode_neg"] ;
+      if (self.jsonMolIonBeam != null) {
+        jsonData["ion_beam_storage"] = self.jsonMolIonBeam["ion_beam_storage"];
+      }
+      jsonData["peaklists"] = [self.jsonPeaksList] ;
+    }
+    jsonData["other_metadata"] = self.jsonOtherMetadata;
+    return jsonData;
+  },
+
+  //USED IN HTML
+  pickChemicalCompound: function() {
+    var self = this ;
+    self.singlePick = true;
+    $("#modalPickCompound-{{ TAB_INDEX_PLACEHOLDER }}").modal("show");
+    $("#add-one-cc-s1-value-{{ TAB_INDEX_PLACEHOLDER }}").focus();
+  },
+
+  //USED HERE
+  pickChemicalCompound4Mix: function(rowNumber) {
+    var self = this ;
+    self.singlePick = false;
+    self.multiPickLine = rowNumber;
+    $("#modalPickCompound-{{ TAB_INDEX_PLACEHOLDER }}").modal("show");
+    $("#add-one-cc-s1-value-{{ TAB_INDEX_PLACEHOLDER }}").focus();
+  },
+
+  //USED HERE
+  searchAjax: function() {
+    var self = this ;
+    var results ;
+    var rawQuery = $('#add-one-cc-s1-value-{{ TAB_INDEX_PLACEHOLDER }}').val();
+    if (rawQuery.length > 2) {
+      return [] ;
+    }
+    results = [] ;
+    $.ajax({ 
+        type: "post",
+        url: "{{ PF_URL_PLACEHOLDER }}/webapp/search",
+        dataType: "json",
+        async: false,
+        data: "query=" + $("#add-one-cc-s1-value-{{ TAB_INDEX_PLACEHOLDER }}").val(),
+        success: function(json) {
+          if (json.success) {
+            $.each(json.compoundNames, function(){
+              results.push(this.name) ;
+            }) ; 
+            $.each(json.compounds, function(){
+              if (this.inChIKey.indexOf(rawQuery)) {
+                results.push(this.inChIKey) ;
+              }
+            }) ;
+          }
+      },
+      error : function(xhr) {
+        self.subjects = [] ;
+        console.log(xhr) ;
+      }
+    }) ;
+    return results ;
+  },
+
+  //USED HERE
+  searchLocalCompound: function() {
+    var self = this ;
+    $("#load-step-1-{{ TAB_INDEX_PLACEHOLDER }}").show();
+    $.ajax({ 
+      type: "get",
+      url: "{{ PF_URL_PLACEHOLDER }}/rest/v2/compounds",
+      async: true,
+      data: "limit=1&query=" + $('#add-one-cc-s1-value-{{ TAB_INDEX_PLACEHOLDER }}').val() + "&query_filter=" + self.fitlerSearchLoadlCpd+"&token={{ PF_TOKEN_PLACEHOLDER }}",
+      success: function(data) {
+        data = data[0] ;
+        var id = parseInt(data["id"]) ;
+        var name = data["name"] ;
+        var inchikey = data["inchikey"] ;
+        var inchi = data["inchi"] ;
+        data = `
+    <div class="table-responsive">
+      <table id="tabPickCpd-{{ TAB_INDEX_PLACEHOLDER }}" class="table table-bordered table-hover table-striped tablesorter" style="cursor: pointer;">
+        <thead>
+          <tr>
+            <th class="header">Chemical Name <i class="fa fa-sort"></i></th>
+            <th class="header">Monoisotopic Mass <i class="fa fa-sort"></i></th>
+            <th class="header">Formula <i class="fa fa-sort"></i></th>
+            <th class="header" style="">Structure</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr class="success" onclick="loadCompoundInForm(${id}, '${inchikey}', '${inchi}', '${data["formula"]}', '${data["exactMass"]}' , 100);">
+            <td style="vertical-align: middle;">
+              <span id="cpt-load-name-${id}-{{ TAB_INDEX_PLACEHOLDER }}">${name}</span> 
+              <br><small style="white-space: nowrap;">${inchikey}</small>
+            </td>
+            <td style="vertical-align: middle;" class="compoundMass">${data["exactMass"]}</td>
+            <td style="vertical-align: middle;" class="compoundFormula">${data["formula"]}</sub></td>
+            <td><span class="avatar">
+              <img class="compoundSVG" src="{{ PF_URL_PLACEHOLDER }}/webapp/image/generic/${inchikey}" alt="${name}">
+              </span>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    <small><sup><b>*</b></sup>: Generic Compounds (abstract "flat" compound without (+) or (-) center).</small>
+    <` + "script" + ` type="text/javascript">
+    $("#tabPickCpd-{{ TAB_INDEX_PLACEHOLDER }}").tablesorter();
+    $.each($(self.prefix+".compoundFormula"), function(id, elem) {
+      var rawFromula = $(elem).text();
+      var formatedFormula = rawFromula;
+      try {
+        $.each($.unique( rawFromula.match(/\d+/g)), function(keyF, valF) {
+          var re = new RegExp(valF,"g");
+          formatedFormula = formatedFormula.replace(re, "<sub>" + valF + "</sub>");
+        });
+      } catch (e){}
+      formatedFormula = formatedFormula.replace("</sub><sub>", "");
+      $(elem).html(formatedFormula);
+    });
+    $.each($(self.prefix+".compoundMass"), function(id, elem) {
+      var exactMass = parseFloat( $(elem).text());
+      exactMass = roundNumber(exactMass,7)
+      $(elem).html(exactMass);
+    });
+    </` + "script" + `>
+
+      </div>
+    <` + "script" + ` type="text/javascript">
+    var listOfRefCompoundsMatch = null;
+
+    loadCompoundInForm = function(id, inchikey, inchi, composition, exactMass, type) {
+      if (ctx().modeEditSpectrum) {
+        var name = $("#cpt-load-name-" + id + "-{{ TAB_INDEX_PLACEHOLDER }}").html();
+        if (ctx().multiPickLine >= 0) {
+          ctx().hot_RCC_ADDED.setDataAtCell(ctx().multiPickLine, 0, name);
+          // ctx().hot_RCC_ADDED.setDataAtCell(ctx().multiPickLine, 1, inchikey);
+
+          // restet form
+          setTimeout(function(){
+            $("#add-one-cc-s1-value-{{ TAB_INDEX_PLACEHOLDER }}").val("");
+            $("#ok-step-1-{{ TAB_INDEX_PLACEHOLDER }}").html("");
+          }, 200);
+          // img
+          var typeS = "chemical";
+          if (type == 100)
+            typeS = "generic";
+          else if (type == 101)
+            typeS = "chemical";
+          // '<img class="mixRCCadd'+ctx().multiPickLine+' compoundSVGZoom" src="{{ PF_URL_PLACEHOLDER }}/webapp/image/'+typeS+'/'+inchikey+'" alt="'+name+'">'
+          var currentCpt = { 
+              "name": name,
+              "type": typeS,
+              "concentration": "?",
+              "inchikey": inchikey
+          }; 
+          updatedCpdMixData[name] = currentCpt;
+        }
+        // display
+        $("#modalPickCompound-{{ TAB_INDEX_PLACEHOLDER }}").modal("hide");
+        $("#modalEditSpectrum-{{ TAB_INDEX_PLACEHOLDER }} .modal-dialog").show();
+        return;
+      } // else: add one spectrum
+      var name = $("#cpt-load-name-" + id + "-{{ TAB_INDEX_PLACEHOLDER }}").html();
+      if (ctx().singlePick) {
+        $("#add1spectrum-sample-inchikey-{{ TAB_INDEX_PLACEHOLDER }}").val(inchikey);
+        $("#add1spectrum-sample-inchikey-{{ TAB_INDEX_PLACEHOLDER }}").change();
+        $("#add1spectrum-sample-inchi-{{ TAB_INDEX_PLACEHOLDER }}").val(inchi);
+        $("#add1spectrum-sample-inchi-{{ TAB_INDEX_PLACEHOLDER }}").change();
+        $("#add1spectrum-sample-commonName-{{ TAB_INDEX_PLACEHOLDER }}").val(name);
+        $("#add1spectrum-sample-commonName-{{ TAB_INDEX_PLACEHOLDER }}").change();  
+
+        $("#importspectrum-sample-inchikey-{{ TAB_INDEX_PLACEHOLDER }}").val(inchikey);
+        $("#importspectrum-sample-inchikey-{{ TAB_INDEX_PLACEHOLDER }}").change();
+      } else if (ctx().multiPickLine >= 0) {
+        ctx().hot_RCC_ADDED.setDataAtCell(ctx().multiPickLine, 0, name);
+        ctx().hot_RCC_ADDED.setDataAtCell(ctx().multiPickLine, 1, inchikey);
+        ctx().hot_RCC_ADDED.setDataAtCell(ctx().multiPickLine, 2, composition);
+        ctx().hot_RCC_ADDED.setDataAtCell(ctx().multiPickLine, 4, exactMass);
+        // restet form
+        setTimeout(function(){
+          $("#add-one-cc-s1-value-{{ TAB_INDEX_PLACEHOLDER }}").val("");
+          $("#ok-step-1-{{ TAB_INDEX_PLACEHOLDER }}").html("");
+        }, 200);
+      }
+      var typeS = "chemical";
+      if (type == 100)
+        typeS = "generic";
+      else if (type == 101)
+        typeS = "chemical";
+      if (ctx().singlePick)
+        $("#sample-bonus-display-{{ TAB_INDEX_PLACEHOLDER }}").html('<img class="" src="{{ PF_URL_PLACEHOLDER }}/webapp/image/'+typeS+'/'+inchikey+'" alt="'+name+'">');
+      else {
+        // delete
+        $(ctx().prefix+"img.mixRCCadd"+ctx().multiPickLine).remove();
+        // add
+        $("#sample-bonus-display-{{ TAB_INDEX_PLACEHOLDER }}").append('<img class="mixRCCadd'+ctx().multiPickLine+' compoundSVGZoom" src="{{ PF_URL_PLACEHOLDER }}/webapp/image/'+typeS+'/'+inchikey+'" alt="'+name+'">');
+        $(ctx().prefix+"img.mixRCCadd"+ctx().multiPickLine+"").mouseenter(function() {
+          $(this).removeClass("compoundSVGZoom");
+        }).mouseleave(function() {
+          $(this).addClass("compoundSVGZoom");
+        });
+      }
+      $("#modalPickCompound-{{ TAB_INDEX_PLACEHOLDER }}").modal("hide");
+    }
+    </` + "script" + `></` + "div" + `>` ;
+        $("#ok-step-1-{{ TAB_INDEX_PLACEHOLDER }}").html(data);
+      },
+      error : function(xhr) {
+        // log
+        console.log(xhr);
+        // error
+        $("#ok-step-1-{{ TAB_INDEX_PLACEHOLDER }}").html("Error: could not process request.");
+      }
+    }).always(function() {
+      $("#load-step-1-{{ TAB_INDEX_PLACEHOLDER }}").hide();
+    });
+  },
+
+  //USED HERE
+  getRCCADDED: function() {
+    var self = this ;
+    jsonRCC_ADDED = [];
+    $.each(self.hot_RCC_ADDED.getData(), function(){
+      var formatData = {};
+      if ("<b>InChIKey</b>" in this && this["<b>InChIKey</b>"]!= undefined && this["<b>InChIKey</b>"] != "") {
+        jsonRCC_ADDED.push(this["<b>InChIKey</b>"]);
+      }
+    });
+    return jsonRCC_ADDED;
+  },
+
+  //USED HERE
+  getRCCADDEDConcentration: function() {
+    var self = this ;
+    jsonRCC_ADDED = [];
+    $.each(self.hot_RCC_ADDED.getData(), function(){
+      var formatData = {};
+      if ("<b>InChIKey</b>" in this && this["<b>InChIKey</b>"]!= undefined && this["<b>InChIKey</b>"] != "") {
+        jsonRCC_ADDED.push(this["<b>concentration (&micro;g/ml)</b>"]);
+      }
+    });
+    return jsonRCC_ADDED;
+  },
+
+  //USED IN HTML
+  clearLine: function() {
+    var self = this ;
+    // restet form
+    setTimeout(function(){
+      $("#add-one-cc-s1-value-{{ TAB_INDEX_PLACEHOLDER }}").val("");
+      $("#ok-step-1-{{ TAB_INDEX_PLACEHOLDER }}").html("");
+    }, 200);
+    $(self.prefix+"img.mixRCCadd"+self.multiPickLine).remove();
+    if (self.singlePick) {
+      $("#add1spectrum-sample-inchikey-{{ TAB_INDEX_PLACEHOLDER }}").val("");
+      $("#add1spectrum-sample-inchikey-{{ TAB_INDEX_PLACEHOLDER }}").change();
+      $("#add1spectrum-sample-inchi-{{ TAB_INDEX_PLACEHOLDER }}").val("");
+      $("#add1spectrum-sample-inchi-{{ TAB_INDEX_PLACEHOLDER }}").change();
+      $("#add1spectrum-sample-commonName-{{ TAB_INDEX_PLACEHOLDER }}").val("");
+      $("#add1spectrum-sample-commonName-{{ TAB_INDEX_PLACEHOLDER }}").change();
+      $("#sample-bonus-display-{{ TAB_INDEX_PLACEHOLDER }}").html("");
+    } else if (self.multiPickLine >= 0) {
+      for(var i = 0 ; i < 6 ; i += 1) {
+        self.hot_RCC_ADDED.setDataAtCell(self.multiPickLine, i, "");
+      }
+    }
+  },
+
+  exit: function() {
+    $.ajax({type: "get", url: "/quit", async: true, success: ()=>window.close()});
+  },
+
+} ;
+
+$.ajax({
+  type: "get",
+  url: "{{ PF_URL_PLACEHOLDER }}/webapp/get-cpd-data?token={{ PF_TOKEN_PLACEHOLDER }}",
+  data: "inchikey=" + "{{ INCHIKEY_PLACEHOLDER }}",
+  dataType: "json",
+  success: function(data) {
+    if (data.success) {
+      var self = context_{{ TAB_INDEX_PLACEHOLDER }} ;
+      console.log(`inchikey {{ INCHIKEY_PLACEHOLDER }} found!!`)
+      inchidata["{{ INCHIKEY_PLACEHOLDER }}"] = data ;
+      self.init() ;
+      self.fitlerSearchLoadlCpd = 5;
+      self.inchikey = null;
+      all_contexts.push(self) ;
+    } else {
+      no_success(
+        {{ TAB_INDEX_PLACEHOLDER }},
+        "{{ INCHIKEY_PLACEHOLDER }}",
+        {{ DEFAULT_DATA }}["name"]
+      ) ;
+    }
+  },
+  error : function(data) {
+    no_success(
+      {{ TAB_INDEX_PLACEHOLDER }},
+      "{{ INCHIKEY_PLACEHOLDER }}",
+      {{ DEFAULT_DATA }}["name"]
+    ) ;
+  }
+}).always(() => null);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common.js	Fri Mar 03 14:10:24 2023 +0000
@@ -0,0 +1,195 @@
+
+this.ctx = () => context;
+var all_contexts = [] ;
+var inchidata = {} ;
+var all_mix_data = {} ;
+function no_success(tab_index, inchi, data) {
+  console.log(`inchikey ${inchi} not found...`) ;
+  console.log(`disabling ${data} ...`)
+  disable_tab(
+    tab_index,
+    "red",
+    "This compound has not been found on peakforest"
+  ) ;
+}
+
+function disable_tab(tab_index, color, title) {
+  var element = $(`#open_tab_${tab_index}`) ;
+  element.attr("disabled", "disabled").off("click");
+  element.attr("onclick", null) ;
+  element.attr("title", title) ;
+  element.attr("data-toggle", null) ;
+  element.attr("href", null) ;
+  element.css("color", color) ;
+  element.css("cursor", "not-allowed") ;
+}
+
+function send_everything_to_peakforest(url, token) {
+  var bundles ;
+  if (context.is_mix()) {
+    bundles = [all_mix_data] ;
+  } else {
+    bundles = create_dataset_bundles() ;
+  }
+  bundles.forEach((bundle, index) => {
+    console.log(bundle)
+    $.ajax({
+      type: "post",
+      url: `${url}/rest/v2/spectrum?token=${token}`,
+      data: JSON.stringify(bundle),
+      contentType: "application/json"
+    })
+  })
+  // console.log(bundles)
+}
+
+function create_dataset_bundles() {
+  var bundles = [] ;
+  var data ;
+  all_contexts.forEach((context, index) => {
+    if ((data = context.sent_json) === null) {
+      return ;
+    }
+    if (bundles.length === 0) {
+      // console.log("First metadata!")
+      return bundles.push(data) ;
+    }
+    if (merge_in_bundle(bundles, data)) {
+      // console.log("Merged!")
+    } else {
+      // console.log("New metadata!")
+    }
+  }) ;
+  return bundles ;
+}
+
+function merge_in_bundle(bundles, data) {
+  for(var i = 0 ; i < bundles.length ; i += 1) {
+    if (
+      identical(bundles[i]["sample"], data["sample"])
+      && identical(bundles[i]["chromatography"], data["chromatography"])
+      && identical(bundles[i]["analyzer"], data["analyzer"])
+      && identical(bundles[i]["ionization_mode_positive"], data["ionization_mode_positive"])
+      && identical(bundles[i]["ionization_mode_negative"], data["ionization_mode_negative"])
+      && identical(bundles[i]["other_metadata"], data["other_metadata"])
+    ) {
+      bundles[i]["peaklists"].push(data["peaklists"][0]) ;
+      return true ;
+    } else {
+      continue ;
+      console.log(
+        "sample: "
+        + identical(bundles[i]["sample"], data["sample"])
+      )
+      console.log(
+        "chromatography: "
+        + identical(bundles[i]["chromatography"], data["chromatography"])
+      )
+      console.log(
+        "analyzer: "
+        + identical(bundles[i]["analyzer"], data["analyzer"])
+      )
+      console.log(
+        "ionization_mode_positive: "
+        + identical(bundles[i]["ionization_mode_positive"], data["ionization_mode_positive"])
+      )
+      console.log(
+        "ionization_mode_negative: "
+        + identical(bundles[i]["ionization_mode_negative"], data["ionization_mode_negative"])
+      )
+      console.log(
+        "other_metadata: "
+        + identical(bundles[i]["other_metadata"], data["other_metadata"])
+      )
+    }
+  }
+  bundles.push(data) ;
+  return false ;
+}
+
+function identical(left, right) {
+  if (typeof left !== typeof right) {
+    return false ;
+  }
+  if (left === null) {
+    return right === null ;
+  }
+  if (right === null) {
+    return left === null ;
+  }
+  switch (typeof left) {
+    case "array":
+      return identical_array(left, right) ;
+      break ;
+    case "object":
+      return identical_object(left, right) ;
+      break ;
+    default:
+      break ;
+  }
+  return (left === right)
+}
+
+function identical_array(left, right) {
+  if (right.length !== left.length) {
+    return false ;
+  }
+  for(var i = 0 ; i < left.length ; i += 1) {
+    if (!identical(left[i], right[i])) {
+      return false ;
+    }
+  }
+  return true ;
+}
+
+function identical_object(left, right) {
+  var left_keys ;
+
+  if (!(
+    share_keys(left, right)
+    && share_keys(right, left)
+  )) {
+    return true ;
+  }
+  left_keys = Object.keys(left) ;
+  for (var i = 0 ; i < left_keys.length ; i += 1){
+    if (!identical(left[left_keys[i]], right[left_keys[i]])) {
+      return false ;
+    }
+  }
+  return true ;
+}
+
+function share_keys(left, right) {
+  var left_keys ;
+  var right_keys ;
+
+  left_keys = Object.keys(left) ;
+  right_keys = Object.keys(right) ;
+  for(var i = 0 ; i < left_keys.length ; i += 1) {
+    if (!right_keys.includes(left_keys[i])) {
+      return false ;
+    }
+  }
+  return true ;
+}
+
+var set_inchi_data = function(data, tab_index) {
+  inchidata[data.inchikey] = data ;
+  $(`#add1spectrum-sample-inchi-${tab_index}`).val(data.inchi);
+  $(`#add1spectrum-sample-inchi-${tab_index}`).change();
+  $(`#add1spectrum-sample-commonName-${tab_index}`).val(data.name);
+  $(`#add1spectrum-sample-commonName-${tab_index}`).change();
+  $(`#sample-bonus-display-${tab_index}`).html(
+    `<img
+      class=""
+      src="{{ PF_URL_PLACEHOLDER }}/webapp/image/${data.type}/${data.inchikey}"
+      alt="${data.name}"
+    >`
+  );
+}
+
+var lightgrayRenderer = function(instance, td, row, col, prop, value, cellProperties) {
+  Handsontable.renderers.TextRenderer.apply(this, arguments);
+  td.style.backgroundColor = "#EEE";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/compound-mix.html	Fri Mar 03 14:10:24 2023 +0000
@@ -0,0 +1,36 @@
+                    <div
+                      class="tab-pane fade {{ ACTIVE_TAB_PLACEHOLDER }} in"
+                      id="tab-index-{{ TAB_INDEX_PLACEHOLDER }}"
+                    >
+                      <div id="spectrum-signal-data-lcms-{{ TAB_INDEX_PLACEHOLDER }}">
+                        <div id="spectrum-peaklist-lcms-{{ TAB_INDEX_PLACEHOLDER }}">
+                          <div class="col-lg-6" >
+                            <div
+                              id="container_MS_Peaks-{{ TAB_INDEX_PLACEHOLDER }}"
+                              class="handsontable"
+                              style="display: inline-block;"
+                            ></div>
+
+                          </div>
+                        </div>
+                        <div class="form-group input-group col-lg-3 has-warning hidden-if-ms-in-msms">
+                          <span class="input-group-addon">
+                            Precursor ion <small>(M/Z)</small>
+                            <i class="fa fa-question-circle" title="2 digits of precision."></i>
+                          </span>
+                          <input
+                            id="add1spectrum-peaksMS-msPrecursorIon-{{ TAB_INDEX_PLACEHOLDER }}"
+                            type="text"
+                            class="form-control add1spectrum add1spectrum-peaksMSForm-peaklist is-mandatory add1spectrum-peaksMSForm-peaklist-reset enabled-if-ms-in-msms"
+                            placeholder="e.g. 123.45"
+                          />
+                        </div>
+                        <div id="spectrum-preview-lcms-{{ TAB_INDEX_PLACEHOLDER }}">
+                          <div
+                            id="containter-lcms-spectrum-preview-{{ TAB_INDEX_PLACEHOLDER }}"
+                            class="col-lg-6"
+                            style="margin-bottom: 20px;"
+                          ></div>
+                        </div>
+                      </div>
+                    </div>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/compound-ref.html	Fri Mar 03 14:10:24 2023 +0000
@@ -0,0 +1,660 @@
+            <div class="tab-pane fade {{ ACTIVE_TAB_PLACEHOLDER }} in" id="tab-index-{{ TAB_INDEX_PLACEHOLDER }}">
+              <div class="panel-group" id="accordion-{{ TAB_INDEX_PLACEHOLDER }}">
+                <div class="panel panel-default">
+                  <div class="panel-heading panel-success">
+                    <h4 class="panel-title">
+                      <a data-toggle="collapse" data-parent="#accordion-{{ TAB_INDEX_PLACEHOLDER }}" href="#step0-{{ TAB_INDEX_PLACEHOLDER }}">
+                        Spectrum Type <i id="step0sign-{{ TAB_INDEX_PLACEHOLDER }}" class="fa fa-question-circle"></i>
+                      </a>
+                    </h4>
+                  </div>
+                  <div id="step0-{{ TAB_INDEX_PLACEHOLDER }}" class="panel-collapse collapse in">
+                    <div class="panel-body">
+                      <button class="btn btn-disabled" disabled="disabled"><i class="fa fa-plus-circle"></i> GC-MS spectrum</button>
+                      <button id="set_LC_MS_spectrum_button" class="btn btn-primary" onclick="ctx().addOneSpectrum(2);"><i class="fa fa-plus-circle"></i> LC-MS spectrum</button>
+                      <button id="set_LC_MSMS_spectrum_button" class="btn btn-primary" onclick="ctx().addOneSpectrum(5);"><i class="fa fa-plus-circle"></i> LC-MSMS spectrum</button>
+                    </div>
+                  </div>
+                </div>
+                <div id="add1spectrum-sampleData-{{ TAB_INDEX_PLACEHOLDER }}" class="panel panel-default" style="display: none;">
+                  <div class="panel-heading panel-success">
+                    <h4 class="panel-title">
+                      <a id="linkActivateStep1-{{ TAB_INDEX_PLACEHOLDER }}" data-toggle="collapse" data-parent="#accordion-{{ TAB_INDEX_PLACEHOLDER }}" href="#step1-{{ TAB_INDEX_PLACEHOLDER }}">
+                        Sample Type <i id="step1sign-{{ TAB_INDEX_PLACEHOLDER }}" class="fa fa-question-circle"></i>
+                      </a>
+                    </h4>
+                  </div>
+                  <div id="step1-{{ TAB_INDEX_PLACEHOLDER }}" class="panel-collapse collapse">
+                    <div class="panel-body">
+                      <div class="col-lg-12">
+                        <div class="col-lg-8">
+                          <div class="panel panel-default">
+                            <div class="panel-heading">
+                              <div class="form-group input-group  has-error">
+                                <span class="input-group-addon">Sample Type</span> 
+                                <select id="add1spectrum-sample-type-{{ TAB_INDEX_PLACEHOLDER }}" class="form-control add1spectrum add1spectrum-sampleForm is-mandatory">
+                                  <option value="" selected="selected" disabled="disabled">choose in list…</option>
+                                  <option value="compound-ref">Ref. Chemical Compound</option>
+                                </select>
+                              </div>
+                            </div>
+                            <div class="panel-body">
+                              <div id="add1spectrum-sample-type-compound-ref-{{ TAB_INDEX_PLACEHOLDER }}" class="add1spectrum-sample-type-panel" style="display:none;">
+                                <div class="form-group input-group  has-error">
+                                  <span class="input-group-addon">InChIKey</span> 
+                                  <input id="add1spectrum-sample-inchikey-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="pickChemicalCompound form-control add1spectrum add1spectrum-sampleForm is-mandatory" placeholder="e.g. RYYVLZVUVIJVGH-UHFFFAOYSA-N">
+                                  <span class="input-group-btn">
+                                    <button class="btn btn-default" type="button" onclick="ctx().pickChemicalCompound();">
+                                      <i class="fa fa-search"></i>
+                                    </button>
+                                  </span>
+                                </div>
+                                <div class="form-group input-group  has-warning">
+                                  <span class="input-group-addon">concentration <small>(mmol/L)</small></span> 
+                                  <input id="add1spectrum-sample-concentration-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-sampleForm is-optional" placeholder="42">
+                                </div>
+                                <div class="form-group input-group opt-ms  has-warning" style="display: none;">
+                                  <span class="input-group-addon">solvent</span> 
+                                  <select id="add1spectrum-sample-lcmsSolvent-{{ TAB_INDEX_PLACEHOLDER }}" class="form-control add1spectrum add1spectrum-sampleForm is-optional"><option value="" selected="selected" disabled="disabled">choose in list…</option><option value="none" class="">none</option><option value="H2O" class="">H2O</option><option value="3/5 in H2O + 0.1% AF/ ACN + 0.1% AF (50/50, V/V)" class="">3/5 in H2O + 0.1% AF/ ACN + 0.1% AF (50/50, V/V)</option><option value="ACN/H2O (1/1, V/V)" class="">ACN/H2O (1/1, v/v)</option><option value="CH3OH/H2O (1/1, V/V)" class="">CH3OH/H2O (1/1, v/v)</option><option value="H2O/ACN (40/60)" class="">H2O/ACN (40/60)</option><option value="H2O/ACN (95/5)" class="">H2O/ACN (95/5)</option><option value="H20+0.1% FORMIC ACID / ACN+0.1% FORMIC ACID (50/50, V/V)" class="">H20+0.1% Formic Acid / ACN+0.1% Formic Acid (50/50, V/V)</option></select>
+                                </div>
+                                <div class="form-group input-group  has-warning">
+                                  <span class="input-group-addon">InChI</span> 
+                                  <input id="add1spectrum-sample-inchi-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="pickChemicalCompound form-control add1spectrum add1spectrum-sampleForm is-optional" placeholder="e.g. InChI=1S/C8H10N4O2/c1-10-4-9-6-5(10)7(13)12(3)8(14)11(6)2/h4H,1-3H3">
+                                </div>
+                                <div class="form-group input-group  has-warning">
+                                  <span class="input-group-addon">Molecule common name</span> 
+                                  <input id="add1spectrum-sample-commonName-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="pickChemicalCompound form-control add1spectrum add1spectrum-sampleForm is-optional" placeholder="e.g. Caffeine">
+                                </div>
+                              </div>
+                              <div id="add1spectrum-sample-type-rcc-added-{{ TAB_INDEX_PLACEHOLDER }}" class="panel panel-default add1spectrum-sample-type-panel" style="display:none;">
+                                <div class="panel-heading">
+                                  <h3 class="panel-title">Reference compound added</h3>
+                                </div>
+                                <div class="panel-body" style="padding: 0px;">
+                                  <div id="container_RCC_ADDED-{{ TAB_INDEX_PLACEHOLDER }}" class="handsontable"></div>
+                                </div>
+                              </div>
+                            </div>
+                          </div>
+                        </div>
+                        <div class="col-lg-4">
+                          <div id="sample-bonus-display-{{ TAB_INDEX_PLACEHOLDER }}"></div>
+                          &nbsp;
+                        </div>
+                      </div>
+                      <div class="col-lg-12">
+                        <div class="col-lg-8">
+                          <br>
+                          <button id="btnSwitch-gotoStep2-{{ TAB_INDEX_PLACEHOLDER }}" onclick="ctx().switchToStep(2);" class="btn btn-disabled switchStep" disabled=""><i class="fa fa-arrow-circle-down"></i> Next!</button>
+                        </div>
+                        <div class="col-lg-4">&nbsp;</div>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+                <div id="add1spectrum-chromatographyData-LC-{{ TAB_INDEX_PLACEHOLDER }}" class="panel panel-default" style="display: none;">
+                  <div class="panel-heading panel-success">
+                    <h4 class="panel-title">
+                      <a id="linkActivateStep2-lc-{{ TAB_INDEX_PLACEHOLDER }}" data-toggle="collapse" data-parent="#accordion-{{ TAB_INDEX_PLACEHOLDER }}" href="#step2-lc-{{ TAB_INDEX_PLACEHOLDER }}">
+                        Liquid Chromatography <i id="step2-lc-sign-{{ TAB_INDEX_PLACEHOLDER }}" class="fa fa-question-circle"></i>
+                      </a>
+                    </h4>
+                  </div>
+                  <div id="step2-lc-{{ TAB_INDEX_PLACEHOLDER }}" class="panel-collapse collapse">
+                    <div class="panel-body">
+                      <div class="col-lg-12">
+                        <div class="col-lg-6">
+                          <div class="panel panel-default">
+                            <div class="panel-heading">
+                              <h3 class="panel-title">Chromatography Param.</h3>
+                            </div>
+                            <div class="panel-body">
+                              <div class="form-group input-group  has-error">
+                                <span class="input-group-addon">Method</span> 
+                                <select
+                                  id="add1spectrum-chromatoLC-method-{{ TAB_INDEX_PLACEHOLDER }}"
+                                  class="form-control add1spectrum add1spectrum-chromatoLCForm is-mandatory"
+                                ></select>
+                              </div>
+                              <div id="alertBoxSelectTemplate-{{ TAB_INDEX_PLACEHOLDER }}"></div>
+                              <div class="form-group input-group  has-error">
+                                <span class="input-group-addon">Column constructor</span> 
+                                <select id="add1spectrum-chromatoLC-colConstructor-{{ TAB_INDEX_PLACEHOLDER }}" class="form-control add1spectrum add1spectrum-chromatoLCForm is-mandatory"><option value="" selected="selected" disabled="disabled">choose in list…</option><option value="Agilent">Agilent</option><option value="alltech">Alltech</option><option value="CIL cluzeau">Cil cluzeau</option><option value="Daicel">Daicel</option><option value="Hypersil">Hypersil</option><option value="Interchim">Interchim</option><option value="Macherey nagel">Macherey nagel</option><option value="Merck">Merck</option><option value="Phenomenex">Phenomenex</option><option value="Shiseido">Shiseido</option><option value="Supelco (sigma-aldrich)">Supelco (sigma-aldrich)</option><option value="Thermo">Thermo</option><option value="Vydac">Vydac</option><option value="Waters">Waters</option><option value="YMC">YMC</option><option value="other">Other</option></select>
+                              </div>
+                              <div class="form-group input-group ">
+                                <span class="input-group-addon">Column constructor (other)</span> 
+                                <input id="add1spectrum-chromatoLC-colConstructorOther-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-chromatoLCForm" disabled="disabled" placeholder="e.g. HAL 9000">
+                              </div>
+                              <div class="form-group input-group  has-warning">
+                                <span class="input-group-addon">Column name</span> 
+                                <input id="add1spectrum-chromatoLC-colName-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-chromatoLCForm is-optional" placeholder="e.g. UPLC HSS T3">
+                              </div>
+                              <div class="form-group input-group  has-error">
+                                <span class="input-group-addon">Column length (mm)</span> 
+                                <input id="add1spectrum-chromatoLC-colLength-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-chromatoLCForm is-mandatory" placeholder="e.g. 150">
+                              </div>
+                              <div class="form-group input-group  has-error">
+                                <span class="input-group-addon">Column diameter (mm)</span> 
+                                <input id="add1spectrum-chromatoLC-colDiameter-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-chromatoLCForm is-mandatory" placeholder="e.g. 2.1">
+                              </div>
+                              <div class="form-group input-group  has-error">
+                                <span class="input-group-addon">Particule size (µm)</span> 
+                                <input id="add1spectrum-chromatoLC-colParticuleSize-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-chromatoLCForm is-mandatory" placeholder="e.g. 1.8">
+                              </div>
+                              <div class="form-group input-group  has-warning">
+                                <span class="input-group-addon">Column temperature (°C)</span> 
+                                <input id="add1spectrum-chromatoLC-colTemperature-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-chromatoLCForm is-optional" placeholder="e.g. 30">
+                              </div>
+                              <div class="form-group input-group  has-error">
+                                <span class="input-group-addon">LC mode</span> 
+                                <select id="add1spectrum-chromatoLC-LCMode-{{ TAB_INDEX_PLACEHOLDER }}" class="form-control add1spectrum add1spectrum-chromatoLCForm is-mandatory">
+                                  <option value="" selected="selected" disabled="disabled">choose in list…</option>
+                                  <option value="Gradient">Gradient</option>
+                                  <option value="Isocratic">Isocratic</option>
+                                </select>
+                              </div>
+                              <div class="form-group input-group  has-warning">
+                                <span class="input-group-addon">Separation flow rate (µL/min)</span> 
+                                <input id="add1spectrum-chromatoLC-separationFlowRate-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-chromatoLCForm is-optional" placeholder="e.g. 400">
+                              </div>
+                              <div class="form-group input-group  has-error">
+                                <span class="input-group-addon">Separation solvent A</span> 
+                                <select id="add1spectrum-chromatoLC-separationSolvA-{{ TAB_INDEX_PLACEHOLDER }}" class="form-control add1spectrum add1spectrum-chromatoLCForm is-mandatory"><option value="" selected="selected" disabled="disabled">choose in list…</option><option value="5% ACN + 0.1% FA in H2O">5% ACN + 0.1% FA in H2O</option><option value="10% ACN + 0.1% FA in H2O">10% ACN + 0.1% FA in H2O</option><option value="10mM (NH4)2CO3 / ACN (20/80) (v/v)">10mM (NH4)2CO3 / ACN (20/80) (v/v)</option><option value="Acetonitrile">Acetonitrile</option><option value="Acetonitrile + 0.1% Formic Acid">Acetonitrile + 0.1% Formic Acid</option><option value="H2O + 0.1% Formic Acid">H2O + 0.1% Formic Acid</option><option value="H2O, ammonium carbonate 10 mM, pH10.5">H2O, ammonium carbonate 10 mM, pH10.5</option><option value="H2O, ammonium acetate 2.5 mM">H2O, ammonium acetate 2.5 mM</option><option value="H2O / CH3OH / CH3CO2H (95/5/0.1)">H2O / CH3OH / CH3CO2H (95/5/0.1)</option><option value="Methanol">Methanol</option><option value="Methanol / H2O (1/1)">Methanol / H2O (1/1)</option><option value="Methanol / H2O (1/1), 0.1% FA">Methanol / H2O (1/1), 0.1% FA</option><option value="Methanol / CH3CO2H (100/0.1)">Methanol / CH3CO2H (100/0.1)</option><option value="Methanol / ACN (60/40) ammonium acetate 2.5 mM">Methanol / ACN (60/40) ammonium acetate 2.5 mM</option></select>
+                              </div>
+                              <div class="form-group input-group  has-warning">
+                                <span class="input-group-addon">pH solvent A (if acqueous solvant)</span> 
+                                <input id="add1spectrum-chromatoLC-separationSolvApH-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-chromatoLCForm is-optional" placeholder="e.g. 7.0">
+                              </div>
+                              <div class="form-group input-group  has-error">
+                                <span class="input-group-addon">Separation solvent B</span> 
+                                <select id="add1spectrum-chromatoLC-separationSolvB-{{ TAB_INDEX_PLACEHOLDER }}" class="form-control add1spectrum add1spectrum-chromatoLCForm is-mandatory"><option value="" selected="selected" disabled="disabled">choose in list…</option><option value="5% ACN + 0.1% FA in H2O">5% ACN + 0.1% FA in H2O</option><option value="10% ACN + 0.1% FA in H2O">10% ACN + 0.1% FA in H2O</option><option value="10mM (NH4)2CO3 / ACN (20/80) (v/v)">10mM (NH4)2CO3 / ACN (20/80) (v/v)</option><option value="Acetonitrile">Acetonitrile</option><option value="Acetonitrile + 0.1% Formic Acid">Acetonitrile + 0.1% Formic Acid</option><option value="H2O + 0.1% Formic Acid">H2O + 0.1% Formic Acid</option><option value="H2O, ammonium carbonate 10 mM, pH10.5">H2O, ammonium carbonate 10 mM, pH10.5</option><option value="H2O, ammonium acetate 2.5 mM">H2O, ammonium acetate 2.5 mM</option><option value="H2O / CH3OH / CH3CO2H (95/5/0.1)">H2O / CH3OH / CH3CO2H (95/5/0.1)</option><option value="Methanol">Methanol</option><option value="Methanol / H2O (1/1)">Methanol / H2O (1/1)</option><option value="Methanol / H2O (1/1), 0.1% FA">Methanol / H2O (1/1), 0.1% FA</option><option value="Methanol / CH3CO2H (100/0.1)">Methanol / CH3CO2H (100/0.1)</option><option value="Methanol / ACN (60/40) ammonium acetate 2.5 mM">Methanol / ACN (60/40) ammonium acetate 2.5 mM</option></select>
+                              </div>
+                              <div class="form-group input-group  has-warning">
+                                <span class="input-group-addon">pH solvent B (if acqueous solvant)</span> 
+                                <input id="add1spectrum-chromatoLC-separationSolvBApH-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-chromatoLCForm is-optional" placeholder="e.g. 7.0">
+                              </div>
+                            </div>
+                          </div>
+                        </div>
+                        <div class="col-lg-6">
+                          <div class="panel panel-default">
+                            <div class="panel-heading">
+                              <h3 class="panel-title">Separation flow gradient</h3>
+                            </div>
+                            <div class="panel-body" style="padding: 0px;">
+                              <div id="container_LC_SFG-{{ TAB_INDEX_PLACEHOLDER }}" class="handsontable"></div>
+                            </div>
+                          </div>
+                        </div>
+                      </div>
+                      <div class="col-lg-12">
+                        <div class="col-lg-8">
+                          <br>
+                          <button id="btnSwitch-gotoStep3-lc-{{ TAB_INDEX_PLACEHOLDER }}" onclick="ctx().switchToStep(3);" class="btn btn-disabled switchStep" disabled=""><i class="fa fa-arrow-circle-down"></i> Next!</button>
+                        </div>
+                        <div class="col-lg-4">&nbsp;</div>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+                <div id="add1spectrum-chromatographyData-GC-{{ TAB_INDEX_PLACEHOLDER }}" class="panel panel-default" style="display: none;">
+                  <div class="panel-heading panel-success">
+                    <h4 class="panel-title">
+                      <a id="linkActivateStep2-gc-{{ TAB_INDEX_PLACEHOLDER }}" data-toggle="collapse" data-parent="#accordion-{{ TAB_INDEX_PLACEHOLDER }}" href="#step2-gc-{{ TAB_INDEX_PLACEHOLDER }}">
+                        Gaz Chromatography <i id="step2-gc-sign-{{ TAB_INDEX_PLACEHOLDER }}" class="fa fa-question-circle"></i>
+                      </a>
+                    </h4>
+                  </div>
+                  <div id="step2-gc-{{ TAB_INDEX_PLACEHOLDER }}" class="panel-collapse collapse">
+                    <div class="panel-body">
+                      <div class="col-lg-12">
+                        <div class="col-lg-6">
+                          i &lt;3 GC
+                        </div>
+                        <div class="col-lg-6">&nbsp;</div>
+                      </div>
+                      <div class="col-lg-12">
+                        <div class="col-lg-8">
+                          <br>
+                          <button id="btnSwitch-gotoStep3-gc-{{ TAB_INDEX_PLACEHOLDER }}" onclick="ctx().switchToStep(3);" class="btn btn-disabled switchStep" disabled=""><i class="fa fa-arrow-circle-down"></i> Next!</button>
+                        </div>
+                        <div class="col-lg-4">&nbsp;</div>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+                <div id="add1spectrum-analyserData-MS-{{ TAB_INDEX_PLACEHOLDER }}" class="panel panel-default" style="display: none;">
+                  <div class="panel-heading panel-success">
+                    <h4 class="panel-title">
+                      <a id="linkActivateStep3-ms-{{ TAB_INDEX_PLACEHOLDER }}" data-toggle="collapse" data-parent="#accordion-{{ TAB_INDEX_PLACEHOLDER }}" href="#step3-ms-{{ TAB_INDEX_PLACEHOLDER }}">
+                        MS Analyzer <i id="step3-ms-sign-{{ TAB_INDEX_PLACEHOLDER }}" class="fa fa-question-circle"></i>
+                      </a>
+                    </h4>
+                  </div>
+                  <div id="step3-ms-{{ TAB_INDEX_PLACEHOLDER }}" class="panel-collapse collapse">
+                    <div class="panel-body">
+                      <div class="col-lg-12">
+                        <div class="col-lg-6">
+                          <br>
+                          <div class="panel panel-default">
+                            <div class="panel-heading">
+                              <h3 class="panel-title">Analyzer</h3>
+                            </div>
+                            <div class="panel-body">
+                              <div class="form-group input-group  has-error">
+                                <span class="input-group-addon">Instrument</span> 
+                                <input id="add1spectrum-analyzer-ms-instrument-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-mandatory" placeholder="e.g. Q-TOF; LTQ - Orbitrap">
+                              </div>
+                              <div class="form-group input-group  has-warning">
+                                <span class="input-group-addon">Model</span> 
+                                <input id="add1spectrum-analyzer-ms-model-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional" placeholder="e.g. QToF micro (Micromass Waters); XL; Impact II; ...">
+                              </div>
+                              <div class="form-group">
+                                <div class="form-group input-group  has-error">
+                                  <span class="input-group-addon">Ion analyzer Type</span> 
+                                  <input id="add1spectrum-analyzer-ms-ionAnalyzerType-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-mandatory" placeholder="e.g. QTOF; QQQ; ...">
+                                </div>
+                                <p class="help-block">
+                                  <small>
+                                    Ion analyzer types are "B", "E", "FT" (include other types using FT like FTICR or Orbitrap), "IT", "Q", "TOF" (e.g.: "QTOF", "QQQ", "EB", "ITFT"); 
+                                    for further informations please refer to <a target="_BLANK" href="https://github.com/MassBank/MassBank-web/blob/main/Documentation/MassBankRecordFormat.md#212-record_title">MassBank Record documentation</a>.
+                                  </small>
+                                </p>
+                              </div>
+                            </div>
+                          </div>
+                        </div>
+                        <div class="col-lg-6">
+                          <br>
+                          <div class="panel panel-default">
+                            <div class="panel-heading">
+                              <h3 class="panel-title">Molecule Ionization</h3>
+                            </div>
+                            <div class="panel-body">
+                              <div class="form-group input-group has-error">
+                                <span class="input-group-addon">Ionization method <small>(POS/NEG)</small></span> 
+                                <select id="add1spectrum-analyzserMS-ionizationMethod-pos-{{ TAB_INDEX_PLACEHOLDER }}" style="max-width: 50%;" class="form-control add1spectrum add1spectrum-analyzerMSForm is-mandatory one-or-more">
+                                  <option value="" selected="selected" disabled="disabled">choose in list… (POS)</option>
+                                <option value="ACPI">ACPI</option><option value="APPI">APPI</option><option value="EI">EI</option><option value="ESI">ESI</option><option value="FAB">FAB</option><option value="MALDI">MALDI</option></select>
+                                <select id="add1spectrum-analyzserMS-ionizationMethod-neg-{{ TAB_INDEX_PLACEHOLDER }}" style="max-width: 50%;" class="form-control add1spectrum add1spectrum-analyzerMSForm is-mandatory one-or-more">
+                                  <option value="" selected="selected" disabled="disabled">choose in list… (NEG)</option>
+                                <option value="ACPI">ACPI</option><option value="APPI">APPI</option><option value="EI">EI</option><option value="ESI">ESI</option><option value="FAB">FAB</option><option value="MALDI">MALDI</option></select>
+                              </div>
+                              <div class="form-group input-group has-warning">
+                                <span class="input-group-addon">Spray (needle) gaz flow <br><small>(arbitrary in Xcalibur, POS/NEG)</small></span> 
+                                <input id="add1spectrum-analyzserMS-sprayGazFlow-pos-{{ TAB_INDEX_PLACEHOLDER }}" style="max-width: 50%;" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional" placeholder="e.g. 40 (POS)">
+                                <input id="add1spectrum-analyzserMS-sprayGazFlow-neg-{{ TAB_INDEX_PLACEHOLDER }}" style="max-width: 50%;" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional" placeholder="e.g. 20 (NEG)">
+                              </div>
+                              <div class="form-group input-group has-warning">
+                                <span class="input-group-addon">Vaporizer gaz flow <br><small>(arbitrary in Xcalibur, POS/NEG)</small></span> 
+                                <input id="add1spectrum-analyzserMS-vaporizerGazFlow-pos-{{ TAB_INDEX_PLACEHOLDER }}" style="max-width: 50%;" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional" placeholder="e.g. 10 (POS)">
+                                <input id="add1spectrum-analyzserMS-vaporizerGazFlow-neg-{{ TAB_INDEX_PLACEHOLDER }}" style="max-width: 50%;" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional" placeholder="e.g. 5 (NEG)">
+                              </div>
+                              <div class="form-group input-group has-warning">
+                                <span class="input-group-addon">Vaporizer temperature <br><small>(°C, POS/NEG)</small></span> 
+                                <input id="add1spectrum-analyzserMS-vaporizerTemperature-pos-{{ TAB_INDEX_PLACEHOLDER }}" style="max-width: 50%;" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional" placeholder="e.g. 330 (POS)">
+                                <input id="add1spectrum-analyzserMS-vaporizerTemperature-neg-{{ TAB_INDEX_PLACEHOLDER }}" style="max-width: 50%;" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional" placeholder="e.g. 330 (NEG)">
+                              </div>
+                              <div class="form-group input-group has-warning">
+                                <span class="input-group-addon">Source gaz flow <br><small>(arbitrary in Xcalibur, POS/NEG)</small></span> 
+                                <input id="add1spectrum-analyzserMS-sourceGazFlow-pos-{{ TAB_INDEX_PLACEHOLDER }}" style="max-width: 50%;" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional" placeholder="e.g. 40 (POS)">
+                                <input id="add1spectrum-analyzserMS-sourceGazFlow-neg-{{ TAB_INDEX_PLACEHOLDER }}" style="max-width: 50%;" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional" placeholder="e.g. 40 (NEG)">
+                              </div>
+                              <div class="form-group input-group has-warning">
+                                <span class="input-group-addon">Ion transfer tube temperature <br> Transfer capillary temperature <small>(°C, POS/NEG)</small></span> 
+                                <input id="add1spectrum-analyzserMS-ionTransferTubeTemperatureOrTransferCapillaryTemperature-pos-{{ TAB_INDEX_PLACEHOLDER }}" style="max-width: 50%;" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional" placeholder="e.g. 350 (POS)">
+                                <input id="add1spectrum-analyzserMS-ionTransferTubeTemperatureOrTransferCapillaryTemperature-neg-{{ TAB_INDEX_PLACEHOLDER }}" style="max-width: 50%;" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional" placeholder="e.g. 350 (NEG)">
+                              </div>
+                              <div class="form-group input-group has-warning">
+                                <span class="input-group-addon">High voltage (ESI) <br> Corona voltage (APCI) <small>(in kV, POS/NEG)</small></span> 
+                                <input id="add1spectrum-analyzserMS-highVoltageOrCoronaVoltage-pos-{{ TAB_INDEX_PLACEHOLDER }}" style="max-width: 50%;" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional" placeholder="e.g. 3.5 (POS)">
+                                <input id="add1spectrum-analyzserMS-highVoltageOrCoronaVoltage-neg-{{ TAB_INDEX_PLACEHOLDER }}" style="max-width: 50%;" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional" placeholder="e.g. 2.8 (NEG)">
+                              </div>
+                            </div>
+                          </div>
+                        </div>
+                        <div class="col-lg-6 opt-msms">
+                          <br>
+                          <div class="panel panel-default">
+                            <div class="panel-heading">
+                              <h3 class="panel-title">Ion Storage / Ion Beam</h3>
+                            </div>
+                            <div class="panel-body">
+                              <div class="form-group input-group  has-warning">
+                                <span class="input-group-addon">Type <small>(storage / beam)</small></span> 
+                                <select id="add1spectrum-ionTrapBeam-type-{{ TAB_INDEX_PLACEHOLDER }}" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional one-or-more">
+                                  <option value="" selected="selected" disabled="disabled">choose in list…</option>
+                                  <option value="trap">Ion Trap</option>
+                                  <option value="beam">Ion Beam</option>
+                                </select>
+                              </div>
+                              <p class="help-block">
+                                <small>
+                                  Ion storage: Ion Trap (IT) and ICR.
+                                  <br>Ion beam: Q or H collision Cell (QQQ, QQIT, QQ/TOF, Fusion). 
+                                </small>
+                              </p>
+                              <div class="form-group input-group  has-success">
+                                <span class="input-group-addon">Gas </span> 
+                                <select id="add1spectrum-ionTrapBeam-ionGas-{{ TAB_INDEX_PLACEHOLDER }}" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional ">
+                                  <option value="He">He</option>
+                                  <option value="N2">N2</option>
+                                  <option value="Ar">Ar</option>
+                                </select>
+                              </div>
+                              <div class="form-group input-group has-warning">
+                                <span class="input-group-addon">Gas pressure</span> 
+                                <input id="add1spectrum-ionTrapBeam-ionGasPressureValue-{{ TAB_INDEX_PLACEHOLDER }}" type="text" style="max-width: 50%;" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional ">
+                                <select id="add1spectrum-ionTrapBeam-ionGasPressureUnit-{{ TAB_INDEX_PLACEHOLDER }}" style="max-width: 50%;" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional ">
+                                  <option value="" selected="selected" disabled="disabled">choose in list…</option>
+                                  <option value="mbar">mbar</option>
+                                  <option value="au">a.u.</option>
+                                </select>
+                              </div>
+                              <div class="form-group input-group add1spectrum-ionTrap has-warning">
+                                <span class="input-group-addon">Frequency Shift <small>(KHz)</small></span> 
+                                <input id="add1spectrum-ionTrapBeam-ionFrequencyShift-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional" placeholder="e.g.: ...">
+                              </div>
+                              <div class="form-group input-group add1spectrum-ionTrap has-warning">
+                                <span class="input-group-addon">Ion Number <small>(AGC or ICC)</small></span> 
+                                <input id="add1spectrum-ionTrapBeam-ionNumber-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional" placeholder="e.g.: ...">
+                              </div>
+                            </div>
+                          </div>
+                        </div>
+                      </div>
+                      <div class="col-lg-12">
+                        <div class="col-lg-8">
+                          <br>
+                          <button id="btnSwitch-gotoStep4-ms-{{ TAB_INDEX_PLACEHOLDER }}" onclick="ctx().switchToStep(4);" class="btn btn-disabled switchStep" disabled=""><i class="fa fa-arrow-circle-down"></i> Next!</button>
+                        </div>
+                        <div class="col-lg-4">&nbsp;</div>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+                <div id="add1spectrum-peaksData-MS-{{ TAB_INDEX_PLACEHOLDER }}" class="panel panel-default" style="display: none;">
+                  <div class="panel-heading panel-success">
+                    <h4 class="panel-title">
+                      <a id="linkActivateStep4-ms-{{ TAB_INDEX_PLACEHOLDER }}" data-toggle="collapse" data-parent="#accordion-{{ TAB_INDEX_PLACEHOLDER }}" href="#step4-ms-{{ TAB_INDEX_PLACEHOLDER }}">
+                        MS Peaks <i id="step4-ms-sign-{{ TAB_INDEX_PLACEHOLDER }}" class="fa fa-question-circle"></i>
+                      </a>
+                    </h4>
+                  </div>
+                  <div id="step4-ms-{{ TAB_INDEX_PLACEHOLDER }}" class="panel-collapse collapse">
+                    <div class="panel-body">
+                      <div class="col-lg-12">
+                        <div class="form-group input-group col-lg-3 has-success">
+                          <span class="input-group-addon">scan type</span> 
+                          <select style="width: 150px;" id="add1spectrum-peaksMS-msLevel-{{ TAB_INDEX_PLACEHOLDER }}" class="form-control add1spectrum add1spectrum-peaksMSForm-peaklist add1spectrum-peaksMSForm-peaklist-reset add1spectrum-peaksMSForm is-mandatory">
+                            <option value="" disabled="disabled">choose in list…</option>
+                            <option class="enable-if-ms" value="ms" selected="selected">ms</option>
+                            <option class="enable-if-msms" value="ms2">ms2</option>
+                            <!-- <option class="enable-if-msms" value="ms3" disabled="disabled">ms3</option> -->
+                          </select>
+                        </div>
+                        <div class="form-group input-group col-lg-3 has-error">
+                          <span class="input-group-addon">polarity</span> 
+                          <select style="width: 150px;" id="add1spectrum-peaksMS-polarity-{{ TAB_INDEX_PLACEHOLDER }}" class="form-control add1spectrum add1spectrum-peaksMSForm-peaklist add1spectrum-peaksMSForm-peaklist-reset add1spectrum-peaksMSForm is-mandatory">
+                            <option value="" selected="selected" disabled="disabled">choose in list…</option>
+                            <option value="positive">positive</option>
+                            <option value="negative">negative</option>
+                          </select>
+                        </div>
+                        <div class="form-group input-group col-lg-3 has-error">
+                          <span class="input-group-addon">resolution</span> 
+                          <select style="width: 150px;" id="add1spectrum-peaksMS-resolution-{{ TAB_INDEX_PLACEHOLDER }}" class="form-control add1spectrum add1spectrum-peaksMSForm-peaklist add1spectrum-peaksMSForm-peaklist-reset add1spectrum-peaksMSForm is-mandatory">
+                            <option value="" disabled="disabled">choose in list…</option>
+                            <option value="low">low</option>
+                            <option selected="selected" value="high">high</option>
+                          </select>
+                        </div>
+                        <div class="form-group input-group col-lg-3 has-success">
+                          <span class="input-group-addon">curation</span> 
+                          <select style="width: 150px;" id="add1spectrum-peaksMS-curation-{{ TAB_INDEX_PLACEHOLDER }}" class="form-control add1spectrum add1spectrum-peaksMSForm-peaklist add1spectrum-peaksMSForm-peaklist-reset add1spectrum-peaksMSForm is-optional">
+                            <option value="no_curation" selected="selected">no curation</option>
+                            <option value="peaks_RI_sup_1percent">Peaks RI &gt; 1%</option>
+                            <option value="top_50_peaks">Top 50 peaks</option>
+                            <option value="top_20_peaks">Top 20 peaks</option>
+                            <option value="top_10_peaks">Top 10 peaks</option>
+                            <option value="similar_chromatographic_profile">Similar chromatographic profile</option>
+                          </select>
+                        </div>
+                      </div>
+                      <br>
+                      <div class="col-lg-12 opt-msms">
+                        <hr><!-- 
+                        <div class="form-group input-group col-lg-4 has-warning">
+                          <span class="input-group-addon">precursor spectrum</span> 
+                          <select style="width: 150px;" id="add1spectrum-peaksMS-msPrecursor-{{ TAB_INDEX_PLACEHOLDER }}" class="disabled-if-ms-in-msms form-control add1spectrum add1spectrum-peaksMSForm-peaklist add1spectrum-peaksMSForm-peaklist-reset add1spectrum-peaksMSForm is-optional">
+                            <option value="" disabled="disabled">choose in list…</option>
+                          </select>
+                        </div> -->
+
+                        <div class="form-group input-group col-lg-4 has-success">
+                          <span class="input-group-addon">Isolation mode <i class="fa fa-question-circle" title="IT / Q / TOF / ICR"></i></span> 
+                          <select id="add1spectrum-peaksMS-isolationMode-{{ TAB_INDEX_PLACEHOLDER }}" class="disabled-if-ms-in-msms form-control add1spectrum add1spectrum-peaksMSForm-peaklist add1spectrum-peaksMSForm is-optional add1spectrum-peaksMSForm-peaklist-reset">
+                            <option value="" disabled="disabled">choose in list…</option>
+                            <option value="IT">IT</option>
+                            <option value="Q">Q</option>
+                            <option value="TOF">TOF</option>
+                            <option value="ICR">ICR</option>
+                          </select>
+                        </div>
+                        <div class="form-group input-group col-lg-4 has-warning">
+                          <span class="input-group-addon">Isolation window <i class="fa fa-question-circle" title="(+ / - value)"></i></span> 
+                          <input id="add1spectrum-peaksMS-isolationWindow-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="disabled-if-ms-in-msms form-control add1spectrum add1spectrum-peaksMSForm-peaklist is-optional add1spectrum-peaksMSForm-peaklist-reset" placeholder="e.g. ...">
+                        </div>
+                        <div class="form-group input-group col-lg-4 has-warning">
+                          <span class="input-group-addon">qz isolation / activation <i class="fa fa-question-circle" title="if IT"></i> <small>(no unit)</small></span> 
+                          <input id="add1spectrum-peaksMS-qzIsolation-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="disabled-if-ms-in-msms form-control add1spectrum add1spectrum-peaksMSForm-peaklist is-optional add1spectrum-peaksMSForm-peaklist-reset" placeholder="e.g. ...">
+                        </div>
+                        <div class="form-group input-group col-lg-4 has-warning">
+                          <span class="input-group-addon">Activation time <i class="fa fa-question-circle" title="if FT-ICR (SORI-CID) or IT"></i> <small>(ms)</small></span> 
+                          <input id="add1spectrum-peaksMS-activationTime-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="disabled-if-ms-in-msms form-control add1spectrum add1spectrum-peaksMSForm-peaklist is-optional add1spectrum-peaksMSForm-peaklist-reset" placeholder="e.g. ...">
+                        </div>
+                        <div class="form-group input-group col-lg-4 has-success">
+                          <span class="input-group-addon">Mode <i class="fa fa-question-circle" title="HCD / CID / ECD /ETD"></i></span> 
+                          <select id="add1spectrum-peaksMS-mode-{{ TAB_INDEX_PLACEHOLDER }}" class="disabled-if-ms-in-msms form-control add1spectrum add1spectrum-peaksMSForm-peaklist add1spectrum-peaksMSForm is-optional ">
+                            <option value="" disabled="disabled">choose in list…</option>
+                            <option value="HCD">HCD</option>
+                            <option value="CID">CID</option>
+                            <option value="ECD">ECD</option>
+                            <option value="ETD">ETD</option>
+                          </select>
+                        </div>
+
+                        <div class="form-group input-group col-lg-4 has-warning">
+                          <span class="input-group-addon">Frag. energy <i class="fa fa-question-circle" title="without unit"></i> </span> 
+                          <input id="add1spectrum-peaksMS-frag-nrj-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="disabled-if-ms-in-msms form-control add1spectrum add1spectrum-peaksMSForm-peaklist is-optional" placeholder="e.g. ...">
+                        </div>
+                      </div>
+                      <div class="col-lg-12">
+                        <div class="form-group input-group col-lg-4 has-warning">
+                          <span class="input-group-addon">Resolution FWHM <small>(resolution@mass)</small></span> 
+                          <input id="add1spectrum-analyzer-ms-resolutionFWHM-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-peaksMSForm-peaklist is-optional" placeholder="e.g. 6500@1000">
+                        </div>
+                        <div class="form-group input-group col-lg-4 has-error">
+                          <span class="input-group-addon">m/z range <small>(ppm) from / to</small></span> 
+                          <input style="width: 100px;" id="add1spectrum-peaksMS-rangeFrom-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum-peaksMSForm-peaklist add1spectrum  is-mandatory" placeholder="50" value="{{ DEFAULT_MIN_MZ }}">
+                          <input style="width: 100px;" id="add1spectrum-peaksMS-rangeTo-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum-peaksMSForm-peaklist add1spectrum  is-mandatory" placeholder="500" value="{{ DEFAULT_MAX_MZ }}">
+                        </div>
+                        <div class="form-group input-group col-lg-4 has-error">
+                          <span class="input-group-addon">retention time (min) <small>from / to</small></span> 
+                          <input style="width: 100px;" id="add1spectrum-peaksMS-rtMinFrom-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum-peaksMSForm-peaklist add1spectrum  is-mandatory" placeholder="0.9" value="{{ DEFAULT_MIN_RT }}">
+                          <input style="width: 100px;" id="add1spectrum-peaksMS-rtMinTo-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum-peaksMSForm-peaklist add1spectrum  is-mandatory" placeholder="1.4" value="{{ DEFAULT_MAX_RT }}">
+                        </div>
+                      </div>
+                      <div class="col-lg-12">
+                        <div class="form-group input-group col-lg-4 has-warning">
+                          <span class="input-group-addon">retention time (% solvant) <small>from / to</small></span> 
+                          <input style="width: 100px;" id="add1spectrum-peaksMS-rtSolvFrom-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum-peaksMSForm-peaklist add1spectrum  is-optional" placeholder="??">
+                          <input style="width: 100px;" id="add1spectrum-peaksMS-rtSolvTo-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum-peaksMSForm-peaklist add1spectrum  is-optional" placeholder="??">
+                        </div>
+                        <div class="col-lg-4">&nbsp;</div>  
+                      </div>
+                      <div class="col-lg-12">
+                        <div class="col-lg-11">
+                          <br>
+                          <ul class="nav nav-tabs" style="margin-bottom: 15px;">
+                            <li class="active"><a id="link-spectrum-peaklist-lcms-{{ TAB_INDEX_PLACEHOLDER }}" href="#spectrum-peaklist-lcms-{{ TAB_INDEX_PLACEHOLDER }}" data-toggle="tab"><i class="fa fa-table"></i> Peak List</a></li>
+                            <li class=""><a id="link-spectrum-preview-lcms-{{ TAB_INDEX_PLACEHOLDER }}" href="#spectrum-preview-lcms-{{ TAB_INDEX_PLACEHOLDER }}" onclick="ctx().updateLCMSspectraViewer()" data-toggle="tab"><i class="fa fa-bar-chart-o"></i> Spectrum Preview</a></li>
+                          </ul>
+                          <div id="spectrum-signal-data-lcms-{{ TAB_INDEX_PLACEHOLDER }}" class="tab-content" style="">
+                            <div class="tab-pane fade active in" id="spectrum-peaklist-lcms-{{ TAB_INDEX_PLACEHOLDER }}">
+                              <div class="panel-body" style="padding: 0px;">
+                                <div id="container_MS_Peaks-{{ TAB_INDEX_PLACEHOLDER }}" class="handsontable col-lg-6" style="display: inline-block;"></div>
+
+                                <div class="form-group input-group col-lg-3 has-warning hidden-if-ms-in-msms">
+                                  <span class="input-group-addon">Precursor ion <small>(M/Z)</small><i class="fa fa-question-circle" title="2 digits of precision."></i></span>
+                                  <input id="add1spectrum-peaksMS-msPrecursorIon-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-peaksMSForm-peaklist is-mandatory add1spectrum-peaksMSForm-peaklist-reset enabled-if-ms-in-msms" placeholder="e.g. 123.45">
+                                </div>
+                              </div>
+                            </div>
+                            <div class="tab-pane fade" id="spectrum-preview-lcms-{{ TAB_INDEX_PLACEHOLDER }}">
+                              <div id="containter-lcms-spectrum-preview-{{ TAB_INDEX_PLACEHOLDER }}" style="width: 80%;"></div>
+                            </div>
+                          </div>
+                        </div>
+                      </div>
+                      <div class="col-lg-12">
+                        <div class="col-lg-8">
+                          <button class="btn btn-success" onclick="$('#add1spectrum-peaksMS-msPrecursorIon-{{ TAB_INDEX_PLACEHOLDER }}').change()">check</button>
+                          <button id="btnSwitch-gotoStep5-ms-{{ TAB_INDEX_PLACEHOLDER }}" onclick="ctx().switchToStep(5);" class="btn switchStep btn-primary"><i class="fa fa-arrow-circle-down"></i> Next!</button>
+                          <br>
+                          <br>
+                        </div>
+                        <div class="col-lg-4">&nbsp;</div>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+                <div id="add1spectrum-otherData-{{ TAB_INDEX_PLACEHOLDER }}" class="panel panel-default" style="display: none;">
+                  <div class="panel-heading panel-success">
+                    <h4 class="panel-title">
+                      <a id="linkActivateStep5-{{ TAB_INDEX_PLACEHOLDER }}" data-toggle="collapse" data-parent="#accordion-{{ TAB_INDEX_PLACEHOLDER }}" href="#step5-{{ TAB_INDEX_PLACEHOLDER }}">
+                        Other <i id="step5sign-{{ TAB_INDEX_PLACEHOLDER }}" class="fa fa-question-circle"></i>
+                      </a>
+                    </h4>
+                  </div>
+                  <div id="step5-{{ TAB_INDEX_PLACEHOLDER }}" class="panel-collapse collapse">
+                    <div class="panel-body">
+                      <div class="col-lg-12">
+                        <div class="col-lg-6">
+                          <div class="panel panel-default">
+                            <div class="panel-heading">
+                              <h3 class="panel-title">Ownership</h3>
+                            </div>
+                            <div class="panel-body">
+                              <div class="form-group input-group  has-error">
+                                <span class="input-group-addon">data author(s)</span> 
+                                <input id="add1spectrum-other-author-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-otherForm is-mandatory" placeholder="enter your lab. / plateforme name" value="">
+                              </div>
+                              <div class="form-group input-group  has-warning">
+                                <span class="input-group-addon">data validator(s)</span> 
+                                <input id="add1spectrum-other-validator-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-otherForm is-optional" placeholder="name of the personne who checked all data in this file">
+                              </div>
+                              <div class="form-group input-group  has-warning">
+                                <span class="input-group-addon">acquisition date</span> 
+                                <input id="add1spectrum-other-date-{{ TAB_INDEX_PLACEHOLDER }}" data-date-format="yyyy-mm-dd" type="text" class="form-control add1spectrum  add1spectrum-otherForm datepicker is-optional" value="" placeholder="2022-11-04">
+                              </div>
+                              <div class="form-group input-group  has-warning">
+                                <span class="input-group-addon">data ownership</span> 
+                                <input id="add1spectrum-other-owner-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-otherForm is-optional" placeholder="enter your lab. / plateforme name &amp; sample provider;">
+                              </div>
+                            </div>
+                          </div>
+                        </div>
+                        <div class="col-lg-6">
+                          <div class="panel panel-default">
+                            <div class="panel-heading">
+                              <h3 class="panel-title">Raw File</h3>
+                            </div>
+                            <div class="panel-body">
+                              <div class="form-group input-group  has-warning">
+                                <span class="input-group-addon">raw file name</span> 
+                                <input id="add1spectrum-other-fileName-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-otherForm is-optional" placeholder="needed to retrieve file later">
+                              </div>
+                              <div class="form-group input-group  has-warning">
+                                <span class="input-group-addon">raw file size (Ko)</span> 
+                                <input id="add1spectrum-other-fileSize-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-otherForm is-optional" placeholder="optional, to check if the file is correct">
+                              </div>
+                            </div>
+                          </div>
+                        </div>
+                      </div>
+                      <div class="col-lg-12">
+                        <div class="col-lg-8">
+                          <br>
+                          <button id="btnSwitch-gotoStep6-{{ TAB_INDEX_PLACEHOLDER }}" onclick="ctx().switchToStep(6);" class="btn btn-disabled switchStep" disabled=""><i class="fa fa-arrow-circle-right"></i> Validate this compound/mix!</button>
+                          <!-- <button  onclick="enable_tab({{ TAB_INDEX_PLACEHOLDER }})" class="btn btn-success">
+                            <i class="fa fa-arrow-circle-right"></i>Modify this compound/mix!
+                          </button> -->
+                          <span id="import1SpectrumLoadingBare-{{ TAB_INDEX_PLACEHOLDER }}" style="display: none;"><img src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/img/ajax-loader.gif" title="please wait"></span>
+                          <span id="import1SpectrumResults-{{ TAB_INDEX_PLACEHOLDER }}" style="display: none;">
+                            <button id="btnSwitch-view-{{ TAB_INDEX_PLACEHOLDER }}" data-toggle="modal" data-target="#modalShowSpectra-{{ TAB_INDEX_PLACEHOLDER }}" class="btn btn-success"><i class="fa fa-eye"></i> View spectrum</button>
+                            <button id="btnSwitch-returntoStep3-{{ TAB_INDEX_PLACEHOLDER }}" onclick="ctx().switchToStep(3);" class="btn btn-primary"><i class="fa fa-arrow-circle-up"></i> Add new peaklist!</button>
+                          </span>
+                        </div>
+                        <div class="col-lg-4"><br><br><br></div>
+                      </div>
+                      <div class="col-lg-12">
+                        <div class="col-lg-8">
+                          <div id="alertBoxSubmitSpectrum-{{ TAB_INDEX_PLACEHOLDER }}" class="col-lg-6"></div>
+                        </div>
+                        <div class="col-lg-4">&nbsp;</div>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+              <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/handsontable/dist/handsontable.full.min.js"></script>
+              <link rel="stylesheet" media="screen" href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/handsontable/dist/handsontable.full.min.css">
+              <link rel="stylesheet" media="screen" href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/handsontable/bootstrap/handsontable.bootstrap.min.css">
+            </div>
+            <div class="modal" id="modalPickCompound-{{ TAB_INDEX_PLACEHOLDER }}" tabindex="-1" role="dialog" aria-labelledby="modalPickCompoundLabel-{{ TAB_INDEX_PLACEHOLDER }}" aria-hidden="true">
+              <div class="modal-dialog">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+                    <h4 class="modal-title" id="modalPickCompoundLabel-{{ TAB_INDEX_PLACEHOLDER }}">Pick a compound</h4>
+                  </div>
+                  <div class="modal-body">
+                    <div class="form-group input-group">
+                      <span class="input-group-addon">Compound Name</span>
+                      <input id="add-one-cc-s1-value-{{ TAB_INDEX_PLACEHOLDER }}" class="form-control" placeholder="e.g. Caffeine" type="text">
+                      <span class="input-group-btn">
+                        <button class="btn btn-default" type="button" onclick="ctx().searchLocalCompound();">
+                          <i class="fa fa-search"></i>
+                        </button>
+                      </span>
+                    </div>
+                    <div id="load-step-1-{{ TAB_INDEX_PLACEHOLDER }}" style="display: none;">
+                      <img src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/img/ajax-loader.gif" title="please wait">
+                    </div>
+                    <div id="ok-step-1-{{ TAB_INDEX_PLACEHOLDER }}" style="overflow: auto; max-height: 300px;"></div>
+                  </div>
+                  <div class="modal-footer">
+                    <button type="button" class="btn btn-danger" data-dismiss="modal" onclick="ctx().clearLine()">
+                      <i class="fa fa-eraser"></i> Clear
+                    </button>
+                    <button type="button" class="btn btn-default" data-dismiss="modal">Fermer</button>
+                  </div>
+                </div>
+              </div>
+            </div>
+            <div class="modal " id="modalShowSpectra-{{ TAB_INDEX_PLACEHOLDER }}" tabindex="-1" role="dialog" aria-labelledby="modalShowSpectraLabel-{{ TAB_INDEX_PLACEHOLDER }}" aria-hidden="true">
+              <div class="modal-dialog">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+                    <h4 class="modal-title" id="modalShowSpectraLabel-{{ TAB_INDEX_PLACEHOLDER }}">Modal title</h4>
+                  </div>
+                  <div class="modal-body">
+                    <div class="te"></div>
+                  </div>
+                  <div class="modal-footer">
+                  </div>
+                </div>
+              </div>
+            </div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/config.yml	Fri Mar 03 14:10:24 2023 +0000
@@ -0,0 +1,250 @@
+##
+## when you see {{ something }} in a string, this means the string is
+## a template, and the "{{ something }}" will be replaced by its value
+## at runtime.
+## This is usefull to build strings from other strings.
+## to reference an item in a tree, you can use dots as branches:
+##  {{ parameters.flags.help }}
+## will be extrapolated to "Show this help"
+## 
+## But beware:
+##  {{ parameters.mandatory }}
+## will be extrapolated to
+##  "{'input': 'input file path'}"
+##
+
+## this config is used for debug.
+## it allows to define debug options before everything else is parsed
+__meta_config__:
+  __debug__: False
+  __debug_stream__: stderr
+  __only_root_debug__: False
+
+
+## the cli parameters
+parameters:
+  mandatory:
+    ## input is mandatory
+    input: input file path
+  flags:
+    ## help, verbose and version can be provided with no parameter.
+    help: Show this help
+    verbose: More verbose outputs
+    version: Show this tool's version
+    debug: show debug outputs
+    do_run_dry: Runs the whole process, without the server. Usefull for tests.
+    embed_js: Embed js in html file instead of using a separated js file.
+    firefox: Open firefox on the web page.
+    chromium: Open chromium or chrome on the web page.
+  optional:
+    ## these optional parameters need a value "--opt value --opt2 value2"
+    method: "default is {{ defaults.method }}"
+    spectrum_type: "default is {{ defaults.spectrum_type }}"
+    sample_type: "default is {{ defaults.sample_type }}"
+    resolution: "default is {{ defaults.resolution }}"
+    resolution: "default is {{ defaults.resolution }}"
+    name: The precursor name
+    peakforest.url: "default={{ defaults.peakforest.url }}"
+    peakforest.token: The token to comunicate with peakforest api
+    polarity: positive|negative
+    raw_metadata: example - 1-1__INCHIKEY__=QNAYBMKLOCPYGJ-REOHCLBHSA-N_L-Alanine_MS_POS_plasma_RT__=0.84_filtree.csv
+    scan_type: ms|msms|rmn
+    run_dry_html: when "--run-dry", provides the output directory for {{ generated.html }}
+    run_dry_js: when "--run-dry", provides the output directory for {{ generated.js }}
+    raw_metadata_sep: "raw metadata separator"
+    logging.std: "Either out, err, or anything else to not output"
+    logging.file.path: "The file path to output logs to"
+    validation: "Set the default validation to the provided value, TRUE or FALSE."
+    output_json: "Provide a path to output a JSON file."
+  meta:
+    ## meta info about the tool itself
+    author: Lain Pavot
+    version: 1.1.0
+  shortcuts:
+    ## to define things like: "we can use -p instead of --polarity"
+    peakforest.token: t
+    polarity: p
+    help: h
+    version: v
+    verbose: V
+    debug: d
+    logging.std: l
+
+## some default parameters
+## logging defines two elements: std and file.
+## std should be either empty, err or out.
+## it tells where to outputs logs:
+##   - nowhere (empty value)
+##   - in sterr (err)
+##   - in stdout (out)
+## and file. Id a path is provided, add outputs to this file.
+## if append is true, outputs are appended to the file.
+## otherwise, the file is emptied each time the app runs.
+defaults:
+  peakforest:
+    url: https://nightly.peakforest.org
+    token: ''
+  spectrum_type: LC_MSMS
+  method: cf_pfem_urine_method1_qtof-msms
+    # api-msms-fia__idf-cea
+    # cf_pfem_plasma_method1_qtof-msms
+    # cf_pfem_urine_method1_qtof-msms
+    # lc-msms__test
+  scan_type: ms2
+  resolution: high
+  sample_type: compound-ref
+  # sample_type: compound-mix
+  polarity: positive
+  raw_metadata: ''
+  name: ''
+  run_dry_html:
+  run_dry_js:
+  raw_metadata_sep: ','
+  validation: "TRUE"
+  verbose: false
+  debug: true
+  logging: 
+    std: out ## out || err
+    file: 
+      path:
+      append: False
+  output_json: ''
+
+## token related info
+token:
+  ## do you use a file to store your token?
+  use_file: false
+  ## if so, what path the file is located at?
+  file_path: .token
+  ## if not, you can provide you token here
+  value:
+
+network:
+  ip: 0.0.0.0
+  port: 8000
+
+workdir:
+  ## create a tmp directory
+  create_tmp: true
+  ## works in the created tmp directory, or if not created, in /tmp
+  work_in_tmp: true
+  ## generate outputs in the created tmp directory, or if not created, in /tmp
+  generate_in_tmp: true
+
+## the templates paths
+templates:
+  ## meta is what wrapps the whole page.
+  main: meta.html
+  main_mix: meta-compound-mix.html
+  main_ref: meta-compound-ref.html
+  ## form is one instance of a pf form
+  # form: form.html
+  # form: compound-ref.html
+  form: form.html
+  form_mix: compound-mix.html
+  form_ref: compound-ref.html
+  ## one item of the tab list
+  tab_list: tab_list.html
+  ## the js for one form
+  js: add-one-spectrum-index.js
+  ## placeholders for the html templates.
+  ## this will not change the placeholders syntax for this file.
+  placeholders:
+    start: "{{ "
+    stop: " }}"
+
+generated:
+  ## what filename to give to the whole html file
+  html: pf.html
+  ## what filename to give to js files
+  js: add-one-spectrum-{{ index }}.js
+
+regex:
+  values:
+    ## reuse these smol regex in bigger regex!
+    spectrum_type: "NMR|LC_MS|LC_MSMS|MRM(_\\d+)?"
+    matrix: "[Uu]rine|[Pp]lasma|[Pp]las"
+    mode: "POS|NEG"
+    energy: "\\d+ev"
+    pool: "[Pp]ool\\d+"
+    rt: "\\d+\\.\\d+"
+    runs: "\\d+-\\d+"
+    inchikey: "[A-Z]{14}-[A-Z]{10}-[A-Z]"
+
+  ## the "INCHIKEY" word
+  ## one or more underscores
+  ## may be followed by a equal sign
+  ## captures in the "inchikey" variable:
+  ##    14, 10 and 1 letters
+  ##    in caps
+  ##    separated by a hyphen
+  inchikey: "INCHIKEY_+=?(?P<inchikey>{{ regex.values.inchikey }})"
+
+  ## method is one of values defined bellow, between underscores
+  spectrum_type: "_(?P<method>{{ regex.values.method }})_"
+
+  ## matrix is one of values defined bellow, between underscores
+  matrix: "_(?P<matrix>{{ regex.values.matrix }})_"
+
+  ## pool is one of values defined bellow, between underscores
+  pool: "_(?P<pool>{{ regex.values.pool }})_"
+
+  ## molecule everything between inchikey and method.
+  molecule: "{{ regex.inchikey }}_(?P<molecule>.*?){{ regex.method }}"
+
+  ## mode is one of values defined bellow, between underscores
+  mode: "_(?P<mode>{{ regex.values.mode }})_"
+
+  ## energy is one of values defined bellow, between underscores
+  energy: "_(?P<energy>{{ regex.values.energy }})_"
+
+  ##  - A underscore,
+  ##  - the "RT" word,
+  ##  - some underscore(s),
+  ##  - an optional interrogation mark,
+  ##  - the actual RT value is captured here,
+  ##  - there is a underscore at the end
+  rt: "_RT_+=?(?P<rt>{{ regex.values.rt }})_"
+
+  ## "runs" is at the beginning, and ends with an underscore.
+  runs: "^(?P<runs>{{ regex.values.runs }})_"
+
+
+  ## BEGIN
+  ## anything
+  ## the inchikey
+  ## anything else can follow
+  ## END
+  # fragnot: ^.*{{ regex.inchikey }}.*$
+
+
+  ## This is the most exhaustive regex I came with to extract info from
+  ## fragnot files name.
+  # fragnot:
+  #   ^
+  #   .*                                (?# there may be anything at the begining)
+  #   (?P<runs>\d+-\d+)                 (?# the run numbers)
+  #   _+                                (?# followed by anything - underscores usualy)
+  #   INCHIKEY_+=?(?P<inchikey>{{ regex.inchikey }})              (?# we insert inchikey regex here - see above)
+  #   _+                                (?# there is one or more underscores)
+  #   (?P<molecule>.*?)                 (?# the molecule name follows the inchikey)
+  #   _+                                (?# there is one or more underscores)
+  #   (?P<method>{{ regex.method }})    (?# then, there is the method - nmr, ms or mrm???)
+  #   _+                                (?# there may be some underscores to separate)
+  #   (?P<matrix>{{ regex.matrix }})?   (?# the matrix - urine or plasma)
+  #   _*                                (?# there may be some underscores to separate)
+  #   (?P<mode>POS|NEG)                 (?# the acq mode - pos or neg)
+  #   _+                                (?# there may be some underscores to separate)
+  #   (?P<pool>{{ regex.pool }})?       (?# the pool - "Pool"+numbers, optional)
+  #   _*                                (?# there may be some underscores to separate)
+  #   (?P<energy>\d+ev)?                (?# the energy - only for nmr, so it's optional)
+  #   _*                                (?# there may be some underscores to separate)
+  #   (?P<pool2>{{ regex.pool }})?      (?# the pool - sometimes the pool if *after* the energy...)
+  #   _*                                (?# there may be some underscores to separate)
+  #   (?P<matrix2>{{ regex.matrix }})?  (?# the matrix is here, sometimes)
+  #   _*                                (?# there may be anything here)
+  #   RT_+=?(?P<rt>\d+\.\d+)            (?# the retension time is a decimal number)
+  #   _*                                (?# there may be anything at the end)
+  #   (?P<filtered>[fF]iltree)?         (?# sometimes, "Filtree" appears, lets capture it)
+  #   \.[ct]sv                          (?# the extension)
+  #   $
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/macros.xml	Fri Mar 03 14:10:24 2023 +0000
@@ -0,0 +1,202 @@
+<macros>
+    <token name="@TOOL_VERSION@">1.1.0</token>
+    <token name="@MS2PF_COMMON_CMD@">
+        <![CDATA[
+python3 '$__tool_directory__/server.py'
+    --input '${','.join(map(str, $input))}'
+    --raw-metadata '${$raw_metadata_sep.join(str(i.name) for i in $input)}'
+    --raw-metadata-sep '$raw_metadata_sep'
+    --method '$method'
+    --peakforest-url '$peakforest_url'
+    --peakforest-token '$peakforest_token'
+    --spectrum-type '$spectrum_type'
+    --sample-type '$sample_type'
+    --polarity '$polarity'
+    --name '$name'
+    #if $do_output_json:
+    --output-json '$json_output'
+    #end if
+    $advenced.verbose
+    $advenced.debug
+        ]]>
+    <!-- -\-logging-file-path '$ms2pfout' -->
+    </token>
+    <xml name="ms2pf_inputs">
+        <param argument="--input" type="data" multiple="true" format="tabular,csv,tsv"
+            label="list of files to process"
+            help="A data collection from fragnot or a single file from ms2snoop."
+        />
+        <param argument="--peakforest-url" type="select"
+            label="PeakForest instance"
+            help="Determines on which pf instance we push data"
+            refresh_on_change="true"
+        >
+            <option value="https://alpha.peakforest.org">alpha</option>
+            <option value="https://nightly.peakforest.org">nightly</option>
+            <option value="https://metabohub.peakforest.org">metabohub</option>
+        </param>
+        <param argument="--peakforest-token" type="text"
+            label="PeakForest token"
+            help="Please provide you api token for this instance"
+        />
+        <param argument="--spectrum-type" type="select"
+            label="Spectrum Type"
+            help="Spectrum Type"
+            refresh_on_change="true"
+        >
+            <option value="LC_MS">LC-MS</option>
+            <option value="LC_MSMS">LC-MSMS</option>
+            <option value="GC_MS">GC-MS</option>
+        </param>
+
+        <param argument="--method" type="select"
+            label="Chromatography Method"
+            dynamic_options="[list(x.values())+[False] for x in __import__('json').loads(__import__('urllib').request.urlopen(peakforest_url+'/webapp/resources/json/list-'+ ('lc' if spectrum_type == 'LC_MS' else 'lc-msms') +'-methods.json?token='+peakforest_token).read())['methods']]"
+        >
+        </param>
+
+        <param argument="--sample-type" type="select"
+            label="Sample Type"
+            help="Sample Type"
+        >
+            <option value="compound-ref">Compound Ref</option>
+            <option value="compound-mix">Compound Mix</option>
+        </param>
+        <param argument="--polarity" type="select"
+            label="polarity"
+            help="polarity"
+        >
+            <option value="positive">positive</option>
+            <option value="negative">negative</option>
+            <option value="neutral">neutral</option>
+        </param>
+        <param argument="--name" type="text"
+            label="name"
+            help="name, or names separated by commas"
+        />
+        <param argument="--raw-metadata-sep" type="text" value="XXX"
+            label="Files names separator"
+            help="Select a/some character·s that are not in any file name."
+        />
+        <param name="do_output_json" type="boolean"
+            label="Produce JSON"
+            help="
+                Select this option to output a json file of the
+                resulting processing.
+            "
+        />
+        <section name="advenced">
+            <param argument="--debug" type="boolean" truevalue="--debug" falsevalue=""
+                label="Activate debug logs"
+                help="Select to produce more detailed logs."
+            />
+            <param argument="--verbose" type="boolean" truevalue="--verbose" falsevalue=""
+                label="Activate verbose logs"
+                help="Select to produce more logs."
+            />
+        </section>
+    </xml>
+
+    <xml name="ms2pf_help">
+<![CDATA[
+.. class:: infomark
+
+**Authors** Lain Pavot (lain.pavot@inrae.fr)
+
+.. class:: infomark
+
+---------------------------------------------------
+
+==============
+MS2 validation
+==============
+
+-----------
+Description
+-----------
+
+
+-----------------
+Workflow position
+-----------------
+
+--------------
+Upstream tools
+--------------
+
++-------------------------+-----------------+--------+------------+
+| Name                    |  output file    | format |  parameter |
++=========================+=================+========+============+
+| ms2snoop                |       tsv       |   TSV  |    NA      |
++-------------------------+-----------------+--------+------------+
+| abinitfragnot           |       tsv       |   TSV  |    NA      |
++-------------------------+-----------------+--------+------------+
+
+----------------
+Downstream tools
+----------------
+
++-------------------------+-----------------+--------+------------+
+| Name                    |  output file    | format |  parameter |
++=========================+=================+========+============+
+| NA                      |       NA        |   NA   |    NA      |
++-------------------------+-----------------+--------+------------+
+
+
+-----------
+Input files
+-----------
+
++----------------------------------------------+------------+
+| Parameter : num + label                      |   Format   |
++===========================+==================+============+
+| Output from ms2snoop or fragnot              |   TSV,CSV  |
++----------------------------------------------+------------+
+
+----------
+Parameters
+----------
+
+input
+  | The file to process. Should contain spectra with mz, intensities and
+  annotations
+
+method
+  | The method parameter tells which method was used. The value will
+    be the default value in the peakforest form.
+
+peakforest-url
+  | The peakforest-url parameter tells which instance of peakforest to
+    interact with
+
+scan_type
+  | The scan_type parameter which kind of scan was used. The value will
+    be the default value in the peakforest form.
+
+polarity
+  | The polarity parameter which polarity was used. The value will
+    be the default value in the peakforest form.
+
+name
+  | The name parameter provides names each spectra.
+
+
+------------
+Output files
+------------
+
+There is not output file. When you run this tool, keep waiting until the
+« click here to display » link appears.
+
+Click on the link to display peakforest form.
+
+--------------
+Changelog/News
+--------------
+
+1.0.0
+  | First version, published on the toolshed.
+
+]]>
+    </xml>
+</macros>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/meta-compound-mix.html	Fri Mar 03 14:10:24 2023 +0000
@@ -0,0 +1,1272 @@
+<!doctype HTML>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta name="author" content="MetaboHUB">
+    <link rel="icon" type="image/ico" href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/ico/favicon.ico">
+    <title>PeakForest MetaboHub</title>
+    <meta name="keywords" content="spectral database, mass spectrometry, nmr, lc-ms, gc-ms, chemical, metabolomic, compound, library">
+    <meta name="description" content="PeakForest is a spectral data portal for Metabolomics community. It provides storage and annotation services for metabolic profils of biological matrix. It relies on the wide range of complementary methods using UPLC-(API)HRMS, GC-QToF, and NMR.">
+    <style>.file-input-wrapper { overflow: hidden; position: relative; cursor: pointer; z-index: 1; }.file-input-wrapper input[type=file], .file-input-wrapper input[type=file]:focus, .file-input-wrapper input[type=file]:hover { position: absolute; top: 0; left: 0; cursor: pointer; opacity: 0; filter: alpha(opacity=0); z-index: 99; outline: 0; }.file-input-name { margin-left: 8px; }</style>
+    <link href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/css/bootstrap.min.css" rel="stylesheet">
+    <link href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/css/sb-admin.min.css" rel="stylesheet">
+    <link rel="stylesheet" href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/font-awesome/css/font-awesome.min.css">
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/jquery.min.js"></script>
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/bootstrap.min.js"></script>
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/bootstrap-typeahead.min.js"></script>
+    <link href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/css/bootstrap-select.min.css" media="screen" rel="stylesheet" type="text/css">
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/bootstrap-select.min.js" type="text/javascript"></script>
+    <link href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/css/bootstrap-datepicker.min.css" media="screen" rel="stylesheet" type="text/css">
+    <script type="text/javascript" src="/common.js"></script>
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/bootstrap-datepicker.min.js" type="text/javascript"></script>
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/jquery.tmpl.min.js" type="text/javascript"></script>
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/highcharts/js/highcharts.min.js"></script>
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/highcharts/js/modules/exporting.min.js"></script>
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/highcharts/js/themes/grid.min.js"></script>
+    <link href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/css/bootstrap.overwrite.min.css" rel="stylesheet" media="screen">
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/peakforest.min.js"></script>
+    <script type="text/javascript" src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/tablesorter/jquery.tablesorter.min.js"></script>
+    <script type="text/javascript" src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/tablesorter/tables.min.js"></script>
+    <link rel="stylesheet" href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/nmrpro/specdraw.min.css" type="text/css">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+  </head>
+  <body>
+    <div id="page-wrapper" class="peakforest-main-wrapper">
+      <div>
+        <button class="btn btn-validate" onclick="ctx().exit()">Close MS2PF</button>
+      </div>
+      <div class="row" style="margin-bottom: 50px;">
+        <div id="add-spectrum">
+          <div class="panel-group" id="accordion-{{ TAB_INDEX_PLACEHOLDER }}">
+            <div class="panel panel-default">
+              <div class="panel-heading panel-success">
+                <h4 class="panel-title">
+                  <a
+                    data-toggle="collapse"
+                    data-parent="#accordion-{{ TAB_INDEX_PLACEHOLDER }}"
+                    href="#step0-{{ TAB_INDEX_PLACEHOLDER }}"
+                  >
+                    Spectrum Type
+                    <i
+                      id="step0sign-{{ TAB_INDEX_PLACEHOLDER }}"
+                      class="fa fa-question-circle"
+                    ></i>
+                  </a>
+                </h4>
+              </div>
+              <div
+                id="step0-{{ TAB_INDEX_PLACEHOLDER }}"
+                class="panel-collapse collapse in"
+              >
+                <div class="panel-body">
+                  <button
+                    class="btn btn-disabled"
+                    disabled="disabled"><i
+                    class="fa fa-plus-circle"
+                  ></i> GC-MS spectrum</button>
+                  <button
+                    id="set_LC_MS_spectrum_button"
+                    class="btn btn-primary"
+                    onclick="ctx().addOneSpectrum(2);"><i
+                    class="fa fa-plus-circle"
+                  ></i> LC-MS spectrum</button>
+                  <button
+                    id="set_LC_MSMS_spectrum_button"
+                    class="btn btn-primary"
+                    onclick="ctx().addOneSpectrum(5);"><i
+                    class="fa fa-plus-circle"
+                  ></i> LC-MSMS spectrum</button>
+                </div>
+              </div>
+            </div>
+            <div
+              id="add1spectrum-sampleData-{{ TAB_INDEX_PLACEHOLDER }}"
+              class="panel panel-default"
+              style="display: none;">
+              <div
+                class="panel-heading panel-success">
+                <h4
+                  class="panel-title">
+                  <a
+                    id="linkActivateStep1-{{ TAB_INDEX_PLACEHOLDER }}"
+                    data-toggle="collapse"
+                    data-parent="#accordion-{{ TAB_INDEX_PLACEHOLDER }}"
+                    href="#step1-{{ TAB_INDEX_PLACEHOLDER }}"
+                  >
+                    Sample Type
+                    <i
+                      id="step1sign-{{ TAB_INDEX_PLACEHOLDER }}"
+                      class="fa fa-question-circle"
+                    ></i>
+                  </a>
+                </h4>
+              </div>
+              <div
+                id="step1-{{ TAB_INDEX_PLACEHOLDER }}"
+                class="panel-collapse collapse"
+              >
+                <div class="panel-body">
+                  <div class="col-lg-12">
+                    <div class="col-lg-8">
+                      <div class="panel panel-default">
+                        <div class="panel-heading">
+                          <div class="form-group input-group  has-error">
+                            <span class="input-group-addon">
+                              Sample Type
+                            </span>
+                            <select
+                              id="add1spectrum-sample-type-{{ TAB_INDEX_PLACEHOLDER }}"
+                              class="form-control add1spectrum add1spectrum-sampleForm is-mandatory">
+                              <option
+                                value=""
+                                selected="selected"
+                                disabled="disabled">choose in list…</option>
+                              <option value="compound-mix">
+                                Mix of Ref. Chemical Compound
+                              </option>
+                            </select>
+                          </div>
+                        </div>
+                        <div class="panel-body">
+                          <div
+                            id="add1spectrum-sample-type-compound-mix-{{ TAB_INDEX_PLACEHOLDER }}"
+                            class="add1spectrum-sample-type-panel"
+                            style="display:none;">
+                            <div class="form-group input-group  has-warning">
+                              <span class="input-group-addon">solvent</span>
+                              <select
+                                id="add1spectrum-sample-mixSolvent-{{ TAB_INDEX_PLACEHOLDER }}"
+                                class="form-control add1spectrum add1spectrum-sampleForm is-optional"
+                              >
+                                <option value="" selected="selected" disabled="disabled">choose in list…</option>
+                                <option value="H2O/ethanol (75/25)">H2O/ethanol (75/25)</option>
+                              </select>
+                            </div>
+                          </div>
+                          <div
+                            id="add1spectrum-sample-type-rcc-added-{{ TAB_INDEX_PLACEHOLDER }}"
+                            class="panel panel-default add1spectrum-sample-type-panel"
+                            style="display:none"
+                          >
+                            <div class="panel-heading">
+                              <h3 class="panel-title">Reference compound added</h3>
+                            </div>
+                            <div class="panel-body" style="padding: 0px;">
+                              <div
+                                id="container_RCC_ADDED-{{ TAB_INDEX_PLACEHOLDER }}"
+                                class="handsontable"
+                              ></div>
+                            </div>
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                    <div class="col-lg-4">
+                      <div id="sample-bonus-display-{{ TAB_INDEX_PLACEHOLDER }}"></div>
+                      &nbsp;
+                    </div>
+                  </div>
+                  <div class="col-lg-12">
+                    <div class="col-lg-8">
+                      <br>
+                      <button
+                        id="btnSwitch-gotoStep2-{{ TAB_INDEX_PLACEHOLDER }}"
+                        onclick="ctx().switchToStep(2)"
+                        class="btn btn-disabled switchStep"
+                        disabled=""
+                      >
+                        <i class="fa fa-arrow-circle-down"></i> Next!
+                      </button>
+                    </div>
+                    <div class="col-lg-4">&nbsp;</div>
+                  </div>
+                </div>
+              </div>
+            </div>
+            <div
+              id="add1spectrum-chromatographyData-LC-{{ TAB_INDEX_PLACEHOLDER }}"
+              class="panel panel-default"
+              style="display: none;"
+            >
+              <div class="panel-heading panel-success">
+                <h4 class="panel-title">
+                  <a
+                    id="linkActivateStep2-lc-{{ TAB_INDEX_PLACEHOLDER }}"
+                    data-toggle="collapse"
+                    data-parent="#accordion-{{ TAB_INDEX_PLACEHOLDER }}"
+                    href="#step2-lc-{{ TAB_INDEX_PLACEHOLDER }}"
+                  >
+                    Liquid Chromatography
+                    <i
+                      id="step2-lc-sign-{{ TAB_INDEX_PLACEHOLDER }}"
+                      class="fa fa-question-circle"
+                    ></i>
+                  </a>
+                </h4>
+              </div>
+              <div
+                id="step2-lc-{{ TAB_INDEX_PLACEHOLDER }}"
+                class="panel-collapse collapse"
+              >
+                <div class="panel-body">
+                  <div class="col-lg-12">
+                    <div class="col-lg-6">
+                      <div class="panel panel-default">
+                        <div class="panel-heading">
+                          <h3 class="panel-title">Chromatography Param.</h3>
+                        </div>
+                        <div class="panel-body">
+                          <div class="form-group input-group  has-error">
+                            <span class="input-group-addon">Method</span>
+                            <select
+                              id="add1spectrum-chromatoLC-method-{{ TAB_INDEX_PLACEHOLDER }}"
+                              class="form-control add1spectrum add1spectrum-chromatoLCForm is-mandatory"
+                            ></select>
+                          </div>
+                          <div id="alertBoxSelectTemplate-{{ TAB_INDEX_PLACEHOLDER }}"></div>
+                          <div class="form-group input-group  has-error">
+                            <span class="input-group-addon">Column constructor</span>
+                            <select
+                              id="add1spectrum-chromatoLC-colConstructor-{{ TAB_INDEX_PLACEHOLDER }}"
+                              class="form-control add1spectrum add1spectrum-chromatoLCForm is-mandatory"
+                            >
+                              <option value="" selected="selected" disabled="disabled">choose in list…</option>
+                          <!--
+                              <option value="Agilent">Agilent</option>
+                              <option value="alltech">Alltech</option>
+                              <option value="CIL cluzeau">Cil cluzeau</option>
+                              <option value="Daicel">Daicel</option>
+                              <option value="Hypersil">Hypersil</option>
+                              <option value="Interchim">Interchim</option>
+                              <option value="Macherey nagel">Macherey nagel</option>
+                              <option value="Merck">Merck</option>
+                              <option value="Phenomenex">Phenomenex</option>
+                              <option value="Shiseido">Shiseido</option>
+                              <option value="Supelco (sigma-aldrich)">Supelco (sigma-aldrich)</option>
+                              <option value="Thermo">Thermo</option>
+                              <option value="Vydac">Vydac</option>
+                              <option value="Waters">Waters</option>
+                              <option value="YMC">YMC</option>
+                              <option value="other">Other</option>
+                          -->
+                            </select>
+                          </div>
+                          <div class="form-group input-group ">
+                            <span class="input-group-addon">Column constructor (other)</span>
+                            <input id="add1spectrum-chromatoLC-colConstructorOther-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-chromatoLCForm" disabled="disabled" placeholder="e.g. HAL 9000">
+                          </div>
+                          <div class="form-group input-group  has-warning">
+                            <span class="input-group-addon">Column name</span>
+                            <input id="add1spectrum-chromatoLC-colName-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-chromatoLCForm is-optional" placeholder="e.g. UPLC HSS T3">
+                          </div>
+                          <div class="form-group input-group  has-error">
+                            <span class="input-group-addon">Column length (mm)</span>
+                            <input id="add1spectrum-chromatoLC-colLength-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-chromatoLCForm is-mandatory" placeholder="e.g. 150">
+                          </div>
+                          <div class="form-group input-group  has-error">
+                            <span class="input-group-addon">Column diameter (mm)</span>
+                            <input id="add1spectrum-chromatoLC-colDiameter-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-chromatoLCForm is-mandatory" placeholder="e.g. 2.1">
+                          </div>
+                          <div class="form-group input-group  has-error">
+                            <span class="input-group-addon">Particule size (µm)</span>
+                            <input id="add1spectrum-chromatoLC-colParticuleSize-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-chromatoLCForm is-mandatory" placeholder="e.g. 1.8">
+                          </div>
+                          <div class="form-group input-group  has-warning">
+                            <span class="input-group-addon">Column temperature (°C)</span>
+                            <input id="add1spectrum-chromatoLC-colTemperature-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-chromatoLCForm is-optional" placeholder="e.g. 30">
+                          </div>
+                          <div class="form-group input-group  has-error">
+                            <span class="input-group-addon">LC mode</span>
+                            <select id="add1spectrum-chromatoLC-LCMode-{{ TAB_INDEX_PLACEHOLDER }}" class="form-control add1spectrum add1spectrum-chromatoLCForm is-mandatory">
+                              <option value="" selected="selected" disabled="disabled">choose in list…</option>
+                              <option value="Gradient">Gradient</option>
+                              <option value="Isocratic">Isocratic</option>
+                            </select>
+                          </div>
+                          <div class="form-group input-group  has-warning">
+                            <span class="input-group-addon">Separation flow rate (µL/min)</span>
+                            <input id="add1spectrum-chromatoLC-separationFlowRate-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-chromatoLCForm is-optional" placeholder="e.g. 400">
+                          </div>
+                          <div class="form-group input-group  has-error">
+                            <span class="input-group-addon">Separation solvent A</span>
+                            <select
+                              id="add1spectrum-chromatoLC-separationSolvA-{{ TAB_INDEX_PLACEHOLDER }}"
+                              class="form-control add1spectrum add1spectrum-chromatoLCForm is-mandatory"
+                            >
+                              <option value="" selected="selected" disabled="disabled">choose in list…</option>
+                              <!-- 
+                              <option value="5% ACN + 0.1% FA in H2O">5% ACN + 0.1% FA in H2O</option>
+                              <option value="10% ACN + 0.1% FA in H2O">10% ACN + 0.1% FA in H2O</option>
+                              <option value="10mM (NH4)2CO3 / ACN (20/80) (v/v)">10mM (NH4)2CO3 / ACN (20/80) (v/v)</option>
+                              <option value="Acetonitrile">Acetonitrile</option>
+                              <option value="Acetonitrile + 0.1% Formic Acid">Acetonitrile + 0.1% Formic Acid</option>
+                              <option value="H2O + 0.1% Formic Acid">H2O + 0.1% Formic Acid</option>
+                              <option value="H2O, ammonium carbonate 10 mM, pH10.5">H2O, ammonium carbonate 10 mM, pH10.5</option>
+                              <option value="H2O, ammonium acetate 2.5 mM">H2O, ammonium acetate 2.5 mM</option>
+                              <option value="H2O / CH3OH / CH3CO2H (95/5/0.1)">H2O / CH3OH / CH3CO2H (95/5/0.1)</option>
+                              <option value="Methanol">Methanol</option>
+                              <option value="Methanol / H2O (1/1)">Methanol / H2O (1/1)</option>
+                              <option value="Methanol / H2O (1/1), 0.1% FA">Methanol / H2O (1/1), 0.1% FA</option>
+                              <option value="Methanol / CH3CO2H (100/0.1)">Methanol / CH3CO2H (100/0.1)</option>
+                              <option value="Methanol / ACN (60/40) ammonium acetate 2.5 mM">Methanol / ACN (60/40) ammonium acetate 2.5 mM</option>
+                               -->
+                            </select>
+                          </div>
+                          <div class="form-group input-group  has-warning">
+                            <span class="input-group-addon">pH solvent A (if acqueous solvant)</span>
+                            <input id="add1spectrum-chromatoLC-separationSolvApH-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-chromatoLCForm is-optional" placeholder="e.g. 7.0">
+                          </div>
+                          <div class="form-group input-group  has-error">
+                            <span class="input-group-addon">Separation solvent B</span>
+                            <select
+                              id="add1spectrum-chromatoLC-separationSolvB-{{ TAB_INDEX_PLACEHOLDER }}"
+                              class="form-control add1spectrum add1spectrum-chromatoLCForm is-mandatory"
+                            >
+                            <!-- 
+                              <option value="" selected="selected" disabled="disabled">choose in list…</option>
+                              <option value="5% ACN + 0.1% FA in H2O">5% ACN + 0.1% FA in H2O</option>
+                              <option value="10% ACN + 0.1% FA in H2O">10% ACN + 0.1% FA in H2O</option>
+                              <option value="10mM (NH4)2CO3 / ACN (20/80) (v/v)">10mM (NH4)2CO3 / ACN (20/80) (v/v)</option>
+                              <option value="Acetonitrile">Acetonitrile</option>
+                              <option value="Acetonitrile + 0.1% Formic Acid">Acetonitrile + 0.1% Formic Acid</option>
+                              <option value="H2O + 0.1% Formic Acid">H2O + 0.1% Formic Acid</option>
+                              <option value="H2O, ammonium carbonate 10 mM, pH10.5">H2O, ammonium carbonate 10 mM, pH10.5</option>
+                              <option value="H2O, ammonium acetate 2.5 mM">H2O, ammonium acetate 2.5 mM</option>
+                              <option value="H2O / CH3OH / CH3CO2H (95/5/0.1)">H2O / CH3OH / CH3CO2H (95/5/0.1)</option>
+                              <option value="Methanol">Methanol</option>
+                              <option value="Methanol / H2O (1/1)">Methanol / H2O (1/1)</option>
+                              <option value="Methanol / H2O (1/1), 0.1% FA">Methanol / H2O (1/1), 0.1% FA</option>
+                              <option value="Methanol / CH3CO2H (100/0.1)">Methanol / CH3CO2H (100/0.1)</option>
+                              <option value="Methanol / ACN (60/40) ammonium acetate 2.5 mM">Methanol / ACN (60/40) ammonium acetate 2.5 mM</option>
+                            -->
+                            </select>
+                          </div>
+                          <div class="form-group input-group  has-warning">
+                            <span class="input-group-addon">pH solvent B (if acqueous solvant)</span>
+                            <input id="add1spectrum-chromatoLC-separationSolvBApH-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-chromatoLCForm is-optional" placeholder="e.g. 7.0">
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                    <div class="col-lg-6">
+                      <div class="panel panel-default">
+                        <div class="panel-heading">
+                          <h3 class="panel-title">Separation flow gradient</h3>
+                        </div>
+                        <div class="panel-body" style="padding: 0px;">
+                          <div id="container_LC_SFG-{{ TAB_INDEX_PLACEHOLDER }}" class="handsontable"></div>
+                        </div>
+                      </div>
+                    </div>
+                  </div>
+                  <div class="col-lg-12">
+                    <div class="col-lg-8">
+                      <br>
+                      <button id="btnSwitch-gotoStep3-lc-{{ TAB_INDEX_PLACEHOLDER }}" onclick="ctx().switchToStep(3);" class="btn btn-disabled switchStep" disabled=""><i class="fa fa-arrow-circle-down"></i> Next!</button>
+                    </div>
+                    <div class="col-lg-4">&nbsp;</div>
+                  </div>
+                </div>
+              </div>
+            </div>
+            <div id="add1spectrum-chromatographyData-GC-{{ TAB_INDEX_PLACEHOLDER }}" class="panel panel-default" style="display: none;">
+              <div class="panel-heading panel-success">
+                <h4 class="panel-title">
+                  <a id="linkActivateStep2-gc-{{ TAB_INDEX_PLACEHOLDER }}" data-toggle="collapse" data-parent="#accordion-{{ TAB_INDEX_PLACEHOLDER }}" href="#step2-gc-{{ TAB_INDEX_PLACEHOLDER }}">
+                    Gaz Chromatography <i id="step2-gc-sign-{{ TAB_INDEX_PLACEHOLDER }}" class="fa fa-question-circle"></i>
+                  </a>
+                </h4>
+              </div>
+              <div id="step2-gc-{{ TAB_INDEX_PLACEHOLDER }}" class="panel-collapse collapse">
+                <div class="panel-body">
+                  <div class="col-lg-12">
+                    <div class="col-lg-6">
+                      i &lt;3 GC
+                    </div>
+                    <div class="col-lg-6">&nbsp;</div>
+                  </div>
+                  <div class="col-lg-12">
+                    <div class="col-lg-8">
+                      <br>
+                      <button id="btnSwitch-gotoStep3-gc-{{ TAB_INDEX_PLACEHOLDER }}" onclick="ctx().switchToStep(3);" class="btn btn-disabled switchStep" disabled=""><i class="fa fa-arrow-circle-down"></i> Next!</button>
+                    </div>
+                    <div class="col-lg-4">&nbsp;</div>
+                  </div>
+                </div>
+              </div>
+            </div>
+            <div id="add1spectrum-analyserData-MS-{{ TAB_INDEX_PLACEHOLDER }}" class="panel panel-default" style="display: none;">
+              <div class="panel-heading panel-success">
+                <h4 class="panel-title">
+                  <a id="linkActivateStep3-ms-{{ TAB_INDEX_PLACEHOLDER }}" data-toggle="collapse" data-parent="#accordion-{{ TAB_INDEX_PLACEHOLDER }}" href="#step3-ms-{{ TAB_INDEX_PLACEHOLDER }}">
+                    MS Analyzer <i id="step3-ms-sign-{{ TAB_INDEX_PLACEHOLDER }}" class="fa fa-question-circle"></i>
+                  </a>
+                </h4>
+              </div>
+              <div id="step3-ms-{{ TAB_INDEX_PLACEHOLDER }}" class="panel-collapse collapse">
+                <div class="panel-body">
+                  <div class="col-lg-12">
+                    <div class="col-lg-6">
+                      <br>
+                      <div class="panel panel-default">
+                        <div class="panel-heading">
+                          <h3 class="panel-title">Analyzer</h3>
+                        </div>
+                        <div class="panel-body">
+                          <div class="form-group input-group  has-error">
+                            <span class="input-group-addon">Instrument</span>
+                            <input id="add1spectrum-analyzer-ms-instrument-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-mandatory" placeholder="e.g. Q-TOF; LTQ - Orbitrap">
+                          </div>
+                          <div class="form-group input-group  has-warning">
+                            <span class="input-group-addon">Model</span>
+                            <input id="add1spectrum-analyzer-ms-model-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional" placeholder="e.g. QToF micro (Micromass Waters); XL; Impact II; ...">
+                          </div>
+                          <div class="form-group">
+                            <div class="form-group input-group  has-error">
+                              <span class="input-group-addon">Ion analyzer Type</span>
+                              <input id="add1spectrum-analyzer-ms-ionAnalyzerType-{{ TAB_INDEX_PLACEHOLDER }}" type="text" class="form-control add1spectrum add1spectrum-analyzerMSForm is-mandatory" placeholder="e.g. QTOF; QQQ; ...">
+                            </div>
+                            <p class="help-block">
+                              <small>
+                                Ion analyzer types are "B", "E", "FT" (include other types using FT like FTICR or Orbitrap), "IT", "Q", "TOF" (e.g.: "QTOF", "QQQ", "EB", "ITFT"); 
+                                for further informations please refer to <a target="_BLANK" href="https://github.com/MassBank/MassBank-web/blob/main/Documentation/MassBankRecordFormat.md#212-record_title">MassBank Record documentation</a>.
+                              </small>
+                            </p>
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                    <div class="col-lg-6">
+                      <br>
+                      <div class="panel panel-default">
+                        <div class="panel-heading">
+                          <h3 class="panel-title">Molecule Ionization</h3>
+                        </div>
+                        <div class="panel-body">
+                          <div class="form-group input-group has-error">
+                            <span class="input-group-addon">Ionization method <small>(POS/NEG)</small></span>
+                            <select
+                              id="add1spectrum-analyzserMS-ionizationMethod-pos-{{ TAB_INDEX_PLACEHOLDER }}"
+                              style="max-width: 50%;"
+                              class="form-control add1spectrum add1spectrum-analyzerMSForm is-mandatory one-or-more"
+                            >
+                            </select>
+                            <select
+                              id="add1spectrum-analyzserMS-ionizationMethod-neg-{{ TAB_INDEX_PLACEHOLDER }}"
+                              style="max-width: 50%;"
+                              class="form-control add1spectrum add1spectrum-analyzerMSForm is-mandatory one-or-more"
+                            >
+                            </select>
+                          </div>
+                          <div class="form-group input-group has-warning">
+                            <span class="input-group-addon">
+                              Spray (needle) gaz flow <br><small>(arbitrary in Xcalibur, POS/NEG)</small>
+                            </span>
+                            <input
+                              id="add1spectrum-analyzserMS-sprayGazFlow-pos-{{ TAB_INDEX_PLACEHOLDER }}"
+                              style="max-width: 50%;"
+                              type="text"
+                              class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional"
+                              placeholder="e.g. 40 (POS)"
+                            >
+                            <input
+                              id="add1spectrum-analyzserMS-sprayGazFlow-neg-{{ TAB_INDEX_PLACEHOLDER }}"
+                              style="max-width: 50%;"
+                              type="text"
+                              class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional"
+                              placeholder="e.g. 20 (NEG)"
+                            >
+                          </div>
+                          <div class="form-group input-group has-warning">
+                            <span class="input-group-addon">
+                              Vaporizer gaz flow <br><small>(arbitrary in Xcalibur, POS/NEG)</small>
+                            </span>
+                            <input
+                              id="add1spectrum-analyzserMS-vaporizerGazFlow-pos-{{ TAB_INDEX_PLACEHOLDER }}"
+                              style="max-width: 50%;"
+                              type="text"
+                              class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional"
+                              placeholder="e.g. 10 (POS)"
+                            />
+                            <input
+                              id="add1spectrum-analyzserMS-vaporizerGazFlow-neg-{{ TAB_INDEX_PLACEHOLDER }}"
+                              style="max-width: 50%;"
+                              type="text"
+                              class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional"
+                              placeholder="e.g. 5 (NEG)"
+                            />
+                          </div>
+                          <div class="form-group input-group has-warning">
+                            <span class="input-group-addon">
+                              Vaporizer temperature <br><small>(°C, POS/NEG)</small>
+                            </span>
+                            <input
+                              id="add1spectrum-analyzserMS-vaporizerTemperature-pos-{{ TAB_INDEX_PLACEHOLDER }}"
+                              style="max-width: 50%;"
+                              type="text"
+                              class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional"
+                              placeholder="e.g. 330 (POS)"
+                            />
+                            <input
+                              id="add1spectrum-analyzserMS-vaporizerTemperature-neg-{{ TAB_INDEX_PLACEHOLDER }}"
+                              style="max-width: 50%;"
+                              type="text"
+                              class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional"
+                              placeholder="e.g. 330 (NEG)"
+                            />
+                          </div>
+                          <div class="form-group input-group has-warning">
+                            <span class="input-group-addon">
+                              Source gaz flow <br><small>(arbitrary in Xcalibur, POS/NEG)</small>
+                            </span>
+                            <input
+                              id="add1spectrum-analyzserMS-sourceGazFlow-pos-{{ TAB_INDEX_PLACEHOLDER }}"
+                              style="max-width: 50%;"
+                              type="text"
+                              class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional"
+                              placeholder="e.g. 40 (POS)"
+                            />
+                            <input
+                              id="add1spectrum-analyzserMS-sourceGazFlow-neg-{{ TAB_INDEX_PLACEHOLDER }}"
+                              style="max-width: 50%;"
+                              type="text"
+                              class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional"
+                              placeholder="e.g. 40 (NEG)"
+                            />
+                          </div>
+                          <div class="form-group input-group has-warning">
+                            <span class="input-group-addon">
+                              Ion transfer tube temperature <br> Transfer capillary temperature <small>(°C, POS/NEG)</small>
+                            </span>
+                            <input
+                              id="add1spectrum-analyzserMS-ionTransferTubeTemperatureOrTransferCapillaryTemperature-pos-{{ TAB_INDEX_PLACEHOLDER }}"
+                              style="max-width: 50%;"
+                              type="text"
+                              class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional"
+                              placeholder="e.g. 350 (POS)"
+                            />
+                            <input
+                              id="add1spectrum-analyzserMS-ionTransferTubeTemperatureOrTransferCapillaryTemperature-neg-{{ TAB_INDEX_PLACEHOLDER }}"
+                              style="max-width: 50%;"
+                              type="text"
+                              class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional"
+                              placeholder="e.g. 350 (NEG)"
+                            />
+                          </div>
+                          <div class="form-group input-group has-warning">
+                            <span class="input-group-addon">
+                              High voltage (ESI) <br> Corona voltage (APCI) <small>(in kV, POS/NEG)</small>
+                            </span>
+                            <input
+                              id="add1spectrum-analyzserMS-highVoltageOrCoronaVoltage-pos-{{ TAB_INDEX_PLACEHOLDER }}"
+                              style="max-width: 50%;"
+                              type="text"
+                              class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional"
+                              placeholder="e.g. 3.5 (POS)"
+                            />
+                            <input
+                              id="add1spectrum-analyzserMS-highVoltageOrCoronaVoltage-neg-{{ TAB_INDEX_PLACEHOLDER }}"
+                              style="max-width: 50%;"
+                              type="text"
+                              class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional"
+                              placeholder="e.g. 2.8 (NEG)"
+                            />
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                    <div class="col-lg-6 opt-msms">
+                      <br>
+                      <div class="panel panel-default">
+                        <div class="panel-heading">
+                          <h3 class="panel-title">Ion Storage / Ion Beam</h3>
+                        </div>
+                        <div class="panel-body">
+                          <div class="form-group input-group  has-warning">
+                            <span class="input-group-addon">Type <small>(storage / beam)</small></span>
+                            <select id="add1spectrum-ionTrapBeam-type-{{ TAB_INDEX_PLACEHOLDER }}" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional one-or-more">
+                              <option value="" selected="selected" disabled="disabled">choose in list…</option>
+                              <option value="trap">Ion Trap</option>
+                              <option value="beam">Ion Beam</option>
+                            </select>
+                          </div>
+                          <p class="help-block">
+                            <small>
+                              Ion storage: Ion Trap (IT) and ICR.
+                              <br>Ion beam: Q or H collision Cell (QQQ, QQIT, QQ/TOF, Fusion). 
+                            </small>
+                          </p>
+                          <div class="form-group input-group  has-success">
+                            <span class="input-group-addon">Gas </span>
+                            <select id="add1spectrum-ionTrapBeam-ionGas-{{ TAB_INDEX_PLACEHOLDER }}" class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional ">
+                              <option value="He">He</option>
+                              <option value="N2">N2</option>
+                              <option value="Ar">Ar</option>
+                            </select>
+                          </div>
+                          <div class="form-group input-group has-warning">
+                            <span class="input-group-addon">Gas pressure</span>
+                            <input
+                              id="add1spectrum-ionTrapBeam-ionGasPressureValue-{{ TAB_INDEX_PLACEHOLDER }}"
+                              type="text"
+                              style="max-width: 50%;"
+                              class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional"
+                            />
+                            <select
+                              id="add1spectrum-ionTrapBeam-ionGasPressureUnit-{{ TAB_INDEX_PLACEHOLDER }}"
+                              style="max-width: 50%;"
+                              class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional"
+                            >
+                              <option value="" selected="selected" disabled="disabled">choose in list…</option>
+                              <option value="mbar">mbar</option>
+                              <option value="au">a.u.</option>
+                            </select>
+                          </div>
+                          <div class="form-group input-group add1spectrum-ionTrap has-warning">
+                            <span class="input-group-addon">Frequency Shift <small>(KHz)</small></span>
+                            <input
+                              id="add1spectrum-ionTrapBeam-ionFrequencyShift-{{ TAB_INDEX_PLACEHOLDER }}"
+                              type="text"
+                              class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional"
+                              placeholder="e.g.: ..."
+                            />
+                          </div>
+                          <div class="form-group input-group add1spectrum-ionTrap has-warning">
+                            <span class="input-group-addon">Ion Number <small>(AGC or ICC)</small></span>
+                            <input
+                              id="add1spectrum-ionTrapBeam-ionNumber-{{ TAB_INDEX_PLACEHOLDER }}"
+                              type="text"
+                              class="form-control add1spectrum add1spectrum-analyzerMSForm is-optional"
+                              placeholder="e.g.: ..."
+                            />
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                  </div>
+                  <div class="col-lg-12">
+                    <div class="col-lg-8">
+                      <br>
+                      <button
+                        id="btnSwitch-gotoStep4-ms-{{ TAB_INDEX_PLACEHOLDER }}"
+                        onclick="ctx().switchToStep(4);"
+                        class="btn btn-disabled switchStep"
+                        disabled=""
+                      >
+                        <i class="fa fa-arrow-circle-down"></i> Next!
+                      </button>
+                    </div>
+                    <div class="col-lg-4">&nbsp;</div>
+                  </div>
+                </div>
+              </div>
+            </div>
+            <div
+              id="add1spectrum-peaksData-MS-{{ TAB_INDEX_PLACEHOLDER }}"
+              class="panel panel-default"
+              style="display: none;"
+            >
+              <div class="panel-heading panel-success">
+                <h4 class="panel-title">
+                  <a
+                    id="linkActivateStep4-ms-{{ TAB_INDEX_PLACEHOLDER }}"
+                    data-toggle="collapse"
+                    data-parent="#accordion-{{ TAB_INDEX_PLACEHOLDER }}"
+                    href="#step4-ms-{{ TAB_INDEX_PLACEHOLDER }}"
+                  >
+                    MS Peaks
+                    <i
+                      id="step4-ms-sign-{{ TAB_INDEX_PLACEHOLDER }}"
+                      class="fa fa-question-circle"
+                    ></i>
+                  </a>
+                </h4>
+              </div>
+              <div id="step4-ms-{{ TAB_INDEX_PLACEHOLDER }}" class="panel-collapse collapse">
+                <div class="panel-body">
+                  <div class="col-lg-12">
+                    <div class="form-group input-group col-lg-3 has-success">
+                      <span class="input-group-addon">scan type</span>
+                      <select
+                        style="width: 150px;"
+                        id="add1spectrum-peaksMS-msLevel-{{ TAB_INDEX_PLACEHOLDER }}"
+                        class="form-control add1spectrum add1spectrum-peaksMSForm-peaklist add1spectrum-peaksMSForm-peaklist-reset add1spectrum-peaksMSForm is-mandatory"
+                      >
+                        <option value="" disabled="disabled">choose in list…</option>
+                        <option class="enable-if-ms" value="ms" disabled="disabled" selected="selected">ms</option>
+                        <option class="enable-if-msms" value="ms2" disabled="disabled">ms2</option>
+                        <!-- <option class="enable-if-msms" value="ms3" disabled="disabled">ms3</option> -->
+                      </select>
+                    </div>
+                    <div class="form-group input-group col-lg-3 has-error">
+                      <span class="input-group-addon">polarity</span>
+                      <select
+                        style="width: 150px;"
+                        id="add1spectrum-peaksMS-polarity-{{ TAB_INDEX_PLACEHOLDER }}"
+                        class="form-control add1spectrum add1spectrum-peaksMSForm-peaklist add1spectrum-peaksMSForm-peaklist-reset add1spectrum-peaksMSForm is-mandatory"
+                      >
+                        <option value="" selected="selected" disabled="disabled">choose in list…</option>
+                        <option value="positive" disabled="disabled">positive</option>
+                        <option value="negative" disabled="disabled">negative</option>
+                      </select>
+                    </div>
+                    <div class="form-group input-group col-lg-3 has-error">
+                      <span class="input-group-addon">resolution</span>
+                      <select
+                        style="width: 150px;"
+                        id="add1spectrum-peaksMS-resolution-{{ TAB_INDEX_PLACEHOLDER }}"
+                        class="form-control add1spectrum add1spectrum-peaksMSForm-peaklist add1spectrum-peaksMSForm-peaklist-reset add1spectrum-peaksMSForm is-mandatory"
+                      >
+                        <option value="" disabled="disabled">choose in list…</option>
+                        <option value="low">low</option>
+                        <option selected="selected" value="high">high</option>
+                      </select>
+                    </div>
+                    <div class="form-group input-group col-lg-3 has-success">
+                      <span class="input-group-addon">curation</span>
+                      <select
+                        style="width: 150px;"
+                        id="add1spectrum-peaksMS-curation-{{ TAB_INDEX_PLACEHOLDER }}"
+                        class="form-control add1spectrum add1spectrum-peaksMSForm-peaklist add1spectrum-peaksMSForm-peaklist-reset add1spectrum-peaksMSForm is-optional"
+                      >
+                        <option value="no_curation" selected="selected">no curation</option>
+                        <option value="peaks_RI_sup_1percent">Peaks RI &gt; 1%</option>
+                        <option value="top_50_peaks">Top 50 peaks</option>
+                        <option value="top_20_peaks">Top 20 peaks</option>
+                        <option value="top_10_peaks">Top 10 peaks</option>
+                        <option value="similar_chromatographic_profile">Similar chromatographic profile</option>
+                      </select>
+                    </div>
+                  </div>
+                  <br>
+                  <div class="col-lg-12 opt-msms">
+                    <hr>
+                    <div class="form-group input-group col-lg-4 has-success">
+                      <span class="input-group-addon">
+                        Isolation mode <i class="fa fa-question-circle" title="IT / Q / TOF / ICR"></i>
+                      </span>
+                      <select
+                        id="add1spectrum-peaksMS-isolationMode-{{ TAB_INDEX_PLACEHOLDER }}"
+                        class="disabled-if-ms-in-msms form-control add1spectrum add1spectrum-peaksMSForm-peaklist add1spectrum-peaksMSForm is-optional add1spectrum-peaksMSForm-peaklist-reset"
+                      >
+                        <option value="" disabled="disabled">choose in list…</option>
+                        <option value="IT">IT</option>
+                        <option value="Q">Q</option>
+                        <option value="TOF">TOF</option>
+                        <option value="ICR">ICR</option>
+                      </select>
+                    </div>
+                    <div class="form-group input-group col-lg-4 has-warning">
+                      <span class="input-group-addon">
+                        Isolation window <i class="fa fa-question-circle" title="(+ / - value)"></i>
+                      </span>
+                      <input
+                        id="add1spectrum-peaksMS-isolationWindow-{{ TAB_INDEX_PLACEHOLDER }}"
+                        type="text"
+                        class="disabled-if-ms-in-msms form-control add1spectrum add1spectrum-peaksMSForm-peaklist is-optional add1spectrum-peaksMSForm-peaklist-reset"
+                        placeholder="e.g. ..."
+                      />
+                    </div>
+                    <div class="form-group input-group col-lg-4 has-warning">
+                      <span class="input-group-addon">qz isolation / activation <i class="fa fa-question-circle" title="if IT"></i> <small>(no unit)</small></span>
+                      <input
+                        id="add1spectrum-peaksMS-qzIsolation-{{ TAB_INDEX_PLACEHOLDER }}"
+                        type="text"
+                        class="disabled-if-ms-in-msms form-control add1spectrum add1spectrum-peaksMSForm-peaklist is-optional add1spectrum-peaksMSForm-peaklist-reset"
+                        placeholder="e.g. ..."
+                      />
+                    </div>
+                    <div class="form-group input-group col-lg-4 has-warning">
+                      <span class="input-group-addon">Activation time <i class="fa fa-question-circle" title="if FT-ICR (SORI-CID) or IT"></i> <small>(ms)</small></span>
+                      <input
+                        id="add1spectrum-peaksMS-activationTime-{{ TAB_INDEX_PLACEHOLDER }}"
+                        type="text"
+                        class="disabled-if-ms-in-msms form-control add1spectrum add1spectrum-peaksMSForm-peaklist is-optional add1spectrum-peaksMSForm-peaklist-reset"
+                        placeholder="e.g. ..."
+                      />
+                    </div>
+                    <div class="form-group input-group col-lg-4 has-success">
+                      <span class="input-group-addon">
+                        Mode <i class="fa fa-question-circle" title="HCD / CID / ECD /ETD"></i>
+                      </span>
+                      <select
+                        id="add1spectrum-peaksMS-mode-{{ TAB_INDEX_PLACEHOLDER }}"
+                        class="disabled-if-ms-in-msms form-control add1spectrum add1spectrum-peaksMSForm-peaklist add1spectrum-peaksMSForm is-optional"
+                      >
+                        <option value="" disabled="disabled">choose in list…</option>
+                        <option value="HCD">HCD</option>
+                        <option value="CID">CID</option>
+                        <option value="ECD">ECD</option>
+                        <option value="ETD">ETD</option>
+                      </select>
+                    </div>
+
+                    <div class="form-group input-group col-lg-4 has-warning">
+                      <span class="input-group-addon">
+                        Frag. energy <i class="fa fa-question-circle" title="without unit"></i>
+                      </span>
+                      <input
+                        id="add1spectrum-peaksMS-frag-nrj-{{ TAB_INDEX_PLACEHOLDER }}"
+                        type="text"
+                        class="disabled-if-ms-in-msms form-control add1spectrum add1spectrum-peaksMSForm-peaklist is-optional"
+                        placeholder="e.g. ..."
+                      >
+                    </div>
+                  </div>
+                  <div class="col-lg-12">
+                    <div class="form-group input-group col-lg-4 has-warning">
+                      <span class="input-group-addon">
+                        Resolution FWHM <small>(resolution@mass)</small>
+                      </span>
+                      <input
+                        id="add1spectrum-analyzer-ms-resolutionFWHM-{{ TAB_INDEX_PLACEHOLDER }}"
+                        type="text"
+                        class="form-control add1spectrum add1spectrum-peaksMSForm-peaklist is-optional"
+                        placeholder="e.g. 6500@1000"
+                      />
+                    </div>
+                    <div class="form-group input-group col-lg-4 has-error">
+                      <span class="input-group-addon">
+                        m/z range <small>(ppm) from / to</small>
+                      </span>
+                      <input
+                        style="width: 100px;"
+                        id="add1spectrum-peaksMS-rangeFrom-{{ TAB_INDEX_PLACEHOLDER }}"
+                        type="text"
+                        class="form-control add1spectrum-peaksMSForm-peaklist add1spectrum  is-mandatory"
+                        placeholder="50"
+                        value="{{ DEFAULT_MIN_MZ }}"
+                      />
+                      <input
+                        style="width: 100px;"
+                        id="add1spectrum-peaksMS-rangeTo-{{ TAB_INDEX_PLACEHOLDER }}"
+                        type="text"
+                        class="form-control add1spectrum-peaksMSForm-peaklist add1spectrum  is-mandatory"
+                        placeholder="500"
+                        value="{{ DEFAULT_MAX_MZ }}"
+                      />
+                    </div>
+                    <div class="form-group input-group col-lg-4 has-error">
+                      <span class="input-group-addon">
+                        retention time (min) <small>from / to</small>
+                      </span>
+                      <input
+                        style="width: 100px;"
+                        id="add1spectrum-peaksMS-rtMinFrom-{{ TAB_INDEX_PLACEHOLDER }}"
+                        type="text"
+                        class="form-control add1spectrum-peaksMSForm-peaklist add1spectrum  is-mandatory"
+                        placeholder="0.9"
+                        value="{{ DEFAULT_MIN_RT }}"
+                      />
+                      <input
+                        style="width: 100px;"
+                        id="add1spectrum-peaksMS-rtMinTo-{{ TAB_INDEX_PLACEHOLDER }}"
+                        type="text"
+                        class="form-control add1spectrum-peaksMSForm-peaklist add1spectrum  is-mandatory"
+                        placeholder="1.4"
+                        value="{{ DEFAULT_MAX_RT }}"
+                      />
+                    </div>
+                  </div>
+                  <div class="col-lg-12">
+                    <div class="form-group input-group col-lg-4 has-warning">
+                      <span class="input-group-addon">
+                        retention time (% solvant) <small>from / to</small>
+                      </span>
+                      <input
+                        style="width: 100px;"
+                        id="add1spectrum-peaksMS-rtSolvFrom-{{ TAB_INDEX_PLACEHOLDER }}"
+                        type="text"
+                        class="form-control add1spectrum-peaksMSForm-peaklist add1spectrum  is-optional"
+                        placeholder="??"
+                      />
+                      <input
+                        style="width: 100px;"
+                        id="add1spectrum-peaksMS-rtSolvTo-{{ TAB_INDEX_PLACEHOLDER }}"
+                        type="text"
+                        class="form-control add1spectrum-peaksMSForm-peaklist add1spectrum  is-optional"
+                        placeholder="??"
+                      />
+                    </div>
+                    <div class="col-lg-4">&nbsp;</div>
+                    <div class="col-lg-4">&nbsp;</div>
+                  </div>
+                  <div class="col-xs-12 col-lg-12">
+                    <ul class="nav nav-tabs" style="margin-bottom: 15px;display: block;">
+                      {{ TAB_LIST_PLACEHOLDER }}
+                    </ul>
+                    <div class="tab-content">
+                      {{ ADD_SPECTRUM_FORM }}
+                    </div>
+                  </div>
+                  <div class="col-lg-12">
+                    <div class="col-lg-8">
+                      <button
+                        class="btn btn-success add1spectrum"
+                        onclick="$('#add1spectrum-peaksMS-msPrecursorIon-{{ TAB_INDEX_PLACEHOLDER }}').change()"
+                      >check</button>
+                      <button
+                        id="btnSwitch-gotoStep5-ms-{{ TAB_INDEX_PLACEHOLDER }}"
+                        onclick="$('#open_tab_1').click();setTimeout(()=>ctx().switchToStep(5), 100);"
+                        class="btn switchStep btn-primary"
+                      ><i class="fa fa-arrow-circle-down"></i> Next!</button>
+                      <br>
+                      <br>
+                    </div>
+                    <div class="col-lg-4">&nbsp;</div>
+                  </div>
+                </div>
+              </div>
+            </div>
+
+
+            <div
+              id="add1spectrum-otherData-{{ TAB_INDEX_PLACEHOLDER }}"
+              class="panel panel-default"
+              style="display: none;"
+            >
+              <div class="panel-heading panel-success">
+                <h4 class="panel-title">
+                  <a id="linkActivateStep5-{{ TAB_INDEX_PLACEHOLDER }}" data-toggle="collapse" data-parent="#accordion-{{ TAB_INDEX_PLACEHOLDER }}" href="#step5-{{ TAB_INDEX_PLACEHOLDER }}">
+                    Other <i id="step5sign-{{ TAB_INDEX_PLACEHOLDER }}" class="fa fa-question-circle"></i>
+                  </a>
+                </h4>
+              </div>
+              <div id="step5-{{ TAB_INDEX_PLACEHOLDER }}" class="panel-collapse collapse">
+                <div class="panel-body">
+                  <div class="col-lg-12">
+                    <div class="col-lg-6">
+                      <div class="panel panel-default">
+                        <div class="panel-heading">
+                          <h3 class="panel-title">Ownership</h3>
+                        </div>
+                        <div class="panel-body">
+                          <div class="form-group input-group  has-error">
+                            <span class="input-group-addon">data author(s)</span>
+                            <input
+                              id="add1spectrum-other-author-{{ TAB_INDEX_PLACEHOLDER }}"
+                              type="text"
+                              class="form-control add1spectrum add1spectrum-otherForm is-mandatory"
+                              placeholder="enter your lab. / plateforme name"
+                              value=""
+                            />
+                          </div>
+                          <div class="form-group input-group  has-warning">
+                            <span class="input-group-addon">data validator(s)</span>
+                            <input
+                              id="add1spectrum-other-validator-{{ TAB_INDEX_PLACEHOLDER }}"
+                              type="text"
+                              class="form-control add1spectrum add1spectrum-otherForm is-optional"
+                              placeholder="name of the personne who checked all data in this file"
+                            />
+                          </div>
+                          <div class="form-group input-group  has-warning">
+                            <span class="input-group-addon">acquisition date</span>
+                            <input
+                              id="add1spectrum-other-date-{{ TAB_INDEX_PLACEHOLDER }}"
+                              data-date-format="yyyy-mm-dd"
+                              type="text"
+                              class="form-control add1spectrum  add1spectrum-otherForm datepicker is-optional"
+                              value=""
+                              placeholder="2022-11-04"
+                            />
+                          </div>
+                          <div class="form-group input-group  has-warning">
+                            <span class="input-group-addon">data ownership</span>
+                            <input
+                              id="add1spectrum-other-owner-{{ TAB_INDEX_PLACEHOLDER }}"
+                              type="text"
+                              class="form-control add1spectrum add1spectrum-otherForm is-optional"
+                              placeholder="enter your lab. / plateforme name &amp; sample provider;"
+                            />
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                    <div class="col-lg-6">
+                      <div class="panel panel-default">
+                        <div class="panel-heading">
+                          <h3 class="panel-title">Raw File</h3>
+                        </div>
+                        <div class="panel-body">
+                          <div class="form-group input-group  has-warning">
+                            <span class="input-group-addon">raw file name</span>
+                            <input
+                              id="add1spectrum-other-fileName-{{ TAB_INDEX_PLACEHOLDER }}"
+                              type="text"
+                              class="form-control add1spectrum add1spectrum-otherForm is-optional"
+                              placeholder="needed to retrieve file later"
+                            />
+                          </div>
+                          <div class="form-group input-group  has-warning">
+                            <span class="input-group-addon">raw file size (Ko)</span>
+                            <input
+                              id="add1spectrum-other-fileSize-{{ TAB_INDEX_PLACEHOLDER }}"
+                              type="text"
+                              class="form-control add1spectrum add1spectrum-otherForm is-optional"
+                              placeholder="optional, to check if the file is correct"
+                            />
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                  </div>
+                  <div class="col-lg-12">
+                    <div class="col-lg-8">
+                      <br>
+                      <button
+                        id="btnSwitch-gotoStep6-{{ TAB_INDEX_PLACEHOLDER }}"
+                        onclick="ctx().switchToStep(6);"
+                        class="btn btn-disabled switchStep"
+                        disabled=""
+                      >
+                        <i class="fa fa-arrow-circle-right"></i> Validate this compound/mix!
+                      </button>
+                      <span
+                        id="import1SpectrumLoadingBare-{{ TAB_INDEX_PLACEHOLDER }}"
+                        style="display: none;"><img
+                        src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/img/ajax-loader.gif"
+                        title="please wait"
+                      ></span>
+                      <span
+                        id="import1SpectrumResults-{{ TAB_INDEX_PLACEHOLDER }}"
+                        style="display: none;"
+                      >
+                        <button
+                          id="btnSwitch-view-{{ TAB_INDEX_PLACEHOLDER }}"
+                          data-toggle="modal"
+                          data-target="#modalShowSpectra-{{ TAB_INDEX_PLACEHOLDER }}"
+                          class="btn btn-success"
+                        >
+                          <i class="fa fa-eye"></i> View spectrum
+                        </button>
+                        <button
+                          id="btnSwitch-returntoStep3-{{ TAB_INDEX_PLACEHOLDER }}"
+                          onclick="ctx().switchToStep(3);"
+                          class="btn btn-primary"
+                        >
+                          <i class="fa fa-arrow-circle-up"></i> Add new peaklist!
+                        </button>
+                      </span>
+                    </div>
+                    <div class="col-lg-4">
+                      <br>
+                      <br>
+                      <br>
+                    </div>
+                  </div>
+                  <div class="col-lg-12">
+                    <div class="col-lg-8">
+                      <div
+                        id="alertBoxSubmitSpectrum-{{ TAB_INDEX_PLACEHOLDER }}"
+                        class="col-lg-6"
+                      ></div>
+                    </div>
+                    <div class="col-lg-4">&nbsp;</div>
+                  </div>
+                </div>
+              </div>
+            </div>
+            <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/handsontable/dist/handsontable.full.min.js"></script>
+            <link
+              rel="stylesheet"
+              media="screen"
+              href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/handsontable/dist/handsontable.full.min.css"
+            >
+            <link
+              rel="stylesheet"
+              media="screen"
+              href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/handsontable/bootstrap/handsontable.bootstrap.min.css"
+            >
+            <div
+              class="modal"
+              id="modalPickCompound-{{ TAB_INDEX_PLACEHOLDER }}"
+              tabindex="-1"
+              role="dialog"
+              aria-labelledby="modalPickCompoundLabel-{{ TAB_INDEX_PLACEHOLDER }}"
+              aria-hidden="true"
+            >
+              <div class="modal-dialog">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <button
+                      type="button"
+                      class="close"
+                      data-dismiss="modal"
+                      aria-hidden="true"
+                    >×</button>
+                    <h4
+                      class="modal-title"
+                      id="modalPickCompoundLabel-{{ TAB_INDEX_PLACEHOLDER }}"
+                    >
+                      Pick a compound
+                    </h4>
+                  </div>
+                  <div class="modal-body">
+                    <div class="form-group input-group">
+                      <span class="input-group-addon">Compound Name</span>
+                      <input
+                        id="add-one-cc-s1-value-{{ TAB_INDEX_PLACEHOLDER }}"
+                        class="form-control"
+                        placeholder="e.g. Caffeine"
+                        type="text"
+                      />
+                      <span class="input-group-btn">
+                        <button
+                          class="btn btn-default"
+                          type="button"
+                          onclick="ctx().searchLocalCompound();"
+                        >
+                          <i class="fa fa-search"></i>
+                        </button>
+                      </span>
+                    </div>
+                    <div id="load-step-1-{{ TAB_INDEX_PLACEHOLDER }}" style="display: none;">
+                      <img src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/img/ajax-loader.gif" title="please wait">
+                    </div>
+                    <div
+                      id="ok-step-1-{{ TAB_INDEX_PLACEHOLDER }}"
+                      style="overflow: auto; max-height: 300px;"
+                    ></div>
+                  </div>
+                  <div class="modal-footer">
+                    <button
+                      type="button"
+                      class="btn btn-danger"
+                      data-dismiss="modal"
+                      onclick="ctx().clearLine()"
+                    >
+                      <i class="fa fa-eraser"></i> Clear
+                    </button>
+                    <button
+                      type="button"
+                      class="btn btn-default"
+                      data-dismiss="modal"
+                    >Fermer</button>
+                  </div>
+                </div>
+              </div>
+            </div>
+            <div
+              class="modal "
+              id="modalShowSpectra-{{ TAB_INDEX_PLACEHOLDER }}"
+              tabindex="-1"
+              role="dialog"
+              aria-labelledby="modalShowSpectraLabel-{{ TAB_INDEX_PLACEHOLDER }}"
+              aria-hidden="true"
+            >
+              <div class="modal-dialog">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <button
+                      type="button"
+                      class="close"
+                      data-dismiss="modal"
+                      aria-hidden="true"
+                    >×</button>
+                    <h4
+                      class="modal-title"
+                      id="modalShowSpectraLabel-{{ TAB_INDEX_PLACEHOLDER }}"
+                    >Modal title</h4>
+                  </div>
+                  <div class="modal-body">
+                    <div class="te"></div>
+                  </div>
+                  <div class="modal-footer">
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div>
+        <button
+          onclick="send_everything_to_peakforest('{{ PF_URL_PLACEHOLDER }}', '{{ PF_TOKEN_PLACEHOLDER }}')"
+          class="btn btn-success"
+        >
+          <i class="fa fa-arrow-circle-right"></i>
+          Send everything to peakforest!
+        </button>
+        <button class="btn btn-validate" onclick="ctx().exit()">
+          Close MS2PF
+        </button>
+      </div>
+      <script
+        type="text/javascript"
+        src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/jqueryform/2.8/jquery.form.min.js"
+      ></script>
+      <script
+        src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/md5.min.js"
+      ></script>
+    </div>
+    <nav class="navbar navbar-default navbar-fixed-bottom navbar-inverse navbar-main" role="navigation">
+      <div class="navbar-header navbar-inverse footer">
+        <ul id="ul-info-footer" class="nav navbar-nav">
+          <li class="li-info-footer"><a id="linkcontact" href="mailto:contact@peakforest.org?subject=%5Babout%20peakforest%20webapp%5D">Contact</a></li>
+          <li class="li-info-footer">
+            <a id="about-peakforest" href="about-peakforest" data-toggle="modal" data-target="#modalAbout">À propos</a>
+          </li>
+          <li class="li-info-footer">
+            <a href="https://peakforest.org/mypeakforest" target="_blank">Ma PeakForest</a>
+          </li>
+          <li class="li-info-footer">
+            <a href="http://metabohub.fr" target="_blank">metaboHUB</a>
+          </li>
+        </ul>
+      </div>
+    </nav>
+    <div
+      class="modal"
+      id="modalAbout"
+      tabindex="-1"
+      role="dialog"
+      aria-labelledby="modalAboutLabel"
+      aria-hidden="true"
+    >
+      <div class="modal-dialog">
+        <div class="modal-content">
+          <div class="modal-header">
+            <button
+              type="button"
+              class="close"
+              data-dismiss="modal"
+              aria-hidden="true"
+            >×</button>
+            <h4 class="modal-title" id="modalAboutLabel">À propos de la Base de Données PeakForest</h4>
+          </div>
+          <div class="modal-body">
+            <div class="te"></div>
+          </div>
+          <div class="modal-footer">
+          </div>
+        </div>
+      </div>
+    </div>
+    <div
+      class="modal"
+      id="modalMyPeakForest"
+      tabindex="-1"
+      role="dialog"
+      aria-labelledby="modalMyPFLabel"
+      aria-hidden="true"
+    >
+      <div class="modal-dialog">
+        <div class="modal-content">
+          <div class="modal-header">
+            <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+            <h4 class="modal-title" id="modalAboutLabel">Ma base de Données PeakForest</h4>
+          </div>
+          <div class="modal-body">
+            <div class="te"></div>
+          </div>
+          <div class="modal-footer">
+          </div>
+        </div>
+      </div>
+    </div>
+    {{ EMBED_JS }}
+    <script type="text/javascript">context = context_1 ;</script>
+  </body>
+</html>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/meta-compound-ref.html	Fri Mar 03 14:10:24 2023 +0000
@@ -0,0 +1,112 @@
+<!doctype HTML>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta name="author" content="MetaboHUB">
+    <link rel="icon" type="image/ico" href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/ico/favicon.ico">
+    <title>PeakForest MetaboHub</title>
+    <meta name="keywords" content="spectral database, mass spectrometry, nmr, lc-ms, gc-ms, chemical, metabolomic, compound, library">
+    <meta name="description" content="PeakForest is a spectral data portal for Metabolomics community. It provides storage and annotation services for metabolic profils of biological matrix. It relies on the wide range of complementary methods using UPLC-(API)HRMS, GC-QToF, and NMR.">
+    <style>.file-input-wrapper { overflow: hidden; position: relative; cursor: pointer; z-index: 1; }.file-input-wrapper input[type=file], .file-input-wrapper input[type=file]:focus, .file-input-wrapper input[type=file]:hover { position: absolute; top: 0; left: 0; cursor: pointer; opacity: 0; filter: alpha(opacity=0); z-index: 99; outline: 0; }.file-input-name { margin-left: 8px; }</style>
+    <link href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/css/bootstrap.min.css" rel="stylesheet">
+    <link href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/css/sb-admin.min.css" rel="stylesheet">
+    <link rel="stylesheet" href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/font-awesome/css/font-awesome.min.css">
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/jquery.min.js"></script>
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/bootstrap.min.js"></script>
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/bootstrap-typeahead.min.js"></script>
+    <link href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/css/bootstrap-select.min.css" media="screen" rel="stylesheet" type="text/css">
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/bootstrap-select.min.js" type="text/javascript"></script>
+    <link href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/css/bootstrap-datepicker.min.css" media="screen" rel="stylesheet" type="text/css">
+    <script type="text/javascript" src="/common.js"></script>
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/bootstrap-datepicker.min.js" type="text/javascript"></script>
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/jquery.tmpl.min.js" type="text/javascript"></script>
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/highcharts/js/highcharts.min.js"></script>
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/highcharts/js/modules/exporting.min.js"></script>
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/highcharts/js/themes/grid.min.js"></script>
+    <link href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/css/bootstrap.overwrite.min.css" rel="stylesheet" media="screen">
+    <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/peakforest.min.js"></script>
+    <script type="text/javascript" src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/tablesorter/jquery.tablesorter.min.js"></script>
+    <script type="text/javascript" src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/tablesorter/tables.min.js"></script>
+    <link rel="stylesheet" href="{{ PF_URL_PLACEHOLDER }}/webapp/resources/nmrpro/specdraw.min.css" type="text/css">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+  </head>
+  <body>
+      <div id="page-wrapper" class="peakforest-main-wrapper">
+        <div>
+          <button class="btn btn-validate" onclick="ctx().exit()">Close MS2PF</button>
+        </div>
+        <div class="row" style="margin-bottom: 50px;">
+          <ul class="nav nav-tabs" style="margin-bottom: 15px;">
+            {{ TAB_LIST_PLACEHOLDER }}
+          </ul>
+          <div id="add-spectrum" class="tab-content">
+            {{ ADD_SPECTRUM_FORM }}
+          </div>
+        </div>
+        <div>
+          <button
+            onclick="send_everything_to_peakforest(
+              '{{ PF_URL_PLACEHOLDER }}',
+              token
+            )" class="btn btn-success">
+            <i class="fa fa-arrow-circle-right"></i>Send everything to peakforest!
+          </button>
+          <button class="btn btn-validate" onclick="ctx().exit()">Close MS2PF</button>
+        </div>
+        <script type="text/javascript" src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/jqueryform/2.8/jquery.form.min.js"></script>
+        <script src="{{ PF_URL_PLACEHOLDER }}/webapp/resources/js/md5.min.js"></script>
+      </div>
+    </div>
+    <nav class="navbar navbar-default navbar-fixed-bottom navbar-inverse navbar-main" role="navigation">
+      <div class="navbar-header navbar-inverse footer">
+        <ul id="ul-info-footer" class="nav navbar-nav">
+          <li class="li-info-footer"><a id="linkcontact" href="mailto:contact@peakforest.org?subject=%5Babout%20peakforest%20webapp%5D">Contact</a></li>
+          <li class="li-info-footer">
+            <a id="about-peakforest" href="about-peakforest" data-toggle="modal" data-target="#modalAbout">À propos</a>
+          </li>
+          <li class="li-info-footer">
+            <a href="https://peakforest.org/mypeakforest" target="_blank">Ma PeakForest</a>
+          </li>
+          <li class="li-info-footer">
+            <a href="http://metabohub.fr" target="_blank">metaboHUB</a>
+          </li>
+        </ul>
+      </div>
+    </nav>
+    <div class="modal " id="modalAbout" tabindex="-1" role="dialog" aria-labelledby="modalAboutLabel" aria-hidden="true">
+      <div class="modal-dialog">
+        <div class="modal-content">
+          <div class="modal-header">
+            <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+            <h4 class="modal-title" id="modalAboutLabel">À propos de la Base de Données PeakForest</h4>
+          </div>
+          <div class="modal-body">
+            <div class="te"></div>
+          </div>
+          <div class="modal-footer">
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="modal " id="modalMyPeakForest" tabindex="-1" role="dialog" aria-labelledby="modalMyPFLabel" aria-hidden="true">
+      <div class="modal-dialog">
+        <div class="modal-content">
+          <div class="modal-header">
+            <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+            <h4 class="modal-title" id="modalAboutLabel">Ma base de Données PeakForest</h4>
+          </div>
+          <div class="modal-body">
+            <div class="te"></div>
+          </div>
+          <div class="modal-footer">
+          </div>
+        </div>
+      </div>
+    </div>
+    {{ EMBED_JS }}
+    <script type="text/javascript">
+      context = context_1 ; // ,
+    </script>
+  </body>
+</html>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ms2pf_it.xml	Fri Mar 03 14:10:24 2023 +0000
@@ -0,0 +1,144 @@
+<tool id="ms_to_peakforest_it" name="MS To PeakForest IT" tool_type="interactive" version="@TOOL_VERSION@+galaxy@VERSION_SUFFIX@" profile="21.09">
+    <description>
+        adds you MS spectrum to peakforest.
+    </description>
+    <macros>
+        <token name="@VERSION_SUFFIX@">0</token>
+        <import>macros.xml</import>
+    </macros>
+    <requirements>
+        <requirement type="package" version="3.10">python</requirement>
+        <requirement type="package">pyyaml</requirement>
+    </requirements>
+    <entry_points>
+        <entry_point name="MS to PeakForest, with ${','.join(str(i.name) for i in $input)}" requires_domain="True">
+            <port>8000</port>
+            <url>/</url>
+        </entry_point>
+    </entry_points>
+    <version_command>
+<![CDATA[
+python3 '$__tool_directory__/server.py' --version | head -n 1
+]]>
+    </version_command>
+    <command>
+@MS2PF_COMMON_CMD@
+    </command>
+    <inputs>
+        <expand macro="ms2pf_inputs" />
+    </inputs>
+    <outputs>
+        <data name="ms2pfout" format="txt"
+            label="${tool.name} on ${','.join(i.name for i in $input)}"
+        />
+        <data name="json_output" format="json"
+            label="JSON ${tool.name} on ${','.join(i.name for i in $input)}.json"
+        >
+            <filter>do_output_json</filter>
+        </data>
+    </outputs>
+    <tests>
+    </tests>
+    <help>
+<![CDATA[
+.. class:: infomark
+
+**Authors** Lain Pavot (lain.pavot@inrae.fr)
+
+.. class:: infomark
+
+---------------------------------------------------
+
+==============
+MS2 validation
+==============
+
+-----------
+Description
+-----------
+
+
+-----------------
+Workflow position
+-----------------
+
+--------------
+Upstream tools
+--------------
+
++-------------------------+-----------------+--------+------------+
+| Name                    |  output file    | format |  parameter |
++=========================+=================+========+============+
+| ms2snoop                |       tsv       |   TSV  |    NA      |
++-------------------------+-----------------+--------+------------+
+| abinitfragnot           |       tsv       |   TSV  |    NA      |
++-------------------------+-----------------+--------+------------+
+
+----------------
+Downstream tools
+----------------
+
++-------------------------+-----------------+--------+------------+
+| Name                    |  output file    | format |  parameter |
++=========================+=================+========+============+
+| NA                      |       NA        |   NA   |    NA      |
++-------------------------+-----------------+--------+------------+
+
+
+-----------
+Input files
+-----------
+
++----------------------------------------------+------------+
+| Parameter : num + label                      |   Format   |
++===========================+==================+============+
+| Output from ms2snoop or fragnot              |   TSV,CSV  |
++----------------------------------------------+------------+
+
+----------
+Parameters
+----------
+
+input
+  | The file to process. Should contain spectra with mz, intensities and
+  annotations
+
+method
+  | The method parameter tells which method was used. The value will
+    be the default value in the peakforest form.
+
+peakforest-url
+  | The peakforest-url parameter tells which instance of peakforest to
+    interact with
+
+scan_type
+  | The scan_type parameter which kind of scan was used. The value will
+    be the default value in the peakforest form.
+
+polarity
+  | The polarity parameter which polarity was used. The value will
+    be the default value in the peakforest form.
+
+name
+  | The name parameter provides names each spectra.
+
+
+------------
+Output files
+------------
+
+There is not output file. When you run this tool, keep waiting until the
+« click here to display » link appears.
+
+Click on the link to display peakforest form.
+
+--------------
+Changelog/News
+--------------
+
+1.0.0
+  | First version, published on the toolshed.
+
+]]>
+    </help>
+</tool>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server.py	Fri Mar 03 14:10:24 2023 +0000
@@ -0,0 +1,1120 @@
+#!/usr/bin/env python3
+
+import atexit
+import csv
+import http.server
+import json
+import logging
+import os
+import re
+import shutil
+import socketserver
+import sys
+import tempfile
+import yaml
+
+TAB_LIST_PLACEHOLDER = "TAB_LIST_PLACEHOLDER"
+MS_PEAK_VALUES_PLACEHOLDER = "MS_PEAK_VALUES_PLACEHOLDER"
+COMPOUND_NAME_PLACEHOLDER = "COMPOUND_NAME_PLACEHOLDER"
+TAB_INDEX_PLACEHOLDER = "TAB_INDEX_PLACEHOLDER"
+EMBED_JS_PLACEHOLDER = "EMBED_JS"
+ACTIVE_TAB_PLACEHOLDER = "ACTIVE_TAB_PLACEHOLDER"
+ADD_SPECTRUM_FORM = "ADD_SPECTRUM_FORM"
+PRODUCE_JSON_PLACEHOLDER = "PRODUCE_JSON_PLACEHOLDER"
+
+COMPOUND_REF = "compound-ref"
+COMPOUND_MIX = "compound-mix"
+
+END_MS_PEAK_VALUES_PLACEHOLDER = "  ]"
+MS_DATA_COLUMN_NUMBER = 9
+DEFAULT_MS_PEAK_VALUES = (
+  "[\n"
+  + ("  [" + ','.join([' ""'] * MS_DATA_COLUMN_NUMBER) + "],\n") * 17
+  + END_MS_PEAK_VALUES_PLACEHOLDER
+)
+
+FRAGNOT_HEADER = {
+  "m/z": "fragment_mz",
+  "absolute_intensity": "abs_intensity",
+  "relative_intensity": "rel_intensity",
+  "theo_mass": "",
+  "delta_ppm": "ppm",
+  "rdbequiv": "",
+  "composition": "",
+  "attribution": "fragment",
+}
+
+MS_2_SNOOP_HEADER = {
+  "name": str,
+  "inchikey": str,
+  "composition": str,
+  "fragment": str,
+  "fragment_mz": str,
+  "ppm": str,
+  "fileid": str,
+  "correlation": str,
+  "abs_intensity": lambda x:float(x) * 100,
+  "rel_intensity": lambda x:float(x) * 100,
+  "valid_corelation": str
+}
+
+
+class ConfigException(ValueError):
+  """
+  An exception raised when something went wrong in the config and we
+  cannot continue - i.e: when there's no token for peakforest
+  """
+
+class YAMLConfig(dict):
+
+  """
+  Dictionary that handles key with dot in them:
+  test["truc.chose"]
+  is equivalant to
+  test["truc"]["chose"]
+  Assignation works too.
+  Add the possibility to use placeholders:
+  --- yaml
+  test: {{ truc.chose }}
+  truc:
+    chose: bidule
+  ---
+  here, test's value is "bidule"
+  """
+
+  def __init__(self, *args, **kwargs):
+    meta_conf = kwargs.pop("__meta_config__", {})
+    self._debug = meta_conf.get("__debug__", False)
+    self._stream_name = meta_conf.get("__debug_stream__", "stdout")
+    self._debug_stream = getattr(sys, self._stream_name)
+    self._only_root_debug = meta_conf.get("__only_root_debug__", False)
+    if "__root__" in kwargs:
+      if self._only_root_debug:
+        self._debug = False
+      self._name = kwargs.pop("__name__")
+      self._debugger("Is not root config.")
+      self._root = kwargs.pop("__root__")
+    else:
+      self._name = "root"
+      self._debugger("Is root config.")
+      self._root = self
+    super().__init__(*args, **kwargs)
+    for key, value in self.copy().items():
+      if isinstance(value, dict) and not isinstance(value, YAMLConfig):
+        self._debugger(f"Parsing sub-config for {key}")
+        self[key] = self._propagate(value, key)
+    self._replace_placeholders(self)
+    self._extract_defaults()
+
+  def _propagate(self, sub_dict, name):
+    if isinstance(sub_dict, dict) and not isinstance(sub_dict, self.__class__):
+      return YAMLConfig(
+        **sub_dict,
+        __name__=name,
+        __root__=self._root,
+        __meta_config__={
+          "__debug__": self._debug,
+          "__debug_stream__": self._stream_name,
+          "__only_root_debug__": self._only_root_debug,
+        }
+      )
+    return sub_dict
+
+  def _debugger(self, message):
+    if self._debug:
+      self._debug_stream.write(f"[{self._name}]: {message}\n")
+      self._debug_stream.flush()
+
+  def __getattr__(self, attr):
+    if attr in self:
+      return self[attr]
+    if '.' in attr:
+      attr, sub = attr.split('.', 1)
+      return getattr(getattr(self, attr), sub)
+    return super().__getattribute__(attr)
+
+  def _replace_placeholders(self, subpart):
+    self._debugger("Replacing placeholders...")
+    for sub_key, sub_item in subpart.copy().items():
+      if isinstance(sub_item, str):
+        for placeholder in re.findall("{{ (?P<placeholder>.*?) }}", sub_item):
+          if placeholder not in self._root:
+            self._debugger(f"Could not fine replacement for {placeholder}")
+            continue
+          replacement = self._root[placeholder]
+          if isinstance(replacement, str):
+            self._debugger(f"Found placeholder: {placeholder} -> {replacement}")
+            sub_item = sub_item.replace(
+              "{{ " + placeholder + " }}",
+              replacement
+            )
+          else:
+            self._debugger(f"Found placeholder: {placeholder} -> {replacement.__class__.__name__}")
+            sub_item = self._propagate(replacement, placeholder)
+          dict.__setitem__(subpart, sub_key, sub_item)
+      elif isinstance(sub_item, dict):
+        super().__setitem__(sub_key, self._propagate(sub_item, sub_key))
+
+  def _extract_defaults(self):
+    if self._root is not self:
+      return
+    if "defaults" not in self:
+      self._debugger("No defaults here.")
+      return
+    if "arguments" not in self:
+      self._debugger("Arguments creation...")
+      self["arguments"] = self._propagate({}, "arguments")
+    self._debugger("Populating arguments with defaults values")
+    for key, value in self.defaults.items():
+      if key not in self:
+        if isinstance(value, dict):
+          value = self._propagate(value, key)
+        self.arguments[key] = value
+        self._debugger(f"Default {key} = {value}")
+
+  def __setitem__(self, key, value):
+    if isinstance(value, dict):
+      value = self._propagate(value, key)
+    if "." not in key:
+      return super().__setitem__(key, value)
+    curent = self
+    key, subkey = key.rsplit(".", 1)
+    self[key][subkey] = value
+
+  def __getitem__(self, key):
+    if super().__contains__(key):
+      return super().__getitem__(key)
+    if "." not in key:
+      return super().__getitem__(key)
+    curent = self
+    while "." in key:
+      key, subkey = key.split(".", 1)
+      curent = curent[key]
+      key = subkey
+    if subkey not in curent:
+      curent[subkey] = self._propagate({}, subkey)
+    result = curent[subkey]
+    return result
+
+  def __contains__(self, key):
+    if "." not in key:
+      return super().__contains__(key)
+    key, subkey = key.split(".", 1)
+    if not super().__contains__(key):
+      return False
+    return subkey in self[key]
+
+  def copy(self):
+    return {
+      key: (
+        value if not isinstance(value, dict)
+        else value.copy()
+      ) for key, value in self.items()
+    }
+
+class YAMLParameters(YAMLConfig):
+
+  """
+  Parses parameters from the command line and put them
+  in the config.
+  Uses the config to know which parameter is recognized, or not,
+  to know the metadata (author, version),
+  which command is a flag, is optional, the help strings, etc...
+  Assigns default small parameter if not defined in the "shortcut"
+  section of the config file.
+  CLI config must be in the root section "parameters":
+  ---
+  parameters:
+    mandatory:
+      input: input file path
+    flags:
+      help: Show this help
+    optional:
+      method: "default is {{ defaults.method }}"
+    meta:
+      author: Lain Pavot
+      version: 1.1.0
+    shortcuts:
+      help: h
+      ## will autogenerate -i for input and -m for method
+  ---
+  default parameters are searched in the "default" root section.
+  """
+
+  def __init__(self, *args, **kwargs):
+    super().__init__(*args, **kwargs)
+    self._errors = list()
+    if not self.parameters.shortcuts:
+      self.parameters["shortcuts"] = YAMLConfig()
+    self._mandatory = self.parameters.mandatory
+    self._optional = self.parameters.optional
+    self._flags = {
+      flag: False
+      for flag in self.parameters.flags
+    }
+    self._all_params = self._optional.copy()
+    self._all_params.update(self._mandatory)
+    self._all_params.update(self._flags)
+    self._small_params = dict()
+    self._determine_small_params()
+
+  @property
+  def in_error(self):
+    return bool(self._errors)
+
+  @property
+  def sorted_keys(self):
+    return sorted(self._all_params.keys())
+  
+  @property
+  def sorted_items(self):
+    return sorted(self._all_params.items())
+
+  def _determine_small_params(self, verbose=False):
+    self._small_params = (self.parameters.shortcuts or {}).copy()
+    chars = list(map(chr, range(97, 123))) + list(map(chr, range(65, 91)))
+    all_params = self._all_params.copy()
+    for long, short in self._small_params.items():
+      chars.remove(short)
+      del all_params[long]
+    for param in all_params.copy().keys():
+      for operation in (
+        lambda x:x[0],  ## select first char
+        lambda x:x.split('-', 1)[-1][0], ## first char after -
+        lambda x:x.split('_', 1)[-1][0], ## first char after _
+        lambda x:x.split('.', 1)[-1][0], ## first char after .
+        lambda x:x[0].upper(),  ## select first char
+        lambda x:x.split('-', 1)[-1][0].upper(), ## first char after -
+        lambda x:x.split('_', 1)[-1][0].upper(), ## first char after _
+        lambda x:x.split('.', 1)[-1][0].upper(), ## first char after .
+        lambda x: chars[0], ## first letter in the alphabet
+      ):
+        char = operation(param)
+        if char not in self._small_params.values():
+          self._small_params[param] = char
+          chars.remove(char)
+          del all_params[param]
+          break
+
+  def _get_parameter_index(self, parameter, original):
+    if f"--{parameter}" in sys.argv:
+      return sys.argv.index(f"--{parameter}")
+    parameter = self._small_params[original]
+    if f"-{parameter}" in sys.argv:
+      return sys.argv.index(f"-{parameter}")
+    return None
+
+  def as_parameter(self, string):
+    return (
+      string
+      .replace('.', '-')
+      .replace('_', '-')
+    )
+
+  def show_version(self):
+    print(self.parameters.meta.version)
+
+  def show_help(self):
+    parameters = [
+      f"-{self._small_params[arg]}|--{self.as_parameter(arg)} {arg}"
+      for arg in self._mandatory
+    ] + [
+      f"[-{self._small_params[arg]}|--{self.as_parameter(arg)} {arg}]"
+      for arg in self._optional
+    ] + [
+      f"[-{self._small_params[arg]}|--{self.as_parameter(arg)}]"
+      for arg in self._flags
+    ]
+    print(
+      f"Usage: {__file__} " + ' '.join(parameters)
+      + "\n\n"
+      + '\n'.join(
+        f"    -{self._small_params[args]}|--{self.as_parameter(args)}: {help_str}"
+        for args, help_str in self.sorted_items
+      )
+      + "\n\n"
+      + '\n'.join(
+        f"{key}: {value}"
+        for key, value in self.parameters.meta.items()
+      )
+    )
+    sys.exit(0)
+
+  def parse_args(self):
+    errors = list()
+    for kind in ("mandatory", "optional", "flags"):
+      keys = list(sorted(getattr(self, f"_{kind}").keys()))
+      for original_param, actual_param in zip(
+        keys,
+        map(self.as_parameter, keys),
+      ):
+        if original_param in self.defaults:
+          self.arguments[original_param] = self.defaults[original_param]
+        elif kind == "flags":
+          self.arguments[original_param] = False
+        parser = getattr(self, f"parse_{kind}")
+        if (error := parser(original_param, actual_param)):
+          errors.append(error)
+    self._errors = errors
+    return self
+
+  def parse_mandatory(self, original, actual):
+    if (index := self._get_parameter_index(actual, original)) is None:
+      return f"The parameter --{actual} is mandatory."
+    if index == len(sys.argv) - 1:
+      return f"The parameter --{actual} needs a value."
+    self.arguments[original] = sys.argv[index + 1]
+
+  def parse_optional(self, original, actual):
+    if (index := self._get_parameter_index(actual, original)) is None:
+      return
+    if index == len(sys.argv) - 1:
+      return f"The parameter --{actual} needs a value."
+    self.arguments[original] = sys.argv[index + 1]
+
+  def parse_flags(self, original, actual):
+    if (index := self._get_parameter_index(actual, original)) is None:
+      return
+    self.arguments[original] = True
+
+def parse_config(**kwargs):
+  """
+  opens the config file, extract it using pyyaml's safe loader
+  and tries to extract and apply a maximum of informations/directives
+  from the config:
+   - token retrieval
+   - workdir management
+   - tempfile management
+  """
+  root_dir = os.path.dirname(os.path.abspath(__file__))
+  with open(os.path.join(root_dir, "config.yml")) as config_file:
+    config = YAMLConfig(
+      **yaml.load(config_file.read(), Loader=yaml.SafeLoader),
+      **kwargs
+    )
+
+  if not config.token.value:
+    if config.token.use_file:
+      if (not os.path.exists(path := config.token.file_path)):
+        raise ConfigException("Missing token value or token file.")
+      with open(path) as token_file:
+        config.token["value"] = token_file.read()
+    elif config.defaults.peakforest.token:
+      config.token["value"] = config.defaults.peakforest.token
+
+  if config.workdir.create_tmp:
+    tmp_dir = tempfile.mkdtemp()
+    atexit.register(lambda:shutil.rmtree(tmp_dir))
+  else:
+    tmp_dir = tempfile.gettempdir()
+  config.workdir["tmp_dir"] = tmp_dir
+
+  config["root_dir"] = root_dir
+  config["tab_list"] = []
+  config["form_template"] = os.path.join(root_dir, config.templates.form)
+  config["meta_template"] = os.path.join(root_dir, config.templates.main)
+  config["js_template"] = os.path.join(root_dir, config.templates.js)
+  config["tab_list_template"] = os.path.join(root_dir, config.templates.tab_list)
+  config["placeholders"] = dict()
+  config.placeholders[MS_PEAK_VALUES_PLACEHOLDER] = DEFAULT_MS_PEAK_VALUES
+  config.placeholders[TAB_INDEX_PLACEHOLDER] = "1"
+  config.placeholders[ACTIVE_TAB_PLACEHOLDER] = "active"
+  config.placeholders[ADD_SPECTRUM_FORM] = ""
+  config.placeholders[EMBED_JS_PLACEHOLDER] = ""
+  config.placeholders[TAB_LIST_PLACEHOLDER] = ""
+  config.placeholders["DEFAULT_MIN_MZ"] = "50"
+  config.placeholders["DEFAULT_MAX_MZ"] = "500"
+  config.placeholders["DEFAULT_RESOLUTION_LOW"] = ""
+  config.placeholders["DEFAULT_RESOLUTION_HIGH"] = "selected=\"selected\""
+  config.placeholders["DEFAULT_RESOLUTION_UNSET"] = ""
+  config.placeholders["DEFAULT_MIN_RT"] = "0.9"
+  config.placeholders["DEFAULT_MAX_RT"] = "1.4"
+  return config
+
+def parse_parameters(config):
+  """
+  parses command line and checks provided values are acceptable/usable.
+  Raises some error if not.
+  """
+  parameters = YAMLParameters(**config)
+  parameters.parse_args()
+
+  parameters["json_result"] = []
+
+  get_logger(parameters)
+
+  arguments = parameters.arguments
+  if arguments.help:
+    parameters.show_help()
+    sys.exit(0)
+
+  if arguments.version:
+    parameters.show_version()
+    sys.exit(0)
+
+  if parameters.in_error:
+    raise ValueError(
+      "Some errors occured during parameters extraction: \n"
+      + '\n'.join(parameters.errors)
+    )
+
+  if arguments.sample_type == COMPOUND_MIX:
+    parameters["form_template"] = os.path.join(
+      parameters["root_dir"],
+      parameters.templates.form_mix
+    )
+    parameters["meta_template"] = os.path.join(
+      parameters["root_dir"],
+      parameters.templates.main_mix
+    )
+  elif arguments.sample_type == COMPOUND_REF:
+    parameters["form_template"] = os.path.join(
+      parameters["root_dir"],
+      parameters.templates.form_ref
+    )
+    parameters["meta_template"] = os.path.join(
+      parameters["root_dir"],
+      parameters.templates.main_ref
+    )
+
+  arguments["produce_json"] = (
+    "output_json" in arguments
+    and arguments["output_json"] != ""
+  )
+  if arguments.produce_json:
+    parameters.placeholders[PRODUCE_JSON_PLACEHOLDER] = "true"
+    parameters.json_result = []
+    arguments["output_json"] = os.path.abspath(arguments["output_json"])
+    atexit.register(save_json, parameters)
+  else:
+    parameters.placeholders[PRODUCE_JSON_PLACEHOLDER] = "false"
+
+  if arguments.run_dry_html:
+    arguments["do_run_dry"] = True
+    parameters.generated["html"] = os.path.abspath(arguments.run_dry_html)
+
+  if arguments.run_dry_js:
+    arguments["do_run_dry"] = True
+    parameters.generated["js"] = os.path.abspath(arguments.run_dry_js)
+
+  if arguments.do_run_dry:
+    parameters.logger.info("Dry run. Server will ne be run.")
+    if arguments.run_dry_html:
+      parameters.logger.info(f"HTML file will be put in {arguments.run_dry_html}")
+    if arguments.run_dry_js:
+      parameters.logger.info(f"JS file will be put in {arguments.run_dry_js}")
+
+  if arguments.peakforest.token:
+    config.token["value"] = arguments.peakforest.token
+  if not config.token.value:
+    raise ConfigException(
+      "No token provided. We will not be able to connect to peakforest."
+    )
+
+  if os.path.exists(arguments.input):
+    single_file = True
+    file_paths = [arguments.input]
+  else:
+    path_list = arguments.input.split(',')
+    if all(map(os.path.exists, path_list)):
+      single_file = False
+      file_paths = path_list
+    else:
+      raise ValueError(
+        f"Some files cannot be found: "
+        + ', '.join(
+          path for path in path_list
+          if not os.path.exists(path)
+        )
+      )
+  arguments["input"] = list(map(os.path.abspath, file_paths))
+
+  if single_file:
+    arguments["name"] = [arguments.name]
+    arguments["raw_metadata"] = [arguments.raw_metadata]
+    parameters.logger.info(f"Single file processing: {arguments.input}")
+  else:
+    parameters.logger.info(f"Multiple file processing:")
+    arguments["raw_metadata"] = arguments.raw_metadata.split(
+      arguments.raw_metadata_sep
+    )
+    if not arguments.name:
+      arguments["name"] = arguments["raw_metadata"]
+    else:
+      arguments["name"] = arguments.name.split(',')
+  for i in range(len(arguments.name)):
+    parameters.logger.info(f"  - file: {arguments.input[i]}")
+    parameters.logger.info(f"  - name: {arguments.name[i]}")
+    parameters.logger.info(f"  - metadata: {arguments.raw_metadata[i]}")
+    parameters.logger.info(f"  ")
+  if (
+    len(arguments.name) != len(arguments.raw_metadata)
+    or len(arguments.name) != len(arguments.input)
+  ):
+    raise ValueError(
+      "name, raw_metadata and input parameters have different lengths: \n"
+      f"input is {len(arguments.input)} elements long, "
+      f"raw_metadata is {len(arguments.raw_metadata)} elements long "
+      f"and name is {len(arguments.name)} elements long."
+    )
+  if arguments.spectrum_type == "LC_MS":
+    arguments["scan_type"] = "ms"
+  elif arguments.spectrum_type == "LC_MSMS":
+    arguments["scan_type"] = "ms2"
+  if arguments.method == "test":
+    if arguments.spectrum_type == "LC_MS":
+      arguments["method"] = "cf_pfem_urine_qtof"
+    else:
+      arguments["method"] = "cf_pfem_urine_method1_qtof-msms"
+  if arguments["sample_type"] == COMPOUND_MIX:
+    check_mix_compound_files(parameters)
+  more_info_in_logs(parameters)
+  return parameters
+
+def check_mix_compound_files(parameters):
+  arguments = parameters.arguments
+  try:
+    numbarz = [
+      list(map(int, os.path.basename(metadata).split("_", 1)[0].split("-")))
+      for metadata in arguments.raw_metadata
+    ]
+  except ValueError:
+    parameters.logger.error(
+      "Metadata/file names does not start with `[0-9]+-[0-9]+_.*` . "
+      "This is necessary in the case of compounds mix."
+    )
+    sys.exit(-1)
+  runs, samples = zip(*numbarz)
+  if not all(runs[0] == i for i in runs[1:]):
+    parameters.logger.error(
+      "Run numbers in metadata/file names are not identical. "
+      "You mixed some files."
+    )
+    sys.exit(-1)
+  length = len(samples)
+  if list(sorted(samples)) != list(range(1, length+1)):
+    if not all(samples.count(i) == 1 for i in samples):
+      parameters.logger.error("Some samples are duplicated. ")
+    else:
+      parameters.logger.error("Some samples files are missing. ")
+    sys.exit(-1)
+
+def more_info_in_logs(config):
+  arguments = config.arguments
+  if arguments.embed_js:
+    config.logger.info(f"JS will be embed in HTML page to form a HTML bundle.")
+  else:
+    config.logger.info(f"JS are separated files, needed to be served.")
+  config.logger.info(f"Choosen parameters:")
+  config.logger.info(f"  - method: {arguments.method}")
+  config.logger.info(f"  - peakforest instance: {arguments.peakforest.url}")
+  config.logger.info(f"  - polarity instance: {arguments.polarity}")
+  config.logger.info(f"  - spectrum type: {arguments.spectrum_type}")
+  config.logger.info(f"  - scan type: {arguments.scan_type}")
+  config.logger.info(f"  - produce JSON: {arguments.produce_json}")
+  config.logger.info(f"  - sample type: {arguments.sample_type}")
+
+def process_all_files(config):
+  """
+  for each file and its metadata, read and process them,
+  then fills the meta html template file with the whole result.
+  """
+  arguments = config.arguments
+  extra_defaults = [
+    process_fragnot_metadata(metadata, config)
+    for metadata in arguments.raw_metadata
+  ]
+  for i, name in enumerate(arguments.name):
+    extra_defaults[i]["name"] = name
+
+  if not extra_defaults:
+    extra_defaults = [{}] * len(arguments.input)
+
+  index = 0
+  for input_path, extra_default in zip(arguments.input, extra_defaults):
+    config.logger.info(f"Processing file at {input_path}...")
+    curent_defaults = arguments.copy()
+    curent_defaults.update(extra_default)
+    if config.arguments.verbose:
+      config.logger.info(
+        "[VERBOSE] Defaults for curent file: "
+        + ';'.join(f"{key}={value}" for key, value in curent_defaults.items())
+      )
+    tsv_content, tsv_data_extractor = read_input(input_path, config)
+    index = process_tsv(
+      tsv_content,
+      tsv_data_extractor,
+      config,
+      defaults_data = curent_defaults,
+      index = index+1,
+    )
+  if arguments.embed_js:
+    config.logger.info(f"Embeding JS in HTML file... ")
+    for index in range(len(config.tab_list)):
+      config.placeholders[EMBED_JS_PLACEHOLDER] += "<script type='text/javascript'>"
+      with open(f"add-one-spectrum-{index+1}.js") as js_file:
+        config.placeholders[EMBED_JS_PLACEHOLDER] += js_file.read()
+      config.placeholders[EMBED_JS_PLACEHOLDER] += "</script>"
+      config.placeholders[EMBED_JS_PLACEHOLDER] += "\n"
+      config.logger.info(f"  - add-one-spectrum-{index+1}.js embed.")
+    config.placeholders[TAB_LIST_PLACEHOLDER] = "\n".join(config.tab_list)
+  else:
+    config.placeholders[EMBED_JS_PLACEHOLDER] += "<script type='text/javascript'>"
+    config.placeholders[EMBED_JS_PLACEHOLDER] += "</script>"
+    config.placeholders[EMBED_JS_PLACEHOLDER] += "\n".join(
+      [""] + [
+        " "*12 + f"<script src=\"./add-one-spectrum-{index+1}.js\"></script>"
+        for index in range(len(config.tab_list))
+      ]
+    )
+    config.placeholders[EMBED_JS_PLACEHOLDER] += "\n"
+    config.placeholders[TAB_LIST_PLACEHOLDER] = "\n".join(config.tab_list)
+
+  fill_template("meta_template", "pf_path", config)
+
+def fill_template(
+  template_name,
+  output_name,
+  config,
+  additional_placeholders=dict()
+):
+  """
+  Fills a template, replaces the placeholders.
+  Either outputs the result in a given file, or returns it if path is none.
+  """
+  template_path = config[template_name]
+  config.logger.debug(f"Filling template {template_name} at {template_path}...")
+  with open(template_path) as template_file:
+    template_content = template_file.read()
+  placeholders = config.placeholders.copy()
+  placeholders.update(additional_placeholders)
+  for placeholder, replacement in placeholders.items():
+    if not placeholder.startswith(config.templates.placeholders.start):
+      placeholder = placeholder.join((
+        config.templates.placeholders.start,
+        config.templates.placeholders.stop
+      ))
+    template_content = template_content.replace(placeholder, replacement)
+  if output_name is None:
+    config.logger.debug(f"Returning template content")
+    return template_content
+  output_path = config[output_name]
+  if "{{ index }}" in output_path:
+    index_value = additional_placeholders["{{ index }}"]
+    config.logger.debug(f"Changing index value for {index_value}")
+    output_path = output_path.replace("{{ index }}", index_value)
+  config.logger.debug(f"Full output path {output_path}")
+  with open(output_path, "w") as output_file:
+    output_file.write(template_content)
+
+def read_input(input_path, config):
+  """
+  reads a tsv file and determin its processor, based on its header.
+  """
+  with open(input_path) as input_file:
+    config.logger.info(f"Reading {input_path}...")
+    tsv_file = csv.reader(input_file, delimiter='\t')
+    header = next(tsv_file)
+    tsv_file = list(tsv_file)
+  config.logger.info(f"Header is: {', '.join(header)}")
+  if header == list(FRAGNOT_HEADER):
+    config.logger.info(f"Fragnot recognized.")
+    processor = fragnot_extractor
+    return uniformize_fragnot(tsv_file, header), processor
+  else:
+    config.logger.info(f"MS2Snoop recognized.")
+    processor = ms2snoop_extractor
+    return uniformize_ms2snoop(tsv_file, header), processor
+
+def uniformize_fragnot(content, header):
+  """
+  sorts fragnot data so they appear always in the same order
+  """
+  return sorted(content, key=lambda x:(float(x[0]), float(x[4])))
+
+def uniformize_ms2snoop(content, header):
+  """
+  sorts ms2snoop data so they appear always in the same order
+  """
+  return sorted(content, key=lambda x:(x[0], float(x[4])))
+
+def process_fragnot_metadata(raw_metadata, config):
+  """
+  Tries to extract informations from the metadata provided by fragnot
+  files names.
+  Heavily based on regex defined in conf file.
+  """
+  regex = config.regex.copy()
+  del regex["values"]
+  result = {}
+  config.logger.info(f"Extracting info from {raw_metadata}...")
+  count = 0
+  for name, expression in regex.items():
+    if (match := re.search(expression, raw_metadata)):
+      result[name] = match[name]
+      count += 1
+      did = "+ did"
+    else:
+      did = "- did not"
+    if config.arguments.verbose:
+      config.logger.info(f"  {did} match {expression}")
+  config.logger.info(f"{count} useful informations extracted.")
+  return result
+
+def process_tsv(
+  tsv_content,
+  tsv_data_extractor,
+  config,
+  defaults_data={},
+  index=1
+):
+  """
+  processes one tsv file, containing one or multiple compounds.
+  Creation of the peak table for each compound
+  """
+  tsv_content = list(tsv_content)
+  curent_name, ms_data = get_ms_data(
+    tsv_content[0],
+    tsv_data_extractor,
+    defaults_data,
+    config
+  )
+  _, second_ms_data = get_ms_data(
+    tsv_content[1],
+    tsv_data_extractor,
+    defaults_data,
+    config
+  )
+  ms_peak_table = []
+  config.logger.info(f"Processing compound {curent_name}...")
+  
+  for line in tsv_content:
+    name, new_ms_data = get_ms_data(line, tsv_data_extractor, defaults_data, config)
+    if name != curent_name:
+      new_compound(curent_name, index, ms_data, config, ms_peak_table)
+      curent_name = name
+      index += 1
+      config.logger.info(f"Processing compound {curent_name}...")
+      ms_peak_table = []
+    ms_data = new_ms_data
+    ms_peak_table.append(
+      ", ".join(
+        f'"{value}"' if value not in ("na", "NA")
+        else '""'
+        for value in (
+          ms_data["fragment_mz"],
+          ms_data["abs_intensity"],
+          ms_data["rel_intensity"],
+          ms_data["ppm"],
+          ms_data["composition"],
+          ms_data["fragment"],
+          str(ms_data["valid_corelation"] == "TRUE").lower(),
+          "true" if ms_data.get("correlation") == "1" else "false"
+        )
+      )
+    )
+  new_compound(curent_name, index, ms_data, config, ms_peak_table)
+  return index
+
+def get_ms_data(line, extractor, defaults, config):
+  ms_data = defaults.copy()
+  ms_data.update(extractor(config, *line))
+  return ms_data["name"], ms_data
+
+def new_compound(name, index, ms_data, config, ms_peak_table):
+  """
+  aggregates informations to form the peak table,
+  adds the compound to the tab list,
+  creates the js file for this tab
+  """
+  if len([x for x in ms_peak_table if x.split(", ")[7] == "\"true\""]) > 1:
+    for i in range(len(ms_peak_table)):
+      ms_peak_table[i] = ", ".join(
+        ms_peak_table[i].split(", ")[:-1] + [", \"false\""]
+      )
+  config.placeholders[MS_PEAK_VALUES_PLACEHOLDER] = f"""[
+    {','.join('['+line+']' for line in ms_peak_table)}
+  ]"""
+  tab_list = fill_template(
+    "tab_list_template",
+    None,
+    config, {
+    COMPOUND_NAME_PLACEHOLDER: name,
+    TAB_INDEX_PLACEHOLDER: str(index),
+  })
+  config.tab_list.append(tab_list)
+  create_js_file(index, ms_data, config)
+  config.placeholders[ADD_SPECTRUM_FORM] += fill_template(
+    "form_template",
+    None,
+    config,
+    {TAB_INDEX_PLACEHOLDER: str(index)},
+  )
+  if index == 1:
+    config.placeholders[ACTIVE_TAB_PLACEHOLDER] = ""
+
+def fragnot_extractor(config, *line):
+  """
+  Fragnot processor - extracts one fragnot line of content and
+  produces a uniformised output.
+  """
+  fragnot_data = {
+    FRAGNOT_HEADER[header]: line[i].strip()
+    for i, header in enumerate(FRAGNOT_HEADER)
+  }
+  fragnot_data["composition"] = "unknown"
+  fragnot_data["valid_corelation"] = config.arguments.validation
+  return fragnot_data
+
+def ms2snoop_extractor(config, *line):
+  """
+  Fragnot processor - extracts one ms2snoop line of content and
+  produces a uniformised output.
+  """
+  ms2snoop_data = {
+    header: MS_2_SNOOP_HEADER[header](line[i])
+    for i, header in enumerate(MS_2_SNOOP_HEADER)
+  }
+  return ms2snoop_data
+
+def create_js_file(index, ms_data, config):
+  """
+  fills the js template file for one tab (compound)
+  """
+  if (method := ms_data["method"]):
+    method = f'"{method}"'
+  else:
+    method = "null"
+  if config.arguments.verbose:
+    config.logger.info(
+      "[VERBOSE] "
+      + ';'.join(f"{key}={value}" for key, value in ms_data.items())
+    )
+  fill_template(
+    "js_template",
+    "js_file",
+    config,
+    {
+      TAB_INDEX_PLACEHOLDER: str(index),
+      "INCHIKEY_PLACEHOLDER": ms_data["inchikey"],
+      "DEFAULT_DATA": f"""{{
+        name: "{ms_data["name"]}",
+        inchikey: "{ms_data["inchikey"]}",
+        method: {method},
+        spectrum_type: "{ms_data["spectrum_type"]}",
+        scan_type: "{ms_data["scan_type"]}",
+        polarity: "{ms_data["polarity"]}",
+        resolution: "{ms_data["resolution"]}",
+        sample_type: "{ms_data["sample_type"]}",
+      }}""",
+      "{{ index }}": str(index)
+    },
+  )
+
+def prepare_workplace(config):
+  """
+  prepares the directory we will work in.
+  """
+  if config.workdir.work_in_tmp:
+    os.chdir(config.workdir.tmp_dir)
+    config.logger.info(f"Moving to {os.getcwd()}")
+  if config.workdir.generate_in_tmp:
+    gen_dir = config.workdir.tmp_dir
+  else:
+    gen_dir = tempfile.gettempdir()
+    config.workdir.tmp_dir = gen_dir
+  shutil.copy(os.path.join(config["root_dir"], "common.js"), gen_dir)
+  config.logger.info(f"Outputs will be generated in {config.workdir.tmp_dir}")
+  return gen_dir
+
+def get_hander_for(directory, config):
+  """
+  generates the handler class for the directory we provide.
+  """
+  config["json_result"] = [{}] * len(config.tab_list)
+
+  class HTTPHandler(http.server.SimpleHTTPRequestHandler):
+
+    def __init__(self, *args, **kwargs):
+      super().__init__(*args, **kwargs, directory=directory)
+
+    def do_POST(self):
+      content_length = int(self.headers.get("Content-Length"))
+      json_bytes = self.rfile.read(content_length).decode("utf-8")
+      json_list = json.loads(json_bytes)
+      for i, obj in enumerate(json_list):
+        print(obj)
+        if obj:
+          config["json_result"][i] = obj
+      save_json(config)
+      self.send_head()
+      self.wfile.write(json_bytes.encode("utf-8"))
+      return
+
+    def do_GET(self):
+      if self.path == "/quit":
+        self.path = "/"
+        super().do_GET()
+        exit(0)
+      self.path = os.path.join(directory, self.path)
+      if self.path == "/":
+        self.path = config.generated.html
+      return super().do_GET()
+
+  return HTTPHandler
+
+
+def save_json(config):
+  json_string = json.dumps(config["json_result"])
+  print(json_string)
+  with open(config.arguments.output_json, "w") as json_file:
+    json_file.write(json_string)
+
+def run_server(config):
+  """
+  prepare and runs the server, with the handler for the given directory
+  """
+  ip, port = config.network.ip, config.network.port
+  config.logger.debug(f"IP and port: {ip}:{port}")
+  socketserver.TCPServer.allow_reuse_address = True
+  config.logger.debug(f"Allow reuse adress.")
+  handler = get_hander_for(config.workdir.tmp_dir, config)
+  config.logger.debug(f"Created server handler for {config.workdir.tmp_dir}")
+  config.logger.debug(
+    f"Content of directory {config.workdir.tmp_dir}: "
+    + "\n"
+    + '\n'.join(sorted(
+      f"  - {path}"for path in os.listdir(config.workdir.tmp_dir)
+      ))
+  )
+  config.logger.debug(f"Creating TCP server...")
+  server = socketserver.TCPServer((ip, port), handler)
+  if ip == "0.0.0.0":
+    displayed_ip = "localhost"
+  else:
+    displayed_ip = ip
+  config.logger.debug(f"Serving...")
+  print()
+  print(f"http://{displayed_ip}:{port}")
+  server.serve_forever()
+
+def get_logger(config, dummy=False):
+  dummy_log = lambda msg:dummy and config.logger.info(msg)
+  arguments = config.arguments
+  if not dummy:
+    logger = logging.getLogger(__file__)
+  if arguments.debug:
+    dummy_log(f"Output debug info.")
+    level = logging.DEBUG
+  else:
+    level = logging.INFO
+  if not dummy:
+    logger.setLevel(level)
+    formatter = logging.Formatter(
+      "%(asctime)s - %(levelname)s - %(message)s"
+    )
+  if arguments.logging.std == "err":
+    dummy_log(f"Handler added to output logs in stderr.")
+    if not dummy:
+      handler = logging.StreamHandler(sys.stderr)
+      handler.setLevel(level)
+      handler.setFormatter(formatter)
+      logger.addHandler(handler)
+  elif arguments.logging.std == "out":
+    dummy_log(f"Handler added to output logs in stdout.")
+    if not dummy:
+      handler = logging.StreamHandler(sys.stdout)
+      handler.setLevel(level)
+      handler.setFormatter(formatter)
+      logger.addHandler(handler)
+  else:
+    dummy_log(f"Logs will not be output in stderr not stdout.")
+  if (path := arguments.logging.file.path):
+    dummy_log(f"Add log file: {arguments.logging.file.path}.")
+    if not arguments.logging.file.append:
+      dummy_log(f"Log file content cleaned.")
+      with open(path, "w"):pass
+    else:
+      dummy_log(f"Logs appended to log file.")
+    if not dummy:
+      file_handler = logging.FileHandler(filename=path)
+      file_handler.setLevel(level)
+      file_handler.setFormatter(formatter)
+      logger.addHandler(file_handler)
+  if not dummy:
+    config["logger"] = logger
+    starting_sequence(logger)
+    get_logger(config, dummy=True)
+    return logger
+
+def starting_sequence(logger):
+  logger.info("*bip* *bop*")
+  logger.info("starting...")
+  logger.info("program...")
+  logger.info("MS2PF is running...")
+  logger.info("*bip* *bop* am a robot")
+  atexit.register(stoping_sequence, logger)
+
+def stoping_sequence(logger):
+  logger.info("*bip* *bop*")
+  logger.info("ending...")
+  logger.info("program...")
+  logger.info("MS2PF is shuting down...")
+  logger.info("...robot")
+  logger.info("*bip* *bop*")
+  logger.info("shutdown")
+  logger.info("...")
+
+if __name__ == "__main__":
+
+  base_config = parse_config()
+  config = parse_parameters(base_config)
+
+  """
+  The config contains result of the parsed config file.
+  """
+  arguments = config.arguments
+
+  config.logger.info(f"Starting MS2PF from {os.getcwd()}")
+
+  gen_dir = prepare_workplace(config)
+
+  config["pf_path"] = os.path.join(gen_dir, config.generated.html)
+  config.logger.info(f"HTML output file will be {config.pf_path}")
+  config["js_file"] = os.path.join(gen_dir, config.generated.js)
+  config.logger.info(f"JS output files will like {config.js_file}")
+  config.placeholders["PF_URL_PLACEHOLDER"] = arguments.peakforest.url
+  config.placeholders["PF_TOKEN_PLACEHOLDER"] = (
+    arguments.peakforest.token
+    or config.token.value
+  )
+  if (token := config.placeholders.PF_TOKEN_PLACEHOLDER):
+    config.logger.info(f"Using a token for authentification - length: {len(token)}")
+  else:
+    config.logger.info(f"No token provided for peakforest authentification.")
+
+  process_all_files(config)
+
+  if not arguments.do_run_dry:
+    config.logger.debug(f"Running the server.")
+    if arguments.firefox or arguments.chromium:
+      config.logger.debug(f"Running the server.")
+      import threading
+      import time
+      if arguments.firefox:
+        browser = "firefox"
+      else:
+        browser = "chromium"
+      if (ip := config.network.ip) == "0.0.0.0":
+        ip = "localhost"
+      adress = f"http://{ip}:{config.network.port}"
+      threading.Thread(
+        target=lambda:(
+          time.sleep(1),
+          os.system(f"{browser} {adress}")
+        ),
+        daemon=True
+      ).start()
+    run_server(config)
+  else:
+    config.logger.debug(f"Server not run.")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tab_list.html	Fri Mar 03 14:10:24 2023 +0000
@@ -0,0 +1,14 @@
+            <li class="{{ ACTIVE_TAB_PLACEHOLDER }}">
+              <a
+                id="open_tab_{{ TAB_INDEX_PLACEHOLDER }}"
+                href="#tab-index-{{ TAB_INDEX_PLACEHOLDER }}"
+                data-toggle="tab"
+                onclick="
+                  context = context_{{ TAB_INDEX_PLACEHOLDER }} ;
+                  context.change_tab() ;
+                "
+              >
+                <i class="fa fa-bar-chart"></i>
+                {{ COMPOUND_NAME_PLACEHOLDER }}
+              </a>
+            </li>