mirror of https://gitee.com/bigwinds/arangodb
This commit is contained in:
parent
9cf2fb9826
commit
87ddbf7f1d
|
@ -64,13 +64,16 @@
|
|||
"frontend/js/lib/sigma.layout.noverlap.js",
|
||||
"frontend/js/lib/sigma.plugins.fullScreen.js",
|
||||
"frontend/js/lib/sigma.layout.fruchtermanReingold.js",
|
||||
"frontend/js/lib/sigma.exporters.svg.js",
|
||||
"frontend/js/lib/worker.js",
|
||||
"frontend/js/lib/supervisor.js",
|
||||
// END SIGMA LIBRARIES
|
||||
// START NEW
|
||||
"frontend/js/lib/wheelnav.slicePath.js",
|
||||
"frontend/js/lib/wheelnav.min.js",
|
||||
"frontend/js/lib/raphael.min.js",
|
||||
"frontend/js/lib/raphael.icons.min.js",
|
||||
// END NEW LIBRARIES
|
||||
"frontend/js/lib/jsoneditor-min.js",
|
||||
"frontend/js/lib/strftime-min.js",
|
||||
"frontend/js/lib/d3.fisheye.min.js",
|
||||
|
|
|
@ -1,28 +1,31 @@
|
|||
/* global AQL_EXECUTE */
|
||||
|
||||
'use strict';
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2010-2013 triAGENS GmbH, Cologne, Germany
|
||||
/// Copyright 2016 ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Michael Hackstein
|
||||
/// @author Alan Plum
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////////////////
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2010-2013 triAGENS GmbH, Cologne, Germany
|
||||
// Copyright 2016 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// @author Michael Hackstein
|
||||
// @author Heiko Kernbach
|
||||
// @author Alan Plum
|
||||
// //////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const joi = require('joi');
|
||||
const dd = require('dedent');
|
||||
|
@ -42,7 +45,7 @@ API_DOCS.basePath = `/_db/${encodeURIComponent(db._name())}`;
|
|||
const router = createRouter();
|
||||
module.exports = router;
|
||||
|
||||
router.get('/config.js', function(req, res) {
|
||||
router.get('/config.js', function (req, res) {
|
||||
const scriptName = req.get('x-script-name');
|
||||
const basePath = req.trustProxy && scriptName || '';
|
||||
res.send(
|
||||
|
@ -56,7 +59,7 @@ router.get('/config.js', function(req, res) {
|
|||
})
|
||||
.response(['text/javascript']);
|
||||
|
||||
router.get('/whoAmI', function(req, res) {
|
||||
router.get('/whoAmI', function (req, res) {
|
||||
res.json({user: req.arangoUser || null});
|
||||
})
|
||||
.summary('Return the current user')
|
||||
|
@ -65,7 +68,6 @@ router.get('/whoAmI', function(req, res) {
|
|||
Returns "false" if authentication is disabled.
|
||||
`);
|
||||
|
||||
|
||||
const authRouter = createRouter();
|
||||
router.use(authRouter);
|
||||
|
||||
|
@ -78,9 +80,8 @@ authRouter.use((req, res, next) => {
|
|||
next();
|
||||
});
|
||||
|
||||
|
||||
router.get('/api/*', module.context.apiDocumentation({
|
||||
swaggerJson(req, res) {
|
||||
swaggerJson (req, res) {
|
||||
res.json(API_DOCS);
|
||||
}
|
||||
}))
|
||||
|
@ -89,8 +90,7 @@ router.get('/api/*', module.context.apiDocumentation({
|
|||
Mounts the system API documentation.
|
||||
`);
|
||||
|
||||
|
||||
authRouter.get('shouldCheckVersion', function(req, res) {
|
||||
authRouter.get('shouldCheckVersion', function (req, res) {
|
||||
const versions = notifications.versions();
|
||||
res.json(Boolean(versions && versions.enableVersionNotification));
|
||||
})
|
||||
|
@ -99,8 +99,7 @@ authRouter.get('shouldCheckVersion', function(req, res) {
|
|||
Check if version check is allowed.
|
||||
`);
|
||||
|
||||
|
||||
authRouter.post('disableVersionCheck', function(req, res) {
|
||||
authRouter.post('disableVersionCheck', function (req, res) {
|
||||
notifications.setVersions({enableVersionNotification: false});
|
||||
res.json('ok');
|
||||
})
|
||||
|
@ -109,8 +108,7 @@ authRouter.post('disableVersionCheck', function(req, res) {
|
|||
Disable the version check in web interface
|
||||
`);
|
||||
|
||||
|
||||
authRouter.post('/query/explain', function(req, res) {
|
||||
authRouter.post('/query/explain', function (req, res) {
|
||||
const bindVars = req.body.bindVars;
|
||||
const query = req.body.query;
|
||||
const id = req.body.id;
|
||||
|
@ -145,8 +143,7 @@ authRouter.post('/query/explain', function(req, res) {
|
|||
Explains a query in a more user-friendly way than the query_api/explain
|
||||
`);
|
||||
|
||||
|
||||
authRouter.post('/query/upload/:user', function(req, res) {
|
||||
authRouter.post('/query/upload/:user', function (req, res) {
|
||||
let user = req.pathParams.user;
|
||||
|
||||
try {
|
||||
|
@ -187,8 +184,7 @@ authRouter.post('/query/upload/:user', function(req, res) {
|
|||
This function uploads all given user queries.
|
||||
`);
|
||||
|
||||
|
||||
authRouter.get('/query/download/:user', function(req, res) {
|
||||
authRouter.get('/query/download/:user', function (req, res) {
|
||||
let user = req.pathParams.user;
|
||||
|
||||
try {
|
||||
|
@ -210,14 +206,12 @@ authRouter.get('/query/download/:user', function(req, res) {
|
|||
Download and export all queries from the given username.
|
||||
`);
|
||||
|
||||
|
||||
authRouter.get('/query/result/download/:query', function(req, res) {
|
||||
authRouter.get('/query/result/download/:query', function (req, res) {
|
||||
let query;
|
||||
try {
|
||||
query = internal.base64Decode(req.pathParams.query);
|
||||
query = JSON.parse(query);
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
res.throw('bad request', e.message, {cause: e});
|
||||
}
|
||||
|
||||
|
@ -232,8 +226,7 @@ authRouter.get('/query/result/download/:query', function(req, res) {
|
|||
This function downloads the result of a user query.
|
||||
`);
|
||||
|
||||
|
||||
authRouter.post('/graph-examples/create/:name', function(req, res) {
|
||||
authRouter.post('/graph-examples/create/:name', function (req, res) {
|
||||
const name = req.pathParams.name;
|
||||
|
||||
if (['knows_graph', 'social', 'routeplanner'].indexOf(name) === -1) {
|
||||
|
@ -249,8 +242,7 @@ authRouter.post('/graph-examples/create/:name', function(req, res) {
|
|||
Create one of the given example graphs.
|
||||
`);
|
||||
|
||||
|
||||
authRouter.post('/job', function(req, res) {
|
||||
authRouter.post('/job', function (req, res) {
|
||||
db._frontend.save(Object.assign(req.body, {model: 'job'}));
|
||||
res.json(true);
|
||||
})
|
||||
|
@ -265,8 +257,7 @@ authRouter.post('/job', function(req, res) {
|
|||
Create a new job id entry in a specific system database with a given id.
|
||||
`);
|
||||
|
||||
|
||||
authRouter.delete('/job', function(req, res) {
|
||||
authRouter.delete('/job', function (req, res) {
|
||||
db._frontend.removeByExample({model: 'job'}, false);
|
||||
res.json(true);
|
||||
})
|
||||
|
@ -275,8 +266,7 @@ authRouter.delete('/job', function(req, res) {
|
|||
Delete all jobs in a specific system database with a given id.
|
||||
`);
|
||||
|
||||
|
||||
authRouter.delete('/job/:id', function(req, res) {
|
||||
authRouter.delete('/job/:id', function (req, res) {
|
||||
db._frontend.removeByExample({id: req.pathParams.id}, false);
|
||||
res.json(true);
|
||||
})
|
||||
|
@ -285,8 +275,7 @@ authRouter.delete('/job/:id', function(req, res) {
|
|||
Delete an existing job id entry in a specific system database with a given id.
|
||||
`);
|
||||
|
||||
|
||||
authRouter.get('/job', function(req, res) {
|
||||
authRouter.get('/job', function (req, res) {
|
||||
const result = db._frontend.all().toArray();
|
||||
res.json(result);
|
||||
})
|
||||
|
@ -295,28 +284,30 @@ authRouter.get('/job', function(req, res) {
|
|||
This function returns the job ids of all currently running jobs.
|
||||
`);
|
||||
|
||||
|
||||
authRouter.get('/graph/:name', function(req, res) {
|
||||
var _ = require("lodash");
|
||||
authRouter.get('/graph/:name', function (req, res) {
|
||||
var _ = require('lodash');
|
||||
var name = req.pathParams.name;
|
||||
var gm = require("@arangodb/general-graph");
|
||||
//var traversal = require("@arangodb/graph/traversal");
|
||||
var gm = require('@arangodb/general-graph');
|
||||
// var traversal = require("@arangodb/graph/traversal");
|
||||
|
||||
var graph = gm._graph(name);
|
||||
var vertexName = graph._vertexCollections()[0].name();
|
||||
var startVertex = db[vertexName].any();
|
||||
|
||||
var aqlQuery =
|
||||
'FOR v, e, p IN 1..3 ANY "' + startVertex._id + '" GRAPH "' + name + '"' +
|
||||
var aqlQuery =
|
||||
'FOR v, e, p IN 1..3 ANY "' + startVertex._id + '" GRAPH "' + name + '"' +
|
||||
'RETURN p'
|
||||
;
|
||||
|
||||
var cursor = AQL_EXECUTE(aqlQuery);
|
||||
|
||||
var nodesObj = {}, nodesArr = [], edgesObj = {}, edgesArr = [];
|
||||
var nodesObj = {};
|
||||
var nodesArr = [];
|
||||
var edgesObj = {};
|
||||
var edgesArr = [];
|
||||
|
||||
_.each(cursor.json, function(obj) {
|
||||
_.each(obj.edges, function(edge) {
|
||||
_.each(cursor.json, function (obj) {
|
||||
_.each(obj.edges, function (edge) {
|
||||
if (edge._to && edge._from) {
|
||||
edgesObj[edge._from + edge._to] = {
|
||||
id: edge._id,
|
||||
|
@ -327,11 +318,10 @@ authRouter.get('/graph/:name', function(req, res) {
|
|||
}
|
||||
});
|
||||
var label;
|
||||
_.each(obj.vertices, function(node) {
|
||||
_.each(obj.vertices, function (node) {
|
||||
if (node.label) {
|
||||
label = node.label;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
label = node._id;
|
||||
}
|
||||
|
||||
|
@ -346,11 +336,11 @@ authRouter.get('/graph/:name', function(req, res) {
|
|||
});
|
||||
});
|
||||
|
||||
//array format for sigma.js
|
||||
_.each(edgesObj, function(node) {
|
||||
// array format for sigma.js
|
||||
_.each(edgesObj, function (node) {
|
||||
edgesArr.push(node);
|
||||
});
|
||||
_.each(nodesObj, function(node) {
|
||||
_.each(nodesObj, function (node) {
|
||||
nodesArr.push(node);
|
||||
});
|
||||
|
||||
|
@ -358,7 +348,6 @@ authRouter.get('/graph/:name', function(req, res) {
|
|||
nodes: nodesArr,
|
||||
edges: edgesArr
|
||||
});
|
||||
|
||||
})
|
||||
.summary('Return vertices and edges of a graph.')
|
||||
.description(dd`
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* jshint strict: false, unused: false */
|
||||
/* global Backbone, window, arangoDocumentModel, $, arangoHelper */
|
||||
|
||||
window.arangoDocument = Backbone.Collection.extend({
|
||||
window.ArangoDocument = Backbone.Collection.extend({
|
||||
url: '/_api/document/',
|
||||
model: arangoDocumentModel,
|
||||
collectionInfo: {},
|
||||
|
|
|
@ -0,0 +1,248 @@
|
|||
;(function(undefined) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Sigma SVG Exporter
|
||||
* ===================
|
||||
*
|
||||
* This plugin is designed to export a graph to a svg file that can be
|
||||
* downloaded or just used elsewhere.
|
||||
*
|
||||
* Author: Guillaume Plique (Yomguithereal)
|
||||
* Version: 0.0.1
|
||||
*/
|
||||
|
||||
// Terminating if sigma were not to be found
|
||||
if (typeof sigma === 'undefined')
|
||||
throw 'sigma.renderers.snapshot: sigma not in scope.';
|
||||
|
||||
|
||||
/**
|
||||
* Polyfills
|
||||
*/
|
||||
var URL = this.URL || this.webkitURL || this;
|
||||
|
||||
|
||||
/**
|
||||
* Utilities
|
||||
*/
|
||||
function createBlob(data) {
|
||||
return new Blob(
|
||||
[data],
|
||||
{type: 'image/svg+xml;charset=utf-8'}
|
||||
);
|
||||
}
|
||||
|
||||
function download(string, filename) {
|
||||
if (typeof safari !== 'undefined') {
|
||||
var msg = "File download does not work in Safari. Please use a modern web browser such as Firefox, Chrome, or Internet Explorer 11.";
|
||||
alert(msg);
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
// Blob
|
||||
var blob = createBlob(string),
|
||||
objectUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
if (navigator.msSaveBlob) { // IE11+ : (has Blob, but not a[download])
|
||||
navigator.msSaveBlob(blob, filename);
|
||||
} else if (navigator.msSaveOrOpenBlob) { // IE10+ : (has Blob, but not a[download])
|
||||
navigator.msSaveOrOpenBlob(blob, filename);
|
||||
} else {
|
||||
// A-download
|
||||
var anchor = document.createElement('a');
|
||||
anchor.setAttribute('href', objectUrl);
|
||||
anchor.setAttribute('download', filename);
|
||||
|
||||
// Firefox requires the link to be added to the DOM before it can be clicked.
|
||||
document.body.appendChild(anchor);
|
||||
anchor.click();
|
||||
document.body.removeChild(anchor);
|
||||
}
|
||||
|
||||
setTimeout(function() { // Firefox needs a timeout
|
||||
window.URL.revokeObjectURL(objectUrl);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defaults
|
||||
*/
|
||||
var DEFAULTS = {
|
||||
size: '1000',
|
||||
width: '1000',
|
||||
height: '1000',
|
||||
margin: 0.05,
|
||||
classes: true,
|
||||
labels: true,
|
||||
data: false,
|
||||
download: false,
|
||||
filename: 'graph.svg'
|
||||
};
|
||||
|
||||
var XMLNS = 'http://www.w3.org/2000/svg';
|
||||
|
||||
|
||||
/**
|
||||
* Subprocesses
|
||||
*/
|
||||
function optimize(svg, prefix, params) {
|
||||
var nodeColorIndex = {},
|
||||
edgeColorIndex = {},
|
||||
count = 0,
|
||||
color,
|
||||
style,
|
||||
styleText = '',
|
||||
f,
|
||||
i,
|
||||
l;
|
||||
|
||||
// Creating style tag if needed
|
||||
if (params.classes) {
|
||||
style = document.createElementNS(XMLNS, 'style');
|
||||
svg.insertBefore(style, svg.firstChild);
|
||||
}
|
||||
|
||||
// Iterating over nodes
|
||||
var nodes = svg.querySelectorAll('[id="' + prefix + '-group-nodes"] > [class="' + prefix + '-node"]');
|
||||
|
||||
for (i = 0, l = nodes.length, f = true; i < l; i++) {
|
||||
color = nodes[i].getAttribute('fill');
|
||||
|
||||
if (!params.data)
|
||||
nodes[i].removeAttribute('data-node-id');
|
||||
|
||||
if (params.classes) {
|
||||
|
||||
if (!(color in nodeColorIndex)) {
|
||||
nodeColorIndex[color] = (f ? prefix + '-node' : 'c-' + (count++));
|
||||
styleText += '.' + nodeColorIndex[color] + '{fill: ' + color + '}';
|
||||
}
|
||||
|
||||
if (nodeColorIndex[color] !== prefix + '-node')
|
||||
nodes[i].setAttribute('class', nodes[i].getAttribute('class') + ' ' + nodeColorIndex[color]);
|
||||
nodes[i].removeAttribute('fill');
|
||||
}
|
||||
|
||||
f = false;
|
||||
}
|
||||
|
||||
// Iterating over edges
|
||||
var edges = svg.querySelectorAll('[id="' + prefix + '-group-edges"] > [class="' + prefix + '-edge"]');
|
||||
|
||||
for (i = 0, l = edges.length, f = true; i < l; i++) {
|
||||
color = edges[i].getAttribute('stroke');
|
||||
|
||||
if (!params.data)
|
||||
edges[i].removeAttribute('data-edge-id');
|
||||
|
||||
if (params.classes) {
|
||||
|
||||
if (!(color in edgeColorIndex)) {
|
||||
edgeColorIndex[color] = (f ? prefix + '-edge' : 'c-' + (count++));
|
||||
styleText += '.' + edgeColorIndex[color] + '{stroke: ' + color + '}';
|
||||
}
|
||||
|
||||
if (edgeColorIndex[color] !== prefix + '-edge')
|
||||
edges[i].setAttribute('class', edges[i].getAttribute('class') + ' ' + edgeColorIndex[color]);
|
||||
edges[i].removeAttribute('stroke');
|
||||
}
|
||||
|
||||
f = false;
|
||||
}
|
||||
|
||||
if (params.classes)
|
||||
style.appendChild(document.createTextNode(styleText));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extending prototype
|
||||
*/
|
||||
sigma.prototype.toSVG = function(params) {
|
||||
params = params || {};
|
||||
|
||||
var prefix = this.settings('classPrefix'),
|
||||
w = params.size || params.width || DEFAULTS.size,
|
||||
h = params.size || params.height || DEFAULTS.size,
|
||||
margin = params.margin || DEFAULTS.margin;
|
||||
|
||||
// Creating a dummy container
|
||||
var container = document.createElement('div');
|
||||
container.setAttribute('width', w);
|
||||
container.setAttribute('height', h);
|
||||
container.setAttribute('style', 'position:absolute; top: 0px; left:0px; width: ' + w + 'px; height: ' + h + 'px;');
|
||||
|
||||
// Add margin to deal with curved edges
|
||||
var sideMargin = this.settings('sideMargin');
|
||||
this.settings('sideMargin', margin);
|
||||
|
||||
// Fit graph to viewport
|
||||
var autoRescale = this.settings('autoRescale');
|
||||
this.settings('autoRescale', true);
|
||||
|
||||
// Creating a camera
|
||||
var camera = this.addCamera();
|
||||
|
||||
// Creating a svg renderer
|
||||
var renderer = this.addRenderer({
|
||||
camera: camera,
|
||||
container: container,
|
||||
type: 'svg',
|
||||
forceLabels: !!params.labels
|
||||
});
|
||||
|
||||
// Refreshing
|
||||
renderer.resize(w, h);
|
||||
this.refresh();
|
||||
|
||||
// Dropping camera and renderers before something nasty happens
|
||||
this.killRenderer(renderer);
|
||||
this.killCamera(camera);
|
||||
|
||||
// reset setting
|
||||
this.settings('sideMargin', sideMargin);
|
||||
this.settings('autoRescale', autoRescale);
|
||||
|
||||
// Retrieving svg
|
||||
var svg = container.querySelector('svg');
|
||||
svg.removeAttribute('style');
|
||||
svg.setAttribute('width', w + 'px');
|
||||
svg.setAttribute('height', h + 'px');
|
||||
svg.setAttribute('x', '0px');
|
||||
svg.setAttribute('y', '0px');
|
||||
// svg.setAttribute('viewBox', '0 0 1000 1000');
|
||||
|
||||
// Dropping labels
|
||||
if (!params.labels) {
|
||||
var labelGroup = svg.querySelector('[id="' + prefix + '-group-labels"]');
|
||||
svg.removeChild(labelGroup);
|
||||
}
|
||||
|
||||
// Dropping hovers
|
||||
var hoverGroup = svg.querySelector('[id="' + prefix + '-group-hovers"]');
|
||||
svg.removeChild(hoverGroup);
|
||||
|
||||
// Optims?
|
||||
params.classes = (params.classes !== false);
|
||||
if (!params.data || params.classes)
|
||||
optimize(svg, prefix, params);
|
||||
|
||||
// Retrieving svg string
|
||||
var svgString = svg.outerHTML;
|
||||
|
||||
// Paranoid cleanup
|
||||
container = null;
|
||||
|
||||
// Output string
|
||||
var output = '<?xml version="1.0" encoding="utf-8"?>\n';
|
||||
output += '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n';
|
||||
output += svgString;
|
||||
|
||||
if (params.download)
|
||||
download(output, params.filename || DEFAULTS.filename);
|
||||
|
||||
return output;
|
||||
};
|
||||
}).call(this);
|
|
@ -857,6 +857,9 @@
|
|||
if (this.queryView) {
|
||||
this.queryView.resize();
|
||||
}
|
||||
if (this.graphViewer2) {
|
||||
this.graphViewer2.resize();
|
||||
}
|
||||
if (this.documentsView) {
|
||||
this.documentsView.resize();
|
||||
}
|
||||
|
|
|
@ -30,8 +30,38 @@
|
|||
|
||||
<div class="pure-g pure-table pure-table-body">
|
||||
<% _.each(specific, function(val, key) { %>
|
||||
<div class="<%= genClass %> left"><%=key%></div>
|
||||
<div class="<%= genClass %> left"><%=val%></div>
|
||||
<div class="<%= genClass %> left"><%=val.name%></div>
|
||||
<div class="<%= genClass %> left">
|
||||
|
||||
|
||||
<% var VALUE; %>
|
||||
<% if (val.value) { %>
|
||||
<% VALUE = val.value %>
|
||||
<% } else { %>
|
||||
<% VALUE = val.default %>
|
||||
<% } %>
|
||||
|
||||
<% if (val.type === 'string') { %>
|
||||
<input type="text" value="<%=VALUE%>" placeholder=""></input>
|
||||
<% } %>
|
||||
|
||||
<% if (val.type === 'color') { %>
|
||||
<input type='color' name='color' value="<%=VALUE%>"/>
|
||||
<% } %>
|
||||
|
||||
<% if (val.type === 'select') { %>
|
||||
<div class="<%= genClass %> left">
|
||||
<select>
|
||||
<% _.each(val, function(option, optKey) { %>
|
||||
<% if (option.name) { %>
|
||||
<option> <%=option.name%> </option>
|
||||
<% } %>
|
||||
<% }); %>
|
||||
</select>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
</div>
|
||||
<% }); %>
|
||||
</div>
|
||||
|
||||
|
@ -55,15 +85,33 @@
|
|||
</div>
|
||||
|
||||
<div class="pure-g pure-table pure-table-body">
|
||||
|
||||
<% _.each(general, function(val, key) { %>
|
||||
<div class="<%= genClass %> left"><%=key%></div>
|
||||
<div class="<%= genClass %> left"><%=val%></div>
|
||||
|
||||
<% if (val.type === 'select') { %>
|
||||
<div class="<%= genClass %> left">
|
||||
<select>
|
||||
<% _.each(val, function(option, optKey) { %>
|
||||
<% if (option.name) { %>
|
||||
<option> <%=option.name%> </option>
|
||||
<% } %>
|
||||
<% }); %>
|
||||
</select>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<% if (val.type === 'numeric') { %>
|
||||
<input 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="saveGraphSettings" style="margin-top: 20px;" class="button-success pull-right">Restore defaults</button>
|
||||
</div>
|
||||
|
||||
</script>
|
||||
|
|
|
@ -5,10 +5,23 @@
|
|||
<div class="headerButtonBar" style="margin: 0;">
|
||||
<ul class="headerButtonList">
|
||||
<li class="enabled">
|
||||
<a id="addDocument" class="headerButton">
|
||||
<span title="Add new document">
|
||||
<i class="fa fa-file fa-stack-1x"></i>
|
||||
<i class="fa fa-plus fa-stack-1x fa-top"></i>
|
||||
<a id="graph-fullscreen-btn" class="headerButton">
|
||||
<span title="Switch to fullscreen mode">
|
||||
<i class="fa fa-television"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="enabled">
|
||||
<a id="downloadPNG" class="headerButton">
|
||||
<span title="Download visible graph as png image">
|
||||
<i class="fa fa-camera fa-stack-1x"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="enabled">
|
||||
<a id="selectNodes" class="headerButton">
|
||||
<span title="Download visible graph as png image">
|
||||
<i class="fa fa-pencil fa-stack-1x"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -16,7 +29,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div id="graph-fullscreen-btn"><i class="fa fa-television"></i></div>
|
||||
<!--<div id="graph-fullscreen-btn"><i class="fa fa-desktop"></i></div>-->
|
||||
<div id="graph-container" oncontextmenu="return false;"></div>
|
||||
</div>
|
||||
</script>
|
||||
|
|
|
@ -8,14 +8,81 @@
|
|||
el: '#content',
|
||||
|
||||
general: {
|
||||
'layout': undefined,
|
||||
'depth': undefined
|
||||
'Layout': {
|
||||
type: 'select',
|
||||
noverlap: {
|
||||
name: 'No overlap (fast)'
|
||||
},
|
||||
force: {
|
||||
name: 'Force (slow)'
|
||||
},
|
||||
fruchtermann: {
|
||||
name: 'Fruchtermann (very slow)'
|
||||
}
|
||||
},
|
||||
'Renderer': {
|
||||
type: 'select',
|
||||
canvas: {
|
||||
name: 'Canvas (editable)'
|
||||
},
|
||||
webgl: {
|
||||
name: 'WebGL (only display)'
|
||||
}
|
||||
},
|
||||
'depth': {
|
||||
type: 'numeric',
|
||||
value: 2
|
||||
}
|
||||
},
|
||||
|
||||
specific: {
|
||||
'node_label': undefined,
|
||||
'node_color': undefined,
|
||||
'node_size': undefined
|
||||
'nodeLabel': {
|
||||
type: 'string',
|
||||
name: 'Node label',
|
||||
desc: 'Default node color. RGB or HEX value.',
|
||||
default: '_key'
|
||||
},
|
||||
'nodeColor': {
|
||||
type: 'color',
|
||||
name: 'Node 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
|
||||
},
|
||||
'edgeLabel': {
|
||||
type: 'string',
|
||||
name: 'Edge label',
|
||||
desc: 'Default edge label.',
|
||||
value: undefined
|
||||
},
|
||||
'edgeColor': {
|
||||
type: 'color',
|
||||
name: 'Edge 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
|
||||
},
|
||||
'edgeType': {
|
||||
type: 'select',
|
||||
name: 'Edge type',
|
||||
desc: 'The type of the edge',
|
||||
canvas: {
|
||||
name: 'Straight'
|
||||
},
|
||||
webgl: {
|
||||
name: 'Curved'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
template: templateEngine.createTemplate('graphSettingsView.ejs'),
|
||||
|
@ -24,6 +91,14 @@
|
|||
this.name = options.name;
|
||||
},
|
||||
|
||||
loadGraphSettings: function () {
|
||||
|
||||
},
|
||||
|
||||
saveGraphSettings: function () {
|
||||
|
||||
},
|
||||
|
||||
events: {
|
||||
},
|
||||
|
||||
|
@ -33,6 +108,9 @@
|
|||
specific: this.specific
|
||||
}));
|
||||
arangoHelper.buildGraphSubNav(this.name, 'Settings');
|
||||
|
||||
// load graph settings from local storage
|
||||
// apply those values to view then
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -14,6 +14,10 @@
|
|||
this.initSigma();
|
||||
},
|
||||
|
||||
events: {
|
||||
'click #downloadPNG': 'downloadSVG'
|
||||
},
|
||||
|
||||
initSigma: function () {
|
||||
// init sigma
|
||||
try {
|
||||
|
@ -30,19 +34,40 @@
|
|||
} catch (ignore) {}
|
||||
},
|
||||
|
||||
downloadSVG: function () {
|
||||
var self = this;
|
||||
|
||||
this.currentGraph.toSVG({
|
||||
download: true,
|
||||
filename: self.name + '.svg',
|
||||
size: 1000
|
||||
});
|
||||
},
|
||||
|
||||
resize: function () {
|
||||
// adjust container widht + height
|
||||
$('#graph-container').width($('.centralContent').width());
|
||||
$('#graph-container').height($('.centralRow').height() - 150);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.$el.html(this.template.render({}));
|
||||
arangoHelper.buildGraphSubNav(this.name, 'Content');
|
||||
|
||||
// adjust container widht + height
|
||||
$('#graph-container').width($('.centralContent').width());
|
||||
$('#graph-container').height($('.centralRow').height() - 150);
|
||||
|
||||
this.resize();
|
||||
this.fetchGraph();
|
||||
},
|
||||
|
||||
fetchGraph: function () {
|
||||
var self = this;
|
||||
$('#content').append(
|
||||
'<div id="calculatingGraph" style="position: absolute; left: 25px; top: 130px;">' +
|
||||
'<i class="fa fa-circle-o-notch fa-spin" style="margin-right: 10px;"></i>' +
|
||||
'Calculating layout. Please wait ... </div>'
|
||||
);
|
||||
|
||||
// TODO LOAD GRAPH SETTINGS
|
||||
// var settings = this.loadGraphSettings();
|
||||
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
|
@ -50,6 +75,9 @@
|
|||
contentType: 'application/json',
|
||||
success: function (data) {
|
||||
self.renderGraph(data);
|
||||
},
|
||||
error: function () {
|
||||
$('#calculatingGraph').html('Failed to fetch graph information.');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -117,6 +145,12 @@
|
|||
generateMenu(e, nodeId);
|
||||
},
|
||||
|
||||
loadGraphSettings: function () {
|
||||
var settings;
|
||||
|
||||
return settings;
|
||||
},
|
||||
|
||||
editNode: function (id) {
|
||||
var callback = function () {};
|
||||
|
||||
|
@ -132,35 +166,50 @@
|
|||
|
||||
this.Sigma = sigma;
|
||||
|
||||
var algorithm = 'force';
|
||||
var renderer = 'webgl';
|
||||
|
||||
var settings = {
|
||||
doubleClickEnabled: false,
|
||||
minEdgeSize: 0.5,
|
||||
maxEdgeSize: 4,
|
||||
enableEdgeHovering: true,
|
||||
// edgeHoverColor: 'edge',
|
||||
// defaultEdgeHoverColor: '#000',
|
||||
// defaultEdgeType: 'curve',
|
||||
edgeHoverSizeRatio: 1,
|
||||
edgeHoverExtremities: true
|
||||
};
|
||||
|
||||
// adjust display settings for big graphs
|
||||
if (graph.nodes.length > 500) {
|
||||
// show node label if size is 20
|
||||
settings.labelThreshold = 20;
|
||||
}
|
||||
|
||||
// adjust display settings for webgl renderer
|
||||
if (renderer === 'webgl') {
|
||||
settings.enableEdgeHovering = false;
|
||||
}
|
||||
|
||||
// create sigma graph
|
||||
var s = new this.Sigma({
|
||||
graph: graph,
|
||||
container: 'graph-container',
|
||||
renderer: {
|
||||
container: document.getElementById('graph-container'),
|
||||
type: 'canvas'
|
||||
type: renderer
|
||||
},
|
||||
settings: {
|
||||
doubleClickEnabled: false,
|
||||
minEdgeSize: 0.5,
|
||||
maxEdgeSize: 4,
|
||||
enableEdgeHovering: true,
|
||||
// edgeHoverColor: 'edge',
|
||||
// defaultEdgeHoverColor: '#000',
|
||||
// defaultEdgeType: 'curve',
|
||||
edgeHoverSizeRatio: 1,
|
||||
edgeHoverExtremities: true
|
||||
}
|
||||
settings: settings
|
||||
});
|
||||
this.currentGraph = s;
|
||||
|
||||
sigma.plugins.fullScreen({
|
||||
container: 'graph-container',
|
||||
btnId: 'graph-fullscreen-btn'
|
||||
});
|
||||
|
||||
var renderer = 'fruchtermann';
|
||||
|
||||
if (renderer === 'noverlap') {
|
||||
if (algorithm === 'noverlap') {
|
||||
var noverlapListener = s.configNoverlap({
|
||||
nodeMargin: 0.1,
|
||||
scaleNodes: 1.05,
|
||||
|
@ -175,7 +224,7 @@
|
|||
if (e.type === 'interpolate') {
|
||||
}
|
||||
});
|
||||
} else if (renderer === 'fruchtermann') {
|
||||
} else if (algorithm === 'fruchtermann') {
|
||||
var frListener = sigma.layouts.fruchtermanReingold.configure(s, {
|
||||
iterations: 500,
|
||||
easing: 'quadraticInOut',
|
||||
|
@ -192,70 +241,60 @@
|
|||
e.originalColor = e.color;
|
||||
});
|
||||
|
||||
if (document.addEventListener) {
|
||||
document.addEventListener('contextmenu', function (e) {
|
||||
// my custom functionality on right click
|
||||
e.preventDefault();
|
||||
}, false);
|
||||
} else {
|
||||
document.attachEvent('oncontextmenu', function () {
|
||||
// my custom functionality on right click
|
||||
window.event.returnValue = false;
|
||||
if (renderer !== 'webgl') {
|
||||
s.bind('rightClickNode', function (e) {
|
||||
var nodeId = e.data.node.id;
|
||||
self.createNodeContextMenu(nodeId, e);
|
||||
});
|
||||
|
||||
s.bind('doubleClickNode', function (e) {
|
||||
var nodeId = e.data.node.id;
|
||||
var toKeep = s.graph.neighbors(nodeId);
|
||||
toKeep[nodeId] = e.data.node;
|
||||
|
||||
s.graph.nodes().forEach(function (n) {
|
||||
if (toKeep[n.id]) {
|
||||
n.color = n.originalColor;
|
||||
} else {
|
||||
n.color = '#eee';
|
||||
}
|
||||
});
|
||||
|
||||
s.graph.edges().forEach(function (e) {
|
||||
if (toKeep[e.source] && toKeep[e.target]) {
|
||||
e.color = 'rgb(64, 74, 83)';
|
||||
} else {
|
||||
e.color = '#eee';
|
||||
}
|
||||
});
|
||||
|
||||
s.refresh();
|
||||
});
|
||||
|
||||
s.bind('doubleClickStage', function () {
|
||||
s.graph.nodes().forEach(function (n) {
|
||||
n.color = n.originalColor;
|
||||
});
|
||||
|
||||
s.graph.edges().forEach(function (e) {
|
||||
e.color = e.originalColor;
|
||||
});
|
||||
|
||||
s.refresh();
|
||||
});
|
||||
|
||||
s.bind('clickStage', function () {
|
||||
self.clearOldContextMenu();
|
||||
});
|
||||
}
|
||||
|
||||
s.bind('rightClickNode', function (e) {
|
||||
var nodeId = e.data.node.id;
|
||||
self.createNodeContextMenu(nodeId, e);
|
||||
});
|
||||
|
||||
s.bind('doubleClickNode', function (e) {
|
||||
var nodeId = e.data.node.id;
|
||||
var toKeep = s.graph.neighbors(nodeId);
|
||||
toKeep[nodeId] = e.data.node;
|
||||
|
||||
s.graph.nodes().forEach(function (n) {
|
||||
if (toKeep[n.id]) {
|
||||
n.color = n.originalColor;
|
||||
} else {
|
||||
n.color = '#eee';
|
||||
}
|
||||
});
|
||||
|
||||
s.graph.edges().forEach(function (e) {
|
||||
if (toKeep[e.source] && toKeep[e.target]) {
|
||||
e.color = 'rgb(64, 74, 83)';
|
||||
} else {
|
||||
e.color = '#eee';
|
||||
}
|
||||
});
|
||||
|
||||
s.refresh();
|
||||
});
|
||||
|
||||
s.bind('doubleClickStage', function () {
|
||||
s.graph.nodes().forEach(function (n) {
|
||||
n.color = n.originalColor;
|
||||
});
|
||||
|
||||
s.graph.edges().forEach(function (e) {
|
||||
e.color = e.originalColor;
|
||||
});
|
||||
|
||||
s.refresh();
|
||||
});
|
||||
|
||||
s.bind('clickStage', function () {
|
||||
self.clearOldContextMenu();
|
||||
});
|
||||
|
||||
var dragListener;
|
||||
// Initialize the dragNodes plugin:
|
||||
if (renderer === 'noverlap') {
|
||||
if (algorithm === 'noverlap') {
|
||||
s.startNoverlap();
|
||||
// allow draggin nodes
|
||||
dragListener = sigma.plugins.dragNodes(s, s.renderers[0]);
|
||||
} else if (renderer === 'force') {
|
||||
} else if (algorithm === 'force') {
|
||||
s.startForceAtlas2({worker: true, barnesHutOptimize: false});
|
||||
|
||||
window.setTimeout(function () {
|
||||
|
@ -263,7 +302,7 @@
|
|||
dragListener = sigma.plugins.dragNodes(s, s.renderers[0]);
|
||||
console.log('stopped force');
|
||||
}, 3000);
|
||||
} else if (renderer === 'fruchtermann') {
|
||||
} else if (algorithm === 'fruchtermann') {
|
||||
// Start the Fruchterman-Reingold algorithm:
|
||||
sigma.layouts.fruchtermanReingold.start(s);
|
||||
dragListener = sigma.plugins.dragNodes(s, s.renderers[0]);
|
||||
|
@ -271,6 +310,8 @@
|
|||
dragListener = sigma.plugins.dragNodes(s, s.renderers[0]);
|
||||
}
|
||||
console.log(dragListener);
|
||||
|
||||
$('#calculatingGraph').remove();
|
||||
}
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue