diff --git a/doc/developer_manual.md b/doc/developer_manual.md index 4567a56b5c42da34e0c17117999656a92ae6d1f4..569adb52236e6886780582c0084849d577e9ea75 100644 --- a/doc/developer_manual.md +++ b/doc/developer_manual.md @@ -57,3 +57,16 @@ They are handled in Tinaweb.MultipleSelection2. For any node `n` the relevant flags at selection are: - `n.active` iff node is selected - `n.customAttrs.highlight` if node is a neighbor of a selected node + + +## Variae + +#### Facets: node attributes as colors/clusters + +At parsing time, every node attributes are indexed by values. + +This indexes are stored in TW.Clusters and provide an access to sets of nodes that have a given value or range of values. + - if discrete attrvalues with <= 30 classes (colorsBy, clustersBy), the storage structure is: `TW.Clusters[nodeType][clusterType].classes.[possibleValue]` + (the content is a list of ids with the value `possibleValue`) + - if continuous or many possible values (>30) (clustersBy, colorsRelByBins), the storage uses ordered ranges ("bins"): + `TW.Clusters[nodeType][clusterType].ranges.[interval]` diff --git a/extras_explorerjs.js b/extras_explorerjs.js index 5088069f8fef5c9a722179069fb91c5ceb0f45f7..115e95f1f714a36bd885fbd66eecca8f3ad151f7 100755 --- a/extras_explorerjs.js +++ b/extras_explorerjs.js @@ -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.APINAME+'img/ajax-loader.gif"></img>'; +// image='<img style="display:block; margin: 0px auto;" src="'+TW.companionAPI+'img/ajax-loader.gif"></img>'; // $("#tab-container-top").show(); // $("#topPapers").show(); // $("#topPapers").html(image); // $.ajax({ // type: 'GET', -// url: TW.APINAME+'info_div.php', +// url: TW.companionAPI+'info_div.php', // data: "type="+type+"&bi="+bi+"&query="+jsonparams+"&gexf="+thisgexf+"&index="+TW.field[getUrlParam.file], // //contentType: "application/json", // //dataType: 'json', // success : function(data){ -// console.log(TW.APINAME+'info_div.php?'+"type="+type+"&bi="+bi+"&query="+jsonparams+"&gexf="+thisgexf+"&index="+TW.field[getUrlParam.file]); +// console.log(TW.companionAPI+'info_div.php?'+"type="+type+"&bi="+bi+"&query="+jsonparams+"&gexf="+thisgexf+"&index="+TW.field[getUrlParam.file]); // $("#topPapers").html(data); // }, // error: function(){ @@ -370,7 +370,7 @@ function getTopPapers(type){ // $.ajax({ type: 'GET', - url: TW.APINAME, + url: TW.companionAPI, data: {'query': joined_q}, contentType: "application/json", success : function(data){ @@ -506,7 +506,7 @@ function RenderTweet( tweet) { //FOR UNI-PARTITE // function selectionUni(currentNode){ // console.log("\tin selectionUni:"+currentNode.id); -// if(checkBox==false && cursor_size==0) { +// if(checkBox==false && TW.circleSize==0) { // highlightSelectedNodes(false); // opossites = []; // selections = []; @@ -683,7 +683,7 @@ function circleTrackMouse(e) { // } // } - ctx.arc(x, y, cursor_size, 0, Math.PI * 2, true); + ctx.arc(x, y, TW.circleSize, 0, Math.PI * 2, true); ctx.closePath(); ctx.fill(); ctx.stroke(); @@ -694,7 +694,7 @@ function circleTrackMouse(e) { // exact subset of nodes under circle function circleGetAreaNodes(camX0, camY0) { - var cursor_ray = cursor_size * TW.cam.ratio // cursor_size to cam units + var cursor_ray = TW.circleSize * TW.cam.ratio // converting TW.circleSize to cam units // prepare an approximate neighborhood var slightlyLargerNodeset = circleLocalSubset( @@ -799,8 +799,8 @@ function flashNodesArray (nodesArray) { // - 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.DivsFlags) { - if(TW.DivsFlags[key]===false) { + for(var key in TW.conf.DivsFlags) { + if(TW.conf.DivsFlags[key]===false) { $("."+key).remove() ; // hide elements of module's class } else { diff --git a/settings_explorerjs.js b/settings_explorerjs.js index ec7353474a0d2b855d2ec69149f241c535f28f80..123985a6d6b631ad34b14b22f91b773192aadfdc 100644 --- a/settings_explorerjs.js +++ b/settings_explorerjs.js @@ -2,248 +2,199 @@ var TW = {} +TW.conf = (function(TW){ -// £TODO separate files for TW.vars and TW.settings (configfile) - - TW.geomap = false; - TW.colorByAtt = false; - TW.twittertimeline = false; - TW.minimap=false; - TW.getAdditionalInfo = true;// True: Activate TopPapers feature. - TW.filemenu = 'db.json' - // TW.mainfile = "data/mysuperproject/my.gexf" - TW.mainfile = "data/politoscope/ProgrammeDesCandidats.enrichi.gexf" - TW.APINAME = "http://127.0.0.1:5000/twitter_search"; - TW.tagcloud_limit = 50; - TW.bridge={}; - TW.bridge["forFilteredQuery"] = "services/api/graph"; - TW.bridge["forNormalQuery"] = "services/api/graph"; - TW.gexfPaths={}; - TW.Relations = {} - - // module_names to load - // ---------------------- - TW.DivsFlags = {} ; - // flag name is div class to be removed if false - // *and* subdirectory to import if true - // see also ProcessDivsFlags() - TW.DivsFlags["histogramModule"] = false ; - TW.DivsFlags["histogramDailyVariantModule"] = false ; - // TODO more generic module integrating the variants cf. experiments/histogramModule_STUB_GENERIQUE - TW.DivsFlags["crowdsourcingModule"] = false ; - - TW.SystemState = {} - TW.SystemState.level = true; - TW.SystemState.type = [ true, false ] // usually overridden by initialActivetypes - TW.SystemState.selections = []; - TW.SystemState.opposites = []; - TW.catSoc = "Document"; - TW.catSem = "NGram"; - - TW.strSearchBar = "Select or suggest topics"; + let TWConf = {} -var ParseCustom = function () {}; -var SigmaUtils = function () {}; -var TinaWebJS = function () {}; + TWConf.branding = 'test bipart' // <----- the name displayed in upper left -// node sizes -// ---------- -var sizeMult = []; - sizeMult[TW.catSoc] = 0.0; - sizeMult[TW.catSem] = 0.0; + // ========================== + // TINA POSSIBLE DATA SOURCES + // ========================== -var minLengthAutoComplete = 1; -var maxSearchResults = 10; + // the graph input depends on TWConf.sourcemode (or manual url arg 'sourcemode') + TWConf.sourcemode = "api" // accepted: "api" | "serverfile" | "servermenu" | "localfile" -var cursor_size_min= 0; -var cursor_size= 0; -var cursor_size_max= 100; + // server-side gexf default source + TWConf.sourceFile = "data/politoscope/ProgrammeDesCandidats.enrichi.gexf" -var desirableTagCloudFont_MIN=12; -var desirableTagCloudFont_MAX=20; -var desirableNodeSizeMIN=1; -var desirableNodeSizeMAX=12; + // or remote bridge to default source api ajax queries + TWConf.sourceAPI={}; + TWConf.sourceAPI["forNormalQuery"] = "services/api/graph"; + TWConf.sourceAPI["forFilteredQuery"] = "services/api/graph"; -// layouts -// ------- -// just a simple flag on this one -TW.disperseAvailable=false; // disperseButton hidden at start, disperse stopped forever -TW.fa2Available=false; // fa2Button hidden at start, fa2 stopped forever -// if fa2Button: - TW.fa2enabled=0; // fa2 auto-running at start ? - TW.minNodesForAutoFA2 = 5 // fa2 not run if graph has less nodes than this threshold + // =========== + // DATA FACETS + // =========== + // to handle node attributes from data + // => clusters (discrete numeric or str vars), + // => colors (continuous numeric vars) -// ============ < / DEVELOPER OPTIONS > ============ -TW.branding = 'test bipart' -TW.libspath = 'libs' // NB path vars should not be used after page load + // for continuous attrvalues/colors (cf. clustersBy), how many levels in legend? + TWConf.legendsBins = 7 ; -TW.nodeClusAtt = "modularity_class" + // some specific attributes may have other number of levels + TWConf.customLegendsBins = { + 'age': 8, + 'growth_rate': 12 + } -TW.filterSliders = true + // =================== + // 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 ; -TW.histogramStartThreshold = 10 ; + TWConf.libspath = 'libs' // FIXME path vars should not be used after page load ! -TW.defaultNodeColor = "rgb(40,40,40)" -TW.edgeDefaultOpacity = 0.4 // opacity when true_color -TW.edgeGreyColor = "rgba(150, 150, 150, 0.5)"; -TW.nodesGreyBorderColor = "rgba(100, 100, 100, 0.5)"; -TW.selectedColor = "default" // "node" for a background like the node's color, - // "default" for note-like yellow + // ============= + // TINA BEHAVIOR + // ============= -TW.overSampling = false // costly hi-def rendering (true => pixelRatio x 2) + // Node typology + TWConf.catSoc = "Document"; + TWConf.catSem = "NGram"; -TW.deselectOnclickStage = true // will a click on the background remove selection ? (except when dragging) -var showLabelsIfZoom=1.0; + // Events handling + TWConf.deselectOnclickStage = true // will a click on the background remove selection ? (except when dragging) -// for continuous attrvalues/colors (cf. clustersBy), how many levels in legend? -TW.legendsBins = 7 ; -// some specific attributes may have other number of levels -TW.customLegendsBins = { - 'age': 8, - 'growth_rate': 12 -} + // debug flags & log levels + TWConf.debug = { + initialShowAll: false, // show all nodes on bipartite case init (docs + terms in one view) + // 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 + } -TW.debugFlags = { - initialShowAll: false, // show all nodes on bipartite case init (docs + terms in one view) - // 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: true -} + // Layouts + // ------- + TWConf.fa2Available=true; // show/hide fa2Button + TWConf.disperseAvailable=true; // show/hide disperseButton -// triggers overriding sigma.canvas renderers: nodes.def, labels.def, edges.def -TW.ourRendering = true ; + // if fa2Available, the auto-run config: -// ============ < / DEVELOPER OPTIONS > ============ + 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 -// ============ < SIGMA.JS PROPERTIES > ============ + // Full-text search + // ---------------- + TWConf.minLengthAutoComplete = 1; + TWConf.maxSearchResults = 10; -var sigmaJsDrawingProperties = { - defaultLabelColor: 'black', - defaultLabelSize: 30, // in fact usually overridden by node data... - labelSizeRatio: 1, // ...but this ratio allows truly adjusting the sizes - labelThreshold: 5, - defaultEdgeType: 'curve', // 'curve' or 'line' + // SIGMA BEHAVIOR SETTINGS - defaultBorderView: "always", + // SIGMA RENDERING SETTINGS + // triggers overriding sigma.canvas renderers: nodes.def, labels.def, edges.def + TWConf.ourRendering = true ; - // new sigma.js only for hover + new settingnames - defaultHoverLabelBGColor: '#fff', - defaultHoverLabelColor: '#000', - borderSize: 2.5, // (only for hovered nodes) - defaultNodeBorderColor: "black", - nodeBorderColor: "default", // vs. node - // for custom TW node renderer with borders - twNodeRendBorderSize: 1, // (for all normal nodes, iff TW.nodeRendBorder) - twNodeRendBorderColor: "#222", - // twNodeRendBorderColor: "#eee", + TWConf.sigmaJsDrawingProperties = { + defaultLabelColor: 'black', + defaultLabelSize: 30, // in fact usually overridden by node data... + labelSizeRatio: 1, // ...but this ratio allows truly adjusting the sizes - font: "Droid Sans", - // font: "Crete Round", - // font: "Ubuntu Condensed", - fontStyle: "bold", -}; -var sigmaJsGraphProperties = { - minEdgeSize: 3, - // maxEdgeSize: 10 -}; -var sigmaJsMouseProperties = { - minRatio: .03125, // 1/32 pour permettre zoom x32 - maxRatio: 2 -}; -// ============ < / SIGMA.JS PROPERTIES > ============ + labelThreshold: 5, // <- replaces deprecated showLabelsIfZoom + defaultEdgeType: 'curve', // 'curve' or 'line' + defaultBorderView: "always", + // new sigma.js only for hover + new settingnames + defaultHoverLabelBGColor: '#fff', + defaultHoverLabelColor: '#000', + borderSize: 2.5, // (only for hovered nodes) + defaultNodeBorderColor: "black", + nodeBorderColor: "default", // vs. node -// ============ < VARIABLES.JS > ============ + // for custom TW node renderer with borders + twNodeRendBorderSize: 1, // (for all normal nodes, iff TWConf.nodeRendBorder) + twNodeRendBorderColor: "#222", + // twNodeRendBorderColor: "#eee", -var twjs="tinawebJS/"; + font: "Droid Sans", + // font: "Crete Round", + // font: "Ubuntu Condensed", + fontStyle: "bold", + }; -// possible node types and their inverted map -TW.categories = []; -TW.catDict = {}; + TWConf.sigmaJsGraphProperties = { + minEdgeSize: 3, + // maxEdgeSize: 10 + }; -var gexfFile; -//var zoom=0; + TWConf.sigmaJsMouseProperties = { + minRatio: .03125, // 1/32 pour permettre zoom x32 + maxRatio: 2 + }; -var checkBox=false; -var manuallyChecked = false; -var overNodes=false; -var shift_key=false; -var NOW="A"; -var PAST="--"; + // ======================= + // TINA RENDERING SETTINGS + // ======================= + TWConf.overSampling = false // costly hi-def rendering (true => pixelRatio x 2) -var swclickActual=""; -var swclickPrev=""; -var swMacro=true; + TWConf.sizeMult = []; -var socsemFlag=false; -var constantNGramFilter; + 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; -var lastFilter = [] - lastFilter["#slidercat0nodesweight"] = {"orig":"-" , "last":"-"} - lastFilter["#slidercat1nodesweight"] = {"orig":"-" , "last":"-"} - lastFilter["#slidercat0edgesweight"] = {"orig":"-" , "last":"-"} - lastFilter["#slidercat1edgesweight"] = {"orig":"-" , "last":"-"} + // ======== + // A RANGER £TODO + // ======== -// -// var overviewWidth = 200; -// var overviewHeight = 175; -// var overviewScale = 0.25; -// var overviewHover=false; -var moveDelay = 80, zoomDelay = 2; -//var Vecindad; -TW.partialGraph; -var otherGraph; -TW.Nodes = []; -TW.Edges = []; -TW.Clusters = []; + TWConf.nodeClusAtt = "modularity_class" -// new dev properties -TW.scanClusters = true // build TW.Clusters in an (attr+val => nodes) reverse index (aka facets) -TW.maxDiscreteValues = 40 // max discrete levels in facet legend (aka bins) -// new TW.Clusters structure -// -------------------------- -// was: built in separate loop from read of all attr values -// TW.Clusters[nodeType][clusterType][possibleValue] = clst_idx_of_possible_value + TWConf.filterSliders = true -// from now on: -// still built in ChangeGraphAppearanceByAtt -// POSS: build in parseCustom (when reading all nodes attributes anyway) -// if discrete attrvalues with <= 30 classes (colorsBy, clustersBy) -// => TW.Clusters[nodeType][clusterType].classes.[possibleValue] = list of ids with the value -// if continuous or many possible values (>30) (clustersBy, colorsRelByBins) -// => TW.Clusters[nodeType][clusterType].ranges.[interval] = list of ids in the interval + 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 -TW.fa2milliseconds=5000; // for initial auto-run if fa2enabled and any - // subsequent auto-runs if graph modified + console.warn("current conf:", TWConf) + return TWConf +})() -var nodeslength=0; -var labels = []; +// INITIALIZED VARS (£TODO move to main or Tina) +// ================ +TW.Nodes = []; +TW.Edges = []; +TW.Relations = {} +TW.Clusters = []; // A "by value" index, built in parseCustom, (aka facets) -var numberOfDocs=0; // not used! -var numberOfNGrams=0; // not used, but cf TW.nNodes TW.nEdges +TW.gexfPaths={}; +TW.labels=[]; // FIXME should become TW.* var selections = []; @@ -259,44 +210,50 @@ var nodes2 = {}; var bipartiteD2N = {}; var bipartiteN2D = {}; -var flag=0; -var firstime=0; -var leftright=true; -var edgesTF=false; - -//This variables will be updated in sigma.parseCustom.js -var minNodeSize=10000000; -var maxNodeSize=0; -// var minEdgeWeight=5.0; // in fact never used... -// var maxEdgeWeight=0.0; -//--------------------------------------------------- - -var bipartite=false; - -var colorList = ["#000000", "#FFFF00", "#1CE6FF", "#FF34FF", "#FF4A46", "#008941", "#006FA6", "#A30059", "#FFDBE5", "#7A4900", "#0000A6", "#63FFAC", "#B79762", "#004D43", "#8FB0FF", "#997D87", "#5A0007", "#809693", "#FEFFE6", "#1B4400", "#4FC601", "#3B5DFF", "#4A3B53", "#FF2F80", "#61615A", "#BA0900", "#6B7900", "#00C2A0", "#FFAA92", "#FF90C9", "#B903AA", "#D16100", "#DDEFFF", "#000035", "#7B4F4B", "#A1C299", "#300018", "#0AA6D8", "#013349", "#00846F", "#372101", "#FFB500", "#C2FFED", "#A079BF", "#CC0744", "#C0B9B2", "#C2FF99", "#001E09", "#00489C", "#6F0062", "#0CBD66", "#EEC3FF", "#456D75", "#B77B68", "#7A87A1", "#788D66", "#885578", "#FAD09F", "#FF8A9A", "#D157A0", "#BEC459", "#456648", "#0086ED", "#886F4C","#34362D", "#B4A8BD", "#00A6AA", "#452C2C", "#636375", "#A3C8C9", "#FF913F", "#938A81", "#575329", "#00FECF", "#B05B6F", "#8CD0FF", "#3B9700", "#04F757", "#C8A1A1", "#1E6E00", "#7900D7", "#A77500", "#6367A9", "#A05837", "#6B002C", "#772600", "#D790FF", "#9B9700", "#549E79", "#FFF69F", "#201625", "#72418F", "#BC23FF", "#99ADC0", "#3A2465", "#922329", "#5B4534", "#FDE8DC", "#404E55", "#0089A3", "#CB7E98", "#A4E804", "#324E72", "#6A3A4C", "#83AB58", "#001C1E", "#D1F7CE", "#004B28", "#C8D0F6", "#A3A489", "#806C66", "#222800", "#BF5650", "#E83000", "#66796D", "#DA007C", "#FF1A59", "#8ADBB4", "#1E0200", "#5B4E51", "#C895C5", "#320033", "#FF6832", "#66E1D3", "#CFCDAC", "#D0AC94", "#7ED379", "#012C58"]; - -var RVUniformC = function(seed){ - this.a=16807; - this.b=0; - this.m=2147483647; - this.u; - this.seed=seed; - this.x = this.seed; - // this.generar = function(n){ - // uniforme = []; - // x = 0.0; - // x = this.seed; - // for(i = 1; i < n ; i++){ - // x = ((x*this.a)+this.b)%this.m; - // uniforme[i] = x/this.m; - // } - // return uniforme; - // }; - this.getRandom = function(){ - x = ((this.x*this.a)+this.b)%this.m; - this.x = x; - this.u = this.x/this.m; - return this.u; - }; -} -//unifCont = new RVUniformC(100000000) +// possible node types and their inverted map +TW.categories = []; +TW.catDict = {}; + + +TW.nodeslength = 0 // <=== £TODO harmonize use with TW.partialGraph.graph.nNodes() + +var gexfFile; +//var zoom=0; +TW.checkBox=false; +TW.shiftKey=false; +TW.manuallyChecked = false; + +TW.SystemState = {} +TW.SystemState.level = true; +TW.SystemState.type = [ true, false ] // usually overridden by initialActivetypes +TW.SystemState.selections = []; +TW.SystemState.opposites = []; + + +TW.colorList = ["#000000", "#FFFF00", "#1CE6FF", "#FF34FF", "#FF4A46", "#008941", "#006FA6", "#A30059", "#FFDBE5", "#7A4900", "#0000A6", "#63FFAC", "#B79762", "#004D43", "#8FB0FF", "#997D87", "#5A0007", "#809693", "#FEFFE6", "#1B4400", "#4FC601", "#3B5DFF", "#4A3B53", "#FF2F80", "#61615A", "#BA0900", "#6B7900", "#00C2A0", "#FFAA92", "#FF90C9", "#B903AA", "#D16100", "#DDEFFF", "#000035", "#7B4F4B", "#A1C299", "#300018", "#0AA6D8", "#013349", "#00846F", "#372101", "#FFB500", "#C2FFED", "#A079BF", "#CC0744", "#C0B9B2", "#C2FF99", "#001E09", "#00489C", "#6F0062", "#0CBD66", "#EEC3FF", "#456D75", "#B77B68", "#7A87A1", "#788D66", "#885578", "#FAD09F", "#FF8A9A", "#D157A0", "#BEC459", "#456648", "#0086ED", "#886F4C","#34362D", "#B4A8BD", "#00A6AA", "#452C2C", "#636375", "#A3C8C9", "#FF913F", "#938A81", "#575329", "#00FECF", "#B05B6F", "#8CD0FF", "#3B9700", "#04F757", "#C8A1A1", "#1E6E00", "#7900D7", "#A77500", "#6367A9", "#A05837", "#6B002C", "#772600", "#D790FF", "#9B9700", "#549E79", "#FFF69F", "#201625", "#72418F", "#BC23FF", "#99ADC0", "#3A2465", "#922329", "#5B4534", "#FDE8DC", "#404E55", "#0089A3", "#CB7E98", "#A4E804", "#324E72", "#6A3A4C", "#83AB58", "#001C1E", "#D1F7CE", "#004B28", "#C8D0F6", "#A3A489", "#806C66", "#222800", "#BF5650", "#E83000", "#66796D", "#DA007C", "#FF1A59", "#8ADBB4", "#1E0200", "#5B4E51", "#C895C5", "#320033", "#FF6832", "#66E1D3", "#CFCDAC", "#D0AC94", "#7ED379", "#012C58"]; + + + +var ParseCustom = function () {}; +var SigmaUtils = function () {}; +var TinaWebJS = function () {}; + + + +// £TODO à ranger +TW.circleSize= 0; +var lastFilter = [] + lastFilter["#slidercat0nodesweight"] = {"orig":"-" , "last":"-"} + lastFilter["#slidercat1nodesweight"] = {"orig":"-" , "last":"-"} + lastFilter["#slidercat0edgesweight"] = {"orig":"-" , "last":"-"} + lastFilter["#slidercat1edgesweight"] = {"orig":"-" , "last":"-"} + +var desirableTagCloudFont_MIN=12; +var desirableTagCloudFont_MAX=20; +var desirableNodeSizeMIN=1; +var desirableNodeSizeMAX=12; + + +// These variables will be updated in sigma.parseCustom.js +var minNodeSize +var maxNodeSize diff --git a/tinawebJS/Tinaweb.js b/tinawebJS/Tinaweb.js index ea51e126ef61daa4398b155c5faee0e4993bcdbb..d07a87e8671cc901cd64b32826be797b8cb775bc 100644 --- a/tinawebJS/Tinaweb.js +++ b/tinawebJS/Tinaweb.js @@ -83,7 +83,7 @@ function SelectionEngine() { coincd.push(results[i].id) } var targeted = this.SelectorEngine( { - addvalue:checkBox, + addvalue:TW.checkBox, prevsels:selections, currsels:coincd } ) @@ -147,7 +147,7 @@ function SelectionEngine() { // ==================== this.MultipleSelection2 = (function(nodes,nodesDict,edgesDict) { - if (TW.debugFlags.selections) { + if (TW.conf.debug.selections) { var tMS2_deb = performance.now() console.log("IN SelectionEngine.MultipleSelection2:") @@ -273,9 +273,6 @@ function SelectionEngine() { // alert("MultipleSelection2=======\nthe_new_sels:" + JSON.stringify(the_new_sels)) - // global flag - TW.selectionActive = true - // we send our "gotNodeSet" event // (signal for plugins that a search-selection was done or a new hand picked selection) $('#searchinput').trigger({ @@ -315,19 +312,20 @@ function SelectionEngine() { return b-a }); - if (TW.debugFlags.selections) { + if (TW.conf.debug.selections) { console.debug('selections', selections) console.debug('oppos', oppos) console.debug('same', same) } - overNodes=true; + // global flag + TW.selectionActive = true TW.partialGraph.render(); updateRelatedNodesPanel( selections , same, oppos ); - if (TW.debugFlags.selections) { + if (TW.conf.debug.selections) { var tMS2_fin = performance.now() console.log("end MultipleSelection2, own time:", tMS2_fin-tMS2_deb) } @@ -344,7 +342,7 @@ TinaWebJS = function ( sigmacanvas ) { // functions that modify the sigma module (not sigma instance!) this.init = function () { - if (TW.debugFlags.logSettings) console.info("TW settings", TW) + if (TW.conf.debug.logSettings) console.info("TW settings", TW) let initErrMsg = null @@ -354,12 +352,12 @@ TinaWebJS = function ( sigmacanvas ) { else { this.prepareSigmaCustomIndices(sigma) - if (TW.ourRendering) + if (TW.conf.ourRendering) this.prepareSigmaCustomRenderers(sigma) } // overriding pixelRatio is possible if we need high definition - if (TW.overSampling) { + if (TW.conf.overSampling) { var realRatio = sigma.utils.getPixelRatio sigma.utils.getPixelRatio = function() { return 2 * realRatio() @@ -476,7 +474,7 @@ TinaWebJS = function ( sigmacanvas ) { // - additionnaly supports 'active/forcelabel' node property (magnify x 3) sigmaModule.canvas.hovers.def = tempo.twRender.canvas.hovers.largerall - if (TW.debugFlags.logSettings) console.log('tw renderers registered in sigma module') + if (TW.conf.debug.logSettings) console.log('tw renderers registered in sigma module') } this.initSearchListeners = function () { @@ -487,10 +485,10 @@ TinaWebJS = function ( sigmacanvas ) { source: function(request, response) { // labels initialized in settings, filled in updateSearchLabels // console.log(labels.length) - matches = []; + var matches = []; var matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i"); // grep at heart - var results = $.grep(labels, function(e) { + var results = $.grep(TW.labels, function(e) { return matcher.test(e.label); //|| matcher.test(e.desc); }); @@ -499,11 +497,11 @@ TinaWebJS = function ( sigmacanvas ) { } else { $("#noresults").empty(); } - matches = results.slice(0, maxSearchResults); + matches = results.slice(0, TW.conf.minLengthAutoComplete); response(matches); }, - minLength: minLengthAutoComplete, + minLength: TW.conf.minLengthAutoComplete, // ----------------------->8--------------------- @@ -574,7 +572,7 @@ TinaWebJS = function ( sigmacanvas ) { setTimeout( function (){ targeted = SelInst.SelectorEngine( { - addvalue:checkBox, + addvalue:TW.checkBox, clicktype:"double", prevsels:selections, currsels:coincidences @@ -582,12 +580,12 @@ TinaWebJS = function ( sigmacanvas ) { // tricky stuff for simulating a multiple selection D: // ... to be improved in the future ... - var prev_cursor_size = cursor_size; + var prev_cursor_size = TW.circleSize; if(targeted.length>0) { - cursor_size = (cursor_size==0)? 1 : cursor_size; + TW.circleSize = (TW.circleSize==0)? 1 : TW.circleSize; cancelSelection(false); SelInst.MultipleSelection2({nodes:targeted}); - cursor_size = prev_cursor_size; + TW.circleSize = prev_cursor_size; } $("input#searchinput").val(""); @@ -621,7 +619,7 @@ TinaWebJS = function ( sigmacanvas ) { setTimeout( function() { targeted = SelInst.SelectorEngine( { - addvalue:checkBox, + addvalue:TW.checkBox, clicktype:"double", prevsels:selections, currsels:[exfnd.id] @@ -739,7 +737,7 @@ TinaWebJS = function ( sigmacanvas ) { TW.partialGraph.camera.goTo({x:0, y:0, ratio:1.2}) }); - if (TW.fa2Available) { + if (TW.conf.fa2Available) { $("#layoutButton").click(function () { sigma_utils.smartForceAtlas() }); @@ -748,7 +746,7 @@ TinaWebJS = function ( sigmacanvas ) { $("#layoutButton").hide() } - if (TW.disperseAvailable) { + if (TW.conf.disperseAvailable) { $("#noverlapButton").click(function () { if(! TW.partialGraph.isNoverlapRunning()) { // show waiting cursor on page and button @@ -785,12 +783,12 @@ TinaWebJS = function ( sigmacanvas ) { //Cursor Size slider var cursorSlider = $("#unranged-value").freshslider({ step: 1, - min:cursor_size_min, - max:cursor_size_max, - value:cursor_size, + min:TW.conf.circleSizeMin, + max:TW.conf.circleSizeMax, + value:TW.circleSize, onchange:function(value){ // console.log("en cursorsize: "+value); - cursor_size=value; + TW.circleSize=value; } }); @@ -813,11 +811,11 @@ TinaWebJS = function ( sigmacanvas ) { // console.log("init: slider resize") // for(j=0 ; j<TW.partialGraph.nNodes ; j++){ // if (nds[j] - // && nds[j].type == TW.catSem) { + // && nds[j].type == TW.conf.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; + // sizeMult[TW.conf.catSem] = parseFloat(value-1)*0.3; // } // } // partialGraph.render() @@ -851,10 +849,10 @@ TinaWebJS = function ( sigmacanvas ) { // 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 + TW.checkBox = TW.manuallyChecked || e.shiftKey - // show it in the real checkbox too - $('#checkboxdiv').prop("checked", manuallyChecked || e.shiftKey) + // show it in the real TW.checkBox too + $('#checkboxdiv').prop("checked", TW.manuallyChecked || e.shiftKey) } ); } // finish envListeners @@ -873,7 +871,7 @@ TinaWebJS = function ( sigmacanvas ) { // cases: // 'click' - simple click, early event - // used for area (with global: cursor_size) + // used for area (with global: TW.circleSize) // 'clickNode'- simple click, second event if one node // POSS easy in new sigma.js: @@ -887,7 +885,7 @@ TinaWebJS = function ( sigmacanvas ) { // console.log("sigma click event e", e) // case with a selector circle cursor handled here - if (cursor_size > 0) { + if (TW.circleSize > 0) { // actual click position, but in graph coords var x = e.data.x var y = e.data.y @@ -895,19 +893,19 @@ TinaWebJS = function ( sigmacanvas ) { // convert var camCoords = TW.cam.cameraPosition(x,y) - // retrieve area nodes, using indexed quadtree and global cursor_size + // retrieve area nodes, using indexed quadtree and global TW.circleSize var circleNodes = circleGetAreaNodes( camCoords.x, camCoords.y ) - // 1) clear previous while keeping its list (useful iff 'Add' checkBox) + // 1) clear previous while keeping its list (useful iff 'Add' TW.checkBox) var previousSelection = selections cancelSelection(false) // 2) show selection + do all related effects var targeted = SelInst.SelectorEngine( { - addvalue:checkBox, + addvalue:TW.checkBox, currsels:circleNodes, prevsels:previousSelection } ) @@ -929,9 +927,9 @@ TinaWebJS = function ( sigmacanvas ) { var previousSelection = selections cancelSelection(false, {norender:true}); // no need to render before MS2 - if (cursor_size == 0) { + if (TW.circleSize == 0) { var targeted = SelInst.SelectorEngine( { - addvalue:checkBox, + addvalue:TW.checkBox, currsels:[theNodeId], prevsels:previousSelection } ) @@ -945,13 +943,13 @@ TinaWebJS = function ( sigmacanvas ) { // when click in the empty background // ================================== - if (TW.deselectOnclickStage) { + if (TW.conf.deselectOnclickStage) { partialGraph.bind('clickStage', function(e) { // console.log("clickStage event e", e) if (! e.data.captor.isDragging && Object.keys(selections).length - && ! cursor_size) { + && ! TW.circleSize) { // we clear selections and all its effects cancelSelection(false); @@ -982,7 +980,7 @@ TinaWebJS = function ( sigmacanvas ) { .mousemove(function(e){ if(!isUndef(partialGraph)) { // show/move selector circle cursor - if(cursor_size>0) circleTrackMouse(e); + if(TW.circleSize>0) circleTrackMouse(e); } }) @@ -998,8 +996,8 @@ TinaWebJS = function ( sigmacanvas ) { // new sigma.js current zoom ratio value: partialGraph.camera.ratio, - min: 1 / sigmaJsMouseProperties.maxRatio, // ex x.5 - max: 1 / sigmaJsMouseProperties.minRatio, // ex x32 + min: 1 / TW.conf.sigmaJsMouseProperties.maxRatio, // ex x.5 + max: 1 / TW.conf.sigmaJsMouseProperties.minRatio, // ex x32 // range: true, step: .2, value: 1, @@ -1013,7 +1011,7 @@ TinaWebJS = function ( sigmacanvas ) { $("#zoomPlusButton").click(function () { var newRatio = TW.cam.ratio * .75 - if (newRatio >= sigmaJsMouseProperties.minRatio) { + if (newRatio >= TW.conf.sigmaJsMouseProperties.minRatio) { // triggers coordinatesUpdated which sets the slider cursor partialGraph.camera.goTo({ratio: newRatio}); return false; @@ -1022,14 +1020,14 @@ TinaWebJS = function ( sigmacanvas ) { $("#zoomMinusButton").click(function () { var newRatio = TW.cam.ratio * 1.25 - if (newRatio <= sigmaJsMouseProperties.maxRatio) { + if (newRatio <= TW.conf.sigmaJsMouseProperties.maxRatio) { // triggers coordinatesUpdated which sets the slider cursor partialGraph.camera.goTo({ratio: newRatio}); return false; } }); - if (TW.filterSliders) { + if (TW.conf.filterSliders) { // args: for display: target div , // for context: family/type prop value, @@ -1051,7 +1049,7 @@ TinaWebJS = function ( sigmacanvas ) { step:.25, min:0, max:5, - value: sigmaJsDrawingProperties['labelSizeRatio'] || 1, + value: TW.conf.sigmaJsDrawingProperties['labelSizeRatio'] || 1, bgcolor:"#27c470", onchange:function(value){ if (labelSizeTimeout) { @@ -1097,7 +1095,7 @@ TinaWebJS = function ( sigmacanvas ) { } this.allPossibleActivetypes = function (cats) { - if (TW.debugFlags.logSettings) console.debug(`allPossibleActivetypes(cats=${cats})`) + if (TW.conf.debug.logSettings) console.debug(`allPossibleActivetypes(cats=${cats})`) var possibleActivetypes = {} var N=Math.pow(2 , cats.length); diff --git a/tinawebJS/enviroment.js b/tinawebJS/enviroment.js index 73ad51b83294f719704d93fa30a9b5bd8005a79a..462067a59bee28b90cb10e86b84490c4c14097b6 100755 --- a/tinawebJS/enviroment.js +++ b/tinawebJS/enviroment.js @@ -907,7 +907,7 @@ function showDisabledSlider(someDivId) { //============================= < SEARCH > =============================// function updateSearchLabels(id,name,type){ - labels.push({ + TW.labels.push({ 'id' : id, 'label' : name, 'desc': type diff --git a/tinawebJS/main.js b/tinawebJS/main.js index fbdc000f0aee76c382aba8f7a16990a389b3f248..95d2ada5a5e254bdfdef3b5e26cd29e093024174 100644 --- a/tinawebJS/main.js +++ b/tinawebJS/main.js @@ -37,7 +37,7 @@ var AjaxSync = (function(TYPE, URL, DATA, DT) { else DT = 'text' // ie "if not json then raw xml string" - if (TW.debugFlags.logFetchers) + if (TW.conf.debug.logFetchers) console.log("---AjaxSync---\n", TYPE, URL, DATA, DT, "\n--------------") $.ajax({ @@ -60,7 +60,7 @@ var AjaxSync = (function(TYPE, URL, DATA, DT) { format = "gexf" ; } else { - if (TW.debugFlags.logFetchers) + if (TW.conf.debug.logFetchers) console.debug("after AjaxSync("+URL+") => response header="+header +"not xml => fallback on json"); format = "json" ; } @@ -101,7 +101,7 @@ TW.instance.initGUIListeners(); TW.instance.initSearchListeners(); // show the custom name of the app -writeBrand(TW.branding) +writeBrand(TW.conf.branding) console.log("Starting TWJS") @@ -140,9 +140,15 @@ function syncRemoteGraphData () { var inFormat; // = { db|api.json , somefile.json|gexf } var inData; // = {nodes: [....], edges: [....], cats:...} - // case (1) read from DB => one ajax to api eg /services/api/graph?q=filters... - if (getUrlParam.type) { - console.warn("input case: @type [future: @sourcemode=api], using TW.bridge") + var mapLabel; // user displayed label for this input dataset + + // type of input + let sourcemode = isUndef(getUrlParam.sourcemode)?TW.sourcemode:getUrlParam.sourcemode + + // case (1) read from remote DB via API bridge fetching + // ex: /services/api/graph?q=filters... + if (sourcemode == "api") { + console.log("input case: api, using TW.conf.sourceAPI") // the only API format, cf. inData inFormat = 'json' @@ -151,29 +157,29 @@ function syncRemoteGraphData () { var sourceinfo = getUrlParam.nodeidparam var qtype = getUrlParam.type if(isUndef(sourceinfo) || isUndef(qtype)) { - console.warn("missing nodes filter/id param"); + console.warn("missing nodes filter/id param to transmit to source api"); } else { console.log("Received query of type:", qtype) if(qtype == "filter" || qtype == "uid"){ - var theurl,thedata,thename; + var theurl,thedata // console.warn("===> PASSING ON QUERY (type "+qtype+") TO BRIDGE <===") - if(qtype=="uid") { + if (qtype=="uid") { // pr("bring the noise, case: unique_id"); // pr(getClientTime()+" : DataExt Ini"); // < === DATA EXTRACTION === > - theurl = TW.bridge["forNormalQuery"] + theurl = TW.conf.sourceAPI["forNormalQuery"] // NB before also passed it for Fa2 iterations (useless?) thedata = "qtype=uid&unique_id="+sourceinfo; - thename = "unique scholar"; + mapLabel = "unique scholar"; } if (qtype=="filter") { // pr("bring the noise, case: multipleQuery"); // pr(getClientTime()+" : DataExt Ini"); - theurl = TW.bridge["forFilteredQuery"]; + theurl = TW.conf.sourceAPI["forFilteredQuery"]; // json is twice URI encoded by whoswho to avoid both '"' and '%22' var json_constraints = decodeURIComponent(sourceinfo) @@ -191,7 +197,7 @@ function syncRemoteGraphData () { // => thedata (for comexAPI): // keywords[]="complex systems"&keywords[]="something"&countries="France"&countries[]="USA" - // => thename (for user display): + // => mapLabel (for user display): // ("complex systems" or "something") and ("France" or "USA") // console.log("decoded filtering query", filteringKeyArrayPairs) @@ -212,16 +218,16 @@ function syncRemoteGraphData () { if (restParams.length) { thedata = "qtype=filters&" + restParams.join("&") - thename = nameElts.join(" and ") + mapLabel = nameElts.join(" and ") } else { thedata = "qtype=filters&query=*" - thename = "(ENTIRE NETWORK)" + mapLabel = "(ENTIRE NETWORK)" } } // Assigning name for the network - if (! thename) { + if (! mapLabel) { elements = [] queryarray = JSON.parse(ourGetUrlParam.nodeidparam) for(var i in queryarray) { @@ -230,52 +236,47 @@ function syncRemoteGraphData () { for(var j in item) elements.push(item[j]) } } - thename = '"'+elements.join('" , "')+'"'; + mapLabel = '"'+elements.join('" , "')+'"'; } - // £TODO restore thename - console.warn (thename, ":name not used anymore since refacto 10/05 could put on same level as inData as inMapname or smth") - var bridgeRes = AjaxSync({ URL: theurl, DATA:thedata, TYPE:'GET', DT:'json' }) // should be a js object with 'nodes' and 'edges' properties inData = bridgeRes.data - if (TW.debugFlags.logFetchers) console.info("JSON input data", inData) + if (TW.conf.debug.logFetchers) console.info("JSON input data", inData) } else { console.warn ("=> unsupported query type !") } } } - // case (2) gexf => in params or in preRes db.json, then 2nd ajax for a gexf file => covered here - // sourcemode == "serverfile" + + + // cases (2) and (3) : we'll read a file from server + // sourcemode == "serverfile" or "servermenu" (several files with <select>) else { - console.warn("input case: @mode=db.json or @file=... [future: @sourcemode=serverfile], using server's gexf(s)") - // subcases: - // -> gexf file path is already specified in TW.mainfile - // -> gexf file path is in the urlparam @file - // -> @mode is db.json, files are listed in db.json file - // --> if @file also in url, choose the db.json one matching - // --> otherwise, choose the "first_file" from db.json list + console.log("input case: server-side file, using db.json 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 + // --> otherwise, choose the "first_file" from db.json list + + // -> @mode is serverfile + // -> gexf file path is in the urlparam @file + // -> gexf file path is already specified in TW.conf.sourceFile // =================== var the_file = ""; // =================== - - // TODO check if legacy apps use db.json name otherwise use mode == 'menufile' - // and 'db.json' should be hardcoded (safer) - // if (!isUndef(getUrlParam.mode) && getUrlParam.mode == 'menufile') { - - - // menufile case comes before single file because it uses urlparam file too - if (!isUndef(getUrlParam.mode) && getUrlParam.mode == 'db.json') { + // 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") // 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" - if (TW.debugFlags.logFetchers) console.info(`attempting to load infofile ${infofile}`) + if (TW.conf.debug.logFetchers) console.info(`attempting to load infofile ${infofile}`) var preRES = AjaxSync({ URL: infofile, DT:"json" }); if (preRES['OK'] && preRES.data) { @@ -297,29 +298,30 @@ function syncRemoteGraphData () { if( isUndef(getUrlParam.file) ) { the_file = first_path+"/"+first_file } else { + // £POSS; match on the full paths from db.json the_file = first_path+"/"+getUrlParam.file } var files_selector = '<select onchange="jsActionOnGexfSelector(this.value);">' for( var path in preRES.data ) { - var the_gexfs = preRES.data[path]["gexfs"] - for(var aGexf in the_gexfs) { + var theGexfs = preRES.data[path]["gexfs"] + for(var aGexf in theGexfs) { var gexfBasename = aGexf.replace(/\.gexf$/, "") // more human-readable in the menu TW.gexfPaths[gexfBasename] = path+"/"+aGexf // ex : "RiskV2PageRank1000.gexf":data/AXA/RiskV2PageRank1000.gexf // (we assume there's no duplicate basenames) - if (TW.debugFlags.logFetchers) - console.log("\t\t\t"+gexfBasename+ " -> table:" +the_gexfs[aGexf]["semantic"]["table"] ) + if (TW.conf.debug.logFetchers) + console.log("\t\t\t"+gexfBasename+ " -> table:" +theGexfs[aGexf]["semantic"]["table"] ) // -------------------------->8------------------------------------------ // £TODO this part is underspecified // if used in some usecases, port it to nodetypes // otherwise remove - // TW.field[path+"/"+aGexf] = the_gexfs[aGexf]["semantic"]["table"] + // TW.field[path+"/"+aGexf] = theGexfs[aGexf]["semantic"]["table"] // ex : data/AXA/RiskV2PageRank5000.gexf:"ISItermsAxa_2015" // -------------------------->8------------------------------------------ @@ -334,24 +336,25 @@ function syncRemoteGraphData () { console.log("files_selector HTML", files_selector) $("#network").html(files_selector) } + // direct urlparam file case else if( !isUndef(getUrlParam.file) ) { the_file = getUrlParam.file } // direct file fallback case: specified file in settings_explorer - else if (TW.mainfile && linkCheck(TW.mainfile)) { + else if (TW.conf.sourceFile && linkCheck(TW.conf.sourceFile)) { console.log("no @file arg: trying TW.mainfile from settings") - the_file = TW.mainfile; + the_file = TW.conf.sourceFile; } else { - console.warn("No specified input!") + console.error(`No specified input and neither db.json nor TW.conf.sourceFile ${TW.conf.sourceFile} are present`) } var finalRes = AjaxSync({ URL: the_file }); inData = finalRes["data"] inFormat = finalRes["format"] - if (TW.debugFlags.logFetchers) { + if (TW.conf.debug.logFetchers) { console.warn('@the_file', finalRes["OK"], the_file) console.log(' fetch result: format', inFormat) console.log(' fetch result: typeof data', typeof inData) @@ -359,7 +362,7 @@ function syncRemoteGraphData () { } } - return [inFormat, inData] + return [inFormat, inData, mapLabel] } @@ -380,13 +383,13 @@ function mainStartGraph(inFormat, inData, twInstance) { } else { - if (TW.debugFlags.logParsers) console.log("parsing the data") + if (TW.conf.debug.logParsers) console.log("parsing the data") let start = new ParseCustom( inFormat , inData ); let catsInfos = start.scanFile(); TW.categories = catsInfos.categories - if (TW.debugFlags.logParsers){ + if (TW.conf.debug.logParsers){ console.log(`Source scan found ${TW.categories.length} node categories: ${TW.categories}`) } @@ -407,7 +410,7 @@ function mainStartGraph(inFormat, inData, twInstance) { // XML parsing from ParseCustom var dicts = start.makeDicts(TW.categories); // > parse json or gexf, dictfy - if (TW.debugFlags.logParsers) console.info("parsing result:", dicts) + if (TW.conf.debug.logParsers) console.info("parsing result:", dicts) TW.Nodes = dicts.nodes; TW.Edges = dicts.edges; @@ -436,21 +439,21 @@ function mainStartGraph(inFormat, inData, twInstance) { // so we just need to handle mismatches here (when user-suggested cats were absent) if (TW.categories.length == 2) { console.info("== 'bipartite' case ==") - if (TW.catSoc != TW.categories[0]) { - console.warn(`Observed social category "${TW.categories[0]}" overwrites user-suggested TW.catSoc ("${TW.catSoc}")`) - TW.catSoc = TW.categories[0] + if (TW.conf.catSoc != TW.categories[0]) { + console.warn(`Observed social category "${TW.categories[0]}" overwrites user-suggested TW.conf.catSoc ("${TW.conf.catSoc}")`) + TW.conf.catSoc = TW.categories[0] } - if (TW.catSem != TW.categories[1]) { - console.warn(`Observed semantic category "${TW.categories[1]}" overwrites user-suggested TW.catSem "(${TW.catSem})"`) - TW.catSem = TW.categories[1] + if (TW.conf.catSem != TW.categories[1]) { + console.warn(`Observed semantic category "${TW.categories[1]}" overwrites user-suggested TW.conf.catSem "(${TW.conf.catSem})"`) + TW.conf.catSem = TW.categories[1] } } else if (TW.categories.length == 1) { console.info("== monopartite case ==") // FIXME it would be more coherent with all tina usecases (like gargantext or tweetoscope) for the default category to be catSem instead of Soc - if (TW.catSoc != TW.categories[0]) { - console.warn(`Observed unique category "${TW.categories[0]}" overwrites user-suggested TW.catSoc ("${TW.catSoc}")`) - TW.catSoc = TW.categories[0] + if (TW.conf.catSoc != TW.categories[0]) { + console.warn(`Observed unique category "${TW.categories[0]}" overwrites user-suggested TW.conf.catSoc ("${TW.conf.catSoc}")`) + TW.conf.catSoc = TW.categories[0] } } else { @@ -528,13 +531,13 @@ function mainStartGraph(inFormat, inData, twInstance) { }, // 2) settings_explorer values - sigmaJsDrawingProperties, - sigmaJsGraphProperties, - sigmaJsMouseProperties + TW.conf.sigmaJsDrawingProperties, + TW.conf.sigmaJsGraphProperties, + TW.conf.sigmaJsMouseProperties ) - if (TW.debugFlags.logSettings) console.info("sigma settings", TW.customSettings) + if (TW.conf.debug.logSettings) console.info("sigma settings", TW.customSettings) // ================================================================== @@ -629,7 +632,7 @@ function mainStartGraph(inFormat, inData, twInstance) { LevelButtonDisable(true) // recreate sliders after activetype or level changes - if (TW.filterSliders + if (TW.conf.filterSliders && (present.level != past.level || present.activetypes.map(Number).join("|") != past.activetypes.map(Number).join("|"))) { @@ -666,7 +669,7 @@ function mainStartGraph(inFormat, inData, twInstance) { }; - if (!TW.filterSliders) { + if (!TW.conf.filterSliders) { var filterEls = document.getElementsByClassName('weight-selectors') @@ -698,7 +701,7 @@ function mainStartGraph(inFormat, inData, twInstance) { outboundAttractionDistribution: false } - if (TW.debugFlags.logSettings) console.info("FA2 settings", TW.FA2Params) + if (TW.conf.debug.logSettings) console.info("FA2 settings", TW.FA2Params) // init FA2 for any future forceAtlas2 calls TW.partialGraph.configForceAtlas2(TW.FA2Params) @@ -720,9 +723,9 @@ 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.fa2enabled = true + if (inFormat == "json") TW.conf.fa2Enabled = true - // will run fa2 if enough nodes and TW.fa2enabled == true + // will run fa2 if enough nodes and TW.conf.fa2Enabled == true sigma_utils.smartForceAtlas() @@ -731,7 +734,7 @@ function mainStartGraph(inFormat, inData, twInstance) { $("#changetype").hide(); $("#taboppos").hide(); - // if (TW.catSem && TW.catSoc) { + // if (TW.conf.catSem && TW.conf.catSoc) { setTimeout(function () { // tabneigh: show "Related" tab document.querySelector('.etabs a[href="#tabs2"]').click() diff --git a/tinawebJS/methods.js b/tinawebJS/methods.js index 20230c6a3d033b692a57d1f74ef0971484a9a4ac..7f35248544ca6f85a49fd87b754d219413fca2f0 100755 --- a/tinawebJS/methods.js +++ b/tinawebJS/methods.js @@ -3,7 +3,7 @@ // settings: {norender: Bool} function cancelSelection (fromTagCloud, settings) { - if (TW.debugFlags.selections) { console.log("\t***in cancelSelection"); } + if (TW.conf.debug.selections) { console.log("\t***in cancelSelection"); } if (!settings) settings = {} highlightSelectedNodes(false); //Unselect the selected ones :D @@ -14,8 +14,9 @@ function cancelSelection (fromTagCloud, settings) { TW.partialGraph.states.slice(-1)[0].selections=[] - //Nodes colors go back to normal - overNodes=false; + // global flag + TW.selectionActive = false + //Edges colors go back to normal if (TW.partialGraph.settings('drawEdges')) { @@ -77,9 +78,6 @@ function cancelSelection (fromTagCloud, settings) { if(TW.partialGraph.states.slice(-1)[0].level) LevelButtonDisable(true); - // global flag - TW.selectionActive = false - if (!settings.norender) { // finally redraw TW.partialGraph.render(); @@ -115,7 +113,7 @@ function getActivetypesKey() { function highlightSelectedNodes(flag){ - if (TW.debugFlags.logSelections) + if (TW.conf.debug.logSelections) console.log("\t***methods.js:highlightSelectedNodes(flag)"+flag+" selEmpty:"+is_empty(selections)) if(!is_empty(selections)){ for(var i in selections) { @@ -126,11 +124,11 @@ function highlightSelectedNodes(flag){ function alertCheckBox(eventCheck){ // NB: we use 2 booleans to adapt to SHIFT checking - // - var checkBox ---------> has the real box state - // - var manuallyChecked --> remembers if it was changed here + // - var TW.checkBox ---------> has the real box state + // - var TW.manuallyChecked --> remembers if it was changed here if(!isUndef(eventCheck.checked)) { - checkBox=eventCheck.checked; - manuallyChecked = eventCheck.checked + TW.checkBox=eventCheck.checked; + TW.manuallyChecked = eventCheck.checked } } @@ -174,7 +172,7 @@ function RefreshState(newNOW){ // N : number of nodes // k : number of ( selected nodes + their neighbors ) // s : number of selections - var N=( Object.keys(TW.Nodes).filter(function(n){return TW.Nodes[n].type==TW.catSoc}) ).length + var N=( Object.keys(TW.Nodes).filter(function(n){return TW.Nodes[n].type==TW.conf.catSoc}) ).length var k=Object.keys(getNeighs(Object.keys(selections),nodes1)).length var s=Object.keys(selections).length console.log("in social N: "+N+" - k: "+k+" - s: "+s) @@ -195,7 +193,7 @@ function RefreshState(newNOW){ } if(NOW=="B" || NOW=="b") { - var N=( Object.keys(TW.Nodes).filter(function(n){return TW.Nodes[n].type==TW.catSem}) ).length + var N=( Object.keys(TW.Nodes).filter(function(n){return TW.Nodes[n].type==TW.conf.catSem}) ).length var k=Object.keys(getNeighs(Object.keys(selections),nodes2)).length var s=Object.keys(selections).length console.log("in semantic N: "+N+" - k: "+k+" - s: "+s) @@ -357,7 +355,7 @@ function htmlfied_nodesatts(elems){ } socnodes.push(information); } else { - if(node.type==TW.catSoc){ + if(node.type==TW.conf.catSoc){ information += '<li><b>' + node.label + '</b></li>'; if(node.htmlCont==""){ if (!isUndef(node.level)) { @@ -369,7 +367,7 @@ function htmlfied_nodesatts(elems){ socnodes.push(information) } - if(node.type==TW.catSem){ + 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>'; @@ -606,7 +604,7 @@ function graphTagCloudElem(nodes) { TW.partialGraph.camera.goTo({x:0, y:0, ratio:0.9, angle: 0}) TW.partialGraph.refresh({skipIndexation:true}); - sigma_utils.smartForceAtlas(TW.fa2milliseconds/2) + sigma_utils.smartForceAtlas(TW.conf.fa2Milliseconds/2) // // ChangeGraphAppearanceByAtt(true) @@ -681,9 +679,11 @@ function prepareNodesRenderingProperties(nodesDict) { for (var nid in nodesDict) { var n = nodesDict[nid] + let sizeFactor = TW.conf.sizeMult[TW.catDict[n.type]] || 1 + // 3 decimals is way more tractable // and quite enough in precision !! - n.size = Math.round(n.size*1000)/1000 + n.size = Math.round(n.size*sizeFactor*1000)/1000 // new initial setup of properties n.active = false @@ -722,8 +722,8 @@ function prepareNodesRenderingProperties(nodesDict) { n.color = `rgb(${rgbStr})` } else { - n.color = TW.defaultNodeColor - rgbStr = TW.defaultNodeColor.split(',').splice(0, 3).join(','); + n.color = TW.conf.defaultNodeColor + rgbStr = TW.conf.defaultNodeColor.split(',').splice(0, 3).join(','); } n.customAttrs = { @@ -757,7 +757,7 @@ function prepareEdgesRenderingProperties(edgesDict, nodesDict) { var rgbStr = sigmaTools.edgeRGB(nodesDict[e.source].color, nodesDict[e.target].color) - e.color = "rgba("+rgbStr+","+TW.edgeDefaultOpacity+")" + e.color = "rgba("+rgbStr+","+TW.conf.edgeDefaultOpacity+")" e.customAttrs = { grey: false, activeEdge : false, diff --git a/tinawebJS/sigma.parseCustom.js b/tinawebJS/sigma.parseCustom.js index 3e08386df36a544fbbe950975882fcb9696dc453..8c22055d3d79aa88ebb19ac8be66bcb7293d4314 100755 --- a/tinawebJS/sigma.parseCustom.js +++ b/tinawebJS/sigma.parseCustom.js @@ -213,7 +213,7 @@ function scanGexf(gexfContent) { // sorting observed node types into Sem/Soc (factorized 11/05/2017) // -------------------- // FIXME this factorizes what we had twice (json & gexf scanFile workflows), -// and we just added missing TW.catSoc/Sem comparisons +// and we just added missing TW.conf.catSoc/Sem comparisons // *but it doesn't fix the underlying logic* // (current expected structure in 'categories' can only accomodate 2 types // and the way it and catDict are used is not entirely coherent throughout @@ -239,7 +239,7 @@ function sortNodeTypes(observedTypesDict) { // but in practice it's more often terms. anyways doesn't affect much catDict[observedTypes[0]] = 0; - if (TW.debugFlags.logParsers) + if (TW.conf.debug.logParsers) console.log(`cat unique (${observedTypes[0]}) =>0`) } if(nTypes>1) { @@ -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.catSoc || (c != TW.catSem && c.indexOf("term")==-1)) {// NOT a term-category + if(c == TW.conf.catSoc || (c != TW.catSem && c.indexOf("term")==-1)) {// NOT a term-category newcats[0] = c; catDict[c] = 0; } @@ -281,14 +281,14 @@ function facetsBinning (valuesIdx, Atts_2_Exclude) { let facetIdx = {} - if (TW.debugFlags.logFacets) { + if (TW.conf.debug.logFacets) { console.log('dictfyGexf: begin TW.Clusters') var classvalues_deb = performance.now() } // var gotClusters = false // for (var nodecat in valuesIdx) { - // gotClusters = gotClusters || (valuesIdx[nodecat]['cluster_index'] || valuesIdx[nodecat][TW.nodeClusAtt]) + // gotClusters = gotClusters || (valuesIdx[nodecat]['cluster_index'] || valuesIdx[nodecat][TW.conf.nodeClusAtt]) // } // all scanned attributes get an inverted index @@ -332,11 +332,11 @@ function facetsBinning (valuesIdx, Atts_2_Exclude) { // how many bins for this attribute ? var nBins = 3 - if (TW.customLegendsBins && TW.customLegendsBins[at]) { - nBins = TW.customLegendsBins[at] + if (TW.conf.customLegendsBins && TW.conf.customLegendsBins[at]) { + nBins = TW.conf.customLegendsBins[at] } - else if (TW.legendsBins) { - nBins = TW.legendsBins + else if (TW.conf.legendsBins) { + nBins = TW.conf.legendsBins } // create tick thresholds @@ -345,7 +345,7 @@ function facetsBinning (valuesIdx, Atts_2_Exclude) { legendRefTicks.push(valuesIdx[cat][at].vals[nthVal]) } - if (TW.debugFlags.logFacets) console.debug("intervals for", at, legendRefTicks) + if (TW.conf.debug.logFacets) console.debug("intervals for", at, legendRefTicks) var nTicks = legendRefTicks.length var sortedDistinctVals = Object.keys(valuesIdx[cat][at].map).sort(function(a,b){return Number(a)-Number(b)}) @@ -413,16 +413,16 @@ function facetsBinning (valuesIdx, Atts_2_Exclude) { } // 'clust_default' is an alias to the user-defined default clustering - if (TW.nodeClusAtt != undefined - && facetIdx[cat][TW.nodeClusAtt] // <= if found in data + if (TW.conf.nodeClusAtt != undefined + && facetIdx[cat][TW.conf.nodeClusAtt] // <= if found in data && !facetIdx[cat]['clust_default'] // <= and if an attr named 'clust_default' was not already in data ) { - facetIdx[cat]['clust_default'] = facetIdx[cat][TW.nodeClusAtt] + facetIdx[cat]['clust_default'] = facetIdx[cat][TW.conf.nodeClusAtt] } } - if (TW.debugFlags.logFacets) { + if (TW.conf.debug.logFacets) { var classvalues_fin = performance.now() console.log('end TW.Clusters, own time:', classvalues_fin-classvalues_deb) } @@ -435,7 +435,7 @@ function facetsBinning (valuesIdx, Atts_2_Exclude) { // for {1,2}partite graphs function dictfyGexf( gexf , categories ){ - if (TW.debugFlags.logParsers) + if (TW.conf.debug.logParsers) console.log("ParseCustom gexf 2nd loop, main data extraction, with categories", categories) @@ -455,11 +455,9 @@ function dictfyGexf( gexf , categories ){ // var edgesAttributes = declaredAtts.eAttrs var elsNodes = gexf.getElementsByTagName('nodes') // The list of xml nodes 'nodes' (plural) - labels = []; - minNodeSize=999.00; - maxNodeSize=0.001; - numberOfDocs=0; - numberOfNGrams=0; + TW.labels = []; + minNodeSize=10000000; + maxNodeSize=0; // debug: for local stats // let allSizes = [] @@ -579,8 +577,8 @@ function dictfyGexf( gexf , categories ){ if(!node.size) console.log("node without size: "+node.id+" : "+node.label); // user-indicated default => copy for old default accessors - if (node.attributes[TW.nodeClusAtt]) { - node.attributes['clust_default'] = node.attributes[TW.nodeClusAtt] + if (node.attributes[TW.conf.nodeClusAtt]) { + node.attributes['clust_default'] = node.attributes[TW.conf.nodeClusAtt] } // save record @@ -636,7 +634,7 @@ function dictfyGexf( gexf , categories ){ var edgesNode = edgesNodes[i]; var edgeNodes = edgesNode.getElementsByTagName('edge'); - if (TW.debugFlags.logParsers) + if (TW.conf.debug.logParsers) console.log("edgeNodes.length", edgeNodes.length) for(j=0; j<edgeNodes.length; j++) { @@ -651,7 +649,7 @@ function dictfyGexf( gexf , categories ){ id: indice, source: source, target: target, - type : (type) ? type : sigmaJsDrawingProperties['defaultEdgeType'], + type : (type) ? type : TW.conf.sigmaJsDrawingProperties['defaultEdgeType'], label: "", categ: "", attributes: [] @@ -895,7 +893,7 @@ function scanJSON( data ) { // Level-00 // for {1,2}partite graphs function dictfyJSON( data , categories ) { - if (TW.debugFlags.logParsers) + if (TW.conf.debug.logParsers) console.log("ParseCustom json 2nd loop, main data extraction, with categories", categories) var catDict = {} @@ -952,11 +950,11 @@ function dictfyJSON( data , categories ) { TW.Clusters = facetsBinning (tmpVals, Atts_2_Exclude) - colorList.sort(function(){ return Math.random()-0.5; }); + TW.colorList.sort(function(){ return Math.random()-0.5; }); for (var i in nodes ){ if (nodes[i].color=="#FFFFFF") { var attval = ( isUndef(nodes[i].attributes) || isUndef(nodes[i].attributes["clust_default"]) )? 0 : nodes[i].attributes["clust_default"] ; - nodes[i].color = colorList[ attval ] + nodes[i].color = TW.colorList[ attval ] } } diff --git a/tinawebJS/sigmaUtils.js b/tinawebJS/sigmaUtils.js index dae3f919a8225e6514fa0008266dd742b6f04bda..aff145b14151a07b000109cf3bd29710c80c47aa 100755 --- a/tinawebJS/sigmaUtils.js +++ b/tinawebJS/sigmaUtils.js @@ -14,7 +14,7 @@ SigmaUtils = function () { var n = nodes[i]; // console.debug('tr >>> fgr node', n) - if(initialActivetypes[catDict[n.type]] || TW.debugFlags.initialShowAll) { + if(initialActivetypes[catDict[n.type]] || TW.conf.debug.initialShowAll) { // var node = { // id : n.id, // label : n.label, @@ -111,7 +111,7 @@ SigmaUtils = function () { context.beginPath(); - if (TW.selectedColor == "node") + if (TW.conf.nodesGreyBorderColor == "node") context.fillStyle = TW.handpickedcolor? node.customAttrs.alt_color : node.color; // node's else context.fillStyle = "#F7E521"; // yellow @@ -204,11 +204,11 @@ SigmaUtils = function () { // console.debug(`t=${tstamp()} curve render activeedge: ${edgeInfos(edge)})`) } else if (edge.customAttrs.grey) { - color = TW.edgeGreyColor + color = TW.conf.edgeGreyColor size = 1 } else { - color = "rgba( "+baseRGB+" , "+TW.edgeDefaultOpacity+")"; + color = "rgba( "+baseRGB+" , "+TW.conf.edgeDefaultOpacity+")"; size = defSize } @@ -256,11 +256,11 @@ SigmaUtils = function () { color = 'rgba('+rgb.join()+',.7)' } else if (edge.customAttrs.grey) { - color = TW.edgeGreyColor + color = TW.conf.edgeGreyColor size = 1 } else { - // color = "rgba( "+rgb.join()+" , "+TW.edgeDefaultOpacity+")"; + // color = "rgba( "+rgb.join()+" , "+TW.conf.edgeDefaultOpacity+")"; color = edge.customAttrs.true_color size = defSize } @@ -294,7 +294,7 @@ SigmaUtils = function () { // mode variants if (TW.selectionActive) { - // passive nodes should blend in the grey of TW.edgeGreyColor + // passive nodes should blend in the grey of TW.conf.edgeGreyColor // cf settings_explorerjs, defgrey_color and greyEverything() if (node.customAttrs.grey) { if (! TW.handpickedcolor) { @@ -310,7 +310,7 @@ SigmaUtils = function () { nodeColor = node.customAttrs.altgrey_color } // nice looking uniform grey - borderColor = TW.nodesGreyBorderColor + borderColor = TW.conf.nodesGreyBorderColor } // neighbor nodes <=> (highlight flag AND selectionActive) else if(node.customAttrs.highlight) { @@ -501,10 +501,9 @@ SigmaUtils = function () { // - conditions on graph size (£TODO use these to slowDown small graphs) // - edges management (turns them off and restores them after finished) this.smartForceAtlas = function (fa2duration) { - - if (TW.fa2Available) { + if (TW.conf.fa2Available) { if (!fa2duration) { - fa2duration = parseInt(TW.fa2milliseconds) || 4000 + fa2duration = parseInt(TW.conf.fa2Milliseconds) || 4000 } // togglability case @@ -514,7 +513,7 @@ SigmaUtils = function () { } // normal case else { - if ( TW.fa2enabled && TW.partialGraph.graph.nNodes() >= TW.minNodesForAutoFA2) { + if ( TW.conf.fa2Enabled && TW.partialGraph.graph.nNodes() >= TW.conf.minNodesForAutoFA2) { // hide edges during work for smaller cpu load if (TW.partialGraph.settings('drawEdges')) { this.toggleEdges(false) @@ -547,7 +546,7 @@ SigmaUtils = function () { function createWaitIcon(idname, width) { let icon = document.createElement('img') - icon.src = TW.libspath + '/img2/loader.gif' + icon.src = TW.conf.libspath + '/img2/loader.gif' icon.style.position = 'absolute' icon.style.left = '0' @@ -886,7 +885,7 @@ function repaintEdges() { // rewrite of clustersBy with binning and for attributes that can have negative float values // NB - binning is done at parseCustom -// - number of bins can be specified by attribute name in TW.customLegendsBins +// - number of bins can be specified by attribute name in TW.conf.customLegendsBins function colorsRelByBins(daclass) { var binColors var doModifyLabel = false @@ -1202,7 +1201,7 @@ function colorsBy(daclass) { } else { // shuffle on entire array is better than random sorting function on each element - var randomColorList = shuffle(colorList) + var randomColorList = shuffle(TW.colorList) for(var j in TW.nodeIds) { var the_node = TW.Nodes[ TW.nodeIds[j] ] diff --git a/tinawebJS/sigma_tools.js b/tinawebJS/sigma_tools.js index f35769a7838cd8fd2e0e4da93c9f4f8f2172fa7f..d31c5c92132383c2f509a2847da9da12d245ec7c 100644 --- a/tinawebJS/sigma_tools.js +++ b/tinawebJS/sigma_tools.js @@ -45,7 +45,7 @@ sigmaTools = (function(stools) { var rawEdge = rawGexfEdges[i] var rgbStr = sigmaTools.edgeRGB(newNodes[rawEdge.source].color, newNodes[rawEdge.target].color) - var leColor = "rgba("+rgbStr+","+TW.edgeDefaultOpacity+")" + var leColor = "rgba("+rgbStr+","+TW.conf.edgeDefaultOpacity+")" var newEid = rawEdge.source+";"+rawEdge.target; var newEdge = {