Commit 24648495 authored by Romain Loth's avatar Romain Loth

finished reldb entries and logic: gui tab mecanisms + doc

parent 79407bcd
...@@ -13,9 +13,9 @@ Après commits de la semaine 26-30 juin 2017, une structure plus facile pour les ...@@ -13,9 +13,9 @@ Après commits de la semaine 26-30 juin 2017, une structure plus facile pour les
│   └── (fusionné avec ./doc) │   └── (fusionné avec ./doc)
├── data ├── data
│   └── (graphes par sous-projets) │   └── (graphes par sous-projets)
├── db.json
├── explorerjs.html <= point d'entrée lancement ├── explorerjs.html <= point d'entrée lancement
├── settings_explorerjs.js <= point d'entrée config ├── settings_explorerjs.js <= config générale
├── db.json <= config additionnelle par sources gexf/json
├── favicon.ico ├── favicon.ico
├── LICENSE ├── LICENSE
├── README.md ├── README.md
...@@ -34,7 +34,6 @@ Après commits de la semaine 26-30 juin 2017, une structure plus facile pour les ...@@ -34,7 +34,6 @@ Après commits de la semaine 26-30 juin 2017, une structure plus facile pour les
│   ├── jquery-3 │   ├── jquery-3
│   ├── readmore.js │   ├── readmore.js
│   ├── sigma_v1.2 │   ├── sigma_v1.2
│   ├── sigma_v1.5
│   └── tweets │   └── tweets
| |
├── twmain <= ancien dossier tinawebJS ├── twmain <= ancien dossier tinawebJS
......
...@@ -64,55 +64,103 @@ Having a node0.name entry and optionally a node1.name is enough to display the g ...@@ -64,55 +64,103 @@ Having a node0.name entry and optionally a node1.name is enough to display the g
The servermenu file also allows configuration of associated queries for selected node(s): **relatedDocs** The servermenu file also allows configuration of associated queries for selected node(s): **relatedDocs**
To enable it, you need to add to your node entry the `reldbfile` key: To enable it, you need to add to your node entry the `reldbs` key with minimally a db type :
```json ```json
"node0": { "node0": {
"name": "$$blabla", "name": "$$blabla",
"reldbfile": "$$relpath/to/csv/or/sqlite" "reldbs": {
"$$myType" : {}
}
} }
``` ```
The presence of this property `reldbfile` makes the API usable in db.json. The presence of this property "reldbs" makes the API usable in db.json.
##### More relatedDocs settings ##### More relatedDocs settings
In addition, for full configuration, the following entries can be set under node0 or node1. In addition, for full configuration, the following entries can be set under node0 or node1.
###### => for a CSV doc-by-doc table ###### => for a CSV doc-by-doc table
Expected type is `"csv"` and you should fill the columns to search in. Expected type is `"csv"` and you should fill the columns to search in and the template to use to render hits
```json ```json
"reldbtype": "csv", "reldbs": {
"reldbqcols": ["list", "of", "columns", "to", "search", "in", "for", "node0"] "csv" : {
"file": "$$relpath/to/some.csv",
"qcols": ["list", "of", "cols", "to", "search", "in", "for", "node0"],
"template": "bib_details"
}
}
``` ```
###### Real life example ###### => for a cortext sql base
Expected type is `"CortextDB"` and you should fill the tables to search in.
```json
"reldbs": {
"CortextDB": {
"file": "$$relpath/to/some.db",
"qtable": "$$tableNameToSearchIn",
"template": "cortext_with_link"
}
}
```
###### => for twitter queries
Expected type is `"twitter"` and no additional conf is needed (POSS for the future: add twitter query context, ex: "Présidentielles 2017 AND (query)").
```json
"reldbs": {
"twitter": {}
}
```
###### Real life examples
```json ```json
"data/gargistex": { "data/gargistex": {
"first": "shale_and_ice.gexf", "graphs":{
"graphs": { "model_calibration.gexf": {
"shale_and_ice.gexf": { "node0": {
"node0": { "name": "terms",
"name": "terms", "reldbs": {
"reldbtype": "csv", "csv": {
"reldbfile": "shale_and_ice.csv", "file": "model_calibration.csv",
"reldbqcols": ["title", "abstract"] "qcols": ["title"],
"template": "bib_details"
},
"twitter": {}
}
}
}
}
},
"data/test": {
"first" : "mini_for_csv.gexf",
"graphs": {
"mini_for_csv.gexf": {
"node0": {
"name": "terms",
"reldbs": {
"csv": {
"file": "mini_for_csv.csv",
"qcols": ["title","keywords","text"],
"template": "bib_details"
},
"twitter": {}
} }
}, },
"model_calibration.gexf": { "node1": {
"node0": { "name": "authors",
"name": "terms", "reldbs": {
"reldbtype": "csv", "csv": {
"reldbfile": "model_calibration.csv", "file": "mini_for_csv.csv",
"reldbqcols": ["title", "abstract"] "qcols": ["author"],
"template": "bib_details"
}
} }
} }
} }
}
} }
``` ```
###### => for CortextDB SQL tables In the last exemple, we have two nodetypes:
Expected type is `"CortextDB"` and you should fill the table to search in. - node0 allows both CSV and twitter relatedDocs tabs.
```json - node1 allows only the CSV relatedDocs tab.
"reldbtype": "CortextDB",
"reldbqtable": []
```
This is a stub for a future documentation for developers. This is a stub for a future documentation for developers.
#### About settings
- system-wide settings are in `settings_explorerjs.js`
- source-by-source settings (nodetypes, relatedDocs APIs) are in `db.json`
## Graph input choices ## Graph input choices
...@@ -13,7 +16,7 @@ Tina allows 3 main ways of input : ...@@ -13,7 +16,7 @@ Tina allows 3 main ways of input :
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 `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 `serverfile` option has an extended version called `servermenu`. It opens the list of files from `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. The detailed implementation of these choices can be found in the function `syncRemoteGraphData()` in main.js.
...@@ -32,7 +35,8 @@ This will still evolve but the main steps for any graph initialization messily u ...@@ -32,7 +35,8 @@ This will still evolve but the main steps for any graph initialization messily u
4. [`main.js`] mainStartGraph() function runs all the rest 4. [`main.js`] mainStartGraph() function runs all the rest
1. precomputes display properties (grey color, etc.) 1. precomputes display properties (grey color, etc.)
2. calls [`sigmaUtils`] where the function `FillGraph()` was a central point for filtering and preparing properties but now with 2 and 3 it just creates a filtered copy of the nodes and edges of the current active types to a new structure that groups them together (POSSIBLE remove this extra step) 2. calls [`sigmaUtils`] where the function `FillGraph()` was a central point for filtering and preparing properties but now with 2 and 3 it just creates a filtered copy of the nodes and edges of the current active types to a new structure that groups them together (POSSIBLE remove this extra step)
3. back in [`main.js`], finally all sigma settings (user + defaults) are merged and we initialize the sigma instance (`new sigma` etc.) 3. back in [`main.js`], finally all sigma settings (user + defaults) are merged and we initialize the sigma instance (`new sigma` etc.)
at this point, any additional conf located in db.json is used for nodeTypes and relatedDocsTypes
4. finally a call to [`TinawebJS`] initializes the action listeners and this phase should crucially initialize items that need the sigma instance (because they may depend the displayed categories, the number of displayed nodes, etc) 4. finally a call to [`TinawebJS`] initializes the action listeners and this phase should crucially initialize items that need the sigma instance (because they may depend the displayed categories, the number of displayed nodes, etc)
......
{ {
"data/ClimateChange": {
"graphs": {
"Maps_S_800.gexf": {
"node0": {
"name": "ISItermsWhitelistV2Oct_5 &amp; ISItermsWhitelistV2Oct_5",
"reldbs": {
"CortextDB": {
"file": "wos_climate-change_title_2014-2015.db",
"qtable": "ISItermsWhitelistV2Oct_5",
"template": "cortext_with_link"
},
"twitter": {}
}
}
}
}
},
"data/gargistex": { "data/gargistex": {
"first" : "shale_and_ice.gexf", "first" : "shale_and_ice.gexf",
"graphs":{ "graphs":{
...@@ -81,23 +98,6 @@ ...@@ -81,23 +98,6 @@
} }
} }
}, },
"data/ClimateChange": {
"graphs": {
"Maps_S_800.gexf": {
"node0": {
"name": "ISItermsWhitelistV2Oct_5 &amp; ISItermsWhitelistV2Oct_5",
"reldbs": {
"CortextDB": {
"file": "wos_climate-change_title_2014-2015.db",
"qtable": "ISItermsWhitelistV2Oct_5",
"template": "cortext_with_link"
},
"twitter": {}
}
}
}
}
},
"graphapi": { "graphapi": {
"__comment__": "special subproject for api sourcemode", "__comment__": "special subproject for api sourcemode",
"__comment__": "allows setting nodetypes and reldocs for api graph sources", "__comment__": "allows setting nodetypes and reldocs for api graph sources",
......
...@@ -31,7 +31,7 @@ TW.conf = (function(TW){ ...@@ -31,7 +31,7 @@ TW.conf = (function(TW){
TWConf.getRelatedDocs = true TWConf.getRelatedDocs = true
TWConf.relatedDocsMax = 10 TWConf.relatedDocsMax = 10
// fallback type (overriden by new replacement from db.json) // fallback type (if no detailed source-by-source conf from db.json)
TWConf.relatedDocsType = "csv" // accepted: "twitter" | "csv" | "CortextDB" TWConf.relatedDocsType = "csv" // accepted: "twitter" | "csv" | "CortextDB"
// POSSible: "elastic" // POSSible: "elastic"
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
background-color: #BBB; background-color: #BBB;
} }
#topPapers { .topPapers {
-moz-box-shadow: none ; -moz-box-shadow: none ;
-webkit-box-shadow: none ; -webkit-box-shadow: none ;
box-shadow: none; box-shadow: none;
......
...@@ -325,12 +325,14 @@ ul.infoitems { ...@@ -325,12 +325,14 @@ ul.infoitems {
border-right: 1px solid #222; border-right: 1px solid #222;
} }
#topPapers{ .topPapers{
display: none; display: none;
color:black; color:black;
} }
.tab-pane {
transition: height 0.5s ease-out;
}
......
...@@ -1058,93 +1058,8 @@ var TinaWebJS = function ( sigmacanvas ) { ...@@ -1058,93 +1058,8 @@ var TinaWebJS = function ( sigmacanvas ) {
let moreConfKey = optionalConfEntry || TW.File let moreConfKey = optionalConfEntry || TW.File
let ul = document.getElementById('reldocs-tabs') resetTabs(initialActivetypes, TW.gmenuInfos[moreConfKey])
let divs = document.getElementById('reldocs-boxes')
// remove any previous tabs
ul.innerHTML = ""
divs.innerHTML = ""
TW.gui.reldocTabs = [{},{}]
// for all existing nodetypes
for (let nodetypeId in initialActivetypes) {
let additionalConf = TW.gmenuInfos[moreConfKey][nodetypeId]
if (TW.conf.debug.logSettings)
console.log ("additionalConf for this source", additionalConf)
let possibleAPIs = []
if (additionalConf.reldbs) {
possibleAPIs = additionalConf.reldbs
console.log("Tabs init from db.json")
}
else {
possibleAPIs = [TW.conf.relatedDocsType]
console.log("Tabs init from default")
}
if (additionalConf.reldbs) {
for (var possibleAPI in possibleAPIs){
// the tab's id
let tabref = `rd-${nodetypeId}-${possibleAPI}`
// create valid tabs
let newLi = document.createElement('li')
newLi.setAttribute("role", "presentation")
let newRDTab = document.createElement('a')
newRDTab.text = `${possibleAPI} (${nodetypeId==0?'sem':'soc'})`
newRDTab.href = "#"+tabref
newRDTab.setAttribute("role", "tab")
newRDTab.dataset.toggle = 'tab'
newRDTab.dataset.reldocstype = possibleAPI
newRDTab.dataset.nodetype = nodetypeId
newRDTab.setAttribute("class", `for-nodecategory-${nodetypeId}`)
// keep access
TW.gui.reldocTabs[nodetypeId][possibleAPI] = newRDTab
// create corresponding content box
let newContentDiv = document.createElement('div')
newContentDiv.setAttribute("role", "tabpanel")
newContentDiv.setAttribute("class", "topPapers tab-pane")
newContentDiv.id = tabref
// add to DOM
ul.append(newLi)
newLi.append(newRDTab)
divs.append(newContentDiv)
// select currently preferred reldoc tabs
if (possibleAPI == TW.conf.relatedDocsType) {
newLi.classList.add("active")
newContentDiv.classList.add("active")
}
}
// afterwards to already have all initialized rdtypes in DOM
for (let rdtype in TW.gui.reldocTabs[nodetypeId]) {
let tab = TW.gui.reldocTabs[nodetypeId][rdtype]
// init toggle mecanisms (bootstrap.native/#componentTab)
// (just used for the tabs active/inactive handling,
// content is *always* topPapers and we modify it ourselves)
new Tab(tab);
// add handler to switch relatedDocsType
tab.addEventListener('click', function(e){
let reldType = e.target.dataset.reldocstype
let nodeType = e.target.dataset.nodetype
let qWords = queryForType(nodeType)
console.log('tab click', `rd-${nodeType}-${reldType}`)
getTopPapers(qWords, nodeType, reldType, `rd-${nodeType}-${reldType}`)
})
}
}
}
} }
// select currently active sliders // select currently active sliders
......
...@@ -1128,6 +1128,120 @@ function createWaitIcon(idname, width) { ...@@ -1128,6 +1128,120 @@ function createWaitIcon(idname, width) {
return icon return icon
} }
activateRDTab = function(elTgt) {
let relDbType = elTgt.dataset.reldocstype
let ndTypeId = elTgt.dataset.nodetype
let tabs = document.querySelectorAll('ul#reldocs-tabs > li')
for (var tabLi of tabs) {
if (tabLi != elTgt.parentNode)
tabLi.classList.remove("active")
else
tabLi.classList.add("active")
}
let divs = document.querySelectorAll("div#reldocs-boxes > div.tab-pane")
let theId = `rd-${ndTypeId}-${relDbType}`
// POSS: animate with transitions here
for (var tabDiv of divs) {
if (tabDiv.id != theId)
tabDiv.classList.remove("active", "in")
else
tabDiv.classList.add("active", "in")
}
}
// set up tabs for a given activetypes state and db.json entry
function resetTabs(activetypes, dbconf) {
let ul = document.getElementById('reldocs-tabs')
let divs = document.getElementById('reldocs-boxes')
// remove any previous tabs
ul.innerHTML = ""
divs.innerHTML = ""
TW.gui.reldocTabs = [{},{}]
// used with no args for full reset
if (!activetypes || !dbconf) {
return
}
console.log("dbconf for this source", dbconf)
// for all active nodetypes
for (let nodetypeId in activetypes) {
if (activetypes[nodetypeId]) {
let additionalConf = dbconf[nodetypeId]
if (TW.conf.debug.logSettings)
console.log ("additionalConf for this source", additionalConf)
let possibleAPIs = []
if (additionalConf.reldbs) {
possibleAPIs = additionalConf.reldbs
// 3 vars to know which one to activate
let nAPIs = Object.keys(possibleAPIs).length
let iAPI = 0
let didActiveFlag = false
for (var possibleAPI in possibleAPIs){
// the tab's id
let tabref = `rd-${nodetypeId}-${possibleAPI}`
// create valid tabs
let newLi = document.createElement('li')
newLi.setAttribute("role", "presentation")
let newRDTab = document.createElement('a')
newRDTab.text = `${possibleAPI} (${nodetypeId==0?'sem':'soc'})`
newRDTab.setAttribute("role", "tab")
newRDTab.dataset.reldocstype = possibleAPI
newRDTab.dataset.nodetype = nodetypeId
newRDTab.setAttribute("class", `for-nodecategory-${nodetypeId}`)
// newRDTab.dataset.toggle = 'tab' // only needed if using bootstrap
// keep access
TW.gui.reldocTabs[nodetypeId][possibleAPI] = newRDTab
// create corresponding content box
let newContentDiv = document.createElement('div')
newContentDiv.setAttribute("role", "tabpanel")
newContentDiv.setAttribute("class", "topPapers tab-pane")
newContentDiv.id = tabref
// add to DOM
ul.append(newLi)
newLi.append(newRDTab)
divs.append(newContentDiv)
// select currently preferred reldoc tabs
// (we activate if favorite or if no matching favorite and last)
if (possibleAPI == TW.conf.relatedDocsType
|| (!didActiveFlag && iAPI == nAPIs - 1)) {
newLi.classList.add("active")
newContentDiv.classList.add("active", "in")
didActiveFlag = true
}
// add handler to switch relatedDocsType
newRDTab.addEventListener('click', function(e){
// tab mecanism
activateRDTab(e.target)
// no need to run associated query:
// (updateRelatedNodesPanel did it at selection time)
})
iAPI++
}
}
}
}
}
function jsActionOnGexfSelector(graphBasename){ function jsActionOnGexfSelector(graphBasename){
let graphPath = TW.gmenuPaths[graphBasename] || graphBasename+".gexf" let graphPath = TW.gmenuPaths[graphBasename] || graphBasename+".gexf"
......
...@@ -298,7 +298,6 @@ saferString = function(string) { ...@@ -298,7 +298,6 @@ saferString = function(string) {
} }
/** /**
* function to test if file exists * function to test if file exists
* via XHR, enhanced from http://stackoverflow.com/questions/5115141 * via XHR, enhanced from http://stackoverflow.com/questions/5115141
......
...@@ -78,8 +78,9 @@ if (window.location.protocol == 'file:' || sourcemode == 'localfile') { ...@@ -78,8 +78,9 @@ if (window.location.protocol == 'file:' || sourcemode == 'localfile') {
// traditional cases: remote read from API or prepared server-side file // traditional cases: remote read from API or prepared server-side file
else { else {
try { try {
// we'll first retrieve the menu of available sources in db.json, then get the real data in a second ajax via API or server file // we'll first retrieve the menu of available sources in db.json,
[TW.gmenuPaths, TW.gmenuInfos] = readMenu(TW.conf.paths.sourceMenu) // then get the real data in a second ajax via API or server file
[TW.gmenuPaths, TW.gmenuInfos, TW.File] = readMenu(TW.conf.paths.sourceMenu)
// NB: this menu used to be a file list for only one sourcemode // NB: this menu used to be a file list for only one sourcemode
// but now also contains settings for nodetypes and for // but now also contains settings for nodetypes and for
...@@ -238,11 +239,12 @@ function syncRemoteGraphData () { ...@@ -238,11 +239,12 @@ function syncRemoteGraphData () {
var files_selector = '<select onchange="jsActionOnGexfSelector(this.value);">' var files_selector = '<select onchange="jsActionOnGexfSelector(this.value);">'
for (let shortname in TW.gmenuPaths) { for (let shortname in TW.gmenuPaths) {
let fullPath = TW.gmenuPaths[shortname] let fullPath = TW.gmenuPaths[shortname]
let cssFileSelected = (TW.File==fullPath)?"selected":"" files_selector += '<option>'+shortname+'</option>'
files_selector += '<option '+cssFileSelected+'>'+shortname+'</option>'
} }
files_selector += "</select>" files_selector += "</select>"
$("#network").html(files_selector) $("#network").html(files_selector)
// in this case we keep the TW.File that was already set from readMenu
} }
// direct urlparam file case // direct urlparam file case
...@@ -262,6 +264,7 @@ function syncRemoteGraphData () { ...@@ -262,6 +264,7 @@ function syncRemoteGraphData () {
inData = finalRes["data"] inData = finalRes["data"]
inFormat = finalRes["format"] inFormat = finalRes["format"]
inConfKey = TW.File inConfKey = TW.File
mapLabel = TW.File
if (TW.conf.debug.logFetchers) { if (TW.conf.debug.logFetchers) {
console.warn('@TW.File', finalRes["OK"], TW.File) console.warn('@TW.File', finalRes["OK"], TW.File)
......
...@@ -60,6 +60,16 @@ TW.pushGUIState = function( args ) { ...@@ -60,6 +60,16 @@ TW.pushGUIState = function( args ) {
} }
} }
// recreate tabs after type changes
// db.json conf entry (£TODO unify s/(?:TW.File|inConfKey)/TW.sourceId/g)
let inConfKey = (sourcemode != "api") ? TW.File : 'graphapi/default'
if (TW.conf.getRelatedDocs
&& !isUndef(args.activetypes)
&& TW.gmenuInfos[inConfKey]) {
resetTabs(newState.activetypes, TW.gmenuInfos[inConfKey])
}
// 4) store it in TW.states // 4) store it in TW.states
TW.states.push(newState) TW.states.push(newState)
...@@ -78,6 +88,9 @@ TW.resetGraph = function() { ...@@ -78,6 +88,9 @@ TW.resetGraph = function() {
// remove the selection // remove the selection
cancelSelection(false, {norender: true}) cancelSelection(false, {norender: true})
// and set tabs to none
resetTabs()
// call the sigma graph clearing // call the sigma graph clearing
TW.instance.clearSigma() TW.instance.clearSigma()
...@@ -104,6 +117,7 @@ TW.resetGraph = function() { ...@@ -104,6 +117,7 @@ TW.resetGraph = function() {
function readMenu(infofile) { function readMenu(infofile) {
// exemple entry // exemple entry
// --------------
// "data/gargistex": { // "data/gargistex": {
// "first" : "shale_and_ice.gexf", // "first" : "shale_and_ice.gexf",
// "graphs":{ // "graphs":{
...@@ -123,8 +137,6 @@ function readMenu(infofile) { ...@@ -123,8 +137,6 @@ function readMenu(infofile) {
// } // }
// } // }
let paths = {}
let details = {}
if (TW.conf.debug.logFetchers) console.info(`attempting to load filemenu ${infofile}`) if (TW.conf.debug.logFetchers) console.info(`attempting to load filemenu ${infofile}`)
var preRES = AjaxSync({ url: infofile, datatype:"json" }); var preRES = AjaxSync({ url: infofile, datatype:"json" });
...@@ -133,25 +145,19 @@ function readMenu(infofile) { ...@@ -133,25 +145,19 @@ function readMenu(infofile) {
if (TW.conf.debug.logFetchers) console.log('initial AjaxSync result preRES', preRES) if (TW.conf.debug.logFetchers) console.log('initial AjaxSync result preRES', preRES)
} }
var first_file = "" , first_path = "" // 1 - store the first one (b/c we'll loose order)
var first_file = "", first_dir = "" , first_path = ""
for( var path in preRES.data ) { for( var path in preRES.data ) {
if (TW.conf.debug.logFetchers) console.log("db.json path", path)
if (TW.conf.debug.logFetchers) console.log("db.json path", path) first_file = preRES.data[path]["first"] || Object.keys(preRES.data[path]["graphs"])[0]
first_dir = path
first_file = preRES.data[path]["first"] break;
first_path = path
break;
} }
first_path = first_dir+"/"+first_file
// the first or a specified one (ie both mode and file params are present) // 2 - process all the paths and associated confs
if( isUndef(getUrlParam.file) ) { let paths = {}
TW.File = first_path+"/"+first_file let details = {}
mapLabel = first_file
} else {
// £POSS; match on the full paths from db.json
TW.File = first_path+"/"+getUrlParam.file
mapLabel = getUrlParam.file
}
for( var path in preRES.data ) { for( var path in preRES.data ) {
var theGraphs = preRES.data[path]["graphs"] var theGraphs = preRES.data[path]["graphs"]
...@@ -162,35 +168,27 @@ function readMenu(infofile) { ...@@ -162,35 +168,27 @@ function readMenu(infofile) {
// ex : "RiskV2PageRank1000.gexf":data/AXA/RiskV2PageRank1000.gexf // ex : "RiskV2PageRank1000.gexf":data/AXA/RiskV2PageRank1000.gexf
// (we assume there's no duplicate basenames) // (we assume there's no duplicate basenames)
if (TW.conf.debug.logSettings) if (TW.conf.debug.logSettings)
console.log("db conf entry: "+graphBasename) console.log("db conf entry: "+graphBasename)
// for associated LocalDB php queries: CSV (or CortextDBs sql) // for associated LocalDB php queries: CSV (or CortextDBs sql)
if (theGraphs[aGraph]) { if (theGraphs[aGraph]) {
let gSrcEntry = theGraphs[aGraph] let gSrcEntry = theGraphs[aGraph]
details[path+"/"+aGraph] = new Array(2) details[path+"/"+aGraph] = new Array(2)
if (gSrcEntry.node0) { if (gSrcEntry.node0) {
details[path+"/"+aGraph][0] = gSrcEntry.node0 details[path+"/"+aGraph][0] = gSrcEntry.node0
} }
if (gSrcEntry.node1) { if (gSrcEntry.node1) {
details[path+"/"+aGraph][1] = gSrcEntry.node1 details[path+"/"+aGraph][1] = gSrcEntry.node1
} }
} }
else { else {
details[path+"/"+aGraph] = null details[path+"/"+aGraph] = null
} }
} }
// console.log( files_selector )
} }
return [paths, details] return [paths, details, first_path]
} }
...@@ -520,20 +518,28 @@ function updateRelatedNodesPanel( sels , same, oppos ) { ...@@ -520,20 +518,28 @@ function updateRelatedNodesPanel( sels , same, oppos ) {
$("#information").html(informationDIV); $("#information").html(informationDIV);
if (TW.conf.getRelatedDocs) { if (TW.conf.getRelatedDocs) {
$("#reldocs-tabs-wrapper").show(); let rdTabCount = 0
// update all related docs tabs // update all related docs tabs
for (let ntId in TW.SystemState().activetypes) { for (let ntId in TW.SystemState().activetypes) {
if (TW.SystemState().activetypes[ntId]) { if (TW.SystemState().activetypes[ntId]) {
let qWords = queryForType(ntId) let qWords = queryForType(ntId)
// console.log("available topPapers tabs:", TW.gui.reldocTabs[ntId])
for (let relDbType in TW.gui.reldocTabs[ntId]) {
let tabId = `rd-${ntId}-${relDbType}`
rdTabCount ++
console.log("available topPapers tabs:", TW.gui.reldocTabs[ntId]) // if not already done
if (! TW.lastRelDocQueries[tabId]
|| TW.lastRelDocQueries[tabId] != qWords) {
getTopPapers(qWords, ntId, relDbType, tabId)
for (let relDbType in TW.gui.reldocTabs[ntId]) { // memoize
getTopPapers(qWords, ntId, relDbType, `rd-${ntId}-${relDbType}`) TW.lastRelDocQueries[tabId] = qWords
}
} }
} }
} }
if (rdTabCount > 0) $("#reldocs-tabs-wrapper").show();
} }
else { else {
$("#reldocs-tabs-wrapper").hide(); $("#reldocs-tabs-wrapper").hide();
......
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