diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..82c96d8
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,6 @@
+{
+ "parserOptions": {
+ "ecmaVersion": 6
+ },
+ "extends": "google"
+}
diff --git a/.gitignore b/.gitignore
index c465040..4521803 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,41 +1,58 @@
-
-# Compiled source #
-###################
-*.com
-*.class
-*.dll
-*.exe
-*.o
-*.so
-*.pyc
-
-# Packages #
-############
-# it's better to unpack these files and commit the raw source
-# git has its own built in compression methods
-*.7z
-*.dmg
-*.gz
-*.iso
-*.jar
-*.rar
-*.tar
-*.zip
-
-# Logs and databases #
-######################
+# Logs
+logs
*.log
-*.sql
-*.db
-*.sqlite
-*.gexf
-*.json
-
-# OS generated files #
-######################
-.DS_Store
-.DS_Store?
-.Spotlight-V100
-.Trashes
-data/*
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# Typescript v1 declaration files
+typings/
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
diff --git a/README.md b/README.md
index cff8c10..7d9540a 100755
--- a/README.md
+++ b/README.md
@@ -2,21 +2,20 @@
### Presentation
-Thank you for using ProjectExplorer/TinawebJS.
+Thank you for using Tinaweb.
This work is lead by the Complex Systems Institute of Paris Ile-de-France ([ISCPIF](http://iscpif.fr)) and the [Centre d'Analyse et de Mathématique Sociales](http://cams.ehess.fr/), both [CNRS](http://www.cnrs.fr/) entities.
###### Source code repository
- https://github.com/moma/ProjectExplorer
+ https://github.com/ISCPIF/tinaweb
###### Authors
-Researchers and engineers of the [ISCPIF/CNRS - UPS 3611](http://iscpif.fr)
- - Dr. David Chavalarias
+This work is conducted under the supervision of Dr. David Chavalarias (CNRS, CAMS & ISC-PIF), with the following contributors to the development :
- Samuel Castillo
- Romain Loth
-You can contact the authors by email ().
+You can contact us by email ().
###### Acknowledgements
- TinawebJS is build on top of Alexis Jacomy and Guillaume Plique's [sigmaJS](http://sigmajs.org)
@@ -28,21 +27,21 @@ You can contact the authors by email ().
### Usage
-ProjectExplorer is a versatile app that can be used as standalone or as a client library. The documentation concerning the different setup cases is being updated after a major refactoring and will grow in time.
+Tinaweb is a versatile app that can be used as standalone or as a client library. The documentation concerning the different setup cases is being updated after a major refactoring and will grow in time.
Here are the main points.
###### Getting started
In the simplest setup, just clone the repository and open explorerjs.html in a modern browser.
```
-git clone https://github.com/moma/ProjectExplorer.git
-cd ProjectExplorer
+git clone git@github.com:ISCPIF/tinaweb.git
+cd tinaweb
firefox explorerjs.html
```
=> An input in the upper right side allows you to open any gexf file.
[]
###### Application and data structures
-An overview of the application structure can be found in the extended documentation under [00.DOCUMENTATION/A-Introduction/app_structure.md](https://github.com/moma/ProjectExplorer/blob/master/00.DOCUMENTATION/A-Introduction/app_structure.md) (comments are in french).
+An overview of the application structure can be found in the extended documentation under [00.DOCUMENTATION/A-Introduction/app_structure.md](https://github.com/ISCPIF/tinaweb/blob/master/00.DOCUMENTATION/A-Introduction/app_structure.md) (comments are in french).
###### Usage on a web server
To activate all features, you should:
@@ -59,14 +58,14 @@ Once you have this webserver running and some source data files, you may also co
- it will be shown as a **menu** to select graphs in the interface
- it allows to define associated **node types** for each source
- it allows to define associated **search backends** for each source
- - to use this, follow the guidelines in the **[Project config HOWTO](https://github.com/moma/ProjectExplorer/blob/master/00.DOCUMENTATION/A-Introduction/project_config.md)**
+ - to use this, follow the guidelines in the **[Project config HOWTO](https://github.com/ISCPIF/tinaweb/blob/master/00.DOCUMENTATION/A-Introduction/project_config.md)**
###### Integration in a larger app
-To integrate ProjectExplorer in a larger web application, you may have several locations with subdirectories defined on your server. In this case, you'll need to use the provided path modification tool (see this [integration procedure example](https://github.com/moma/ProjectExplorer/tree/master/00.DOCUMENTATION/A-Introduction#integration-policy))
+To integrate ProjectExplorer in a larger web application, you may have several locations with subdirectories defined on your server. In this case, you'll need to use the provided path modification tool (see this [integration procedure example](https://github.com/ISCPIF/tinaweb/tree/master/00.DOCUMENTATION/A-Introduction#integration-policy))
###### Advanced settings
-For more information about ProjectExplorer's settings (settings file, input modes, attribute processing options), please refer to the [detailed introduction](https://github.com/moma/ProjectExplorer/blob/master/00.DOCUMENTATION/A-Introduction/README.md)
- and the [developer's manual](https://github.com/moma/ProjectExplorer/blob/master/00.DOCUMENTATION/C-advanced/developer_manual.md).
+For more information about ProjectExplorer's settings (settings file, input modes, attribute processing options), please refer to the [detailed introduction](https://github.com/ISCPIF/tinaweb/blob/master/00.DOCUMENTATION/A-Introduction/README.md)
+ and the [developer's manual](https://github.com/ISCPIF/tinaweb/blob/master/00.DOCUMENTATION/C-advanced/developer_manual.md).
### Copyright and license
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..ffe1c83
--- /dev/null
+++ b/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "tinaweb",
+ "version": "1.0.0",
+ "description": "TODO",
+ "main": "settings_explorerjs.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "lint": "eslint **/*.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/ISCPIF/tinaweb.git"
+ },
+ "author": "",
+ "license": "GPL-3.0",
+ "bugs": {
+ "url": "https://github.com/ISCPIF/tinaweb/issues"
+ },
+ "homepage": "https://github.com/ISCPIF/tinaweb#readme",
+ "devDependencies": {
+ "eslint": "^4.9.0",
+ "eslint-config-google": "^0.9.1"
+ }
+}
diff --git a/twmain/Tinaweb.js b/twmain/Tinaweb.js
index 9bff6e7..4a3ca9e 100644
--- a/twmain/Tinaweb.js
+++ b/twmain/Tinaweb.js
@@ -1,1345 +1,1276 @@
'use strict';
-// this class will be instanciated once (and exposed as TW.instance.selNgn)
-function SelectionEngine() {
-
- // "main" selection function for a typical event
- // - checks the cases
- // - calls the other SelectionEngine routines
- // - triggers GUI effects and state
- this.runAndEffects = function (eventTargets) {
- // 1)
- var targeted = this.SelectorEngine( {
- addvalue:TW.gui.checkBox,
- currsels: eventTargets,
- prevsels: TW.SystemState().selectionNids
- } )
- // 2)
- if(targeted.length>0) {
-
- // we still check if the selection is unchanged before create state
- let currentNids = TW.SystemState().selectionNids
- let sameNids = true
- if (currentNids.length != targeted.length) {
- sameNids = false
- }
- else {
- for (var j in currentNids) {
- if (currentNids[j] != targeted[j]) {
- sameNids = false
- break
- }
+/**
+ * This class will be instanciated once (and exposed as TW.instance.selNgn).
+ */
+class SelectionEngine {
+ /**
+ * "main" selection function for a typical event
+ * - checks the cases
+ * - calls the other SelectionEngine routines
+ * - triggers GUI effects and state
+ */
+ runAndEffects(eventTargets) {
+ let targeted = this.selectorEngine({
+ addvalue: TW.gui.checkBox,
+ currsels: eventTargets,
+ prevsels: TW.SystemState().selectionNids,
+ });
+
+ if (targeted.length > 0) {
+ // we still check if the selection is unchanged before create state
+ let currentNids = TW.SystemState().selectionNids;
+ let sameNids = true;
+ if (currentNids.length != targeted.length) {
+ sameNids = false;
+ } else {
+ for (let j in currentNids) {
+ if (currentNids[j] != targeted[j]) {
+ sameNids = false;
+ break;
}
}
-
- // iff new selection, create effects and state
- if (!sameNids) {
- this.MultipleSelection2( {nodes:targeted} )
- }
}
- // or clear previous selection, also with effects and state
- else {
- cancelSelection(false)
+
+ // iff new selection, create effects and state
+ if (!sameNids) {
+ this.multipleSelection2({ nodes: targeted });
}
+ } else { // or clear previous selection, also with effects and state
+ cancelSelection(false);
+ }
+ }
+
+ /**
+ * Creates the union of prevsels and currsels, if addvalue.
+ * Called for:
+ * clickNode selections
+ * circleArea selections
+ * searchInput selections
+ */
+ selectorEngine(args) {
+ // console.log("addvalue, prevsels, currsels", args)
+
+ if (!args) args = {};
+ if (!args.addvalue) args.addvalue = false;
+ if (!args.prevsels) args.prevsels = [];
+ if (!args.currsels) args.currsels = [];
+
+ let targeted = [];
+
+ // currsels = bunch of nodes from a click in the map
+ if (args.addvalue) {
+ // complementary select if disjoint, deselect if overlap
+ targeted = args.currsels.filter(function(item) {
+ return args.prevsels.indexOf(item) < 0;
+ }).concat(args.prevsels.filter(function(item) {
+ return args.currsels.indexOf(item) < 0;
+ }));
+ } else if (TW.gui.circleSize) { // circle select default: deselect if overlap
+ targeted = args.currsels.filter(function(item) {
+ return args.prevsels.indexOf(item) < 0;
+ });
+ } else { // other view default: only new targets
+ targeted = args.currsels;
}
- // creates the union of prevsels and currsels, if addvalue
- // called for:
- // clickNode selections
- // circleArea selections
- // searchInput selections
- this.SelectorEngine = function( args ) {
-
- // console.log("addvalue, prevsels, currsels", args)
-
- if (!args) args = {}
- if (!args.addvalue) args.addvalue = false
- if (!args.prevsels) args.prevsels = []
- if (!args.currsels) args.currsels = []
-
- var targeted = []
-
- // currsels = bunch of nodes from a click in the map
- if(args.addvalue) {
- // complementary select if disjoint, deselect if overlap
- targeted = args.currsels.filter(function (item) {
- return args.prevsels.indexOf(item) < 0;
- }).concat(args.prevsels.filter(function (item) {
- return args.currsels.indexOf(item) < 0;
- }));
- }
- // circle select default: deselect if overlap
- else if (TW.gui.circleSize) {
- targeted = args.currsels.filter(function (item) {
- return args.prevsels.indexOf(item) < 0;
- });
- }
- // other view default: only new targets
- else {
- targeted = args.currsels;
- }
-
- if(targeted.length==0) return [];
-
- return targeted;
- };
-
-
- // uses: SelectorEngine() and MultipleSelection2()
- // we assume string is normalized
- this.search_n_select = function(string) {
+ if (targeted.length == 0) return [];
- let previousSelections = TW.SystemState().selectionNids
+ return targeted;
+ };
- cancelSelection(false, {norender:true});
+ // uses: selectorEngine() and multipleSelection2()
+ // we assume string is normalized
+ searchNSelect(string) {
+ let previousSelections = TW.SystemState().selectionNids;
- if (typeof string != "string") {
- return -1 ;
- }
- else if (string.length == 0) {
- // alert("really? empty query?") ;
- return 0 ;
- }
- else {
- var id_node = '';
- var resultNids = find(string)
+ cancelSelection(false, {norender: true});
- var coincd=[]
- for(var i in resultNids) {
- coincd.push(resultNids[i])
- }
- var targeted = this.SelectorEngine( {
- addvalue:TW.gui.checkBox,
- prevsels:previousSelections,
- currsels:coincd
- } )
+ if (typeof string != 'string') {
+ return -1;
+ } else if (string.length == 0) {
+ return 0;
+ } else {
+ let resultNids = find(string);
- if(targeted.length>0) {
- // got results
- // -----------
- this.MultipleSelection2({nodes:targeted});
+ let coincd = [];
+ for (let nid of resultNids) {
+ coincd.push(nid);
+ }
+ let targeted = this.selectorEngine({
+ addvalue: TW.gui.checkBox,
+ prevsels: previousSelections,
+ currsels: coincd,
+ });
- }
- else {
- // no results
- // ----------
- // we send event for plugins
- // (eg: propose to add the missing string to the graph)
- $('#searchinput').trigger({
- type: "tw:emptyNodeSet",
- q: string,
- nodeIds: []
- });
- // console.log("\n\n\n sent [[ emptyNodes ]] \n\n ")
- }
+ if (targeted.length > 0) {
+ // got results
+ // -----------
+ this.multipleSelection2({nodes: targeted});
+ } else {
+ // no results
+ // ----------
+ // we send event for plugins
+ // (eg: propose to add the missing string to the graph)
+ $('#searchinput').trigger({
+ type: 'tw:emptyNodeSet',
+ q: string,
+ nodeIds: [],
+ });
+ }
- // anyway
- // ------
- TW.lastQuery = string ;
- $("input#searchinput").val("");
- $("input#searchinput").autocomplete( "close" );
+ // anyway
+ // ------
+ TW.lastQuery = string;
+ $('input#searchinput').val('');
+ $('input#searchinput').autocomplete('close');
- return targeted.length
- }
+ return targeted.length;
}
-
- //Util
- this.intersect_safe = function(a, b) {
- a.sort()
- b.sort()
- var ai=0, bi=0;
- var result = new Array();
-
- while( ai < a.length && bi < b.length ) {
- if (a[ai] < b[bi] ){ ai++; }
- else if (a[ai] > b[bi] ){ bi++; }
- else /* they're equal */ {
- result.push(a[ai]);
- ai++;
- bi++;
- }
- }
- return result;
+ }
+
+ /**
+ * Main function for any selecting action
+ *
+ * @nodes: eg targeted array (only ids)
+ * @noState: bool flag to avoid registering new state (useful for CTRL+Z)
+ *
+ * external usage : clickHandler, search, changeType, filters, tag click...
+ */
+ // ====================
+ multipleSelection2(args) {
+ if (!args) args = {};
+ if (isUndef(args.nodes)) args.nodes = [];
+ if (isUndef(args.noState)) args.noState = false;
+ let tMS2Start;
+
+ if (TW.conf.debug.logSelections) {
+ tMS2Start = performance.now();
+ console.log(
+ 'IN SelectionEngine.multipleSelection2:', args.nodes,
+ 'noState:', args.noState
+ );
}
- /**
- * Main function for any selecting action
- *
- * @nodes: eg targeted array (only ids)
- * @noState: bool flag to avoid registering new state (useful for CTRL+Z)
- *
- * external usage : clickHandler, search, changeType, filters, tag click...
- */
- // ====================
- this.MultipleSelection2 = function(args) {
-
- if (!args) args = {}
- if (isUndef(args.nodes)) args.nodes = []
- if (isUndef(args.noState)) args.noState = false
-
- if (TW.conf.debug.logSelections) {
- var tMS2_deb = performance.now()
- console.log(
- "IN SelectionEngine.MultipleSelection2:", args.nodes,
- "noState:", args.noState
- )
- }
-
- // deselects only the active ones (based on SystemState())
- deselectNodes()
-
- // TW.SystemState() is the present graph state
- // eg
- // {categories: ["someNodeCat"]
- // categoriesDict: {"someNodeCat":0} // where val 0 or 1 is type sem or soc
- // opposites:
- // selections:
- // type:[0]}
-
-
- var activereltypes = TW.SystemState().activereltypes
-
+ // deselects only the active ones (based on SystemState())
+ deselectNodes();
- // Dictionaries of: selection+neighbors for the new state and updateRelatedNodesPanel
- let selections = {}
+ // TW.SystemState() is the present graph state
+ // eg
+ // {categories: ["someNodeCat"]
+ // categoriesDict: {"someNodeCat":0} // where val 0 or 1 is type sem or soc
+ // opposites:
+ // selections:
+ // type:[0]}
- // detailed relations sorted by types and srcid (for state cache, deselects etc)
- let activeRelations = {}
- for (var k in activereltypes) {
- let activereltype = activereltypes[k]
- activeRelations[activereltype] = {}
- }
-
- // cumulated neighbor weights no matter what srcid (for tagCloud etc)
- let sameSideNeighbors = {}
- let oppoSideNeighbors = {}
-
- // targeted arg 'nodes' can be nid array or single nid
- var ndsids=[]
- if(args.nodes) {
- if(! $.isArray(args.nodes)) ndsids.push(args.nodes);
- else ndsids=args.nodes;
-
- for(var i in ndsids) {
- var srcnid = ndsids[i];
-
-
- for (var k in activereltypes) {
- let activereltype = activereltypes[k]
-
- if(TW.Relations[activereltype] && TW.Relations[activereltype][srcnid] ) {
- var neighs = TW.Relations[activereltype][srcnid]
-
- activeRelations[activereltype][srcnid] = {}
-
- if(neighs) {
- for(var j in neighs) {
- var tgtnid = neighs[j]
+ let activereltypes = TW.SystemState().activereltypes;
- let tgt = TW.partialGraph.graph.nodes(tgtnid)
- // highlight edges (except if n hidden or e dropped (<=> lock))
- // POSS: use sigma's own index to avoid checking if edge dropped
- if (tgt && !tgt.hidden) {
- let eid1 = srcnid+';'+tgtnid
- let eid2 = tgtnid+';'+srcnid
+ // Dictionaries of: selection+neighbors for the new state and updateRelatedNodesPanel
+ let selections = {};
- if ( (TW.Edges[eid1] && !TW.Edges[eid1].lock)
- ||
- (TW.Edges[eid2] && !TW.Edges[eid2].lock) ) {
+ // detailed relations sorted by types and srcid (for state cache, deselects etc)
+ let activeRelations = {};
- let e1 = TW.partialGraph.graph.edges(eid1)
- let e2 = TW.partialGraph.graph.edges(eid2)
-
- // since we're there we'll also keep the neighbors info
- if (typeof sameSideNeighbors[tgtnid] == 'undefined') {
-
- // except when XR because it'll already be in oppoSideNeighbors
- if (activereltype != 'XR')
- sameSideNeighbors[tgtnid]=0
- }
-
- // and the detailed info
- if (typeof activeRelations[activereltype][srcnid][tgtnid] == 'undefined') {
- activeRelations[activereltype][srcnid][tgtnid]=0
- }
+ for (let activereltype of activereltypes) {
+ activeRelations[activereltype] = {};
+ }
- // **make the edge active**
- if (e1 && !e1.hidden) {
- e1.customAttrs.activeEdge = 1;
- activeRelations[activereltype][srcnid][tgtnid] += e1.weight || 1
+ // cumulated neighbor weights no matter what srcid (for tagCloud etc)
+ let sameSideNeighbors = {};
+ let oppoSideNeighbors = {};
+
+ // targeted arg 'nodes' can be nid array or single nid
+ let ndsids = [];
+ if (args.nodes) {
+ if (! $.isArray(args.nodes)) ndsids.push(args.nodes);
+ else ndsids = args.nodes;
+
+ for (let srcnid of ndsids) {
+ for (let activereltype of activereltypes) {
+ if (TW.Relations[activereltype] && TW.Relations[activereltype][srcnid] ) {
+ let neighs = TW.Relations[activereltype][srcnid];
+
+ activeRelations[activereltype][srcnid] = {};
+
+ if (neighs) {
+ for (let tgtnid of neighs) {
+ let tgt = TW.partialGraph.graph.nodes(tgtnid);
+ // highlight edges (except if n hidden or e dropped (<=> lock))
+ // POSS: use sigma's own index to avoid checking if edge dropped
+ if (tgt && !tgt.hidden) {
+ let eid1 = srcnid+';'+tgtnid;
+ let eid2 = tgtnid+';'+srcnid;
+
+ if ((TW.Edges[eid1] && !TW.Edges[eid1].lock)
+ ||
+ (TW.Edges[eid2] && !TW.Edges[eid2].lock)) {
+ let e1 = TW.partialGraph.graph.edges(eid1);
+ let e2 = TW.partialGraph.graph.edges(eid2);
+
+ // since we're there we'll also keep the neighbors info
+ if (typeof sameSideNeighbors[tgtnid] == 'undefined') {
+ // except when XR because it'll already be in oppoSideNeighbors
+ if (activereltype != 'XR') {
+ sameSideNeighbors[tgtnid] = 0;
+ }
+ }
- // + enrich neighbor's info except if duplicate with oppoSideNeighbors
- if (activereltype != 'XR')
- sameSideNeighbors[tgtnid] += e1.weight || 1
- }
- if (e2 && !e2.hidden) {
- e2.customAttrs.activeEdge = 1;
- activeRelations[activereltype][srcnid][tgtnid] += e2.weight || 1
+ // and the detailed info
+ if (typeof activeRelations[activereltype][srcnid][tgtnid] == 'undefined') {
+ activeRelations[activereltype][srcnid][tgtnid] = 0;
+ }
- // + enrich neighbor's info except if duplicate with oppoSideNeighbors
- if (activereltype != 'XR')
- sameSideNeighbors[tgtnid] += e2.weight || 1
- }
+ // **make the edge active**
+ if (e1 && !e1.hidden) {
+ e1.customAttrs.activeEdge = 1;
+ activeRelations[activereltype][srcnid][tgtnid] += e1.weight || 1;
- // we add as neighbor to color it (except if already in targeted)
- if (!tgt.customAttrs.active) tgt.customAttrs.highlight = 1;
- }
- }
- }
+ // + enrich neighbor's info except if duplicate with oppoSideNeighbors
+ if (activereltype != 'XR') {
+ sameSideNeighbors[tgtnid] += e1.weight || 1;
}
- }
+ }
+ if (e2 && !e2.hidden) {
+ e2.customAttrs.activeEdge = 1;
+ activeRelations[activereltype][srcnid][tgtnid] += e2.weight || 1;
+
+ // + enrich neighbor's info except if duplicate with oppoSideNeighbors
+ if (activereltype != 'XR') {
+ sameSideNeighbors[tgtnid] += e2.weight || 1;
+ }
+ }
+ // we add as neighbor to color it (except if already in targeted)
+ if (!tgt.customAttrs.active) tgt.customAttrs.highlight = 1;
+ }
}
-
- // we make the selected (source) node active too
- let src = TW.partialGraph.graph.nodes(srcnid)
- src.customAttrs.active = true;
-
- // update local selections dict
- selections[ndsids[i]]=1;
+ }
}
+ }
}
- // show the button to remove selection
- $("#unselectbutton").show() ;
+ // we make the selected (source) node active too
+ let src = TW.partialGraph.graph.nodes(srcnid);
+ src.customAttrs.active = true;
- let theSelection = Object.keys(selections)
-
- // neighbors of the opposite type
- if(TW.Relations["XR"]) {
+ // update local selections dict
+ selections[ndsids[i]]=1;
+ }
+ }
- activeRelations["XR"] = {}
+ // show the button to remove selection
+ $('#unselectbutton').show();
- for(var i in theSelection) {
- let srcnid = theSelection[i]
+ let theSelection = Object.keys(selections);
- var bipaNeighs = TW.Relations["XR"][srcnid];
+ // neighbors of the opposite type
+ if (TW.Relations['XR']) {
+ activeRelations['XR'] = {};
- activeRelations["XR"][srcnid] = {}
+ for (let srcnid of theSelection) {
+ let bipaNeighs = TW.Relations['XR'][srcnid];
- for(var k in bipaNeighs) {
+ activeRelations['XR'][srcnid] = {};
- let eid1 = srcnid+';'+bipaNeighs[k]
- let eid2 = bipaNeighs[k]+';'+srcnid
+ for (let neigh of bipaNeighs) {
+ let eid1 = srcnid + ';' + neigh;
+ let eid2 = neigh + ';' + srcnid;
- var edgeWeight = 1
- if (TW.Edges[eid1]) edgeWeight = TW.Edges[eid1].weight
- else if (TW.Edges[eid2]) edgeWeight = TW.Edges[eid2].weight
+ let edgeWeight = 1;
+ if (TW.Edges[eid1]) edgeWeight = TW.Edges[eid1].weight;
+ else if (TW.Edges[eid2]) edgeWeight = TW.Edges[eid2].weight;
- if (typeof activeRelations["XR"][srcnid][bipaNeighs[k]] == "undefined") {
- activeRelations["XR"][srcnid][bipaNeighs[k]] = 0;
- }
- if (typeof oppoSideNeighbors[bipaNeighs[k]] == "undefined") {
- oppoSideNeighbors[bipaNeighs[k]] = 0 ;
- }
+ if (typeof activeRelations['XR'][srcnid][neigh] == 'undefined') {
+ activeRelations['XR'][srcnid][neigh] = 0;
+ }
+ if (typeof oppoSideNeighbors[neigh] == 'undefined') {
+ oppoSideNeighbors[neigh] = 0;
+ }
- // cumulated weight for all srcnids
- oppoSideNeighbors[bipaNeighs[k]] += edgeWeight
+ // cumulated weight for all srcnids
+ oppoSideNeighbors[neigh] += edgeWeight;
- // console.log('edgeWeight', edgeWeight)
+ // console.log('edgeWeight', edgeWeight)
- // and the details
- activeRelations["XR"][srcnid][bipaNeighs[k]] += edgeWeight
- }
- }
+ // and the details
+ activeRelations['XR'][srcnid][neigh] += edgeWeight;
}
+ }
+ }
- // Sort by descending value
- // and rewrites dict[id]: val as array[order]: {key:id, value:val}
- let oppos = []
- let same = []
+ // Sort by descending value
+ // and rewrites dict[id]: val as array[order]: {key:id, value:val}
+ let oppos = [];
+ let same = [];
- if (activeRelations["XR"]) {
- oppos = ArraySortByValue(oppoSideNeighbors, function(a,b){
- return b-a
- });
- }
+ if (activeRelations['XR']) {
+ oppos = ArraySortByValue(oppoSideNeighbors, function(a, b) {
+ return b-a;
+ });
+ }
- same = ArraySortByValue(sameSideNeighbors, function(a,b){
- return b-a
- });
+ same = ArraySortByValue(sameSideNeighbors, function(a, b) {
+ return b-a;
+ });
- if (TW.conf.debug.logSelections) {
- console.log('new states\'s selectionNids', theSelection)
- console.log('oppos', oppos)
- console.log('same', same)
- }
+ if (TW.conf.debug.logSelections) {
+ console.log('new states\'s selectionNids', theSelection);
+ console.log('oppos', oppos);
+ console.log('same', same);
+ }
- // it's a new SystemState
- if (! args.noState) {
- TW.pushGUIState( { 'sels': theSelection,
- 'rels': activeRelations } )
- }
+ // it's a new SystemState
+ if (! args.noState) {
+ TW.pushGUIState({'sels': theSelection,
+ 'rels': activeRelations});
+ }
- // we send our "gotNodeSet" event
- // (signal for plugins that a search-selection was done or a new hand picked selection)
- $('#searchinput').trigger({
- type: "tw:gotNodeSet",
- q: $("#searchinput").val(),
- nodeIds: theSelection
- });
+ // we send our "gotNodeSet" event
+ // (signal for plugins that a search-selection was done or a new hand picked selection)
+ $('#searchinput').trigger({
+ type: 'tw:gotNodeSet',
+ q: $('#searchinput').val(),
+ nodeIds: theSelection,
+ });
- // global flag
- TW.gui.selectionActive = true
+ // global flag
+ TW.gui.selectionActive = true;
- TW.partialGraph.render();
+ TW.partialGraph.render();
- updateRelatedNodesPanel( theSelection , same, oppos )
+ updateRelatedNodesPanel(theSelection, same, oppos);
- if (TW.conf.debug.logSelections) {
- var tMS2_fin = performance.now()
- console.log("end MultipleSelection2, own time:", tMS2_fin-tMS2_deb)
- }
+ if (TW.conf.debug.logSelections) {
+ let tMS2End = performance.now();
+ console.log('end multipleSelection2, own time:', tMS2End-tMS2Start);
}
-};
+ }
+}
-var TinaWebJS = function ( sigmacanvas ) {
+class TinaWebJS {
+ constructor(sigmacanvas) {
this.sigmacanvas = sigmacanvas;
this.selNgn = new SelectionEngine();
+ }
- // functions that modify the sigma module (not sigma instance!)
- this.init = function () {
+ // functions that modify the sigma module (not sigma instance!)
+ init() {
+ if (TW.conf.debug.logSettings) console.info('TW settings', TW);
- if (TW.conf.debug.logSettings) console.info("TW settings", TW)
+ let initErrMsg = null;
- let initErrMsg = null
+ if (typeof sigma == 'undefined') {
+ initErrMsg = 'no sigma library';
+ } else {
+ this.prepareSigmaCustomIndices(sigma);
- if (typeof sigma == 'undefined') {
- initErrMsg = "no sigma library"
- }
- else {
- this.prepareSigmaCustomIndices(sigma)
-
- if (TW.conf.twRendering)
- this.prepareSigmaCustomRenderers(sigma)
- }
+ if (TW.conf.twRendering) {
+this.prepareSigmaCustomRenderers(sigma);
+}
+ }
- // overriding pixelRatio is possible if we need high definition
- if (TW.conf.overSampling) {
- var realRatio = sigma.utils.getPixelRatio
- sigma.utils.getPixelRatio = function() {
- return 2 * realRatio()
- }
- }
+ // overriding pixelRatio is possible if we need high definition
+ if (TW.conf.overSampling) {
+ let realRatio = sigma.utils.getPixelRatio;
+ sigma.utils.getPixelRatio = function() {
+ return 2 * realRatio();
+ };
+ }
- // show any already existing panel
- document.getElementById("graph-panels").style.display = "block"
+ // show any already existing panel
+ document.getElementById('graph-panels').style.display = 'block';
- // grey message in the search bar from settings
- $("#searchinput").attr('placeholder', TW.conf.strSearchBar) ;
+ // grey message in the search bar from settings
+ $('#searchinput').attr('placeholder', TW.conf.strSearchBar);
- // load optional modules
- activateModules() ;
+ // load optional modules
+ activateModules();
- if (initErrMsg) {
- console.error(initErrMsg)
- }
+ if (initErrMsg) {
+ console.error(initErrMsg);
}
-
- this.prepareSigmaCustomIndices = function(sigmaModule) {
- // register direct access methods for nNodes nEdges
- sigmaModule.classes.graph.addMethod('nNodes', function() {
- return this.nodesArray.length;
- });
- sigmaModule.classes.graph.addMethod('nEdges', function() {
- return this.edgesArray.length;
- });
-
- // register an index for nodes by type and size (<= origNode.size||origNode.weight)
- sigmaModule.classes.graph.addIndex('nodesByTypeNSize', {
- constructor: function() {
- this.nodesByTypeNSize = {};
- },
- addNode: function(n) {
- // POSS: index by ntypeId = 0 for nodes0 or 1 for nodes1
- if (n.type && n.size) {
- let typekey = TW.catDict[n.type]
- let sizekey = parseFloat(n.size)
- if (!this.nodesByTypeNSize[typekey])
- this.nodesByTypeNSize[typekey] = {}
- if (!this.nodesByTypeNSize[typekey][sizekey])
- this.nodesByTypeNSize[typekey][sizekey] = {}
- this.nodesByTypeNSize[typekey][sizekey][n.id] = true
- }
- else {
- // should never happen
- console.warn("warning: couldn't add node to index ?", n)
+ }
+
+ prepareSigmaCustomIndices(sigmaModule) {
+ // register direct access methods for nNodes nEdges
+ sigmaModule.classes.graph.addMethod('nNodes', function() {
+ return this.nodesArray.length;
+ });
+ sigmaModule.classes.graph.addMethod('nEdges', function() {
+ return this.edgesArray.length;
+ });
+
+ // register an index for nodes by type and size (<= origNode.size||origNode.weight)
+ sigmaModule.classes.graph.addIndex('nodesByTypeNSize', {
+ constructor: function() {
+ this.nodesByTypeNSize = {};
+ },
+ addNode: function(n) {
+ // POSS: index by ntypeId = 0 for nodes0 or 1 for nodes1
+ if (n.type && n.size) {
+ let typekey = TW.catDict[n.type];
+ let sizekey = parseFloat(n.size);
+ if (!this.nodesByTypeNSize[typekey]) {
+this.nodesByTypeNSize[typekey] = {};
+}
+ if (!this.nodesByTypeNSize[typekey][sizekey]) {
+this.nodesByTypeNSize[typekey][sizekey] = {};
+}
+ this.nodesByTypeNSize[typekey][sizekey][n.id] = true;
+ } else {
+ // should never happen
+ console.warn('warning: couldn\'t add node to index ?', n);
+ }
+ },
+ dropNode: function(n) {
+ if (n.type && n.size) {
+ let typekey = TW.catDict[n.type];
+ delete(this.nodesByTypeNSize[typekey][n.size][n.id]);
+ }
+ },
+ });
+
+ // @typekey: a node type id among {0,1}
+ // @aSizeSelector can be:
+ // - a numeric value
+ // - a range (ordered array of 2 numeric values)
+ sigmaModule.classes.graph.addMethod('getNodesBySize', function(typekey, aSizeSelector) {
+ let res = [];
+
+ // shortcut case for commodity: entire index if no arg
+ if (isUndef(aSizeSelector)) {
+ res = this.nodesByTypeNSize[typekey];
+ } else if (isNumeric(aSizeSelector)) { // normal cases
+ let sizekey = parseFloat(aSizeSelector);
+ if (this.nodesByTypeNSize[typekey][sizekey]) {
+ res = Object.keys(this.nodesByTypeNSize[typekey][sizekey]);
+ }
+ } else if (Array.isArray(aSizeSelector)
+ && aSizeSelector.length == 2
+ && isNumeric(aSizeSelector[0])
+ && isNumeric(aSizeSelector[1])
+ ) {
+ let sizeMin = parseFloat(aSizeSelector[0]);
+ let sizeMax = parseFloat(aSizeSelector[1]);
+
+ // the available sizes
+ let sortedSizes = Object.keys(this.nodesByTypeNSize[typekey])
+ .sort(function(a, b) {
+ return a-b;
+ });
+
+ // the nodes with sizes in range
+ for (let val of sortedSizes) {
+ if (val > sizeMax) {
+ break;
}
- },
- dropNode: function(n) {
- if (n.type && n.size) {
- let typekey = TW.catDict[n.type]
- delete(this.nodesByTypeNSize[typekey][n.size][n.id])
+ if (val >= sizeMin) {
+ res = res.concat(Object.keys(this.nodesByTypeNSize[typekey][val]));
}
}
- });
-
- // @typekey: a node type id among {0,1}
- // @aSizeSelector can be:
- // - a numeric value
- // - a range (ordered array of 2 numeric values)
- sigmaModule.classes.graph.addMethod('getNodesBySize', function(typekey, aSizeSelector) {
- let res = []
+ }
+ return res;
+ });
+
+ // All nodes *in the instance* by type
+ // NB: not used at the moment but easy and perhaps very useful in future
+ // arg:
+ // @typekey: a node type id among {0,1}
+ sigmaModule.classes.graph.addMethod('getNodesByType', function(typekey) {
+ let res = [];
+ // concatenate all sizes because this detail doesn't matter to us here
+ for (let nodes of this.nodesByTypeNSize[typekey]) {
+ res = res.concat(Object.keys(nodes));
+ }
+ return res;
+ });
+ }
+
+ // register our renderers in sigma module
+ prepareSigmaCustomRenderers(sigmaModule) {
+ // £TODO group the rendering primitives it all together
+ // and perhaps move here where the preparation occurs
+ let tempo = new SigmaUtils();
+
+ // custom nodes rendering
+ // overriding the def is simplest
+ // (could also do it by type)
+ sigmaModule.canvas.nodes.def = tempo.twRender.canvas.nodes.withBorders;
+
+ // custom edges rendering registered under 'curve'
+ sigmaModule.canvas.edges.curve = tempo.twRender.canvas.edges.curve;
+ sigmaModule.canvas.edges.line = tempo.twRender.canvas.edges.line;
+
+ // custom labels rendering
+ // - based on the normal one sigma.canvas.labels.def
+ // - additionnaly supports 'active/highlight' node property (magnify x 3)
+ // - also handles 'forceLabel' property
+ sigmaModule.canvas.labels.def = tempo.twRender.canvas.labels.largeractive;
+
+ // custom hovers rendering
+ // - based on the normal one sigma.canvas.hovers.def
+ // - additionnaly magnifies all labels x 2
+ // - additionnaly supports 'active/highlight' node property (magnify x 3)
+ sigmaModule.canvas.hovers.def = tempo.twRender.canvas.hovers.largerall;
+
+ if (TW.conf.debug.logSettings) console.log('tw renderers registered in sigma module');
+ }
+
+ initSearchListeners() {
+ let selectionEngine = this.selNgn;
+
+ $('input#searchinput').autocomplete({
+ source: function(request, response) {
+ // labels initialized in settings, filled in updateSearchLabels
+ // console.log(labels.length)
+ let matches = [];
+ let matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), 'i');
+ // grep at heart
+ let results = $.grep(TW.labels, function(e) {
+ return matcher.test(e.label); // || matcher.test(e.desc);
+ });
- // shortcut case for commodity: entire index if no arg
- if (isUndef(aSizeSelector)) {
- res = this.nodesByTypeNSize[typekey]
+ if (!results.length) {
+ $('#noresults').text('Pas de résultats');
+ } else {
+ $('#noresults').empty();
}
-
- // normal cases
- else if (isNumeric(aSizeSelector)) {
- let sizekey = parseFloat(aSizeSelector)
- if (this.nodesByTypeNSize[typekey][sizekey]) {
- res = Object.keys(this.nodesByTypeNSize[typekey][sizekey])
- }
+ matches = results.slice(0, TW.conf.maxSuggestionsAutoComplete);
+ response(matches);
+ },
+ minLength: TW.conf.minLengthAutoComplete,
+
+
+ // ----------------------->8---------------------
+ // send a "no more suggestions event"
+ response: function(event, uiResponseArray) {
+ // exemple in uiResponseArray
+ // {"content":
+ // [{id: 239, label:"warning system",desc:"ISIterms.."},
+ // ...
+ // ]
+ // }
+
+ if (uiResponseArray.content.length == 0) {
+ // we send our "noAutocomplete" event
+ $('#searchinput').trigger('tw:noAutocomplete');
+ // (signal for plugins like crowdsourcing that
+ // are sensitive to autocomplete being empty)
+ // console.log("000 Event [noAutocomplete] sent from Tinaweb search")
+ } else {
+ // we send a "hasAutocomplete" event
+ // (/!\ will be sent for each typed char)
+ $('#searchinput').trigger('tw:gotAutocomplete');
+ // console.log("+++ Event [gotAutocomplete] sent from Tinaweb search")
}
- else if (Array.isArray(aSizeSelector)
- && aSizeSelector.length == 2
- && isNumeric(aSizeSelector[0])
- && isNumeric(aSizeSelector[1])
- ) {
- let sizeMin = parseFloat(aSizeSelector[0])
- let sizeMax = parseFloat(aSizeSelector[1])
-
- // the available sizes
- let sortedSizes = Object.keys(this.nodesByTypeNSize[typekey]).sort(function(a,b){return a-b})
-
- // the nodes with sizes in range
- for (var k in sortedSizes) {
- let val = sortedSizes[k]
- if (val > sizeMax) {
- break
- }
- if (val >= sizeMin) {
- res = res.concat(Object.keys(this.nodesByTypeNSize[typekey][val]))
- }
- }
+ },
+ // ----------------------->8---------------------
+ });
+
+ // Search by click on the search button, independently from autocomplete
+ $('#searchbutton').click(function() {
+ let query = normalizeString($('#searchinput').val());
+ // console.log('===\nyour query was: "'+query+'"');
+
+ // --- SelectionEngine.search() -------------------
+ // -> will call sigmaUtils.find()
+ // over sigmaUtils.getnodesIndex()
+ // -> then call this.selectorEngine
+ // and this.multipleSelection2
+ selectionEngine.searchNSelect(query);
+ // ------------------------------------------------
+ });
+
+ // Search by pressing ENTER, independently from autocomplete
+ $('#searchinput').keydown(function(e) {
+ if (e.keyCode == 13) {
+ let query = normalizeString($('#searchinput').val());
+ selectionEngine.searchNSelect(query);
+ }
+ });
+ }
+
+ // to init handlers + elts for tina GUI environment (run once on page load)
+ initGUIListeners() {
+ let body = document.getElementsByTagName('body')[0];
+ body.style.paddingTop = '41px';
+
+ // side panel width
+ if (TW.conf.sidePanelSize && TW.conf.sidePanelSize != '400px') {
+ // change stylesheet rules preferably to element style directly
+ // (this way we don't block the mobile-variants CSS effects,
+ // b/c twjs-mobile.css is loaded after twjs.css in the html)
+ if (TW.gui.sheets) {
+ if (TW.gui.sheets.main) {
+ TW.gui.sheets.main.insertRule(
+ `#sidebar {width: ${TW.conf.sidePanelSize};}`,
+ TW.gui.sheets.main.cssRules.length
+ );
+ TW.gui.sheets.main.insertRule(
+ `#sigma-contnr {right: ${TW.conf.sidePanelSize};}`,
+ TW.gui.sheets.main.cssRules.length
+ );
}
- return res;
- });
-
- // All nodes *in the instance* by type
- // NB: not used at the moment but easy and perhaps very useful in future
- // arg:
- // @typekey: a node type id among {0,1}
- sigmaModule.classes.graph.addMethod('getNodesByType', function(typekey) {
- let res = []
- // concatenate all sizes because this detail doesn't matter to us here
- for (let szk in this.nodesByTypeNSize[typekey]) {
- res = res.concat(Object.keys(this.nodesByTypeNSize[typekey][szk]))
+ if (TW.gui.sheets.panels) {
+ TW.gui.sheets.panels.insertRule(
+ `#ctlzoom {right: calc(${TW.conf.sidePanelSize} + 10px);}`,
+ TW.gui.sheets.panels.cssRules.length
+ );
}
- return res;
- });
+ } else { // otherwise we do it the easy way
+ console.warn('Couldn\'t identify twjs.css and selection-panels.css');
+ document.getElementById('sidebar').style.width = TW.conf.sidePanelSize;
+ document.getElementById('sigma-contnr').style.right = TW.conf.sidePanelSize;
+ document.getElementById('ctlzoom').style.right = `calc(${TW.conf.sidePanelSize} + 10px)`;
+ }
}
+ // tab handlers
+ $('.etabs').click(function() {
+ setTimeout(
+ function() {
+ $('#read-opposite-neighs').readmore({maxHeight: 200});
+ $('#read-sameside-neighs').readmore({maxHeight: 200});
+ },
+ 500
+ );
+ });
+
+ $('#changetype').click(function() {
+ console.log('changeTYPE click');
+ if (TW.partialGraph.isForceAtlas2Running()) {
+ sigma_utils.ourStopFA2();
+ }
- // register our renderers in sigma module
- this.prepareSigmaCustomRenderers = function(sigmaModule) {
-
- // £TODO group the rendering primitives it all together
- // and perhaps move here where the preparation occurs
- var tempo = new SigmaUtils();
-
+ changeType();
+ setTimeout(function() {
+ // $('.etabs a[href="#tagCloudXR"]').trigger('click');
+ $('#selection-tabs-contnr').easytabs('select', '#tagcloud-XR');
+ }, 500);
+ });
+
+ $('#changelevel').click(function() {
+ console.log('changeLEVEL click');
+ if (TW.partialGraph.isForceAtlas2Running()) {
+ sigma_utils.ourStopFA2();
+ }
+ changeLevel();
+ });
+
+ // sidepanel folding
+ $('#sidefold').click(function() {
+ if (window.innerWidth >= 768) {
+ let width = TW.conf.sidePanelSize || '400px';
+ // $("#ctlzoom").css('right','10px')
+ $('#ctlzoom').animate(
+ {'right': '10px'}, 'slow'
+ );
+ $('#sidebar').animate(
+ {'right': `-${width}`}, 'slow',
+ function() {
+ $('#sigma-contnr').css('right', 0);
+ $('#sidebar').hide();
+ TW.partialGraph.refresh();
+ $('#sidefold').hide();
+ $('#sideunfold').show();
+ }
+ );
+ TW.gui.foldedSide = true;
+ }
+ });
+
+ $('#sideunfold').click(function() {
+ if (window.innerWidth >= 768) {
+ let width = TW.conf.sidePanelSize || '400px';
+ $('#sidebar').show();
+ $('#sidebar').animate(
+ {'right': 0}, 'slow',
+ function() {
+ $('#sigma-contnr').css('right', width);
+ TW.partialGraph.refresh();
+ $('#sideunfold').hide();
+ $('#sidefold').show();
+ $('#ctlzoom').css('right', `calc(${width} + 10px)`);
+ }
+ );
+ TW.gui.foldedSide = false;
+ }
+ });
- // custom nodes rendering
- // overriding the def is simplest
- // (could also do it by type)
- sigmaModule.canvas.nodes.def = tempo.twRender.canvas.nodes.withBorders
+ $('#tips').html(getTips());
- // custom edges rendering registered under 'curve'
- sigmaModule.canvas.edges.curve = tempo.twRender.canvas.edges.curve
- sigmaModule.canvas.edges.line = tempo.twRender.canvas.edges.line
+ // we start with no selection
+ $('#selection-tabs-contnr').hide();
- // custom labels rendering
- // - based on the normal one sigma.canvas.labels.def
- // - additionnaly supports 'active/highlight' node property (magnify x 3)
- // - also handles 'forceLabel' property
- sigmaModule.canvas.labels.def = tempo.twRender.canvas.labels.largeractive
+ // #saveAs => toggle #savemodal initialized in html + bootstrap-native
- // custom hovers rendering
- // - based on the normal one sigma.canvas.hovers.def
- // - additionnaly magnifies all labels x 2
- // - additionnaly supports 'active/highlight' node property (magnify x 3)
- sigmaModule.canvas.hovers.def = tempo.twRender.canvas.hovers.largerall
+ // button CENTER
+ $('#lensButton').click(function() {
+ // new sigma.js
+ TW.partialGraph.camera.goTo({x: 0, y: 0, ratio: 1.2});
+ });
- if (TW.conf.debug.logSettings) console.log('tw renderers registered in sigma module')
+ if (!TW.conf.colorByAtt) {
+ $('#setcolorsMenu').hide();
}
- this.initSearchListeners = function () {
-
- var selectionEngine = this.selNgn
-
- $('input#searchinput').autocomplete({
- source: function(request, response) {
- // labels initialized in settings, filled in updateSearchLabels
- // console.log(labels.length)
- var matches = [];
- var matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i");
- // grep at heart
- var results = $.grep(TW.labels, function(e) {
- return matcher.test(e.label); //|| matcher.test(e.desc);
- });
-
- if (!results.length) {
- $("#noresults").text("Pas de résultats");
- } else {
- $("#noresults").empty();
- }
- matches = results.slice(0, TW.conf.maxSuggestionsAutoComplete);
- response(matches);
-
- },
- minLength: TW.conf.minLengthAutoComplete,
-
-
- // ----------------------->8---------------------
- // send a "no more suggestions event"
- response: function (event, ui_response_array) {
- // exemple in ui_response_array
- // {"content":
- // [{id: 239, label:"warning system",desc:"ISIterms.."},
- // ...
- // ]
- // }
-
- if (ui_response_array.content.length == 0) {
- // we send our "noAutocomplete" event
- $('#searchinput').trigger("tw:noAutocomplete");
- // (signal for plugins like crowdsourcing that
- // are sensitive to autocomplete being empty)
- // console.log("000 Event [noAutocomplete] sent from Tinaweb search")
- }
- else {
- // we send a "hasAutocomplete" event
- // (/!\ will be sent for each typed char)
- $('#searchinput').trigger("tw:gotAutocomplete");
- // console.log("+++ Event [gotAutocomplete] sent from Tinaweb search")
- }
- }
- // ----------------------->8---------------------
- });
+ if (TW.conf.fa2Available) {
+ $('#layoutButton').click(function() {
+ sigma_utils.smartForceAtlas({'manual': true});
+ });
+ } else {
+ $('#layoutButton').hide();
+ }
- // Search by click on the search button, independently from autocomplete
- $("#searchbutton").click(function() {
- var query = normalizeString($("#searchinput").val())
- // console.log('===\nyour query was: "'+query+'"');
-
- // --- SelectionEngine.search() -------------------
- // -> will call sigmaUtils.find()
- // over sigmaUtils.getnodesIndex()
- // -> then call this.SelectorEngine
- // and this.MultipleSelection2
- selectionEngine.search_n_select(query)
- // ------------------------------------------------
- });
+ if (TW.conf.disperseAvailable) {
+ $('#noverlapButton').click(function() {
+ if (! TW.partialGraph.isNoverlapRunning()) {
+ // show waiting cursor on page and button
+ TW.gui.elHtml.classList.add('waiting');
+ this.style.cursor = 'wait';
+ // and waiting icon
+ this.insertBefore(createWaitIcon('noverlapwait'), this.children[0]);
+
+ // reconfigure to account for nodesizes (if current sizes up => margin needs up)
+ let sizeFactor = Math.max.apply(null, TW.gui.sizeRatios);
+ TW.gui.noverlapConf.nodeMargin = .5 * sizeFactor;
+ TW.gui.noverlapConf.scaleNodes = 1.5 * sizeFactor;
+ TW.partialGraph.configNoverlap(TW.gui.noverlapConf);
+ let listener = TW.partialGraph.startNoverlap();
+ let noverButton = this;
+ listener.bind('stop', function(event) {
+ let stillRunning = document.getElementById('noverlapwait');
+ if (stillRunning) {
+ reInitFa2({
+ localZoneSettings: !TW.SystemState().level,
+ skipHidden: !TW.conf.stablePositions,
+ callback: function() {
+ console.debug('noverlap: updated fa2 positions');
+ },
+ });
- // Search by pressing ENTER, independently from autocomplete
- $("#searchinput").keydown(function (e) {
- if (e.keyCode == 13) {
- var query = normalizeString($("#searchinput").val())
- selectionEngine.search_n_select(query)
+ TW.gui.elHtml.classList.remove('waiting');
+ noverButton.style.cursor = 'auto';
+ stillRunning.remove();
}
- });
- }
+ });
- // to init handlers + elts for tina GUI environment (run once on page load)
- this.initGUIListeners = function () {
-
- var body=document.getElementsByTagName('body')[0];
- body.style.paddingTop="41px";
-
-
- // side panel width
- if(TW.conf.sidePanelSize && TW.conf.sidePanelSize != "400px") {
- // change stylesheet rules preferably to element style directly
- // (this way we don't block the mobile-variants CSS effects,
- // b/c twjs-mobile.css is loaded after twjs.css in the html)
- if (TW.gui.sheets) {
- if (TW.gui.sheets.main) {
- TW.gui.sheets.main.insertRule(
- `#sidebar {width: ${TW.conf.sidePanelSize};}`,
- TW.gui.sheets.main.cssRules.length
- )
- TW.gui.sheets.main.insertRule(
- `#sigma-contnr {right: ${TW.conf.sidePanelSize};}`,
- TW.gui.sheets.main.cssRules.length
- )
- }
- if (TW.gui.sheets.panels) {
- TW.gui.sheets.panels.insertRule(
- `#ctlzoom {right: calc(${TW.conf.sidePanelSize} + 10px);}`,
- TW.gui.sheets.panels.cssRules.length
- )
- }
- }
- // otherwise we do it the easy way
- else {
- console.warn("Couldn't identify twjs.css and selection-panels.css")
- document.getElementById('sidebar').style.width = TW.conf.sidePanelSize
- document.getElementById('sigma-contnr').style.right = TW.conf.sidePanelSize
- document.getElementById('ctlzoom').style.right = `calc(${TW.conf.sidePanelSize} + 10px)`
- }
+ return;
}
+ });
+ } else {
+ $('#noverlapButton').hide();
+ }
- // tab handlers
- $('.etabs').click(function(){
- setTimeout(
- function() {
- $("#read-opposite-neighs").readmore({maxHeight:200});
- $("#read-sameside-neighs").readmore({maxHeight:200});
- },
- 500
- )
- });
-
- $("#changetype").click(function(){
- console.log("changeTYPE click");
- if (TW.partialGraph.isForceAtlas2Running())
- sigma_utils.ourStopFA2();
-
- changeType();
- setTimeout(function(){
- // $('.etabs a[href="#tagCloudXR"]').trigger('click');
- $('#selection-tabs-contnr').easytabs('select', '#tagcloud-XR')
- },500)
- });
-
- $("#changelevel").click(function(){
- console.log("changeLEVEL click");
- if (TW.partialGraph.isForceAtlas2Running())
- sigma_utils.ourStopFA2();
- changeLevel();
- });
-
- // sidepanel folding
- $('#sidefold').click(function(){
- if (window.innerWidth >= 768) {
- let width = TW.conf.sidePanelSize || '400px'
- // $("#ctlzoom").css('right','10px')
- $("#ctlzoom").animate(
- {"right": "10px"}, "slow"
- )
- $("#sidebar").animate(
- {"right":`-${width}`}, "slow",
- function(){
- $("#sigma-contnr").css('right',0)
- $("#sidebar").hide()
- TW.partialGraph.refresh()
- $("#sidefold").hide()
- $("#sideunfold").show()
- }
- );
- TW.gui.foldedSide = true
- }
- })
-
- $('#sideunfold').click(function(){
- if (window.innerWidth >= 768) {
- let width = TW.conf.sidePanelSize || '400px'
- $("#sidebar").show()
- $("#sidebar").animate(
- {"right": 0}, "slow",
- function(){
- $("#sigma-contnr").css('right', width)
- TW.partialGraph.refresh()
- $("#sideunfold").hide()
- $("#sidefold").show()
- $("#ctlzoom").css('right',`calc(${width} + 10px)`)
- }
- );
- TW.gui.foldedSide = false
- }
- })
-
- $("#tips").html(getTips());
-
- // we start with no selection
- $("#selection-tabs-contnr").hide();
-
- // #saveAs => toggle #savemodal initialized in html + bootstrap-native
-
- // button CENTER
- $("#lensButton").click(function () {
- // new sigma.js
- TW.partialGraph.camera.goTo({x:0, y:0, ratio:1.2})
- });
-
- if (!TW.conf.colorByAtt) {
- $("#setcolorsMenu").hide()
- }
- if (TW.conf.fa2Available) {
- $("#layoutButton").click(function () {
- sigma_utils.smartForceAtlas({'manual': true})
- });
+ $('#edges-switch').click(function() {
+ sigma_utils.toggleEdges(this.checked);
+ });
+
+
+ // Cursor Size slider
+ TW.gui.circleSlider = $('#unranged-value').freshslider({
+ step: 1,
+ min: TW.conf.circleSizeMin,
+ max: TW.conf.circleSizeMax,
+ value: TW.gui.circleSize,
+ onchange: function(value) {
+ // console.log("en cursorsize: "+value);
+ TW.gui.circleSize = value;
+ },
+ });
+
+ // double click on cursor selector slider => set it to 0
+ $('#areacircle-size').dblclick(function() {
+ TW.gui.circleSlider.setValue(0);
+ });
+
+ // costly entire refresh (~400ms) only after stopped resizing for 3s
+ // NB: rescale middleware already reacted and, except for large win size changes, it handles the resize fine
+ // (so this fragment is only to accomodate the large changes)
+ let winResizeTimeout = null;
+ window.addEventListener('resize', function(ev) {
+ if (winResizeTimeout) {
+ clearTimeout(winResizeTimeout);
+ }
+ winResizeTimeout = setTimeout(function() {
+ if (window.TW.partialGraph && window.TW.partialGraph.refresh) {
+ window.TW.partialGraph.refresh();
+ // console.log('did refresh')
}
- else {
- $("#layoutButton").hide()
+ if (TW.gui.elHtml.classList) {
+ TW.gui.elHtml.classList.remove('waiting');
}
- if (TW.conf.disperseAvailable) {
- $("#noverlapButton").click(function () {
- if(! TW.partialGraph.isNoverlapRunning()) {
- // show waiting cursor on page and button
- TW.gui.elHtml.classList.add('waiting');
- this.style.cursor = 'wait'
- // and waiting icon
- this.insertBefore(createWaitIcon('noverlapwait'), this.children[0])
-
- // reconfigure to account for nodesizes (if current sizes up => margin needs up)
- let sizeFactor = Math.max.apply(null, TW.gui.sizeRatios)
- TW.gui.noverlapConf.nodeMargin = .5 * sizeFactor
- TW.gui.noverlapConf.scaleNodes = 1.5 * sizeFactor
- TW.partialGraph.configNoverlap(TW.gui.noverlapConf)
- var listener = TW.partialGraph.startNoverlap();
- var noverButton = this
- listener.bind('stop', function(event) {
- var stillRunning = document.getElementById('noverlapwait')
- if (stillRunning) {
- reInitFa2({
- localZoneSettings: !TW.SystemState().level,
- skipHidden: !TW.conf.stablePositions,
- callback: function() {console.debug("noverlap: updated fa2 positions")}
- })
-
- TW.gui.elHtml.classList.remove('waiting');
- noverButton.style.cursor = 'auto'
- stillRunning.remove()
- }
- });
- return;
- }
- });
+ // monitor passing out of or into smaller width
+ // (along with twjs-mobile.css and selection-panels.mobile.css)
+ if (ev.target.innerWidth < 768 && !TW.gui.smallView) {
+ TW.gui.smallView = true;
+ cssReset();
+ $('#sideunfold,#sidefold').hide();
+ } else if (ev.target.innerWidth >= 768 && TW.gui.smallView) {
+ TW.gui.smallView = false;
+ foldingReset();
}
- else {
- $("#noverlapButton").hide()
- }
-
-
- $("#edges-switch").click(function () {
- sigma_utils.toggleEdges(this.checked)
- });
-
-
- //Cursor Size slider
- TW.gui.circleSlider = $("#unranged-value").freshslider({
- step: 1,
- min:TW.conf.circleSizeMin,
- max:TW.conf.circleSizeMax,
- value:TW.gui.circleSize,
- onchange:function(value){
- // console.log("en cursorsize: "+value);
- TW.gui.circleSize=value;
- }
- });
-
- // double click on cursor selector slider => set it to 0
- $("#areacircle-size").dblclick(function(){
- TW.gui.circleSlider.setValue(0)
- });
-
- // costly entire refresh (~400ms) only after stopped resizing for 3s
- // NB: rescale middleware already reacted and, except for large win size changes, it handles the resize fine
- // (so this fragment is only to accomodate the large changes)
- var winResizeTimeout = null
- window.addEventListener('resize', function(ev){
- if (winResizeTimeout) {
- clearTimeout(winResizeTimeout)
- }
- winResizeTimeout = setTimeout(function() {
-
- if (window.TW.partialGraph && window.TW.partialGraph.refresh) {
- window.TW.partialGraph.refresh()
- // console.log('did refresh')
- }
- if (TW.gui.elHtml.classList) {
- TW.gui.elHtml.classList.remove('waiting');
- }
-
-
- // monitor passing out of or into smaller width
- // (along with twjs-mobile.css and selection-panels.mobile.css)
- if (ev.target.innerWidth < 768 && !TW.gui.smallView) {
- TW.gui.smallView = true
- cssReset()
- $('#sideunfold,#sidefold').hide()
- }
- else if (ev.target.innerWidth >= 768 && TW.gui.smallView) {
- TW.gui.smallView = false
- foldingReset()
- }
-
- }, 1000)
- }, true)
+ }, 1000);
+ }, true);
- // general listener: shift key in the window <=> add to selection
- $(document).on('keyup keydown', function(e){
- // changes the global boolean ("add node to selection" status) if keydown and SHIFT
- TW.gui.checkBox = TW.gui.manuallyChecked || e.shiftKey
+ // 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
+ TW.gui.checkBox = TW.gui.manuallyChecked || e.shiftKey;
- // show it in the real TW.gui.checkBox too
- $('#checkboxdiv').prop("checked", TW.gui.manuallyChecked || e.shiftKey)
+ // show it in the real TW.gui.checkBox too
+ $('#checkboxdiv').prop('checked', TW.gui.manuallyChecked || e.shiftKey);
- // also listen for CTRL+Z 17 + 90
- if (e.type == "keyup"
- && (e.which == 90 || e.keyCode == 90) && e.ctrlKey
- && TW.states.length > 2
- ) {
+ // also listen for CTRL+Z 17 + 90
+ if (e.type == 'keyup'
+ && (e.which == 90 || e.keyCode == 90) && e.ctrlKey
+ && TW.states.length > 2
+ ) {
+ if (timeoutIdCTRLZ) {
+ window.clearTimeout(timeoutIdCTRLZ);
+ }
- if (timeoutIdCTRLZ) {
- window.clearTimeout(timeoutIdCTRLZ)
+ let timeoutIdCTRLZ = window.setTimeout(function() {
+ // console.log("pop state")
+ let previousState = TW.states.pop();
+
+ deselectNodes(previousState);
+
+ let returningState = TW.SystemState();
+
+ // restoring level (will also restore selections)
+ if (returningState.level != previousState.level) {
+ changeLevel(returningState);
+ } else {
+ // restoring selection
+ if (returningState.selectionNids.length) {
+ TW.gui.selectionActive = true;
+ // changes active/highlight and refresh
+ // POSS turn the nostate version into a select fun like deselect (ie no state, no refresh)
+ TW.instance.selNgn.multipleSelection2({
+ nodes: returningState.selectionNids,
+ noState: true,
+ });
+ } else {
+ TW.gui.selectionActive = false;
+ TW.partialGraph.refresh();
}
-
- var timeoutIdCTRLZ = window.setTimeout(function() {
- // console.log("pop state")
- let previousState = TW.states.pop()
-
- deselectNodes(previousState)
-
- let returningState = TW.SystemState()
-
- // restoring level (will also restore selections)
- if (returningState.level != previousState.level) {
- changeLevel(returningState)
- }
- else {
- // restoring selection
- if (returningState.selectionNids.length) {
- TW.gui.selectionActive = true
- // changes active/highlight and refresh
- // POSS turn the nostate version into a select fun like deselect (ie no state, no refresh)
- TW.instance.selNgn.MultipleSelection2({
- nodes: returningState.selectionNids,
- noState: true
- })
- }
- else {
- TW.gui.selectionActive = false
- TW.partialGraph.refresh()
- }
- }
- }, 100)
}
- } );
-
- } // finish envListeners
-
-
- // to init local, instance-related listeners (need to run at new sigma instance)
- // args: @partialGraph = a sigma instance
- // accessed globals: TW.Facets
- this.initSigmaListeners = function(partialGraph, initialActivetypes, initialActivereltypes, optionalRelDocsConf) {
-
- // console.log("initSigmaListeners TW.categories / types array / reltypeskeys array: ", TW.categories, initialActivetypes, initialActivereltypes)
-
- var selectionEngine = this.selNgn
-
- // changetype button
- if (TW.categories.length == 1) {
- $("#changetype").hide();
- }
- else {
- $("#changetype").show();
+ }, 100);
}
+ } );
+ }
+
+
+ // to init local, instance-related listeners (need to run at new sigma instance)
+ // args: @partialGraph = a sigma instance
+ // accessed globals: TW.Facets
+ initSigmaListeners(partialGraph, initialActivetypes, initialActivereltypes,
+ optionalRelDocsConf) {
+ let selectionEngine = this.selNgn;
+
+ // changetype button
+ if (TW.categories.length == 1) {
+ $('#changetype').hide();
+ } else {
+ $('#changetype').show();
+ }
- // sigma events bindings
- // ---------------------
+ // sigma events bindings
+ // ---------------------
- // cf. https://github.com/jacomyal/sigma.js/wiki/Events-API
+ // cf. https://github.com/jacomyal/sigma.js/wiki/Events-API
- // cases:
- // 'click' - simple click, early event
- // used for area (with global: TW.gui.circleSize)
- // 'clickNode'- simple click, second event if one node
+ // cases:
+ // 'click' - simple click, early event
+ // used for area (with global: TW.gui.circleSize)
+ // 'clickNode'- simple click, second event if one node
- // when circle area select
- // ========================
- // 1st event, even before we know if there are nodes
- partialGraph.bind('click', function(e) {
- // console.log("sigma click event e", e)
+ // when circle area select
+ // ========================
+ // 1st event, even before we know if there are nodes
+ partialGraph.bind('click', function(e) {
+ // console.log("sigma click event e", e)
- // case with a selector circle cursor handled here
- if (TW.gui.circleSize > 0) {
- // actual click position, but in graph coords
- var x = e.data.x
- var y = e.data.y
+ // case with a selector circle cursor handled here
+ if (TW.gui.circleSize > 0) {
+ // actual click position, but in graph coords
+ let x = e.data.x;
+ let y = e.data.y;
- // convert
- var camCoords = TW.cam.cameraPosition(x,y)
+ // convert
+ let camCoords = TW.cam.cameraPosition(x, y);
- // retrieve area nodes, using indexed quadtree and global TW.gui.circleSize
- var circleNodes = circleGetAreaNodes(
- camCoords.x,
- camCoords.y
- )
+ // retrieve area nodes, using indexed quadtree and global TW.gui.circleSize
+ let circleNodes = circleGetAreaNodes(
+ camCoords.x,
+ camCoords.y
+ );
- selectionEngine.runAndEffects(circleNodes)
- }
- })
+ selectionEngine.runAndEffects(circleNodes);
+ }
+ });
- // when one node and normal click
- // ===============================
- partialGraph.bind('clickNode', function(e) {
- // console.log("clickNode event e", e)
+ // when one node and normal click
+ // ===============================
+ partialGraph.bind('clickNode', function(e) {
+ // console.log("clickNode event e", e)
- // new sigma.js gives easy access to clicked node!
- var theNodeId = e.data.node.id
+ // new sigma.js gives easy access to clicked node!
+ let theNodeId = e.data.node.id;
- if (TW.gui.circleSize == 0) {
- selectionEngine.runAndEffects([theNodeId])
- }
- // case with a selector circle cursor handled
- // just before, at click event
- })
+ if (TW.gui.circleSize == 0) {
+ selectionEngine.runAndEffects([theNodeId]);
+ }
+ // case with a selector circle cursor handled
+ // just before, at click event
+ });
- // doubleClick creates new meso view around clicked node
- partialGraph.bind('doubleClickNode', function(e) {
- // /!\ doubleClick will also fire 2 singleClick events /!\
- //
- // https://github.com/jacomyal/sigma.js/issues/208
- // https://github.com/jacomyal/sigma.js/issues/506
- //
- // (order: clickNode, doubleClickNode, clickNode)
- // 1st 2nd (NOW) 3rd
+ // doubleClick creates new meso view around clicked node
+ partialGraph.bind('doubleClickNode', function(e) {
+ // /!\ doubleClick will also fire 2 singleClick events /!\
+ //
+ // https://github.com/jacomyal/sigma.js/issues/208
+ // https://github.com/jacomyal/sigma.js/issues/506
+ //
+ // (order: clickNode, doubleClickNode, clickNode)
+ // 1st 2nd (NOW) 3rd
- // so if this was also a new selection, the 1st clickNode did handle it
- // => we just create the new zoom level
+ // so if this was also a new selection, the 1st clickNode did handle it
+ // => we just create the new zoom level
- // A - create new zoom level state
- TW.pushGUIState({ level: false })
- // NB2: we never switch back to macro level from doubleClick
+ // A - create new zoom level state
+ TW.pushGUIState({level: false});
+ // NB2: we never switch back to macro level from doubleClick
- // B - apply it without changing state
- changeLevel(TW.SystemState())
- })
+ // B - apply it without changing state
+ changeLevel(TW.SystemState());
+ });
- // when click in the empty background
- // ==================================
- if (TW.conf.deselectOnclickStage) {
- partialGraph.bind('clickStage', function(e) {
- // console.log("clickStage event e", e)
+ // when click in the empty background
+ // ==================================
+ if (TW.conf.deselectOnclickStage) {
+ partialGraph.bind('clickStage', function(e) {
+ // console.log("clickStage event e", e)
- if (! e.data.captor.isDragging
+ if (! e.data.captor.isDragging
&& TW.gui.selectionActive
&& ! TW.gui.circleSize) {
+ // we clear selections and all its effects
+ cancelSelection(false);
+ }
+ });
+ }
- // we clear selections and all its effects
- cancelSelection(false);
- }
- })
- }
+ // for all TW.cam.goTo (move/zoom) events
+ // ===============
+ TW.cam.bind('coordinatesUpdated', function(e) {
+ $('#zoomSlider').slider('value', Math.log(1/(TW.cam.ratio+zoomSliRangeRatio)));
+ });
+
+
+ // dragNodes plugin
+ if (TW.conf.dragNodesAvailable) {
+ let dragListener = sigma.plugins.dragNodes(partialGraph, partialGraph.renderers[0]);
+
+ dragListener.bind('dragend', function(dragEndEvent) {
+ let mouseEvent = dragEndEvent.data.captor;
+ if (mouseEvent.ctrlKey) {
+ // update FA2 positions array
+ reInitFa2({
+ callback: function() {
+console.debug('dragNodes: updated fa2 positions');
+},
+ });
+ }
+ });
- // for all TW.cam.goTo (move/zoom) events
- // ===============
- var zoomTimeoutId = null
- TW.cam.bind('coordinatesUpdated', function(e) {
- $("#zoomSlider").slider("value",Math.log(1/(TW.cam.ratio+zoomSliRangeRatio)))
- })
-
-
- // dragNodes plugin
- if (TW.conf.dragNodesAvailable) {
- var dragListener = sigma.plugins.dragNodes(partialGraph, partialGraph.renderers[0]);
-
- dragListener.bind("dragend", function(dragEndEvent){
- let mouseEvent = dragEndEvent.data.captor
- if (mouseEvent.ctrlKey) {
- // update FA2 positions array
- reInitFa2({
- callback: function() {console.debug("dragNodes: updated fa2 positions")}
- })
- }
- })
-
- // intercept dragNodes events if not CTRL+click
- document.getElementById('sigma-contnr').addEventListener(
- 'mousemove',
- function(ev) {
- if (!ev.ctrlKey) {
- ev.stopPropagation()
- }
+ // intercept dragNodes events if not CTRL+click
+ document.getElementById('sigma-contnr').addEventListener(
+ 'mousemove',
+ function(ev) {
+ if (!ev.ctrlKey) {
+ ev.stopPropagation();
}
- )
- }
-
- // ---------------------------------------------------------------------
+ }
+ );
+ }
- // POSS: bind to captors (0=>mouse, 1=>touch)
- // TW.rend.captors[0].bind('mousemove', function(e) {
- // console.log("mousemove event e", e.data.node)
- //
- // })
+ // ---------------------------------------------------------------------
- // ---------------------------------------------------------------------
+ // POSS: bind to captors (0=>mouse, 1=>touch)
+ // TW.rend.captors[0].bind('mousemove', function(e) {
+ // console.log("mousemove event e", e.data.node)
+ //
+ // })
- // raw events (non-sigma): handlers attached to the container
- // ==========
- $("#sigma-contnr")
- .mousemove(function(e){
- if(!isUndef(partialGraph)) {
- // show/move selector circle cursor
- if(TW.gui.circleSize>0) circleTrackMouse(e);
- }
- })
-
- // POSSible for the future: add tools to contextmenu
- // .contextmenu(function(){
- // return false;
- // })
-
- // sliders events
- // ==============
-
- var zoomSliRangeRatio = TW.conf.zoomMin/TW.conf.zoomMax
- var zoomSliBoundaryTop = Math.log(1/(TW.conf.zoomMin+zoomSliRangeRatio))
- var zoomSliBoundaryBot = Math.log(1/(TW.conf.zoomMax+zoomSliRangeRatio))
- var zoomSliOrigin = Math.log(1/(TW.cam.ratio+zoomSliRangeRatio))
- var zoomSliStep = (zoomSliBoundaryTop - zoomSliBoundaryBot)/50
-
- $("#zoomSlider").slider({
- orientation: "vertical",
-
- // new sigma.js current zoom ratio
- value: partialGraph.camera.ratio,
- min: zoomSliBoundaryBot, // ex log(1/(ZOOM-OUT_RATIO+k))
- max: zoomSliBoundaryTop, // ex log(1/(ZOOM-IN_RATIO+k))
- // where k is the ratio of the full range
-
- // range: true,
- step: zoomSliStep,
- value: zoomSliOrigin,
- slide: function( event, ui ) {
- TW.partialGraph.camera.goTo({
- // we use 1/e^x -k transform for result like logscale on 1/x
- ratio: 1/Math.exp(ui.value) - zoomSliRangeRatio
- });
- }
- });
+ // ---------------------------------------------------------------------
- $("#zoomPlusButton").click(function () {
- var newRatio = TW.cam.ratio * .75
- if (newRatio >= TW.conf.zoomMin) {
- // triggers coordinatesUpdated which sets the slider cursor
- partialGraph.camera.goTo({ratio: newRatio});
- return false;
- }
- });
-
- $("#zoomMinusButton").click(function () {
- var newRatio = TW.cam.ratio * 1.25
- if (newRatio <= TW.conf.zoomMax) {
- // triggers coordinatesUpdated which sets the slider cursor
- partialGraph.camera.goTo({ratio: newRatio});
- return false;
+ // raw events (non-sigma): handlers attached to the container
+ // ==========
+ $('#sigma-contnr')
+ .mousemove(function(e) {
+ if (!isUndef(partialGraph)) {
+ // show/move selector circle cursor
+ if (TW.gui.circleSize>0) circleTrackMouse(e);
}
});
- // initialize selection tabs (order: show => easytabs => hide => readmore)
- if (TW.categories.length == 1) {
- $('#selection-tabs-contnr').easytabs({
- updateHash:false,
- defaultTab: 'li#tabsameside'
+ // POSSible for the future: add tools to contextmenu
+ // .contextmenu(function(){
+ // return false;
+ // })
+
+ // sliders events
+ // ==============
+
+ let zoomSliRangeRatio = TW.conf.zoomMin/TW.conf.zoomMax;
+ let zoomSliBoundaryTop = Math.log(1/(TW.conf.zoomMin+zoomSliRangeRatio));
+ let zoomSliBoundaryBot = Math.log(1/(TW.conf.zoomMax+zoomSliRangeRatio));
+ let zoomSliOrigin = Math.log(1/(TW.cam.ratio+zoomSliRangeRatio));
+ let zoomSliStep = (zoomSliBoundaryTop - zoomSliBoundaryBot)/50;
+
+ $('#zoomSlider').slider({
+ orientation: 'vertical',
+
+ // new sigma.js current zoom ratio
+ value: partialGraph.camera.ratio,
+ min: zoomSliBoundaryBot, // ex log(1/(ZOOM-OUT_RATIO+k))
+ max: zoomSliBoundaryTop, // ex log(1/(ZOOM-IN_RATIO+k))
+ // where k is the ratio of the full range
+
+ // range: true,
+ step: zoomSliStep,
+ value: zoomSliOrigin,
+ slide: function(event, ui) {
+ TW.partialGraph.camera.goTo({
+ // we use 1/e^x -k transform for result like logscale on 1/x
+ ratio: 1/Math.exp(ui.value) - zoomSliRangeRatio,
});
- $("#taboppos").hide();
- $("#read-sameside-neighs").readmore({maxHeight:200});
+ },
+ });
+
+ $('#zoomPlusButton').click(function() {
+ let newRatio = TW.cam.ratio * .75;
+ if (newRatio >= TW.conf.zoomMin) {
+ // triggers coordinatesUpdated which sets the slider cursor
+ partialGraph.camera.goTo({ratio: newRatio});
+ return false;
}
- else {
- $("#taboppos").show();
- $('#selection-tabs-contnr').easytabs({
- updateHash:false,
- defaultTab: 'li#taboppos'
- });
- $("#read-sameside-neighs").readmore({maxHeight:200});
- $("#read-opposite-neighs").readmore({maxHeight:200});
+ });
+
+ $('#zoomMinusButton').click(function() {
+ let newRatio = TW.cam.ratio * 1.25;
+ if (newRatio <= TW.conf.zoomMax) {
+ // triggers coordinatesUpdated which sets the slider cursor
+ partialGraph.camera.goTo({ratio: newRatio});
+ return false;
}
+ });
- // initialize reldocs tabs if declared in optionalRelDocsConf
- // (optionalRelDocsConf function-scope name of TW.currentRelDocsDBs)
- if (TW.conf.getRelatedDocs && optionalRelDocsConf) {
- resetTabs(initialActivetypes, optionalRelDocsConf)
- }
+ // initialize selection tabs (order: show => easytabs => hide => readmore)
+ if (TW.categories.length == 1) {
+ $('#selection-tabs-contnr').easytabs({
+ updateHash: false,
+ defaultTab: 'li#tabsameside',
+ });
+ $('#taboppos').hide();
+ $('#read-sameside-neighs').readmore({maxHeight: 200});
+ } else {
+ $('#taboppos').show();
+ $('#selection-tabs-contnr').easytabs({
+ updateHash: false,
+ defaultTab: 'li#taboppos',
+ });
+ $('#read-sameside-neighs').readmore({maxHeight: 200});
+ $('#read-opposite-neighs').readmore({maxHeight: 200});
+ }
- // defaultColoring: an attribute name to immediately apply color with
- let madeDefaultColor = false
- if (TW.conf.defaultColoring) {
- let colMethodName, colMethod
- if (TW.facetOptions[TW.conf.defaultColoring]) {
- colMethodName = TW.gui.colorFuns[TW.facetOptions[TW.conf.defaultColoring]['col']]
- }
- if (! colMethodName) {
- if(TW.conf.defaultColoring.indexOf("clust")>-1||TW.conf.defaultColoring.indexOf("class")>-1) {
- // for classes and clusters
- colMethod = "clusterColoring"
- }
- else {
- colMethod = "heatmapColoring"
- }
+ // initialize reldocs tabs if declared in optionalRelDocsConf
+ // (optionalRelDocsConf function-scope name of TW.currentRelDocsDBs)
+ if (TW.conf.getRelatedDocs && optionalRelDocsConf) {
+ resetTabs(initialActivetypes, optionalRelDocsConf);
+ }
+
+ // defaultColoring: an attribute name to immediately apply color with
+ let madeDefaultColor = false;
+ if (TW.conf.defaultColoring) {
+ let colMethodName;
+ let colMethod;
+ if (TW.facetOptions[TW.conf.defaultColoring]) {
+ colMethodName = TW.gui.colorFuns[TW.facetOptions[TW.conf.defaultColoring]['col']];
+ }
+ if (! colMethodName) {
+ if (TW.conf.defaultColoring.indexOf('clust')>-1||TW.conf.defaultColoring.indexOf('class')>-1) {
+ // for classes and clusters
+ colMethod = 'clusterColoring';
+ } else {
+ colMethod = 'heatmapColoring';
}
+ }
- // retrieve the actual function and if there, try and run it
- colMethod = window[colMethodName]
- if (colMethod && typeof colMethod == "function") {
- try {
- colMethod(TW.conf.defaultColoring)
- madeDefaultColor = true
- }
- catch(err) {
- console.warn(`Settings asked for defaultColoring by the
+ // retrieve the actual function and if there, try and run it
+ colMethod = window[colMethodName];
+ if (colMethod && typeof colMethod == 'function') {
+ try {
+ colMethod(TW.conf.defaultColoring);
+ madeDefaultColor = true;
+ } catch (err) {
+ console.warn(`Settings asked for defaultColoring by the
attribute "${TW.conf.defaultColoring}" but
- it's not present in the dataset => skip action`)
- }
+ it's not present in the dataset => skip action`);
}
}
- // otherwise, set the default legend
- if (! madeDefaultColor) {
- updateColorsLegend ( "clust_default" )
- }
+ }
+ // otherwise, set the default legend
+ if (! madeDefaultColor) {
+ updateColorsLegend('clust_default');
+ }
- // select currently active sliders
- if (TW.conf.filterSliders) {
- // also for all active cats
- for (let activeId in initialActivetypes) {
- if (initialActivetypes[activeId]) {
- // args: for display: target div ,
- // for context: family/type prop value,
- // for values: the property to filter
- NodeWeightFilter (`#slidercat${activeId}nodesweight` , activeId);
- EdgeWeightFilter(`#slidercat${activeId}edgesweight`,
- activeId.toString().repeat(2),
- "weight"
- );
- $(`.for-nodecategory-${activeId}`).show()
- }
- else {
- $(`.for-nodecategory-${activeId}`).hide()
- }
+ // select currently active sliders
+ if (TW.conf.filterSliders) {
+ // also for all active cats
+ for (let activeId in initialActivetypes) {
+ if (initialActivetypes[activeId]) {
+ // args: for display: target div ,
+ // for context: family/type prop value,
+ // for values: the property to filter
+ NodeWeightFilter(`#slidercat${activeId}nodesweight`, activeId);
+ EdgeWeightFilter(`#slidercat${activeId}edgesweight`,
+ activeId.toString().repeat(2),
+ 'weight'
+ );
+ $(`.for-nodecategory-${activeId}`).show();
+ } else {
+ $(`.for-nodecategory-${activeId}`).hide();
}
}
+ }
- // node's label size
- var labelSizeTimeout = null
- $("#sliderlabelsize0").freshslider({
- step:.25,
- min:.25,
- max:5,
- value: 1,
- bgcolor:"#FFA500",
- onchange:function(value){
- if (labelSizeTimeout) {
- clearTimeout(labelSizeTimeout)
- }
- labelSizeTimeout = setTimeout(function(){
- if (TW.gui.sizeRatios[0] != value) {
- TW.gui.sizeRatios[0] = value
- // also adapt label threshold
- TW.partialGraph.settings('labelThreshold', getSizeFactor())
- TW.partialGraph.render()
- }
- }, 200)
- }
- });
- var labelSizeTimeout = null
- $("#sliderlabelsize1").freshslider({
- step:.25,
- min:.25,
- max:5,
- value: 1,
- bgcolor:"#27c470",
- onchange:function(value){
- if (labelSizeTimeout) {
- clearTimeout(labelSizeTimeout)
- }
- labelSizeTimeout = setTimeout(function(){
- if (TW.gui.sizeRatios[1] != value) {
- TW.gui.sizeRatios[1] = value
- // also adapt label threshold
- TW.partialGraph.settings('labelThreshold', getSizeFactor())
- TW.partialGraph.render()
- }
- }, 200)
+ // node's label size
+ let labelSizeTimeout = null;
+ $('#sliderlabelsize0').freshslider({
+ step: .25,
+ min: .25,
+ max: 5,
+ value: 1,
+ bgcolor: '#FFA500',
+ onchange: function(value) {
+ if (labelSizeTimeout) {
+ clearTimeout(labelSizeTimeout);
+ }
+ labelSizeTimeout = setTimeout(function() {
+ if (TW.gui.sizeRatios[0] != value) {
+ TW.gui.sizeRatios[0] = value;
+ // also adapt label threshold
+ TW.partialGraph.settings('labelThreshold', getSizeFactor());
+ TW.partialGraph.render();
}
- });
-
- // set the switch
- document.getElementById('edges-switch').checked = TW.customSettings.drawEdges
-
- // hide GUI elements of inactive types
- // (frontend currently allows max 2 types)
- for (var possibleTypeid in [0,1]) {
- if ( ! TW.categories[possibleTypeid]
- || ! initialActivetypes[possibleTypeid]) {
- $(".for-nodecategory-"+possibleTypeid).hide();
+ }, 200);
+ },
+ });
+
+ $('#sliderlabelsize1').freshslider({
+ step: .25,
+ min: .25,
+ max: 5,
+ value: 1,
+ bgcolor: '#27c470',
+ onchange: function(value) {
+ if (labelSizeTimeout) {
+ clearTimeout(labelSizeTimeout);
}
+ labelSizeTimeout = setTimeout(function() {
+ if (TW.gui.sizeRatios[1] != value) {
+ TW.gui.sizeRatios[1] = value;
+ // also adapt label threshold
+ TW.partialGraph.settings('labelThreshold', getSizeFactor());
+ TW.partialGraph.render();
+ }
+ }, 200);
+ },
+ });
+
+ // set the switch
+ document.getElementById('edges-switch').checked = TW.customSettings.drawEdges;
+
+ // hide GUI elements of inactive types
+ // (frontend currently allows max 2 types)
+ for (let possibleTypeid in [0, 1]) {
+ if (!TW.categories[possibleTypeid]
+ || !initialActivetypes[possibleTypeid]) {
+ $('.for-nodecategory-'+possibleTypeid).hide();
}
+ }
- // attributes' facet-options init & handler
- fillAttrsInForm('choose-attr')
- document.getElementById('choose-attr').onchange = showAttrConf
+ // attributes' facet-options init & handler
+ fillAttrsInForm('choose-attr');
+ document.getElementById('choose-attr').onchange = showAttrConf;
- // add all numeric attributes to titlingMetric with option type fromFacets
- fillAttrsInForm('attr-titling-metric', 'num')
+ // add all numeric attributes to titlingMetric with option type fromFacets
+ fillAttrsInForm('attr-titling-metric', 'num');
- // cancelSelection(false);
- }
+ // cancelSelection(false);
+ }
+ /**
+ * Clears the graph instance.
+ */
+ clearSigma() {
+ if (TW.partialGraph && TW.partialGraph.graph) {
+ TW.partialGraph.graph.clear();
+ TW.partialGraph.refresh();
- // clears the graph instance
- this.clearSigma = function() {
- if (TW.partialGraph && TW.partialGraph.graph) {
- TW.partialGraph.graph.clear()
- TW.partialGraph.refresh()
-
- TW.pushGUIState({'sels':[]})
- TW.SystemState().selectionNids = []
+ TW.pushGUIState({'sels': []});
+ TW.SystemState().selectionNids = [];
+ }
+ }
+
+ // our current choice: show only the last cat
+ // except when setting TW.conf.debug.initialShowAll
+ initialActivetypes(categories) {
+ let firstActivetypes = [];
+ for (let i = 0; i < categories.length; i++) {
+ if (TW.conf.debug.initialShowAll || i == categories.length-1) {
+ firstActivetypes.push(true);
+ } else {
+ firstActivetypes.push(false);
}
}
-
-
- // our current choice: show only the last cat
- // except when setting TW.conf.debug.initialShowAll
- this.initialActivetypes = function( categories ) {
- let firstActivetypes = []
- for(var i=0; i all reltypes
+ if (nodeActivetypes.indexOf(false) == -1) {
+ if (TW.categories.length == 1) {
+ activereltypes = ['00'];
+ } else if (TW.categories.length == 2) {
+ activereltypes = ['00', '11', 'XR'];
+ }
+ // POSSible: generalize if length > 1: recurse to generate all true/false combinations except the all-false one
+ } else { // normal case: one activereltype, equal to the initialActivetype key
+ activereltypes = [nodeActivetypes.indexOf(true).toString().repeat(2)];
}
- // new business logic associating some activetypes to some activerels
- // (it now allows multiple "relation-families" to be added as visible edges)
- this.inferActivereltypes = function( nodeActivetypes ) {
- let activereltypes = []
- // multiple nodetypes all true => all reltypes
- if (nodeActivetypes.indexOf(false) == -1) {
- if (TW.categories.length == 1) {
- activereltypes = ['00']
- }
- else if (TW.categories.length == 2) {
- activereltypes = ['00', '11', 'XR']
- }
- // POSSible: generalize if length > 1: recurse to generate all true/false combinations except the all-false one
- }
- // normal case: one activereltype, equal to the initialActivetype key
- else {
- activereltypes = [nodeActivetypes.indexOf(true).toString().repeat(2)]
- }
-
- return activereltypes;
- }
+ return activereltypes;
+ }
- // POSS for one type => many (jutsu case) results are also interesting when
- // "disconnecting" previous direct neighbors (making them indirect via XR)
- // ie:
- // 00 => [11, XR]
- // 11 => [00, XR]
-};
+ // POSS for one type => many (jutsu case) results are also interesting when
+ // "disconnecting" previous direct neighbors (making them indirect via XR)
+ // ie:
+ // 00 => [11, XR]
+ // 11 => [00, XR]
+}
diff --git a/twmain/enviroment.js b/twmain/enviroment.js
index 02026c5..4a11769 100755
--- a/twmain/enviroment.js
+++ b/twmain/enviroment.js
@@ -1,42 +1,41 @@
-'use strict;'
+'use strict;';
// GUI commodity pointers
-TW.gui = {}
-TW.gui.elHtml = document.getElementsByTagName('html')[0]
-TW.gui.elContainer = document.getElementById('sigma-contnr')
-TW.gui.sheets = {}
-for (var i in document.styleSheets) {
+TW.gui = {};
+TW.gui.elHtml = document.getElementsByTagName('html')[0];
+TW.gui.elContainer = document.getElementById('sigma-contnr');
+TW.gui.sheets = {};
+for (let i in document.styleSheets) {
if (/twjs.css$/.test(document.styleSheets[i].href)) {
- TW.gui.sheets.main = document.styleSheets[i]
- }
- else if (/selection-panels.css$/.test(document.styleSheets[i].href)) {
- TW.gui.sheets.panels = document.styleSheets[i]
+ TW.gui.sheets.main = document.styleSheets[i];
+ } else if (/selection-panels.css$/.test(document.styleSheets[i].href)) {
+ TW.gui.sheets.panels = document.styleSheets[i];
}
}
// GUI vars
-TW.gui.selectionActive = false // <== changes rendering mode
-TW.gui.smallView=false; // tracks if small width
+TW.gui.selectionActive = false; // <== changes rendering mode
+TW.gui.smallView=false; // tracks if small width
TW.gui.circleSize = 0;
-TW.gui.circleSlider = null
+TW.gui.circleSlider = null;
TW.gui.checkBox=false;
TW.gui.shiftKey=false;
TW.gui.foldedSide=false;
TW.gui.manuallyChecked = false;
-TW.gui.lastFilters = {} // <= last values, by slider id
-TW.gui.reldocTabs = [{}, {}] // <= by nodetype and then dbtype
-
-TW.gui.sizeRatios = [1,1] // sizeRatios per nodetype
-TW.gui.handpickedcolors = {}; // <= changes rendering, by nodetype
-TW.gui.handpickedcolorsReset = function (forTypes = TW.categories) {
- TW.gui.handpickedcolors = {}
- for (var k in forTypes) {
+TW.gui.lastFilters = {}; // <= last values, by slider id
+TW.gui.reldocTabs = [{}, {}]; // <= by nodetype and then dbtype
+
+TW.gui.sizeRatios = [1, 1]; // sizeRatios per nodetype
+TW.gui.handpickedcolors = {}; // <= changes rendering, by nodetype
+TW.gui.handpickedcolorsReset = function(forTypes = TW.categories) {
+ TW.gui.handpickedcolors = {};
+ for (let k in forTypes) {
TW.gui.handpickedcolors[forTypes[k]] = {
'alton': false,
- 'altattr': null
- }
+ 'altattr': null,
+ };
}
-}
+};
TW.gui.noverlapConf = {
nodeMargin: .4,
@@ -45,15 +44,15 @@ TW.gui.noverlapConf = {
speed: 7,
maxIterations: 8,
easing: 'quadraticOut', // animation transition function
- duration: 1500 // animation duration
+ duration: 1500, // animation duration
// NB animation happens *after* processing
-}
+};
TW.FA2Params = {
// adapting speed -------------
slowDown: 1.5,
- startingIterations: 2, // keep it an even number to reduce visible oscillations at rendering
- iterationsPerRender: 4, // idem
+ startingIterations: 2, // keep it an even number to reduce visible oscillations at rendering
+ iterationsPerRender: 4, // idem
barnesHutOptimize: false,
// barnesHutTheta: .5,
@@ -64,67 +63,67 @@ TW.FA2Params = {
strongGravityMode: false,
scalingRatio: 1,
- adjustSizes: false, // ~ messy but sort of in favor of overlap prevention
+ adjustSizes: false, // ~ messy but sort of in favor of overlap prevention
// favors global centrality
// (but rather not needed when data already shows topic-centered
// node groups and/nor when preferential attachment type of data)
- outboundAttractionDistribution: false
-}
+ outboundAttractionDistribution: false,
+};
// POSS: themed variants (ex: for dark bg vs light bg)
// contrasted color list for clusterColoring()
-TW.gui.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"];
+TW.gui.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'];
// 24 colors + White, divided in cold and warm range for getHeatmapColors() fun
-TW.gui.heatmapColorListWhite = "#F9F7ED"
+TW.gui.heatmapColorListWhite = '#F9F7ED';
TW.gui.heatmapColorListToColdest = [
- "#B4FF50",
- "#A4FF24",
- "#79FF23",
- "#42F923",
- "#22F226",
- "#02CB36",
- "#01C462",
- "#01BC8D",
- "#00B5B1",
- "#0088AE",
- "#005197",
- "#002FA0"
-]
+ '#B4FF50',
+ '#A4FF24',
+ '#79FF23',
+ '#42F923',
+ '#22F226',
+ '#02CB36',
+ '#01C462',
+ '#01BC8D',
+ '#00B5B1',
+ '#0088AE',
+ '#005197',
+ '#002FA0',
+];
TW.gui.heatmapColorListToWarmest = [
- "#FFE37A",
- "#FFE008",
- "#F0C508",
- "#E89A09",
- "#E48509",
- "#DF7009",
- "#DB5B09",
- "#D7450A",
- "#D3300A",
- "#CF1B0A",
- "#CB060B",
- "#B21014"
+ '#FFE37A',
+ '#FFE008',
+ '#F0C508',
+ '#E89A09',
+ '#E48509',
+ '#DF7009',
+ '#DB5B09',
+ '#D7450A',
+ '#D3300A',
+ '#CF1B0A',
+ '#CB060B',
+ '#B21014',
];
@@ -134,108 +133,105 @@ TW.gui.heatmapColorListToWarmest = [
// then the 2 middle categories
// will get the white "epsilon" color
function getHeatmapColors(nClasses) {
-
// our dev param == colorListToWarmest.length == colorListToColdest.length
- let listsLen = 12
+ let listsLen = 12;
// our result
- var outColors = []
+ let outColors = [];
if (nClasses > listsLen*2) {
- throw(`this function implementation can only give up to ${listsLen*2} classes`)
+ throw (`this function implementation can only give up to ${listsLen*2} classes`);
}
- let nHalfToPick = 0
+ let nHalfToPick = 0;
if (nClasses % 2 == 0) {
- nHalfToPick = nClasses/2 - 1
- }
- else {
- nHalfToPick = (nClasses-1)/2
+ nHalfToPick = nClasses/2 - 1;
+ } else {
+ nHalfToPick = (nClasses-1)/2;
}
// floor
- let exactStep = listsLen / nHalfToPick
- let skipStep = parseInt(exactStep) // incrmt must be int (used for arr idx)
+ let exactStep = listsLen / nHalfToPick;
+ let skipStep = parseInt(exactStep); // incrmt must be int (used for arr idx)
// to compensate for the fractional part
- let delta = exactStep - skipStep
- let drift = 0
+ let delta = exactStep - skipStep;
+ let drift = 0;
// cold colors, starting from deepest color
- for (let i = listsLen-1 ; i > 0 ; i -= skipStep ) {
- outColors.push(TW.gui.heatmapColorListToColdest[i])
+ for (let i = listsLen-1; i > 0; i -= skipStep ) {
+ outColors.push(TW.gui.heatmapColorListToColdest[i]);
// catching back one step from time to time
- drift += delta
+ drift += delta;
if (drift >= 1) {
- i--
- drift -= 1
+ i--;
+ drift -= 1;
}
}
// white
- outColors.push(TW.gui.heatmapColorListWhite)
+ outColors.push(TW.gui.heatmapColorListWhite);
if (nClasses % 2 == 0) {
- outColors.push(TW.gui.heatmapColorListWhite)
+ outColors.push(TW.gui.heatmapColorListWhite);
}
// warm colors
- for (let i = 0 ; i < listsLen ; i += skipStep ) {
- outColors.push(TW.gui.heatmapColorListToWarmest[i])
+ for (let i = 0; i < listsLen; i += skipStep ) {
+ outColors.push(TW.gui.heatmapColorListToWarmest[i]);
}
- return outColors
+ return outColors;
}
-function writeBrand (brandString, brandLink) {
- let elTitle = document.getElementById('twbrand')
+function writeBrand(brandString, brandLink) {
+ let elTitle = document.getElementById('twbrand');
if (elTitle) {
- elTitle.innerHTML = brandString
+ elTitle.innerHTML = brandString;
}
- let anchors = document.getElementsByClassName('twbrand-link')
- for (var k in anchors) {
+ let anchors = document.getElementsByClassName('twbrand-link');
+ for (let k in anchors) {
if (anchors[k] && anchors[k].href) {
- anchors[k].href = brandLink
+ anchors[k].href = brandLink;
}
}
}
-function writeLabel (aMapLabel) {
- document.getElementById('maplabel').innerHTML = aMapLabel
+function writeLabel(aMapLabel) {
+ document.getElementById('maplabel').innerHTML = aMapLabel;
}
// some actions handled by js overrides the CSS from our stylesheets
// => this function removes all these changes to restore stylesheet rules
function cssReset() {
- $("#sigma-contnr").css('right', '')
- $("#ctlzoom").css('right','')
- $("#sidebar").css('right','')
- $("#sidebar").show()
- TW.gui.foldedSide = false
+ $('#sigma-contnr').css('right', '');
+ $('#ctlzoom').css('right', '');
+ $('#sidebar').css('right', '');
+ $('#sidebar').show();
+ TW.gui.foldedSide = false;
}
function foldingReset() {
if (TW.gui.foldedSide) {
- $("#sideunfold").show()
- $("#sidefold").hide()
- }
- else {
- $("#sideunfold").hide()
- $("#sidefold").show()
+ $('#sideunfold').show();
+ $('#sidefold').hide();
+ } else {
+ $('#sideunfold').hide();
+ $('#sidefold').show();
}
}
-function alertCheckBox(eventCheck){
+function alertCheckBox(eventCheck) {
// NB: we use 2 booleans to adapt to SHIFT checking
// - var TW.gui.checkBox ---------> has the real box state
// - var TW.gui.manuallyChecked --> remembers if it was changed here
- if(!isUndef(eventCheck.checked)) {
+ if (!isUndef(eventCheck.checked)) {
TW.gui.checkBox=eventCheck.checked;
- TW.gui.manuallyChecked = eventCheck.checked
+ TW.gui.manuallyChecked = eventCheck.checked;
}
}
@@ -243,74 +239,68 @@ function alertCheckBox(eventCheck){
// fileChooser: added to the environment when user opens explorer as local file
// -----------
// TODO: because source files now get a project_conf.md, find a way to open it too if it exists
-function createFilechooserEl () {
-
- var inputComment = document.createElement("p")
- inputComment.innerHTML = `Choose a graph from your filesystem (gexf or json).`
- inputComment.classList.add('comment')
- inputComment.classList.add('centered')
-
- var graphFileInput = document.createElement('input')
- graphFileInput.id = 'localgraphfile'
- graphFileInput.type = 'file'
- graphFileInput.accept = 'application/xml,application/gexf,application/json'
- graphFileInput.classList.add('centered')
+function createFilechooserEl() {
+ let inputComment = document.createElement('p');
+ inputComment.innerHTML = `Choose a graph from your filesystem (gexf or json).`;
+ inputComment.classList.add('comment');
+ inputComment.classList.add('centered');
+
+ let graphFileInput = document.createElement('input');
+ graphFileInput.id = 'localgraphfile';
+ graphFileInput.type = 'file';
+ graphFileInput.accept = 'application/xml,application/gexf,application/json';
+ graphFileInput.classList.add('centered');
// NB file input will trigger mainStartGraph() when the user chooses something
graphFileInput.onchange = function() {
if (this.files && this.files[0]) {
-
- let clientLocalGraphFile = this.files[0]
+ let clientLocalGraphFile = this.files[0];
// determine the format
- let theFormat
+ let theFormat;
if (/\.(?:gexf|xml)$/.test(clientLocalGraphFile.name)) {
- theFormat = 'gexf'
- }
- else if (/\.json$/.test(clientLocalGraphFile.name)) {
- theFormat = 'json'
- }
- else {
- alert('unrecognized file format')
+ theFormat = 'gexf';
+ } else if (/\.json$/.test(clientLocalGraphFile.name)) {
+ theFormat = 'json';
+ } else {
+ alert('unrecognized file format');
}
// retrieving the content
- let rdr = new FileReader()
+ let rdr = new FileReader();
rdr.onload = function() {
- if (! rdr.result || !rdr.result.length) {
- alert('the selected file is not readable')
- writeLabel(`Local file: unreadable!`)
- }
- else {
+ 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
- TW.resetGraph()
+ TW.resetGraph();
// run
- mainStartGraph(theFormat, rdr.result, TW.instance)
+ mainStartGraph(theFormat, rdr.result, TW.instance);
// NB 3rd arg null = we got no additional conf for this "unknown" file
- writeLabel(`Local file: ${clientLocalGraphFile.name}`)
+ writeLabel(`Local file: ${clientLocalGraphFile.name}`);
}
- }
- rdr.readAsText(clientLocalGraphFile)
+ };
+ rdr.readAsText(clientLocalGraphFile);
}
- }
+ };
- var filechooserBox = document.createElement('div')
- filechooserBox.appendChild(inputComment)
- filechooserBox.appendChild(graphFileInput)
+ let filechooserBox = document.createElement('div');
+ filechooserBox.appendChild(inputComment);
+ filechooserBox.appendChild(graphFileInput);
- return filechooserBox
+ return filechooserBox;
}
-//============================ < NEW BUTTONS > =============================//
+// ============================ < NEW BUTTONS > =============================//
function changeType(optionaltypeFlag) {
-
// RELATION TYPES
//
// SOURCE NODE TARGET NODE
@@ -367,123 +357,123 @@ function changeType(optionaltypeFlag) {
// |
// updates the sliders and selection panels
- let typeFlag
- let outgoing = TW.SystemState()
- let oldTypeId = outgoing.activetypes.indexOf(true)
- let mixedState = (outgoing.activereltypes.length > 1)
+ let typeFlag;
+ let outgoing = TW.SystemState();
+ let oldTypeId = outgoing.activetypes.indexOf(true);
+ let mixedState = (outgoing.activereltypes.length > 1);
// needed selection content diagnostic for mixed meso target choice
- let selectionTypeId = false
+ let selectionTypeId = false;
if (outgoing.selectionNids.length) {
if (!mixedState) {
- selectionTypeId = oldTypeId
- }
- else if (!outgoing.level) {
- let selMajorityType = TW.categories[oldTypeId]
- let counts = {}
+ selectionTypeId = oldTypeId;
+ } else if (!outgoing.level) {
+ let selMajorityType = TW.categories[oldTypeId];
+ let counts = {};
for (var j in outgoing.selectionNids) {
- let ty = TW.Nodes[outgoing.selectionNids[j]].type
- if (! counts[ty]) counts[ty] = 1
- else counts[ty] += 1
+ let ty = TW.Nodes[outgoing.selectionNids[j]].type;
+ if (! counts[ty]) counts[ty] = 1;
+ else counts[ty] += 1;
}
for (var ty in counts) {
if (counts[ty] > counts[selMajorityType]) {
- selMajorityType = ty
+ selMajorityType = ty;
}
}
- selectionTypeId = TW.catDict[ty]
+ selectionTypeId = TW.catDict[ty];
}
}
// 1 - make the targetTypes choices
if (!isUndef(optionaltypeFlag)) {
- typeFlag = optionaltypeFlag
- }
- else {
+ typeFlag = optionaltypeFlag;
+ } else {
// "comeback" case: going back from mixed view to selections majority view
// (or last non-mixed view if no selection)
// ----------
if (mixedState) {
- typeFlag = selectionTypeId || outgoing.comingFromType || 0
+ typeFlag = selectionTypeId || outgoing.comingFromType || 0;
}
// "jutsu" case: macrolevel opens mixed view
// -------
else if (!outgoing.level) {
- typeFlag = 'all'
+ typeFlag = 'all';
}
// normal case: show the opposite type
// -----------
else {
- typeFlag = (oldTypeId + 1) % 2 // binary toggle next 0 => 1
+ typeFlag = (oldTypeId + 1) % 2; // binary toggle next 0 => 1
// 1 => 0
}
}
// 2 - infer consequences of targetTypes
- let newActivetypes = []
+ let newActivetypes = [];
if (typeFlag == 'all') {
- for (var i in TW.categories) { newActivetypes.push(true) }
- }
- else {
for (var i in TW.categories) {
- if (i == typeFlag) newActivetypes.push(true)
- else newActivetypes.push(false)
+ newActivetypes.push(true);
+}
+ } else {
+ for (var i in TW.categories) {
+ if (i == typeFlag) newActivetypes.push(true);
+ else newActivetypes.push(false);
}
}
// console.log('newActivetypes', newActivetypes)
- let newReltypes = TW.instance.inferActivereltypes(newActivetypes)
+ let newReltypes = TW.instance.inferActivereltypes(newActivetypes);
// console.log('newReltypes', newReltypes)
// nodes already in target type
- let alreadyOk = {}
+ let alreadyOk = {};
if (mixedState) {
- let arr = TW.partialGraph.graph.getNodesByType(typeFlag)
- for (var i in arr) {alreadyOk[arr[i]] = true}
+ let arr = TW.partialGraph.graph.getNodesByType(typeFlag);
+ for (var i in arr) {
+alreadyOk[arr[i]] = true;
+}
}
// 3 - define the projected selection (sourceNids => corresponding opposites)
- let sourceNids = outgoing.selectionNids
+ let sourceNids = outgoing.selectionNids;
// when jutsu and no selection => we pick one selection at random (in meso mode we can't go to another meso with no selection, because nothing would appear)
if (typeFlag == 'all' && !sourceNids.length) {
- sourceNids = []
+ sourceNids = [];
for (var nid in TW.Nodes) {
if (! TW.Nodes[nid].hidden) {
- sourceNids.push(nid)
- break
+ sourceNids.push(nid);
+ break;
}
}
}
- let targetNids = {}
+ let targetNids = {};
if (!mixedState) {
- targetNids = getNeighbors(sourceNids, 'XR')
- }
- else {
+ targetNids = getNeighbors(sourceNids, 'XR');
+ } else {
// in mixed local state we need to separate those already tgt state from others
- let needXRTransition = []
+ let needXRTransition = [];
for (var i in sourceNids) {
- let nid = sourceNids[i]
- if (alreadyOk[nid]) targetNids[nid] = true
- else needXRTransition.push(nid)
+ let nid = sourceNids[i];
+ if (alreadyOk[nid]) targetNids[nid] = true;
+ else needXRTransition.push(nid);
}
// if none of the selection in new type => selection's projection
// if some of the selection in new type => this majority subset of selection
// without the projection of others
if (! Object.keys(targetNids).length) {
- targetNids = getNeighbors(needXRTransition, "XR")
+ targetNids = getNeighbors(needXRTransition, 'XR');
}
// console.log("mixedState start, selections targetNids:", targetNids)
}
// 4 - define the nodes to be added
- let newNodes = {}
+ let newNodes = {};
// in mode all the current selection (and only it) is preserved
if (typeFlag == 'all') {
for (var i in outgoing.selectionNids) {
- let nid = outgoing.selectionNids[i]
- newNodes[nid] = TW.Nodes[nid]
+ let nid = outgoing.selectionNids[i];
+ newNodes[nid] = TW.Nodes[nid];
}
}
@@ -491,76 +481,76 @@ function changeType(optionaltypeFlag) {
if (outgoing.level) {
for (let typeId in newActivetypes) {
if (newActivetypes[typeId]) {
- newNodes = Object.assign(newNodes, getNodesOfType(typeId))
+ newNodes = Object.assign(newNodes, getNodesOfType(typeId));
}
}
- }
- else {
+ } else {
if (Object.keys(targetNids).length) {
for (var nid in targetNids) {
- newNodes[nid] = TW.Nodes[nid]
+ newNodes[nid] = TW.Nodes[nid];
}
// also more added because they are the "meso" sameside neighbors of the selection
- let rel = typeFlag.toString().repeat(2)
- let additionalNewTypeNids = getNeighbors(Object.keys(targetNids), rel)
+ let rel = typeFlag.toString().repeat(2);
+ let additionalNewTypeNids = getNeighbors(Object.keys(targetNids), rel);
for (var nid in additionalNewTypeNids) {
- newNodes[nid] = TW.Nodes[nid]
+ newNodes[nid] = TW.Nodes[nid];
}
}
// if no selection, meso shouldn't be possible, but we can still
// show something: those that were already of the correct type
else if (mixedState) {
for (var nid in alreadyOk) {
- newNodes[nid] = TW.Nodes[nid]
+ newNodes[nid] = TW.Nodes[nid];
}
}
}
// console.log('newNodes', newNodes)
// 5 - define the new selection
- let newselsArr = []
+ let newselsArr = [];
if (outgoing.selectionNids.length) {
if (typeFlag != 'all') {
- newselsArr = Object.keys(targetNids)
+ newselsArr = Object.keys(targetNids);
// NB: if mixedState we already filtered them at step 3
- }
- else {
+ } else {
// not extending selection to all transitive neighbors
// makes the operation stable (when clicked several times,
// without changing selection, we go back to original state)
- newselsArr = outgoing.selectionNids
+ newselsArr = outgoing.selectionNids;
}
}
// 6 - effect the changes on nodes
- deselectNodes()
- TW.partialGraph.graph.clear() // a new start
+ deselectNodes();
+ TW.partialGraph.graph.clear(); // a new start
for (var nid in newNodes) {
try {
- TW.partialGraph.graph.addNode(newNodes[nid])
- } catch(e) {continue}
+ TW.partialGraph.graph.addNode(newNodes[nid]);
+ } catch (e) {
+continue;
+}
}
// 7 - add the relations
- let newEdges = {}
- for (var srcnid in newNodes) {
- for (var k in newReltypes) {
- let relKey = newReltypes[k]
+ let newEdges = {};
+ for (let srcnid in newNodes) {
+ for (let k in newReltypes) {
+ let relKey = newReltypes[k];
if (TW.Relations[relKey]
&& TW.Relations[relKey][srcnid]
&& TW.Relations[relKey][srcnid].length) {
for (var j in TW.Relations[relKey][srcnid]) {
- let tgtnid = TW.Relations[relKey][srcnid][j]
+ let tgtnid = TW.Relations[relKey][srcnid][j];
if (newNodes[tgtnid]) {
- let eids = [`${srcnid};${tgtnid}`, `${tgtnid};${srcnid}`]
- for (var l in eids) {
- let eid = eids[l]
- if (eid && TW.Edges[eid] && !TW.partialGraph.graph.edges(eid)){
- newEdges[eid] = TW.Edges[eid]
- break
+ let eids = [`${srcnid};${tgtnid}`, `${tgtnid};${srcnid}`];
+ for (let l in eids) {
+ let eid = eids[l];
+ if (eid && TW.Edges[eid] && !TW.partialGraph.graph.edges(eid)) {
+ newEdges[eid] = TW.Edges[eid];
+ break;
}
}
}
@@ -570,72 +560,73 @@ function changeType(optionaltypeFlag) {
}
// 8 - effect the changes on edges
- for (var eid in newEdges) {
+ for (let eid in newEdges) {
try {
- TW.partialGraph.graph.addEdge(newEdges[eid])
- } catch(e) {continue}
+ TW.partialGraph.graph.addEdge(newEdges[eid]);
+ } catch (e) {
+continue;
+}
}
// 9 - refresh view and record the state
- TW.partialGraph.camera.goTo({x:0, y:0, ratio:1, angle: 0})
- TW.partialGraph.refresh()
+ TW.partialGraph.camera.goTo({x: 0, y: 0, ratio: 1, angle: 0});
+ TW.partialGraph.refresh();
- if (typeFlag != "all") {
+ if (typeFlag != 'all') {
TW.pushGUIState({
activetypes: newActivetypes,
activereltypes: newReltypes,
- sels: newselsArr
+ sels: newselsArr,
// rels: added by MS2 (highlighted opposite- and same-side neighbours)
// possible: add it in an early way here and request that MS2 doesn't change state
- })
- }
- else {
+ });
+ } else {
TW.pushGUIState({
activetypes: newActivetypes,
comingFromType: oldTypeId,
activereltypes: newReltypes,
- sels: newselsArr
- })
+ sels: newselsArr,
+ });
}
// to recreate the new selection in the new type graph, if we had one before
// NB relies on new actypes so should be after pushState
if (newselsArr.length) {
- TW.instance.selNgn.MultipleSelection2({nodes: newselsArr});
- if (TW.conf.debug.logSelections)
- console.log("selection transitive projection from",sourceNids, "to", newselsArr)
+ TW.instance.selNgn.multipleSelection2({nodes: newselsArr});
+ if (TW.conf.debug.logSelections) {
+console.log('selection transitive projection from', sourceNids, 'to', newselsArr);
+}
}
// update search labels
- TW.labels.splice(0, TW.labels.length)
+ TW.labels.splice(0, TW.labels.length);
for (var nid in newNodes) {
- updateSearchLabels(nid,newNodes[nid].label,newNodes[nid].type);
+ updateSearchLabels(nid, newNodes[nid].label, newNodes[nid].type);
}
// update the gui (TODO handle by TW.pushGUIState) =========================
- updateDynamicFacets()
+ updateDynamicFacets();
// console.log("outgoing.activetypes", outgoing.activetypes)
// console.log("newActivetypes", newActivetypes)
- changeGraphAppearanceByFacets( getActivetypesNames() )
+ changeGraphAppearanceByFacets( getActivetypesNames() );
// turn off the altcolors for outgoing types
- for (var tyId in TW.categories) {
- let ty = TW.categories[tyId]
+ for (let tyId in TW.categories) {
+ let ty = TW.categories[tyId];
if (outgoing.activetypes[tyId] && ! newActivetypes[tyId]) {
if (TW.gui.handpickedcolors[ty].alton) {
- clearColorLegend([ty])
- TW.gui.handpickedcolors[ty].alton = false
+ clearColorLegend([ty]);
+ TW.gui.handpickedcolors[ty].alton = false;
}
- }
- else if (!outgoing.activetypes[tyId] && newActivetypes[tyId]) {
+ } else if (!outgoing.activetypes[tyId] && newActivetypes[tyId]) {
if (TW.gui.handpickedcolors[ty].altattr) {
- TW.gui.handpickedcolors[ty].alton = true
+ TW.gui.handpickedcolors[ty].alton = true;
// this re-coloring can be avoided if "hidden" was used in changeLevel and sliders
- let recolorMethod = getColorFunction(TW.gui.handpickedcolors[ty].altattr)
- window[recolorMethod](TW.gui.handpickedcolors[ty].altattr, [ty])
+ let recolorMethod = getColorFunction(TW.gui.handpickedcolors[ty].altattr);
+ window[recolorMethod](TW.gui.handpickedcolors[ty].altattr, [ty]);
// without re-coloring step, we would only need to recreate legend box
// updateColorsLegend(TW.gui.handpickedcolors[ty].altattr, [ty])
@@ -643,51 +634,51 @@ function changeType(optionaltypeFlag) {
}
}
- TW.partialGraph.settings('labelThreshold', getSizeFactor())
- fillAttrsInForm('choose-attr')
- fillAttrsInForm('attr-titling-metric', 'num')
+ TW.partialGraph.settings('labelThreshold', getSizeFactor());
+ fillAttrsInForm('choose-attr');
+ fillAttrsInForm('attr-titling-metric', 'num');
// recreates FA2 nodes array from new nodes
reInitFa2({
useSoftMethod: false,
callback: function() {
- sigma_utils.smartForceAtlas()
- }
- })
+ sigma_utils.smartForceAtlas();
+ },
+ });
// end update the gui ======================================================
}
// the pool of available nodes of a given type
-function getNodesOfType (typeid){
- let res = {}
+function getNodesOfType(typeid) {
+ let res = {};
if (TW.ByType[typeid]) {
- for (var j in TW.ByType[typeid]) {
- let nid = TW.ByType[typeid][j]
- let n = TW.Nodes[TW.ByType[typeid][j]]
- res[nid] = n
+ for (let j in TW.ByType[typeid]) {
+ let nid = TW.ByType[typeid][j];
+ let n = TW.Nodes[TW.ByType[typeid][j]];
+ res[nid] = n;
}
}
- return res
+ return res;
}
// one transitive step
function getNeighbors(sourceNids, relKey) {
- let targetDict = {}
- for (var i in sourceNids) {
- let srcnid = sourceNids[i]
+ let targetDict = {};
+ for (let i in sourceNids) {
+ let srcnid = sourceNids[i];
if (TW.Relations[relKey]
&& !isUndef(TW.Relations[relKey][srcnid])
&& TW.Relations[relKey][srcnid].length) {
- for (var j in TW.Relations[relKey][srcnid]) {
- let tgtnid = TW.Relations[relKey][srcnid][j]
- targetDict[tgtnid] = true
+ for (let j in TW.Relations[relKey][srcnid]) {
+ let tgtnid = TW.Relations[relKey][srcnid][j];
+ targetDict[tgtnid] = true;
}
}
}
- return targetDict
+ return targetDict;
}
@@ -703,28 +694,25 @@ function getNeighbors(sourceNids, relKey) {
// (=> avoid redoing property computations and state push)
// POSS: rewrite using .hidden instead of add/remove
function changeLevel(optionalTgtState) {
-
// show waiting cursor
TW.gui.elHtml.classList.add('waiting');
// let the waiting cursor appear
setTimeout(function() {
-
// array of nids [144, 384, 543]
- var sels
+ let sels;
if (optionalTgtState) {
- sels = optionalTgtState.selectionNids
- }
- else {
+ sels = optionalTgtState.selectionNids;
+ } else {
var present = TW.SystemState(); // Last
- sels = present.selectionNids
+ sels = present.selectionNids;
}
- deselectNodes()
+ deselectNodes();
- let selsChecker = {}
+ let selsChecker = {};
for (let i in sels) {
- selsChecker[sels[i]] = true
+ selsChecker[sels[i]] = true;
}
// type "grammar"
@@ -734,92 +722,91 @@ function changeLevel(optionalTgtState) {
// [true, true] <=> '1|1'
if (optionalTgtState) {
- activetypes = optionalTgtState.activetypes
- activereltypes = optionalTgtState.activereltypes
- }
- else {
+ activetypes = optionalTgtState.activetypes;
+ activereltypes = optionalTgtState.activereltypes;
+ } else {
activetypes = present.activetypes;
activereltypes = present.activereltypes;
}
- let activetypesDict = {}
+ let activetypesDict = {};
for (var i in activetypes) {
if (activetypes[i]) {
- activetypesDict[TW.categories[i]] = true
+ activetypesDict[TW.categories[i]] = true;
}
}
TW.partialGraph.graph.clear();
- var voisinage = {}
+ let voisinage = {};
// Dictionaries of: selection+neighbors
- var nodesToAdd = {}
- var edgesToAdd = {}
+ let nodesToAdd = {};
+ let edgesToAdd = {};
- for(var i in sels) {
+ for (var i in sels) {
s = sels[i];
nodesToAdd[s]=true;
for (var k in activereltypes) {
- let activereltype = activereltypes[k]
+ let activereltype = activereltypes[k];
if (TW.Relations[activereltype]) {
- neigh = TW.Relations[activereltype][s]
- if(neigh) {
- for(var j in neigh) {
- t = neigh[j]
+ neigh = TW.Relations[activereltype][s];
+ if (neigh) {
+ for (var j in neigh) {
+ t = neigh[j];
nodesToAdd[t]=true;
- edgesToAdd[s+";"+t]=true;
- edgesToAdd[t+";"+s]=true;
- if( !selsChecker[t] )
- voisinage[ t ] = true;
+ edgesToAdd[s+';'+t]=true;
+ edgesToAdd[t+';'+s]=true;
+ if ( !selsChecker[t] ) {
+voisinage[t] = true;
+}
}
}
- }
- else {
+ } else {
// case where no edges at all (ex: scholars have no common keywords)
- console.log("no edges between these nodes")
+ console.log('no edges between these nodes');
}
}
}
- var futurelevel = optionalTgtState ? optionalTgtState.level : !present.level
-
- if(!futurelevel) { // [Change to Local] when level=Global(1)
+ let futurelevel = optionalTgtState ? optionalTgtState.level : !present.level;
- TW.gui.elContainer.style.backgroundColor = TW.conf.mesoBackground
+ if (!futurelevel) { // [Change to Local] when level=Global(1)
+ TW.gui.elContainer.style.backgroundColor = TW.conf.mesoBackground;
- for(var nid in nodesToAdd)
- add1Elem(nid)
- for(var eid in edgesToAdd) {
- add1Elem(eid)
+ for (var nid in nodesToAdd) {
+add1Elem(nid);
+}
+ for (var eid in edgesToAdd) {
+ add1Elem(eid);
}
// Adding intra-neighbors edges O(voisinage²)
- voisinage = Object.keys(voisinage)
- for(var i=0;i =============================//
-
-
-
+// ============================= NEW BUTTONS > =============================//
-//=========================== < FILTERS-SLIDERS > ===========================//
+// =========================== < FILTERS-SLIDERS > ===========================//
// census of edges by property criterion (eg 'weight'), for a given type
// (replaces deprecated AlgorithmForSliders, but without the sqrt transform)
function edgeSizesLookup(eTypeStrs, criterion) {
- var edgeweis = {}
+ let edgeweis = {};
for (let eid in TW.Edges) {
- let e = TW.partialGraph.graph.edges(eid)
+ let e = TW.partialGraph.graph.edges(eid);
- for (var etype_i in eTypeStrs) {
- let eTypeStr = eTypeStrs[etype_i]
+ for (let etype_i in eTypeStrs) {
+ let eTypeStr = eTypeStrs[etype_i];
if (e && e.categ && e.categ == eTypeStr) {
- if (!edgeweis[e.categ]) edgeweis[e.categ] = {}
- if (!edgeweis[e.categ][e[criterion]]) edgeweis[e.categ][e[criterion]] = []
+ if (!edgeweis[e.categ]) edgeweis[e.categ] = {};
+ if (!edgeweis[e.categ][e[criterion]]) edgeweis[e.categ][e[criterion]] = [];
- edgeweis[e.categ][e[criterion]].push(e.id)
+ edgeweis[e.categ][e[criterion]].push(e.id);
}
}
}
- return edgeweis
+ return edgeweis;
}
// strategy : if < 25 distinct values: each distinct val becomes a step
// if > 25 distinct values: we group in 25 bins of +- equal pop
function edgeSizesSteps(eTypeStr, esizesCensus) {
- let allValsToIdsArray = []
- let stepToIdsArray = []
+ let allValsToIdsArray = [];
+ let stepToIdsArray = [];
if (esizesCensus[eTypeStr]) {
- var sortedSizes = Object.keys(
+ let sortedSizes = Object.keys(
esizesCensus[eTypeStr]
- ).sort(function(a,b){return a-b})
+ ).sort(function(a, b) {
+return a-b;
+});
- let nEdges = 0
+ let nEdges = 0;
for (let l in sortedSizes) {
- let distinctVal = sortedSizes[l]
- allValsToIdsArray.push(esizesCensus[eTypeStr][distinctVal])
- nEdges += esizesCensus[eTypeStr][sortedSizes[l]].length
+ let distinctVal = sortedSizes[l];
+ allValsToIdsArray.push(esizesCensus[eTypeStr][distinctVal]);
+ nEdges += esizesCensus[eTypeStr][sortedSizes[l]].length;
}
// now allValsToIdsArray has length == nb of distinct values
if (sortedSizes.length <= 25) {
- stepToIdsArray = allValsToIdsArray
- }
- else {
- chunkSize = parseInt(nEdges / 25)
+ stepToIdsArray = allValsToIdsArray;
+ } else {
+ chunkSize = parseInt(nEdges / 25);
// console.log("nEdges, nDistinct, chunkSize", nEdges, sortedSizes.length, chunkSize)
- stepToIdsArray = makeSteps(chunkSize, allValsToIdsArray, [])
+ stepToIdsArray = makeSteps(chunkSize, allValsToIdsArray, []);
}
}
- return stepToIdsArray
+ return stepToIdsArray;
}
// recursive method (POSS: use a drift counter to compensate big exaequo subgroups
@@ -944,15 +930,14 @@ function edgeSizesSteps(eTypeStr, esizesCensus) {
// in facetsBinning (samepop case)
function makeSteps(chunkSize, remainder, groupedSteps) {
if (!remainder.length) {
- return groupedSteps
- }
- else {
- let newChunkIdsArray = []
- while(newChunkIdsArray.length < chunkSize) {
- newChunkIdsArray = newChunkIdsArray.concat(remainder.shift())
+ return groupedSteps;
+ } else {
+ let newChunkIdsArray = [];
+ while (newChunkIdsArray.length < chunkSize) {
+ newChunkIdsArray = newChunkIdsArray.concat(remainder.shift());
}
- groupedSteps.push(newChunkIdsArray)
- return makeSteps(chunkSize, remainder, groupedSteps)
+ groupedSteps.push(newChunkIdsArray);
+ return makeSteps(chunkSize, remainder, groupedSteps);
}
}
@@ -962,40 +947,39 @@ function makeSteps(chunkSize, remainder, groupedSteps) {
// NB new sigma js: dropEdge is quite slow so we add a waiting cursor
-function EdgeWeightFilter(sliderDivID , reltypestr , criteria) {
-
- if(TW.partialGraph.graph.nEdges()<2) {
- console.log('not enough edges for subsets: skipping GUI slider init')
- showDisabledSlider(sliderDivID)
+function EdgeWeightFilter(sliderDivID, reltypestr, criteria) {
+ if (TW.partialGraph.graph.nEdges()<2) {
+ console.log('not enough edges for subsets: skipping GUI slider init');
+ showDisabledSlider(sliderDivID);
return;
}
// building the index with the generic indexing function
- var esizesCensus = edgeSizesLookup([reltypestr], criteria)
+ let esizesCensus = edgeSizesLookup([reltypestr], criteria);
// sorting it for the type we need
- var stepToIdsArr = edgeSizesSteps(reltypestr, esizesCensus)
- var steps = stepToIdsArr.length
+ let stepToIdsArr = edgeSizesSteps(reltypestr, esizesCensus);
+ let steps = stepToIdsArr.length;
- if(steps<2) {
- console.log('no size steps for edges: skipping GUI slider init')
- showDisabledSlider(sliderDivID)
+ if (steps<2) {
+ console.log('no size steps for edges: skipping GUI slider init');
+ showDisabledSlider(sliderDivID);
return;
}
// cache initial value
- var initialValue=("0-"+(steps-1));
- TW.gui.lastFilters[`${sliderDivID}/${reltypestr}`] = initialValue
+ let initialValue=('0-'+(steps-1));
+ TW.gui.lastFilters[`${sliderDivID}/${reltypestr}`] = initialValue;
- var present = TW.states.slice(-1)[0];
+ let present = TW.states.slice(-1)[0];
// console.log('init freshslider for edges, steps:', steps, sliderDivID)
- var edgeSlideTimeout = null
+ let edgeSlideTimeout = null;
if (steps == 0) {
- return
+ return;
}
@@ -1006,13 +990,13 @@ function EdgeWeightFilter(sliderDivID , reltypestr , criteria) {
// orange for terms, green for docs/people/contexts, violet for bipartite
switch (reltypestr) {
case '00':
- edgeTypeColor = "#FFA500"
+ edgeTypeColor = '#FFA500';
break;
case '11':
- edgeTypeColor = "#27c470"
+ edgeTypeColor = '#27c470';
break;
case 'XR':
- edgeTypeColor = "#A40DFF"
+ edgeTypeColor = '#A40DFF';
break;
}
@@ -1020,126 +1004,122 @@ function EdgeWeightFilter(sliderDivID , reltypestr , criteria) {
$(sliderDivID).freshslider({
range: true,
step: 1,
- min:0,
+ min: 0,
bgcolor: edgeTypeColor,
- max:steps-1,
- value:[0,steps-1],
- onchange:function(low, high) {
+ max: steps-1,
+ value: [0, steps-1],
+ onchange: function(low, high) {
TW.gui.elHtml.classList.add('waiting');
// 40ms timeout to let the waiting cursor appear
setTimeout(function() {
-
- var totalDeletingTime = 0
+ let totalDeletingTime = 0;
// debounced
- if (edgeSlideTimeout){
+ if (edgeSlideTimeout) {
// console.log('clearing updated function', edgeSlideTimeout)
- clearTimeout(edgeSlideTimeout)
+ clearTimeout(edgeSlideTimeout);
}
// scheduled: costly graph rm edges
- edgeSlideTimeout = setTimeout ( function () {
-
- var filtervalue = low+"-"+high
- var lastvalue = TW.gui.lastFilters[`${sliderDivID}/${reltypestr}`]
-
- if(filtervalue!=lastvalue) {
+ edgeSlideTimeout = setTimeout( function() {
+ let filtervalue = low+'-'+high;
+ let lastvalue = TW.gui.lastFilters[`${sliderDivID}/${reltypestr}`];
+ if (filtervalue!=lastvalue) {
// [ Stopping FA2 ]
- if (TW.partialGraph.isForceAtlas2Running())
- sigma_utils.ourStopFA2();
+ if (TW.partialGraph.isForceAtlas2Running()) {
+sigma_utils.ourStopFA2();
+}
// [ / Stopping FA2 ]
- var t0 = lastvalue.split("-")
- var mint0=parseInt(t0[0]), maxt0=parseInt(t0[1]), mint1=parseInt(low), maxt1=parseInt(high);
- var addflag = false;
- var delflag = false;
+ var t0 = lastvalue.split('-');
+ let mint0=parseInt(t0[0]), maxt0=parseInt(t0[1]), mint1=parseInt(low), maxt1=parseInt(high);
+ let addflag = false;
+ let delflag = false;
- var iterarr = []
+ let iterarr = [];
- if(mint0!=mint1) {
- if(mint0--------|| a la derecha")
}
- if(mint0>mint1) {
+ if (mint0>mint1) {
addflag = true;
// console.log("cotainferior --<||--------|| a la izquierda")
}
- iterarr = calc_range(mint0,mint1).sort(compareNumbers);
+ iterarr = calc_range(mint0, mint1).sort(compareNumbers);
}
- if(maxt0!=maxt1) {
- if(maxt0-- a la derecha")
}
- if(maxt0>maxt1) {
+ if (maxt0>maxt1) {
delflag = true;
// console.log("cotasuperior ||--------<||-- a la izquierda")
}
- iterarr = calc_range(maxt0,maxt1).sort(compareNumbers);
+ iterarr = calc_range(maxt0, maxt1).sort(compareNumbers);
}
// do the important stuff
// ex iterarr [0:0, 1:1...]
// ex stepToIdsArr [0: [eid1,eid2], 1:[eid3,eid4,eid5]...]
- for( var c in iterarr ) {
-
+ for ( let c in iterarr ) {
var i = iterarr[c];
- var eids = stepToIdsArr[i]
+ let eids = stepToIdsArr[i];
- if(i>=low && i<=high) {
- if(addflag) {
+ if (i>=low && i<=high) {
+ if (addflag) {
// console.log("adding "+ids.join())
- for(var i in eids) {
- let eid = eids[i]
+ for (var i in eids) {
+ let eid = eids[i];
// we need to distinguish between
// - absent edges
// => keep them absent because of local
// - edges with absent nodes
// => effect of a node slider => make them appear
- let e = TW.partialGraph.graph.edges(eid)
+ let e = TW.partialGraph.graph.edges(eid);
if (e) {
- let nidkeys = eid.split(';')
+ let nidkeys = eid.split(';');
if (nidkeys.length != 2) {
- console.error("invalid edge id:" + eid)
- }
- else {
- let sid = nidkeys[0]
- let tid = nidkeys[1]
+ console.error('invalid edge id:' + eid);
+ } else {
+ let sid = nidkeys[0];
+ let tid = nidkeys[1];
- let src = TW.partialGraph.graph.nodes(sid)
- let tgt = TW.partialGraph.graph.nodes(tid)
+ let src = TW.partialGraph.graph.nodes(sid);
+ let tgt = TW.partialGraph.graph.nodes(tid);
// if nothing is absent (nodes not
// removed by changetype or local view)
if (src && tgt) {
// unhide in case nodeslider hid them
- src.hidden = false
- tgt.hidden = false
- e.hidden = false
+ src.hidden = false;
+ tgt.hidden = false;
+ e.hidden = false;
}
}
}
}
}
} else {
- if(delflag) {
- for(var i in eids) {
- let eid = eids[i]
- if(!isUndef(TW.partialGraph.graph.edges(eid))) {
- var t0 = performance.now()
+ if (delflag) {
+ for (var i in eids) {
+ let eid = eids[i];
+ if (!isUndef(TW.partialGraph.graph.edges(eid))) {
+ var t0 = performance.now();
if (TW.partialGraph.graph.edges(eid)) {
- TW.partialGraph.graph.edges(eid).hidden = true
+ TW.partialGraph.graph.edges(eid).hidden = true;
// TW.Edges[eid].lock = true;
}
- var t1 = performance.now()
+ let t1 = performance.now();
- totalDeletingTime += (t1-t0)
+ totalDeletingTime += (t1-t0);
// console.log("\thiding "+eid)
@@ -1152,305 +1132,304 @@ function EdgeWeightFilter(sliderDivID , reltypestr , criteria) {
}
}
- if(delflag) {
- console.info('totalDeletingTime', totalDeletingTime)
+ if (delflag) {
+ console.info('totalDeletingTime', totalDeletingTime);
// NB: performance with hiding: more or less 8x faster than with dropEdge on large graphs
}
// [ Starting FA2 ]
setTimeout(function() {
- sigma_utils.smartForceAtlas({'duration': 2000}) // shorter FA2 sufficient
- }, 10)
+ sigma_utils.smartForceAtlas({'duration': 2000}); // shorter FA2 sufficient
+ }, 10);
// [ / Starting FA2 ]
// memoize as last value
- TW.gui.lastFilters[`${sliderDivID}/${reltypestr}`] = filtervalue
+ TW.gui.lastFilters[`${sliderDivID}/${reltypestr}`] = filtervalue;
}
// in any case
setTimeout( function() {
- TW.partialGraph.refresh()
- TW.gui.elHtml.classList.remove('waiting')
- }, 40)
-
- }, 1000) // large-ish debounce timeout
-
- }, 40) // wait cursor timeout
-
- }
+ TW.partialGraph.refresh();
+ TW.gui.elHtml.classList.remove('waiting');
+ }, 40);
+ }, 1000); // large-ish debounce timeout
+ }, 40); // wait cursor timeout
+ },
});
}
-
// Execution modes:
// NodeWeightFilter ( "#sliderANodeWeight" , 1)
// NodeWeightFilter ( "#sliderBNodeWeight" , 0)
-function NodeWeightFilter( sliderDivID , tgtNodeKey) {
-
- if (typeof tgtNodeKey == "undefined") {
- throw 'no nodetype'
+function NodeWeightFilter( sliderDivID, tgtNodeKey) {
+ if (typeof tgtNodeKey == 'undefined') {
+ throw 'no nodetype';
}
- if(TW.partialGraph.graph.nNodes() < 2) {
- console.debug('not enough nodes for subsets: skipping GUI slider init')
- showDisabledSlider(sliderDivID)
+ if (TW.partialGraph.graph.nNodes() < 2) {
+ console.debug('not enough nodes for subsets: skipping GUI slider init');
+ showDisabledSlider(sliderDivID);
return;
}
// ids per weight level
// we use live index from prepareSigmaCustomIndices
- let nodesByTypeNSize = TW.partialGraph.graph.getNodesBySize(tgtNodeKey)
+ let nodesByTypeNSize = TW.partialGraph.graph.getNodesBySize(tgtNodeKey);
- var sortedSizes = []
- if (nodesByTypeNSize)
- sortedSizes = Object.keys(nodesByTypeNSize).sort(function(a,b){return a-b})
+ let sortedSizes = [];
+ if (nodesByTypeNSize) {
+sortedSizes = Object.keys(nodesByTypeNSize).sort(function(a, b) {
+return a-b;
+});
+}
- var stepToIdsArr = []
+ let stepToIdsArr = [];
for (let l in sortedSizes) {
-
- var nidsWithThatSize = TW.partialGraph.graph.getNodesBySize(tgtNodeKey, sortedSizes[l])
+ let nidsWithThatSize = TW.partialGraph.graph.getNodesBySize(tgtNodeKey, sortedSizes[l]);
if (nidsWithThatSize.length) {
- stepToIdsArr.push(nidsWithThatSize)
+ stepToIdsArr.push(nidsWithThatSize);
}
}
- var steps = stepToIdsArr.length
+ let steps = stepToIdsArr.length;
// console.warn('NodeWeightFilter: steps', steps)
- if(steps<2) {
- console.debug('no size steps for nodes: skipping GUI slider init')
- showDisabledSlider(sliderDivID)
+ if (steps<2) {
+ console.debug('no size steps for nodes: skipping GUI slider init');
+ showDisabledSlider(sliderDivID);
return;
}
- var nodeSlideTimeout = null
+ let nodeSlideTimeout = null;
// cache initial value
- TW.gui.lastFilters[sliderDivID] = `0-${steps-1}`
+ TW.gui.lastFilters[sliderDivID] = `0-${steps-1}`;
// freshslider widget
$(sliderDivID).freshslider({
range: true,
step: 1,
- min:0,
- max:steps-1,
- bgcolor:( tgtNodeKey==0 )?"#FFA500":"#27c470" ,
- value:[0,steps-1],
+ min: 0,
+ max: steps-1,
+ bgcolor: ( tgtNodeKey==0 )?'#FFA500':'#27c470',
+ value: [0, steps-1],
// handler
- onchange:function(low, high){
-
- var filtervalue = low+"-"+high
+ onchange: function(low, high) {
+ let filtervalue = low+'-'+high;
// [ Stopping FA2 ]
- if (TW.partialGraph.isForceAtlas2Running())
- sigma_utils.ourStopFA2();
+ if (TW.partialGraph.isForceAtlas2Running()) {
+sigma_utils.ourStopFA2();
+}
// [ / Stopping FA2 ]
// debounced
- if (nodeSlideTimeout){
- clearTimeout(nodeSlideTimeout)
+ if (nodeSlideTimeout) {
+ clearTimeout(nodeSlideTimeout);
}
// scheduled: graph rm nodes
- nodeSlideTimeout = setTimeout ( function () {
-
+ nodeSlideTimeout = setTimeout( function() {
// check memoized value to see if any changes needed
- if(filtervalue!=TW.gui.lastFilters[sliderDivID]) {
-
- for(var i in stepToIdsArr) {
- ids = stepToIdsArr[i]
- if(i>=low && i<=high){
- for(var id in ids) {
- ID = ids[id]
+ if (filtervalue!=TW.gui.lastFilters[sliderDivID]) {
+ for (let i in stepToIdsArr) {
+ ids = stepToIdsArr[i];
+ if (i>=low && i<=high) {
+ for (var id in ids) {
+ ID = ids[id];
if (! TW.Nodes[ID]) {
- console.warn ('nodeslider asks for nonexistatn ID', ID)
+ console.warn('nodeslider asks for nonexistatn ID', ID);
continue;
}
TW.Nodes[ID].lock = false;
- if(TW.partialGraph.graph.nodes(ID))
- TW.partialGraph.graph.nodes(ID).hidden = false;
+ if (TW.partialGraph.graph.nodes(ID)) {
+TW.partialGraph.graph.nodes(ID).hidden = false;
+}
}
} else {
- for(var id in ids) {
- ID = ids[id]
+ for (var id in ids) {
+ ID = ids[id];
TW.Nodes[ID].lock = true;
- if(TW.partialGraph.graph.nodes(ID))
- TW.partialGraph.graph.nodes(ID).hidden = true;
+ if (TW.partialGraph.graph.nodes(ID)) {
+TW.partialGraph.graph.nodes(ID).hidden = true;
+}
}
}
}
- TW.gui.lastFilters[sliderDivID] = filtervalue
+ TW.gui.lastFilters[sliderDivID] = filtervalue;
- TW.partialGraph.render()
+ TW.partialGraph.render();
// [ Starting FA2 ]
setTimeout(function() {
- sigma_utils.smartForceAtlas({'duration': 2000}) // shorter FA2 sufficient
- }, 10)
+ sigma_utils.smartForceAtlas({'duration': 2000}); // shorter FA2 sufficient
+ }, 10);
// [ / Starting FA2 ]
}
- }, 1000)
- }
+ }, 1000);
+ },
});
}
function showDisabledSlider(someDivId) {
$(someDivId).freshslider({
range: true,
- step:1,
+ step: 1,
min: 0,
max: 1,
- value:[0, 1],
- enabled: false
+ value: [0, 1],
+ enabled: false,
});
- $(someDivId).css('cursor', 'not-allowed')
+ $(someDivId).css('cursor', 'not-allowed');
}
-//=========================== FILTERS-SLIDERS > ===========================//
+// =========================== FILTERS-SLIDERS > ===========================//
-//============================= < SEARCH > =============================//
-function updateSearchLabels(id,name,type){
+// ============================= < SEARCH > =============================//
+function updateSearchLabels(id, name, type) {
TW.labels.push({
- 'id' : id,
- 'label' : name,
- 'desc': type
+ 'id': id,
+ 'label': name,
+ 'desc': type,
});
}
-//============================ < / SEARCH > ============================//
+// ============================ < / SEARCH > ============================//
-//============================= < OTHER ACTIONS > =============================//
+// ============================= < OTHER ACTIONS > =============================//
function createWaitIcon(idname, width) {
- let icon = document.createElement('img')
+ let icon = document.createElement('img');
- icon.src = TW.conf.paths.ourlibs+'/img/loader.gif'
+ icon.src = TW.conf.paths.ourlibs+'/img/loader.gif';
- icon.style.position = 'absolute'
- icon.style.left = '0'
- icon.style.width = width || '100%'
+ icon.style.position = 'absolute';
+ icon.style.left = '0';
+ icon.style.width = width || '100%';
if (idname) {
- icon.id = idname
+ icon.id = idname;
}
- 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 relDbType = elTgt.dataset.reldocstype;
+ let ndTypeId = elTgt.dataset.nodetype;
+
+ let tabs = document.querySelectorAll('ul#reldocs-tabs > li');
+ for (let 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 divs = document.querySelectorAll('div#reldocs-boxes > div.tab-pane');
- let theId = `rd-${ndTypeId}-${relDbType}`
+ 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")
- }
+ for (let 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 project_conf.json relDB entry
function resetTabs(activetypes, dbconf) {
- let ul = document.getElementById('reldocs-tabs')
- let divs = document.getElementById('reldocs-boxes')
+ let ul = document.getElementById('reldocs-tabs');
+ let divs = document.getElementById('reldocs-boxes');
// remove any previous tabs
- ul.innerHTML = ""
- divs.innerHTML = ""
- TW.gui.reldocTabs = [{},{}]
+ ul.innerHTML = '';
+ divs.innerHTML = '';
+ TW.gui.reldocTabs = [{}, {}];
// used with no args for full reset
if (!activetypes || !dbconf) {
- return
+ return;
}
// for all active nodetypes
for (let nodetypeId in activetypes) {
if (activetypes[nodetypeId]) {
- let possibleAPIs = []
+ let possibleAPIs = [];
if (dbconf[nodetypeId]) {
- if (TW.conf.debug.logSettings)
- console.log ("additional db conf for this source", dbconf[nodetypeId])
- possibleAPIs = dbconf[nodetypeId]
+ if (TW.conf.debug.logSettings) {
+console.log('additional db conf for this source', dbconf[nodetypeId]);
+}
+ possibleAPIs = dbconf[nodetypeId];
}
- let nAPIs = Object.keys(possibleAPIs).length
+ let nAPIs = Object.keys(possibleAPIs).length;
if (nAPIs > 0) {
// some more vars to know which one to activate
- let iAPI = 0
- let didActiveFlag = false
-
- for (var possibleAPI in possibleAPIs){
+ let iAPI = 0;
+ let didActiveFlag = false;
+ for (let possibleAPI in possibleAPIs) {
// the tab's id
- let tabref = `rd-${nodetypeId}-${possibleAPI}`
+ 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}`)
+ 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
+ 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
+ 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)
+ 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
+ newLi.classList.add('active');
+ newContentDiv.classList.add('active', 'in');
+ didActiveFlag = true;
}
// add handler to switch relatedDocsType
- newRDTab.addEventListener('click', function(e){
+ newRDTab.addEventListener('click', function(e) {
// tab mecanism
- activateRDTab(e.target)
+ activateRDTab(e.target);
// no need to run associated query:
// (updateRelatedNodesPanel did it at selection time)
- })
+ });
- iAPI++
+ iAPI++;
}
}
}
@@ -1458,21 +1437,22 @@ function resetTabs(activetypes, dbconf) {
}
-function openGraph(graphPath){
- let serverPrefix = ''
- var pathcomponents = window.location.pathname.split('/')
- for (var i in pathcomponents) {
- if (pathcomponents[i] != 'explorerjs.html')
- serverPrefix += '/'+pathcomponents[i]
+function openGraph(graphPath) {
+ let serverPrefix = '';
+ let pathcomponents = window.location.pathname.split('/');
+ for (let i in pathcomponents) {
+ if (pathcomponents[i] != 'explorerjs.html') {
+serverPrefix += '/'+pathcomponents[i];
+}
}
- var newDataRes = AjaxSync({ "url": window.location.origin+serverPrefix+'/'+graphPath });
+ let newDataRes = AjaxSync({'url': window.location.origin+serverPrefix+'/'+graphPath});
// remove any previous instance and flags
- TW.resetGraph()
+ TW.resetGraph();
- TW.File = graphPath
- mainStartGraph(newDataRes["format"], newDataRes["data"], TW.instance)
- writeLabel(graphPathToLabel(graphPath))
+ TW.File = graphPath;
+ mainStartGraph(newDataRes['format'], newDataRes['data'], TW.instance);
+ writeLabel(graphPathToLabel(graphPath));
}
-//============================= =============================//
+// ============================= =============================//
diff --git a/twmain/extras_explorerjs.js b/twmain/extras_explorerjs.js
index 8f1a61f..482666d 100755
--- a/twmain/extras_explorerjs.js
+++ b/twmain/extras_explorerjs.js
@@ -6,40 +6,57 @@
// settings to function name
TW.gui.colorFuns = {
- 'heatmap': "heatmapColoring",
- 'gradient': "gradientColoring",
- 'cluster': "clusterColoring"
-}
+ 'heatmap': 'heatmapColoring',
+ 'gradient': 'gradientColoring',
+ 'cluster': 'clusterColoring',
+};
// sigma has dynamic attributes.. the functions below return their resp. getters
TW.sigmaAttributes = {
- 'auto-degree' : function(sigInst) { return function(nd) {return sigInst.graph.degree(nd.id)}},
- 'auto-outdegree' : function(sigInst) { return function(nd) {return sigInst.graph.degree(nd.id, 'out')}},
- 'auto-indegree' : function(sigInst) { return function(nd) {return sigInst.graph.degree(nd.id, 'in')}},
- 'auto-size' : function() { return function(nd) {return nd.size}}
-}
+ 'auto-degree': function(sigInst) {
+ return function(nd) {
+return sigInst.graph.degree(nd.id);
+};
+},
+ 'auto-outdegree': function(sigInst) {
+ return function(nd) {
+return sigInst.graph.degree(nd.id, 'out');
+};
+},
+ 'auto-indegree': function(sigInst) {
+ return function(nd) {
+return sigInst.graph.degree(nd.id, 'in');
+};
+},
+ 'auto-size': function() {
+ return function(nd) {
+return nd.size;
+};
+},
+};
// update the Auto-Facets
// (bins over dynamic sigma attributes like degree,
// available since we initialized the sigma instance)
function updateDynamicFacets(optionalFilter) {
- let autoVals = {}
- for (var icat in TW.categories) {
- let nodecat = TW.categories[icat]
- autoVals[nodecat] = {}
+ let autoVals = {};
+ for (let icat in TW.categories) {
+ let nodecat = TW.categories[icat];
+ autoVals[nodecat] = {};
for (var autoAttr in TW.sigmaAttributes) {
if (!optionalFilter || autoAttr == optionalFilter) {
- autoVals[nodecat][autoAttr] = {'map':{},'vals':{'vstr':[],'vnum':[]}}
- let getVal = TW.sigmaAttributes[autoAttr](TW.partialGraph)
- for (var nid of TW.ByType[icat]) {
- let nd = TW.partialGraph.graph.nodes(nid)
+ autoVals[nodecat][autoAttr] = {'map': {}, 'vals': {'vstr': [], 'vnum': []}};
+ let getVal = TW.sigmaAttributes[autoAttr](TW.partialGraph);
+ for (let nid of TW.ByType[icat]) {
+ let nd = TW.partialGraph.graph.nodes(nid);
if (nd) {
- let val = getVal(TW.partialGraph.graph.nodes(nid))
- if (! (val in autoVals[nodecat][autoAttr].map))
- autoVals[nodecat][autoAttr].map[val] = []
- autoVals[nodecat][autoAttr].map[val].push(nid)
- autoVals[nodecat][autoAttr].vals.vnum.push(val)
+ let val = getVal(TW.partialGraph.graph.nodes(nid));
+ if (! (val in autoVals[nodecat][autoAttr].map)) {
+autoVals[nodecat][autoAttr].map[val] = [];
+}
+ autoVals[nodecat][autoAttr].map[val].push(nid);
+ autoVals[nodecat][autoAttr].vals.vnum.push(val);
}
}
}
@@ -48,79 +65,77 @@ function updateDynamicFacets(optionalFilter) {
// console.log("reparse dynamic attr, raw result", autoVals)
- let autoFacets = facetsBinning(autoVals)
+ let autoFacets = facetsBinning(autoVals);
// merge them into clusters
- for (var nodecat in TW.catDict) {
+ for (let nodecat in TW.catDict) {
for (var autoAttr in TW.sigmaAttributes) {
- for (var facet in autoFacets[nodecat]) {
- TW.Facets[nodecat][facet] = autoFacets[nodecat][facet]
+ for (let facet in autoFacets[nodecat]) {
+ TW.Facets[nodecat][facet] = autoFacets[nodecat][facet];
}
}
}
-
}
// Execution: changeGraphAppearanceByFacets( true )
// It reads scanned node-attributes and prepared legends in TW.Facets
// to add the button in the html with the sigmaUtils.gradientColoring(x) listener.
function changeGraphAppearanceByFacets(actypes) {
- if(!TW.conf.colorByAtt) return;
+ if (!TW.conf.colorByAtt) return;
- if (!actypes) actypes = getActivetypesNames()
+ if (!actypes) actypes = getActivetypesNames();
- let currentNbNodes = {}
+ let currentNbNodes = {};
for (var k in actypes) {
- currentNbNodes[actypes[k]] = TW.partialGraph.graph.getNodesByType(TW.catDict[actypes[k]]).length
+ currentNbNodes[actypes[k]] = TW.partialGraph.graph.getNodesByType(TW.catDict[actypes[k]]).length;
}
// create colormenu and 1st default entry
- var color_menu_info = 'By Default';
+ let color_menu_info = 'By Default';
- if( $( "#colorgraph-menu" ).length>0 ) {
+ if ( $( '#colorgraph-menu' ).length>0 ) {
for (var k in actypes) {
- let ty = actypes[k]
+ let ty = actypes[k];
// heading by type iff more than one type
- if (actypes.length > 1)
- color_menu_info += `${ty}`
+ if (actypes.length > 1) {
+color_menu_info += `${ty}`;
+}
- for (var attTitle in TW.Facets[ty]) {
- let nbDomain = currentNbNodes[ty]
+ for (let attTitle in TW.Facets[ty]) {
+ let nbDomain = currentNbNodes[ty];
// attribute counts: nb of classes
// POSS here distinguish [ty][attTitle].classes.length and ranges.length
- let nbOutput = TW.Facets[ty][attTitle].invIdx.length
+ let nbOutput = TW.Facets[ty][attTitle].invIdx.length;
if (nbOutput) {
- let lastClass = TW.Facets[ty][attTitle].invIdx[nbOutput-1]
+ let lastClass = TW.Facets[ty][attTitle].invIdx[nbOutput-1];
if (lastClass.labl && /^_non_numeric_/.test(lastClass.labl) && lastClass.nids) {
if (lastClass.nids.length) {
- nbDomain -= lastClass.nids.length
- }
- else {
- nbOutput -= 1
+ nbDomain -= lastClass.nids.length;
+ } else {
+ nbOutput -= 1;
}
}
}
// note any previous louvains
- if (attTitle == 'clust_louvain') gotPreviousLouvain = true
+ if (attTitle == 'clust_louvain') gotPreviousLouvain = true;
// coloring function
- let colMethod = getColorFunction(attTitle)
+ let colMethod = getColorFunction(attTitle);
// family label :)
- var attLabel ;
+ var attLabel;
if (TW.facetOptions[attTitle] && TW.facetOptions[attTitle]['legend']) {
- attLabel = TW.facetOptions[attTitle]['legend']
- }
- else attLabel = attTitle
+ attLabel = TW.facetOptions[attTitle]['legend'];
+ } else attLabel = attTitle;
color_menu_info += `
By ${attLabel} (${nbOutput} | ${nbDomain})
- `
+ `;
// for ex country with 26 classes on 75 nodes:
//
@@ -128,20 +143,18 @@ function changeGraphAppearanceByFacets(actypes) {
// By Country (26 | 75)
//
//
-
}
// if had not been already done, louvain added manually
if (!TW.Facets[ty]['clust_louvain']) {
- color_menu_info += `By Louvain clustering ( ? | ${currentNbNodes[ty]})`
+ color_menu_info += `By Louvain clustering ( ? | ${currentNbNodes[ty]})`;
}
-
}
// for debug
// console.warn('color_menu_info', color_menu_info)
- $("#colorgraph-menu").html(color_menu_info)
+ $('#colorgraph-menu').html(color_menu_info);
}
// Legend slots were prepared in TW.Facets
@@ -149,123 +162,121 @@ function changeGraphAppearanceByFacets(actypes) {
function getColorFunction(attTitle) {
// coloringFunction name as str
- var colMethod
+ let colMethod;
// read from user settings
if (TW.facetOptions[attTitle] && TW.facetOptions[attTitle]['col']) {
- colMethod = TW.gui.colorFuns[TW.facetOptions[attTitle]['col']]
+ colMethod = TW.gui.colorFuns[TW.facetOptions[attTitle]['col']];
}
// fallback guess-values
if (! colMethod) {
- if(attTitle.indexOf("clust")>-1||attTitle.indexOf("class")>-1) {
+ if (attTitle.indexOf('clust')>-1||attTitle.indexOf('class')>-1) {
// for classes and clusters
- colMethod = "clusterColoring"
- }
- else {
- colMethod = "gradientColoring"
+ colMethod = 'clusterColoring';
+ } else {
+ colMethod = 'gradientColoring';
}
}
- return colMethod
+ return colMethod;
}
// @cb: optional callback
function RunLouvain(cb) {
+ let node_realdata = [];
+ let nodesV = getVisibleNodes();
+ for (let n in nodesV) {
+node_realdata.push( nodesV[n].id );
+}
- var node_realdata = []
- var nodesV = getVisibleNodes()
- for(var n in nodesV)
- node_realdata.push( nodesV[n].id )
-
- var edge_realdata = []
- var edgesV = getVisibleEdges()
- for(var e in edgesV) {
- var st = edgesV[e].id.split(";")
- var info = {
- "source":st[0],
- "target":st[1],
- "weight":edgesV[e].weight
- }
- edge_realdata.push(info)
+ let edge_realdata = [];
+ let edgesV = getVisibleEdges();
+ for (let e in edgesV) {
+ let st = edgesV[e].id.split(';');
+ let info = {
+ 'source': st[0],
+ 'target': st[1],
+ 'weight': edgesV[e].weight,
+ };
+ edge_realdata.push(info);
}
- var community = jLouvain().nodes(node_realdata).edges(edge_realdata);
- var results = community();
+ let community = jLouvain().nodes(node_realdata).edges(edge_realdata);
+ let results = community();
- var louvainValNids = {}
+ let louvainValNids = {};
- for(var i in results) {
- let n = TW.partialGraph.graph.nodes(i) // <= new way: like all other colors
+ for (let i in results) {
+ let n = TW.partialGraph.graph.nodes(i); // <= new way: like all other colors
if (n) {
- n.attributes["clust_louvain"] = results[i]
+ n.attributes['clust_louvain'] = results[i];
}
// also create legend's facets
- louvainValNids = updateValueFacets(louvainValNids, n, "clust_louvain")
+ louvainValNids = updateValueFacets(louvainValNids, n, 'clust_louvain');
}
- let nClasses = 0
- for (let typ in louvainValNids) {
- let reinvIdx = louvainValNids[typ]["clust_louvain"]['map']
+ let nClasses = 0;
+ for (let typ in louvainValNids) {
+ let reinvIdx = louvainValNids[typ]['clust_louvain']['map'];
// init a new legend in TW.Facets
- TW.Facets[typ]['clust_louvain'] = {'meta':{}, 'invIdx':[]}
+ TW.Facets[typ]['clust_louvain'] = {'meta': {}, 'invIdx': []};
for (let entry in reinvIdx) {
- let len = reinvIdx[entry].length
+ let len = reinvIdx[entry].length;
if (len) {
TW.Facets[typ]['clust_louvain'].invIdx.push({
'labl': `cluster n°${entry} (${len})`,
'fullLabl': `${typ}||Louvain||${entry} (${len})`,
'nids': reinvIdx[entry],
- 'val': entry
- })
- nClasses ++
+ 'val': entry,
+ });
+ nClasses ++;
}
}
}
// finally update the html menu
- let menu = document.getElementById('louvainN')
+ let menu = document.getElementById('louvainN');
if (menu) {
- menu.innerHTML = nClasses
+ menu.innerHTML = nClasses;
}
if (! TW.facetOptions['clust_louvain']) {
- TW.facetOptions['clust_louvain'] = {'col': 'cluster'}
+ TW.facetOptions['clust_louvain'] = {'col': 'cluster'};
}
// update state and menu
- TW.SystemState().LouvainFait = true
- changeGraphAppearanceByFacets()
+ TW.SystemState().LouvainFait = true;
+ changeGraphAppearanceByFacets();
// callback
if (cb && typeof cb == 'function') {
- cb()
+ cb();
}
}
-
// Highlights nodes with given value using id map
// previously: highlighted nodes with given value using loop on node values
function SomeEffect( ValueclassCode ) {
// console.debug("highlighting:", ValueclassCode )
- cancelSelection(false, {"norender": true})
+ cancelSelection(false, {'norender': true});
- TW.gui.selectionActive = true
+ TW.gui.selectionActive = true;
- var nodes_2_colour = {};
- var edges_2_colour = {};
+ let nodes_2_colour = {};
+ let edges_2_colour = {};
// ex: ISItermsriskV2_140 & ISItermsriskV2_140::clust_default::7
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv vvvvv v
// type Cluster key class iClu
// (family) (array index)
- var raw = ValueclassCode.split("::")
- var nodeType=raw[0],
+ let raw = ValueclassCode.split('::');
+ let nodeType=raw[0],
cluType=raw[1],
iClu=Number(raw[2]);
@@ -274,43 +285,44 @@ function SomeEffect( ValueclassCode ) {
// we still filter it due to Level or sliders filters
filteredNodes = TW.Facets[nodeType][cluType].invIdx[iClu].nids.filter(
- function(nid){
- return Boolean(TW.partialGraph.graph.nodes(nid))
+ function(nid) {
+ return Boolean(TW.partialGraph.graph.nodes(nid));
}
- )
+ );
if (filteredNodes.length) {
- TW.instance.selNgn.MultipleSelection2(
+ TW.instance.selNgn.multipleSelection2(
{nodes: filteredNodes}
- )
+ );
}
- TW.partialGraph.refresh()
+ TW.partialGraph.refresh();
}
// some colorings cases also modify size and label
-function graphResetLabelsAndSizes(){
- for(let nid in TW.Nodes) {
- let n = TW.partialGraph.graph.nodes(nid)
+function graphResetLabelsAndSizes() {
+ for (let nid in TW.Nodes) {
+ let n = TW.partialGraph.graph.nodes(nid);
if (n) {
- n.label = TW.Nodes[n.id].label
- n.size = TW.Nodes[n.id].size
+ n.label = TW.Nodes[n.id].label;
+ n.size = TW.Nodes[n.id].size;
}
}
}
function graphResetAllColors() {
- graphResetLabelsAndSizes()
- TW.gui.handpickedcolorsReset()
- updateColorsLegend()
+ graphResetLabelsAndSizes();
+ TW.gui.handpickedcolorsReset();
+ updateColorsLegend();
}
// removes selectively for an array of nodetypes
-function clearColorLegend (forTypes) {
+function clearColorLegend(forTypes) {
// console.log('clearColorLegend', forTypes)
- for (var ty of forTypes) {
- let legTy = document.getElementById("legend-for-"+ty)
- if (legTy)
- legTy.remove()
+ for (let ty of forTypes) {
+ let legTy = document.getElementById('legend-for-'+ty);
+ if (legTy) {
+legTy.remove();
+}
}
}
@@ -318,74 +330,71 @@ function clearColorLegend (forTypes) {
// @daclass: the name of a numeric/categorical attribute from node.attributes
// @forTypes: array of which typenames are concerned
// @groupingTicks: an optional threshold's array expressing ranges with their low/up bounds label and ref to matchin nodeIds
-function updateColorsLegend ( daclass, forTypes, groupedByTicks ) {
-
+function updateColorsLegend( daclass, forTypes, groupedByTicks ) {
// console.warn("making legend forTypes", forTypes)
// shortcut to erase legends for all types
- if(daclass == null) {
- clearColorLegend(TW.categories)
- $("#legend-for-facets").html("")
+ if (daclass == null) {
+ clearColorLegend(TW.categories);
+ $('#legend-for-facets').html('');
};
// current display among TW.categories (ex: ['terms'])
if (typeof forTypes == 'undefined' || ! forTypes.length) {
- forTypes = getActivetypesNames().filter(function(ty){
- return daclass in TW.Facets[ty]
- })
+ forTypes = getActivetypesNames().filter(function(ty) {
+ return daclass in TW.Facets[ty];
+ });
}
// (we ignore other types: their color legends remain the same by default)
- for (var k in forTypes) {
- let curType = forTypes[k]
- var LegendDiv = ""
+ for (let k in forTypes) {
+ let curType = forTypes[k];
+ let LegendDiv = '
';
// all infos in a bin array
- var legendInfo = []
+ var legendInfo = [];
// sample node color
- var ClustNB_CurrentColor = {}
+ let ClustNB_CurrentColor = {};
// passed as arg or prepared in parseCustom
if (!groupedByTicks && (!TW.Facets[curType] || !TW.Facets[curType][daclass])) {
- console.warn(`no class bins for ${curType} ${daclass}`)
- }
- else {
- let daclassLabel = daclass
- if (TW.facetOptions
+ console.warn(`no class bins for ${curType} ${daclass}`);
+ } else {
+ let daclassLabel = daclass;
+ if (TW.facetOptions
&& TW.facetOptions[daclass]
&& TW.facetOptions[daclass].legend) {
- daclassLabel = TW.facetOptions[daclass].legend
+ daclassLabel = TW.facetOptions[daclass].legend;
}
- LegendDiv += `
${curType}: ${daclassLabel}
`
- LegendDiv += '
'
+ LegendDiv += ' ';
+ LegendDiv += '
';
+ LegendDiv += '
';
- let perhapsPreviousLegend = document.getElementById("legend-for-"+curType)
+ let perhapsPreviousLegend = document.getElementById('legend-for-'+curType);
if (perhapsPreviousLegend) {
- perhapsPreviousLegend.outerHTML = LegendDiv
- }
- else {
- let newLegend = document.createElement('div')
- $("#legend-for-facets").prepend(newLegend)
- newLegend.outerHTML = LegendDiv
+ perhapsPreviousLegend.outerHTML = LegendDiv;
+ } else {
+ let newLegend = document.createElement('div');
+ $('#legend-for-facets').prepend(newLegend);
+ newLegend.outerHTML = LegendDiv;
}
}
}
- $("#legend-for-facets").show()
+ $('#legend-for-facets').show();
}
// = = = = = = = = = = = [ / Clusters Plugin ] = = = = = = = = = = = //
@@ -508,18 +521,18 @@ function updateColorsLegend ( daclass, forTypes, groupedByTicks ) {
// queryForType:
// prepare query words from "selections of a given nodetype"
-function queryForType(ntypeId){
+function queryForType(ntypeId) {
let subSels = TW.SystemState().selectionNids.filter(function(nid) {
- return TW.catDict[TW.Nodes[nid].type] == ntypeId
- })
+ return TW.catDict[TW.Nodes[nid].type] == ntypeId;
+ });
- let qWordsForType = []
- for (var j in subSels) {
- let n = TW.Nodes[subSels[j]]
- qWordsForType.push(n.label)
+ let qWordsForType = [];
+ for (let j in subSels) {
+ let n = TW.Nodes[subSels[j]];
+ qWordsForType.push(n.label);
}
- return qWordsForType
+ return qWordsForType;
}
@@ -533,59 +546,58 @@ function queryForType(ntypeId){
// - chosenAPI: the API "switch" dbtype ('twitter'||'csv'||'CortextDB')
// - tgtDivId: the div #id to update
function getTopPapers(qWords, nodetypeId, chosenAPI, tgtDivId) {
-
// waiting image
let image='
';
- document.getElementById(tgtDivId).innerHTML = image
+ document.getElementById(tgtDivId).innerHTML = image;
// args and defaults
- if (! chosenAPI) chosenAPI = TW.conf.relatedDocsType
+ if (! chosenAPI) chosenAPI = TW.conf.relatedDocsType;
- let apiurl = TW.conf.relatedDocsAPIS[chosenAPI]
+ let apiurl = TW.conf.relatedDocsAPIS[chosenAPI];
if (! apiurl) {
- apiurl = TW.conf.relatedDocsAPI
+ apiurl = TW.conf.relatedDocsAPI;
}
let cbDisplay = function(jsonData) {
// console.log("cbDisplay", jsonData)
- return displayTopPapers(jsonData, nodetypeId, chosenAPI, tgtDivId)
- }
+ return displayTopPapers(jsonData, nodetypeId, chosenAPI, tgtDivId);
+ };
let stockErrMsg = `
The API ${chosenAPI} couldn't be connected to.
The queried route found in TW.conf was: ${apiurl}
-
Check if it is running and accessible.
`
+
Check if it is running and accessible.
`;
- let resHTML = ''
+ let resHTML = '';
- if (chosenAPI == "twitter") {
- let joinedQ = qWords.map(function(w){return'('+w+')'}).join(' AND ')
+ if (chosenAPI == 'twitter') {
+ let joinedQ = qWords.map(function(w) {
+return '('+w+')';
+}).join(' AND ');
$.ajax({
type: 'GET',
url: apiurl,
data: {'query': joinedQ},
- contentType: "application/json",
- success : function(data){
+ contentType: 'application/json',
+ success: function(data) {
if (data.length) {
- cbDisplay(data)
- }
- else {
+ cbDisplay(data);
+ } else {
cbDisplay([{
- "error": `The query
+ 'error': `
The query
${joinedQ} delivers
- no results on Twitter.
`
- }])
+ no results on Twitter.`,
+ }]);
}
},
- error: function(){
- console.log(`Not found: relatedDocs for ${apiurl}`)
- cbDisplay([{ "error": stockErrMsg }])
- }
+ error: function() {
+ console.log(`Not found: relatedDocs for ${apiurl}`);
+ cbDisplay([{'error': stockErrMsg}]);
+ },
});
- }
- else {
- let thisRelDocsConf = TW.currentRelDocsDBs[nodetypeId][chosenAPI]
+ } else {
+ let thisRelDocsConf = TW.currentRelDocsDBs[nodetypeId][chosenAPI];
// /!\ documentation and specification needed for the php use cases /!\
let joinedQ = JSON.stringify(qWords).split('&').join('__and__');
// cf. the php code for these url args:
@@ -594,20 +606,20 @@ function getTopPapers(qWords, nodetypeId, chosenAPI, tgtDivId) {
// (we send it as param because phpAPI supports different dbtypes)
// POSS object + join.map(join)
- let urlParams = "ndtype="+nodetypeId+"&dbtype="+chosenAPI+"&query="+joinedQ+"&gexf="+TW.File+"&n="+TW.conf.relatedDocsMax ;
+ let urlParams = 'ndtype='+nodetypeId+'&dbtype='+chosenAPI+'&query='+joinedQ+'&gexf='+TW.File+'&n='+TW.conf.relatedDocsMax;
$.ajax({
type: 'GET',
url: apiurl + '/info_div.php',
data: urlParams,
- contentType: "application/json",
- success : function(data){
- cbDisplay(data.hits)
+ contentType: 'application/json',
+ success: function(data) {
+ cbDisplay(data.hits);
+ },
+ error: function() {
+ console.log(`Not found: relatedDocs for ${apiurl}`);
+ cbDisplay([{'error': stockErrMsg}]);
},
- error: function(){
- console.log(`Not found: relatedDocs for ${apiurl}`)
- cbDisplay([{ "error": stockErrMsg }])
- }
});
}
}
@@ -616,189 +628,176 @@ function getTopPapers(qWords, nodetypeId, chosenAPI, tgtDivId) {
// 1) project-local data/myproject/hit_templates directory
// 2) app default twlibs/default_hit_templates directory
function makeRendererFromTemplate(tmplName) {
- let tmplStr = ''
- let tmplURL
- let gotTemplate
+ let tmplStr = '';
+ let tmplURL;
+ let gotTemplate;
let defRenderer = function(jsonHit) {
- return JSON.stringify(jsonHit, null, ' ').replace(/\n/g, '
') + '
'
- }
+ return JSON.stringify(jsonHit, null, ' ').replace(/\n/g, '
') + '
';
+ };
if (! tmplName) {
- return defRenderer
+ return defRenderer;
}
// (1)
if (TW.Project) {
- tmplURL = TW.Project + '/hit_templates/' + tmplName + '.html' ;
+ tmplURL = TW.Project + '/hit_templates/' + tmplName + '.html';
if (linkCheck(tmplURL)) {
- gotTemplate = AjaxSync({ url: tmplURL });
+ gotTemplate = AjaxSync({url: tmplURL});
}
}
// (2)
if (! gotTemplate || ! gotTemplate['OK']) {
- tmplURL = TW.conf.paths.templates + '/' + tmplName + '.html'
+ tmplURL = TW.conf.paths.templates + '/' + tmplName + '.html';
if (linkCheck(tmplURL)) {
- gotTemplate = AjaxSync({ url: tmplURL });
+ gotTemplate = AjaxSync({url: tmplURL});
}
}
if (gotTemplate && gotTemplate['OK']) {
- tmplStr = gotTemplate.data
+ tmplStr = gotTemplate.data;
// we return a customized renderJsonToHtml function
return function(jsonHit) {
- let htmlOut = tmplStr
+ let htmlOut = tmplStr;
for (key in jsonHit) {
// our tags look like this in the template ==> by $${author}, [$${date}]
- let reKey = new RegExp('\\$\\$\\{'+key+'\\}', 'g')
+ let reKey = new RegExp('\\$\\$\\{'+key+'\\}', 'g');
// we replace them by value
- htmlOut = htmlOut.replace(reKey, jsonHit[key])
+ htmlOut = htmlOut.replace(reKey, jsonHit[key]);
}
// we also replace any not found keys by 'N/A'
- let reKeyAll = new RegExp('\\$\\$\\{[^\\}]+\\}', 'g')
- htmlOut = htmlOut.replace(reKeyAll, "N/A")
+ let reKeyAll = new RegExp('\\$\\$\\{[^\\}]+\\}', 'g');
+ htmlOut = htmlOut.replace(reKeyAll, 'N/A');
- return htmlOut
- }
- }
- else {
+ return htmlOut;
+ };
+ } else {
console.error(`couldn't find template ${tmplName} at ${tmplURL},
- using raw hits display`)
- return defRenderer
+ using raw hits display`);
+ return defRenderer;
}
}
function displayTopPapers(jsonHits, ndtypeId, chosenAPI, targetDiv) {
-
// console.log('jsonHits', jsonHits)
- let resHTML = ''
- let toHtmlFun = function(){}
+ let resHTML = '';
+ let toHtmlFun = function() {};
if (chosenAPI == 'twitter') {
- toHtmlFun = renderTweet
- }
- else if (chosenAPI == "CortextDB" || chosenAPI == "csv") {
- let thisRelDocsConf = TW.currentRelDocsDBs[ndtypeId][chosenAPI]
+ toHtmlFun = renderTweet;
+ } else if (chosenAPI == 'CortextDB' || chosenAPI == 'csv') {
+ let thisRelDocsConf = TW.currentRelDocsDBs[ndtypeId][chosenAPI];
if (thisRelDocsConf && thisRelDocsConf.template) {
- toHtmlFun = makeRendererFromTemplate(thisRelDocsConf.template)
- }
- else {
- console.warn(`no rendering template found in ${TW.conf.paths.sourceMenu} for this source ${TW.File}...`)
+ toHtmlFun = makeRendererFromTemplate(thisRelDocsConf.template);
+ } else {
+ console.warn(`no rendering template found in ${TW.conf.paths.sourceMenu} for this source ${TW.File}...`);
// try the universal template
- toHtmlFun = makeRendererFromTemplate("universal")
+ toHtmlFun = makeRendererFromTemplate('universal');
}
}
- for (var k in jsonHits) {
- let hitJson = jsonHits[k]
+ for (let k in jsonHits) {
+ let hitJson = jsonHits[k];
if (hitJson.error) {
- resHTML += hitJson.error
- }
- else {
- resHTML += toHtmlFun(hitJson)
+ resHTML += hitJson.error;
+ } else {
+ resHTML += toHtmlFun(hitJson);
}
}
- resHTML += '
'
+ resHTML += '
';
// effect the changes in topPapers
- document.getElementById(targetDiv).innerHTML = resHTML
+ document.getElementById(targetDiv).innerHTML = resHTML;
}
function newPopup(url) {
- popupWindow = window.open(url,'popUpWindow','height=700,width=800,left=10,top=10,resizable=yes,scrollbars=yes,toolbar=no,menubar=no,location=no,directories=no,status=no')
+ popupWindow = window.open(url, 'popUpWindow', 'height=700,width=800,left=10,top=10,resizable=yes,scrollbars=yes,toolbar=no,menubar=no,location=no,directories=no,status=no');
}
function clickInsideTweet(e, tweetSrcUrl) {
- e.preventDefault()
- var tgt = e.target
- let max = 5
- if (tgt.tagName.toLowerCase() == "a") {
- window.open(tgt.href, "Link in tweet")
- }
- else {
+ e.preventDefault();
+ let tgt = e.target;
+ let max = 5;
+ if (tgt.tagName.toLowerCase() == 'a') {
+ window.open(tgt.href, 'Link in tweet');
+ } else {
while (tgt = tgt.parentElement) {
- if (tgt.tagName.toLowerCase() == "a"
+ if (tgt.tagName.toLowerCase() == 'a'
|| tgt.classList.contains('Tweet')
|| tgt.id == 'sidebar'
|| max <= 0) {
- break
- }
- else {
- max--
+ break;
+ } else {
+ max--;
}
}
- if (tgt.tagName.toLowerCase() == "a") {
- window.open(tgt.href, "Parent Link in tweet")
- }
- else {
- window.open(tweetSrcUrl, "Source Tweet")
+ if (tgt.tagName.toLowerCase() == 'a') {
+ window.open(tgt.href, 'Parent Link in tweet');
+ } else {
+ window.open(tweetSrcUrl, 'Source Tweet');
}
}
}
-function formatDateLikeTwitter (aDate) {
+function formatDateLikeTwitter(aDate) {
// partly inspired by https://github.com/hijonathan/moment.twitter/
- let msDiff = Date.now() - aDate
+ let msDiff = Date.now() - aDate;
- let resStr = ''
+ let resStr = '';
if (msDiff < 6000) {
- resStr = parseInt(msDiff/1000)+'s'
- }
- else if (msDiff < 36000) {
- resStr = parseInt(msDiff/6000)+'m'
- }
- else if (msDiff < 86400000) {
- resStr = parseInt(msDiff/36000)+'h'
- }
- else if (msDiff < 86400000) {
- resStr = parseInt(msDiff/36000)+'h'
- }
- else if (msDiff < 6048e5) {
- resStr = parseInt(msDiff/86400000)+'d'
- }
- else {
+ resStr = parseInt(msDiff/1000)+'s';
+ } else if (msDiff < 36000) {
+ resStr = parseInt(msDiff/6000)+'m';
+ } else if (msDiff < 86400000) {
+ resStr = parseInt(msDiff/36000)+'h';
+ } else if (msDiff < 86400000) {
+ resStr = parseInt(msDiff/36000)+'h';
+ } else if (msDiff < 6048e5) {
+ resStr = parseInt(msDiff/86400000)+'d';
+ } else {
resStr = aDate.asDate.toLocaleDateString(
'en-US',
- { 'year': 'numeric',
+ {'year': 'numeric',
'month': 'short',
- 'day': 'numeric' }
- )
+ 'day': 'numeric'}
+ );
}
- return ' · '+resStr
+ return ' · '+resStr;
}
function renderTweet( tweet) {
- var tweet_links = true
+ let tweet_links = true;
- var tweetText = tweet.text
+ let tweetText = tweet.text;
// raw links
- tweetText = tweetText.replace(/(https?:\/\/[\w\.\/\_]+)/g, '$1')
+ tweetText = tweetText.replace(/(https?:\/\/[\w\.\/\_]+)/g, '$1');
// #hashtags
- tweetText = tweetText.replace(/#(\w+)/g, '#$1')
+ tweetText = tweetText.replace(/#(\w+)/g, '#$1');
// @users
- tweetText = tweetText.replace(/@(\w+)/g, '@$1')
+ tweetText = tweetText.replace(/@(\w+)/g, '@$1');
// date
- var tweetDate = new Date(tweet.created_at)
-
- var author_url = "http://twitter.com/"+tweet["user"]["screen_name"];
- var tweet_url = author_url+"/status/"+tweet["id_str"]
- var image_normal = author_url+"/profile_image?size=original";
- var image_bigger = "";
- if( tweet["user"]["profile_image_url"] ) {
- let saferUrl = tweet["user"]["profile_image_url"].replace(/^http:/, "https:")
- image_normal = saferUrl
- image_bigger = saferUrl.replace("_normal","_bigger")
+ let tweetDate = new Date(tweet.created_at);
+
+ let author_url = 'http://twitter.com/'+tweet['user']['screen_name'];
+ let tweet_url = author_url+'/status/'+tweet['id_str'];
+ let image_normal = author_url+'/profile_image?size=original';
+ let image_bigger = '';
+ if ( tweet['user']['profile_image_url'] ) {
+ let saferUrl = tweet['user']['profile_image_url'].replace(/^http:/, 'https:');
+ image_normal = saferUrl;
+ image_bigger = saferUrl.replace('_normal', '_bigger');
}
- var html = ""
- html += '\t\t'+ '' + '\n';
+ let html = '';
+ html += '\t\t'+ '' + '\n';
html += '\t\t\t'+ '' + '\n';
@@ -834,32 +833,32 @@ function renderTweet( tweet) {
html += '\t\t\t\t'+ '' + '\n';
- if( !isUndef(tweet["retweet_count"]) || !isUndef(tweet["favourites_count"]) ) {
+ if ( !isUndef(tweet['retweet_count']) || !isUndef(tweet['favourites_count']) ) {
html += '\t\t\t\t'+ '