mirror of https://gitee.com/bigwinds/arangodb
This commit is contained in:
parent
a0ff5e9dc3
commit
604637abdd
|
@ -61,8 +61,10 @@
|
||||||
"frontend/js/lib/sigma.min.js",
|
"frontend/js/lib/sigma.min.js",
|
||||||
"frontend/js/lib/sigma.plugins.animate.js",
|
"frontend/js/lib/sigma.plugins.animate.js",
|
||||||
"frontend/js/lib/sigma.plugins.dragNodes.js",
|
"frontend/js/lib/sigma.plugins.dragNodes.js",
|
||||||
"frontend/js/lib/sigma.layout.noverlap.js",
|
|
||||||
"frontend/js/lib/sigma.plugins.fullScreen.js",
|
"frontend/js/lib/sigma.plugins.fullScreen.js",
|
||||||
|
"frontend/js/lib/sigma.plugins.filter.js",
|
||||||
|
"frontend/js/lib/sigma.plugins.lasso.js",
|
||||||
|
"frontend/js/lib/sigma.layout.noverlap.js",
|
||||||
"frontend/js/lib/sigma.layout.fruchtermanReingold.js",
|
"frontend/js/lib/sigma.layout.fruchtermanReingold.js",
|
||||||
"frontend/js/lib/sigma.exporters.svg.js",
|
"frontend/js/lib/sigma.exporters.svg.js",
|
||||||
"frontend/js/lib/sigma.canvas.edges.labels.curve.js",
|
"frontend/js/lib/sigma.canvas.edges.labels.curve.js",
|
||||||
|
|
|
@ -92,7 +92,7 @@ window.ArangoDocument = Backbone.Collection.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function (data) {
|
error: function (data) {
|
||||||
callback(true, data._id);
|
callback(true, data._id, data.responseJSON);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,504 @@
|
||||||
|
;(function(undefined) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
if (typeof sigma === 'undefined')
|
||||||
|
throw 'sigma is not declared';
|
||||||
|
|
||||||
|
// Initialize package:
|
||||||
|
sigma.utils.pkg('sigma.plugins');
|
||||||
|
|
||||||
|
// Add custom graph methods:
|
||||||
|
/**
|
||||||
|
* This methods returns an array of nodes that are adjacent to a node.
|
||||||
|
*
|
||||||
|
* @param {string} id The node id.
|
||||||
|
* @return {array} The array of adjacent nodes.
|
||||||
|
*/
|
||||||
|
if (!sigma.classes.graph.hasMethod('adjacentNodes'))
|
||||||
|
sigma.classes.graph.addMethod('adjacentNodes', function(id) {
|
||||||
|
if (typeof id !== 'string')
|
||||||
|
throw 'adjacentNodes: the node id must be a string.';
|
||||||
|
|
||||||
|
var target,
|
||||||
|
nodes = [];
|
||||||
|
for(target in this.allNeighborsIndex[id]) {
|
||||||
|
nodes.push(this.nodesIndex[target]);
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This methods returns an array of edges that are adjacent to a node.
|
||||||
|
*
|
||||||
|
* @param {string} id The node id.
|
||||||
|
* @return {array} The array of adjacent edges.
|
||||||
|
*/
|
||||||
|
if (!sigma.classes.graph.hasMethod('adjacentEdges'))
|
||||||
|
sigma.classes.graph.addMethod('adjacentEdges', function(id) {
|
||||||
|
if (typeof id !== 'string')
|
||||||
|
throw 'adjacentEdges: the node id must be a string.';
|
||||||
|
|
||||||
|
var a = this.allNeighborsIndex[id],
|
||||||
|
eid,
|
||||||
|
target,
|
||||||
|
edges = [];
|
||||||
|
for(target in a) {
|
||||||
|
for(eid in a[target]) {
|
||||||
|
edges.push(a[target][eid]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return edges;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sigma Filter
|
||||||
|
* =============================
|
||||||
|
*
|
||||||
|
* @author Sébastien Heymann <seb@linkurio.us> (Linkurious)
|
||||||
|
* @version 0.1
|
||||||
|
*/
|
||||||
|
|
||||||
|
var _g = undefined,
|
||||||
|
_s = undefined,
|
||||||
|
_chain = [], // chain of wrapped filters
|
||||||
|
_keysIndex = Object.create(null),
|
||||||
|
Processors = {}; // available predicate processors
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Library of processors
|
||||||
|
* ------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {function} fn The predicate.
|
||||||
|
*/
|
||||||
|
Processors.nodes = function nodes(fn) {
|
||||||
|
var n = _g.nodes(),
|
||||||
|
ln = n.length,
|
||||||
|
e = _g.edges(),
|
||||||
|
le = e.length;
|
||||||
|
|
||||||
|
// hide node, or keep former value
|
||||||
|
while(ln--)
|
||||||
|
n[ln].hidden = !fn.call(_g, n[ln]) || n[ln].hidden;
|
||||||
|
|
||||||
|
while(le--)
|
||||||
|
if (_g.nodes(e[le].source).hidden || _g.nodes(e[le].target).hidden)
|
||||||
|
e[le].hidden = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {function} fn The predicate.
|
||||||
|
*/
|
||||||
|
Processors.edges = function edges(fn) {
|
||||||
|
var e = _g.edges(),
|
||||||
|
le = e.length;
|
||||||
|
|
||||||
|
// hide edge, or keep former value
|
||||||
|
while(le--)
|
||||||
|
e[le].hidden = !fn.call(_g, e[le]) || e[le].hidden;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} id The center node.
|
||||||
|
*/
|
||||||
|
Processors.neighbors = function neighbors(id) {
|
||||||
|
var n = _g.nodes(),
|
||||||
|
ln = n.length,
|
||||||
|
e = _g.edges(),
|
||||||
|
le = e.length,
|
||||||
|
neighbors = _g.adjacentNodes(id),
|
||||||
|
nn = neighbors.length,
|
||||||
|
no = {};
|
||||||
|
|
||||||
|
while(nn--)
|
||||||
|
no[neighbors[nn].id] = true;
|
||||||
|
|
||||||
|
while(ln--)
|
||||||
|
if (n[ln].id !== id && !(n[ln].id in no))
|
||||||
|
n[ln].hidden = true;
|
||||||
|
|
||||||
|
while(le--)
|
||||||
|
if (_g.nodes(e[le].source).hidden || _g.nodes(e[le].target).hidden)
|
||||||
|
e[le].hidden = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function adds a filter to the chain of filters.
|
||||||
|
*
|
||||||
|
* @param {function} fn The filter (i.e. predicate processor).
|
||||||
|
* @param {function} p The predicate.
|
||||||
|
* @param {?string} key The key to identify the filter.
|
||||||
|
*/
|
||||||
|
function register(fn, p, key) {
|
||||||
|
if (key != undefined && typeof key !== 'string')
|
||||||
|
throw 'The filter key "'+ key.toString() +'" must be a string.';
|
||||||
|
|
||||||
|
if (key != undefined && !key.length)
|
||||||
|
throw 'The filter key must be a non-empty string.';
|
||||||
|
|
||||||
|
if (typeof fn !== 'function')
|
||||||
|
throw 'The predicate of key "'+ key +'" must be a function.';
|
||||||
|
|
||||||
|
if ('undo' === key)
|
||||||
|
throw '"undo" is a reserved key.';
|
||||||
|
|
||||||
|
if (_keysIndex[key])
|
||||||
|
throw 'The filter "' + key + '" already exists.';
|
||||||
|
|
||||||
|
if (key)
|
||||||
|
_keysIndex[key] = true;
|
||||||
|
|
||||||
|
_chain.push({
|
||||||
|
'key': key,
|
||||||
|
'processor': fn,
|
||||||
|
'predicate': p
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function removes a set of filters from the chain.
|
||||||
|
*
|
||||||
|
* @param {object} o The filter keys.
|
||||||
|
*/
|
||||||
|
function unregister (o) {
|
||||||
|
_chain = _chain.filter(function(a) {
|
||||||
|
return !(a.key in o);
|
||||||
|
});
|
||||||
|
|
||||||
|
for(var key in o)
|
||||||
|
delete _keysIndex[key];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter Object
|
||||||
|
* ------------------
|
||||||
|
* @param {sigma} s The related sigma instance.
|
||||||
|
*/
|
||||||
|
function Filter(s) {
|
||||||
|
_s = s;
|
||||||
|
_g = s.graph;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to filter the nodes. The method must be called with
|
||||||
|
* the predicate, which is a function that takes a node as argument and
|
||||||
|
* returns a boolean. It may take an identifier as argument to undo the
|
||||||
|
* filter later. The method wraps the predicate into an anonymous function
|
||||||
|
* that looks through each node in the graph. When executed, the anonymous
|
||||||
|
* function hides the nodes that fail a truth test (predicate). The method
|
||||||
|
* adds the anonymous function to the chain of filters. The filter is not
|
||||||
|
* executed until the apply() method is called.
|
||||||
|
*
|
||||||
|
* > var filter = new sigma.plugins.filter(s);
|
||||||
|
* > filter.nodesBy(function(n) {
|
||||||
|
* > return this.degree(n.id) > 0;
|
||||||
|
* > }, 'degreeNotNull');
|
||||||
|
*
|
||||||
|
* @param {function} fn The filter predicate.
|
||||||
|
* @param {?string} key The key to identify the filter.
|
||||||
|
* @return {sigma.plugins.filter} Returns the instance.
|
||||||
|
*/
|
||||||
|
Filter.prototype.nodesBy = function(fn, key) {
|
||||||
|
// Wrap the predicate to be applied on the graph and add it to the chain.
|
||||||
|
register(Processors.nodes, fn, key);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to filter the edges. The method must be called with
|
||||||
|
* the predicate, which is a function that takes a node as argument and
|
||||||
|
* returns a boolean. It may take an identifier as argument to undo the
|
||||||
|
* filter later. The method wraps the predicate into an anonymous function
|
||||||
|
* that looks through each edge in the graph. When executed, the anonymous
|
||||||
|
* function hides the edges that fail a truth test (predicate). The method
|
||||||
|
* adds the anonymous function to the chain of filters. The filter is not
|
||||||
|
* executed until the apply() method is called.
|
||||||
|
*
|
||||||
|
* > var filter = new sigma.plugins.filter(s);
|
||||||
|
* > filter.edgesBy(function(e) {
|
||||||
|
* > return e.size > 1;
|
||||||
|
* > }, 'edgeSize');
|
||||||
|
*
|
||||||
|
* @param {function} fn The filter predicate.
|
||||||
|
* @param {?string} key The key to identify the filter.
|
||||||
|
* @return {sigma.plugins.filter} Returns the instance.
|
||||||
|
*/
|
||||||
|
Filter.prototype.edgesBy = function(fn, key) {
|
||||||
|
// Wrap the predicate to be applied on the graph and add it to the chain.
|
||||||
|
register(Processors.edges, fn, key);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to filter the nodes which are not direct connections
|
||||||
|
* of a given node. The method must be called with the node identifier. It
|
||||||
|
* may take an identifier as argument to undo the filter later. The filter
|
||||||
|
* is not executed until the apply() method is called.
|
||||||
|
*
|
||||||
|
* > var filter = new sigma.plugins.filter(s);
|
||||||
|
* > filter.neighborsOf('n0');
|
||||||
|
*
|
||||||
|
* @param {string} id The node id.
|
||||||
|
* @param {?string} key The key to identify the filter.
|
||||||
|
* @return {sigma.plugins.filter} Returns the instance.
|
||||||
|
*/
|
||||||
|
Filter.prototype.neighborsOf = function(id, key) {
|
||||||
|
if (typeof id !== 'string')
|
||||||
|
throw 'The node id "'+ id.toString() +'" must be a string.';
|
||||||
|
if (!id.length)
|
||||||
|
throw 'The node id must be a non-empty string.';
|
||||||
|
|
||||||
|
// Wrap the predicate to be applied on the graph and add it to the chain.
|
||||||
|
register(Processors.neighbors, id, key);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to execute the chain of filters and to refresh the
|
||||||
|
* display.
|
||||||
|
*
|
||||||
|
* > var filter = new sigma.plugins.filter(s);
|
||||||
|
* > filter
|
||||||
|
* > .nodesBy(function(n) {
|
||||||
|
* > return this.degree(n.id) > 0;
|
||||||
|
* > }, 'degreeNotNull')
|
||||||
|
* > .apply();
|
||||||
|
*
|
||||||
|
* @return {sigma.plugins.filter} Returns the instance.
|
||||||
|
*/
|
||||||
|
Filter.prototype.apply = function() {
|
||||||
|
for (var i = 0, len = _chain.length; i < len; ++i) {
|
||||||
|
_chain[i].processor(_chain[i].predicate);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_chain[0] && 'undo' === _chain[0].key) {
|
||||||
|
_chain.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
_s.refresh();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method undoes one or several filters, depending on how it is called.
|
||||||
|
*
|
||||||
|
* To undo all filters, call "undo" without argument. To undo a specific
|
||||||
|
* filter, call it with the key of the filter. To undo multiple filters, call
|
||||||
|
* it with an array of keys or multiple arguments, and it will undo each
|
||||||
|
* filter, in the same order. The undo is not executed until the apply()
|
||||||
|
* method is called. For instance:
|
||||||
|
*
|
||||||
|
* > var filter = new sigma.plugins.filter(s);
|
||||||
|
* > filter
|
||||||
|
* > .nodesBy(function(n) {
|
||||||
|
* > return this.degree(n.id) > 0;
|
||||||
|
* > }, 'degreeNotNull');
|
||||||
|
* > .edgesBy(function(e) {
|
||||||
|
* > return e.size > 1;
|
||||||
|
* > }, 'edgeSize')
|
||||||
|
* > .undo();
|
||||||
|
*
|
||||||
|
* Other examples:
|
||||||
|
* > filter.undo();
|
||||||
|
* > filter.undo('myfilter');
|
||||||
|
* > filter.undo(['myfilter1', 'myfilter2']);
|
||||||
|
* > filter.undo('myfilter1', 'myfilter2');
|
||||||
|
*
|
||||||
|
* @param {?(string|array|*string))} v Eventually one key, an array of keys.
|
||||||
|
* @return {sigma.plugins.filter} Returns the instance.
|
||||||
|
*/
|
||||||
|
Filter.prototype.undo = function(v) {
|
||||||
|
var q = Object.create(null),
|
||||||
|
la = arguments.length;
|
||||||
|
|
||||||
|
// find removable filters
|
||||||
|
if (la === 1) {
|
||||||
|
if (Object.prototype.toString.call(v) === '[object Array]')
|
||||||
|
for (var i = 0, len = v.length; i < len; i++)
|
||||||
|
q[v[i]] = true;
|
||||||
|
|
||||||
|
else // 1 filter key
|
||||||
|
q[v] = true;
|
||||||
|
|
||||||
|
} else if (la > 1) {
|
||||||
|
for (var i = 0; i < la; i++)
|
||||||
|
q[arguments[i]] = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.clear();
|
||||||
|
|
||||||
|
unregister(q);
|
||||||
|
|
||||||
|
function processor() {
|
||||||
|
var n = _g.nodes(),
|
||||||
|
ln = n.length,
|
||||||
|
e = _g.edges(),
|
||||||
|
le = e.length;
|
||||||
|
|
||||||
|
while(ln--)
|
||||||
|
n[ln].hidden = false;
|
||||||
|
|
||||||
|
while(le--)
|
||||||
|
e[le].hidden = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
_chain.unshift({
|
||||||
|
'key': 'undo',
|
||||||
|
'processor': processor
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
// fast deep copy function
|
||||||
|
function deepCopy(o) {
|
||||||
|
var copy = Object.create(null);
|
||||||
|
for (var i in o) {
|
||||||
|
if (typeof o[i] === "object" && o[i] !== null) {
|
||||||
|
copy[i] = deepCopy(o[i]);
|
||||||
|
}
|
||||||
|
else if (typeof o[i] === "function" && o[i] !== null) {
|
||||||
|
// clone function:
|
||||||
|
eval(" copy[i] = " + o[i].toString());
|
||||||
|
//copy[i] = o[i].bind(_g);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
copy[i] = o[i];
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
};
|
||||||
|
|
||||||
|
function cloneChain(chain) {
|
||||||
|
// Clone the array of filters:
|
||||||
|
var copy = chain.slice(0);
|
||||||
|
for (var i = 0, len = copy.length; i < len; i++) {
|
||||||
|
copy[i] = deepCopy(copy[i]);
|
||||||
|
if (typeof copy[i].processor === "function")
|
||||||
|
copy[i].processor = 'filter.processors.' + copy[i].processor.name;
|
||||||
|
};
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to empty the chain of filters.
|
||||||
|
* Prefer the undo() method to reset filters.
|
||||||
|
*
|
||||||
|
* > var filter = new sigma.plugins.filter(s);
|
||||||
|
* > filter.clear();
|
||||||
|
*
|
||||||
|
* @return {sigma.plugins.filter} Returns the instance.
|
||||||
|
*/
|
||||||
|
Filter.prototype.clear = function() {
|
||||||
|
_chain.length = 0; // clear the array
|
||||||
|
_keysIndex = Object.create(null);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method clones the filter chain and return the copy.
|
||||||
|
*
|
||||||
|
* > var filter = new sigma.plugins.filter(s);
|
||||||
|
* > var chain = filter.export();
|
||||||
|
*
|
||||||
|
* @return {object} The cloned chain of filters.
|
||||||
|
*/
|
||||||
|
Filter.prototype.export = function() {
|
||||||
|
var c = cloneChain(_chain);
|
||||||
|
return c;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the chain of filters with the specified chain.
|
||||||
|
*
|
||||||
|
* > var filter = new sigma.plugins.filter(s);
|
||||||
|
* > var chain = [
|
||||||
|
* > {
|
||||||
|
* > key: 'my-filter',
|
||||||
|
* > predicate: function(n) {...},
|
||||||
|
* > processor: 'filter.processors.nodes'
|
||||||
|
* > }, ...
|
||||||
|
* > ];
|
||||||
|
* > filter.import(chain);
|
||||||
|
*
|
||||||
|
* @param {array} chain The chain of filters.
|
||||||
|
* @return {sigma.plugins.filter} Returns the instance.
|
||||||
|
*/
|
||||||
|
Filter.prototype.import = function(chain) {
|
||||||
|
if (chain === undefined)
|
||||||
|
throw 'Wrong arguments.';
|
||||||
|
|
||||||
|
if (Object.prototype.toString.call(chain) !== '[object Array]')
|
||||||
|
throw 'The chain" must be an array.';
|
||||||
|
|
||||||
|
var copy = cloneChain(chain);
|
||||||
|
|
||||||
|
for (var i = 0, len = copy.length; i < len; i++) {
|
||||||
|
if (copy[i].predicate === undefined || copy[i].processor === undefined)
|
||||||
|
throw 'Wrong arguments.';
|
||||||
|
|
||||||
|
if (copy[i].key != undefined && typeof copy[i].key !== 'string')
|
||||||
|
throw 'The filter key "'+ copy[i].key.toString() +'" must be a string.';
|
||||||
|
|
||||||
|
if (typeof copy[i].predicate !== 'function')
|
||||||
|
throw 'The predicate of key "'+ copy[i].key +'" must be a function.';
|
||||||
|
|
||||||
|
if (typeof copy[i].processor !== 'string')
|
||||||
|
throw 'The processor of key "'+ copy[i].key +'" must be a string.';
|
||||||
|
|
||||||
|
// Replace the processor name by the corresponding function:
|
||||||
|
switch(copy[i].processor) {
|
||||||
|
case 'filter.processors.nodes':
|
||||||
|
copy[i].processor = Processors.nodes;
|
||||||
|
break;
|
||||||
|
case 'filter.processors.edges':
|
||||||
|
copy[i].processor = Processors.edges;
|
||||||
|
break;
|
||||||
|
case 'filter.processors.neighbors':
|
||||||
|
copy[i].processor = Processors.neighbors;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw 'Unknown processor ' + copy[i].processor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_chain = copy;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface
|
||||||
|
* ------------------
|
||||||
|
*
|
||||||
|
* > var filter = new sigma.plugins.filter(s);
|
||||||
|
*/
|
||||||
|
var filter = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {sigma} s The related sigma instance.
|
||||||
|
*/
|
||||||
|
sigma.plugins.filter = function(s) {
|
||||||
|
// Create filter if undefined
|
||||||
|
if (!filter) {
|
||||||
|
filter = new Filter(s);
|
||||||
|
}
|
||||||
|
return filter;
|
||||||
|
};
|
||||||
|
|
||||||
|
}).call(this);
|
|
@ -0,0 +1,337 @@
|
||||||
|
/**
|
||||||
|
* Sigma Lasso
|
||||||
|
* =============================
|
||||||
|
*
|
||||||
|
* @author Florent Schildknecht <florent.schildknecht@gmail.com> (Florent Schildknecht)
|
||||||
|
* @version 0.0.2
|
||||||
|
*/
|
||||||
|
;(function (undefined) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
if (typeof sigma === 'undefined')
|
||||||
|
throw 'sigma is not declared';
|
||||||
|
|
||||||
|
// Initialize package:
|
||||||
|
sigma.utils.pkg('sigma.plugins');
|
||||||
|
|
||||||
|
var _body = undefined,
|
||||||
|
_instances = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lasso Object
|
||||||
|
* ------------------
|
||||||
|
* @param {sigma} sigmaInstance The related sigma instance.
|
||||||
|
* @param {renderer} renderer The sigma instance renderer.
|
||||||
|
* @param {sigma.classes.configurable} settings A settings class
|
||||||
|
*/
|
||||||
|
function Lasso (sigmaInstance, renderer, settings) {
|
||||||
|
// Lasso is also an event dispatcher
|
||||||
|
sigma.classes.dispatcher.extend(this);
|
||||||
|
|
||||||
|
// A quick hardcoded rule to prevent people from using this plugin with the
|
||||||
|
// WebGL renderer (which is impossible at the moment):
|
||||||
|
if (
|
||||||
|
sigma.renderers.webgl &&
|
||||||
|
renderer instanceof sigma.renderers.webgl
|
||||||
|
)
|
||||||
|
throw new Error(
|
||||||
|
'The sigma.plugins.lasso is not compatible with the WebGL renderer'
|
||||||
|
);
|
||||||
|
|
||||||
|
this.sigmaInstance = sigmaInstance;
|
||||||
|
this.renderer = renderer;
|
||||||
|
this.drawingCanvas = undefined;
|
||||||
|
this.drawingContext = undefined;
|
||||||
|
this.drewPoints = [];
|
||||||
|
this.selectedNodes = [];
|
||||||
|
this.isActive = false;
|
||||||
|
this.isDrawing = false;
|
||||||
|
|
||||||
|
_body = document.body;
|
||||||
|
|
||||||
|
// Extends default settings
|
||||||
|
this.settings = new sigma.classes.configurable({
|
||||||
|
'strokeStyle': 'black',
|
||||||
|
'lineWidth': 2,
|
||||||
|
'fillWhileDrawing': false,
|
||||||
|
'fillStyle': 'rgba(200, 200, 200, 0.25)',
|
||||||
|
'cursor': 'crosshair'
|
||||||
|
}, settings || {});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to destroy the lasso.
|
||||||
|
*
|
||||||
|
* > var lasso = new sigma.plugins.lasso(sigmaInstance);
|
||||||
|
* > lasso.clear();
|
||||||
|
*
|
||||||
|
* @return {sigma.plugins.lasso} Returns the instance.
|
||||||
|
*/
|
||||||
|
Lasso.prototype.clear = function () {
|
||||||
|
this.deactivate();
|
||||||
|
|
||||||
|
this.sigmaInstance = undefined;
|
||||||
|
this.renderer = undefined;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Lasso.prototype.getSigmaInstance = function () {
|
||||||
|
// return this.sigmaInstance;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to activate the lasso mode.
|
||||||
|
*
|
||||||
|
* > var lasso = new sigma.plugins.lasso(sigmaInstance);
|
||||||
|
* > lasso.activate();
|
||||||
|
*
|
||||||
|
* @return {sigma.plugins.lasso} Returns the instance.
|
||||||
|
*/
|
||||||
|
Lasso.prototype.activate = function () {
|
||||||
|
if (this.sigmaInstance && !this.isActive) {
|
||||||
|
this.isActive = true;
|
||||||
|
|
||||||
|
// Add a new background layout canvas to draw the path on
|
||||||
|
if (!this.renderer.domElements['lasso']) {
|
||||||
|
this.renderer.initDOM('canvas', 'lasso');
|
||||||
|
this.drawingCanvas = this.renderer.domElements['lasso'];
|
||||||
|
|
||||||
|
this.drawingCanvas.width = this.renderer.container.offsetWidth;
|
||||||
|
this.drawingCanvas.height = this.renderer.container.offsetHeight;
|
||||||
|
this.renderer.container.appendChild(this.drawingCanvas);
|
||||||
|
this.drawingContext = this.drawingCanvas.getContext('2d');
|
||||||
|
this.drawingCanvas.style.cursor = this.settings('cursor');
|
||||||
|
}
|
||||||
|
|
||||||
|
_bindAll.apply(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to deactivate the lasso mode.
|
||||||
|
*
|
||||||
|
* > var lasso = new sigma.plugins.lasso(sigmaInstance);
|
||||||
|
* > lasso.deactivate();
|
||||||
|
*
|
||||||
|
* @return {sigma.plugins.lasso} Returns the instance.
|
||||||
|
*/
|
||||||
|
Lasso.prototype.deactivate = function () {
|
||||||
|
if (this.sigmaInstance && this.isActive) {
|
||||||
|
this.isActive = false;
|
||||||
|
this.isDrawing = false;
|
||||||
|
|
||||||
|
_unbindAll.apply(this);
|
||||||
|
|
||||||
|
if (this.renderer.domElements['lasso']) {
|
||||||
|
this.renderer.container.removeChild(this.renderer.domElements['lasso']);
|
||||||
|
delete this.renderer.domElements['lasso'];
|
||||||
|
this.drawingCanvas.style.cursor = '';
|
||||||
|
this.drawingCanvas = undefined;
|
||||||
|
this.drawingContext = undefined;
|
||||||
|
this.drewPoints = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to bind all events of the lasso mode.
|
||||||
|
*
|
||||||
|
* > var lasso = new sigma.plugins.lasso(sigmaInstance);
|
||||||
|
* > lasso.activate();
|
||||||
|
*
|
||||||
|
* @return {sigma.plugins.lasso} Returns the instance.
|
||||||
|
*/
|
||||||
|
var _bindAll = function () {
|
||||||
|
// Mouse events
|
||||||
|
this.drawingCanvas.addEventListener('mousedown', onDrawingStart.bind(this));
|
||||||
|
_body.addEventListener('mousemove', onDrawing.bind(this));
|
||||||
|
_body.addEventListener('mouseup', onDrawingEnd.bind(this));
|
||||||
|
// Touch events
|
||||||
|
this.drawingCanvas.addEventListener('touchstart', onDrawingStart.bind(this));
|
||||||
|
_body.addEventListener('touchmove', onDrawing.bind(this));
|
||||||
|
_body.addEventListener('touchcancel', onDrawingEnd.bind(this));
|
||||||
|
_body.addEventListener('touchleave', onDrawingEnd.bind(this));
|
||||||
|
_body.addEventListener('touchend', onDrawingEnd.bind(this));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to unbind all events of the lasso mode.
|
||||||
|
*
|
||||||
|
* > var lasso = new sigma.plugins.lasso(sigmaInstance);
|
||||||
|
* > lasso.activate();
|
||||||
|
*
|
||||||
|
* @return {sigma.plugins.lasso} Returns the instance.
|
||||||
|
*/
|
||||||
|
var _unbindAll = function () {
|
||||||
|
// Mouse events
|
||||||
|
this.drawingCanvas.removeEventListener('mousedown', onDrawingStart.bind(this));
|
||||||
|
_body.removeEventListener('mousemove', onDrawing.bind(this));
|
||||||
|
_body.removeEventListener('mouseup', onDrawingEnd.bind(this));
|
||||||
|
// Touch events
|
||||||
|
this.drawingCanvas.removeEventListener('touchstart', onDrawingStart.bind(this));
|
||||||
|
this.drawingCanvas.removeEventListener('touchmove', onDrawing.bind(this));
|
||||||
|
_body.removeEventListener('touchcancel', onDrawingEnd.bind(this));
|
||||||
|
_body.removeEventListener('touchleave', onDrawingEnd.bind(this));
|
||||||
|
_body.removeEventListener('touchend', onDrawingEnd.bind(this));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to retrieve the previously selected nodes
|
||||||
|
*
|
||||||
|
* > var lasso = new sigma.plugins.lasso(sigmaInstance);
|
||||||
|
* > lasso.getSelectedNodes();
|
||||||
|
*
|
||||||
|
* @return {array} Returns an array of nodes.
|
||||||
|
*/
|
||||||
|
Lasso.prototype.getSelectedNodes = function () {
|
||||||
|
return this.selectedNodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
function onDrawingStart (event) {
|
||||||
|
var drawingRectangle = this.drawingCanvas.getBoundingClientRect();
|
||||||
|
|
||||||
|
if (this.isActive) {
|
||||||
|
this.isDrawing = true;
|
||||||
|
this.drewPoints = [];
|
||||||
|
this.selectedNodes = [];
|
||||||
|
|
||||||
|
this.sigmaInstance.refresh();
|
||||||
|
|
||||||
|
this.drewPoints.push({
|
||||||
|
x: event.clientX - drawingRectangle.left,
|
||||||
|
y: event.clientY - drawingRectangle.top
|
||||||
|
});
|
||||||
|
|
||||||
|
this.drawingCanvas.style.cursor = this.settings('cursor');
|
||||||
|
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDrawing (event) {
|
||||||
|
if (this.isActive && this.isDrawing) {
|
||||||
|
var x = 0,
|
||||||
|
y = 0,
|
||||||
|
drawingRectangle = this.drawingCanvas.getBoundingClientRect();
|
||||||
|
switch (event.type) {
|
||||||
|
case 'touchmove':
|
||||||
|
x = event.touches[0].clientX;
|
||||||
|
y = event.touches[0].clientY;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
x = event.clientX;
|
||||||
|
y = event.clientY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.drewPoints.push({
|
||||||
|
x: x - drawingRectangle.left,
|
||||||
|
y: y - drawingRectangle.top
|
||||||
|
});
|
||||||
|
|
||||||
|
// Drawing styles
|
||||||
|
this.drawingContext.lineWidth = this.settings('lineWidth');
|
||||||
|
this.drawingContext.strokeStyle = this.settings('strokeStyle');
|
||||||
|
this.drawingContext.fillStyle = this.settings('fillStyle');
|
||||||
|
this.drawingContext.lineJoin = 'round';
|
||||||
|
this.drawingContext.lineCap = 'round';
|
||||||
|
|
||||||
|
// Clear the canvas
|
||||||
|
this.drawingContext.clearRect(0, 0, this.drawingContext.canvas.width, this.drawingContext.canvas.height);
|
||||||
|
|
||||||
|
// Redraw the complete path for a smoother effect
|
||||||
|
// Even smoother with quadratic curves
|
||||||
|
var sourcePoint = this.drewPoints[0],
|
||||||
|
destinationPoint = this.drewPoints[1],
|
||||||
|
pointsLength = this.drewPoints.length,
|
||||||
|
getMiddlePointCoordinates = function (firstPoint, secondPoint) {
|
||||||
|
return {
|
||||||
|
x: firstPoint.x + (secondPoint.x - firstPoint.x) / 2,
|
||||||
|
y: firstPoint.y + (secondPoint.y - firstPoint.y) / 2
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
this.drawingContext.beginPath();
|
||||||
|
this.drawingContext.moveTo(sourcePoint.x, sourcePoint.y);
|
||||||
|
|
||||||
|
for (var i = 1; i < pointsLength; i++) {
|
||||||
|
var middlePoint = getMiddlePointCoordinates(sourcePoint, destinationPoint);
|
||||||
|
// this.drawingContext.lineTo(this.drewPoints[i].x, this.drewPoints[i].y);
|
||||||
|
this.drawingContext.quadraticCurveTo(sourcePoint.x, sourcePoint.y, middlePoint.x, middlePoint.y);
|
||||||
|
sourcePoint = this.drewPoints[i];
|
||||||
|
destinationPoint = this.drewPoints[i+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.drawingContext.lineTo(sourcePoint.x, sourcePoint.y);
|
||||||
|
this.drawingContext.stroke();
|
||||||
|
|
||||||
|
if (this.settings('fillWhileDrawing')) {
|
||||||
|
this.drawingContext.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDrawingEnd (event) {
|
||||||
|
if (this.isActive && this.isDrawing) {
|
||||||
|
this.isDrawing = false;
|
||||||
|
|
||||||
|
// Select the nodes inside the path
|
||||||
|
var nodes = this.renderer.nodesOnScreen,
|
||||||
|
nodesLength = nodes.length,
|
||||||
|
i = 0,
|
||||||
|
prefix = this.renderer.options.prefix || '';
|
||||||
|
|
||||||
|
// Loop on all nodes and check if they are in the path
|
||||||
|
while (nodesLength--) {
|
||||||
|
var node = nodes[nodesLength],
|
||||||
|
x = node[prefix + 'x'],
|
||||||
|
y = node[prefix + 'y'];
|
||||||
|
|
||||||
|
if (this.drawingContext.isPointInPath(x, y) && !node.hidden) {
|
||||||
|
this.selectedNodes.push(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch event with selected nodes
|
||||||
|
this.dispatchEvent('selectedNodes', this.selectedNodes);
|
||||||
|
|
||||||
|
// Clear the drawing canvas
|
||||||
|
this.drawingContext.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
|
||||||
|
|
||||||
|
this.drawingCanvas.style.cursor = this.settings('cursor');
|
||||||
|
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {sigma} sigmaInstance The related sigma instance.
|
||||||
|
* @param {renderer} renderer The sigma instance renderer.
|
||||||
|
* @param {sigma.classes.configurable} settings A settings class
|
||||||
|
*
|
||||||
|
* @return {sigma.plugins.lasso} Returns the instance
|
||||||
|
*/
|
||||||
|
sigma.plugins.lasso = function (sigmaInstance, renderer, settings) {
|
||||||
|
// Create lasso if undefined
|
||||||
|
if (!_instances[sigmaInstance.id]) {
|
||||||
|
_instances[sigmaInstance.id] = new Lasso(sigmaInstance, renderer, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for sigmaInstance kill event, and remove the lasso isntance
|
||||||
|
sigmaInstance.bind('kill', function () {
|
||||||
|
if (_instances[sigmaInstance.id] instanceof Lasso) {
|
||||||
|
_instances[sigmaInstance.id].clear();
|
||||||
|
delete _instances[sigmaInstance.id];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return _instances[sigmaInstance.id];
|
||||||
|
};
|
||||||
|
|
||||||
|
}).call(this);
|
|
@ -53,6 +53,10 @@
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback.apply(this, args);
|
callback.apply(this, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.graphViewer2) {
|
||||||
|
this.graphViewer2.graphSettingsView.hide();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
checkUser: function () {
|
checkUser: function () {
|
||||||
|
|
|
@ -11,23 +11,58 @@
|
||||||
<div id="graphSettingsView" class="innerContent">
|
<div id="graphSettingsView" class="innerContent">
|
||||||
|
|
||||||
<div class="pure-g" style="margin-top: -15px">
|
<div class="pure-g" style="margin-top: -15px">
|
||||||
|
<div class="pure-u-1-1 pure-u-md-1-1 pure-u-lg-1-1 pure-u-xl-1-1">
|
||||||
|
|
||||||
|
<div class="pure-g pure-table pure-table-body">
|
||||||
|
|
||||||
|
<% _.each(general, function(val, key) { %>
|
||||||
|
<% if (val.type === 'divider') { %>
|
||||||
|
<div class="pure-u-1-1 left heading"><%=val.name%></div>
|
||||||
|
<% } else { %>
|
||||||
|
<div class="<%= genClass %> left"><%=val.name%></div>
|
||||||
|
<div class="<%= genClass2 %> left">
|
||||||
|
|
||||||
|
<% if (val.type === 'select') { %>
|
||||||
|
<select id="g_<%=key%>">
|
||||||
|
<% _.each(val, function(option, optKey) { %>
|
||||||
|
<% if (option.name) { %>
|
||||||
|
<option value="<%=option.val%>"> <%=option.name%> </option>
|
||||||
|
<% } %>
|
||||||
|
<% }); %>
|
||||||
|
</select>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (val.type === 'string') { %>
|
||||||
|
<input id="g_<%=key%>" type="text" placeholder="string"></input>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (val.type === 'number') { %>
|
||||||
|
<input id="g_<%=key%>" type="text" id="<%=val %>" value="<%=val.value %>" placeholder=""></input>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (val.type === 'range') { %>
|
||||||
|
<input id="g_<%=key%>" type='range' min="0" max="50" val="<%=VALUE%>"/>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (val.type === 'color') { %>
|
||||||
|
<input id="g_<%=key%>" type='color' name='color' value="<%=VALUE%>"/>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% }); %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="pure-u-1-1 pure-u-md-1-1 pure-u-lg-1-1 pure-u-xl-1-1">
|
<div class="pure-u-1-1 pure-u-md-1-1 pure-u-lg-1-1 pure-u-xl-1-1">
|
||||||
|
|
||||||
<div class="sectionHeader pure-g">
|
|
||||||
<div class="pure-u-1-1">
|
|
||||||
<div class="title">
|
|
||||||
Graph specific
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="pure-g pure-table pure-table-body">
|
<div class="pure-g pure-table pure-table-body">
|
||||||
<% _.each(specific, function(val, key) { %>
|
<% _.each(specific, function(val, key) { %>
|
||||||
|
|
||||||
<% if (val.type === 'divider') { %>
|
<% if (val.type === 'divider') { %>
|
||||||
<div class="heading <%= genClass %> left"><%=val.name%></div>
|
<div class="pure-u-1-1 left heading"><%=val.name%></div>
|
||||||
<div class="<%= genClass2 %> left"></div>
|
|
||||||
<% } else { %>
|
<% } else { %>
|
||||||
|
|
||||||
<div class="<%= genClass %> left"><%=val.name%></div>
|
<div class="<%= genClass %> left"><%=val.name%></div>
|
||||||
|
@ -53,6 +88,10 @@
|
||||||
<input id="g_<%=key%>" type='color' name='color' value="<%=VALUE%>"/>
|
<input id="g_<%=key%>" type='color' name='color' value="<%=VALUE%>"/>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
|
<% if (val.type === 'range') { %>
|
||||||
|
<input id="g_<%=key%>" type='range' min="0" max="100" val="<%=VALUE%>"/>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
<% if (val.type === 'select') { %>
|
<% if (val.type === 'select') { %>
|
||||||
<select id="g_<%=key%>">
|
<select id="g_<%=key%>">
|
||||||
<% _.each(val, function(option, optKey) { %>
|
<% _.each(val, function(option, optKey) { %>
|
||||||
|
@ -70,50 +109,8 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-u-1-1 pure-u-md-1-1 pure-u-lg-1-1 pure-u-xl-1-1">
|
|
||||||
|
|
||||||
<div class="sectionHeader pure-g">
|
|
||||||
<div class="pure-u-1-1">
|
|
||||||
<div class="title">
|
|
||||||
General
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="pure-g pure-table pure-table-body">
|
<button id="saveGraphSettings" style="margin-top: 20px; margin-right: 10px;" class="button-success pull-right">Save</button>
|
||||||
|
|
||||||
<% _.each(general, function(val, key) { %>
|
|
||||||
<% if (val.type === 'divider') { %>
|
|
||||||
<div class="heading <%= genClass %> left"><%=val.name%></div>
|
|
||||||
<div class="<%= genClass2 %> left"></div>
|
|
||||||
<% } else { %>
|
|
||||||
<div class="<%= genClass %> left"><%=val.name%></div>
|
|
||||||
|
|
||||||
<% if (val.type === 'select') { %>
|
|
||||||
<select id="g_<%=key%>">
|
|
||||||
<% _.each(val, function(option, optKey) { %>
|
|
||||||
<% if (option.name) { %>
|
|
||||||
<option value="<%=option.val%>"> <%=option.name%> </option>
|
|
||||||
<% } %>
|
|
||||||
<% }); %>
|
|
||||||
</select>
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
<% if (val.type === 'string') { %>
|
|
||||||
<input id="g_<%=key%>" type="text" placeholder="string"></input>
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
<% if (val.type === 'number') { %>
|
|
||||||
<input id="g_<%=key%>" type="text" id="<%=val %>" value="<%=val.value %>" placeholder=""></input>
|
|
||||||
<% } %>
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
<% }); %>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button id="saveGraphSettings" style="margin-top: 20px;" class="button-success pull-right">Save</button>
|
|
||||||
<button id="restoreGraphSettings" style="margin-top: 20px;" class="button-success pull-right">Restore defaults</button>
|
<button id="restoreGraphSettings" style="margin-top: 20px;" class="button-success pull-right">Restore defaults</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,13 @@
|
||||||
'nodeLabel': {
|
'nodeLabel': {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'Label',
|
name: 'Label',
|
||||||
desc: 'Default node color. RGB or HEX value.',
|
desc: 'Node label. Please choose a valid and available node attribute.',
|
||||||
|
default: '_key'
|
||||||
|
},
|
||||||
|
'nodeLabelThreshold': {
|
||||||
|
type: 'range',
|
||||||
|
name: 'Node label threshold',
|
||||||
|
desc: 'The minimum size a node must have on screen to see its label displayed. This does not affect hovering behavior.',
|
||||||
default: '_key'
|
default: '_key'
|
||||||
},
|
},
|
||||||
'nodeColor': {
|
'nodeColor': {
|
||||||
|
@ -90,6 +96,12 @@
|
||||||
name: 'Label',
|
name: 'Label',
|
||||||
desc: 'Default edge label.'
|
desc: 'Default edge label.'
|
||||||
},
|
},
|
||||||
|
'edgeLabelThreshold': {
|
||||||
|
type: 'range',
|
||||||
|
name: 'Edge label threshold',
|
||||||
|
desc: 'The minimum size an edge must have on screen to see its label displayed. This does not affect hovering behavior.',
|
||||||
|
default: '_key'
|
||||||
|
},
|
||||||
'edgeColor': {
|
'edgeColor': {
|
||||||
type: 'color',
|
type: 'color',
|
||||||
name: 'Color',
|
name: 'Color',
|
||||||
|
@ -112,7 +124,8 @@
|
||||||
curve: {
|
curve: {
|
||||||
name: 'Curve',
|
name: 'Curve',
|
||||||
val: 'curve'
|
val: 'curve'
|
||||||
},
|
}
|
||||||
|
/*
|
||||||
arrow: {
|
arrow: {
|
||||||
name: 'Arrow',
|
name: 'Arrow',
|
||||||
val: 'arrow'
|
val: 'arrow'
|
||||||
|
@ -121,6 +134,7 @@
|
||||||
name: 'Curved Arrow',
|
name: 'Curved Arrow',
|
||||||
val: 'curvedArrow'
|
val: 'curvedArrow'
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -137,13 +151,14 @@
|
||||||
'click #restoreGraphSettings': 'restoreGraphSettings',
|
'click #restoreGraphSettings': 'restoreGraphSettings',
|
||||||
'keyup #graphSettingsView input': 'checkEnterKey',
|
'keyup #graphSettingsView input': 'checkEnterKey',
|
||||||
'keyup #graphSettingsView select': 'checkEnterKey',
|
'keyup #graphSettingsView select': 'checkEnterKey',
|
||||||
|
'change input[type="range"]': 'saveGraphSettings',
|
||||||
|
'change input[type="color"]': 'checkColor',
|
||||||
|
'change select': 'saveGraphSettings',
|
||||||
'focus #graphSettingsView input': 'lastFocus',
|
'focus #graphSettingsView input': 'lastFocus',
|
||||||
'focus #graphSettingsView select': 'lastFocus'
|
'focus #graphSettingsView select': 'lastFocus'
|
||||||
},
|
},
|
||||||
|
|
||||||
lastFocus: function (e) {
|
lastFocus: function (e) {
|
||||||
console.log(e.currentTarget.id);
|
|
||||||
console.log(e.currentTarget);
|
|
||||||
this.lastFocussed = e.currentTarget.id;
|
this.lastFocussed = e.currentTarget.id;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -167,17 +182,25 @@
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
saveGraphSettings: function () {
|
checkColor: function () {
|
||||||
|
this.saveGraphSettings(true);
|
||||||
|
},
|
||||||
|
|
||||||
|
saveGraphSettings: function (color, nodeStart) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
console.log('CLICK');
|
||||||
var combinedName = window.App.currentDB.toJSON().name + '_' + this.name;
|
var combinedName = window.App.currentDB.toJSON().name + '_' + this.name;
|
||||||
|
|
||||||
var config = {};
|
var config = {};
|
||||||
|
|
||||||
config[combinedName] = {
|
config[combinedName] = {
|
||||||
layout: $('#g_layout').val(),
|
layout: $('#g_layout').val(),
|
||||||
renderer: $('#g_renderer').val(),
|
renderer: $('#g_renderer').val(),
|
||||||
depth: $('#g_depth').val(),
|
depth: $('#g_depth').val(),
|
||||||
nodeColor: $('#g_nodeColor').val(),
|
nodeColor: $('#g_nodeColor').val(),
|
||||||
|
nodeLabelThreshold: $('#g_nodeLabelThreshold').val(),
|
||||||
edgeColor: $('#g_edgeColor').val(),
|
edgeColor: $('#g_edgeColor').val(),
|
||||||
|
edgeLabelThreshold: $('#g_edgeLabelThreshold').val(),
|
||||||
nodeLabel: $('#g_nodeLabel').val(),
|
nodeLabel: $('#g_nodeLabel').val(),
|
||||||
edgeLabel: $('#g_edgeLabel').val(),
|
edgeLabel: $('#g_edgeLabel').val(),
|
||||||
edgeType: $('#g_edgeType').val(),
|
edgeType: $('#g_edgeType').val(),
|
||||||
|
@ -186,9 +209,17 @@
|
||||||
nodeStart: $('#g_nodeStart').val()
|
nodeStart: $('#g_nodeStart').val()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (nodeStart) {
|
||||||
|
config[combinedName].nodeStart = nodeStart;
|
||||||
|
}
|
||||||
|
|
||||||
var callback = function () {
|
var callback = function () {
|
||||||
if (window.App.graphViewer2) {
|
if (window.App.graphViewer2) {
|
||||||
window.App.graphViewer2.render(self.lastFocussed);
|
if (color) {
|
||||||
|
window.App.graphViewer2.updateColors();
|
||||||
|
} else {
|
||||||
|
window.App.graphViewer2.render(self.lastFocussed);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
arangoHelper.arangoNotification('Graph ' + this.name, 'Configuration saved.');
|
arangoHelper.arangoNotification('Graph ' + this.name, 'Configuration saved.');
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,8 @@
|
||||||
events: {
|
events: {
|
||||||
'click #downloadPNG': 'downloadSVG',
|
'click #downloadPNG': 'downloadSVG',
|
||||||
'click #reloadGraph': 'reloadGraph',
|
'click #reloadGraph': 'reloadGraph',
|
||||||
'click #settingsMenu': 'toggleSettings'
|
'click #settingsMenu': 'toggleSettings',
|
||||||
|
'click #noGraphToggle': 'toggleSettings'
|
||||||
},
|
},
|
||||||
|
|
||||||
cursorX: 0,
|
cursorX: 0,
|
||||||
|
@ -62,6 +63,18 @@
|
||||||
}
|
}
|
||||||
return neighbors;
|
return neighbors;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
sigma.classes.graph.addMethod('getNodeEdges', function (nodeId) {
|
||||||
|
var edges = this.edges();
|
||||||
|
var edgesToReturn = [];
|
||||||
|
|
||||||
|
_.each(edges, function (edge) {
|
||||||
|
if (edge.source === nodeId || edge.target === nodeId) {
|
||||||
|
edgesToReturn.push(edge.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return edgesToReturn;
|
||||||
|
});
|
||||||
} catch (ignore) {}
|
} catch (ignore) {}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -220,19 +233,96 @@
|
||||||
this.cursorY = e.y;
|
this.cursorY = e.y;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
deleteNode: function () {
|
||||||
|
var self = this;
|
||||||
|
var documentKey = $('#delete-node-attr-id').text();
|
||||||
|
var collectionId = documentKey.split('/')[0];
|
||||||
|
var documentId = documentKey.split('/')[1];
|
||||||
|
|
||||||
|
if ($('#delete-node-edges-attr').val() === 'yes') {
|
||||||
|
$.ajax({
|
||||||
|
cache: false,
|
||||||
|
type: 'DELETE',
|
||||||
|
contentType: 'application/json',
|
||||||
|
url: arangoHelper.databaseUrl(
|
||||||
|
'/_api/gharial/' + encodeURIComponent(self.name) + '/vertex/' + encodeURIComponent(documentKey.split('/')[0]) + '/' + encodeURIComponent(documentKey.split('/')[1])
|
||||||
|
),
|
||||||
|
success: function (data) {
|
||||||
|
self.currentGraph.graph.dropNode(documentKey);
|
||||||
|
self.currentGraph.refresh();
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
arangoHelper.arangoError('Graph', 'Could not delete node.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var callback = function (error) {
|
||||||
|
if (!error) {
|
||||||
|
self.currentGraph.graph.dropNode(documentKey);
|
||||||
|
|
||||||
|
// rerender graph
|
||||||
|
self.currentGraph.refresh();
|
||||||
|
} else {
|
||||||
|
arangoHelper.arangoError('Graph', 'Could not delete node.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.documentStore.deleteDocument(collectionId, documentId, callback);
|
||||||
|
}
|
||||||
|
window.modalView.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteNodeModal: function (nodeId) {
|
||||||
|
var buttons = []; var tableContent = [];
|
||||||
|
|
||||||
|
tableContent.push(
|
||||||
|
window.modalView.createReadOnlyEntry('delete-node-attr-id', 'Really delete node', nodeId)
|
||||||
|
);
|
||||||
|
|
||||||
|
tableContent.push(
|
||||||
|
window.modalView.createSelectEntry(
|
||||||
|
'delete-node-edges-attr',
|
||||||
|
'Also delete edges?',
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
value: 'yes',
|
||||||
|
label: 'Yes'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'no',
|
||||||
|
label: 'No'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
buttons.push(
|
||||||
|
window.modalView.createDeleteButton('Delete', this.deleteNode.bind(this))
|
||||||
|
);
|
||||||
|
|
||||||
|
window.modalView.show(
|
||||||
|
'modalTable.ejs',
|
||||||
|
'Delete node',
|
||||||
|
buttons,
|
||||||
|
tableContent
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
addNode: function () {
|
addNode: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var collectionId = $('.modal-body #new-node-collection-attr').val();
|
var collectionId = $('.modal-body #new-node-collection-attr').val();
|
||||||
var key = $('.modal-body #new-node-key-attr').last().val();
|
var key = $('.modal-body #new-node-key-attr').last().val();
|
||||||
|
|
||||||
var callback = function (error, id) {
|
var callback = function (error, id, msg) {
|
||||||
if (error) {
|
if (error) {
|
||||||
arangoHelper.arangoError('Error', 'Could not create node');
|
arangoHelper.arangoError('Could not create node', msg.errorMessage);
|
||||||
} else {
|
} else {
|
||||||
self.currentGraph.graph.addNode({
|
self.currentGraph.graph.addNode({
|
||||||
id: id,
|
id: id,
|
||||||
label: self.graphConfig.nodeLabel,
|
label: self.graphConfig.nodeLabel || '',
|
||||||
size: self.graphConfig.nodeSize || Math.random(),
|
size: self.graphConfig.nodeSize || Math.random(),
|
||||||
color: self.graphConfig.nodeColor || '#2ecc71',
|
color: self.graphConfig.nodeColor || '#2ecc71',
|
||||||
x: self.cursorX,
|
x: self.cursorX,
|
||||||
|
@ -412,6 +502,26 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateColors: function () {
|
||||||
|
var combinedName = window.App.currentDB.toJSON().name + '_' + this.name;
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.userConfig.fetch({
|
||||||
|
success: function (data) {
|
||||||
|
self.graphConfig = data.toJSON().graphs[combinedName];
|
||||||
|
self.currentGraph.graph.nodes().forEach(function (n) {
|
||||||
|
n.color = self.graphConfig.nodeColor;
|
||||||
|
});
|
||||||
|
|
||||||
|
self.currentGraph.graph.edges().forEach(function (e) {
|
||||||
|
e.color = self.graphConfig.edgeColor;
|
||||||
|
});
|
||||||
|
|
||||||
|
self.currentGraph.refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
// right click background context menu
|
// right click background context menu
|
||||||
createContextMenu: function (e) {
|
createContextMenu: function (e) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -498,7 +608,7 @@
|
||||||
wheel.multiSelect = true;
|
wheel.multiSelect = true;
|
||||||
wheel.clickModeRotate = false;
|
wheel.clickModeRotate = false;
|
||||||
wheel.slicePathFunction = slicePath().DonutSlice;
|
wheel.slicePathFunction = slicePath().DonutSlice;
|
||||||
wheel.createWheel([icon.edit, icon.trash, icon.arrowleft2, icon.connect]);
|
wheel.createWheel([icon.edit, icon.trash, icon.play, icon.connect]);
|
||||||
|
|
||||||
wheel.navItems[0].selected = false;
|
wheel.navItems[0].selected = false;
|
||||||
wheel.navItems[0].hovered = false;
|
wheel.navItems[0].hovered = false;
|
||||||
|
@ -513,7 +623,7 @@
|
||||||
// function 1: delete
|
// function 1: delete
|
||||||
wheel.navItems[1].navigateFunction = function (e) {
|
wheel.navItems[1].navigateFunction = function (e) {
|
||||||
self.clearOldContextMenu();
|
self.clearOldContextMenu();
|
||||||
self.deleteNode(nodeId);
|
self.deleteNodeModal(nodeId);
|
||||||
};
|
};
|
||||||
|
|
||||||
// function 2: mark as start node
|
// function 2: mark as start node
|
||||||
|
@ -599,6 +709,11 @@
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setStartNode: function (id) {
|
||||||
|
this.graphConfig.nodeStart = id;
|
||||||
|
this.graphSettingsView.saveGraphSettings(null, id);
|
||||||
|
},
|
||||||
|
|
||||||
editNode: function (id) {
|
editNode: function (id) {
|
||||||
var callback = function () {};
|
var callback = function () {};
|
||||||
|
|
||||||
|
@ -627,6 +742,44 @@
|
||||||
return array;
|
return array;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
initializeGraph: function (sigmaInstance, graph) {
|
||||||
|
var self = this;
|
||||||
|
// sigmaInstance.graph.read(graph);
|
||||||
|
sigmaInstance.refresh();
|
||||||
|
|
||||||
|
this.Sigma.plugins.Lasso = sigma.plugins.lasso;
|
||||||
|
|
||||||
|
var lasso = new this.Sigma.plugins.Lasso(sigmaInstance, sigmaInstance.renderers[0], {
|
||||||
|
'strokeStyle': 'black',
|
||||||
|
'lineWidth': 1,
|
||||||
|
'fillWhileDrawing': true,
|
||||||
|
'fillStyle': 'rgba(41, 41, 41, 0.2)',
|
||||||
|
'cursor': 'crosshair'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for selectedNodes event
|
||||||
|
lasso.bind('selectedNodes', function (event) {
|
||||||
|
// Do something with the selected nodes
|
||||||
|
var nodes = event.data;
|
||||||
|
|
||||||
|
console.log('nodes', nodes);
|
||||||
|
|
||||||
|
// For instance, reset all node size as their initial size
|
||||||
|
sigmaInstance.graph.nodes().forEach(function (node) {
|
||||||
|
node.color = self.graphConfig.nodeColor;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Then increase the size of selected nodes...
|
||||||
|
nodes.forEach(function (node) {
|
||||||
|
node.color = 'red';
|
||||||
|
});
|
||||||
|
|
||||||
|
sigmaInstance.refresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
return lasso;
|
||||||
|
},
|
||||||
|
|
||||||
renderGraph: function (graph, toFocus) {
|
renderGraph: function (graph, toFocus) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
@ -634,16 +787,22 @@
|
||||||
|
|
||||||
if (graph.edges.length === 0) {
|
if (graph.edges.length === 0) {
|
||||||
var string = 'No edges found for starting point: <span style="font-weight: 400">' + self.graphSettings.startVertex._id + '</span>';
|
var string = 'No edges found for starting point: <span style="font-weight: 400">' + self.graphSettings.startVertex._id + '</span>';
|
||||||
arangoHelper.arangoError('Graph', string);
|
|
||||||
$('#calculatingGraph').html(
|
$('#calculatingGraph').html(
|
||||||
'<div style="font-weight: 300; font-size: 10.5pt"><span style="font-weight: 400">Stopped. </span></br></br>' +
|
'<div style="font-weight: 300; font-size: 10.5pt"><span style="font-weight: 400">Stopped. </span></br></br>' +
|
||||||
string +
|
string +
|
||||||
'. Please <a style="color: #3498db" href="' + window.location.href +
|
'. Please <span id="noGraphToggle" style="cursor: pointer; color: #3498db">choose a different start node </span>or try to reload the graph. ' +
|
||||||
'/settings">choose a different start node </a>or try to reload the graph. ' +
|
|
||||||
'<i id="reloadGraph" class="fa fa-refresh" style="cursor: pointer"></i></div>'
|
'<i id="reloadGraph" class="fa fa-refresh" style="cursor: pointer"></i></div>'
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
$('#content').append(
|
||||||
|
'<div style="position: absolute; right: 25px; bottom: 45px;">' +
|
||||||
|
'<span style="margin-right: 10px" class="arangoState">' + graph.nodes.length + ' nodes</span>' +
|
||||||
|
'<span class="arangoState">' + graph.edges.length + ' edges</span>' +
|
||||||
|
'</div>'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Sigma = sigma;
|
this.Sigma = sigma;
|
||||||
|
|
||||||
// defaults
|
// defaults
|
||||||
|
@ -674,15 +833,15 @@
|
||||||
defaultEdgeHoverColor: '#000',
|
defaultEdgeHoverColor: '#000',
|
||||||
defaultEdgeType: 'line',
|
defaultEdgeType: 'line',
|
||||||
edgeHoverSizeRatio: 2,
|
edgeHoverSizeRatio: 2,
|
||||||
edgeHoverExtremities: true
|
edgeHoverExtremities: true,
|
||||||
|
// lasso settings
|
||||||
|
autoRescale: true,
|
||||||
|
mouseEnabled: true,
|
||||||
|
touchEnabled: true,
|
||||||
|
nodesPowRatio: 1,
|
||||||
|
edgesPowRatio: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.graphConfig) {
|
|
||||||
if (this.graphConfig.edgeType) {
|
|
||||||
settings.defaultEdgeType = this.graphConfig.edgeType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// adjust display settings for big graphs
|
// adjust display settings for big graphs
|
||||||
if (graph.nodes.length > 500) {
|
if (graph.nodes.length > 500) {
|
||||||
// show node label if size is 15
|
// show node label if size is 15
|
||||||
|
@ -690,6 +849,20 @@
|
||||||
settings.hideEdgesOnMove = true;
|
settings.hideEdgesOnMove = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.graphConfig) {
|
||||||
|
if (this.graphConfig.edgeType) {
|
||||||
|
settings.defaultEdgeType = this.graphConfig.edgeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.graphConfig.nodeLabelThreshold) {
|
||||||
|
settings.labelThreshold = this.graphConfig.nodeLabelThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.graphConfig.edgeLabelThreshold) {
|
||||||
|
settings.edgeLabelThreshold = this.graphConfig.edgeLabelThreshold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// adjust display settings for webgl renderer
|
// adjust display settings for webgl renderer
|
||||||
if (renderer === 'webgl') {
|
if (renderer === 'webgl') {
|
||||||
settings.enableEdgeHovering = false;
|
settings.enableEdgeHovering = false;
|
||||||
|
@ -751,6 +924,54 @@
|
||||||
self.clearMouseCanvas();
|
self.clearMouseCanvas();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
s.bind('overNode', function (e) {
|
||||||
|
$('.nodeInfoDiv').remove();
|
||||||
|
if (self.contextState.createEdge === false) {
|
||||||
|
var callback = function (error, data) {
|
||||||
|
if (!error) {
|
||||||
|
var obj = {};
|
||||||
|
var counter = 0;
|
||||||
|
var more = false;
|
||||||
|
|
||||||
|
_.each(data, function (val, key) {
|
||||||
|
if (counter < 15) {
|
||||||
|
if (typeof val === 'string') {
|
||||||
|
if (val.length > 10) {
|
||||||
|
obj[key] = val.substr(0, 10) + ' ...';
|
||||||
|
} else {
|
||||||
|
obj[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
more = true;
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
|
});
|
||||||
|
|
||||||
|
var string = '<div id="nodeInfoDiv" class="nodeInfoDiv">' +
|
||||||
|
'<pre>' + JSON.stringify(obj, null, 2);
|
||||||
|
|
||||||
|
if (more) {
|
||||||
|
string = string.substr(0, string.length - 2);
|
||||||
|
string += ' \n\n ... \n\n } </pre></div>';
|
||||||
|
} else {
|
||||||
|
string += '</pre></div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#content').append(string);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.documentStore.getDocument(e.data.node.id.split('/')[0], e.data.node.id.split('/')[1], callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
s.bind('outNode', function (e) {
|
||||||
|
if (self.contextState.createEdge === false) {
|
||||||
|
$('.nodeInfoDiv').remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
s.bind('clickNode', function (e) {
|
s.bind('clickNode', function (e) {
|
||||||
if (self.contextState.createEdge === true) {
|
if (self.contextState.createEdge === true) {
|
||||||
// create the edge
|
// create the edge
|
||||||
|
@ -831,6 +1052,7 @@
|
||||||
window.setTimeout(function () {
|
window.setTimeout(function () {
|
||||||
s.stopForceAtlas2();
|
s.stopForceAtlas2();
|
||||||
dragListener = sigma.plugins.dragNodes(s, s.renderers[0]);
|
dragListener = sigma.plugins.dragNodes(s, s.renderers[0]);
|
||||||
|
console.log(dragListener);
|
||||||
}, duration);
|
}, duration);
|
||||||
} else if (algorithm === 'fruchtermann') {
|
} else if (algorithm === 'fruchtermann') {
|
||||||
// Start the Fruchterman-Reingold algorithm:
|
// Start the Fruchterman-Reingold algorithm:
|
||||||
|
@ -839,7 +1061,6 @@
|
||||||
} else {
|
} else {
|
||||||
dragListener = sigma.plugins.dragNodes(s, s.renderers[0]);
|
dragListener = sigma.plugins.dragNodes(s, s.renderers[0]);
|
||||||
}
|
}
|
||||||
console.log(dragListener);
|
|
||||||
|
|
||||||
// add listener to keep track of cursor position
|
// add listener to keep track of cursor position
|
||||||
var c = document.getElementsByClassName('sigma-mouse')[0];
|
var c = document.getElementsByClassName('sigma-mouse')[0];
|
||||||
|
@ -849,6 +1070,28 @@
|
||||||
if (toFocus) {
|
if (toFocus) {
|
||||||
$('#' + toFocus).focus();
|
$('#' + toFocus).focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// init graph lasso
|
||||||
|
self.graphLasso = self.initializeGraph(s, graph);
|
||||||
|
self.graphLasso.activate();
|
||||||
|
self.graphLasso.deactivate();
|
||||||
|
|
||||||
|
// add lasso event
|
||||||
|
// Toggle lasso activation on Alt + l
|
||||||
|
document.addEventListener('keyup', function (event) {
|
||||||
|
switch (event.keyCode) {
|
||||||
|
case 76:
|
||||||
|
if (event.altKey) {
|
||||||
|
if (self.graphLasso.isActive) {
|
||||||
|
self.graphLasso.deactivate();
|
||||||
|
} else {
|
||||||
|
self.graphLasso.activate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// clear up info div
|
// clear up info div
|
||||||
$('#calculatingGraph').remove();
|
$('#calculatingGraph').remove();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1473,15 +1473,6 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (data.extra.stats.scannedFull > 0) {
|
|
||||||
appendSpan(
|
|
||||||
'full collection scan', 'fa-exclamation-circle warning', 'additional'
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
appendSpan(
|
|
||||||
'no full collection scan', 'fa-check-circle positive', 'additional'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
.graphContent {
|
.graphContent {
|
||||||
|
|
||||||
|
margin-top: 3px;
|
||||||
|
|
||||||
#graph-container {
|
#graph-container {
|
||||||
background-color: $c-white;
|
background-color: $c-white;
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
|
@ -40,6 +42,22 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nodeInfoDiv {
|
||||||
|
left: 25px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
top: 130px;
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background-color: rgba(64, 74, 83, .9);
|
||||||
|
border-radius: 2px;
|
||||||
|
color: $c-white;
|
||||||
|
max-height: 400px;
|
||||||
|
max-width: 330px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.nodeContextMenu {
|
.nodeContextMenu {
|
||||||
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@ -67,6 +85,11 @@
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
overflow-y: scroll;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-bottom: 125px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#graphSettingsContent {
|
#graphSettingsContent {
|
||||||
|
@ -79,13 +102,29 @@
|
||||||
width: 400px;
|
width: 400px;
|
||||||
|
|
||||||
.pure-g {
|
.pure-g {
|
||||||
|
|
||||||
|
font-size: 10pt;
|
||||||
|
|
||||||
|
input,
|
||||||
|
select {
|
||||||
|
color: $c-black;
|
||||||
|
}
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
color: $c-white;
|
color: $c-white;
|
||||||
|
height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pure-u-2-3 {
|
.pure-u-2-3 {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.heading {
|
||||||
|
border-bottom: 1px solid $c-white;
|
||||||
|
height: 10px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pure-table {
|
.pure-table {
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
input[type=range] {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
border: 1px solid $c-white;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin-top: 10px;
|
||||||
|
width: 218px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=range]::-webkit-slider-runnable-track {
|
||||||
|
background: $c-tab-bottom-border;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 3px;
|
||||||
|
height: 5px;
|
||||||
|
width: 218px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=range]::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
background: $c-positive;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
height: 16px;
|
||||||
|
margin-top: -5px;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=range]:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=range]:focus::-webkit-slider-runnable-track {
|
||||||
|
background: $c-accordion-heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=range]::-moz-range-track {
|
||||||
|
background: $c-e1grey;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 3px;
|
||||||
|
height: 5px;
|
||||||
|
width: 218px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=range]::-moz-range-thumb {
|
||||||
|
background: $c-positive;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=range]:-moz-focusring {
|
||||||
|
outline: 1px solid $c-white;
|
||||||
|
outline-offset: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=range]::-ms-track {
|
||||||
|
background: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
border-width: 6px 0;
|
||||||
|
color: transparent;
|
||||||
|
height: 5px;
|
||||||
|
width: 218px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=range]::-ms-fill-lower {
|
||||||
|
background: $c-darker-grey;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=range]::-ms-fill-upper {
|
||||||
|
background: $c-tab-bottom-border;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=range]::-ms-thumb {
|
||||||
|
background: $c-positive;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=range]:focus::-ms-fill-lower {
|
||||||
|
background: $c-dark-grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=range]:focus::-ms-fill-upper {
|
||||||
|
background: $c-accordion-heading;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
.arangoState {
|
||||||
|
background-color: $c-bluegrey-dark;
|
||||||
|
border-radius: 3px;
|
||||||
|
color: $c-white;
|
||||||
|
font-size: 10pt;
|
||||||
|
font-weight: 100;
|
||||||
|
padding: 5px 8px;
|
||||||
|
}
|
|
@ -86,6 +86,11 @@
|
||||||
// screen shards
|
// screen shards
|
||||||
@import 'shards';
|
@import 'shards';
|
||||||
|
|
||||||
|
// input type range
|
||||||
|
@import 'range';
|
||||||
|
// state
|
||||||
|
@import 'state';
|
||||||
|
|
||||||
//arangoTable Template
|
//arangoTable Template
|
||||||
@import 'arangoTable';
|
@import 'arangoTable';
|
||||||
//arangoTabbar Template
|
//arangoTabbar Template
|
||||||
|
|
Loading…
Reference in New Issue