diff --git a/js/apps/system/_admin/aardvark/APP/GruntFile.js b/js/apps/system/_admin/aardvark/APP/GruntFile.js
index 72e23b1109..bddd6d6638 100644
--- a/js/apps/system/_admin/aardvark/APP/GruntFile.js
+++ b/js/apps/system/_admin/aardvark/APP/GruntFile.js
@@ -65,6 +65,9 @@
"frontend/js/lib/sigma.plugins.fullScreen.js",
"frontend/js/lib/sigma.layout.fruchtermanReingold.js",
"frontend/js/lib/sigma.exporters.svg.js",
+ "frontend/js/lib/sigma.canvas.edges.labels.curve.js",
+ "frontend/js/lib/sigma.canvas.edges.labels.curvedArrow.js",
+ "frontend/js/lib/sigma.canvas.edges.labels.def.js",
"frontend/js/lib/worker.js",
"frontend/js/lib/supervisor.js",
// END SIGMA LIBRARIES
diff --git a/js/apps/system/_admin/aardvark/APP/aardvark.js b/js/apps/system/_admin/aardvark/APP/aardvark.js
index a0b73bb46a..ae5da384d0 100644
--- a/js/apps/system/_admin/aardvark/APP/aardvark.js
+++ b/js/apps/system/_admin/aardvark/APP/aardvark.js
@@ -292,10 +292,33 @@ authRouter.get('/graph/:name', function (req, res) {
var graph = gm._graph(name);
var vertexName = graph._vertexCollections()[0].name();
- var startVertex = db[vertexName].any();
+
+ var startVertex;
+ var config;
+
+ try {
+ config = req.queryParams;
+ } catch (e) {
+ res.throw('bad request', e.message, {cause: e});
+ }
+
+ if (config.nodeStart) {
+ try {
+ startVertex = db._document(config.nodeStart);
+ } catch (e) {
+ res.throw('bad request', e.message, {cause: e});
+ }
+
+ console.log(startVertex);
+ if (!startVertex) {
+ startVertex = db[vertexName].any();
+ }
+ } else {
+ startVertex = db[vertexName].any();
+ }
var aqlQuery =
- 'FOR v, e, p IN 1..3 ANY "' + startVertex._id + '" GRAPH "' + name + '"' +
+ 'FOR v, e, p IN 1..' + (config.depth || '2') + ' ANY "' + startVertex._id + '" GRAPH "' + name + '"' +
'RETURN p'
;
@@ -307,29 +330,53 @@ authRouter.get('/graph/:name', function (req, res) {
var edgesArr = [];
_.each(cursor.json, function (obj) {
+ var edgeLabel;
+
_.each(obj.edges, function (edge) {
if (edge._to && edge._from) {
+ if (config.edgeLabel) {
+ // configure edge labels
+ edgeLabel = edge[config.edgeLabel];
+
+ if (edgeLabel) {
+ edgeLabel = edgeLabel.toString();
+ }
+
+ if (!edgeLabel) {
+ edgeLabel = 'attribute ' + config.edgeLabel + ' not found';
+ }
+ }
+
edgesObj[edge._from + edge._to] = {
id: edge._id,
source: edge._from,
- color: '#cccccc',
+ label: edgeLabel,
+ color: config.edgeColor || '#cccccc',
target: edge._to
};
}
});
- var label;
+
+ var nodeLabel;
+ var nodeSize;
+
_.each(obj.vertices, function (node) {
- if (node.label) {
- label = node.label;
- } else {
- label = node._id;
+ if (config.nodeLabel) {
+ nodeLabel = node[config.nodeLabel];
+ }
+ if (!nodeLabel) {
+ nodeLabel = node._id;
+ }
+
+ if (config.nodeSize) {
+ nodeSize = node[config.nodeSize];
}
nodesObj[node._id] = {
id: node._id,
- label: label,
- size: Math.random(),
- color: '#2ecc71',
+ label: nodeLabel,
+ size: nodeSize || Math.random(),
+ color: config.nodeColor || '#2ecc71',
x: Math.random(),
y: Math.random()
};
diff --git a/js/apps/system/_admin/aardvark/APP/frontend/js/lib/sigma.canvas.edges.labels.curve.js b/js/apps/system/_admin/aardvark/APP/frontend/js/lib/sigma.canvas.edges.labels.curve.js
new file mode 100644
index 0000000000..69f906ffd2
--- /dev/null
+++ b/js/apps/system/_admin/aardvark/APP/frontend/js/lib/sigma.canvas.edges.labels.curve.js
@@ -0,0 +1,112 @@
+;(function(undefined) {
+ 'use strict';
+
+ if (typeof sigma === 'undefined')
+ throw 'sigma is not declared';
+
+ // Initialize packages:
+ sigma.utils.pkg('sigma.canvas.edges.labels');
+
+ /**
+ * This label renderer will just display the label on the curve of the edge.
+ * The label is rendered at half distance of the edge extremities, and is
+ * always oriented from left to right on the top side of the curve.
+ *
+ * @param {object} edge The edge object.
+ * @param {object} source node The edge source node.
+ * @param {object} target node The edge target node.
+ * @param {CanvasRenderingContext2D} context The canvas context.
+ * @param {configurable} settings The settings function.
+ */
+ sigma.canvas.edges.labels.curve =
+ function(edge, source, target, context, settings) {
+ if (typeof edge.label !== 'string')
+ return;
+
+ var prefix = settings('prefix') || '',
+ size = edge[prefix + 'size'] || 1;
+
+ if (size < settings('edgeLabelThreshold'))
+ return;
+
+ var fontSize,
+ sSize = source[prefix + 'size'],
+ sX = source[prefix + 'x'],
+ sY = source[prefix + 'y'],
+ tX = target[prefix + 'x'],
+ tY = target[prefix + 'y'],
+ dX = tX - sX,
+ dY = tY - sY,
+ sign = (sX < tX) ? 1 : -1,
+ cp = {},
+ c,
+ angle,
+ t = 0.5; //length of the curve
+
+ if (source.id === target.id) {
+ cp = sigma.utils.getSelfLoopControlPoints(sX, sY, sSize);
+ c = sigma.utils.getPointOnBezierCurve(
+ t, sX, sY, tX, tY, cp.x1, cp.y1, cp.x2, cp.y2
+ );
+ angle = Math.atan2(1, 1); // 45°
+ } else {
+ cp = sigma.utils.getQuadraticControlPoint(sX, sY, tX, tY);
+ c = sigma.utils.getPointOnQuadraticCurve(t, sX, sY, tX, tY, cp.x, cp.y);
+ angle = Math.atan2(dY * sign, dX * sign);
+ }
+
+ // The font size is sublineraly proportional to the edge size, in order to
+ // avoid very large labels on screen.
+ // This is achieved by f(x) = x * x^(-1/ a), where 'x' is the size and 'a'
+ // is the edgeLabelSizePowRatio. Notice that f(1) = 1.
+ // The final form is:
+ // f'(x) = b * x * x^(-1 / a), thus f'(1) = b. Application:
+ // fontSize = defaultEdgeLabelSize if edgeLabelSizePowRatio = 1
+ fontSize = (settings('edgeLabelSize') === 'fixed') ?
+ settings('defaultEdgeLabelSize') :
+ settings('defaultEdgeLabelSize') *
+ size *
+ Math.pow(size, -1 / settings('edgeLabelSizePowRatio'));
+
+ context.save();
+
+
+ if (edge.active) {
+ context.font = [
+ settings('activeFontStyle'),
+ fontSize + 'px',
+ settings('activeFont') || settings('font')
+ ].join(' ');
+
+ context.fillStyle =
+ settings('edgeActiveColor') === 'edge' ?
+ (edge.active_color || settings('defaultEdgeActiveColor')) :
+ settings('defaultEdgeLabelActiveColor');
+ }
+ else {
+ context.font = [
+ settings('fontStyle'),
+ fontSize + 'px',
+ settings('font')
+ ].join(' ');
+
+ context.fillStyle =
+ (settings('edgeLabelColor') === 'edge') ?
+ (edge.color || settings('defaultEdgeColor')) :
+ settings('defaultEdgeLabelColor');
+ }
+
+ context.textAlign = 'center';
+ context.textBaseline = 'alphabetic';
+
+ context.translate(c.x, c.y);
+ context.rotate(angle);
+ context.fillText(
+ edge.label,
+ 0,
+ (-size / 2) - 3
+ );
+
+ context.restore();
+ };
+}).call(this);
diff --git a/js/apps/system/_admin/aardvark/APP/frontend/js/lib/sigma.canvas.edges.labels.curvedArrow.js b/js/apps/system/_admin/aardvark/APP/frontend/js/lib/sigma.canvas.edges.labels.curvedArrow.js
new file mode 100644
index 0000000000..e32012c9fa
--- /dev/null
+++ b/js/apps/system/_admin/aardvark/APP/frontend/js/lib/sigma.canvas.edges.labels.curvedArrow.js
@@ -0,0 +1,25 @@
+;(function(undefined) {
+ 'use strict';
+
+ if (typeof sigma === 'undefined')
+ throw 'sigma is not declared';
+
+ // Initialize packages:
+ sigma.utils.pkg('sigma.canvas.edges.labels');
+
+ /**
+ * This label renderer will just display the label on the curve of the edge.
+ * The label is rendered at half distance of the edge extremities, and is
+ * always oriented from left to right on the top side of the curve.
+ *
+ * @param {object} edge The edge object.
+ * @param {object} source node The edge source node.
+ * @param {object} target node The edge target node.
+ * @param {CanvasRenderingContext2D} context The canvas context.
+ * @param {configurable} settings The settings function.
+ */
+ sigma.canvas.edges.labels.curvedArrow =
+ function(edge, source, target, context, settings) {
+ sigma.canvas.edges.labels.curve(edge, source, target, context, settings);
+ };
+}).call(this);
diff --git a/js/apps/system/_admin/aardvark/APP/frontend/js/lib/sigma.canvas.edges.labels.def.js b/js/apps/system/_admin/aardvark/APP/frontend/js/lib/sigma.canvas.edges.labels.def.js
new file mode 100644
index 0000000000..51f4bf96c1
--- /dev/null
+++ b/js/apps/system/_admin/aardvark/APP/frontend/js/lib/sigma.canvas.edges.labels.def.js
@@ -0,0 +1,96 @@
+;(function(undefined) {
+ 'use strict';
+
+ if (typeof sigma === 'undefined')
+ throw 'sigma is not declared';
+
+ // Initialize packages:
+ sigma.utils.pkg('sigma.canvas.edges.labels');
+
+ /**
+ * This label renderer will just display the label on the line of the edge.
+ * The label is rendered at half distance of the edge extremities, and is
+ * always oriented from left to right on the top side of the line.
+ *
+ * @param {object} edge The edge object.
+ * @param {object} source node The edge source node.
+ * @param {object} target node The edge target node.
+ * @param {CanvasRenderingContext2D} context The canvas context.
+ * @param {configurable} settings The settings function.
+ */
+ sigma.canvas.edges.labels.def =
+ function(edge, source, target, context, settings) {
+ if (typeof edge.label !== 'string' || source == target)
+ return;
+
+ var prefix = settings('prefix') || '',
+ size = edge[prefix + 'size'] || 1;
+
+ if (size < settings('edgeLabelThreshold'))
+ return;
+
+ if (0 === settings('edgeLabelSizePowRatio'))
+ throw '"edgeLabelSizePowRatio" must not be 0.';
+
+ var fontSize,
+ x = (source[prefix + 'x'] + target[prefix + 'x']) / 2,
+ y = (source[prefix + 'y'] + target[prefix + 'y']) / 2,
+ dX = target[prefix + 'x'] - source[prefix + 'x'],
+ dY = target[prefix + 'y'] - source[prefix + 'y'],
+ sign = (source[prefix + 'x'] < target[prefix + 'x']) ? 1 : -1,
+ angle = Math.atan2(dY * sign, dX * sign);
+
+ // The font size is sublineraly proportional to the edge size, in order to
+ // avoid very large labels on screen.
+ // This is achieved by f(x) = x * x^(-1/ a), where 'x' is the size and 'a'
+ // is the edgeLabelSizePowRatio. Notice that f(1) = 1.
+ // The final form is:
+ // f'(x) = b * x * x^(-1 / a), thus f'(1) = b. Application:
+ // fontSize = defaultEdgeLabelSize if edgeLabelSizePowRatio = 1
+ fontSize = (settings('edgeLabelSize') === 'fixed') ?
+ settings('defaultEdgeLabelSize') :
+ settings('defaultEdgeLabelSize') *
+ size *
+ Math.pow(size, -1 / settings('edgeLabelSizePowRatio'));
+
+ context.save();
+
+ if (edge.active) {
+ context.font = [
+ settings('activeFontStyle'),
+ fontSize + 'px',
+ settings('activeFont') || settings('font')
+ ].join(' ');
+
+ context.fillStyle =
+ settings('edgeActiveColor') === 'edge' ?
+ (edge.active_color || settings('defaultEdgeActiveColor')) :
+ settings('defaultEdgeLabelActiveColor');
+ }
+ else {
+ context.font = [
+ settings('fontStyle'),
+ fontSize + 'px',
+ settings('font')
+ ].join(' ');
+
+ context.fillStyle =
+ (settings('edgeLabelColor') === 'edge') ?
+ (edge.color || settings('defaultEdgeColor')) :
+ settings('defaultEdgeLabelColor');
+ }
+
+ context.textAlign = 'center';
+ context.textBaseline = 'alphabetic';
+
+ context.translate(x, y);
+ context.rotate(angle);
+ context.fillText(
+ edge.label,
+ 0,
+ (-size / 2) - 3
+ );
+
+ context.restore();
+ };
+}).call(this);
diff --git a/js/apps/system/_admin/aardvark/APP/frontend/js/models/userConfig.js b/js/apps/system/_admin/aardvark/APP/frontend/js/models/userConfig.js
index f69bebba45..eba3139f69 100644
--- a/js/apps/system/_admin/aardvark/APP/frontend/js/models/userConfig.js
+++ b/js/apps/system/_admin/aardvark/APP/frontend/js/models/userConfig.js
@@ -42,7 +42,7 @@ window.UserConfig = Backbone.Model.extend({
}
},
error: function () {
- arangoHelper.arangoNotification('User configuration', 'Could not update user configuration for key: ' + keyName);
+ arangoHelper.arangoError('User configuration', 'Could not update user configuration for key: ' + keyName);
}
});
},
@@ -61,7 +61,7 @@ window.UserConfig = Backbone.Model.extend({
callback(keyValue);
},
error: function () {
- arangoHelper.arangoNotification('User configuration', 'Could not fetch user configuration for key: ' + keyName);
+ arangoHelper.arangoError('User configuration', 'Could not fetch user configuration for key: ' + keyName);
}
});
}
diff --git a/js/apps/system/_admin/aardvark/APP/frontend/js/routers/router.js b/js/apps/system/_admin/aardvark/APP/frontend/js/routers/router.js
index fb00c199e7..76d20359c3 100644
--- a/js/apps/system/_admin/aardvark/APP/frontend/js/routers/router.js
+++ b/js/apps/system/_admin/aardvark/APP/frontend/js/routers/router.js
@@ -671,6 +671,9 @@
this.waitForInit(this.graph2.bind(this), name);
return;
}
+ if (this.graphViewer2) {
+ this.graphViewer2.remove();
+ }
this.graphViewer2 = new window.GraphViewer2({
name: name,
userConfig: this.userConfig
@@ -684,6 +687,9 @@
this.waitForInit(this.graph2settings.bind(this), name);
return;
}
+ if (this.graphSettingsView) {
+ this.graphSettingsView.remove();
+ }
this.graphSettingsView = new window.GraphSettingsView({
name: name,
userConfig: this.userConfig
diff --git a/js/apps/system/_admin/aardvark/APP/frontend/js/templates/graphSettingsView.ejs b/js/apps/system/_admin/aardvark/APP/frontend/js/templates/graphSettingsView.ejs
index 6b45d1a181..e0a124629d 100644
--- a/js/apps/system/_admin/aardvark/APP/frontend/js/templates/graphSettingsView.ejs
+++ b/js/apps/system/_admin/aardvark/APP/frontend/js/templates/graphSettingsView.ejs
@@ -7,7 +7,7 @@
<% return name.charAt(0).toUpperCase() + string.slice(1);%>
<% }; %>
-
+
-
-
<% _.each(specific, function(val, key) { %>
-
<%=val.name%>
-
+
+ <% if (val.type === 'divider') { %>
+
<%=val.name%>
+
+ <% } else { %>
+
+
<%=val.name%>
+
+ <% if (val.type === 'select') { %>
+
+
+
+ <% } %>
+
+
+ <% } %>
<% }); %>
@@ -77,16 +81,13 @@
-
-
<% _.each(general, function(val, key) { %>
+ <% if (val.type === 'divider') { %>
+
<%=val.name%>
+
+ <% } else { %>
<%=val.name%>
<% if (val.type === 'select') { %>
@@ -101,9 +102,14 @@
<% } %>
- <% if (val.type === 'numeric') { %>
+ <% if (val.type === 'string') { %>
+
+ <% } %>
+
+ <% if (val.type === 'number') { %>
<% } %>
+ <% } %>
<% }); %>
diff --git a/js/apps/system/_admin/aardvark/APP/frontend/js/views/graphSettingsView.js b/js/apps/system/_admin/aardvark/APP/frontend/js/views/graphSettingsView.js
index 76b49cad23..2eb07212fd 100644
--- a/js/apps/system/_admin/aardvark/APP/frontend/js/views/graphSettingsView.js
+++ b/js/apps/system/_admin/aardvark/APP/frontend/js/views/graphSettingsView.js
@@ -7,7 +7,23 @@
window.GraphSettingsView = Backbone.View.extend({
el: '#content',
+ remove: function () {
+ this.$el.empty().off(); /* off to unbind the events */
+ this.stopListening();
+ return this;
+ },
+
general: {
+ 'graph': {
+ type: 'divider',
+ name: 'Graph'
+ },
+ 'nodeStart': {
+ type: 'string',
+ name: 'Starting node',
+ desc: 'A valid node id. If empty, a random node will be chosen.',
+ value: 2
+ },
'layout': {
type: 'select',
name: 'Layout algorithm',
@@ -37,58 +53,73 @@
}
},
'depth': {
- type: 'numeric',
+ type: 'number',
name: 'Search depth',
value: 2
}
},
specific: {
+ 'nodes': {
+ type: 'divider',
+ name: 'Nodes'
+ },
'nodeLabel': {
type: 'string',
- name: 'Node label',
+ name: 'Label',
desc: 'Default node color. RGB or HEX value.',
default: '_key'
},
'nodeColor': {
type: 'color',
- name: 'Node color',
+ name: 'Color',
desc: 'Default node color. RGB or HEX value.',
default: '#2ecc71'
},
'nodeSize': {
type: 'string',
- name: 'Node size',
- desc: 'Default node size. Numeric value > 0.',
- value: undefined
+ name: 'Sizing attribute',
+ desc: 'Default node size. Numeric value > 0.'
+ },
+ 'edges': {
+ type: 'divider',
+ name: 'Edges'
},
'edgeLabel': {
type: 'string',
- name: 'Edge label',
- desc: 'Default edge label.',
- value: undefined
+ name: 'Label',
+ desc: 'Default edge label.'
},
'edgeColor': {
type: 'color',
- name: 'Edge color',
+ name: 'Color',
desc: 'Default edge color. RGB or HEX value.',
default: '#cccccc'
},
'edgeSize': {
- type: 'string',
- name: 'Edge thickness',
- desc: 'Default edge thickness. Numeric value > 0.',
- value: undefined
+ type: 'number',
+ name: 'Sizing',
+ desc: 'Default edge thickness. Numeric value > 0.'
},
'edgeType': {
type: 'select',
- name: 'Edge type',
+ name: 'Type',
desc: 'The type of the edge',
- canvas: {
- name: 'Straight'
+ line: {
+ name: 'Line',
+ val: 'line'
},
- webgl: {
- name: 'Curved'
+ curve: {
+ name: 'Curve',
+ val: 'curve'
+ },
+ arrow: {
+ name: 'Arrow',
+ val: 'arrow'
+ },
+ curvedArrow: {
+ name: 'Curved Arrow',
+ val: 'curvedArrow'
}
}
},
@@ -112,7 +143,6 @@
this.userConfig.fetch({
success: function (data) {
self.graphConfig = data.toJSON().graphs[combinedName];
-
if (render) {
self.continueRender();
}
@@ -127,7 +157,15 @@
config[combinedName] = {
layout: $('#g_layout').val(),
renderer: $('#g_renderer').val(),
- depth: $('#g_depth').val()
+ depth: $('#g_depth').val(),
+ nodeColor: $('#g_nodeColor').val(),
+ edgeColor: $('#g_edgeColor').val(),
+ nodeLabel: $('#g_nodeLabel').val(),
+ edgeLabel: $('#g_edgeLabel').val(),
+ edgeType: $('#g_edgeType').val(),
+ nodeSize: $('#g_nodeSize').val(),
+ edgeSize: $('#g_edgeSize').val(),
+ nodeStart: $('#g_nodeStart').val()
};
var callback = function () {
diff --git a/js/apps/system/_admin/aardvark/APP/frontend/js/views/graphViewer2.js b/js/apps/system/_admin/aardvark/APP/frontend/js/views/graphViewer2.js
index 1a2a64378b..6774f5f091 100644
--- a/js/apps/system/_admin/aardvark/APP/frontend/js/views/graphViewer2.js
+++ b/js/apps/system/_admin/aardvark/APP/frontend/js/views/graphViewer2.js
@@ -1,12 +1,18 @@
/* jshint browser: true */
/* jshint unused: false */
-/* global arangoHelper, slicePath, icon, wheelnav, document, sigma, Backbone, templateEngine, $, window*/
+/* global arangoHelper, _, slicePath, icon, wheelnav, document, sigma, Backbone, templateEngine, $, window*/
(function () {
'use strict';
window.GraphViewer2 = Backbone.View.extend({
el: '#content',
+ remove: function () {
+ this.$el.empty().off(); /* off to unbind the events */
+ this.stopListening();
+ return this;
+ },
+
template: templateEngine.createTemplate('graphViewer2.ejs'),
initialize: function (options) {
@@ -64,30 +70,104 @@
$('#content').append(
'' +
'' +
- 'Calculating layout. Please wait ...
'
+ 'Fetching graph data. Please wait ... '
);
- var fetchGraph = function () {
+ var continueFetchGraph = function () {
+ var ajaxData = {};
+ if (this.graphConfig) {
+ ajaxData = _.clone(this.graphConfig);
+
+ // remove not needed params
+ delete ajaxData.layout;
+ delete ajaxData.edgeType;
+ delete ajaxData.renderer;
+ }
+
+ this.setupSigma();
+
$.ajax({
type: 'GET',
url: arangoHelper.databaseUrl('/_admin/aardvark/graph/' + encodeURIComponent(this.name)),
contentType: 'application/json',
+ data: ajaxData,
success: function (data) {
+ $('#calcText').html('Calculating layout. Please wait ... ');
arangoHelper.buildGraphSubNav(self.name, 'Content');
self.renderGraph(data);
},
- error: function () {
+ error: function (e) {
+ console.log(e);
+ try {
+ arangoHelper.arangoError('Graph', e.responseJSON.exception);
+ } catch (ignore) {}
+
$('#calculatingGraph').html('Failed to fetch graph information.');
}
});
}.bind(this);
- // TODO LOAD GRAPH SETTINGS
- this.getGraphSettings(fetchGraph);
+ // load graph configuration
+ this.getGraphSettings(continueFetchGraph);
},
- clearOldContextMenu: function () {
+ setupSigma: function () {
+ if (this.graphConfig) {
+ if (this.graphConfig.edgeLabel) {
+ // Initialize package:
+ sigma.utils.pkg('sigma.settings');
+
+ var settings = {
+ defaultEdgeLabelColor: '#000',
+ defaultEdgeLabelActiveColor: '#000',
+ defaultEdgeLabelSize: 10,
+ edgeLabelSize: 'fixed',
+ edgeLabelSizePowRatio: 1,
+ edgeLabelThreshold: 1
+ };
+
+ // Export the previously designed settings:
+ sigma.settings = sigma.utils.extend(sigma.settings || {}, settings);
+
+ // Override default settings:
+ sigma.settings.drawEdgeLabels = true;
+ }
+ }
+ },
+
+ contextState: {
+ createEdge: false,
+ _from: false,
+ _to: false,
+ fromX: false,
+ fromY: false
+ },
+
+ clearOldContextMenu: function (states) {
+ var self = this;
+
+ // clear dom
$('#nodeContextMenu').remove();
+ var string = '';
+ $('#graph-container').append(string);
+
+ // clear state
+ if (states) {
+ _.each(this.contextState, function (val, key) {
+ self.contextState[key] = false;
+ });
+ }
+
+ // clear info div
+ },
+
+ createContextMenu: function (e) {
+ var x = e.data.captor.clientX;
+ var y = e.data.captor.clientX;
+ console.log('Context menu');
+ console.log(x);
+ console.log(y);
+ this.clearOldContextMenu();
},
createNodeContextMenu: function (nodeId, e) {
@@ -97,8 +177,6 @@
var y = e.data.node['renderer1:y'];
this.clearOldContextMenu();
- var string = '';
- $('#graph-container').append(string);
var generateMenu = function (e, nodeId) {
var hotaru = ['#364C4A', '#497C7F', '#92C5C0', '#858168', '#CCBCA5'];
@@ -110,35 +188,49 @@
wheel.wheelRadius = 50;
wheel.clockwise = false;
wheel.colors = hotaru;
+ wheel.multiSelect = true;
wheel.clickModeRotate = false;
wheel.slicePathFunction = slicePath().DonutSlice;
- wheel.createWheel([icon.edit, icon.trash, icon.smallgear, icon.smallgear]);
+ wheel.createWheel([icon.edit, icon.trash, icon.arrowleft2, icon.connect]);
+ wheel.navItems[0].selected = false;
+ wheel.navItems[0].hovered = false;
// add menu events
// function 0: edit
- wheel.navItems[0].navigateFunction = function () {
+ wheel.navItems[0].navigateFunction = function (e) {
self.clearOldContextMenu();
self.editNode(nodeId);
};
- // function 1:
- wheel.navItems[1].navigateFunction = function () {
+ // function 1: delete
+ wheel.navItems[1].navigateFunction = function (e) {
self.clearOldContextMenu();
- self.editNode(nodeId);
+ self.deleteNode(nodeId);
};
- // function 2:
- wheel.navItems[2].navigateFunction = function () {
+ // function 2: mark as start node
+ wheel.navItems[2].navigateFunction = function (e) {
self.clearOldContextMenu();
- self.editNode(nodeId);
+ self.setStartNode(nodeId);
};
- // function 3: delete
- wheel.navItems[3].navigateFunction = function () {
+ // function 3: create edge
+ wheel.navItems[3].navigateFunction = function (e) {
+ self.contextState.createEdge = true;
+ self.contextState._from = nodeId;
+ self.contextState.fromX = x;
+ self.contextState.fromY = y;
+
+ var c = document.getElementsByClassName('sigma-mouse')[0];
+ c.addEventListener('mousemove', self.drawLine.bind(this), false);
+
self.clearOldContextMenu();
- self.editNode(nodeId);
};
+
+ // deselect active default entry
+ wheel.navItems[0].selected = false;
+ wheel.navItems[0].hovered = false;
};
$('#nodeContextMenu').css('left', x + 115);
@@ -149,6 +241,31 @@
generateMenu(e, nodeId);
},
+ clearMouseCanvas: function () {
+ var c = document.getElementsByClassName('sigma-mouse')[0];
+ var ctx = c.getContext('2d');
+ ctx.clearRect(0, 0, $(c).width(), $(c).height());
+ },
+
+ drawLine: function (e) {
+ var context = window.App.graphViewer2.contextState;
+
+ if (context.createEdge) {
+ var fromX = context.fromX;
+ var fromY = context.fromY;
+ var toX = e.offsetX;
+ var toY = e.offsetY;
+
+ var c = document.getElementsByClassName('sigma-mouse')[0];
+ var ctx = c.getContext('2d');
+ ctx.clearRect(0, 0, $(c).width(), $(c).height());
+ ctx.beginPath();
+ ctx.moveTo(fromX, fromY);
+ ctx.lineTo(toX, toY);
+ ctx.stroke();
+ }
+ },
+
getGraphSettings: function (callback) {
var self = this;
var combinedName = window.App.currentDB.toJSON().name + '_' + this.name;
@@ -158,7 +275,7 @@
self.graphConfig = data.toJSON().graphs[combinedName];
if (callback) {
- callback();
+ callback(self.graphConfig);
}
}
});
@@ -184,8 +301,6 @@
var renderer = 'canvas';
if (this.graphConfig) {
- console.log(this.graphConfig);
-
if (this.graphConfig.layout) {
algorithm = this.graphConfig.layout;
}
@@ -201,20 +316,28 @@
var settings = {
doubleClickEnabled: false,
- minEdgeSize: 0.5,
+ minNodeSize: 3.5,
+ minEdgeSize: 1,
maxEdgeSize: 4,
enableEdgeHovering: true,
- // edgeHoverColor: 'edge',
- // defaultEdgeHoverColor: '#000',
- // defaultEdgeType: 'curve',
- edgeHoverSizeRatio: 1,
+ edgeHoverColor: '#000',
+ defaultEdgeHoverColor: '#000',
+ defaultEdgeType: 'line',
+ edgeHoverSizeRatio: 2,
edgeHoverExtremities: true
};
+ if (this.graphConfig) {
+ if (this.graphConfig.edgeType) {
+ settings.defaultEdgeType = this.graphConfig.edgeType;
+ }
+ }
+
// adjust display settings for big graphs
if (graph.nodes.length > 500) {
- // show node label if size is 20
- settings.labelThreshold = 20;
+ // show node label if size is 15
+ settings.labelThreshold = 15;
+ settings.hideEdgesOnMove = true;
}
// adjust display settings for webgl renderer
@@ -271,7 +394,33 @@
e.originalColor = e.color;
});
- if (renderer !== 'webgl') {
+ // for canvas renderer allow graph editing
+ if (renderer === 'canvas') {
+ s.bind('rightClickStage', function (e) {
+ self.createContextMenu(e);
+ self.clearMouseCanvas();
+ });
+
+ s.bind('clickNode', function (e) {
+ if (self.contextState.createEdge === true) {
+ // create the edge
+ self.contextState._to = e.data.node.id;
+
+ self.currentGraph.graph.addEdge({
+ source: self.contextState._from,
+ target: self.contextState._to,
+ id: Math.random(),
+ color: self.graphConfig.edgeColor
+ });
+
+ // rerender graph
+ self.currentGraph.refresh();
+
+ // then clear states
+ self.clearOldContextMenu(true);
+ }
+ });
+
s.bind('rightClickNode', function (e) {
var nodeId = e.data.node.id;
self.createNodeContextMenu(nodeId, e);
@@ -314,7 +463,8 @@
});
s.bind('clickStage', function () {
- self.clearOldContextMenu();
+ self.clearOldContextMenu(true);
+ self.clearMouseCanvas();
});
}
@@ -327,11 +477,18 @@
} else if (algorithm === 'force') {
s.startForceAtlas2({worker: true, barnesHutOptimize: false});
+ var duration = 3000;
+
+ if (graph.nodes.length > 2500) {
+ duration = 5000;
+ } else if (graph.nodes.length < 50) {
+ duration = 500;
+ }
+
window.setTimeout(function () {
s.stopForceAtlas2();
dragListener = sigma.plugins.dragNodes(s, s.renderers[0]);
- console.log('stopped force');
- }, 3000);
+ }, duration);
} else if (algorithm === 'fruchtermann') {
// Start the Fruchterman-Reingold algorithm:
sigma.layouts.fruchtermanReingold.start(s);
diff --git a/js/apps/system/_admin/aardvark/APP/frontend/scss/_graphViewer2.scss b/js/apps/system/_admin/aardvark/APP/frontend/scss/_graphViewer2.scss
index c4a7760ff1..2ad6ad4920 100644
--- a/js/apps/system/_admin/aardvark/APP/frontend/scss/_graphViewer2.scss
+++ b/js/apps/system/_admin/aardvark/APP/frontend/scss/_graphViewer2.scss
@@ -45,8 +45,19 @@
position: fixed;
svg {
- path {
- font-size: 2pt !important;
+ #wheelnav-nodeContextMenu-title-0 {
+ transform: translate(24px, 14px) scale(.7) !important;
+ }
+
+ #wheelnav-nodeContextMenu-title-0,
+ #wheelnav-nodeContextMenu-title-1,
+ #wheelnav-nodeContextMenu-title-2,
+ #wheelnav-nodeContextMenu-title-3 {
+ fill: $c-white;
+
+ &:hover {
+ fill: $c-positive;
+ }
}
}
}
diff --git a/js/apps/system/_admin/aardvark/APP/frontend/scss/_pure.scss b/js/apps/system/_admin/aardvark/APP/frontend/scss/_pure.scss
index 45493dd71f..6796dcc0e7 100644
--- a/js/apps/system/_admin/aardvark/APP/frontend/scss/_pure.scss
+++ b/js/apps/system/_admin/aardvark/APP/frontend/scss/_pure.scss
@@ -9,6 +9,13 @@
.dataTables_empty {
padding-left: 10px;
}
+
+ .heading {
+ font-weight: 600;
+ height: 40px;
+ padding-bottom: 10px;
+ padding-top: 10px;
+ }
}
.pure-table {