Commit 2ef3187e authored by Mikaël Salson's avatar Mikaël Salson

Merge branch 'feature-c/browser-rework-update' into 'dev'

Feature c/browser rework update

Closes #2462

See merge request !541
parents d6a49afb bd6e09ba
Pipeline #119562 passed with stage
in 8 seconds
......@@ -281,6 +281,8 @@
<div id="fps" style="display: none"> </div>
<div id="header_messages" class="message_container header"></div>
<div id='updateIcon' style="display: none; align-items: center; background: transparent; width:1px"></div>
<div id='live-ajax' style="display: flex; align-items: center">
<div id='live-ajax-msg'>
</div>
......
......@@ -463,6 +463,7 @@ Builder.prototype = {
build_top_container: function () {
var self = this;
var parent = document.getElementById("top_info");
if (parent == null) return;
parent.removeAllChildren();
parent.appendChild(document.createTextNode(this.m.getPrintableAnalysisName()));
......
......@@ -74,7 +74,8 @@
* */
function Graph(id, model, database) {
//
View.call(this, model);
View.call(this, model);
this.useSmartUpdateElemStyle = false;
this.id = id;
this.resizeW = 1; //coeff d'agrandissement/réduction largeur
......@@ -319,7 +320,7 @@ Graph.prototype = {
this.text_position_x = 60;
this.text_position_x2 = div_width - 60;
this.update(speed);
this.smartUpdate(speed);
},
/* ************************************************ *
......@@ -331,22 +332,11 @@ Graph.prototype = {
* */
update : function (speed) {
speed = typeof speed !== 'undefined' ? speed : 500;
var startTime = new Date()
.getTime();
var elapsedTime = 0;
this.initAxis()
.initData()
.updateRes()
.updateClones()
.draw(speed);
elapsedTime = new Date()
.getTime() - startTime;
// console.log("update Graph: " + elapsedTime + "ms");
return this
},
/* update resolution curves
......@@ -431,7 +421,7 @@ Graph.prototype = {
document.getElementById("clones_container")
.appendChild(line);
}
this.drawClones(0);
this.drawClones(0, list);
return this
},
......@@ -1254,7 +1244,7 @@ Graph.prototype = {
selected_clones = this.g_clone;
if (typeof list != "undefined"){
selected_clones = this.g_clone.filter(function(d, i) {
if (list.indeOf(d.id) != -1) return true;
if (list.indexOf(d.id) != -1) return true;
return false
});
}
......@@ -1381,7 +1371,7 @@ Graph.prototype = {
shouldRefresh: function() {
this.init();
this.update();
this.smartUpdate();
this.resize();
}
......
......@@ -31,6 +31,41 @@
*
*/
function CloneDom(div) {
var self=this;
this.div_elem = div;
this.div = {};
this.div_content = {};
}
CloneDom.prototype = {
getDiv: function(divName){
//if first time => find the div in dom and store it
if (typeof (this.div[divName]) == "undefined")
this.div[divName] = this.div_elem.getElementsByClassName(divName)[0];
return this.div[divName];
},
updateDivContent: function(divName, newContent){
var div = this.getDiv(divName);
var currentContent = this.div_content[divName];
if (currentContent != newContent){
div.innerHTML = newContent;
this.div_content[divName] = newContent;
}
},
clearDivContent: function(divName){
var div = this.getDiv(divName);
div.removeAllChildren();
this.div_content[divName] = '';
}
}
/**
* List view - build a list of clones/data and keep them up to date with the model
......@@ -157,7 +192,7 @@ List.prototype = {
div.id = i;
div_list_clones.appendChild(div);
this.index[i] = div;
this.index[i] = new CloneDom(div);
}
for (var j = 0; j < this.m.clones.length; j++) {
......@@ -345,10 +380,7 @@ List.prototype = {
* update all content for list and data list
* */
update: function () {
var startTime = new Date()
.getTime();
var elapsedTime = 0;
var list = [];
for (var i = 0; i < this.m.clones.length; i++) {
list.push(i);
......@@ -356,10 +388,6 @@ List.prototype = {
this.updateElem(list);
this.update_data_list()
elapsedTime = new Date()
.getTime() - startTime;
//console.log("update List: " + elapsedTime + "ms");
//TODO check order
document.getElementById("list_sort_select").selectedIndex = 0;
},
......@@ -412,7 +440,7 @@ List.prototype = {
if (clone.hasSizeConstant())
span_name.className += " cloneName";
span_name.ondblclick = function () {
self.editName(cloneID, this);
self.editName(cloneID);
}
span_name.onclick = function (e) {
self.clickList(e, cloneID);
......@@ -458,7 +486,9 @@ List.prototype = {
var clone = this.m.clone(cloneID);
var self = this;
var div_elem = this.index[cloneID];
var cloneDom = this.index[cloneID];
var div_elem = cloneDom.div_elem;
// pas testé; vraiment necessaire ?
if (!( (clone.isActive() && this.m.clusters[cloneID].length !== 0) ||
......@@ -477,34 +507,40 @@ List.prototype = {
div_elem.style.display = "block";
//complete namebox/cloneName
var span_name = div_elem.getElementsByClassName("nameBox")[0];
var span_name = cloneDom.getDiv("nameBox");
if (clone.hasSizeConstant())
span_name = div_elem.getElementsByClassName("cloneName")[0];
span_name = cloneDom.getDiv("cloneName");
if (typeof span_name == "undefined") return false;
if (typeof span_name == "undefined") console.log(cloneID);
span_name.innerHTML = clone.getShortName();
span_name.title = clone.getNameAndCode();
span_name.style.color = clone.getColor();
if (clone.hasSizeConstant())
cloneDom.updateDivContent("cloneName", clone.getShortName());
else
cloneDom.updateDivContent("nameBox", clone.getShortName());
//update clone axis
var span_axis = div_elem.getElementsByClassName("axisBox")[0];
var span_axis = cloneDom.getDiv("axisBox");
span_axis.style.color = clone.getColor();
var axis = this.selectedAxis;
span_axis.removeAllChildren();
span_axis.appendChild(axis.pretty ? axis.pretty(axis.fct(clone)) : document.createTextNode(axis.fct(clone)));
cloneDom.updateDivContent("axisBox", axis.pretty ? axis.pretty(axis.fct(clone)).outerHTML : document.createTextNode(axis.fct(clone)).outerHTML)
//span_axis.appendChild(axis.pretty ? axis.pretty(axis.fct(clone)) : document.createTextNode(axis.fct(clone)));
// span_axis.setAttribute('title', clone.getPrintableSize());
//update cluster icon
var span_cluster = div_elem.getElementsByClassName("clusterBox")[0];
span_cluster.removeAllChildren();
var span_cluster = cloneDom.getDiv("clusterBox");
if (this.m.clusters[cloneID].length > 1) {
if (clone.split) {
span_cluster.onclick = cluster_hide;
span_cluster.appendChild(icon('icon-minus', 'Hide the subclones'));
cloneDom.updateDivContent("clusterBox", icon('icon-minus', 'Hide the subclones').outerHTML)
//span_cluster.appendChild(icon('icon-minus', 'Hide the subclones'));
this.showClusterContent(cloneID, false)
} else {
span_cluster.onclick = cluster_show;
span_cluster.appendChild(icon('icon-plus', 'Show the subclones'));
cloneDom.updateDivContent("clusterBox", icon('icon-plus', 'Show the subclones').outerHTML)
//span_cluster.appendChild(icon('icon-plus', 'Show the subclones'));
this.hideClusterContent(cloneID, false)
}
self.div_cluster(document.getElementById("cluster" + cloneID), cloneID);
......@@ -614,14 +650,14 @@ List.prototype = {
* @param {integer} cloneID - clone index
* @param {dom_object} elem - div where will be the edit field
* */
editName: function (cloneID, elem) {
editName: function (cloneID) {
var self = this;
if (document.getElementById("new_name")) {
this.update();
}
var divParent = elem;
var divParent = this.index[cloneID].getDiv("nameBox");
var old_event = divParent.onclick;
divParent.removeAllChildren();
this.index[cloneID].clearDivContent("nameBox");
if (cloneID[0] == 's')
cloneID = cloneID.substr(3);
......@@ -656,6 +692,7 @@ List.prototype = {
a.onclick = function (event) {
event.preventDefault()
event.stopPropagation()
self.index[cloneID]= new CloneDom(self.index[cloneID].div_elem);
var newName = document.getElementById("new_name")
.value;
self.m.clone(cloneID).changeName(newName);
......@@ -668,7 +705,7 @@ List.prototype = {
/**
* */
buildElem: function (cloneID) {
var div = this.index[cloneID];
var div = this.index[cloneID].div_elem;
div.style.display = "block";
if (!this.m.clone(cloneID).isActive()) div.style.display = "none";
div.removeAllChildren();
......@@ -690,7 +727,7 @@ List.prototype = {
updateElemStyle: function (list) {
for (var i = 0; i < list.length; i++) {
var div = this.index[list[i]];
var div = this.index[list[i]].div_elem;
var clone = this.m.clone(list[i])
......@@ -834,13 +871,10 @@ List.prototype = {
showClusterContent: function(cloneID, update){
var self = this
var fct;
if (update == true || update == undefined) {
fct = function () { self.m.updateElem([cloneID]) }
} else if (update == false) {
fct = function(){}
}
if (update == true || update == undefined)
self.m.updateElem([cloneID])
$("#cluster" + cloneID)
.show(50, fct );
.show(50);
},
/**
......@@ -863,13 +897,10 @@ List.prototype = {
hideClusterContent: function(cloneID, update){
var self = this
var fct;
if (update == true || update == undefined) {
fct = function () { self.m.updateElem([cloneID]) }
} else if (update == false) {
fct = function(){}
}
if (update == true || update == undefined)
self.m.updateElem([cloneID])
$("#cluster" + cloneID)
.hide(50, fct );
.hide(50);
},
/**
......
......@@ -63,6 +63,8 @@ function Model() {
this.NORM_EXTERNAL = "external"
this.normalization_mode = this.NORM_FALSE
this.axes = new Axes(this)
setInterval(function(){return self.updateIcon()}, 100);
}
......@@ -1063,6 +1065,7 @@ changeAlleleNotation: function(alleleNotation) {
* */
multiSelect: function (list) {
if (list.length == 0) return;
console.log("select() (clone " + list + ")");
var tmp = []
......@@ -1081,8 +1084,8 @@ changeAlleleNotation: function(alleleNotation) {
this.orderedSelectedClones.push(tmp[j].id);
list[j] = tmp[j].id
}
this.updateElemStyle(list);
this.update();
this.updateModel();
this.updateElemStyle(this.orderedSelectedClones);
},
/**
......@@ -1101,9 +1104,12 @@ changeAlleleNotation: function(alleleNotation) {
unselectAll: function () {
console.log("unselectAll()");
this.orderedSelectedClones = [];
var list = this.getSelected();
for (var i = 0; i < list.length; i++) {
this.clone(list[i]).select = false;
var list = [];
for (var i=0; i<this.clones.length; i++){
if (this.clone(i).select == true){
list.push(i);
this.clone(i).select = false;
}
}
this.updateElemStyle(list);
},
......@@ -1228,24 +1234,19 @@ changeAlleleNotation: function(alleleNotation) {
* this function must be call for major change in the model
* */
update: function () {
var startTime = new Date()
.getTime();
var elapsedTime = 0;
this.update_normalization();
this.update_precision();
this.updateModel();
for (var i = 0; i < this.view.length; i++) {
this.view[i].update();
if (this.view[i].useSmartUpdate)
this.view[i].smartUpdate();
else
this.view[i].update();
}
elapsedTime = new Date()
.getTime() - startTime;
console.log("update(): " + elapsedTime + "ms");
this.updateIcon();
},
/**
* ask all linked views to update a clone list
* @param {integer[]} - list - array of clone index
......@@ -1258,8 +1259,12 @@ changeAlleleNotation: function(alleleNotation) {
this.updateModel()
for (var i = 0; i < this.view.length; i++) {
this.view[i].updateElem(list);
if (this.view[i].useSmartUpdateElem)
this.view[i].smartUpdateElem(list);
else
this.view[i].updateElem(list);
}
this.updateIcon();
},
/**
......@@ -1272,8 +1277,36 @@ changeAlleleNotation: function(alleleNotation) {
this.clone(list[i]).updateCloneTagIcon();
}
for (i = 0; i < this.view.length; i++) {
this.view[i].updateElemStyle(list);
if (this.view[i].useSmartUpdateElemStyle)
this.view[i].smartUpdateElemStyle(list);
else
this.view[i].updateElemStyle(list);
}
this.updateIcon();
},
/**
* return true if a view has not finished an update
*/
updateIsPending:function(){
for (var i = 0; i < this.view.length; i++) {
if (this.view[i].updateIsPending())
return true;
}
return false;
},
/**
* display an icon in the top-container if a view has not finished an update
*/
updateIcon:function(){
var div = document.getElementById("updateIcon");
if (div==null) return
if (this.updateIsPending())
div.style.display = "flex";
else
div.style.display = "none";
},
/**
......@@ -2267,7 +2300,9 @@ changeAlleleNotation: function(alleleNotation) {
* */
changeColorMethod: function (colorM) {
this.colorMethod = colorM;
this.update();
var list = [];
for (var i = 0; i<this.clones.length; i++) list.push(i);
this.updateElemStyle(list);
},
/**
......
......@@ -184,6 +184,7 @@ ScatterPlot.prototype = {
this.initMenu();
this.initSVG();
this.resize();
this.setPreset(this.default_preset)
this.tsne_ready=false;
......@@ -732,6 +733,8 @@ ScatterPlot.prototype = {
.attr("class", function(p) {
if (!self.m.clone(p.id)
.isActive()) return "circle_hidden";
if (self.m.clone(p.id)
.isFiltered)return "circle_hidden";
if (self.m.clone(p.id)
.isSelected()){
if (self.m.clone(p.id)
......@@ -890,15 +893,11 @@ ScatterPlot.prototype = {
* @param {float} [print]
* */
compute_size: function(div_width, div_height, print) {
if (typeof div_height == 'undefined') {
var div = document.getElementById(this.id)
div_height = div.offsetHeight
div_width = div.offsetWidth
if (typeof div_height != 'undefined') {
//recompute resizeW/H only if a custom div_Width/hieght is provided
this.resizeW = div_width - this.margin[3] - this.margin[1];
this.resizeH = div_height - this.margin[0] - this.margin[2];
}
//On prend la largeur de la div
this.resizeW = div_width - this.margin[3] - this.margin[1];
//On prend la hauteur de la div
this.resizeH = div_height - this.margin[0] - this.margin[2];
if (this.splitX == this.AXIS_ALLELE_V || this.splitX == this.AXIS_GENE_V || this.splitX == this.AXIS_ALLELE_J || this.splitX == this.AXIS_GENE_J || this.splitX == "tsneX_system" ||
(this.mode == this.MODE_GRID & (this.splitY == this.AXIS_ALLELE_V || this.splitY == this.AXIS_GENE_V || this.splitY == this.AXIS_ALLELE_J || this.splitY == this.AXIS_GENE_J))) {
......@@ -1113,19 +1112,15 @@ ScatterPlot.prototype = {
update: function() {
var self = this;
try{
var startTime = new Date()
.getTime();
var elapsedTime = 0;
this.compute_size()
.initGrid()
.updateClones()
.updateMenu();
if (this.mode == this.MODE_BAR)
this.updateBar();
//Donne des informations quant au temps de MàJ des données
elapsedTime = new Date()
.getTime() - startTime;
//console.log("update sp: " + elapsedTime + "ms");
} catch(err) {
sendErrorToDb(err, this.db);
}
......@@ -1271,12 +1266,14 @@ ScatterPlot.prototype = {
updateElemStyle: function() {
var self = this;
if (this.mode == this.MODE_BAR) {
this.updateBar();
this.drawBarTab(0);
} else {
this.node
.attr("class", function(p) {
if (!self.m.clone(p.id)
.isActive()) return "circle_hidden";
if (self.m.clone(p.id)
.isFiltered)return "circle_hidden";
if (self.m.clone(p.id)
.isSelected()){
if (self.m.clone(p.id)
......@@ -1723,7 +1720,7 @@ ScatterPlot.prototype = {
if (endbar){
this.endBar();
}else{
this.update();
this.smartUpdate();
}
oldOtherVisibility = this.otherVisibility
......@@ -2048,7 +2045,6 @@ ScatterPlot.prototype = {
shouldRefresh: function () {
this.init();
this.update();
this.resize();
}
}
ScatterPlot.prototype = $.extend(Object.create(View.prototype), ScatterPlot.prototype);
......@@ -482,15 +482,6 @@ Segment.prototype = {
return div_highlight;
},
/**
* update(size/style/position) a list of selected clones <br>
* @param {integer[]} list - array of clone index
* */
updateElem: function (list) {
this.updateElemStyle(list);
},
/**
* clear functionnal data in order to start from a clean instance when loading a new .vidjil file
* */
......@@ -504,37 +495,69 @@ Segment.prototype = {
delete this.sequence[id];
},
/**
* update(style only) a list of selected clones
/**
*
*/
update: function() {
var self = this;
try{
var list = [];
for (var i = 0; i < this.m.clones.length; i++) list.push(i);
this.updateElem(list);
} catch(err) {
sendErrorToDb(err, this.db);
}
},
/**
* check the list for newly selected/unselected clones to add/remove to segmenter
* update others
* @abstract
* @param {integer[]} list - array of clone index
* */
updateElemStyle: function (list) {
updateElem: function (list) {
for (var i = 0; i < list.length; i++) {
if (this.m.clone(list[i]).isSelected()) {
if (document.getElementById("seq" + list[i])) {
var spanF = document.getElementById("f" + list[i]);
this.div_elem(spanF, list[i]);
var spanM = document.getElementById("m" + list[i]);
spanM.innerHTML = this.sequence[list[i]].toString(this);
var cloneID = list[i];
if (this.m.clone(cloneID).isSelected()) {
//the clone is selected
if (this.sequence[cloneID]) {
//it's already present in the segmenter > update
var spanF = document.getElementById("f" + cloneID);
this.div_elem(spanF, cloneID);
var spanM = document.getElementById("m" + cloneID);
spanM.innerHTML = this.sequence[cloneID].toString(this);
} else {
this.addToSegmenter(list[i]);
//it's not present in the segmenter > create
this.addToSegmenter(cloneID);
this.show();
}
} else {
if (document.getElementById("seq" + list[i])) {
var element = document.getElementById("seq" + list[i]);
//the clone is not selected
if (this.sequence[cloneID]) {
//it should not be present in the segmenter > delete
var element = document.getElementById("seq" + cloneID);
element.parentNode.removeChild(element);
delete this.sequence[list[i]];
delete this.sequence[cloneID];
}
}
}
// Update the first clone if needed
this.update_first_clone()
this.updateAlignmentButton()
//this.updateSegmenterWithHighLighSelection();
this.updateStats();
this.updateStats();
},
/**
* used to highlight mouseover clones or selected clones.
* in the segmenter case selecting/unselecting clones produces more than just a highlight and need an update
* @abstract
* @param {integer[]} list - array of clone index
* */
updateElemStyle: function (list) {
this.updateElem(list);
},
isClone: function (id) {
......@@ -1384,12 +1407,18 @@ genSeq.prototype= {
var ref = '';
var seq = '';
if (typeof(this.segmenter.sequence[this.segmenter.first_clone]) == "undefined"){ // the f* is this
console.log("WUT???")
//TODO: store sequences in array and delete first_clone
this.segmenter.first_clone = Object.keys(this.segmenter.sequence)[0]//first key should be the oldest sequence added to segmenter
}
if (this.segmenter.amino) {
seq = this.seqAA;
ref = this.segmenter.sequence[this.segmenter.first_clone].seqAA;
} else {
seq = this.seq;
ref = this.segmenter.sequence[this.segmenter.first_clone].seq;
}
if (this.segmenter.aligned) {
......
function Url(model, win) {
View.call(this, model);
this.m = model;
this.window = (typeof win != "undefined") ? win : window
......@@ -11,19 +12,6 @@ function Url(model, win) {
}
Url.prototype= {
/**
* init the view before use
* @abstract
* */
init : function () {
},
/**
* update all elements, perform a complete rebuild of the view <br>
* by default doing a updateElem() on each clone must do the job
* @abstract
* */
update: function () {
// get selected clones
......@@ -74,31 +62,21 @@ Url.prototype= {
/**
* update(size/style/position) a list of selected clones <br>
* a slight function for operation who impact only a bunch of clones (merge/split/...)
* @abstract
* @param {integer[]} list - array of clone index
* */
updateElem : function (list) {
this.update();
this.update();
},
/**
* update(style only) a list of selected clones <br>
* a slight function for operation who impact only styles of clones (select/focus)
* @abstract
* @param {integer[]} list - array of clone index
* */
updateElemStyle : function () {
this.update();
},
/**
* resize view to match his div size <br>
* each view must be able to match the size of it's div
* @abstract
* */
resize : function () {
},
applyURL : function() {
var straight_params = this.getStraightParams();
......
......@@ -32,6 +32,29 @@
function View(model) {
this.m = model;
this.m.view.push(this); //Model's sync
//smartUpdate
this.useSmartUpdate = true