{
this.props.UIUpdateManager("controlPanelVisible");
diff --git a/components/interface/TreeWidget.js b/components/interface/TreeWidget.js
index 7e1eb7068..814abbbac 100644
--- a/components/interface/TreeWidget.js
+++ b/components/interface/TreeWidget.js
@@ -21,10 +21,11 @@ export default class TreeWidget extends React.Component {
super(props);
this.state = {
- instance: undefined,
+ errors: undefined,
dataTree: undefined,
root: undefined,
loading: false,
+ edges: undefined,
nodes: undefined,
nodeSelected: undefined,
displayColorPicker: false,
@@ -33,21 +34,26 @@ export default class TreeWidget extends React.Component {
this.initTree = this.initTree.bind(this);
this.getNodes = this.getNodes.bind(this);
- this.sortData = this.sortData.bind(this);
this.restPost = this.restPost.bind(this);
this.nodeClick = this.nodeClick.bind(this);
this.updateTree = this.updateTree.bind(this);
this.getButtons = this.getButtons.bind(this);
this.selectNode = this.selectNode.bind(this);
- this.convertEdges = this.convertEdges.bind(this);
- this.convertNodes = this.convertNodes.bind(this);
this.findChildren = this.findChildren.bind(this);
- this.searchChildren = this.searchChildren.bind(this);
this.insertChildren = this.insertChildren.bind(this);
+ this.updateSubtitle = this.updateSubtitle.bind(this);
this.monitorMouseClick = this.monitorMouseClick.bind(this);
- this.defaultComparator = this.defaultComparator.bind(this);
this.convertDataForTree = this.convertDataForTree.bind(this);
- this.parseGraphResultData = this.parseGraphResultData.bind(this);
+
+ this.isNumber = require('./VFBTree/helper').isNumber;
+ this.sortData = require('./VFBTree/helper').sortData;
+ this.findRoot = require('./VFBTree/helper').findRoot;
+ this.convertEdges = require('./VFBTree/helper').convertEdges;
+ this.convertNodes = require('./VFBTree/helper').convertNodes;
+ this.searchChildren = require('./VFBTree/helper').searchChildren;
+ this.defaultComparator = require('./VFBTree/helper').defaultComparator;
+ this.parseGraphResultData = require('./VFBTree/helper').parseGraphResultData;
+ this.buildDictClassToIndividual = require('./VFBTree/helper').buildDictClassToIndividual;
this.theme = createMuiTheme({ overrides: { MuiTooltip: { tooltip: { fontSize: "12px" } } } });
this.AUTHORIZATION = "Basic " + btoa("neo4j:vfb");
@@ -65,13 +71,6 @@ export default class TreeWidget extends React.Component {
this.nodeWithColorPicker = undefined;
}
- isNumber (variable) {
- if (isNaN(variable)) {
- return variable;
- } else {
- return parseFloat(variable);
- }
- }
restPost (data) {
var strData = JSON.stringify(data);
return $.ajax({
@@ -87,175 +86,6 @@ export default class TreeWidget extends React.Component {
});
}
- defaultComparator (a, b, key) {
- if (this.isNumber(a[key]) < this.isNumber(b[key])) {
- return -1;
- }
- if (this.isNumber(a[key]) > this.isNumber(b[key])) {
- return 1;
- }
- return 0;
- }
-
- sortData (unsortedArray, key, comparator) {
- // Create a sortable array to return.
- const sortedArray = [ ...unsortedArray ];
-
- // Recursively sort sub-arrays.
- const recursiveSort = (start, end) => {
-
- // If this sub-array is empty, it's sorted.
- if (end - start < 1) {
- return;
- }
-
- const pivotValue = sortedArray[end];
- let splitIndex = start;
- for (let i = start; i < end; i++) {
- const sort = comparator(sortedArray[i], pivotValue, key);
-
- // This value is less than the pivot value.
- if (sort === -1) {
-
- /*
- * If the element just to the right of the split index,
- * isn't this element, swap them.
- */
- if (splitIndex !== i) {
- const temp = sortedArray[splitIndex];
- sortedArray[splitIndex] = sortedArray[i];
- sortedArray[i] = temp;
- }
-
- /*
- * Move the split index to the right by one,
- * denoting an increase in the less-than sub-array size.
- */
- splitIndex++;
- }
-
- /*
- * Leave values that are greater than or equal to
- * the pivot value where they are.
- */
- }
-
- // Move the pivot value to between the split.
- sortedArray[end] = sortedArray[splitIndex];
- sortedArray[splitIndex] = pivotValue;
-
- // Recursively sort the less-than and greater-than arrays.
- recursiveSort(start, splitIndex - 1);
- recursiveSort(splitIndex + 1, end);
- };
-
- // Sort the entire array.
- recursiveSort(0, unsortedArray.length - 1);
- return sortedArray;
- }
-
- convertEdges (edges) {
- var convertedEdges = [];
- edges.forEach(function (edge) {
- var relatType = edge.type.replace("_"," ");
- if (relatType.indexOf("Related") > -1){
- relatType = edge.properties['label'].replace("_"," ");
- }
- if (convertedEdges.length > 0) {
-
- } else {
- convertedEdges.push({
- from: edge.endNode,
- to: edge.startNode,
- label: relatType
- });
- }
- convertedEdges.push({
- from: edge.endNode,
- to: edge.startNode,
- label: relatType
- });
- });
- return convertedEdges;
- }
-
- convertNodes (nodes) {
- var convertedNodes = [];
- nodes.forEach(function (node) {
- var nodeLabel = node.properties['short_form'];
- var displayedLabel = node.properties['label'];
- var description = node.properties['description']
- convertedNodes.push({
- title: displayedLabel,
- subtitle: nodeLabel,
- instanceId: nodeLabel,
- info: description,
- id: node.id,
- })
- });
- return convertedNodes;
- }
-
- parseGraphResultData (data) {
- var nodes = {}, edges = {};
- data.results[0].data.forEach(function (row) {
- row.graph.nodes.forEach(function (n) {
- if (!nodes.hasOwnProperty(n.id)) {
- nodes[n.id] = n;
- }
- });
- row.graph.relationships.forEach(function (r) {
- if (!edges.hasOwnProperty(r.id)) {
- edges[r.id] = r;
- }
- });
- });
- var nodesArray = [], edgesArray = [];
- for (var p in nodes) {
- if (nodes.hasOwnProperty(p)) {
- nodesArray.push(nodes[p]);
- }
- }
- for (var q in edges) {
- if (edges.hasOwnProperty(q)) {
- edgesArray.push(edges[q])
- }
- }
- return { nodes: nodesArray, edges: edgesArray };
- }
-
- searchChildren (array, key, target, label){
- // Define Start and End Index
- let startIndex = 0;
- let endIndex = array.length - 1;
-
- // While Start Index is less than or equal to End Index
- while (startIndex <= endIndex) {
- // Define Middle Index (This will change when comparing )
- let middleIndex = Math.floor((startIndex + endIndex) / 2);
- // Compare Middle Index with Target for match
- if (this.isNumber(array[middleIndex][key]) === this.isNumber(target[key])) {
- // check for target relationship (label)
- if (array[middleIndex].label === label){
- return middleIndex;
- } else {
- // move on if not matching target relationship (label)
- startIndex = middleIndex + 1;
- }
- }
- // Search Right Side Of Array
- if (this.isNumber(target[key]) > this.isNumber(array[middleIndex][key])) {
- startIndex = middleIndex + 1;
- }
- // Search Left Side Of Array
- if (this.isNumber(target[key]) < this.isNumber(array[middleIndex][key])) {
- endIndex = middleIndex - 1;
- }
- }
- // If Target Is Not Found
- return undefined;
- }
-
findChildren (parent, key, familyList, label) {
var childrenList = [];
var childKey = this.searchChildren(familyList, key, parent, label);
@@ -275,7 +105,7 @@ export default class TreeWidget extends React.Component {
return childrenList;
}
- insertChildren (nodes, edges, child) {
+ insertChildren (nodes, edges, child, imagesMap) {
var childrenList = this.findChildren({ from: child.id }, "from", edges, "part of");
// child.images = this.findChildren({ from: child.id }, "from", edges, "INSTANCEOF");
var nodesList = [];
@@ -284,45 +114,33 @@ export default class TreeWidget extends React.Component {
}
var uniqNodes = [...new Set(nodesList)];
- for (var j = uniqNodes.length - 1; j >= 0 ; j--) {
- var node = nodes[this.findChildren({ id: uniqNodes[j] }, "id", nodes)[0]];
- if (node.instanceId.indexOf("VFB_") > -1) {
- child.instanceId = node.instanceId;
- node.subtitle = child.subtitle;
- // child.subtitle = child.subtitle + " " + node.instanceId;
- uniqNodes.splice(j, 1);
- }
- }
-
for ( var j = 0; j < uniqNodes.length; j++) {
var node = nodes[this.findChildren({ id: uniqNodes[j] }, "id", nodes)[0]];
- if (node.instanceId.indexOf("VFB_") > -1) {
- child.instanceId = node.instanceId;
- node.subtitle = child.subtitle;
- } else {
- child.children.push({
- title: node.title,
- subtitle: node.instanceId,
- description: node.info,
- instanceId: node.instanceId,
- id: node.id,
- showColorPicker: false,
- children: []
- });
- this.insertChildren(nodes, edges, child.children[j])
- }
+ let imageId = node.instanceId;
+ child.children.push({
+ title: node.title,
+ subtitle: node.classId,
+ description: node.info,
+ classId: node.classId,
+ instanceId: node.instanceId,
+ id: node.id,
+ showColorPicker: false,
+ children: []
+ });
+ this.insertChildren(nodes, edges, child.children[j], imagesMap)
}
}
- convertDataForTree (nodes, edges, vertix) {
+ convertDataForTree (nodes, edges, vertix, imagesMap) {
// This will create the data structure for the react-sortable-tree library, starting from the vertix node.
var refinedDataTree = [];
for ( var i = 0; i < nodes.length; i++ ) {
if (vertix === nodes[i].id) {
refinedDataTree.push({
title: nodes[i].title,
- subtitle: nodes[i].instanceId,
+ subtitle: nodes[i].classId,
description: nodes[i].info,
+ classId: nodes[i].classId,
instanceId: nodes[i].instanceId,
id: nodes[i].id,
showColorPicker: false,
@@ -333,12 +151,31 @@ export default class TreeWidget extends React.Component {
}
var child = refinedDataTree[0];
// Once the vertix has been established we call insertChildren recursively on each child.
- this.insertChildren(nodes, edges, child);
+ this.insertChildren(nodes, edges, child, imagesMap);
return refinedDataTree;
}
+ updateSubtitle (tree, idSelected) {
+ var node = undefined;
+ if (tree.length !== undefined) {
+ node = tree[0];
+ } else {
+ node = tree;
+ }
+ if (node.instanceId === idSelected || node.classId === idSelected) {
+ node.subtitle = idSelected;
+ }
+ for (let i = 0; i < node.children.length; i++) {
+ this.updateSubtitle(node.children[i], idSelected);
+ }
+ }
+
selectNode (instance) {
if (this.state.nodeSelected !== undefined && this.state.nodeSelected.instanceId !== instance.instanceId) {
+ /*
+ * var treeData = this.state.dataTree;
+ * this.updateSubtitle(treeData, instance.instanceId);
+ */
this.setState({ nodeSelected: instance });
}
}
@@ -354,9 +191,12 @@ export default class TreeWidget extends React.Component {
} else {
innerInstance = instance;
}
+ var idToSearch = innerInstance.getId();
- if (this.state.instance !== undefined && innerInstance.id !== this.state.instance.id) {
- if (innerInstance.id === window.templateID) {
+ if (this.state.nodeSelected !== undefined
+ && idToSearch !== this.state.nodeSelected.instanceId
+ && idToSearch !== this.state.nodeSelected.classId) {
+ if (idToSearch === window.templateID) {
this.selectNode(this.state.dataTree[0])
return;
}
@@ -367,8 +207,7 @@ export default class TreeWidget extends React.Component {
* in the searchQuery in the render to move the tree focus on this node
*/
while (this.state.nodes.length > i) {
- var idToSearch = innerInstance.getId();
- if (idToSearch === this.state.nodes[i]["instanceId"]) {
+ if (idToSearch === this.state.nodes[i]["instanceId"] || idToSearch === this.state.nodes[i]["classId"]) {
node.push(i);
break;
}
@@ -382,23 +221,31 @@ export default class TreeWidget extends React.Component {
initTree (instance) {
// This function is the core and starting point of the component itself
+ var that = this;
this.setState({ loading: true });
this.restPost(treeCypherQuery(instance)).done(data => {
/*
* we take the data provided by the cypher query and consume the until we obtain the treeData that can be given
* to the react-sortable-tree since it understands this data structure
*/
+ if (data.errors.length > 0) {
+ console.log("-- ERROR TREE COMPONENT --");
+ console.log(data.errors);
+ this.setState({ errors: "Error retrieving the data - check the console for additional information" });
+ }
+
if (data.results[0].data.length > 0) {
var dataTree = this.parseGraphResultData(data);
- var vertix = this.findRoot(data.results[0].data[0].graph.nodes);
- var nodes = this.sortData(this.convertNodes(dataTree.nodes), "id", this.defaultComparator);
+ var vertix = this.findRoot(data);
+ var imagesMap = this.buildDictClassToIndividual(data);
+ var nodes = this.sortData(this.convertNodes(dataTree.nodes, imagesMap), "id", this.defaultComparator);
var edges = this.sortData(this.convertEdges(dataTree.edges), "from", this.defaultComparator);
- var treeData = this.convertDataForTree(nodes, edges, vertix);
+ var treeData = this.convertDataForTree(nodes, edges, vertix, imagesMap);
this.setState({
- instance: { id: instance },
+ loading: false,
dataTree: treeData,
root: vertix,
- loading: false,
+ edges: edges,
nodes: nodes,
nodeSelected: (this.props.instance === undefined
? treeData[0]
@@ -413,7 +260,6 @@ export default class TreeWidget extends React.Component {
children: []
}];
this.setState({
- instance: { id: instance },
dataTree: treeData,
root: undefined,
loading: false
@@ -422,29 +268,37 @@ export default class TreeWidget extends React.Component {
});
}
- findRoot (nodes) {
- let min = nodes[0].id;
- for ( let i = 1; i < nodes.length; i++) {
- if (nodes[i].id < min) {
- min = nodes[i].id;
- }
- }
- return min;
- }
-
nodeClick (event, rowInfo) {
- this.selectNode(rowInfo.node);
+ if (event.target.getAttribute("type") !== "button" && (event.target.getAttribute("aria-label") !== "Collapse" || event.target.getAttribute("aria-label") !== "Expand")) {
+ this.selectNode(rowInfo.node);
+ }
}
monitorMouseClick (e) {
- // event handler to monitor when we click outside the color picker and close it.
- if (!(this.colorPickerContainer !== undefined && this.colorPickerContainer !== null && this.colorPickerContainer.contains(e.target))) {
+ const clickCoord = {
+ INSIDE: 'inside',
+ OUTSIDE: 'outside',
+ PICKER_PRESENT: 'picker_present',
+ NODE_PRESENT: 'node_present'
+ };
+
+ let clickCondition = undefined;
+ if (this.colorPickerContainer !== undefined && this.colorPickerContainer !== null) {
+ clickCondition = clickCoord.PICKER_PRESENT;
+ if (!this.colorPickerContainer.contains(e.target)) {
+ clickCondition = clickCoord.OUTSIDE;
+ }
+ }
+
+ switch (clickCondition) {
+ case clickCoord.OUTSIDE:
if (this.nodeWithColorPicker !== undefined) {
this.nodeWithColorPicker.showColorPicker = false;
this.nodeWithColorPicker = undefined;
}
this.colorPickerContainer = undefined;
this.setState({ displayColorPicker: false });
+ break;
}
}
@@ -452,9 +306,16 @@ export default class TreeWidget extends React.Component {
// As per name, provided by the react-sortable-tree api, we use this to attach to each node custom buttons
var buttons = [];
var fillCondition = "unknown";
+ var instanceLoaded = false;
if (rowInfo.node.instanceId.indexOf("VFB_") > -1) {
fillCondition = "3dAvailable";
- if (Instances[rowInfo.node.instanceId] === undefined) {
+ for (var i = 1; i < Instances.length; i++) {
+ if (Instances[i].id !== undefined && Instances[i].id === rowInfo.node.instanceId) {
+ instanceLoaded = true;
+ break;
+ }
+ }
+ if (!instanceLoaded) {
fillCondition = "3dToLoad";
} else {
if ((typeof Instances[rowInfo.node.instanceId].isVisible !== "undefined") && (Instances[rowInfo.node.instanceId].isVisible())) {
@@ -471,7 +332,7 @@ export default class TreeWidget extends React.Component {
aria-hidden="true"
onClick={ e => {
e.stopPropagation();
- rowInfo.node.subtitle = rowInfo.node.instanceId;
+ // rowInfo.node.subtitle = rowInfo.node.instanceId;
this.props.selectionHandler(rowInfo.node.instanceId);
this.setState({ nodeSelected: rowInfo.node });
}} />);
@@ -560,13 +421,24 @@ export default class TreeWidget extends React.Component {
{rowInfo.node.description}
)}>
{
e.stopPropagation();
this.colorPickerContainer = undefined;
- this.props.selectionHandler(rowInfo.node.subtitle);
+ let instanceFound = false;
+ for (let i = 0; i < Instances.length; i++) {
+ if (Instances[i].getId() === rowInfo.node.instanceId) {
+ instanceFound = true;
+ break;
+ }
+ }
+ if (instanceFound && typeof Instances[rowInfo.node.instanceId].isVisible === "function") {
+ this.props.selectionHandler(rowInfo.node.instanceId);
+ } else {
+ this.props.selectionHandler(rowInfo.node.classId);
+ }
this.setState({ nodeSelected: rowInfo.node });
}}>
{rowInfo.node.title}
@@ -590,7 +462,18 @@ export default class TreeWidget extends React.Component {
componentDidMount () {
var that = this;
document.addEventListener('mousedown', this.monitorMouseClick, false);
+
GEPPETTO.on(GEPPETTO.Events.Select, function (instance) {
+ that.updateTree(instance);
+ });
+
+ GEPPETTO.on(GEPPETTO.Events.Instance_deleted, function (parameters) {
+ if (Instances[parameters] !== undefined ) {
+ that.setState({ nodeSelected: undefined });
+ }
+ });
+
+ GEPPETTO.on(GEPPETTO.Events.Instances_created, function () {
that.setState({ displayColorPicker: false });
});
}
diff --git a/components/interface/VFBToolBar.js b/components/interface/VFBToolBar.js
index 2196eb0e7..cf236e62c 100644
--- a/components/interface/VFBToolBar.js
+++ b/components/interface/VFBToolBar.js
@@ -117,19 +117,19 @@ export default class VFBToolBar extends React.Component {
} else if ((verOffset = nAgt.indexOf("MSIE")) != -1) { // In MSIE, the true version is after "MSIE" in userAgent
browserName = "Microsoft Internet Explorer";
fullVersion = nAgt.substring(verOffset + 5);
- } else if ((verOffset = nAgt.indexOf("Chrome")) != -1) { // In Chrome, the true version is after "Chrome"
+ } else if ((verOffset = nAgt.indexOf("Chrome")) != -1) { // In Chrome, the true version is after "Chrome"
browserName = "Chrome";
fullVersion = nAgt.substring(verOffset + 7);
- } else if ((verOffset = nAgt.indexOf("Safari")) != -1) { // In Safari, the true version is after "Safari" or after "Version"
+ } else if ((verOffset = nAgt.indexOf("Safari")) != -1) { // In Safari, the true version is after "Safari" or after "Version"
browserName = "Safari";
fullVersion = nAgt.substring(verOffset + 7);
if ((verOffset = nAgt.indexOf("Version")) != -1) {
fullVersion = nAgt.substring(verOffset + 8);
}
- } else if ((verOffset = nAgt.indexOf("Firefox")) != -1) { // In Firefox, the true version is after "Firefox"
+ } else if ((verOffset = nAgt.indexOf("Firefox")) != -1) { // In Firefox, the true version is after "Firefox"
browserName = "Firefox";
fullVersion = nAgt.substring(verOffset + 8);
- } else if ( (nameOffset = nAgt.lastIndexOf(' ') + 1) < (verOffset = nAgt.lastIndexOf('/')) ) { // In most other browsers, "name/version" is at the end of userAgent
+ } else if ( (nameOffset = nAgt.lastIndexOf(' ') + 1) < (verOffset = nAgt.lastIndexOf('/')) ) { // In most other browsers, "name/version" is at the end of userAgent
browserName = nAgt.substring(nameOffset,verOffset);
fullVersion = nAgt.substring(verOffset + 1);
if (browserName.toLowerCase() == browserName.toUpperCase()) {
@@ -145,10 +145,32 @@ export default class VFBToolBar extends React.Component {
}
majorVersion = parseInt('' + fullVersion,10);
if (isNaN(majorVersion)) {
- fullVersion = '' + parseFloat(navigator.appVersion);
+ fullVersion = '' + parseFloat(navigator.appVersion);
majorVersion = parseInt(navigator.appVersion,10);
}
-
+ // return as much of the log up to the last 10 events < 1000 characters:
+ var logLength = -10;
+ var limitedLog = window.console.logs.slice(logLength).join('%0A').replace(
+ /\&/g,escape('&')
+ ).replace(
+ /\#/g,escape('#')
+ ).replace(
+ /\-/g,'%2D'
+ ).replace(
+ /\+/g,'%2B'
+ );
+ while (limitedLog.length > 1000 && logLength < 0) {
+ logLength += 1;
+ limitedLog = window.console.logs.slice(logLength).join('%0A').replace(
+ /\&/g,escape('&')
+ ).replace(
+ /\#/g,escape('#')
+ ).replace(
+ /\-/g,'%2D'
+ ).replace(
+ /\+/g,'%2B'
+ );
+ }
this.props.htmlOutputHandler(
htmlContent.replace(
/\$URL\$/g,window.location.href.replace(
@@ -165,15 +187,7 @@ export default class VFBToolBar extends React.Component {
).replace(
/\$SCREEN\$/g, window.innerWidth + ',' + window.innerHeight
).replace(
- /\$LOG\$/g, window.console.logs.slice(-10).join('%0A').replace(
- /\&/g,escape('&')
- ).replace(
- /\#/g,escape('#')
- ).replace(
- /\-/g,'%2D'
- ).replace(
- /\+/g,'%2B'
- )
+ /\$LOG\$/g, limitedLog
).replace(
/\$COLOURLOG\$/g, window.console.logs.join('
').replace(
/\&/g,escape('&')
diff --git a/components/interface/VFBTree/helper.js b/components/interface/VFBTree/helper.js
new file mode 100644
index 000000000..084051717
--- /dev/null
+++ b/components/interface/VFBTree/helper.js
@@ -0,0 +1,206 @@
+/* eslint-disable no-prototype-builtins */
+
+const parseGraphResultData = data => {
+ var nodes = {}, edges = {};
+ data.results[0].data.forEach(function (row) {
+ row.graph.nodes.forEach(function (n) {
+ if (!nodes.hasOwnProperty(n.id)) {
+ nodes[n.id] = n;
+ }
+ });
+ row.graph.relationships.forEach(function (r) {
+ if (!edges.hasOwnProperty(r.id)) {
+ edges[r.id] = r;
+ }
+ });
+ });
+ var nodesArray = [], edgesArray = [];
+ for (var p in nodes) {
+ if (nodes.hasOwnProperty(p)) {
+ nodesArray.push(nodes[p]);
+ }
+ }
+ for (var q in edges) {
+ if (edges.hasOwnProperty(q)) {
+ edgesArray.push(edges[q])
+ }
+ }
+ return { nodes: nodesArray, edges: edgesArray };
+}
+
+const findRoot = data => {
+ let columns = data.results[0].columns;
+ let rootIndex = columns.indexOf("root");
+ return data.results[0].data[0].row[rootIndex].toString();
+}
+
+const buildDictClassToIndividual = data => {
+ var dictionaryIndividuals = {};
+ let columns = data.results[0].columns;
+ let imagesIndex = columns.indexOf("image_nodes");
+ let nodes = data.results[0].data[0].row[imagesIndex];
+ for (let i = 0; i < nodes.length; i++) {
+ dictionaryIndividuals[nodes[i].short_form] = {
+ id: nodes[i].node_id,
+ image: nodes[i].image
+ }
+ }
+ return dictionaryIndividuals;
+};
+
+const searchChildren = (array, key, target, label) => {
+ // Define Start and End Index
+ let startIndex = 0;
+ let endIndex = array.length - 1;
+
+ // While Start Index is less than or equal to End Index
+ while (startIndex <= endIndex) {
+ // Define Middle Index (This will change when comparing )
+ let middleIndex = Math.floor((startIndex + endIndex) / 2);
+ // Compare Middle Index with Target for match
+ if (isNumber(array[middleIndex][key]) === isNumber(target[key])) {
+ // check for target relationship (label)
+ if (array[middleIndex].label === label){
+ return middleIndex;
+ } else {
+ // move on if not matching target relationship (label)
+ startIndex = middleIndex + 1;
+ }
+ }
+ // Search Right Side Of Array
+ if (isNumber(target[key]) > isNumber(array[middleIndex][key])) {
+ startIndex = middleIndex + 1;
+ }
+ // Search Left Side Of Array
+ if (isNumber(target[key]) < isNumber(array[middleIndex][key])) {
+ endIndex = middleIndex - 1;
+ }
+ }
+ // If Target Is Not Found
+ return undefined;
+}
+
+const sortData = (unsortedArray, key, comparator) => {
+ // Create a sortable array to return.
+ const sortedArray = [ ...unsortedArray ];
+ // Recursively sort sub-arrays.
+ const recursiveSort = (start, end) => {
+ // If this sub-array is empty, it's sorted.
+ if (end - start < 1) {
+ return;
+ }
+ const pivotValue = sortedArray[end];
+ let splitIndex = start;
+ for (let i = start; i < end; i++) {
+ const sort = comparator(sortedArray[i], pivotValue, key);
+ // This value is less than the pivot value.
+ if (sort === -1) {
+ /*
+ * If the element just to the right of the split index,
+ * isn't this element, swap them.
+ */
+ if (splitIndex !== i) {
+ const temp = sortedArray[splitIndex];
+ sortedArray[splitIndex] = sortedArray[i];
+ sortedArray[i] = temp;
+ }
+ /*
+ * Move the split index to the right by one,
+ * denoting an increase in the less-than sub-array size.
+ */
+ splitIndex++;
+ }
+ /*
+ * Leave values that are greater than or equal to
+ * the pivot value where they are.
+ */
+ }
+ // Move the pivot value to between the split.
+ sortedArray[end] = sortedArray[splitIndex];
+ sortedArray[splitIndex] = pivotValue;
+ // Recursively sort the less-than and greater-than arrays.
+ recursiveSort(start, splitIndex - 1);
+ recursiveSort(splitIndex + 1, end);
+ };
+ // Sort the entire array.
+ recursiveSort(0, unsortedArray.length - 1);
+ return sortedArray;
+}
+
+
+const convertEdges = edges => {
+ var convertedEdges = [];
+ edges.forEach(function (edge) {
+ var relatType = edge.type.replace("_"," ");
+ if (relatType.indexOf("Related") > -1){
+ relatType = edge.properties['label'].replace("_"," ");
+ }
+ if (convertedEdges.length > 0) {
+
+ } else {
+ convertedEdges.push({
+ from: edge.endNode,
+ to: edge.startNode,
+ label: relatType
+ });
+ }
+ convertedEdges.push({
+ from: edge.endNode,
+ to: edge.startNode,
+ label: relatType
+ });
+ });
+ return convertedEdges;
+}
+
+const convertNodes = (nodes, imagesMap) => {
+ var convertedNodes = [];
+ nodes.forEach(function (node) {
+ var nodeLabel = node.properties['short_form'];
+ var nodeImage = nodeLabel;
+ if (imagesMap[nodeLabel] !== undefined && imagesMap[nodeLabel].id.toString() === node.id.toString()) {
+ nodeImage = imagesMap[nodeLabel].image;
+ }
+ var displayedLabel = node.properties['label'];
+ var description = node.properties['description']
+ convertedNodes.push({
+ title: displayedLabel,
+ subtitle: nodeLabel,
+ instanceId: nodeImage,
+ classId: nodeLabel,
+ info: description,
+ id: node.id,
+ })
+ });
+ return convertedNodes;
+}
+
+const defaultComparator = (a, b, key) => {
+ if (isNumber(a[key]) < isNumber(b[key])) {
+ return -1;
+ }
+ if (isNumber(a[key]) > isNumber(b[key])) {
+ return 1;
+ }
+ return 0;
+}
+
+const isNumber = variable => {
+ if (isNaN(variable)) {
+ return variable;
+ } else {
+ return parseFloat(variable);
+ }
+}
+
+module.exports = {
+ isNumber,
+ findRoot,
+ sortData,
+ convertNodes,
+ convertEdges,
+ searchChildren,
+ defaultComparator,
+ parseGraphResultData,
+ buildDictClassToIndividual
+};
diff --git a/css/VFBTermInfo.less b/css/VFBTermInfo.less
index 979414556..ccfaa0493 100644
--- a/css/VFBTermInfo.less
+++ b/css/VFBTermInfo.less
@@ -380,6 +380,10 @@
background-color:#534700 !important;
}
+.label.types>.label.label-Split {
+ background-color:#e012e3 !important;
+}
+
.button-bar-vfbHistoryLinks-back {
position:absolute;
left:10px;
diff --git a/model/vfb.xmi b/model/vfb.xmi
index 90c64d59a..de6d449ef 100644
--- a/model/vfb.xmi
+++ b/model/vfb.xmi
@@ -129,6 +129,9 @@
+
+ name="Query for anatomy from expression "
+ description="Get JSON for anat_2_ep query">
+ query=""statement": "MATCH (ep:Expression_pattern:Class)<-[ar:overlaps|part_of]-(anoni:Individual)-[:INSTANCEOF]->(anat:Class) WHERE ep.short_form in [{ID}] WITH anoni, anat, ar OPTIONAL MATCH (p:pub { short_form: ar.pub}) WITH anat, anoni, { core: { short_form: p.short_form, label: coalesce(p.label,''), iri: p.iri, types: labels(p) } , PubMed: coalesce(p.PMID, ''), FlyBase: coalesce(p.FlyBase, ''), DOI: coalesce(p.DOI, '') } AS pub OPTIONAL MATCH (anoni)-[r:Related]->(o:FBdv) WITH CASE WHEN o IS NULL THEN [] ELSE COLLECT ({ relation: { label: r.label, iri: r.uri, type: type(r) } , object: { short_form: o.short_form, label: coalesce(o.label,''), iri: o.iri, types: labels(o) } }) END AS stages ,anoni,anat,pub OPTIONAL MATCH (anat:Synaptic_neuropil)<-[:has_source|SUBCLASSOF|INSTANCEOF*]-(i:Individual)<-[:depicts]-(channel:Individual)-[irw:in_register_with]->(template:Individual)-[:depicts]->(template_anat:Individual) WITH template, channel, template_anat, irw, anoni, anat, pub, stages , i OPTIONAL MATCH (channel)-[:is_specified_output_of]->(technique:Class) WITH CASE WHEN channel IS NULL THEN [] ELSE COLLECT({ anatomy: { short_form: i.short_form, label: coalesce(i.label,''), iri: i.iri, types: labels(i) } , channel_image: { channel: { short_form: channel.short_form, label: coalesce(channel.label,''), iri: channel.iri, types: labels(channel) } , imaging_technique: { short_form: technique.short_form, label: coalesce(technique.label,''), iri: technique.iri, types: labels(technique) } ,image: { template_channel : { short_form: template.short_form, label: coalesce(template.label,''), iri: template.iri, types: labels(template) } , template_anatomy: { short_form: template_anat.short_form, label: coalesce(template_anat.label,''), iri: template_anat.iri, types: labels(template_anat) } ,image_folder: irw.folder, index: coalesce(irw.index, []) + [] }} }) END AS anatomy_channel_image ,anoni,anat,pub,stages RETURN { short_form: anat.short_form, label: coalesce(anat.label,''), iri: anat.iri, types: labels(anat) } AS anatomy, 'Get JSON for ep_2_anat query' AS query, 'ca9ab19' AS version , pub, stages, anatomy_channel_image", "parameters" : { "ID" : "$ID" }"
+ countQuery=""statement": "MATCH (ep:Class:Expression_pattern)<-[ar:overlaps|part_of]-(anoni:Individual)-[:INSTANCEOF]->(anat:Class) WHERE ep.short_form in [{ID}] RETURN count(anat) as count", "parameters" : { "ID" : "$ID" }"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -424,9 +487,9 @@
@@ -444,9 +507,9 @@
@@ -454,9 +517,9 @@
@@ -881,4 +944,22 @@
+
+
+
+
+
+