Commit 82a36d00 authored by Romain Loth's avatar Romain Loth

finish inits restructuration allowing repeated graph load

old initListeners split in three: initGUIListeners and initSearchListeners that are run once at page load, and initSigmaListeners that is run for each new graph open from local file... also env function and CSS for the local graph file input
parent 279768e3
...@@ -437,6 +437,8 @@ ...@@ -437,6 +437,8 @@
<div id="topPapers"></div> <div id="topPapers"></div>
<div id="localInput"></div>
<div id="information"></div> <div id="information"></div>
<!-- <!--
......
...@@ -269,6 +269,21 @@ p.micromessage{ ...@@ -269,6 +269,21 @@ p.micromessage{
font-size:80%; font-size:80%;
} }
#localInput {
font-size: 80%;
padding: 10px;
}
#localgraphfile {
margin-left: auto ;
margin-right: auto ;
}
.comment {
padding: 10px
}
.centered { .centered {
text-align: center; text-align: center;
} }
......
...@@ -40,7 +40,8 @@ var TW = {} ...@@ -40,7 +40,8 @@ var TW = {}
TW.SystemStates = {} TW.SystemStates = {}
TW.SystemStates.level = true; TW.SystemStates.level = true;
TW.SystemStates.type = [ true ] //[ true , false ]; //social activated! // TW.SystemStates.type = [ true ] //[ true , false ]; //social activated!
TW.SystemStates.type = [ true, false ] //[ true , false ]; //social activated!
TW.SystemStates.selections = []; TW.SystemStates.selections = [];
TW.SystemStates.opposites = []; TW.SystemStates.opposites = [];
TW.catSoc = "Document"; TW.catSoc = "Document";
...@@ -119,7 +120,7 @@ TW.customLegendsBins = { ...@@ -119,7 +120,7 @@ TW.customLegendsBins = {
TW.debugFlags = { TW.debugFlags = {
initialShowAll: false, // show all nodes on bipartite case init (docs + terms) initialShowAll: false, // show all nodes on bipartite case init (docs + terms in one view)
// show verbose console logs... // show verbose console logs...
logFetchers: false, // ...about ajax/fetching of graph data logFetchers: false, // ...about ajax/fetching of graph data
......
...@@ -470,28 +470,14 @@ TinaWebJS = function ( sigmacanvas ) { ...@@ -470,28 +470,14 @@ TinaWebJS = function ( sigmacanvas ) {
if (TW.debugFlags.logSettings) console.log('tw renderers registered in sigma module') if (TW.debugFlags.logSettings) console.log('tw renderers registered in sigma module')
} }
this.SearchListeners = function () { this.initSearchListeners = function () {
// REFA tempo expose var SelInst = new SelectionEngine();
// var SelInst = new SelectionEngine();
SelInst = new SelectionEngine();
//~ $.ui.autocomplete.prototype._renderItem = function(ul, item) {
//~ var searchVal = $("#searchinput").val();
//~ var desc = extractContext(item.desc, searchVal);
//~ return $('<li onclick=\'var s = "'+item.label+'"; search(s);$("#searchinput").val(strSearchBar);\'></li>')
//~ .data('item.autocomplete', item)
//~ .append("<a><span class=\"labelresult\">" + item.label + "</span></a>" )
//~ .appendTo(ul);
//~ };
$('input#searchinput').autocomplete({ $('input#searchinput').autocomplete({
source: function(request, response) { source: function(request, response) {
// console.log("in autocomplete:")
// labels initialized in settings, filled in updateSearchLabels // labels initialized in settings, filled in updateSearchLabels
// console.log(labels.length) // console.log(labels.length)
// console.log(" - - - - - - - - - ")
matches = []; matches = [];
var matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i"); var matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i");
// grep at heart // grep at heart
...@@ -646,18 +632,8 @@ TinaWebJS = function ( sigmacanvas ) { ...@@ -646,18 +632,8 @@ TinaWebJS = function ( sigmacanvas ) {
}); });
} }
// external usage: SelectorEngine*() , MultipleSelection2() , // to init handlers for tina GUI environment (run once on page load)
// enviroment.js:changeType()|changeLevel()|NodeWeightFilter()|EdgeWeightFilter this.initGUIListeners = function () {
this.initListeners = function (categories, partialGraph) {
var SelInst = new SelectionEngine();
document.getElementById('edges-switch').checked = TW.customSettings.drawEdges
// £TODO test if still needed
$("#semLoader").hide();
$("#closeloader").click();
var body=document.getElementsByTagName('body')[0]; var body=document.getElementsByTagName('body')[0];
body.style.paddingTop="41px"; body.style.paddingTop="41px";
...@@ -738,16 +714,8 @@ TinaWebJS = function ( sigmacanvas ) { ...@@ -738,16 +714,8 @@ TinaWebJS = function ( sigmacanvas ) {
} }
}); });
// £TODO test if still needed
pushSWClick("social");
cancelSelection(false);
$("#tips").html(getTips()); $("#tips").html(getTips());
// a bit costly, TODO make conditional or deprecated // a bit costly, TODO make conditional or deprecated
// showMeSomeLabels(6); // showMeSomeLabels(6);
...@@ -756,16 +724,130 @@ TinaWebJS = function ( sigmacanvas ) { ...@@ -756,16 +724,130 @@ TinaWebJS = function ( sigmacanvas ) {
// #saveAs => toggle #savemodal initialized in html + bootstrap-native // #saveAs => toggle #savemodal initialized in html + bootstrap-native
this.SearchListeners();
// button CENTER // button CENTER
$("#lensButton").click(function () { $("#lensButton").click(function () {
// new sigma.js // new sigma.js
partialGraph.camera.goTo({x:0, y:0, ratio:1.2}) partialGraph.camera.goTo({x:0, y:0, ratio:1.2})
}); });
// --------------------------------------------------------------------- $("#layoutButton").click(function () {
// new sigma.js: sigma events bindings sigma_utils.smartForceAtlas()
});
$("#noverlapButton").click(function () {
if(! TW.partialGraph.isNoverlapRunning()) {
// show waiting cursor on page and button
theHtml.classList.add('waiting');
this.style.cursor = 'wait'
// and waiting icon
this.insertBefore(createWaitIcon('noverlapwait'), this.children[0])
var listener = TW.partialGraph.startNoverlap();
var noverButton = this
listener.bind('stop', function(event) {
var stillRunning = document.getElementById('noverlapwait')
if (stillRunning) {
theHtml.classList.remove('waiting');
noverButton.style.cursor = 'auto'
stillRunning.remove()
}
});
return;
}
});
$("#edges-switch").click(function () {
sigma_utils.toggleEdges(this.checked)
});
//Cursor Size slider
var cursorSlider = $("#unranged-value").freshslider({
step: 1,
min:cursor_size_min,
max:cursor_size_max,
value:cursor_size,
onchange:function(value){
// console.log("en cursorsize: "+value);
cursor_size=value;
}
});
// double click on cursor selector slider => set it to 0
$("#areacircle-size").dblclick(function(){
cursorSlider.setValue(0)
});
// //finished
// $("#slidercat1nodessize").freshslider({
// step:1,
// min:-20,
// max:20,
// value:0,
// bgcolor:"#FFA500",
// onchange:function(value){
// setTimeout(function (){
// // new sigma.js loop on nodes POSS optimize
// nds = TW.partialGraph.graph.nodes()
// console.log("init: slider resize")
// for(j=0 ; j<TW.partialGraph.nNodes ; j++){
// if (nds[j]
// && nds[j].type == TW.catSem) {
// var n = nds[j]
// var newval = parseFloat(TW.Nodes[n.id].size) + parseFloat((value-1))*0.3
// n.size = (newval<1.0)?1:newval;
// sizeMult[TW.catSem] = parseFloat(value-1)*0.3;
// }
// }
// partialGraph.render()
// },
// 100);
// }
// });
// costly entire refresh (~400ms) only after stopped resizing for 3s
// NB: rescale middleware already reacted and, except for large win size changes, it handles the resize fine
// (so this fragment is only to accomodate the large changes)
var winResizeTimeout = null
window.addEventListener('resize', function(){
if (winResizeTimeout) {
clearTimeout(winResizeTimeout)
}
winResizeTimeout = setTimeout(function() {
console.log('did refresh')
if (window.TW.partialGraph && window.TW.partialGraph.refresh) {
window.TW.partialGraph.refresh()
}
if (theHtml.classList) {
theHtml.classList.remove('waiting');
}
}, 3000)
}, true)
// general listener: shift key in the window <=> add to selection
$(document).on('keyup keydown', function(e){
// changes the global boolean ("add node to selection" status) if keydown and SHIFT
checkBox = manuallyChecked || e.shiftKey
// show it in the real checkbox too
$('#checkboxdiv').prop("checked", manuallyChecked || e.shiftKey)
} );
} // finish envListeners
// to init local, instance-related listeners (need to run at new sigma instance)
// args: @partialGraph = a sigma instance
this.SigmaListeners = function(partialGraph) {
var SelInst = new SelectionEngine();
// sigma events bindings
// --------------------- // ---------------------
// cf. https://github.com/jacomyal/sigma.js/wiki/Events-API // cf. https://github.com/jacomyal/sigma.js/wiki/Events-API
...@@ -782,7 +864,7 @@ TinaWebJS = function ( sigmacanvas ) { ...@@ -782,7 +864,7 @@ TinaWebJS = function ( sigmacanvas ) {
// when circle area select // when circle area select
// ======================== // ========================
// 1st event, even before we know if there are nodes // 1st event, even before we know if there are nodes
TW.partialGraph.bind('click', function(e) { partialGraph.bind('click', function(e) {
// console.log("sigma click event e", e) // console.log("sigma click event e", e)
// case with a selector circle cursor handled here // case with a selector circle cursor handled here
...@@ -818,7 +900,7 @@ TinaWebJS = function ( sigmacanvas ) { ...@@ -818,7 +900,7 @@ TinaWebJS = function ( sigmacanvas ) {
// when one node and normal click // when one node and normal click
// =============================== // ===============================
TW.partialGraph.bind('clickNode', function(e) { partialGraph.bind('clickNode', function(e) {
// console.log("clickNode event e", e.data.node) // console.log("clickNode event e", e.data.node)
// new sigma.js gives easy access to clicked node! // new sigma.js gives easy access to clicked node!
...@@ -845,7 +927,7 @@ TinaWebJS = function ( sigmacanvas ) { ...@@ -845,7 +927,7 @@ TinaWebJS = function ( sigmacanvas ) {
// when click in the empty background // when click in the empty background
// ================================== // ==================================
if (TW.deselectOnclickStage) { if (TW.deselectOnclickStage) {
TW.partialGraph.bind('clickStage', function(e) { partialGraph.bind('clickStage', function(e) {
// console.log("clickStage event e", e) // console.log("clickStage event e", e)
if (! e.data.captor.isDragging if (! e.data.captor.isDragging
...@@ -878,7 +960,6 @@ TinaWebJS = function ( sigmacanvas ) { ...@@ -878,7 +960,6 @@ TinaWebJS = function ( sigmacanvas ) {
// raw events (non-sigma): handlers attached to the container // raw events (non-sigma): handlers attached to the container
// ========== // ==========
$("#sigma-contnr") $("#sigma-contnr")
.mousemove(function(e){ .mousemove(function(e){
if(!isUndef(partialGraph)) { if(!isUndef(partialGraph)) {
// show/move selector circle cursor // show/move selector circle cursor
...@@ -929,48 +1010,13 @@ TinaWebJS = function ( sigmacanvas ) { ...@@ -929,48 +1010,13 @@ TinaWebJS = function ( sigmacanvas ) {
} }
}); });
$("#layoutButton").click(function () {
sigma_utils.smartForceAtlas()
});
$("#noverlapButton").click(function () {
if(! TW.partialGraph.isNoverlapRunning()) {
// show waiting cursor on page and button
theHtml.classList.add('waiting');
this.style.cursor = 'wait'
// and waiting icon
this.insertBefore(createWaitIcon('noverlapwait'), this.children[0])
var listener = TW.partialGraph.startNoverlap();
var noverButton = this
listener.bind('stop', function(event) {
var stillRunning = document.getElementById('noverlapwait')
if (stillRunning) {
theHtml.classList.remove('waiting');
noverButton.style.cursor = 'auto'
stillRunning.remove()
}
});
return;
}
});
$("#edges-switch").click(function () {
sigma_utils.toggleEdges(this.checked)
});
if (TW.filterSliders) { if (TW.filterSliders) {
// args: for display: target div , // args: for display: target div ,
// for context: family/type prop value, // for context: family/type prop value,
// for values: the property to filter // for values: the property to filter
NodeWeightFilter ( "#slidercat0nodesweight" , NodeWeightFilter ( "#slidercat0nodesweight" ,
categories[0] , TW.categories[0] ,
"size" "size"
); );
...@@ -980,15 +1026,13 @@ TinaWebJS = function ( sigmacanvas ) { ...@@ -980,15 +1026,13 @@ TinaWebJS = function ( sigmacanvas ) {
); );
} }
$("#category1").hide(); // node's label size
//finished
var labelSizeTimeout = null var labelSizeTimeout = null
$("#sliderlabelsize").freshslider({ $("#sliderlabelsize").freshslider({
step:.25, step:.25,
min:0, min:0,
max:5, max:5,
value: TW.partialGraph.settings('labelSizeRatio'), value: sigmaJsDrawingProperties['labelSizeRatio'] || 1,
bgcolor:"#27c470", bgcolor:"#27c470",
onchange:function(value){ onchange:function(value){
if (labelSizeTimeout) { if (labelSizeTimeout) {
...@@ -1008,76 +1052,9 @@ TinaWebJS = function ( sigmacanvas ) { ...@@ -1008,76 +1052,9 @@ TinaWebJS = function ( sigmacanvas ) {
} }
}); });
// //finished document.getElementById('edges-switch').checked = TW.customSettings.drawEdges
// $("#slidercat1nodessize").freshslider({
// step:1,
// min:-20,
// max:20,
// value:0,
// bgcolor:"#FFA500",
// onchange:function(value){
// setTimeout(function (){
// // new sigma.js loop on nodes POSS optimize
// nds = TW.partialGraph.graph.nodes()
// console.log("init: slider resize")
// for(j=0 ; j<TW.partialGraph.nNodes ; j++){
// if (nds[j]
// && nds[j].type == TW.catSem) {
// var n = nds[j]
// var newval = parseFloat(TW.Nodes[n.id].size) + parseFloat((value-1))*0.3
// n.size = (newval<1.0)?1:newval;
// sizeMult[TW.catSem] = parseFloat(value-1)*0.3;
// }
// }
// partialGraph.render()
// },
// 100);
// }
// });
//Cursor Size slider
var cursorSlider = $("#unranged-value").freshslider({
step: 1,
min:cursor_size_min,
max:cursor_size_max,
value:cursor_size,
onchange:function(value){
// console.log("en cursorsize: "+value);
cursor_size=value;
}
});
// double click on cursor selector slider => set it to 0
$("#areacircle-size").dblclick(function(){
cursorSlider.setValue(0)
});
// general listener: shift key in the window <=> add to selection
$(document).on('keyup keydown', function(e){
// changes the global boolean ("add node to selection" status) if keydown and SHIFT
checkBox = manuallyChecked || e.shiftKey
// show it in the real checkbox too
$('#checkboxdiv').prop("checked", manuallyChecked || e.shiftKey)
} );
// costly entire refresh (~400ms) only after stopped resizing for 3s cancelSelection(false);
// NB: rescale middleware already reacted and, except for large win size changes, it handles the resize fine
// (so this fragment is only to accomodate the large changes)
var winResizeTimeout = null
window.addEventListener('resize', function(){
if (winResizeTimeout) {
clearTimeout(winResizeTimeout)
}
winResizeTimeout = setTimeout(function() {
console.log('did refresh')
TW.partialGraph.refresh()
if (theHtml.classList) {
theHtml.classList.remove('waiting');
} }
}, 3000)
}, true)
} // finish initListeners
}; };
...@@ -7,6 +7,71 @@ function writeBrand (brandString) { ...@@ -7,6 +7,71 @@ function writeBrand (brandString) {
document.getElementById('twbrand').innerHTML = brandString document.getElementById('twbrand').innerHTML = brandString
} }
function createFilechooserEl () {
var inputComment = document.createElement("p")
inputComment.innerHTML = `<strong>Choose a graph from your filesystem (gexf or json).</strong>`
inputComment.classList.add('comment')
inputComment.classList.add('centered')
var graphFileInput = document.createElement('input')
graphFileInput.id = 'localgraphfile'
graphFileInput.type = 'file'
graphFileInput.accept = 'application/xml,application/gexf,application/json'
graphFileInput.classList.add('centered')
// NB file input will trigger mainStartGraph() when the user chooses something
graphFileInput.onchange = function() {
if (this.files && this.files[0]) {
let clientLocalGraphFile = this.files[0]
// determine the format
let theFormat
if (/\.(?:gexf|xml)$/.test(clientLocalGraphFile.name)) {
theFormat = 'gexf'
}
else if (/\.json$/.test(clientLocalGraphFile.name)) {
theFormat = 'json'
}
else {
alert('unrecognized file format')
}
// retrieving the content
let rdr = new FileReader()
rdr.onload = function() {
if (! rdr.result || !rdr.result.length) {
alert('the selected file is not readable')
}
else {
// we might have a previous graph opened
if (TW.partialGraph && TW.partialGraph.graph) {
TW.partialGraph.graph.clear()
TW.partialGraph.refresh()
selections = []
}
// run
if (theFormat == 'json')
mainStartGraph(theFormat, JSON.parse(rdr.result), TW.instance)
else
mainStartGraph(theFormat, rdr.result, TW.instance)
}
}
rdr.readAsText(clientLocalGraphFile)
}
}
var filechooserBox = document.createElement('div')
filechooserBox.appendChild(inputComment)
filechooserBox.appendChild(graphFileInput)
return filechooserBox
}
//============================ < NEW BUTTONS > =============================// //============================ < NEW BUTTONS > =============================//
// Documentation Level: ***** // Documentation Level: *****
......
...@@ -98,6 +98,10 @@ TW.instance = new TinaWebJS('#sigma-contnr'); ...@@ -98,6 +98,10 @@ TW.instance = new TinaWebJS('#sigma-contnr');
// add once our tw rendering and index customizations into sigma module // add once our tw rendering and index customizations into sigma module
TW.instance.init() TW.instance.init()
// init the button, sliders and search handlers, also only once
TW.instance.initGUIListeners();
TW.instance.initSearchListeners();
// show the custom name of the app // show the custom name of the app
writeBrand(TW.branding) writeBrand(TW.branding)
...@@ -108,58 +112,19 @@ console.log("Starting TWJS") ...@@ -108,58 +112,19 @@ console.log("Starting TWJS")
// if page is being run locally ==> only possible source shall be via file input // if page is being run locally ==> only possible source shall be via file input
if (window.location.protocol == 'file:') { if (window.location.protocol == 'file:') {
// POSS we could actually provide this local file chooser in all cases let inputDiv = document.getElementById('localInput')
var graphFileInput = document.createElement('input') inputDiv.style.display = 'block'
graphFileInput.id = 'localgraphfile'
graphFileInput.type = 'file'
graphFileInput.accept = 'application/xml,application/gexf,application/json'
document.getElementById('gexfs').appendChild(graphFileInput)
// NB file input will trigger mainStartGraph() when the user chooses something
graphFileInput.onchange = function() {
if (this.files && this.files[0]) {
let clientLocalGraphFile = this.files[0]
// determine the format var remark = document.createElement("p")
let theFormat remark.innerHTML = `You're running project explorer as a local html file (no syncing).`
if (/\.(?:gexf|xml)$/.test(clientLocalGraphFile.name)) { remark.classList.add('comment')
theFormat = 'gexf' remark.classList.add('centered')
} inputDiv.appendChild(remark)
else if (/\.json$/.test(clientLocalGraphFile.name)) {
theFormat = 'json'
}
else {
alert('unrecognized file format')
}
// retrieving the content
let rdr = new FileReader()
rdr.onload = function() { // user can open a gexf or json from his fs
if (! rdr.result || !rdr.result.length) { // POSS we could actually provide this local file chooser in all cases
alert('the selected file is not readable') var graphFileInput = createFilechooserEl()
} inputDiv.appendChild(graphFileInput)
else {
// we might have a previous graph opened
if (TW.partialGraph && TW.partialGraph.graph) {
TW.partialGraph.graph.clear()
TW.partialGraph.refresh()
selections = []
}
// run
if (theFormat == 'json')
mainStartGraph(theFormat, JSON.parse(rdr.result), TW.instance)
else
mainStartGraph(theFormat, rdr.result, TW.instance)
}
}
rdr.readAsText(clientLocalGraphFile)
}
}
} }
// traditional cases: remote read from API or prepared server-side file // traditional cases: remote read from API or prepared server-side file
else { else {
...@@ -627,6 +592,9 @@ function mainStartGraph(inFormat, inData, twInstance) { ...@@ -627,6 +592,9 @@ function mainStartGraph(inFormat, inData, twInstance) {
// by default category0 is the initial type // by default category0 is the initial type
$(".category1").hide(); $(".category1").hide();
// now that we have a sigma instance, let's bind our handlers to it
TW.instance.SigmaListeners(TW.partialGraph)
// [ / Poblating the Sigma-Graph ] // [ / Poblating the Sigma-Graph ]
...@@ -780,7 +748,8 @@ function mainStartGraph(inFormat, inData, twInstance) { ...@@ -780,7 +748,8 @@ function mainStartGraph(inFormat, inData, twInstance) {
// REFA new sigma.js // REFA new sigma.js
TW.partialGraph.camera.goTo({x:0, y:0, ratio:0.9, angle: 0}) TW.partialGraph.camera.goTo({x:0, y:0, ratio:0.9, angle: 0})
twInstance.initListeners(TW.categories , TW.partialGraph);
$("#category1").hide();
// mostly json data are extracts provided by DB apis => no positions // mostly json data are extracts provided by DB apis => no positions
if (inFormat == "json") TW.fa2enabled = true if (inFormat == "json") TW.fa2enabled = true
......
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