Commit 523ee85f authored by Mathieu Giraud's avatar Mathieu Giraud

Merge branch 'feature-c/indexed-dom' into 'dev'

Indexed DOM

See merge request !566
parents 9dfdd135 97c751ce
Pipeline #113068 passed with stages
in 5 minutes and 58 seconds
......@@ -82,6 +82,7 @@ function loadAfterConf() {
"../autocomplete",
"../tips",
"../tokeniser",
"../indexedDom",
// Speed test
"../speed_test",
"../form_builder",
......
/*
* This file is part of Vidjil <http://www.vidjil.org>,
* 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:
* Marc Duez <marc.duez@vidjil.org>
* The Vidjil Team <contact@vidjil.org>
*
* "Vidjil" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "Vidjil" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "Vidjil". If not, see <http://www.gnu.org/licenses/>
*/
/* an object that keep track of a specified dom element and his children
* provide a set of function to update contents/colors/display with minimal dom access
*
* the childs of the indexed element can be accessed by their className, the parent by using "main" as className
*
* limit:
* /!\ don't use native/jquery function to modify indexed element or you will lose sync with indexed content
* /!\ don't index nested element (or do it but don't use content() on the top element for obvious reason)
* */
function IndexedDom(div) {
this.div = {
main: {element: div}
};
}
IndexedDom.prototype = {
/* return the first child with the given className
* save his DOM position for later use if not already done
*/
getElement: function(className){
// The first time, stores the DOM element
if (typeof (this.div[className]) == "undefined")
this.div[className] = { element : this.div.main.element.getElementsByClassName(className)[0]};
// Returns the stored DOM element
return this.div[className].element;
},
/* Setters
The setters both stores the updated value in the IndexedDom as well as in the DOM.
Changes are done only when needed, because updating the DOM is slow... and these
setters are often called during an .update() without any actual changes.
*/
content: function(className, newContent){
var div = this.getElement(className);
if (this.div[className].content != newContent)
{
this.div[className].content = newContent;
div.innerHTML = newContent;
}
},
classname: function(className, newClassName){
var div = this.getElement(className);
if (this.div[className].classname != newClassName)
{
this.div[className].classname = newClassName
div.className = newClassName;
}
},
display: function(className, display){
var div = this.getElement(className);
if (this.div[className].display != display)
{
this.div[className].display = display;
div.style.display = display;
}
},
color: function(className, color){
var div = this.getElement(className);
if (this.div[className].color != color)
{
this.div[className].color = color;
div.style.color = color;
}
},
title: function(className, title){
var div = this.getElement(className);
if (this.div[className].title != title)
{
this.div[className].title = title;
div.title = title;
}
},
/* Clear */
clear: function(className){
if (className == "main"){
var tmp = this.getElement(className);
this.div = {
main: {element: tmp}
};
return;
}
this.div[className] = undefined;
}
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
QUnit.module("IndexedDom", {
});
QUnit.test("get/set", function(assert) {
//init model
var m = new Model()
m.parseJsonData(json_data,100)
m.loadGermline()
m.initClones()
//init list view
var list = new List("list","data",m)
list.init()
//create indexedDom for clones 1/2/3 in list
var indexedDom1 = new IndexedDom(document.getElementById("1"))
var indexedDom2 = new IndexedDom(document.getElementById("2"))
var indexedDom3 = new IndexedDom(document.getElementById("3"))
//content
//set a custom content using indexedDom
indexedDom1.content("nameBox", "hello")
//check if custom content is really in dom using native js
assert.equal(document.getElementById("1").getElementsByClassName("nameBox")[0].innerHTML,
"hello",
"update dom content using indexedDom")
//adding classname
indexedDom1.classname("nameBox", "nameBox customClassname")
assert.notEqual(document.getElementById("1").getElementsByClassName("customClassname")[0],
undefined,
"add a classname using indexedDom")
//removing classname
indexedDom1.classname("nameBox", "nameBox")
assert.equal(document.getElementById("1").getElementsByClassName("customClassname")[0],
undefined,
"removing a classname using indexedDom")
//display hide
indexedDom2.display("systemBox", "none")
assert.equal(document.getElementById("2").getElementsByClassName("systemBox")[0].style.display,
"none",
"hide a dom element using indexedDom")
//display show
indexedDom2.display("systemBox", "block")
assert.equal(document.getElementById("2").getElementsByClassName("systemBox")[0].style.display,
"block",
"hide a dom element using indexedDom")
//color
indexedDom3.color("systemBox", "blue")
assert.equal(document.getElementById("3").getElementsByClassName("systemBox")[0].style.color,
"blue",
"change style color using indexedDom")
//title
indexedDom3.title("systemBox", "popipo")
assert.equal(document.getElementById("3").getElementsByClassName("systemBox")[0].title,
"popipo",
"change title using indexedDom")
//getElementByclassname
assert.equal(document.getElementById("3").getElementsByClassName("systemBox")[0],
indexedDom3.getElement("systemBox"),
"access dom using indexedDom")
});
QUnit.test("speed", function(assert) {
//init model
var m = new Model()
m.parseJsonData(json_data,100)
m.loadGermline()
m.initClones()
//init list view
var list = new List("list","data",m)
list.init()
//create indexedDom for clone 1 in list
var indexedDom1 = new IndexedDom(document.getElementById("1"))
accessTime1 = []
accessTime2 = []
accessTime3 = []
for (i = 0; i<10000; i++){
startTime = window.performance.now()
indexedDom1.content("nameBox", "hello") // first access to nameBox(not indexed yet) + update (high cost)
accessTime1.push(window.performance.now()- startTime)
startTime = window.performance.now()
indexedDom1.content("nameBox", "hola") // second access to nameBox(already indexed) + update (moderate cost)
accessTime2.push(window.performance.now()- startTime)
startTime = window.performance.now()
indexedDom1.content("nameBox", "hola") // third access to nameBox(already indexed) + update not needed (no cost)
accessTime3.push(window.performance.now()- startTime)
indexedDom1.clear("main") //drop all indexed stuff
}
//sort times
accessTime1.sort(function(a,b) { return a - b;})
accessTime2.sort(function(a,b) { return a - b;})
accessTime3.sort(function(a,b) { return a - b;})
//median
mAccessTime1 = (accessTime1[Math.floor(accessTime1.length/2)]*1000).toFixed(4)
mAccessTime2 = (accessTime2[Math.floor(accessTime2.length/2)]*1000).toFixed(4)
mAccessTime3 = (accessTime3[Math.floor(accessTime3.length/2)]*1000).toFixed(4)
//average
aAccessTime1 = 0
for (i = 0; i<accessTime1.length; i++) aAccessTime1+=accessTime1[i]
aAccessTime1 = (aAccessTime1*1000/accessTime1.length)
aAccessTime2 = 0
for (i = 0; i<accessTime2.length; i++) aAccessTime2+=accessTime2[i]
aAccessTime2 = (aAccessTime2*1000/accessTime2.length)
aAccessTime3 = 0
for (i = 0; i<accessTime3.length; i++) aAccessTime3+=accessTime3[i]
aAccessTime3 = (aAccessTime3*1000/accessTime3.length)
assert.ok(aAccessTime1 > aAccessTime2, "dom access using indexed dom after first use are faster "+aAccessTime1.toFixed(5)+"ns > "+aAccessTime2.toFixed(5)+"ns"+
" ("+(((aAccessTime1/aAccessTime2)-1)*100).toFixed(0)+"% faster)" );
assert.ok(aAccessTime2 > aAccessTime3, "avoid dom access for unneccesary update "+aAccessTime2.toFixed(5)+"ns > "+aAccessTime3.toFixed(5)+"ns"+
" ("+(((aAccessTime2/aAccessTime3)-1)*100).toFixed(0)+"% faster)" );
});
\ No newline at end of file
......@@ -21,7 +21,7 @@ QUnit.test("edit", function(assert) {
list.editName(1)
assert.notEqual(list.index[1].div_elem.innerHTML.indexOf("save"), -1, "open edit clone name : Ok")
assert.notEqual(list.index[1].getElement("main").innerHTML.indexOf("save"), -1, "open edit clone name : Ok")
setTimeout( function() {
$("#new_name").val("editName");
......
......@@ -11,8 +11,10 @@ QUnit.test("segmenter", function(assert) {
m.initClones()
var segment = new Segment("segment", m);
assert.equal(segment.first_clone, -1, "segment.first_clone is set to -1 at init")
assert.equal(segment.sequence_order.length, 0, "no clones in segmenter at init")
segment.init()
segment.update()
segment.useSmartUpdateElemStyle = false;
var done = assert.async(15);
var delay = 0;
......@@ -21,7 +23,7 @@ QUnit.test("segmenter", function(assert) {
//select test
m.select(0)
setTimeout( function() {
var div0 = document.getElementById("f0");
var div0 = document.getElementById("seq0").getElementsByClassName("seq-fixed")[0];
assert.notEqual(div0.innerHTML.indexOf("test1"), -1, "select : Ok")
m.select(1)
......@@ -29,23 +31,23 @@ QUnit.test("segmenter", function(assert) {
}, delay+=step);
setTimeout( function() {
var div1 = document.getElementById("f1");
var div1 = document.getElementById("seq1").getElementsByClassName("seq-fixed")[0];;
assert.notEqual(div1.innerHTML.indexOf("test2"), -1, "select : Ok")
assert.equal(segment.first_clone, 0, "segment.first_clone still set to 0 if clones 0 and 1 are selected")
assert.equal(segment.sequence_order[0], 0, "segment.first_clone still set to 0 if clones 0 and 1 are selected")
m.unselect(0)
done()
}, delay+=step);
setTimeout( function() {
assert.equal(segment.first_clone, 1, "segment.first_clone is set to 1 if clones 0 is unselected")
assert.equal(segment.sequence_order[0], 1, "segment.first_clone is set to 1 if clones 0 is unselected")
m.select(2)
done()
}, delay+=step);
setTimeout( function() {
var div2 = document.getElementById("f2");
var div2 = document.getElementById("seq2").getElementsByClassName("seq-fixed")[0];
assert.notEqual(div2.innerHTML.indexOf("test3"), -1, "select : Ok")
m.unselectAll()
......@@ -53,10 +55,10 @@ QUnit.test("segmenter", function(assert) {
}, delay+=step);
setTimeout( function() {
assert.equal(document.getElementById("f0"), null, "unselect : Ok")
assert.equal(document.getElementById("f1"), null, "unselect : Ok")
assert.equal(document.getElementById("f2"), null, "unselect : Ok")
assert.equal(segment.first_clone, -1, "segment.first_clone is set to -1 when no clones are selected")
assert.equal(document.getElementById("seq0").style.display, "none", "unselect : Ok")
assert.equal(document.getElementById("seq1").style.display, "none", "unselect : Ok")
assert.equal(document.getElementById("seq2").style.display, "none", "unselect : Ok")
assert.equal(segment.sequence_order.length, 0, "segment.first_clone is set to -1 when no clones are selected")
m.select(0);
m.select(2);
......@@ -65,9 +67,9 @@ QUnit.test("segmenter", function(assert) {
}, delay+=step);
setTimeout( function() {
var div0 = document.getElementById("f0");
var div0 = document.getElementById("seq0").getElementsByClassName("seq-fixed")[0];
assert.notEqual(div0.innerHTML.indexOf("test1"), -1, "select : Ok")
assert.equal(document.getElementById("f2"), null, "unselect : Ok")
assert.equal(document.getElementById("seq2").style.display, "none" , "unselect : Ok")
m.unselectAll();
done()
......@@ -84,7 +86,7 @@ QUnit.test("segmenter", function(assert) {
// assert.deepEqual(segment.toFasta(), "> test1 // 5.000%\naaaaaaaaaaaaaaaaaaaAG\n","toFasta :Ok")
setTimeout( function() {
assert.equal($("#f0").css("background-color"), "rgb(204, 204, 204)", "focus : Ok")
assert.equal(document.getElementById("seq0").getElementsByClassName("seq-fixed")[0].className.includes("list_focus"), true, "focus : Ok")
m.focusOut()
m.updateElem([0])
......@@ -128,8 +130,8 @@ QUnit.test("segmenter", function(assert) {
var clone1_pos = list_html.search('"seq1"');
var clone2_pos = list_html.search('"seq2"');
// Clone 2 is more abundant than clone 1 more abundant than clone 0
assert.ok(clone2_pos > clone1_pos);
assert.ok(clone1_pos > clone0_pos);
assert.ok(clone2_pos < clone1_pos);
assert.ok(clone1_pos < clone0_pos);
var fasta = segment.toFasta();
assert.ok(fasta.indexOf('> test3') < fasta.indexOf('> test2'));
assert.ok(fasta.indexOf('> test2') < fasta.indexOf('> test1'));
......@@ -150,7 +152,7 @@ QUnit.test("segmenter", function(assert) {
}, delay+=step);
setTimeout( function() {
assert.equal(segment.first_clone, 2, "segment.first_clone is set to 2, even if bigger clone is selected")
assert.equal(segment.sequence_order[0], 2, "segment.first_clone is set to 2, even if bigger clone is selected")
done()
}, delay+=step);
......@@ -162,6 +164,7 @@ QUnit.test("sequence", function(assert) {
m.initClones()
var segment = new Segment("segment", m);
segment.init()
segment.update()
segment.addToSegmenter(4)
seq1 = new Sequence(4, m, segment)
......@@ -195,7 +198,7 @@ QUnit.test("segt", function (assert) {
var segment = new Segment("segment",m)
segment.init();
segment.update()
var done = assert.async(3);
var delay = 0;
......@@ -258,6 +261,7 @@ QUnit.test("segt", function (assert) {
m.initClones();
var segment = new Segment("segment",m);
segment.init();
segment.update();
m.select(2)
segment.addGermlineToSegmenter("IGHD1-1*01","IGH");
assert.equal(segment.sequence["IGHD1-1*01"].seq.join("").toUpperCase(), "GGGCGCCGGGGCAGATTCTGAACAGCCCCGAGTCACGGTGGGTACAACTGGAACGAC")
......
......@@ -51,6 +51,7 @@
<script type="text/javascript" src='../../js/speed_test.js' data-cover></script>
<script type="text/javascript" src='../../js/closeable.js' data-cover></script>
<script type="text/javascript" src='../../js/tokeniser.js' data-cover></script>
<script type="text/javascript" src='../../js/indexedDom.js' data-cover></script>
<script type="text/javascript" src='../../js/form_builder.js' data-cover></script>
<script type="text/javascript" src="./testFiles/data_test.js"></script>
......@@ -111,6 +112,7 @@
<script src="./testFiles/com_test.js"></script>
<script src="./testFiles/info_test.js"></script>
<script src="./testFiles/list_test.js"></script>
<script src="./testFiles/indexedDom_test.js"></script>
<script src="./testFiles/segmenter_test.js"></script>
<script src="./testFiles/tools_test.js"></script>
<script src="./testFiles/germline_test.js"></script>
......
......@@ -149,19 +149,19 @@ end
clustered = $b.clone_info('1')
assert ($b.clone_in_scatterplot('1', :class => "circle_select").exists?)
assert ($b.clone_in_graph('1', :class=> "graph_select").exists?)
assert ($b.clone_in_segmenter('1').exists? ), ">> fail to add clone to segmenter by clicking on the list or scatterplot"
assert ($b.clone_in_segmenter('1').present? ), ">> fail to add clone to segmenter by clicking on the list or scatterplot"
assert ( not $b.clone_in_scatterplot('2', :class => "circle_select").exists?)
assert ( not $b.clone_in_graph('2', :class=> "graph_select").exists?)
assert ( not $b.clone_in_segmenter('2').exists? ), ">> fail to add clone to segmenter by clicking on the list or scatterplot"
assert ( not $b.clone_in_segmenter('2').present? ), ">> fail to add clone to segmenter by clicking on the list or scatterplot"
clustered[:cluster].click
assert ($b.clone_in_scatterplot('1', :class => "circle_select").exists?)
assert ($b.clone_in_graph('1', :class=> "graph_select").exists?)
assert ($b.clone_in_segmenter('1').exists? ), ">> fail to add clone to segmenter by clicking on the list or scatterplot"
assert ($b.clone_in_segmenter('1').present? ), ">> fail to add clone to segmenter by clicking on the list or scatterplot"
$b.until { $b.clone_in_scatterplot('2', :class => "circle_select").exists? }
assert ( $b.clone_in_graph('2', :class=> "graph_select").exists?)
assert ( $b.clone_in_segmenter('2').exists? ), ">> fail to add clone to segmenter by clicking on the list or scatterplot"
assert ( $b.clone_in_segmenter('2').present? ), ">> fail to add clone to segmenter by clicking on the list or scatterplot"
clustered[:cluster].click
$b.update_icon.wait_while(&:present?)
......
......@@ -140,7 +140,7 @@ class TestMultilocus < BrowserTest
assert ( $b.clone_in_list('25').class_name.include? "list_select" ), ">> Incorrect class name, clone is not selected"
assert ( $b.clone_in_scatterplot('25', :class => "circle_select").exists?)
assert ( $b.clone_in_graph('25', :class=> "graph_select").exists?)
assert ( $b.clone_in_segmenter('25').exists? ), ">> fail to add clone to segmenter by clicking on the list or scatterplot"
assert ( $b.clone_in_segmenter('25').present? ), ">> fail to add clone to segmenter by clicking on the list or scatterplot"
stats = $b.statsline
assert (stats.text.include? '1 clone'), ">> Incorrect stats, should have one clone"
......@@ -343,13 +343,13 @@ class TestMultilocus < BrowserTest
$b.update_icon.wait_while(&:present?)
$b.until { $b.clone_in_scatterplot('1', :class => "circle_select").exists?}
assert ( $b.clone_in_graph('1', :class=> "graph_select").exists?)
assert ( $b.clone_in_segmenter('1').exists? ), ">> fail to add clone to segmenter by clicking on the list or scatterplot"
assert ( $b.clone_in_segmenter('1').present? ), ">> fail to add clone to segmenter by clicking on the list or scatterplot"
assert ( $b.clone_in_scatterplot('37', :class => "circle_select").exists?)
assert ( $b.clone_in_graph('37', :class=> "graph_select").exists?)
assert ( $b.clone_in_segmenter('37').exists? ), ">> fail to add clone to segmenter by clicking on the list or scatterplot"
assert ( $b.clone_in_segmenter('37').present? ), ">> fail to add clone to segmenter by clicking on the list or scatterplot"
assert ( $b.clone_in_scatterplot('90', :class => "circle_select").exists?)
assert ( $b.clone_in_graph('90', :class=> "graph_select").exists?)
assert ( $b.clone_in_segmenter('90').exists? ), ">> fail to add clone to segmenter by clicking on the list or scatterplot"
assert ( $b.clone_in_segmenter('90').present? ), ">> fail to add clone to segmenter by clicking on the list or scatterplot"
$b.clone_in_cluster('90', '1')[:delete].click
$b.clone_in_cluster('90', '37')[:delete].click
......
......@@ -39,19 +39,19 @@ class TestScatterplot < BrowserTest
$b.clone_in_list("0").click
$b.update_icon.wait_while(&:present?)
assert ( $b.clone_in_segmenter('0').exists? ), ">> Firste click; Correct selection of clone 0 by click in scatterplot"
assert ( not $b.clone_in_segmenter('1').exists? ), ">> Firste click; Clone 1 should not be present in segmenter"
assert ( $b.clone_in_segmenter('0').present? ), ">> Firste click; Correct selection of clone 0 by click in scatterplot"
assert ( not $b.clone_in_segmenter('1').present? ), ">> Firste click; Clone 1 should not be present in segmenter"
$b.clone_in_list("1").click
$b.update_icon.wait_while(&:present?)
assert ( not $b.clone_in_segmenter('0').exists? ), ">> Another click; Clone 0 should not be present anymore in segmenter"
assert ( $b.clone_in_segmenter('1').exists? ), ">> Another click; Correct selection of clone 1 after second click in scatterplot"
assert ( not $b.clone_in_segmenter('0').present? ), ">> Another click; Clone 0 should not be present anymore in segmenter"
assert ( $b.clone_in_segmenter('1').present? ), ">> Another click; Correct selection of clone 1 after second click in scatterplot"
$b.clone_in_list("0").click(:control)
$b.update_icon.wait_while(&:present?)
assert ( $b.clone_in_segmenter('0').exists? ), ">> ctrl+click; Clone 0 should be present in segmenter"
assert ( $b.clone_in_segmenter('1').exists? ), ">> ctrl+click; Clone 1 should be present in segmenter"
assert ( $b.clone_in_segmenter('0').present? ), ">> ctrl+click; Clone 0 should be present in segmenter"
assert ( $b.clone_in_segmenter('1').present? ), ">> ctrl+click; Clone 1 should be present in segmenter"
end
......@@ -64,19 +64,19 @@ class TestScatterplot < BrowserTest
$b.clone_in_list("0").click
$b.update_icon.wait_while(&:present?)
assert ( $b.clone_in_segmenter('0').exists? ), ">> Firste click; Correct selection of clone 0 by click in scatterplot"
assert ( not $b.clone_in_segmenter('1').exists? ), ">> Firste click; Clone 1 should not be present in segmenter"
assert ( $b.clone_in_segmenter('0').present? ), ">> Firste click; Correct selection of clone 0 by click in scatterplot"
assert ( not $b.clone_in_segmenter('1').present? ), ">> Firste click; Clone 1 should not be present in segmenter"
$b.clone_in_list("1").click
$b.update_icon.wait_while(&:present?)
assert ( not $b.clone_in_segmenter('0').exists? ), ">> Another click; Clone 0 should not be present anymore in segmenter"
assert ( $b.clone_in_segmenter('1').exists? ), ">> Another click; Correct selection of clone 1 after second click in scatterplot"
assert ( not $b.clone_in_segmenter('0').present? ), ">> Another click; Clone 0 should not be present anymore in segmenter"
assert ( $b.clone_in_segmenter('1').present? ), ">> Another click; Correct selection of clone 1 after second click in scatterplot"
$b.clone_in_list("0").click(:control)
$b.update_icon.wait_while(&:present?)
assert ( $b.clone_in_segmenter('0').exists? ), ">> ctrl+click; Clone 0 should be present in segmenter"
assert ( $b.clone_in_segmenter('1').exists? ), ">> ctrl+click; Clone 1 should be present in segmenter"
assert ( $b.clone_in_segmenter('0').present? ), ">> ctrl+click; Clone 0 should be present in segmenter"
assert ( $b.clone_in_segmenter('1').present? ), ">> ctrl+click; Clone 1 should be present in segmenter"
end
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment