Commit eceb74e3 authored by Romain Loth's avatar Romain Loth

settings refacto + urlparams 2/2

renamed (topPapers to relatedDocs, divFlags to moduleFlags...), fixed several config effects that were disconnected (sizeMult, tagcloud sizes, colorsByAtt), generalized the TW.conf prefix
parent d76ab522
......@@ -2,6 +2,9 @@
/* --------------------- crowdsourcingTerms --------------------- */
/* ---------------------------------------------------------------- */
// update message in the search bar
TW.conf.strSearchBar = "Select or suggest topics";
$("#searchinput").attr('placeholder', TW.conf.strSearchBar) ;
/* 3 possible events affect crowdsourcing */
......
This is a stub for a future documentation for developers.
## Graph input choices
Tina allows 3 main ways of input :
- a local file from the client machine
activated by `sourcemode=localfile` or by opening the entry point explorerjs.html via file protocol (<=> locally)
- a static file from the remote server
`sourcemode=serverfile`
- a dataset from a remote server API
`sourcemode=api`
The `sourcemode` value is by default the one in settings_explorerjs.js (`TW.conf.sourcemode`), unless an url argument of the same name is present.
The `serverfile` option has an extended version called `servermenu`. It opens a list of files called `db.json` on the server, providing a menu to choose from it.
The detailed implementation of these choices can be found in the function `syncRemoteGraphData()` in main.js.
## Graph initialization
This will still evolve but the main steps for any graph initialization messily use functions across several modules, so it can be useful to list them here together:
......@@ -29,7 +45,7 @@ This will still evolve but the main steps for any graph initialization messily u
- any attribute listed in the sourcenode.attributes will be indexed if the TW.scanClusters flag is true
- the mapping from attribute values to matching nodes is in TW.Clusters.aType.anAttr.aValue.map
- coloration: "`age`" "`growth_rate`" + any attribute of type float or int
- clustering: "`cluster_index`" ou nom figurant dans `TW.nodeClusAtt`
- clustering: "`cluster_index`" ou nom figurant dans `TW.conf.nodeClusAtt`
- vocabulary: (en cours) any attribute of type string and where the amount of distinct values is < TW.somesettings
......
......@@ -77,14 +77,14 @@
</li>
<li style="margin-left:10px;">
<a href="#" class="navbar-middle navbar-brand">
<!-- will be replaced by TW.branding -->
<!-- will be replaced by TW.conf.branding -->
<span id="twbrand">TinaWebJS</span>
</a>
</li>
<li class="disabled">
<a class="navbar-middle">
<span class="label label-default label-lg">MAP: </span>
<span class="label label-default label-lg">MAP: <span id="maplabel"></span></span>
</a>
</li>
......@@ -188,7 +188,7 @@
<div id="sliderlabelsize" class="settingslider"></div>
</a></li>
<li class="dropdown">
<li id="setcolorsMenu" class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">
Set Colors <img title="Set Colors" src="libs/img2/colors.png" width="20px"><span class="caret"></span>
</a>
......
......@@ -14,8 +14,8 @@ function newPopup(url) {
// to add the button in the html with the sigmaUtils.clustersBy(x) listener.
function changeGraphAppearanceByFacets( manualflag ) {
if ( !isUndef(manualflag) && !TW.colorByAtt ) TW.colorByAtt = manualflag;
if(!TW.colorByAtt) return;
if ( !isUndef(manualflag) && !TW.conf.colorByAtt ) TW.conf.colorByAtt = manualflag;
if(!TW.conf.colorByAtt) return;
// for GUI html: if present, rename raw attribute key by a proper label
var AttsTranslations = {
......@@ -93,13 +93,13 @@ function changeGraphAppearanceByFacets( manualflag ) {
}
// creates TW.legendsBins bins
// creates TW.conf.legendsBins bins
// @sortedValues array, mandatory
function intervalsInventory(sortedValues) {
var binmins = []
var len = sortedValues.length
for (var l=0 ; l < TW.legendsBins ; l++) {
let nthVal = Math.floor(len * l / TW.legendsBins)
for (var l=0 ; l < TW.conf.legendsBins ; l++) {
let nthVal = Math.floor(len * l / TW.conf.legendsBins)
binmins.push(sortedValues[nthVal])
}
// console.info("legendRefTicks", binmins)
......@@ -328,7 +328,7 @@ function set_ClustersLegend ( daclass, groupedByTicks ) {
//For CNRS
// function getTopPapers(type){
// if(TW.getAdditionalInfo){
// if(TW.conf.getRelatedDocs){
// console.log("getTopPapers")
// jsonparams=JSON.stringify(getSelections());
// bi=(Object.keys(categories).length==2)?1:0;
......@@ -336,18 +336,18 @@ function set_ClustersLegend ( daclass, groupedByTicks ) {
// jsonparams = jsonparams.split('&').join('__and__');
// //dbsPaths.push(getGlobalDBs());
// thisgexf=JSON.stringify(decodeURIComponent(getUrlParam.file));
// image='<img style="display:block; margin: 0px auto;" src="'+TW.companionAPI+'img/ajax-loader.gif"></img>';
// image='<img style="display:block; margin: 0px auto;" src="'+TW.conf.relatedDocsAPI+'img/ajax-loader.gif"></img>';
// $("#tab-container-top").show();
// $("#topPapers").show();
// $("#topPapers").html(image);
// $.ajax({
// type: 'GET',
// url: TW.companionAPI+'info_div.php',
// data: "type="+type+"&bi="+bi+"&query="+jsonparams+"&gexf="+thisgexf+"&index="+TW.field[getUrlParam.file],
// url: TW.conf.relatedDocsAPI+'info_div.php',
// data: "type="+nodetype+"&bi="+bi+"&query="+jsonparams+"&gexf="+thisgexf+"&index="+TW.field[getUrlParam.file],
// //contentType: "application/json",
// //dataType: 'json',
// success : function(data){
// console.log(TW.companionAPI+'info_div.php?'+"type="+type+"&bi="+bi+"&query="+jsonparams+"&gexf="+thisgexf+"&index="+TW.field[getUrlParam.file]);
// console.log(TW.conf.relatedDocsAPI+'info_div.php?'+"type="+nodetype+"&bi="+bi+"&query="+jsonparams+"&gexf="+thisgexf+"&index="+TW.field[getUrlParam.file]);
// $("#topPapers").html(data);
// },
// error: function(){
......@@ -359,8 +359,11 @@ function set_ClustersLegend ( daclass, groupedByTicks ) {
// a custom variant of twitter plugin written for politoscope
function getTopPapers(type){
if(TW.getAdditionalInfo){
// NB: this variant only for nodetype semantic
function getTopPapers(nodetypeLegacy){
if (nodetypeLegacy == 'semantic' && TW.conf.getRelatedDocs) {
jsonparams=getSelections();
var joined_q = jsonparams.map(function(w) {return '('+w+')'}).join(' AND ')
......@@ -370,7 +373,7 @@ function getTopPapers(type){
//
$.ajax({
type: 'GET',
url: TW.companionAPI,
url: TW.conf.relatedDocsAPI,
data: {'query': joined_q},
contentType: "application/json",
success : function(data){
......@@ -385,7 +388,7 @@ function getTopPapers(type){
}
}
else {
topTweetsHtml = `<p class="micromessage centered">The query <span class=code>${joined_q}</span> delivers no results on Twitter with the topic #Presidentielles2017 and most related hashtags</p>`
topTweetsHtml = `<p class="micromessage centered">The query <span class=code>${joined_q}</span> delivers no results on Twitter.</p>`
}
$("#topPapers").html(topTweetsHtml);
......@@ -399,9 +402,7 @@ function getTopPapers(type){
}
function clickInsideTweet(e, tweetSrcUrl) {
console.log('>>>>> event target tag', e.target.tag)
console.log("event")
console.log(e)
console.debug('inside tweet tagName', e.target.tagName)
var tgt = e.target
if (tgt.tagName.toLowerCase() == "a")
window.open(tgt.href, "Link in tweet")
......@@ -506,7 +507,7 @@ function RenderTweet( tweet) {
//FOR UNI-PARTITE
// function selectionUni(currentNode){
// console.log("\tin selectionUni:"+currentNode.id);
// if(checkBox==false && TW.circleSize==0) {
// if(TW.checkBox==false && TW.circleSize==0) {
// highlightSelectedNodes(false);
// opossites = [];
// selections = [];
......@@ -793,23 +794,26 @@ function flashNodesArray (nodesArray) {
// BASIC MODULARITY
// =================
// ProcessDivsFlags is for adding/removing features from TinawebJS
// activateModules is for adding/removing features from TinawebJS
// each flag is simultaneously 3 things:
// - the key of a bool config value in DivsFlags (settings_explorerjs)
// - the key of a bool config value in TW.conf.ModulesFlags (settings_explorerjs)
// - the dirname of the submodule's files (with a mandatory init.js)
// - the css class of all html elements added by the submodule
function ProcessDivsFlags() {
for(var key in TW.conf.DivsFlags) {
if(TW.conf.DivsFlags[key]===false) {
function activateModules() {
for(var key in TW.conf.ModulesFlags) {
if(TW.conf.ModulesFlags[key]===false) {
$("."+key).remove() ; // hide elements of module's class
}
else {
// console.log("extras:ProcessDivsFlags: key is true: "+key)
// console.log("extras:activateModules: key is true: "+key)
// load JS+CSS items corresponding to the flagname
my_src_dir = key
let my_src_dir = key
// synchronous ajax
let moduleIsPresent = linkCheck(my_src_dir+"/init.js")
// TODO check if async not a problem
if (linkCheck(my_src_dir+"/init.js")) {
if (moduleIsPresent) {
loadJS(my_src_dir+"/init.js") ;
}
else {
......
......@@ -6,29 +6,50 @@ TW.conf = (function(TW){
let TWConf = {}
TWConf.branding = 'test bipart' // <----- the name displayed in upper left
TWConf.branding = 'ProjectExplorer' // <--- the name displayed in upper left
// ==========================
// TINA POSSIBLE DATA SOURCES
// ==========================
// Graph data source
// -----------------
// the graph input depends on TWConf.sourcemode (or manual url arg 'sourcemode')
TWConf.sourcemode = "api" // accepted: "api" | "serverfile" | "servermenu" | "localfile"
// server-side gexf default source
// server-side .gexf|.json default source
TWConf.sourceFile = "data/politoscope/ProgrammeDesCandidats.enrichi.gexf"
// or remote bridge to default source api ajax queries
// ...or server-side gexf default source list
TWConf.sourceMenu = "db.json"
// ...or remote bridge to default source api ajax queries
TWConf.sourceAPI={};
TWConf.sourceAPI["forNormalQuery"] = "services/api/graph";
TWConf.sourceAPI["forFilteredQuery"] = "services/api/graph";
// Related documents (topPapers) data source
// -----------------------------------------
TWConf.getRelatedDocs = true
TWConf.relatedDocsAPI = "http://127.0.0.1:5000/twitter_search"
// £TODO : allow to choose between twitter or elasticsearch topPapers (choic of post-process function in extras_explorer)
// TWConf.relatedDocsType
// ===========
// DATA FACETS
// ===========
// create facets ?
TWConf.scanClusters = true
// to handle node attributes from data
// => clusters (discrete numeric or str vars),
// => colors (continuous numeric vars)
......@@ -36,73 +57,120 @@ TW.conf = (function(TW){
// for continuous attrvalues/colors (cf. clustersBy), how many levels in legend?
TWConf.legendsBins = 7 ;
// max discrete levels in facet legend (if attribute has more distinct values then binning)
TWConf.maxDiscreteValues = 40
// £TODO transform for new specifications
// some specific attributes may have other number of levels
TWConf.customLegendsBins = {
'age': 8,
'growth_rate': 12
}
// default clustering (used to show as initial color)
TWConf.nodeClusAtt = "modularity_class"
// ===================
// TINA ACTIVE MODULES
// ===================
TWConf.DivsFlags = {} ;
// flag name is div class to be removed if false
// *and* subdirectory to import if true
// see also ProcessDivsFlags()
TWConf.DivsFlags["histogramModule"] = false ;
TWConf.DivsFlags["histogramDailyVariantModule"] = false ;
// TODO more generic module integrating the variants cf. experiments/histogramModule_STUB_GENERIQUE
TWConf.DivsFlags["crowdsourcingModule"] = false ;
TWConf.libspath = 'libs' // FIXME path vars should not be used after page load !
// =============
// TINA BEHAVIOR
// =============
// Node typology
// Node typology (searched in nodes data, overridden if data has other types)
// (FIXME cf. comment in sortNodeTypes and swActual functions
// about the limits of how these 2 values and
// TW.categories are used in older functions)
TWConf.catSoc = "Document";
TWConf.catSem = "NGram";
// Events handling
TWConf.deselectOnclickStage = true // will a click on the background remove selection ? (except when dragging)
// Active modules
// --------------
TWConf.ModulesFlags = {} ;
// flag name is div class to be removed if false
// *and* subdirectory to import if true
// see also activateModules()
TWConf.ModulesFlags["histogramModule"] = false ;
TWConf.ModulesFlags["histogramDailyVariantModule"] = false ;
// TODO more generic module integrating the variants cf. experiments/histogramModule_STUB_GENERIQUE
TWConf.ModulesFlags["crowdsourcingModule"] = true ;
// debug flags & log levels
TWConf.debug = {
initialShowAll: false, // show all nodes on bipartite case init (docs + terms in one view)
// Other optional functionalities
// -----------------------------
TWConf.filterSliders = true // show sliders for nodes/edges subsets
// show verbose console logs...
logFetchers: false, // ...about ajax/fetching of graph data
logParsers: false, // ...about parsing said data
logFacets: false, // ...about parsing node attribute:value facets
logSettings: false, // ...about settings at Tina and Sigma init time
logSelections: false
}
TWConf.colorsByAtt = false; // show "Set colors" menu
TWConf.deselectOnclickStage = true // click on background remove selection ?
// (except when dragging)
TWConf.histogramStartThreshold = 10 ; // for daily histo module
// (from how many docs are significant)
// Layouts
// -------
// £TODO these exist only in git branches
// (geomap: ademe, timeline: tweetoscope)
// ==> ask if need to be restored
// TW.geomap = false;
// TW.twittertimeline = false;
// Layout options
// --------------
TWConf.fa2Available=true; // show/hide fa2Button
TWConf.disperseAvailable=true; // show/hide disperseButton
// if fa2Available, the auto-run config:
TWConf.fa2Enabled= true; // fa2 auto-run at start and after graph modified ?
TWConf.fa2Milliseconds=5000; // duration of auto-run
TWConf.minNodesForAutoFA2 = 5 // graph size threshold to auto-run
TWConf.fa2Enabled= false; // fa2 auto-run at start and after graph modified ?
TWConf.fa2Milliseconds=5000; // duration of auto-run
TWConf.minNodesForAutoFA2 = 5 // graph size threshold to auto-run
// Full-text search
// ----------------
TWConf.minLengthAutoComplete = 1;
TWConf.maxSearchResults = 10;
TWConf.maxSearchResults = 10; // how many "top papers" to display
TWConf.minLengthAutoComplete = 1; // how many chars to type for autocomp
TWConf.strSearchBar = "Select topics";
// =======================
// TINA RENDERING SETTINGS
// =======================
TWConf.overSampling = true // costly hi-def rendering (true => pixelRatio x 2)
// relative sizes (iff graph display with both nodetypes)
TWConf.sizeMult = [];
TWConf.sizeMult[0] = 1.5; // ie for node type 0
TWConf.sizeMult[1] = 1.0; // ie for node type 1
// circle selection cursor
TWConf.circleSizeMin = 0;
TWConf.circleSizeMax = 100;
// SIGMA BEHAVIOR SETTINGS
// size range for neighbor nodes "tagcloud"
TWConf.tagcloudFontsizeMin = 12;
TWConf.tagcloudFontsizeMax = 24;
TWConf.tagcloudSameLimit = 50 // display at most how many neighbors of the same type
TWConf.tagcloudOpposLimit = 10 // display at most how many neighbors of the opposite type
TWConf.defaultNodeColor = "rgb(40,40,40)"
// selected/deselected rendering
TWConf.nodesGreyBorderColor = "rgba(100, 100, 100, 0.5)"; // not selected nodes
TWConf.selectedColor = "default" // "node" for a background like the node's color,
// "default" for note-like yellow
TWConf.edgeDefaultOpacity = 0.4 // opacity when true_color
TWConf.edgeGreyColor = "rgba(150, 150, 150, 0.5)"; // not selected edges
// ========================
// SIGMA RENDERING SETTINGS
// ========================
// triggers overriding sigma.canvas renderers: nodes.def, labels.def, edges.def
TWConf.ourRendering = true ;
......@@ -147,46 +215,34 @@ TW.conf = (function(TW){
};
// =======================
// TINA RENDERING SETTINGS
// =======================
TWConf.overSampling = false // costly hi-def rendering (true => pixelRatio x 2)
TWConf.sizeMult = [];
TWConf.sizeMult[0] = 1.0; // ie for node type 0
TWConf.sizeMult[1] = 1.0; // ie for node type 1
TWConf.circleSizeMin= 0;
TWConf.circleSizeMax= 100;
// ========
// A RANGER £TODO
// ========
TWConf.nodeClusAtt = "modularity_class"
// ===========
// DEBUG FLAGS
// ===========
TWConf.debug = {
initialShowAll: false, // show all nodes on bipartite case init (docs + terms in one view)
TWConf.filterSliders = true
// show verbose console logs...
logFetchers: false, // ...about ajax/fetching of graph data
logParsers: false, // ...about parsing said data
logFacets: true, // ...about parsing node attribute:value facets
logSettings: false, // ...about settings at Tina and Sigma init time
logSelections: true
}
TWConf.histogramStartThreshold = 10 ;
TWConf.defaultNodeColor = "rgb(40,40,40)"
TWConf.edgeDefaultOpacity = 0.4 // opacity when true_color
TWConf.edgeGreyColor = "rgba(150, 150, 150, 0.5)";
TWConf.nodesGreyBorderColor = "rgba(100, 100, 100, 0.5)";
TWConf.selectedColor = "default" // "node" for a background like the node's color,
// "default" for note-like yellow
// £TODO: fix these 2 settings with a better dir structure
// + but avoid path injection
// + find a place for modules *INSIDE* tinawebJS dir for easier deployment
TWConf.ModulesPath = ''
TWConf.libspath = 'libs'
console.warn("current conf:", TWConf)
return TWConf
})()
// INITIALIZED VARS (£TODO move to main or Tina)
// INITIALIZED VARS
// ================
TW.Nodes = [];
TW.Edges = [];
......@@ -214,9 +270,6 @@ var bipartiteN2D = {};
TW.categories = [];
TW.catDict = {};
TW.nodeslength = 0 // <=== £TODO harmonize use with TW.partialGraph.graph.nNodes()
var gexfFile;
//var zoom=0;
TW.checkBox=false;
......@@ -248,8 +301,6 @@ var lastFilter = []
lastFilter["#slidercat0edgesweight"] = {"orig":"-" , "last":"-"}
lastFilter["#slidercat1edgesweight"] = {"orig":"-" , "last":"-"}
var desirableTagCloudFont_MIN=12;
var desirableTagCloudFont_MAX=20;
var desirableNodeSizeMIN=1;
var desirableNodeSizeMAX=12;
......
......@@ -147,7 +147,7 @@ function SelectionEngine() {
// ====================
this.MultipleSelection2 = (function(nodes,nodesDict,edgesDict) {
if (TW.conf.debug.selections) {
if (TW.conf.debug.logSelections) {
var tMS2_deb = performance.now()
console.log("IN SelectionEngine.MultipleSelection2:")
......@@ -214,7 +214,12 @@ function SelectionEngine() {
if (typeof sameSideNeighbors[t] == 'undefined') {
sameSideNeighbors[t]=0
}
sameSideNeighbors[t]++
if (TW.Edges[s+";"+t])
sameSideNeighbors[t] += TW.Edges[s+";"+t].weight || 1
if (TW.Edges[t+";"+s])
sameSideNeighbors[t] += TW.Edges[t+";"+s].weight || 1
}
}
}
......@@ -290,6 +295,8 @@ function SelectionEngine() {
for(var n in bipaNeighs) {
if (typeof oppositeSideNeighbors[bipaNeighs[n]] == "undefined")
oppositeSideNeighbors[bipaNeighs[n]] = 0;
// £TODO weighted increment
oppositeSideNeighbors[bipaNeighs[n]]++;
}
}
......@@ -312,7 +319,7 @@ function SelectionEngine() {
return b-a
});
if (TW.conf.debug.selections) {
if (TW.conf.debug.logSelections) {
console.debug('selections', selections)
console.debug('oppos', oppos)
console.debug('same', same)
......@@ -325,11 +332,13 @@ function SelectionEngine() {
updateRelatedNodesPanel( selections , same, oppos );
if (TW.conf.debug.selections) {
if (TW.conf.debug.logSelections) {
var tMS2_fin = performance.now()
console.log("end MultipleSelection2, own time:", tMS2_fin-tMS2_deb)
}
}).index()
};
......@@ -737,6 +746,10 @@ TinaWebJS = function ( sigmacanvas ) {
TW.partialGraph.camera.goTo({x:0, y:0, ratio:1.2})
});
if (!TW.conf.colorsByAtt) {
$("#setcolorsMenu").hide()
}
if (TW.conf.fa2Available) {
$("#layoutButton").click(function () {
sigma_utils.smartForceAtlas()
......
......@@ -7,6 +7,9 @@ function writeBrand (brandString) {
document.getElementById('twbrand').innerHTML = brandString
}
function writeLabel (aMapLabel) {
document.getElementById('maplabel').innerHTML = aMapLabel
}
function createFilechooserEl () {
......@@ -45,6 +48,7 @@ function createFilechooserEl () {
rdr.onload = function() {
if (! rdr.result || !rdr.result.length) {
alert('the selected file is not readable')
writeLabel(`Local file: unreadable!`)
}
else {
// we might have a previous graph opened
......@@ -59,6 +63,8 @@ function createFilechooserEl () {
mainStartGraph(theFormat, JSON.parse(rdr.result), TW.instance)
else
mainStartGraph(theFormat, rdr.result, TW.instance)
writeLabel(`Local file: ${clientLocalGraphFile.name}`)
}
}
rdr.readAsText(clientLocalGraphFile)
......@@ -787,6 +793,10 @@ function EdgeWeightFilter(sliderDivID , typestr , criteria) {
// NodeWeightFilter ( "#sliderBNodeWeight" , "NGram" , "size")
function NodeWeightFilter( sliderDivID , tgtNodeType , criteria) {
if (typeof tgtNodeType == "undefined") {
throw 'no nodetype'
}
if(TW.partialGraph.graph.nNodes() < 2) {
console.warn('not enough nodes for subsets: skipping GUI slider init')
showDisabledSlider(sliderDivID)
......@@ -903,8 +913,6 @@ function showDisabledSlider(someDivId) {
//=========================== </ FILTERS-SLIDERS > ===========================//
//============================= < SEARCH > =============================//
function updateSearchLabels(id,name,type){
TW.labels.push({
......@@ -959,3 +967,19 @@ function searchLabel(string){
}
}
//============================ < / SEARCH > ============================//
//============================= < OTHER ACTIONS > =============================//
function jsActionOnGexfSelector(gexfBasename){
let gexfPath = TW.gexfPaths[gexfBasename] || gexfBasename+".gexf"
let serverPrefix = ''
var pathcomponents = window.location.pathname.split('/')
for (var i in pathcomponents) {
if (pathcomponents[i] != 'explorerjs.html')
serverPrefix += '/'+pathcomponents[i]
}
var newDataRes = AjaxSync({ URL: window.location.origin+serverPrefix+'/'+gexfPath });
mainStartGraph(newDataRes["format"], newDataRes["data"], TW.instance)
writeLabel(gexfBasename)
}
//============================= </OTHER ACTIONS > =============================//
......@@ -143,38 +143,16 @@ getUrlParam = (function () {
function ArraySortByValue(array, sortFunc){
var tmp = [];
// oposMAX=0;
for (var k in array) {
if (array.hasOwnProperty(k)) {
tmp.push({
key: k,
value: array[k]
});
// if((array[k]) > oposMAX) oposMAX= array[k];
}
}
tmp.sort(function(o1, o2) {
return sortFunc(o1.value, o2.value);
});
return tmp;
}
function ArraySortByKey(array, sortFunc){
var tmp = [];
for (var k in array) {
if (array.hasOwnProperty(k)) {
tmp.push({
key: k,
value: array[k]
});
}
tmp.push({
key: k,
value: array[k]
});
}
// reverse numeric on prop 'value'
tmp.sort(function(o1, o2) {
return sortFunc(o1.key, o2.key);
return (parseFloat(o2.value) - parseFloat(o1.value));
});
return tmp;
}
......
......@@ -74,19 +74,10 @@ var AjaxSync = (function(TYPE, URL, DATA, DT) {
return Result;
}).index();
function jsActionOnGexfSelector(gexfBasename){
let gexfPath = TW.gexfPaths[gexfBasename] || gexfBasename+".gexf"
let serverPrefix = ''
var pathcomponents = window.location.pathname.split('/')
for (var i in pathcomponents) {
if (pathcomponents[i] != 'explorerjs.html')
serverPrefix += '/'+pathcomponents[i]
}
var newDataRes = AjaxSync({ URL: window.location.origin+serverPrefix+'/'+gexfPath });
mainStartGraph(newDataRes["format"], newDataRes["data"], TW.instance)
}
// === [ what to do at start ] === //
console.log("Starting TWJS")
// NB this method-holding instance could be initialized just once or even removed?
var sigma_utils = new SigmaUtils();
......@@ -103,12 +94,12 @@ TW.instance.initSearchListeners();
// show the custom name of the app
writeBrand(TW.conf.branding)
console.log("Starting TWJS")
// choosing the 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:'
|| (!isUndef(getUrlParam.sourcemode) && getUrlParam.sourcemode == 'localfile')) {
let inputDiv = document.getElementById('localInput')
inputDiv.style.display = 'block'
......@@ -120,15 +111,15 @@ if (window.location.protocol == 'file:') {
inputDiv.appendChild(remark)
// user can open a gexf or json from his fs
// POSS we could actually provide this local file chooser in all cases
var graphFileInput = createFilechooserEl()
inputDiv.appendChild(graphFileInput)
}
// traditional cases: remote read from API or prepared server-side file
else {
// NB it will use global urlParams and TW.settings to choose the source
var [inFormat, inData] = syncRemoteGraphData()
var [inFormat, inData, mapLabel] = syncRemoteGraphData()
mainStartGraph(inFormat, inData, TW.instance)
writeLabel(mapLabel)
}
// === [ / what to do at start ] === //
......@@ -256,7 +247,7 @@ function syncRemoteGraphData () {
// cases (2) and (3) : we'll read a file from server
// sourcemode == "serverfile" or "servermenu" (several files with <select>)
else {
console.log("input case: server-side file, using db.json or getUrlParam.file or TW.conf.sourceFile")
console.log("input case: server-side file, using TW.conf.sourceMenu or getUrlParam.file or TW.conf.sourceFile")
// -> @mode is servermenu, files are listed in db.json file (preRes ajax)
// --> if @file also in url, choose the db.json one matching <=== £TODO THIS CASE STILL TO FIX
......@@ -272,9 +263,9 @@ function syncRemoteGraphData () {
// menufile case : a list of source files in ./db.json
if (sourcemode == 'servermenu') {
console.log("no @file arg nor TW.mainfile: trying FILEMENU db.json")
console.log("no @file arg nor TW.mainfile: trying FILEMENU TW.conf.sourceMenu")
// we'll first retrieve the menu of available files in db.json, then get the real data in a second ajax
var infofile = "db.json"
var infofile = TW.conf.sourceMenu
if (TW.conf.debug.logFetchers) console.info(`attempting to load infofile ${infofile}`)
var preRES = AjaxSync({ URL: infofile, DT:"json" });
......@@ -297,9 +288,11 @@ function syncRemoteGraphData () {
// the first or a specified one (ie both mode and file params are present)
if( isUndef(getUrlParam.file) ) {
the_file = first_path+"/"+first_file
mapLabel = first_file
} else {
// £POSS; match on the full paths from db.json
the_file = first_path+"/"+getUrlParam.file
mapLabel = getUrlParam.file
}
var files_selector = '<select onchange="jsActionOnGexfSelector(this.value);">'
......@@ -723,7 +716,7 @@ function mainStartGraph(inFormat, inData, twInstance) {
TW.partialGraph.camera.goTo({x:0, y:0, ratio:0.9, angle: 0})
// mostly json data are extracts provided by DB apis => no positions
if (inFormat == "json") TW.conf.fa2Enabled = true
// if (inFormat == "json") TW.conf.fa2Enabled = true
// will run fa2 if enough nodes and TW.conf.fa2Enabled == true
sigma_utils.smartForceAtlas()
......@@ -761,13 +754,13 @@ function mainStartGraph(inFormat, inData, twInstance) {
// load optional modules
ProcessDivsFlags() ;
activateModules() ;
// show any already existing panel
document.getElementById("graph-panels").style.display = "block"
// grey message in the search bar from settings
$("#searchinput").attr('placeholder', TW.strSearchBar) ;
$("#searchinput").attr('placeholder', TW.conf.strSearchBar) ;
setTimeout( function() {
......
......@@ -3,7 +3,7 @@
// settings: {norender: Bool}
function cancelSelection (fromTagCloud, settings) {
if (TW.conf.debug.selections) { console.log("\t***in cancelSelection"); }
if (TW.conf.debug.logSelections) { console.log("\t***in cancelSelection"); }
if (!settings) settings = {}
highlightSelectedNodes(false); //Unselect the selected ones :D
......@@ -111,6 +111,24 @@ function getActivetypesKey() {
return lastState.activetypes.map(Number).join('|')
}
// transitional function:
// ----------------------
// Goal: determine if a single nodetype or global activetype is semantic or social
// Explanation: some older functions (eg topPapers) used this distinction
// (via semi-deprecated global swclickActual),
// but the specification changed twice since then:
// - 1st change: types described as type 0 and type 1 and possible default type
// - 2nd change default type of monopartite case changed from document to semantic
function swActual(aNodetype) {
if (TW.categories.length == 1) {
return 'semantic'
}
else if (TW.categories.length == 2) {
return (aNodetype == TW.categories[0]) ? 'social' : 'semantic'
}
}
function highlightSelectedNodes(flag){
if (TW.conf.debug.logSelections)
......@@ -242,17 +260,21 @@ function htmlfied_alternodes(elems) {
var js1='onclick="graphTagCloudElem(\'';
var js2="');\""
var frecMAX=elems[0].value
console.log("htmlfied_alternodes elems", elems)
console.log("htmlfied_alternodes frecMAX", frecMAX)
for(var i in elems){
var id=elems[i].key
var frec=elems[i].value
var fontSize
var htmlfied_alternode
if(frecMAX==1) fontSize=desirableTagCloudFont_MIN;
if(frecMAX==1) fontSize=TW.conf.tagcloudFontsizeMin;
else {
fontSize=
desirableTagCloudFont_MIN+
TW.conf.tagcloudFontsizeMin+
(frec-1)*
((desirableTagCloudFont_MAX-desirableTagCloudFont_MIN)/(frecMAX-1));
((TW.conf.tagcloudFontsizeMax-TW.conf.tagcloudFontsizeMin)/(frecMAX-1));
}
if(!isUndef(TW.Nodes[id])){
......@@ -316,22 +338,6 @@ function clearHover() {
)
}
// TODO rm ? function doesn't make sense, probably replaced by htmlfied_tagcloud
// function htmlfied_samenodes(elems) {
// var sameNodes=[]
// js1=' onmouseover="manualForceLabel(this.id,true, true);" ';
// js2=' onmouseout="manualForceLabel(this.id,true, true);" ';
// if(elems.length>0) {
// var A = getVisibleNodes()
// for (var a in A){
// n = A[a]
// if(!n.active && n.color.charAt(0)=="#" ) {
// sameNodes.push('<li onmouseover="manualForceLabel(\''+n.id+'\',true, true)" onmouseout="manualForceLabel(\''+n.id+'\',false, true)" ><a>'+ n.label+ '</a></li>')
// }
// }
// }
// return sameNodes
// }
// nodes information div
function htmlfied_nodesatts(elems){
......@@ -369,9 +375,9 @@ function htmlfied_nodesatts(elems){
if(node.type==TW.conf.catSem){
information += '<li><b>' + node.label + '</b></li>';
google='<a href=http://www.google.com/#hl=en&source=hp&q=%20'+node.label.replace(" ","+")+'%20><img src="'+'img/google.png"></img></a>';
wiki = '<a href=http://en.wikipedia.org/wiki/'+node.label.replace(" ","_")+'><img src="'+'img/wikipedia.png"></img></a>';
flickr= '<a href=http://www.flickr.com/search/?w=all&q='+node.label.replace(" ","+")+'><img src="'+'img/flickr.png"></img></a>';
let google='<a href=http://www.google.com/#hl=en&source=hp&q=%20'+node.label.replace(" ","+")+'%20><img src="'+'img/google.png"></img></a>';
let wiki = '<a href=http://en.wikipedia.org/wiki/'+node.label.replace(" ","_")+'><img src="'+'img/wikipedia.png"></img></a>';
let flickr= '<a href=http://www.flickr.com/search/?w=all&q='+node.label.replace(" ","+")+'><img src="'+'img/flickr.png"></img></a>';
information += '<li>'+google+"&nbsp;"+wiki+"&nbsp;"+flickr+'</li><br>';
semnodes.push(information)
}
......@@ -388,33 +394,40 @@ function manualSelectNode ( nodeid ) {
// (MultipleSelection2 will do the re-rendering)
}
function htmlfied_tagcloud(elems , limit) {
function htmlProportionalLabels(elems , limit, selectableFlag) {
if(elems.length==0) return false;
var oppositesNodes=[]
var fontSize=desirableTagCloudFont_MIN
let frecMAX=elems[0].value
let resHtml=[]
let fontSize // <-- normalized for display
// we assume already sorted
let frecMax = elems[0].value
let frecMin = elems.slice(-1)[0].value
for(var i in elems){
if(i==limit)
break
let id=elems[i].key
let frec=elems[i].value
if(frecMAX > 1) {
fontSize=
desirableTagCloudFont_MIN+
(frec-1)*
((desirableTagCloudFont_MAX-desirableTagCloudFont_MIN)/(frecMAX-1));
}
// FIXME: works but is optimizable (precompute stable part)
fontSize = ((frec - frecMin) * (TW.conf.tagcloudFontsizeMax - TW.conf.tagcloudFontsizeMin) / (frecMax - frecMin)) + TW.conf.tagcloudFontsizeMin
// debug
// console.log('htmlfied_tagcloud (',id, TW.Nodes[id].label,') freq',frec,' fontSize', fontSize)
if(!isUndef(TW.Nodes[id])){
var jspart = ' onclick="manualSelectNode(\''+id+'\')" onmouseover="manualForceLabel(\''+id+'\',true, true)" onmouseout="manualForceLabel(\''+id+'\',false, true)"'
let htmlfied_alternode = '<span class="tagcloud-item" style="font-size:'+fontSize+'px;" '+jspart+'>'+ TW.Nodes[id].label+ '</span>';
oppositesNodes.push(htmlfied_alternode)
var jspart = ''
if (selectableFlag) {
jspart = ' onclick="manualSelectNode(\''+id+'\')" onmouseover="manualForceLabel(\''+id+'\',true, true)" onmouseout="manualForceLabel(\''+id+'\',false, true)"'
}
let htmlLabel = '<span class="tagcloud-item" style="font-size:'+fontSize+'px;" '+jspart+'>'+ TW.Nodes[id].label+ '</span>';
resHtml.push(htmlLabel)
}
}
return oppositesNodes
return resHtml
}
//missing: getTopPapers for both node types
......@@ -449,13 +462,13 @@ function updateRelatedNodesPanel( sels , same, oppos ) {
if(oppos.length>0) {
alterNodesDIV+='<div id="oppositesBox">';//tagcloud
alterNodesDIV+= htmlfied_alternodes( oppos ).join("\n")
alterNodesDIV+= htmlProportionalLabels( oppos , TW.conf.tagcloudOpposLimit, false).join("\n")
alterNodesDIV+= '</div>';
}
if(getNodeIDs(sels).length>0) {
sameNodesDIV+='<div id="sameNodes">';//tagcloud
var sameNeighTagcloudHtml = htmlfied_tagcloud( same , TW.tagcloud_limit)
var sameNeighTagcloudHtml = htmlProportionalLabels( same , TW.conf.tagcloudSameLimit, true )
sameNodesDIV+= (sameNeighTagcloudHtml!=false) ? sameNeighTagcloudHtml.join("\n") : "No related items.";
sameNodesDIV+= '</div>';
}
......@@ -481,7 +494,7 @@ function updateRelatedNodesPanel( sels , same, oppos ) {
$("#tips").html("");
if(TW.categories.length==1) getTopPapers("semantic");
else getTopPapers(swclickActual);
else getTopPapers(swActual(getActivetypes()[0]));
}
function printStates() {
......@@ -577,7 +590,7 @@ function graphTagCloudElem(nodes) {
nodesDict:nodes_2_colour,
edgesDict:edges_2_colour
});
overNodes=true; // deprecated
TW.selectionActive = true
}
var present = TW.partialGraph.states.slice(-1)[0]; // Last
......@@ -799,7 +812,6 @@ function add1Elem(id) {
if(!n.lock) {
updateSearchLabels(id,n.label,n.type);
nodeslength++;
}
// TW.partialGraph.graph.addNode(anode);
TW.partialGraph.graph.addNode(n);
......
......@@ -248,7 +248,7 @@ function sortNodeTypes(observedTypesDict) {
// POSSible: allow more than 2 cats
for(var i in observedTypes) {
let c = observedTypes[i]
if(c == TW.conf.catSoc || (c != TW.catSem && c.indexOf("term")==-1)) {// NOT a term-category
if(c == TW.conf.catSoc || (c != TW.conf.catSem && c.indexOf("term")==-1)) {// NOT a term-category
newcats[0] = c;
catDict[c] = 0;
}
......@@ -299,6 +299,7 @@ function facetsBinning (valuesIdx, Atts_2_Exclude) {
// console.log(`======= ${cat}::${at} =======`)
// skip non-numeric or already done
// £TODO finish changes to Atts_2_Exclude from 69e7c039
if (Atts_2_Exclude[at] || at == "clust_default") {
continue
}
......@@ -307,7 +308,7 @@ function facetsBinning (valuesIdx, Atts_2_Exclude) {
facetIdx[cat][at] = []
// if n possible values doesn't need binify
if (Object.keys(valuesIdx[cat][at].map).length <= TW.maxDiscreteValues) {
if (Object.keys(valuesIdx[cat][at].map).length <= TW.conf.maxDiscreteValues) {
for (var pval in valuesIdx[cat][at].map) {
facetIdx[cat][at].push({
'labl': `${cat}||${at}||${pval}`,
......@@ -592,7 +593,7 @@ function dictfyGexf( gexf , categories ){
// console.debug("node.attributes", node.attributes)
// creating a faceted index from node.attributes
if (TW.scanClusters) {
if (TW.conf.scanClusters) {
[tmpVals, Atts_2_Exclude] = updateValueFacets(tmpVals, Atts_2_Exclude, node)
}
......@@ -615,6 +616,7 @@ function dictfyGexf( gexf , categories ){
// clusters and other facets => type => name => [{label,val/range,nodeids}]
// £TODO finish changes to Atts_2_Exclude from 69e7c039 (new specif: dtype str is accepted for classes)
TW.Clusters = facetsBinning(tmpVals, Atts_2_Exclude)
......
'use strict';
SigmaUtils = function () {
this.nbCats = 0;
// input = GEXFstring
this.FillGraph = function( initialActivetypes , catDict , nodes, edges , graph ) {
......
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