![JAR search and dependency download from the Maven repository](/logo.png)
app.panels.force.module.js Maven / Gradle / Ivy
The newest version!
/** @scratch /panels/5
* include::panels/force.asciidoc[]
*/
/** @scratch /panels/force/0
* == Force diagram
* Status: *Experimental*
*
* This panel creates a force chart between the src_ip and dst_ip fields.
*/
define([
'angular',
'app',
'lodash',
'jquery',
'http://d3js.org/d3.v3.js'
],
function (angular, app, _, $, d3) {
'use strict';
var module = angular.module('kibana.panels.force', []);
app.useModule(module);
console.log('force module loaded');
module.controller('force', function($scope, $rootScope, querySrv, dashboard, filterSrv) {
console.log('force controller loaded');
$scope.panelMeta = {
editorTabs : [
{title:'Queries', src:'app/partials/querySelect.html'}
],
modals : [
{
description: "Inspect",
icon: "icon-info-sign",
partial: "app/partials/inspector.html",
show: $scope.panel.spyable
}
],
status : "Experimental",
description : "Displays a force plot based on a source and a destination field."
};
$scope.dashboard = dashboard;
// Set and populate defaults
var _d = {
/** @scratch /panels/force/3
* spyable:: Setting spyable to false disables the inspect icon.
*/
spyable : true,
/** @scratch /panels/map/3
* size:: Max number of nodes to draw
*/
size : 50,
/** @scratch /panels/force/5
* ==== Queries
* queries object:: This object describes the queries to use on this panel.
* queries.mode::: Of the queries available, which to use. Options: +all, pinned, unpinned, selected+
* queries.ids::: In +selected+ mode, which query ids are selected.
*/
queries : {
mode : 'all',
ids : []
}
};
_.defaults($scope.panel,_d);
$scope.init = function() {
console.log('force scope init');
$scope.$on('refresh', function(){$scope.get_data();});
$scope.get_data();
};
$scope.get_data = function() {
console.log('force scope get_data');
$scope.panelMeta.loading = true;
var request,
boolQuery,
queries;
var ejs = $scope.ejs;
$scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries);
queries = querySrv.getQueryObjs($scope.panel.queries.ids);
boolQuery = $scope.ejs.BoolQuery();
_.each(queries,function(q) {
boolQuery = boolQuery.should(querySrv.toEjsObj(q));
});
request = $scope.ejs.Request();
request = request
.facet($scope.ejs.TermsFacet('src_terms')
.field($scope.panel.src_field)
.size($scope.panel.size)
.facetFilter($scope.ejs.QueryFilter(
$scope.ejs.FilteredQuery(
boolQuery,
filterSrv.getBoolFilter(filterSrv.ids)
)
))
)
.facet($scope.ejs.TermsFacet('dst_terms')
.field($scope.panel.dst_field)
.size($scope.panel.size)
.facetFilter($scope.ejs.QueryFilter(
$scope.ejs.FilteredQuery(
boolQuery,
filterSrv.getBoolFilter(filterSrv.ids)
)
))
)
.size(0);
$scope.populate_modal(request);
$scope.data = {};
$scope.ejs.doSearch(dashboard.indices, request).then(function(results) {
$scope.data.src_terms = [];
_.each(results.facets.src_terms.terms, function(v) {
$scope.data.src_terms.push(v.term);
});
$scope.data.dst_terms = [];
_.each(results.facets.dst_terms.terms, function(v) {
$scope.data.dst_terms.push(v.term);
});
console.log("Src terms", $scope.data.src_terms);
console.log("Dst terms", $scope.data.dst_terms);
// build a new request to compute the connections between the nodes
request = $scope.ejs.Request();
_.each($scope.data.src_terms, function(src) {
_.each($scope.data.dst_terms, function(dst) {
request = request
.facet(ejs.FilterFacet(src + '->' + dst)
.filter(ejs.AndFilter([
ejs.TermFilter($scope.panel.src_field, src),
ejs.TermFilter($scope.panel.dst_field, dst)
]))
.facetFilter($scope.ejs.QueryFilter(
$scope.ejs.FilteredQuery(
boolQuery,
filterSrv.getBoolFilter(filterSrv.ids)
)
))
).size(0);
});
});
$scope.ejs.doSearch(dashboard.indices, request).then(function(results) {
$scope.data.connections = {};
_.each(results.facets, function(v, name) {
$scope.data.connections[name] = v.count;
});
console.log('Connections: ', $scope.data.connections);
$scope.panelMeta.loading = false;
$scope.$emit('render');
});
});
return;
};
$scope.populate_modal = function(request) {
$scope.inspector = request.toJSON();
};
});
module.directive('force', function() {
return {
restrict: 'A',
link: function(scope, elem) {
console.log('link function called');
elem.html('![](img/load_big.gif)
');
// Receive render events
scope.$on('render',function(){
render_panel();
});
// Or if the window is resized
angular.element(window).bind('resize', function(){
render_panel();
});
function render_panel() {
console.log('force render event received');
elem.css({height:scope.panel.height||scope.row.height});
elem.text('');
scope.panelMeta.loading = false;
// compute the nodes and the links
var links = [], nodes = {};
var max_value = 0;
_.each(scope.data.connections, function(v, conn) {
if (v === 0) {
return;
}
var src = conn.substring(0, conn.indexOf('->')),
dst = conn.substring(conn.indexOf('->') + 2, conn.length),
link = {};
link.source = nodes[src] || (nodes[src] = {name: src});
link.target = nodes[dst] || (nodes[dst] = {name: dst});
link.value = v;
if (v > max_value) {
max_value = v;
}
links.push(link);
});
console.log("Links", links);
console.log("Nodes", d3.values(nodes));
// add the curvy lines
function tick() {
path.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" +
d.source.x + "," +
d.source.y + "A" +
dr + "," + dr + " 0 0,1 " +
d.target.x + "," +
d.target.y;
});
node
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
var style = scope.dashboard.current.style;
var width = $(elem[0]).width(),
height = $(elem[0]).height();
var force = d3.layout.force()
.nodes(d3.values(nodes))
.links(links)
.size([width, height])
.linkDistance(150)
.charge(-1200)
.on("tick", tick)
.start();
var svg = d3.select(elem[0]).append("svg")
.attr("width", width)
.attr("height", height);
// build the arrow.
svg.append("svg:defs").selectAll("marker")
.data(["end"]) // Different link/path types can be defined here
.enter().append("svg:marker") // This section adds in the arrows
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.style("fill", "#2980b9")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
// add the links and the arrows
var path = svg.append("svg:g").selectAll("path")
.data(force.links())
.enter().append("svg:path")
.attr("class", "link-path")
//.attr("marker-end", "url(#end)")
.style('fill', 'none')
.style('stroke', '#8c8c8c')
.style('stroke-width', function (link) {
return (0.5 + (link.value * 2) / max_value) + 'px';
});
// define the nodes
var node = svg.selectAll(".node")
.data(force.nodes())
.enter().append("g")
.attr("class", "node")
.call(force.drag);
// add the nodes
node.append("circle")
.attr("r", 25)
.style('fill', '#2980b9')
.on('mouseover', function(d) {
console.log('Node: ', d);
d3.select(this).style('fill', '#7ab6b6');
svg.selectAll('.link-path')
.filter(function(link) {
return link.source === d || link.target === d;
})
.style('stroke', '#7ab6b6');
})
.on('mouseout', function() {
d3.select(this).style('fill', '#2980b9');
svg.selectAll('.link-path')
.style('stroke', '#8c8c8c');
});
// add the text
node.append("text")
.attr("x", 27)
.attr("dy", ".5em")
.style('fill', style === 'light' ? '#222' : '#eee')
.text(function(d) { return d.name; });
}
}
};
});
});
© 2015 - 2025 Weber Informatics LLC | Privacy Policy