MISP/app/webroot/js/correlation-graph.js

541 lines
13 KiB
JavaScript

var pinNodes = true;
function togglePhysics() {
d3.selectAll(".node").each(
function(d) {
d.fixed = pinNodes;
}
);
pinNodes = !pinNodes;
}
$(document).ready( function() {
var currentMousePos = { x: -1, y: -1 };
$(document).mousemove(function(event) {
currentMousePos .x = event.pageX;
currentMousePos.y = event.pageY;
});
var margin = {top: -5, right: -5, bottom: -5, left: -5},
width = $(window).width() - margin.left - margin.right,
height = $(window).height() - 115 - margin.top - margin.bottom;
var menu_x_buffer_ = width - 150;
var menu_y_buffer = height - 100;
if ($('#graph_init').data('ajax')) {
$('.menu-container').css('left', '20px');
$('#hover-menu-container').css('top', '20px');
$('#selected-menu-container').css('top', '270px');
} else {
$('.menu-container').css('left', '200px');
$('#hover-menu-container').css('top', '50px');
$('#selected-menu-container').css('top', '400px');
}
$('#hover-menu-container').css('z-index', 0);
$('#selected-menu-container').css('z-index', 1);
$('#fullscreen-btn-correlation').click(function() {
var network_div = $('#correlationgraph_div');
var fullscreen_enabled = !network_div.data('fullscreen');
network_div.data('fullscreen', fullscreen_enabled);
var height_val = fullscreen_enabled == true ? "calc(100vh - 42px - 42px - 10px)" : "500px";
network_div.css("height", height_val);
network_div[0].scrollIntoView({
behavior: "smooth",
});
});
var root;
var highlighted;
var hovered;
var icon_sizes = {
"event": 24,
"object": 12,
"attribute": 12,
"galaxy": 32,
"tag": 24
}
var selection_radius_sizes = {
"event": 18,
"object": 12,
"attribute": 12,
"galaxy": 18,
"tag": 18
}
var force = d3.layout.force()
.linkDistance(function (d) {
return d.linkDistance;
})
.linkStrength(0.9)
.friction(0.5)
.theta(0.9)
.charge(-500)
.gravity(0.21)
.size([width, height])
.on("tick", tick);
var vis = d3.select("#chart");
var svg = vis.append("svg:svg")
.attr("width", width)
.attr("height", height)
.attr("pointer-events", "all");
var rect = svg.append("svg:rect")
.attr('width', width)
.attr('height', height)
.attr('fill', 'white')
.call(d3.behavior.zoom().on("zoom", zoomhandler));
var plotting_area = svg.append("g")
.attr("class", "plotting-area");
var drag1 = d3.behavior.drag()
.on("dragstart", dragstart)
.on("drag", dragmove)
.on("dragend", dragend);
var link = plotting_area.selectAll(".link");
var node = plotting_area.selectAll(".node");
var scope_id = $('#graph_init').data('id');
var scope = $('#graph_init').data('scope');
d3.json(baseurl + "/events/updateGraph/" + scope_id + "/" + scope + ".json", function(error, json) {
root = json;
update();
});
var graphElementScale = 1;
var graphElementTranslate = [0, 0];
function zoomhandler() {
plotting_area.attr("transform",
"translate(" + d3.event.translate + ")"
+ " scale(" + d3.event.scale + ")");
graphElementScale = d3.event.scale;
graphElementTranslate = d3.event.translate;
}
function update() {
var nodes = root['nodes'], links = root['links'];
// Restart the force layout.
force.nodes(nodes).links(links).start();
// Update links.
link = link.data(links);
link.exit().remove();
link.enter().insert("line", ".node").attr("class", "link");
// Update nodes.
node = node.data(nodes);
node.exit().remove();
var nodeEnter = node.enter().append("g").attr("class", "node").call(drag1);
nodeEnter.attr('id', function(d) { return 'id-' + d.unique_id; })
nodeEnter.insert("circle", ".circle")
.classed("highlighted_circle", true)
.attr("cx", function(d) { return d.x_axis; })
.attr("cy", function(d) { return d.y_axis; })
.attr("r", function(d) { return selection_radius_sizes[d.type] })
.attr("stroke", "red")
.attr("stroke-opacity", "0")
.attr("fill-opacity", "0")
.attr("fill", "red");
nodeEnter.filter(function(d) {return d.image !== undefined})
.append("svg:image")
.attr("class", "circle")
.attr("xlink:href", function(d) {
return d.image
})
.attr("x", function(d) {
return (0 - (icon_sizes[d.type]/2)) + "px";
})
.attr("y", function(d) {
return (0 - (icon_sizes[d.type]/2)) + "px";
})
.attr("width", function(d) {
return ((icon_sizes[d.type])) + "px";
})
.attr("height", function(d) {
return ((icon_sizes[d.type])) + "px";
});
nodeEnter.filter(function(d) {return d.imgClass !== undefined})
.append("g")
.append('svg:foreignObject')
.attr("width", 12)
.attr("height", 12)
.attr("x", function(d) {
if (d.type == 'galaxy' || d.type == 'tag') {
return '-10px';
} else {
return '-6px';
}
}
)
.attr("y", function(d) {
if (d.type == 'galaxy' || d.type == 'tag') {
return '-12px';
} else {
return '-8px';
}
}
)
.append("xhtml:div")
.html(function (d) {
var result = 'fa-' + d.imgClass;
var namespace = getFontAwesomeNamespace(d.imgClass);
if (d.type == 'galaxy' || d.type == 'tag') result = 'fa-2x ' + result;
return '<i class="' + namespace + ' ' + result + '"></i>';
});
nodeEnter.append("text")
.attr("dy", function(d) {
if (d.type == "event" || d.type == "galaxy") {
return "10px";
} else {
return "0px";
}
})
.attr("fill", function(d) {
if (d.type == "event") {
if (d.expanded == 1) {
return "#0000ff";
} else {
return "#ff0000";
}
}
})
.text(function(d) {
return d.type + ': ' + d.name;
});
node.selectAll("text")
.attr("y", 20);
node.on('mouseover', function(d) {
link.style('stroke', function(l) {
if (d === l.source || d === l.target)
return "#ff0000";
else
return "#9ecae1";
});
link.style('stroke-width', function(l) {
if (d === l.source || d === l.target)
return 2;
else
return 1;
});
showPane(d, 'hover');
});
node.on('mouseout', function() {
link.style('stroke-width', 1);
link.style('stroke', "#9ecae1");
});
node.on("click", function(d) {
showPane(d, 'selected');
});
}
function highlightNode(d) {
d3.selectAll('.highlighted_circle')
.style("stroke-opacity", 0);
d3.select('#id-' + d.unique_id)
.select('.highlighted_circle')
.style("stroke", "red")
.style("stroke-opacity", 0.5);
}
function contextMenu(d, newContext) {
d3.event.preventDefault();
if (d.type == 'event') showPane(d, 'context');
}
function bindExpand(d, type) {
if (!d.expanded) {
var expandName = 'Expand (ctrl+x)';
if (type == 'selected') {
expandName = 'Expand (x)';
}
$("#" + type + "-menu").append('<li id="expand_' + type + '_' + d.id +'" class="graphMenuAction"><span>' + expandName + '</span></li>');
d3.select('#expand_' + type + '_' + d.id)
.on('click', function() {
expand(d);
});
}
}
function remove_node(d) {
var index = root.nodes.indexOf(d);
if (index > -1) {
root.nodes.splice(index, 1);
}
var temp = [];
root['links'].forEach(function(n) {
if (n.source != d && n.target != d) {
temp.push(n);
}
});
root['links'] = temp;
var i = 0;
var links = [];
root.links.forEach(function(l) {
var j = 0;
var temp = {};
root.nodes.forEach(function(n) {
if (l.source == n) {
temp.source = j;
}
if (l.target == n) {
temp.target = j;
}
var j = j+1;
});
temp.linkDistance = l.linkDistance;
links.push(temp);
var i = i+1;
});
root = {
'nodes': root['nodes'],
'links': root['links']
};
update();
}
/*
function bindDelete(d, type) {
var deleteName = 'Delete (ctrl+d)';
if (type == 'selected') {
deleteName = 'Delete (d)';
}
$("#" + type + "-menu").append('<li id="remove_' + type + '_' + d.id +'" class="graphMenuAction"><span>' + deleteName + '</span></li>');
d3.select('#remove_' + type + '_' + d.id)
.on('click', function() {
remove_node(d);
});
}
*/
function createInfoPane(d, data, type) {
var i = 0;
var view_urls = {
'event': baseurl + '/events/view/' + parseInt(d.id),
'tag': baseurl + '/tags/view/' + parseInt(d.id),
'galaxy': baseurl + '/galaxy_clusters/view/' + parseInt(d.id)
};
data["fields"].forEach(function(e) {
var title = e;
if (i == 0) title = d.type;
title = title.split("_").join(" ");
title = title.charAt(0).toUpperCase() + title.slice(1);
var span1 = $('<span />').text(title + ': ');
var span2 = $('<span />').text(d[e]);
var li = $('<li />');
li.append(span1);
li.append(span2);
if (i == 0) li.addClass('graphMenuTitle');
i++;
$("#" + type + "-menu").append(li);
});
$("#" + type + "-menu").append('<li class="graphMenuActions">Actions</li>');
if ($.inArray("navigate", data["actions"]) !== -1) {
$("#" + type + "-menu").append('<li><span><a href="' + view_urls[d.type] + '">Go to ' + d.type + '</a></span></li>');
}
if ($.inArray("expand", data["actions"]) !== -1) {
bindExpand(d, type);
}
if ($.inArray("delete", data["actions"]) !== -1) {
bindDelete(d, type);
}
}
function showPane(d, type) {
if (type == 'hover') {
hovered = d;
} else {
highlighted = d;
highlightNode(d);
}
$('#' + type + '-header').show();
d3.select("#" + type + "-menu").style('display', 'inline-block');
$("#" + type + "-menu").empty();
if (d.type== 'attribute') {
var data = {
"fields": ["id", "name", "category", "type", "comment"],
"actions": ["delete"]
}
}
if (d.type== 'event') {
var tempid = parseInt(d.id);
var data = {
"fields": ["id", "info", "date", "analysis", "org"],
"actions": ["expand", "delete", "navigate"]
}
}
if (d.type == 'tag') {
var data = {
"fields": ["id", "name"],
"actions": ["expand", "delete"]
}
if (d.taxonomy !== undefined) {
data["fields"].push("taxonomy");
data["fields"].push("taxonomy_description");
if (d.description !== "") {
data["fields"].push("Description");
}
}
}
if (d.type == 'galaxy') {
var data = {
"fields": ["id", "name", "galaxy", "synonyms", "authors", "description", "source"],
"actions": ["expand", "delete", "navigate"]
}
}
if (d.type == 'object') {
var data = {
"fields": ["id", "name", "metacategory", "description", "comment"],
"actions": ["delete"]
}
}
createInfoPane(d, data, type);
}
function expand(d) {
if (d.type == 'event' || d.type == 'galaxy' || d.type == 'tag') {
d3.xhr(baseurl + "/events/updateGraph/" + d.id + "/" + d.type + ".json")
.header("Content-Type", "application/json")
.post(
JSON.stringify(root),
function(err, rawData){
root = JSON.parse(rawData.response);
update();
}
);
}
}
function tick() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
}
// Returns a list of all nodes under the root.
function flatten(root) {
var nodes = [], i = 0;
function recurse(node) {
if (node.children) node.children.forEach(recurse);
if (!node.id) node.id = ++i;
nodes.push(node);
}
recurse(root);
return nodes;
}
function dragstart(d, i) {
force.stop();
}
function dragmove(d, i) {
d.px += d3.event.dx;
d.py += d3.event.dy;
d.x += d3.event.dx;
d.y += d3.event.dy;
tick();
}
function dragend(d, i) {
d.fixed = true;
tick();
force.resume();
}
function searchArray(arr, val) {
for (var i=0; i < arr.length; i++)
if (arr[i] === val)
return i;
return false;
}
$("#chart").on('keydown', function(e) {
if (e.which == 69) {
if (highlighted == undefined) {
showPane(root['nodes'][0], 'selected');
} else {
var current = searchArray(root['nodes'], highlighted);
if (current == root['nodes'].length-1) {
showPane(root['nodes'][0], 'selected');
} else {
showPane(root['nodes'][current+1], 'selected');
}
}
}
if (e.which == 81) {
if (highlighted == undefined) {
showPane(root['nodes'][root['nodes'].length-1], 'selected');
} else {
var current = searchArray(root['nodes'], highlighted);
if (current == 0) {
showPane(root['nodes'][root['nodes'].length-1], 'selected');
} else {
showPane(root['nodes'][current-1], 'selected');
}
}
}
});
/*
$(document).on('keydown', function(e) {
if (e.which == 68) {
e.preventDefault();
if (e.ctrlKey) {
if (hovered != undefined) {
remove_node(hovered);
}
} else {
if (highlighted != undefined) {
remove_node(highlighted);
}
}
}
});
*/
$("#chart").on('keydown', function(e) {
if (e.which == 88) {
e.preventDefault();
if (e.ctrlKey) {
if (hovered != undefined) {
expand(hovered);
}
} else {
if (highlighted != undefined) {
expand(highlighted);
}
}
}
});
});