Commit 8f812fb9 authored by Mathieu Giraud's avatar Mathieu Giraud
Browse files

merge -- add clones from sequences

A great upcoming feature by @tydax #1921.
The feature is still under development, but we merge now to include in 'dev'
some useful refactorings.
parents 7c3c6643 95cb675f
......@@ -743,7 +743,8 @@ span .substitution {
border-radius: 2px;
}
#file_menu,
#axis_choice {
#axis_choice,
#add_clone_menu {
z-index: 2;
border: solid;
position: fixed;
......@@ -756,6 +757,26 @@ span .substitution {
background: #000000;
display: none;
}
#addclone_input {
margin-bottom: 10px;
resize: none;
height: 8em;
width: 98%;
border: 1px solid silver;
border-radius: 0.1em;
padding: 1%;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
-moz-transition: border linear 0.2s, box-shadow linear 0.2s;
-o-transition: border linear 0.2s, box-shadow linear 0.2s;
transition: border linear 0.2s, box-shadow linear 0.2s;
transition-property: border, box-shadow;
transition-duration: 0.2s, 0.2s;
transition-timing-function: linear, linear;
transition-delay: 0s, 0s;
}
.buttonSelector {
background: #000000;
cursor: pointer;
......
......@@ -743,7 +743,8 @@ span .substitution {
border-radius: 2px;
}
#file_menu,
#axis_choice {
#axis_choice,
#add_clone_menu {
z-index: 2;
border: solid;
position: fixed;
......@@ -756,6 +757,33 @@ span .substitution {
background: #ffffff;
display: none;
}
#add_clone_menu {
margin-left: -325px;
min-width: 600px;
}
#add_clone_menu > #addclone_input {
margin-bottom: 10px;
resize: none;
height: 12em;
width: 98%;
border: 1px solid silver;
border-radius: 0.1em;
padding: 1%;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
-moz-transition: border linear 0.2s, box-shadow linear 0.2s;
-o-transition: border linear 0.2s, box-shadow linear 0.2s;
transition: border linear 0.2s, box-shadow linear 0.2s;
transition-property: border, box-shadow;
transition-duration: 0.2s, 0.2s;
transition-timing-function: linear, linear;
transition-delay: 0s, 0s;
}
#add_clone_menu > #addclone_input.error {
border: 1px solid #8b0000;
}
.buttonSelector {
background: #ffffff;
cursor: pointer;
......@@ -1560,7 +1588,8 @@ select > option:hover {
background: #fdf6e3;
}
input:focus,
textarea:focus {
textarea:focus,
#add_clone_menu > #addclone_input:focus {
border-color: #cccccc;
-webkit-box-shadow: inset 0 1px 1px #ffffff, 0 0 8px #cccccc;
-moz-box-shadow: inset 0 1px 1px #ffffff, 0 0 8px #cccccc;
......
......@@ -742,7 +742,8 @@ span .substitution {
border-radius: 2px;
}
#file_menu,
#axis_choice {
#axis_choice,
#add_clone_menu {
z-index: 2;
border: solid;
position: fixed;
......@@ -755,6 +756,33 @@ span .substitution {
background: #ffffff;
display: none;
}
#add_clone_menu {
margin-left: -325px;
min-width: 600px;
}
#add_clone_menu > #addclone_input {
margin-bottom: 10px;
resize: none;
height: 12em;
width: 98%;
border: 1px solid silver;
border-radius: 0.1em;
padding: 1%;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
-moz-transition: border linear 0.2s, box-shadow linear 0.2s;
-o-transition: border linear 0.2s, box-shadow linear 0.2s;
transition: border linear 0.2s, box-shadow linear 0.2s;
transition-property: border, box-shadow;
transition-duration: 0.2s, 0.2s;
transition-timing-function: linear, linear;
transition-delay: 0s, 0s;
}
#add_clone_menu > #addclone_input.error {
border: 1px solid #8b0000;
}
.buttonSelector {
background: #ffffff;
cursor: pointer;
......@@ -1559,7 +1587,8 @@ select > option:hover {
background: #777777;
}
input:focus,
textarea:focus {
textarea:focus,
#add_clone_menu > #addclone_input:focus {
border-color: #333333;
-webkit-box-shadow: inset 0 1px 1px #ffffff, 0 0 8px #333333;
-moz-box-shadow: inset 0 1px 1px #ffffff, 0 0 8px #333333;
......
......@@ -743,7 +743,8 @@ span .substitution {
border-radius: 2px;
}
#file_menu,
#axis_choice {
#axis_choice,
#add_clone_menu {
z-index: 2;
border: solid;
position: fixed;
......@@ -756,6 +757,33 @@ span .substitution {
background: #ffffff;
display: none;
}
#add_clone_menu {
margin-left: -325px;
min-width: 600px;
}
#add_clone_menu > #addclone_input {
margin-bottom: 10px;
resize: none;
height: 12em;
width: 98%;
border: 1px solid silver;
border-radius: 0.1em;
padding: 1%;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
-moz-transition: border linear 0.2s, box-shadow linear 0.2s;
-o-transition: border linear 0.2s, box-shadow linear 0.2s;
transition: border linear 0.2s, box-shadow linear 0.2s;
transition-property: border, box-shadow;
transition-duration: 0.2s, 0.2s;
transition-timing-function: linear, linear;
transition-delay: 0s, 0s;
}
#add_clone_menu > #addclone_input.error {
border: 1px solid #8b0000;
}
.buttonSelector {
background: #ffffff;
cursor: pointer;
......@@ -1560,7 +1588,8 @@ select > option:hover {
background: #fdf6e3;
}
input:focus,
textarea:focus {
textarea:focus,
#add_clone_menu > #addclone_input:focus {
border-color: #cccccc;
-webkit-box-shadow: inset 0 1px 1px #ffffff, 0 0 8px #cccccc;
-moz-box-shadow: inset 0 1px 1px #ffffff, 0 0 8px #cccccc;
......
......@@ -894,18 +894,48 @@ span .substitution {
border-radius: 2px;
}
#file_menu, #axis_choice{
#file_menu, #axis_choice, #add_clone_menu {
z-index:2;
border:solid;
position: fixed;
top: 200px;
border:solid;
position: fixed;
top: 200px;
min-width:400px;
margin-left:-250px;
left :50%;
font-size: 13px;
font-size: 13px;
padding: 10px;
background:@background;
display:none;
display:none;
}
#add_clone_menu {
margin-left: -325px;
min-width: 600px;
}
#add_clone_menu > #addclone_input {
margin-bottom: 10px;
resize: none;
height: 12em;
width: 98%;
border: 1px solid silver;
border-radius: 0.1em;
padding: 1%;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-webkit-transition: border linear .2s, box-shadow linear .2s;
-moz-transition: border linear .2s, box-shadow linear .2s;
-o-transition: border linear .2s, box-shadow linear .2s;
transition: border linear .2s, box-shadow linear .2s;
transition-property: border, box-shadow;
transition-duration: 0.2s, 0.2s;
transition-timing-function: linear, linear;
transition-delay: 0s, 0s;
}
#add_clone_menu > #addclone_input.error {
border: 1px solid @message_border_red;
}
.buttonSelector {
......@@ -1760,7 +1790,7 @@ select>option:hover {
background : @highlight;
}
input:focus, textarea:focus {
input:focus, textarea:focus, #add_clone_menu > #addclone_input:focus {
border-color: @border;
-webkit-box-shadow: inset 0 1px 1px @background, 0 0 8px @border;
-moz-box-shadow: inset 0 1px 1px @background, 0 0 8px @border;
......
......@@ -75,6 +75,16 @@
</div></div>
</div>
<div id="add_clone_menu">
<span class="closeButton" onclick="cancel();">X</span>
<h2>Add clones from sequences</h2>
<textarea id="addclone_input" placeholder="Sequence(s) (Fasta)"></textarea>
<button onclick="document.getElementById('add_clone_menu').style.display = 'none';
m.addManualClones('addclone_input');">Add</button>
</div>
<!-- TOP-CONTAINER -->
<div id="top-container" onmouseout="hideSelector()">
<div id="menu-container">
......@@ -108,6 +118,9 @@
<a id="import_data_anchor" id="import_data" class="buttonSelector" onclick="javascript:loadData()">import data (.vidjil)</a>
<a id="import_data_analysis" id="import_analysis" class="buttonSelector" onclick="javascript:loadAnalysis()">import analysis</a>
</div>
<div class="menu_box devel-mode">
<a class="buttonSelector" id ="add_clones" onclick="javascript:showAddManualCloneMenu()">add clones from sequences</a>
</div>
<!--<a class="buttonSelector" onclick="javascript:m.resetAnalysis()">reset analysis</a> -->
<!--<a class="buttonSelector" onclick="javascript:reset()">reset all</a> -->
<div class="menu_box">
......
......@@ -35,11 +35,18 @@ function Clone(data, model, index, virtual) {
this.seg = {};
this.segEdited = false
this.virtual = typeof virtual !== 'undefined' ? virtual : false
var key = Object.keys(data)
for (var i=0; i<key.length; i++ ){
this[key[i]]=data[key[i]]
}
// Default value just in case
if (this.quantifiable == undefined) {
this.quantifiable = true;
}
this.seg = this.m.convertSeg(this.seg)
if (typeof (this.getSequence()) != 'undefined' && typeof (this.name) != 'undefined') {
......@@ -62,6 +69,7 @@ function nullIfZero(x) { return x == 0 ? '' : x }
Clone.prototype = {
NOT_QUANTIFIABLE_SIZE: -1, // should be < 0 to behave correcty with getMaxSize() and other comparisons
COVERAGE_WARN: 0.5,
EVALUE_WARN: 0.001,
......@@ -271,6 +279,10 @@ Clone.prototype = {
* @return {float} size
* */
getSize: function (time) {
if (!this.quantifiable)
return this.NOT_QUANTIFIABLE_SIZE
time = this.m.getTime(time);
if (this.m.reads.segmented[time] == 0 ) return 0;
......@@ -329,6 +341,10 @@ Clone.prototype = {
* @return {float} size
* */
getSize2: function (time) {
if (!this.quantifiable)
return this.NOT_QUANTIFIABLE_SIZE
time = this.m.getTime(time)
if (this.m.reads.segmented[time] == 0 ) return 0
......@@ -433,8 +449,12 @@ Clone.prototype = {
getPrintableSize: function (time) {
var reads = this.getReads(time)
s = this.getSequenceLength() + ' nt, '
s = this.getSequenceLength() + ' nt'
if (!this.quantifiable)
return s
s += ', '
s += this.m.toStringThousands(reads) + ' read' + (reads > 1 ? 's' : '') + ' '
if (reads < this.m.NB_READS_THRESHOLD_QUANTIFIABLE)
......@@ -1095,7 +1115,11 @@ Clone.prototype = {
isVirtual: function () {
return this.virtual
},
isQuantifiable: function() {
return this.quantifiable;
},
isFocus: function () {
return this.index == this.m.focus
},
......
......@@ -1377,6 +1377,12 @@ Graph.prototype = {
})
return this
},
shouldRefresh: function() {
this.init();
this.update();
this.resize();
}
......@@ -1440,38 +1446,13 @@ Stack.prototype = {
}
}
}
//other
for (j=0; j<this.m.samples.number; j++){
this.min[this.m.clones.length-1][j] = this.sum[j]
this.sum[this.m.clones.length-1] += this.m.clone(this.m.clones.length-1).getSize(j)
this.max[this.m.clones.length-1][j] = this.sum[j]
}
}
}
}
......@@ -900,6 +900,10 @@ List.prototype = {
},
shouldRefresh: function () {
this.init();
this.update();
}
} //fin prototype
List.prototype = $.extend(Object.create(View.prototype), List.prototype);
......
......@@ -81,19 +81,21 @@ function changeStyle(newStyle){
}
function loadData() {
if (m.analysisHasChanged){
m.analysisHasChanged = false;
console.log({"type": "popup", "default": "save_analysis",
console.log({"type": "popup", "default": "save_analysis",
"msg": "<div class=\'center\'> <button onclick=\'loadData()\'>Continue</button> "
+ " <button onclick='console.closePopupMsg()'>Cancel</button> </div>"});
return
}
document.getElementById("file_menu")
.style.display = "block";
document.getElementById("axis_choice")
.style.display = "none";
document.getElementById("add_clone_menu")
.style.display = "none";
}
function loadAnalysis() {
......@@ -101,6 +103,8 @@ function loadAnalysis() {
.style.display = "block";
document.getElementById("file_menu")
.style.display = "none";
document.getElementById("add_clone_menu")
.style.display = "none";
}
function cancel() {
......@@ -108,6 +112,8 @@ function cancel() {
.style.display = "none";
document.getElementById("file_menu")
.style.display = "none";
document.getElementById("add_clone_menu")
.style.display = "none";
}
function showSelector(elem) {
......@@ -137,3 +143,30 @@ function showDisplayMenu() {
$('#display-menu')
.toggle("fast");
}
/**
* Shows the "add manual clone" menu.
* @param {boolean} error - represents whether the input should be showed in error style.
*/
function showAddManualCloneMenu(error) {
var addCloneMenuNode = document.getElementById("add_clone_menu");
addCloneMenuNode.style.display = "block";
var inputNode = document.getElementById("addclone_input");
if (error) {
inputNode.classList.add("error");
var onFocus = function(evt) {
evt.target.removeEventListener("focus", onFocus);
evt.target.classList.remove("error");
}
inputNode.addEventListener("focus", onFocus);
} else {
inputNode.classList.remove("error");
}
document.getElementById("axis_choice")
.style.display = "none";
document.getElementById("file_menu")
.style.display = "none";
}
......@@ -3,7 +3,7 @@
* High-throughput Analysis of V(D)J Immune Repertoire.
* Copyright (C) 2013-2017 by Bonsai bioinformatics
* at CRIStAL (UMR CNRS 9189, Université Lille) and Inria Lille
* Contributors:
* Contributors:
* Marc Duez <marc.duez@vidjil.org>
* The Vidjil Team <contact@vidjil.org>
*
......@@ -33,6 +33,8 @@
VIDJIL_JSON_VERSION = '2014.09';
SIZE_MANUALLY_ADDED_CLONE = 100000; // Default size of a manually added clone.
/** Model constructor
* Used to parse a .vidjil file (local file or from url) and store his content in a more convenient way, <br>
* provide manipulation function, <br>
......@@ -773,11 +775,9 @@ changeCloneNotation: function(cloneNotationType) {
console.log("select() (clone " + cloneID + ")");
// others shouldn't be selectable
var posOthers = {};
for (var i = this.clones.length - this.system_available.length; i <= this.clones.length - 1; i++) {
posOthers[i]='';
}
if (cloneID in posOthers) {return 0;};
if (this.clones[cloneID].isVirtual()) {
return 0;
}
if (this.clone(cloneID).isSelected()) {
this.clone(cloneID).select = false;
......@@ -1076,27 +1076,30 @@ changeCloneNotation: function(cloneNotationType) {
}
// compute size for each germlines of newOthers
lenSA = this.system_available.length;
for (var pos = 0; pos < this.clones.length - lenSA; pos++) {
for (var sample = 0; sample < this.samples.number ; sample++) {
if (this.clone(pos).isActive()) {
other_quantifiable_clones = [];
for (var pos = 0; pos < this.clones.length; pos++) {
var c = this.clone(pos)
if (c.isVirtual()) {
other_quantifiable_clones.push(pos);
} else if (c.isActive() && c.quantifiable) {
for (var sample = 0; sample < this.samples.number ; sample++) {
for (var k = 0; k < this.clusters[pos].length; k++) {
if (this.clusters[pos][k] != this.clones.length - lenSA) {
newOthers[this.clone(pos).germline][sample] -= this.clone(this.clusters[pos][k]).get('reads', sample);}
else { break; }
newOthers[c.germline][sample] -= this.clone(this.clusters[pos][k]).get('reads', sample);
}
}
}
}
// values assignation of other
for (var pos = this.clones.length -lenSA; pos < this.clones.length ; pos++) {
var c = this.clone(pos);
//for (var pos = this.clones.length -lenSA; pos < this.clones.length ; pos++) {
var self = this;
other_quantifiable_clones.forEach(function(pos) {
var c = self.clone(pos);
c.reads = newOthers[c.germline];
c.name = c.germline + " smaller clones";
if (this.someClonesFiltered)
if (self.someClonesFiltered)
c.name += " + filtered clones";
}
})
},
/**
......@@ -2304,7 +2307,89 @@ changeCloneNotation: function(cloneNotationType) {
return this.getName(edge.source)+" -- "+this.getName(edge.target)+" == "+edge.len;
},
}; //end prototype Model
DEFAULT_SEGMENTER_URL: "https://dev.vidjil.org/vidjil/segmenter",
/**
* sends an ajax request to manually add special clones
* @param {string} input - the id of the input to extract the sequences from
*/
addManualClones: function(input) {
var url = config && config.segmenter_address ? config.segmenter_address : this.DEFAULT_SEGMENTER_URL;
var inputNode = document.getElementById(input);
if (!inputNode) {
console.log("[model] could not find '" + input + "' input node");
return;
}
if (inputNode) {
var sequences = inputNode.value;
// When empty, show error
if (!sequences) {
showAddManualCloneMenu(true);
} else {
var self = this;
var displayAjax = function (display) {
var liveAjaxNode = document.getElementById("live-ajax");
var bodyNode = document.getElementsByTagName("body")[0];
if (display) {
// var imgNode = document.createElement("img");