You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ea...@apache.org on 2018/12/10 16:59:23 UTC
[1/3] qpid-dispatch git commit: DISPATCH-1217 Treat node.fixed as
bitmap
Repository: qpid-dispatch
Updated Branches:
refs/heads/master e9b7bd5ae -> 81e58b462
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/81e58b46/console/stand-alone/plugin/js/topology/traffic.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/topology/traffic.js b/console/stand-alone/plugin/js/topology/traffic.js
index ff46d63..9a1d2e0 100644
--- a/console/stand-alone/plugin/js/topology/traffic.js
+++ b/console/stand-alone/plugin/js/topology/traffic.js
@@ -19,9 +19,18 @@ under the License.
/* global d3 Promise */
-import { ChordData } from "../chord/data.js";
-import { MIN_CHORD_THRESHOLD } from "../chord/matrix.js";
-import { nextHop } from "./topoUtils.js";
+import {
+ ChordData
+} from "../chord/data.js";
+import {
+ MIN_CHORD_THRESHOLD
+} from "../chord/matrix.js";
+import {
+ nextHop
+} from "./topoUtils.js";
+import {
+ utils
+} from "../amqp/utilities.js";
const transitionDuration = 1000;
const CHORDFILTERKEY = "chordFilter";
@@ -71,9 +80,9 @@ export class Traffic {
this.remove();
this.type = type;
this.vis =
- type === "dots"
- ? new Dots(this, converter, radius)
- : new Congestion(this);
+ type === "dots" ?
+ new Dots(this, converter, radius) :
+ new Congestion(this);
}
// called periodically to refresh the traffic flow
doUpdate() {
@@ -95,7 +104,7 @@ class TrafficAnimation {
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i];
if (node.normals) {
- let normalIndex = node.normals.findIndex(function(normal) {
+ let normalIndex = node.normals.findIndex(function (normal) {
return normal.container === name;
});
if (normalIndex >= 0) return i;
@@ -127,7 +136,7 @@ class Congestion extends TrafficAnimation {
if (attrIndex >= 0) {
for (let i = 0; i < node[entity].results.length; i++) {
if (node[entity].results[i][attrIndex] === value) {
- return this.traffic.QDRService.utilities.flatten(
+ return utils.flatten(
node[entity].attributeNames,
node[entity].results[i]
);
@@ -139,8 +148,13 @@ class Congestion extends TrafficAnimation {
doUpdate() {
let self = this;
this.traffic.QDRService.management.topology.ensureAllEntities(
- [{ entity: "router.link", force: true }, { entity: "connection" }],
- function() {
+ [{
+ entity: "router.link",
+ force: true
+ }, {
+ entity: "connection"
+ }],
+ function () {
let links = {};
let nodeInfo = self.traffic.QDRService.management.topology.nodeInfo();
const nodes = self.traffic.topology.nodes.nodes;
@@ -194,14 +208,18 @@ class Congestion extends TrafficAnimation {
let dir = path.attr("marker-end") === "" ? "start" : "end";
let small = path.attr("class").indexOf("small") > -1;
let id = dir + "-" + congestion.substr(1) + (small ? "-s" : "");
- colors[id] = { dir: dir, color: congestion, small: small };
+ colors[id] = {
+ dir: dir,
+ color: congestion,
+ small: small
+ };
path
.classed("traffic", true)
- .attr("marker-start", function() {
+ .attr("marker-start", function () {
return null;
//return d.left ? 'url(' + self.traffic.prefix + '#' + id + ')' : null;
})
- .attr("marker-end", function() {
+ .attr("marker-end", function () {
return null;
//return d.right ? 'url(' + self.traffic.prefix + '#' + id + ')' : null;
});
@@ -216,31 +234,31 @@ class Congestion extends TrafficAnimation {
let colorKeys = Object.keys(colors);
let custom_markers = self.custom_markers_def
.selectAll("marker")
- .data(colorKeys, function(d) {
+ .data(colorKeys, function (d) {
return d;
});
custom_markers
.enter()
.append("svg:marker")
- .attr("id", function(d) {
+ .attr("id", function (d) {
return d;
})
.attr("viewBox", "0 -5 10 10")
- .attr("refX", function(d) {
+ .attr("refX", function (d) {
return colors[d].dir === "end" ? 24 : colors[d].small ? -24 : -14;
})
.attr("markerWidth", 14)
.attr("markerHeight", 14)
.attr("markerUnits", "userSpaceOnUse")
.attr("orient", "auto")
- .style("fill", function(d) {
+ .style("fill", function (d) {
return colors[d].color;
})
.append("svg:path")
- .attr("d", function(d) {
- return colors[d].dir === "end"
- ? "M 0 -5 L 10 0 L 0 5 z"
- : "M 10 -5 L 0 0 L 10 5 z";
+ .attr("d", function (d) {
+ return colors[d].dir === "end" ?
+ "M 0 -5 L 10 0 L 0 5 z" :
+ "M 10 -5 L 0 0 L 10 5 z";
});
custom_markers.exit().remove();
}
@@ -263,7 +281,7 @@ class Congestion extends TrafficAnimation {
.domain([0, 1, 2, 3])
.interpolate(d3.interpolateHcl)
.range([
- d3.rgb("#000000"),
+ d3.rgb("#999999"),
d3.rgb("#00FF00"),
d3.rgb("#FFA500"),
d3.rgb("#FF0000")
@@ -286,9 +304,9 @@ class Congestion extends TrafficAnimation {
class Dots extends TrafficAnimation {
constructor(traffic, converter, radius) {
super(traffic);
- this.excludedAddresses = localStorage[CHORDFILTERKEY]
- ? JSON.parse(localStorage[CHORDFILTERKEY])
- : [];
+ this.excludedAddresses = localStorage[CHORDFILTERKEY] ?
+ JSON.parse(localStorage[CHORDFILTERKEY]) :
+ [];
this.radius = radius; // the radius of a router circle
this.lastFlows = {}; // the number of dots animated between routers
this.stopped = false;
@@ -296,9 +314,9 @@ class Dots extends TrafficAnimation {
this.chordData.setFilter(this.excludedAddresses);
traffic.$scope.addresses = {};
this.chordData.getMatrix().then(
- function() {
+ function () {
this.traffic.$timeout(
- function() {
+ function () {
this.traffic.$scope.addresses = this.chordData.getAddresses();
for (let address in this.traffic.$scope.addresses) {
this.fillColor(address);
@@ -314,25 +332,25 @@ class Dots extends TrafficAnimation {
}
let self = this;
// event notification that an address checkbox has changed
- traffic.$scope.addressFilterChanged = function() {
- self.updateAddresses().then(function() {
+ traffic.$scope.addressFilterChanged = function () {
+ self.updateAddresses().then(function () {
// don't wait for the next polling cycle. update now
self.traffic.stop();
self.traffic.start();
});
};
// called by angular when mouse enters one of the address legends
- traffic.$scope.enterLegend = function(address) {
+ traffic.$scope.enterLegend = function (address) {
// fade all flows that aren't for this address
self.fadeOtherAddresses(address);
};
// called when the mouse leaves one of the address legends
- traffic.$scope.leaveLegend = function() {
+ traffic.$scope.leaveLegend = function () {
self.unFadeAll();
};
// clicked on the address name. toggle the address checkbox
- traffic.$scope.addressClick = function(address) {
- self.toggleAddress(address).then(function() {
+ traffic.$scope.addressClick = function (address) {
+ self.toggleAddress(address).then(function () {
self.updateAddresses();
});
};
@@ -352,7 +370,7 @@ class Dots extends TrafficAnimation {
}
localStorage[CHORDFILTERKEY] = JSON.stringify(this.excludedAddresses);
if (this.chordData) this.chordData.setFilter(this.excludedAddresses);
- return new Promise(function(resolve) {
+ return new Promise(function (resolve) {
return resolve();
});
}
@@ -360,12 +378,12 @@ class Dots extends TrafficAnimation {
this.traffic.$scope.addresses[address] = !this.traffic.$scope.addresses[
address
];
- return new Promise(function(resolve) {
+ return new Promise(function (resolve) {
return resolve();
});
}
fadeOtherAddresses(address) {
- d3.selectAll("circle.flow").classed("fade", function(d) {
+ d3.selectAll("circle.flow").classed("fade", function (d) {
return d.address !== address;
});
}
@@ -377,10 +395,13 @@ class Dots extends TrafficAnimation {
this.stopped = false;
// we need the nextHop data to show traffic between routers that are connected by intermediaries
this.traffic.QDRService.management.topology.ensureAllEntities(
- [{ entity: "router.node", attrs: ["id", "nextHop"] }],
- function() {
+ [{
+ entity: "router.node",
+ attrs: ["id", "nextHop"]
+ }],
+ function () {
// get the ingressHistogram data for all routers
- self.chordData.getMatrix().then(self.render.bind(self), function(e) {
+ self.chordData.getMatrix().then(self.render.bind(self), function (e) {
console.log("Could not get message histogram" + e);
});
}
@@ -389,7 +410,7 @@ class Dots extends TrafficAnimation {
render(matrix) {
if (this.stopped === false) {
this.traffic.$timeout(
- function() {
+ function () {
this.traffic.$scope.addresses = this.chordData.getAddresses();
}.bind(this)
);
@@ -404,9 +425,9 @@ class Dots extends TrafficAnimation {
.range([1, 1.1]);
// row is ingress router, col is egress router. Value at [row][col] is the rate
matrixMessages.forEach(
- function(row, r) {
+ function (row, r) {
row.forEach(
- function(val, c) {
+ function (val, c) {
if (val > MIN_CHORD_THRESHOLD) {
// translate between matrix row/col and node index
let f = this.nodeIndexFor(
@@ -425,9 +446,9 @@ class Dots extends TrafficAnimation {
this.traffic.topology.nodes.nodes[t],
this.traffic.topology.nodes,
this.traffic.topology.links,
- this.traffic.QDRService,
+ this.traffic.QDRService.management.topology.nodeInfo(),
this.traffic.topology.nodes.nodes[f],
- function(link, fnode, tnode) {
+ function (link, fnode, tnode) {
let key = "-" + link.uid;
let back = fnode.index < tnode.index;
if (!hops[key]) hops[key] = [];
@@ -520,7 +541,10 @@ class Dots extends TrafficAnimation {
let len = Math.max(Math.floor(path.node().getTotalLength() / 50), 1);
let dots = [];
for (let i = 0, offset = this.addressIndex(this, address); i < len; ++i) {
- dots[i] = { i: i + 10 * offset, address: address };
+ dots[i] = {
+ i: i + 10 * offset,
+ address: address
+ };
}
// keep track of the number of dots for each link. If the length of the link is changed,
// re-create the animation
@@ -536,7 +560,7 @@ class Dots extends TrafficAnimation {
let flow = d3
.select("#SVG_ID")
.selectAll("circle.flow" + id)
- .data(dots, function(d) {
+ .data(dots, function (d) {
return d.i + d.address;
});
let circles = flow
@@ -573,10 +597,10 @@ class Dots extends TrafficAnimation {
const ioa = links.attributeNames.indexOf("owningAddr");
const ici = links.attributeNames.indexOf("connectionId");
const ild = links.attributeNames.indexOf("linkDir");
- let foundLinks = links.results.filter(function(l) {
+ let foundLinks = links.results.filter(function (l) {
return (
(l[ilt] === "endpoint" || l[ilt] === "edge-downlink") &&
- address === this.traffic.QDRService.utilities.addr_text(l[ioa]) &&
+ address === utils.addr_text(l[ioa]) &&
l[ild] === cdir
);
}, this);
@@ -585,19 +609,23 @@ class Dots extends TrafficAnimation {
// Now find the created node that each link is associated with
for (let linkIndex = 0; linkIndex < foundLinks.length; linkIndex++) {
// use .some so the loop stops at the 1st match
- nodes.some( function (node) {
+ nodes.some(function (node) {
if (
node.normals &&
- node.normals.some(function(normal) {
+ node.normals.some(function (normal) {
return testNode(normal, key, cdir, foundLinks[linkIndex][ici]);
})
) {
// one of the normals for this node has the traffic
const uuid2 = node.uid();
const key = ["", uuid, uuid2].join("-");
- if (!hops[key])
+ if (!hops[key])
hops[key] = [];
- hops[key].push({ val: val, back: !sender, address: address });
+ hops[key].push({
+ val: val,
+ back: !sender,
+ address: address
+ });
return true;
}
return false;
@@ -612,9 +640,9 @@ class Dots extends TrafficAnimation {
translateDots(radius, path, count, back) {
let pnode = path.node();
// will be called for each element in the flow selection (for each dot)
- return function(d) {
+ return function (d) {
// will be called with t going from 0 to 1 for each dot
- return function(t) {
+ return function (t) {
// start the points at different positions depending on their value (d)
let tt = t * 1000;
let f = ((tt + (d.i * 1000) / count) % 1000) / 1000;
@@ -630,7 +658,7 @@ class Dots extends TrafficAnimation {
// see if this node, or any of the nodes it also connects to
// match the key, dir, and connectionId
-let testNode = function(node, key, dir, connectionId) {
+let testNode = function (node, key, dir, connectionId) {
// does the node match
if (
node.key === key &&
@@ -638,10 +666,10 @@ let testNode = function(node, key, dir, connectionId) {
(node.cdir === dir || node.cdir === "both")
)
return true;
- if (!node.alsoConnectsTo)
+ if (!node.alsoConnectsTo)
return false;
// do any of the alsoConnectsTo nodes match
- return node.alsoConnectsTo.some(function(ac2) {
+ return node.alsoConnectsTo.some(function (ac2) {
return testNode(ac2, key, dir, connectionId);
});
-};
+};
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/81e58b46/console/stand-alone/test/links.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/test/links.js b/console/stand-alone/test/links.js
index ef8ea44..5bc3d01 100644
--- a/console/stand-alone/test/links.js
+++ b/console/stand-alone/test/links.js
@@ -26,15 +26,13 @@ import { Nodes } from '../plugin/js/topology/nodes.js';
class Log {
constructor() {
}
- log (msg) {console.log(msg);}
- debug (msg) {console.log(`Debug: ${msg}`);}
- error (msg) {console.log(`Error: ${msg}`);}
- info (msg) {console.log(`Info: ${msg}`);}
- warn (msg) {console.log(`Warning: ${msg}`);}
+ log(msg) { console.log(msg); }
+ debug(msg) { console.log(`Debug: ${msg}`); }
+ error(msg) { console.log(`Error: ${msg}`); }
+ info(msg) { console.log(`Info: ${msg}`); }
+ warn(msg) { console.log(`Warning: ${msg}`); }
}
var log = new Log();
-var loc = {protocol: function () { return 'http://';}};
-var timeout = {};
var links = new Links(log);
var edgeLinks = new Links(log);
var nodes = new Nodes(log);
@@ -46,61 +44,61 @@ const width = 1024;
const height = 768;
-before(function(done){
+before(function (done) {
let src = '';
- let LAST_PARAM = process.argv[process.argv.length-1];
+ let LAST_PARAM = process.argv[process.argv.length - 1];
- let PARAM_NAME = LAST_PARAM.split('=')[0].replace('--','');
+ let PARAM_NAME = LAST_PARAM.split('=')[0].replace('--', '');
let PARAM_VALUE = LAST_PARAM.split('=')[1];
if (PARAM_NAME == 'src') {
src = PARAM_VALUE;
}
-
- fs.readFile(src + './test/nodes.json', 'utf8', function(err, fileContents) {
+
+ fs.readFile(src + './test/nodes.json', 'utf8', function (err, fileContents) {
if (err) throw err;
nodeInfo = JSON.parse(fileContents);
});
- fs.readFile(src + './test/nodes-edge.json', 'utf8', function(err, fileContents) {
+ fs.readFile(src + './test/nodes-edge.json', 'utf8', function (err, fileContents) {
if (err) throw err;
edgeInfo = JSON.parse(fileContents);
done();
});
});
-describe('Nodes', function() {
- describe('#exists', function() {
- it('should exist', function() {
+describe('Nodes', function () {
+ describe('#exists', function () {
+ it('should exist', function () {
expect(nodes).to.exist;
});
});
- describe('#initializes', function() {
- it('should initialize', function() {
- nodes.initialize(nodeInfo, {}, width, height);
+ describe('#initializes', function () {
+ it('should initialize', function () {
+ nodes.initialize(nodeInfo, {}, width, height, {});
assert.equal(nodes.nodes.length, 6);
});
- it('should initialize edge nodes', function() {
- edgeNodes.initialize(edgeInfo, {}, width, height);
+ it('should initialize edge nodes', function () {
+ edgeNodes.initialize(edgeInfo, {}, width, height, {});
assert.equal(edgeNodes.nodes.length, 2);
});
});
});
-describe('Links', function() {
- describe('#exists', function() {
- it('should exist', function() {
+describe('Links', function () {
+ describe('#exists', function () {
+ it('should exist', function () {
expect(links).to.exist;
});
});
- describe('#initializes', function() {
- it('should initialize', function() {
+ describe('#initializes', function () {
+ it('should initialize', function () {
links.initialize(nodeInfo, nodes, unknowns, {}, width);
assert.equal(links.links.length, 10);
});
- it('should initialize edge links', function() {
+ it('should initialize edge links', function () {
edgeLinks.initialize(edgeInfo, edgeNodes, unknowns, {}, width);
assert.equal(edgeLinks.links.length, 6);
});
- it('should add nodes for edge router groups', function() {
+ it('should add nodes for edge router groups', function () {
assert.equal(edgeNodes.nodes.length, 6);
});
});
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[2/3] qpid-dispatch git commit: DISPATCH-1217 Treat node.fixed as
bitmap
Posted by ea...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/81e58b46/console/stand-alone/plugin/js/topology/qdrTopology.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/topology/qdrTopology.js b/console/stand-alone/plugin/js/topology/qdrTopology.js
index 54168ec..4ada081 100644
--- a/console/stand-alone/plugin/js/topology/qdrTopology.js
+++ b/console/stand-alone/plugin/js/topology/qdrTopology.js
@@ -21,60 +21,95 @@ under the License.
/**
* @module QDR
*/
-import { QDRLogger, QDRRedirectWhenConnected, QDRTemplatePath } from '../qdrGlobals.js';
-import { Traffic } from './traffic.js';
-import { separateAddresses } from '../chord/filters.js';
-import { Nodes } from './nodes.js';
-import { Links } from './links.js';
-import { nextHop, connectionPopupHTML, addStyles } from './topoUtils.js';
-import { BackgroundMap } from './map.js';
+import { QDRLogger, QDRRedirectWhenConnected, QDRTemplatePath } from "../qdrGlobals.js";
+import { Traffic } from "./traffic.js";
+import { separateAddresses } from "../chord/filters.js";
+import { Nodes } from "./nodes.js";
+import { Links } from "./links.js";
+import { nextHop, connectionPopupHTML, getSizes } from "./topoUtils.js";
+import { BackgroundMap } from "./map.js";
+import { utils } from "../amqp/utilities.js";
+import { Legend } from "./legend.js";
+import { appendCircle, appendContent, addGradient, addDefs, updateState } from "./svgUtils.js";
/**
* @module QDR
*/
export class TopologyController {
- constructor(QDRService, $scope, $log, $rootScope, $location, $timeout, $uibModal, $sce) {
- this.controllerName = 'QDR.TopologyController';
-
- let QDRLog = new QDRLogger($log, 'TopologyController');
- const TOPOOPTIONSKEY = 'topoOptions';
+ constructor(
+ QDRService,
+ $scope,
+ $log,
+ $rootScope,
+ $location,
+ $timeout,
+ $uibModal,
+ $sce
+ ) {
+ this.controllerName = "QDR.TopologyController";
+
+ let QDRLog = new QDRLogger($log, "TopologyController");
+ const TOPOOPTIONSKEY = "topoOptions";
// - nodes is an array of router/client info. these are the circles
// - links is an array of connections between the routers. these are the lines with arrows
- let nodes = new Nodes(QDRLog);
- let links = new Links(QDRLog);
- let forceData = {nodes: nodes, links: links};
+ let forceData = {
+ nodes: new Nodes(QDRLog),
+ links: new Links(QDRLog)
+ };
- $scope.legendOptions = angular.fromJson(localStorage[TOPOOPTIONSKEY]) || {showTraffic: false, trafficType: 'dots', mapOpen: false, legendOpen: true};
- if (typeof $scope.legendOptions.mapOpen == 'undefined')
+ // restore the state of the legend sections
+ $scope.legendOptions = angular.fromJson(localStorage[TOPOOPTIONSKEY]) || {
+ showTraffic: false,
+ trafficType: "dots",
+ mapOpen: false,
+ legendOpen: true
+ };
+ if (typeof $scope.legendOptions.mapOpen == "undefined")
$scope.legendOptions.mapOpen = false;
- if (typeof $scope.legendOptions.legendOpen == 'undefined')
+ if (typeof $scope.legendOptions.legendOpen == "undefined")
$scope.legendOptions.legendOpen = false;
- let backgroundMap = new BackgroundMap($scope,
+ let backgroundMap = new BackgroundMap(
+ $scope,
// notify: called each time a pan/zoom is performed
function () {
if ($scope.legend.status.mapOpen) {
// set all the nodes' x,y position based on their saved lon,lat
- nodes.setXY(backgroundMap);
- nodes.savePositions();
+ forceData.nodes.setXY(backgroundMap);
+ forceData.nodes.savePositions();
// redraw the nodes in their x,y position and let non-fixed nodes bungie
force.start();
clearPopups();
}
- });
+ }
+ );
// urlPrefix is used when referring to svg:defs
let urlPrefix = $location.absUrl();
- urlPrefix = urlPrefix.split('#')[0];
+ urlPrefix = urlPrefix.split("#")[0];
if (!$scope.legendOptions.trafficType)
- $scope.legendOptions.trafficType = 'dots';
- $scope.legend = {status: {legendOpen: true, optionsOpen: true, mapOpen: false}};
+ $scope.legendOptions.trafficType = "dots";
+ $scope.legend = {
+ status: {
+ legendOpen: true,
+ optionsOpen: true,
+ mapOpen: false
+ }
+ };
$scope.legend.status.optionsOpen = $scope.legendOptions.showTraffic;
$scope.legend.status.mapOpen = $scope.legendOptions.mapOpen;
- let traffic = new Traffic($scope, $timeout, QDRService, separateAddresses,
- Nodes.radius('inter-router'), forceData, $scope.legendOptions.trafficType, urlPrefix);
+ let traffic = new Traffic(
+ $scope,
+ $timeout,
+ QDRService,
+ separateAddresses,
+ Nodes.radius("inter-router"),
+ forceData,
+ $scope.legendOptions.trafficType,
+ urlPrefix
+ );
// the showTraaffic checkbox was just toggled (or initialized)
- $scope.$watch('legend.status.optionsOpen', function () {
+ $scope.$watch("legend.status.optionsOpen", function () {
$scope.legendOptions.showTraffic = $scope.legend.status.optionsOpen;
localStorage[TOPOOPTIONSKEY] = JSON.stringify($scope.legendOptions);
if ($scope.legend.status.optionsOpen) {
@@ -85,15 +120,21 @@ export class TopologyController {
restart();
}
});
- $scope.$watch('legendOptions.trafficType', function () {
+ // the traffic type was just changed or initialized
+ $scope.$watch("legendOptions.trafficType", function () {
localStorage[TOPOOPTIONSKEY] = JSON.stringify($scope.legendOptions);
if ($scope.legendOptions.showTraffic) {
restart();
- traffic.setAnimationType($scope.legendOptions.trafficType, separateAddresses, Nodes.radius('inter-router'));
+ traffic.setAnimationType(
+ $scope.legendOptions.trafficType,
+ separateAddresses,
+ Nodes.radius("inter-router")
+ );
traffic.start();
}
});
- $scope.$watch('legend.status.mapOpen', function (newvalue, oldvalue) {
+ // the background map was shown or hidden
+ $scope.$watch("legend.status.mapOpen", function (newvalue, oldvalue) {
$scope.legendOptions.mapOpen = $scope.legend.status.mapOpen;
localStorage[TOPOOPTIONSKEY] = JSON.stringify($scope.legendOptions);
// map was shown
@@ -102,16 +143,12 @@ export class TopologyController {
backgroundMap.restartZoom();
// set the main_container div's background color to the ocean color
backgroundMap.updateOceanColor();
- d3.select('g.geo')
- .style('opacity', 1);
+ d3.select("g.geo").style("opacity", 1);
} else {
- if (newvalue !== oldvalue)
- backgroundMap.cancelZoom();
+ if (newvalue !== oldvalue) backgroundMap.cancelZoom();
// hide the map and reset the background color
- d3.select('g.geo')
- .style('opacity', 0);
- d3.select('#main_container')
- .style('background-color', '#FFF');
+ d3.select("g.geo").style("opacity", 0);
+ d3.select("#main_container").style("background-color", "#FFF");
}
});
@@ -121,37 +158,38 @@ export class TopologyController {
mouseup_node = null,
initial_mouse_down_position = null;
- $scope.schema = 'Not connected';
- $scope.current_node = null,
- $scope.mousedown_node = null,
-
+ $scope.schema = "Not connected";
+ $scope.current_node = null;
+ $scope.mousedown_node = null;
$scope.contextNode = null; // node that is associated with the current context menu
- $scope.isRight = function(mode) {
+ $scope.isRight = function (mode) {
return mode.right;
};
+ // show the details dialog for a client or group of clients
function doDialog(d) {
- $uibModal.open({
- backdrop: true,
- keyboard: true,
- backdropClick: true,
- templateUrl: QDRTemplatePath + 'tmplClientDetail.html',
- controller: 'QDR.DetailDialogController',
- resolve: {
- d: function() {
- return d;
+ $uibModal
+ .open({
+ backdrop: true,
+ keyboard: true,
+ backdropClick: true,
+ templateUrl: QDRTemplatePath + "tmplClientDetail.html",
+ controller: "QDR.DetailDialogController",
+ resolve: {
+ d: function () {
+ return d;
+ }
}
- }
- }).result.then(function() {
- });
+ })
+ .result.then(function () { });
}
// called from the html page's popup menu
- $scope.setFixed = function(b) {
+ $scope.setFixed = function (b) {
if ($scope.contextNode) {
- $scope.contextNode.setFixed(b);
- nodes.savePositions();
- nodes.saveLonLat(backgroundMap, $scope.contextNode);
+ forceData.nodes.setFixed($scope.contextNode, b);
+ forceData.nodes.savePositions();
+ forceData.nodes.saveLonLat(backgroundMap, $scope.contextNode);
}
// redraw the circles/links
restart();
@@ -159,20 +197,25 @@ export class TopologyController {
if (!b) {
// let the nodes move to a new position
animate = true;
- force.start();
+ force.start();
}
};
- $scope.isFixed = function() {
- if (!$scope.contextNode)
- return false;
- return ($scope.contextNode.fixed);
+ $scope.isFixed = function () {
+ if (!$scope.contextNode) return false;
+ return $scope.contextNode.fixed;
+ };
+ $scope.addressStyle = function (address) {
+ return {
+ "background-color": $scope.addressColors[address]
+ };
};
let mouseX, mouseY;
var relativeMouse = function () {
- let offset = $('#main_container').offset();
- return {left: (mouseX + $(document).scrollLeft()) - 1,
- top: (mouseY + $(document).scrollTop()) - 1,
+ let offset = $("#main_container").offset();
+ return {
+ left: mouseX + $(document).scrollLeft() - 1,
+ top: mouseY + $(document).scrollTop() - 1,
offset: offset
};
};
@@ -182,66 +225,50 @@ export class TopologyController {
mouseY = e.clientY;
});
$(document).mousemove();
- $(document).click(function() {
+ $(document).click(function () {
$scope.contextNode = null;
- $('.contextMenu').fadeOut(200);
+ $(".contextMenu").fadeOut(200);
});
- let svg, lsvg; // main svg and legend svg
+ let svg; // main svg
let force;
let animate = false; // should the force graph organize itself when it is displayed
- let path, circle;
- let savedKeys = {};
+ let path, circle; // the d3 selections for links and nodes respectively
+ let savedKeys = {}; // so we can redraw the svg if the topology changes
let width = 0;
let height = 0;
- var getSizes = function() {
- const gap = 5;
- let legendWidth = 194;
- let topoWidth = $('#topology').width();
- if (topoWidth < 768)
- legendWidth = 0;
- let width = $('#topology').width() - gap - legendWidth;
- let top = $('#topology').offset().top;
- let height = window.innerHeight - top - gap;
- if (width < 10) {
- QDRLog.info(`page width and height are abnormal w: ${width} h: ${height}`);
- return [0, 0];
- }
- return [width, height];
- };
- var resize = function() {
- if (!svg)
- return;
- let sizes = getSizes();
+ var resize = function () {
+ if (!svg) return;
+ let sizes = getSizes(QDRLog);
width = sizes[0];
height = sizes[1];
if (width > 0) {
// set attrs and 'resume' force
- svg.attr('width', width);
- svg.attr('height', height);
+ svg.attr("width", width);
+ svg.attr("height", height);
force.size(sizes).resume();
}
- $timeout(createLegend);
+ $timeout(updateLegend);
};
// the window is narrow and the page menu icon was clicked.
// Re-create the legend
- $scope.$on('pageMenuClicked', function () {
- $timeout(createLegend);
+ $scope.$on("pageMenuClicked", function () {
+ $timeout(updateLegend);
});
- window.addEventListener('resize', resize);
- let sizes = getSizes();
+ window.addEventListener("resize", resize);
+ let sizes = getSizes(QDRLog);
width = sizes[0];
height = sizes[1];
- if (width <= 0 || height <= 0)
- return;
+ if (width <= 0 || height <= 0) return;
// initialize the nodes and links array from the QDRService.topology._nodeInfo object
- var initForceGraph = function() {
- forceData.nodes = nodes = new Nodes(QDRLog);
- forceData.links = links = new Links(QDRLog);
+ var initForceGraph = function () {
+ if (width < 768) {
+ $scope.legend.status.mapOpen = false;
+ }
let nodeInfo = QDRService.management.topology.nodeInfo();
let nodeCount = Object.keys(nodeInfo).length;
@@ -250,110 +277,78 @@ export class TopologyController {
mouseover_node = null;
selected_node = null;
- d3.select('#SVG_ID').remove();
- svg = d3.select('#topology')
- .append('svg')
- .attr('id', 'SVG_ID')
- .attr('width', width)
- .attr('height', height)
- .on('click', function () {
- clearPopups();
+ if (d3.select("#SVG_ID").empty()) {
+ svg = d3
+ .select("#topology")
+ .append("svg")
+ .attr("id", "SVG_ID")
+ .attr("width", width)
+ .attr("height", height)
+ .on("click", function () {
+ clearPopups();
+ });
+ // read the map data from the data file and build the map layer
+ backgroundMap.init($scope, svg, width, height).then(function () {
+ forceData.nodes.saveLonLat(backgroundMap);
+ backgroundMap.setMapOpacity($scope.legend.status.mapOpen);
});
-
- // the legend
- //d3.select('#topo_svg_legend svg').remove();
- if (d3.select('#svglegend').empty()) {
- lsvg = d3.select('#topo_svg_legend')
- .append('svg')
- .attr('id', 'svglegend');
- lsvg = lsvg.append('svg:g')
- .attr('transform', `translate(${Nodes.maxRadius()}, ${Nodes.maxRadius()})`)
- .selectAll('g');
+ addDefs(svg);
+ addGradient(svg);
+ // handles to link and node element groups
+ path = svg.append("svg:g").attr("class", "links").selectAll("g");
+ circle = svg.append("svg:g").attr("class", "nodes").selectAll("g");
}
-
// mouse event vars
$scope.mousedown_node = null;
mouseup_node = null;
// initialize the list of nodes
- animate = nodes.initialize(nodeInfo, localStorage, width, height);
- nodes.savePositions();
- // read the map data from the data file and build the map layer
- backgroundMap.init($scope, svg, width, height)
- .then( function () {
- nodes.saveLonLat(backgroundMap);
- backgroundMap.setMapOpacity($scope.legend.status.mapOpen);
- });
+ animate = forceData.nodes.initialize(nodeInfo, width, height, localStorage);
+ forceData.nodes.savePositions();
// initialize the list of links
let unknowns = [];
- if (links.initialize(nodeInfo, nodes, unknowns, localStorage, height)) {
+ if (forceData.links.initialize(nodeInfo,
+ forceData.nodes,
+ unknowns,
+ height,
+ localStorage))
animate = true;
- }
$scope.schema = QDRService.management.schema();
// init D3 force layout
- force = d3.layout.force()
- .nodes(nodes.nodes)
- .links(links.links)
+ force = d3.layout
+ .force()
+ .nodes(forceData.nodes.nodes)
+ .links(forceData.links.links)
.size([width, height])
- .linkDistance(function(d) { return nodes.linkDistance(d, nodeCount); })
- .charge(function(d) { return nodes.charge(d, nodeCount); })
- .friction(.10)
- .gravity(function(d) { return nodes.gravity(d, nodeCount); })
- .on('tick', tick)
- .on('end', function () {nodes.savePositions(); nodes.saveLonLat(backgroundMap);})
+ .linkDistance(function (d) {
+ return forceData.nodes.linkDistance(d, nodeCount);
+ })
+ .charge(function (d) {
+ return forceData.nodes.charge(d, nodeCount);
+ })
+ .friction(0.1)
+ .gravity(function (d) {
+ return forceData.nodes.gravity(d, nodeCount);
+ })
+ .on("tick", tick)
+ .on("end", function () {
+ forceData.nodes.savePositions();
+ forceData.nodes.saveLonLat(backgroundMap);
+ })
.start();
- // This section adds in the arrows
- // Generate a marker for each combination of:
- // start|end, ''|selected highlighted, and each possible node radius
- {
- let sten = ['start', 'end'];
- let states = ['', 'selected', 'highlighted', 'unknown'];
- let radii = Nodes.discrete();
- let defs = [];
- for (let isten=0; isten<sten.length; isten++) {
- for (let istate=0; istate<states.length; istate++) {
- for (let iradii=0; iradii<radii.length; iradii++) {
- defs.push({sten: sten[isten], state: states[istate], r: radii[iradii]});
- }
- }
- }
- svg.append('svg:defs').attr('class', 'marker-defs').selectAll('marker')
- .data(defs)
- .enter().append('svg:marker')
- .attr('id', function (d) { return [d.sten, d.state, d.r].join('-'); })
- .attr('viewBox', '0 -5 10 10')
- .attr('refX', function (d) { return Nodes.refX(d.sten, d.r); })
- .attr('markerWidth', 14)
- .attr('markerHeight', 14)
- .attr('markerUnits', 'userSpaceOnUse')
- .attr('orient', 'auto')
- .append('svg:path')
- .attr('d', function (d) {
- return d.sten === 'end' ? 'M 0 -5 L 10 0 L 0 5 z' : 'M 10 -5 L 0 0 L 10 5 z';
- });
- addStyles (sten, {selected: '#33F', highlighted: '#6F6', unknown: '#888'}, radii);
- }
- // gradient for sender/receiver client
- let grad = svg.append('svg:defs').append('linearGradient')
- .attr('id', 'half-circle')
- .attr('x1', '0%')
- .attr('x2', '0%')
- .attr('y1', '100%')
- .attr('y2', '0%');
- grad.append('stop').attr('offset', '50%').style('stop-color', '#C0F0C0');
- grad.append('stop').attr('offset', '50%').style('stop-color', '#F0F000');
-
- // handles to link and node element groups
- path = svg.append('svg:g').attr('class', 'links').selectAll('g'),
- circle = svg.append('svg:g').attr('class', 'nodes').selectAll('g');
-
// app starts here
- //if (unknowns.length === 0)
- restart();
+ if (unknowns.length === 0)
+ restart();
+ // the legend
+ // call updateLegend in timeout because:
+ // If we create the legend right away, then it will be destroyed when the accordian
+ // gets initialized as the page loads.
+ $timeout(updateLegend);
+
if (oldSelectedNode) {
- d3.selectAll('circle.inter-router').classed('selected', function (d) {
+ d3.selectAll("circle.inter-router").classed("selected", function (d) {
if (d.key === oldSelectedNode.key) {
selected_node = d;
return true;
@@ -362,13 +357,19 @@ export class TopologyController {
});
}
if (oldMouseoverNode && selected_node) {
- d3.selectAll('circle.inter-router').each(function (d) {
+ d3.selectAll("circle.inter-router").each(function (d) {
if (d.key === oldMouseoverNode.key) {
mouseover_node = d;
- QDRService.management.topology.ensureAllEntities([{entity: 'router.node', attrs: ['id','nextHop']}], function () {
- nextHopHighlight(selected_node, d);
- restart();
- });
+ QDRService.management.topology.ensureAllEntities(
+ [{
+ entity: "router.node",
+ attrs: ["id", "nextHop"]
+ }],
+ function () {
+ nextHopHighlight(selected_node, d);
+ restart();
+ }
+ );
}
});
}
@@ -384,38 +385,45 @@ export class TopologyController {
setTimeout(continueForce, 100, extra);
}
};
- continueForce(Nodes.forceScale(nodeCount, [0, 200])); // give large graphs time to settle down
+ continueForce(Nodes.forceScale(nodeCount, [0, 200])); // give large graphs time to settle down
};
// To start up quickly, we only get the connection info for each router.
- // That means we don't have the router.link info when links.initialize() is first called.
+ // That means we don't have the router.link info when links.initialize() is first called
+ // and the initial graph is drawn.
// The router.link info is needed to determine which direction the arrows between routers
// and client should point. (Direction between interior routers is determined by connection.dir)
- // So, the first time through links.initialize() we keep track of the nodes for which we
+ // So, the first time through links.initialize() we keep track of the nodes for which we
// need router.link info and fill in that info here.
var resolveUnknowns = function (nodeInfo, unknowns) {
let unknownNodes = {};
// collapse the unknown nodes using an object
- for (let i=0; i<unknowns.length; ++i) {
+ for (let i = 0; i < unknowns.length; ++i) {
unknownNodes[unknowns[i]] = 1;
}
unknownNodes = Object.keys(unknownNodes);
- QDRService.management.topology.ensureEntities(unknownNodes,
- [{entity: 'router.link',
- attrs: ['linkType','connectionId','linkDir','owningAddr'],
- force: true}],
+ QDRService.management.topology.ensureEntities(
+ unknownNodes,
+ [{
+ entity: "router.link",
+ attrs: ["linkType", "connectionId", "linkDir", "owningAddr"],
+ force: true
+ }],
function () {
let nodeInfo = QDRService.management.topology.nodeInfo();
- forceData.nodes = nodes = new Nodes(QDRLog);
- nodes.initialize(nodeInfo, localStorage, width, height);
- forceData.links = links = new Links(QDRLog);
+ forceData.nodes.initialize(nodeInfo, width, height, localStorage);
let edgeUnknowns = [];
- links.initialize(nodeInfo, nodes, edgeUnknowns, localStorage, height);
+ forceData.links.initialize(nodeInfo, forceData.nodes, edgeUnknowns, height, localStorage);
animate = true;
- force.nodes(nodes.nodes).links(links.links).start();
- nodes.saveLonLat(backgroundMap);
+ force
+ .nodes(forceData.nodes.nodes)
+ .links(forceData.links.links)
+ .start();
+ forceData.nodes.saveLonLat(backgroundMap);
restart();
- });
+ updateLegend();
+ }
+ );
};
function resetMouseVars() {
@@ -427,7 +435,7 @@ export class TopologyController {
// update force layout (called automatically each iteration)
function tick() {
// move the circles
- circle.attr('transform', function(d) {
+ circle.attr("transform", function (d) {
// don't let the edges of the circle go beyond the edges of the svg
let r = Nodes.radius(d.nodeType);
d.x = Math.max(Math.min(d.x, width - r), r);
@@ -436,10 +444,9 @@ export class TopologyController {
});
// draw lines from node centers
- path.selectAll('path')
- .attr('d', function(d) {
- return `M${d.source.x},${d.source.y}L${d.target.x},${d.target.y}`;
- });
+ path.selectAll("path").attr("d", function (d) {
+ return `M${d.source.x},${d.source.y}L${d.target.x},${d.target.y}`;
+ });
if (!animate) {
animate = true;
@@ -448,218 +455,259 @@ export class TopologyController {
}
function nextHopHighlight(selected_node, d) {
- nextHop(selected_node, d, nodes, links, QDRService, selected_node, function (hlLink, hnode) {
- hlLink.highlighted = true;
- hnode.highlighted = true;
- });
- let hnode = nodes.nodeFor(d.name);
+ nextHop(
+ selected_node,
+ d,
+ forceData.nodes,
+ forceData.links,
+ QDRService.management.topology.nodeInfo(),
+ selected_node,
+ function (hlLink, hnode) {
+ hlLink.highlighted = true;
+ hnode.highlighted = true;
+ }
+ );
+ let hnode = forceData.nodes.nodeFor(d.name);
hnode.highlighted = true;
}
function clearPopups() {
- d3.select('#crosssection').style('display', 'none');
- $('.hastip').empty();
- d3.select('#multiple_details').style('display', 'none');
- d3.select('#link_details').style('display', 'none');
- d3.select('#node_context_menu').style('display', 'none');
- d3.select('#popover-div').style('display', 'none');
+ d3.select("#crosssection").style("display", "none");
+ $(".hastip").empty();
+ d3.select("#multiple_details").style("display", "none");
+ d3.select("#link_details").style("display", "none");
+ d3.select("#node_context_menu").style("display", "none");
+ d3.select("#popover-div").style("display", "none");
}
function clearAllHighlights() {
- links.clearHighlighted();
- nodes.clearHighlighted();
+ forceData.links.clearHighlighted();
+ forceData.nodes.clearHighlighted();
}
// Takes the forceData.nodes and forceData.links array and creates svg elements
// Also updates any existing svg elements based on the updated values in forceData.nodes
// and forceData.links
function restart() {
- if (!circle)
- return;
+ if (!circle) return;
circle.call(force.drag);
// path is a selection of all g elements under the g.links svg:group
// here we associate the links.links array with the {g.links g} selection
// based on the link.uid
- path = path.data(links.links, function(d) {return d.uid;});
+ path = path.data(forceData.links.links, function (d) {
+ return d.uid;
+ });
// update each existing {g.links g.link} element
- path.select('.link')
- .classed('selected', function(d) {
+ path
+ .select(".link")
+ .classed("selected", function (d) {
return d.selected;
})
- .classed('highlighted', function(d) {
+ .classed("highlighted", function (d) {
return d.highlighted;
})
- .classed('unknown', function (d) {
+ .classed("unknown", function (d) {
return !d.right && !d.left;
});
// reset the markers based on current highlighted/selected
- if (!$scope.legend.status.optionsOpen || $scope.legendOptions.trafficType === 'dots') {
- path.select('.link')
- .attr('marker-end', function(d) {
- return d.right ? `url(${urlPrefix}#end${d.markerId('end')})` : null;
+ if (
+ !$scope.legend.status.optionsOpen ||
+ $scope.legendOptions.trafficType === "dots"
+ ) {
+ path
+ .select(".link")
+ .attr("marker-end", function (d) {
+ return d.right ? `url(${urlPrefix}#end${d.markerId("end")})` : null;
})
- .attr('marker-start', function(d) {
- return (d.left || (!d.left && !d.right)) ? `url(${urlPrefix}#start${d.markerId('start')})` : null;
+ .attr("marker-start", function (d) {
+ return d.left || (!d.left && !d.right) ?
+ `url(${urlPrefix}#start${d.markerId("start")})` :
+ null;
});
}
// add new links. if a link with a new uid is found in the data, add a new path
- let enterpath = path.enter().append('g')
- .on('mouseover', function(d) { // mouse over a path
+ let enterpath = path
+ .enter()
+ .append("g")
+ .on("mouseover", function (d) {
+ // mouse over a path
let event = d3.event;
d.selected = true;
let updateTooltip = function () {
$timeout(function () {
if (d.selected) {
- $scope.trustedpopoverContent = $sce.trustAsHtml(connectionPopupHTML(d, QDRService));
+ $scope.trustedpopoverContent = $sce.trustAsHtml(
+ connectionPopupHTML(
+ d,
+ QDRService.management.topology.nodeInfo()
+ )
+ );
displayTooltip(event);
}
});
};
// update the contents of the popup tooltip each time the data is polled
- QDRService.management.topology.addUpdatedAction('connectionPopupHTML', updateTooltip);
+ QDRService.management.topology.addUpdatedAction(
+ "connectionPopupHTML",
+ updateTooltip
+ );
// request the data and update the tooltip as soon as it arrives
QDRService.management.topology.ensureAllEntities(
- [{ entity: 'router.link', force: true},{entity: 'connection'}], function () {
+ [{
+ entity: "router.link",
+ force: true
+ }, {
+ entity: "connection"
+ }],
+ function () {
updateTooltip();
- });
+ }
+ );
// just show the tooltip with whatever data we have
updateTooltip();
restart();
-
})
- .on('mouseout', function(d) { // mouse out of a path
- QDRService.management.topology.delUpdatedAction('connectionPopupHTML');
- d3.select('#popover-div')
- .style('display', 'none');
+ .on("mouseout", function (d) {
+ // mouse out of a path
+ QDRService.management.topology.delUpdatedAction(
+ "connectionPopupHTML"
+ );
+ d3.select("#popover-div").style("display", "none");
d.selected = false;
connectionPopupHTML();
restart();
})
// left click a path
- .on('click', function () {
+ .on("click", function () {
d3.event.stopPropagation();
clearPopups();
});
- enterpath.append('path')
- .attr('class', 'link')
- .attr('marker-end', function(d) {
- return d.right ? `url(${urlPrefix}#end${d.markerId('end')})` : null;
+ enterpath
+ .append("path")
+ .attr("class", "link")
+ .attr("marker-end", function (d) {
+ return d.right ? `url(${urlPrefix}#end${d.markerId("end")})` : null;
})
- .attr('marker-start', function(d) {
- return (d.left || (!d.left && !d.right)) ? `url(${urlPrefix}#start${d.markerId('start')})` : null;
+ .attr("marker-start", function (d) {
+ return d.left || (!d.left && !d.right) ?
+ `url(${urlPrefix}#start${d.markerId("start")})` :
+ null;
})
- .attr('id', function (d) {
- const si = d.source.uid(QDRService);
- const ti = d.target.uid(QDRService);
- return ['path', si, ti].join('-');
+ .attr("id", function (d) {
+ const si = d.source.uid();
+ const ti = d.target.uid();
+ return ["path", si, ti].join("-");
})
- .classed('unknown', function (d) {
+ .classed("unknown", function (d) {
return !d.right && !d.left;
});
- enterpath.append('path')
- .attr('class', 'hittarget');
+ enterpath.append("path").attr("class", "hittarget");
// remove old links
path.exit().remove();
-
// circle (node) group
- // nodes are known by router id, or for groups, by the router id + 1st connectionId
- circle = circle.data(nodes.nodes, function(d) {
- return d.uid();
- });
+ circle = d3.select("g.nodes").selectAll("g")
+ .data(forceData.nodes.nodes, function (d) {
+ return d.uid();
+ });
// update existing nodes visual states
- circle.selectAll('circle')
- .classed('highlighted', function(d) {
- return d.highlighted;
- })
- .classed('selected', function(d) {
- return (d === selected_node);
- })
- .classed('fixed', function(d) {
- return d.fixed;
- });
- circle
- .classed('multiple', function (d) {
- return (d.normals && d.normals.length > 1);
- });
+ updateState(circle, selected_node);
// add new circle nodes
- let g = circle.enter().append('svg:g')
- .classed('multiple', function(d) {
- return (d.normals && d.normals.length > 1);
- })
- .attr('id', function (d) { return (d.nodeType !== 'normal' ? 'router' : 'client') + '-' + d.index; });
+ let enterCircle = circle
+ .enter()
+ .append("g")
+ .classed("multiple", function (d) {
+ return d.normals && d.normals.length > 1;
+ })
+ .attr("id", function (d) {
+ return (
+ (d.nodeType !== "normal" ? "router" : "client") + "-" + d.index
+ );
+ });
- appendCircle(g)
- .on('mouseover', function(d) { // mouseover a circle
+ appendCircle(enterCircle)
+ .on("mouseover", function (d) {
+ // mouseover a circle
$scope.current_node = d;
- QDRService.management.topology.delUpdatedAction('connectionPopupHTML');
+ QDRService.management.topology.delUpdatedAction(
+ "connectionPopupHTML"
+ );
let e = d3.event;
- d.toolTip(QDRService.management.topology)
- .then( function (toolTip) {
- showToolTip(toolTip, e);
- });
- if (d === $scope.mousedown_node)
- return;
+ d.toolTip(QDRService.management.topology).then(function (toolTip) {
+ showToolTip(toolTip, e);
+ });
+ if (d === $scope.mousedown_node) return;
// enlarge target node
- d3.select(this).attr('transform', 'scale(1.1)');
+ d3.select(this).attr("transform", "scale(1.1)");
if (!selected_node) {
return;
}
// highlight the next-hop route from the selected node to this node
clearAllHighlights();
// we need .router.node info to highlight hops
- QDRService.management.topology.ensureAllEntities([{entity: 'router.node', attrs: ['id','nextHop']}], function () {
- mouseover_node = d; // save this node in case the topology changes so we can restore the highlights
- nextHopHighlight(selected_node, d);
- restart();
- });
+ QDRService.management.topology.ensureAllEntities(
+ [{
+ entity: "router.node",
+ attrs: ["id", "nextHop"]
+ }],
+ function () {
+ mouseover_node = d; // save this node in case the topology changes so we can restore the highlights
+ nextHopHighlight(selected_node, d);
+ restart();
+ }
+ );
})
- .on('mouseout', function() { // mouse out for a circle
+ .on("mouseout", function () {
+ // mouse out for a circle
$scope.current_node = null;
// unenlarge target node
- d3.select('#popover-div')
- .style('display', 'none');
- d3.select(this).attr('transform', '');
+ d3.select("#popover-div").style("display", "none");
+ d3.select(this).attr("transform", "");
clearAllHighlights();
mouseover_node = null;
restart();
})
- .on('mousedown', function(d) { // mouse down for circle
+ .on("mousedown", function (d) {
+ // mouse down for circle
backgroundMap.cancelZoom();
$scope.current_node = d;
- if (d3.event.button !== 0) { // ignore all but left button
+ if (d3.event.button !== 0) {
+ // ignore all but left button
return;
}
$scope.mousedown_node = d;
// mouse position relative to svg
- initial_mouse_down_position = d3.mouse(this.parentNode.parentNode.parentNode).slice();
+ initial_mouse_down_position = d3
+ .mouse(this.parentNode.parentNode.parentNode)
+ .slice();
})
- .on('mouseup', function(d) { // mouse up for circle
+ .on("mouseup", function (d) {
+ // mouse up for circle
backgroundMap.restartZoom();
- if (!$scope.mousedown_node)
- return;
+ if (!$scope.mousedown_node) return;
// unenlarge target node
- d3.select(this).attr('transform', '');
+ d3.select(this).attr("transform", "");
// check for drag
mouseup_node = d;
- let mySvg = d3.select('#SVG_ID').node();
// if we dragged the node, make it fixed
- let cur_mouse = d3.mouse(mySvg);
- if (cur_mouse[0] != initial_mouse_down_position[0] ||
- cur_mouse[1] != initial_mouse_down_position[1]) {
- d.setFixed(true);
- nodes.savePositions();
- nodes.saveLonLat(backgroundMap);
+ let cur_mouse = d3.mouse(svg.node());
+ if (
+ cur_mouse[0] != initial_mouse_down_position[0] ||
+ cur_mouse[1] != initial_mouse_down_position[1]
+ ) {
+ forceData.nodes.setFixed(d, true);
+ forceData.nodes.savePositions();
+ forceData.nodes.saveLonLat(backgroundMap);
resetMouseVars();
restart();
return;
@@ -669,10 +717,12 @@ export class TopologyController {
if ($scope.mousedown_node === selected_node) {
selected_node = null;
} else {
- if (d.nodeType !== 'normal' &&
- d.nodeType !== 'on-demand' &&
- d.nodeType !== 'edge' &&
- d.nodeTYpe !== '_edge')
+ if (
+ d.nodeType !== "normal" &&
+ d.nodeType !== "on-demand" &&
+ d.nodeType !== "edge" &&
+ d.nodeTYpe !== "_edge"
+ )
selected_node = $scope.mousedown_node;
}
clearAllHighlights();
@@ -684,314 +734,115 @@ export class TopologyController {
}
// apply any data changes to the interface
restart();
-
})
- .on('dblclick', function(d) { // circle
+ .on("dblclick", function (d) {
+ // circle
d3.event.preventDefault();
if (d.fixed) {
- d.setFixed(false);
+ forceData.nodes.setFixed(d, false);
restart(); // redraw the node without a dashed line
force.start(); // let the nodes move to a new position
}
})
- .on('contextmenu', function(d) { // circle
+ .on("contextmenu", function (d) {
+ // circle
$(document).click();
d3.event.preventDefault();
let rm = relativeMouse();
- d3.select('#node_context_menu')
- .style({
- display: 'block',
- left: rm.left + 'px',
- top: (rm.top - rm.offset.top) + 'px'
- });
- $timeout( function () {
+ d3.select("#node_context_menu").style({
+ display: "block",
+ left: rm.left + "px",
+ top: rm.top - rm.offset.top + "px"
+ });
+ $timeout(function () {
$scope.contextNode = d;
});
})
- .on('click', function(d) { // circle
- if (!mouseup_node)
- return;
+ .on("click", function (d) {
+ // circle
+ if (!mouseup_node) return;
// clicked on a circle
clearPopups();
if (!d.normals) {
// circle was a router or a broker
- if (QDRService.utilities.isArtemis(d)) {
- const artemisPath = '/jmx/attributes?tab=artemis&con=Artemis';
- window.location = $location.protocol() + '://localhost:8161/hawtio' + artemisPath;
+ if (utils.isArtemis(d)) {
+ const artemisPath = "/jmx/attributes?tab=artemis&con=Artemis";
+ window.location =
+ $location.protocol() + "://localhost:8161/hawtio" + artemisPath;
}
return;
}
d3.event.stopPropagation();
});
- appendContent(g);
- //appendTitle(g);
+ appendContent(enterCircle);
// remove old nodes
circle.exit().remove();
// add text to client circles if there are any that represent multiple clients
- svg.selectAll('.subtext').remove();
- let multiples = svg.selectAll('.multiple');
- multiples.each(function(d) {
+ svg.selectAll(".subtext").remove();
+ let multiples = svg.selectAll(".multiple");
+ multiples.each(function (d) {
let g = d3.select(this);
let r = Nodes.radius(d.nodeType);
- g.append('svg:text')
- .attr('x', r + 4)
- .attr('y', Math.floor((r / 2) - 4))
- .attr('class', 'subtext')
- .text('* ' + d.normals.length);
+ g.append("svg:text")
+ .attr("x", r + 4)
+ .attr("y", Math.floor(r / 2 - 4))
+ .attr("class", "subtext")
+ .text("* " + d.normals.length);
});
- // call createLegend in timeout because:
- // If we create the legend right away, then it will be destroyed when the accordian
- // gets initialized as the page loads.
- $timeout(createLegend);
if (!$scope.mousedown_node || !selected_node)
return;
// set the graph in motion
- //QDRLog.debug("mousedown_node is " + mousedown_node);
force.start();
-
}
- let createLegend = function () {
- // dynamically create the legend based on which node types are present
- d3.select('#topo_svg_legend svg').remove();
- lsvg = d3.select('#topo_svg_legend')
- .append('svg')
- .attr('id', 'svglegend');
- lsvg = lsvg.append('svg:g')
- .attr('transform', `translate(${Nodes.maxRadius()}, ${Nodes.maxRadius()})`)
- .selectAll('g');
- let legendNodes = new Nodes(QDRLog);
- legendNodes.addUsing('Router', '', 'inter-router', undefined, 0, 0, 0, 0, false, {});
- if (!svg.selectAll('circle.edge').empty() || !svg.selectAll('circle._edge').empty()) {
- legendNodes.addUsing('Router', 'Edge', 'edge', undefined, 0, 0, 1, 0, false, {});
- }
- if (!svg.selectAll('circle.console').empty()) {
- legendNodes.addUsing('Console', 'Console', 'normal', undefined, 0, 0, 2, 0, false, {
- console_identifier: 'Dispatch console'
- });
- }
- if (!svg.selectAll('circle.client.in').empty()) {
- legendNodes.addUsing('Sender', 'Sender', 'normal', undefined, 0, 0, 3, 0, false, {}).cdir = 'in';
- }
- if (!svg.selectAll('circle.client.out').empty()) {
- legendNodes.addUsing('Receiver', 'Receiver', 'normal', undefined, 0, 0, 4, 0, false, {}).cdir = 'out';
- }
- if (!svg.selectAll('circle.client.inout').empty()) {
- legendNodes.addUsing('Sender/Receiver', 'Sender/Receiver', 'normal', undefined, 0, 0, 5, 0, false, {}).cdir = 'both';
- }
- if (!svg.selectAll('circle.qpid-cpp').empty()) {
- legendNodes.addUsing('Qpid broker', 'Qpid broker', 'route-container', undefined, 0, 0, 6, 0, false, {
- product: 'qpid-cpp'
- });
- }
- if (!svg.selectAll('circle.artemis').empty()) {
- legendNodes.addUsing('Artemis broker', 'Artemis broker', 'route-container', undefined, 0, 0, 7, 0, false,
- {product: 'apache-activemq-artemis'});
- }
- if (!svg.selectAll('circle.route-container').empty()) {
- legendNodes.addUsing('Service', 'Service', 'route-container', undefined, 0, 0, 8, 0, false,
- {product: ' External Service'});
- }
- lsvg = lsvg.data(legendNodes.nodes, function(d) {
- return d.key + d.name;
- });
- let cury = 0;
- let lg = lsvg.enter().append('svg:g')
- .attr('transform', function(d) {
- let t = `translate(0, ${cury})`;
- cury += (Nodes.radius(d.nodeType) * 2 + 10);
- return t;
- });
- appendCircle(lg);
- appendContent(lg);
- appendTitle(lg);
- lg.append('svg:text')
- .attr('x', 35)
- .attr('y', 6)
- .attr('class', 'label')
- .text(function(d) {
- return d.key;
- });
- lsvg.exit().remove();
- let svgEl = document.getElementById('svglegend');
- if (svgEl) {
- let bb;
- // firefox can throw an exception on getBBox on an svg element
- try {
- bb = svgEl.getBBox();
- } catch (e) {
- bb = {
- y: 0,
- height: 200,
- x: 0,
- width: 200
- };
- }
- svgEl.style.height = (bb.y + bb.height) + 'px';
- svgEl.style.width = (bb.x + bb.width) + 'px';
- }
- };
- let appendCircle = function(g) {
- // add new circles and set their attr/class/behavior
- return g.append('svg:circle')
- .attr('class', 'node')
- .attr('r', function(d) {
- return Nodes.radius(d.nodeType);
- })
- .attr('fill', function (d) {
- if (d.cdir === 'both' && !QDRService.utilities.isConsole(d)) {
- return 'url(' + urlPrefix + '#half-circle)';
- }
- return null;
- })
- .classed('fixed', function(d) {
- return d.fixed;
- })
- .classed('normal', function(d) {
- return d.nodeType == 'normal' || QDRService.utilities.isConsole(d);
- })
- .classed('in', function(d) {
- return d.cdir == 'in';
- })
- .classed('out', function(d) {
- return d.cdir == 'out';
- })
- .classed('inout', function(d) {
- return d.cdir == 'both';
- })
- .classed('inter-router', function(d) {
- return d.nodeType == 'inter-router' || d.nodeType === '_topo';
- })
- .classed('on-demand', function(d) {
- return d.nodeType == 'on-demand';
- })
- .classed('edge', function(d) {
- return d.nodeType === 'edge' || d.nodeType === '_edge';
- })
- .classed('console', function(d) {
- return QDRService.utilities.isConsole(d);
- })
- .classed('artemis', function(d) {
- return QDRService.utilities.isArtemis(d);
- })
- .classed('qpid-cpp', function(d) {
- return QDRService.utilities.isQpid(d);
- })
- .classed('route-container', function (d) {
- return (!QDRService.utilities.isArtemis(d) && !QDRService.utilities.isQpid(d) && d.nodeType === 'route-container');
- })
- .classed('client', function(d) {
- return d.nodeType === 'normal' && !d.properties.console_identifier;
- });
- };
- let appendContent = function(g) {
- // show node IDs
- g.append('svg:text')
- .attr('x', 0)
- .attr('y', function(d) {
- let y = 7;
- if (QDRService.utilities.isArtemis(d))
- y = 8;
- else if (QDRService.utilities.isQpid(d))
- y = 9;
- else if (d.nodeType === 'inter-router')
- y = 4;
- else if (d.nodeType === 'route-container')
- y = 5;
- else if (d.nodeType === 'edge' || d.nodeType === '_edge')
- y = 4;
- return y;
- })
- .attr('class', 'id')
- .classed('console', function(d) {
- return QDRService.utilities.isConsole(d);
- })
- .classed('normal', function(d) {
- return d.nodeType === 'normal';
- })
- .classed('on-demand', function(d) {
- return d.nodeType === 'on-demand';
- })
- .classed('edge', function(d) {
- return d.nodeType === 'edge';
- })
- .classed('edge', function(d) {
- return d.nodeType === '_edge';
- })
- .classed('artemis', function(d) {
- return QDRService.utilities.isArtemis(d);
- })
- .classed('qpid-cpp', function(d) {
- return QDRService.utilities.isQpid(d);
- })
- .text(function(d) {
- if (QDRService.utilities.isConsole(d)) {
- return '\uf108'; // icon-desktop for this console
- } else if (QDRService.utilities.isArtemis(d)) {
- return '\ue900';
- } else if (QDRService.utilities.isQpid(d)) {
- return '\ue901';
- } else if (d.nodeType === 'route-container') {
- return d.properties.product ? d.properties.product[0].toUpperCase() : 'S';
- } else if (d.nodeType === 'normal') {
- return '\uf109'; // icon-laptop for clients
- } else if (d.nodeType === 'edge' || d.nodeType === '_edge') {
- return 'Edge';
- }
- return d.name.length > 7 ?
- d.name.substr(0, 3) + '...' + d.name.substr(d.name.length-3, 3) :
- d.name;
- });
- };
- let appendTitle = function(g) {
- g.append('svg:title').text(function(d) {
- return d.title();
- });
- };
+ function updateLegend() {
+ // dynamically create/update the legend based on which node types are present
+ let lsvg = new Legend(svg);
+ lsvg.update(svg, QDRLog, urlPrefix);
+ }
- let showToolTip = function (title, event) {
+ function showToolTip(title, event) {
// show the tooltip
- $timeout ( function () {
+ $timeout(function () {
$scope.trustedpopoverContent = $sce.trustAsHtml(title);
displayTooltip(event);
});
- };
+ }
- let displayTooltip = function (event) {
- $timeout( function () {
- let top = $('#topology').offset().top - 5;
- let width = $('#topology').width();
- d3.select('#popover-div')
- .style('visibility', 'hidden')
- .style('display', 'block')
- .style('left', (event.pageX+5)+'px')
- .style('top', (event.pageY-top)+'px');
- let pwidth = $('#popover-div').width();
- d3.select('#popover-div')
- .style('visibility', 'visible')
- .style('left',(Math.min(width-pwidth, event.pageX+5) + 'px'))
- .on('mouseout', function () {
- d3.select(this)
- .style('display', 'none');
+ function displayTooltip(event) {
+ $timeout(function () {
+ let top = $("#topology").offset().top - 5;
+ let width = $("#topology").width();
+ d3.select("#popover-div")
+ .style("visibility", "hidden")
+ .style("display", "block")
+ .style("left", event.pageX + 5 + "px")
+ .style("top", event.pageY - top + "px");
+ let pwidth = $("#popover-div").width();
+ d3.select("#popover-div")
+ .style("visibility", "visible")
+ .style("left", Math.min(width - pwidth, event.pageX + 5) + "px")
+ .on("mouseout", function () {
+ d3.select(this).style("display", "none");
});
});
- };
+ }
function hasChanged() {
// Don't update the underlying topology diagram if we are adding a new node.
// Once adding is completed, the topology will update automatically if it has changed
let nodeInfo = QDRService.management.topology.nodeInfo();
// don't count the nodes without connection info
- let cnodes = Object.keys(nodeInfo).filter ( function (node) {
- return (nodeInfo[node]['connection']);
+ let cnodes = Object.keys(nodeInfo).filter(function (node) {
+ return nodeInfo[node]["connection"];
});
- let routers = nodes.nodes.filter( function (node) {
- return node.nodeType === '_topo';
+ let routers = forceData.nodes.nodes.filter(function (node) {
+ return node.nodeType === "_topo";
});
if (routers.length > cnodes.length) {
return -1;
@@ -1000,14 +851,13 @@ export class TopologyController {
return cnodes.length > Object.keys(savedKeys).length ? 1 : -1;
}
// we may have dropped a node and added a different node in the same update cycle
- for (let i=0; i<cnodes.length; i++) {
+ for (let i = 0; i < cnodes.length; i++) {
let key = cnodes[i];
// if this node isn't in the saved node list
- if (!savedKeys.hasOwnProperty(key))
- return 1;
+ if (!savedKeys.hasOwnProperty(key)) return 1;
// if the number of connections for this node chaanged
- if (nodeInfo[key]['connection'].results.length !== savedKeys[key])
- return nodeInfo[key]['connection'].results.length - savedKeys[key];
+ if (nodeInfo[key]["connection"].results.length !== savedKeys[key])
+ return nodeInfo[key]["connection"].results.length - savedKeys[key];
}
return 0;
}
@@ -1017,52 +867,53 @@ export class TopologyController {
let nodeInfo = QDRService.management.topology.nodeInfo();
// save the number of connections per node
for (let key in nodeInfo) {
- if (nodeInfo[key]['connection'])
- savedKeys[key] = nodeInfo[key]['connection'].results.length;
+ if (nodeInfo[key]["connection"])
+ savedKeys[key] = nodeInfo[key]["connection"].results.length;
}
}
- function destroy () {
- nodes.savePositions();
+
+ function destroy() {
+ forceData.nodes.savePositions();
QDRService.management.topology.setUpdateEntities([]);
QDRService.management.topology.stopUpdating();
- QDRService.management.topology.delUpdatedAction('normalsStats');
- QDRService.management.topology.delUpdatedAction('topology');
- QDRService.management.topology.delUpdatedAction('connectionPopupHTML');
+ QDRService.management.topology.delUpdatedAction("normalsStats");
+ QDRService.management.topology.delUpdatedAction("topology");
+ QDRService.management.topology.delUpdatedAction("connectionPopupHTML");
- d3.select('#SVG_ID').remove();
- window.removeEventListener('resize', resize);
+ d3.select("#SVG_ID").remove();
+ window.removeEventListener("resize", resize);
traffic.stop();
- d3.select('#main_container')
- .style('background-color', 'white');
+ d3.select("#main_container").style("background-color", "white");
}
// When the DOM element is removed from the page,
// AngularJS will trigger the $destroy event on
// the scope
- $scope.$on('$destroy', function() {
+ $scope.$on("$destroy", function () {
destroy();
});
// we are about to leave the page, save the node positions
- $rootScope.$on('$locationChangeStart', function() {
+ $rootScope.$on("$locationChangeStart", function () {
destroy();
});
function handleInitialUpdate() {
// we only need to update connections during steady-state
- QDRService.management.topology.setUpdateEntities(['connection']);
+ QDRService.management.topology.setUpdateEntities(["connection"]);
// we currently have all entities available on all routers
initForceGraph();
saveChanged();
// after the graph is displayed fetch all .router.node info. This is done so highlighting between nodes
// doesn't incur a delay
- QDRService.management.topology.addUpdateEntities([
- {entity: 'router.node', attrs: ['id','nextHop']}
- ]);
+ QDRService.management.topology.addUpdateEntities([{
+ entity: "router.node",
+ attrs: ["id", "nextHop"]
+ }]);
// call this function every time a background update is done
- QDRService.management.topology.addUpdatedAction('topology', function() {
+ QDRService.management.topology.addUpdatedAction("topology", function () {
let changed = hasChanged();
// there is a new node, we need to get all of it's entities before drawing the graph
if (changed > 0) {
- QDRService.management.topology.delUpdatedAction('topology');
+ QDRService.management.topology.delUpdatedAction("topology");
animate = true;
setupInitialUpdate();
} else if (changed === -1) {
@@ -1073,27 +924,36 @@ export class TopologyController {
} else {
//QDRLog.debug("topology didn't change")
}
-
});
}
+
function setupInitialUpdate() {
// make sure all router nodes have .connection info. if not then fetch any missing info
QDRService.management.topology.ensureAllEntities(
- [{entity: 'connection'}],
- handleInitialUpdate);
+ [{
+ entity: "connection"
+ }],
+ handleInitialUpdate
+ );
}
if (!QDRService.management.connection.is_connected()) {
// we are not connected. we probably got here from a bookmark or manual page reload
- QDRRedirectWhenConnected($location, 'topology');
+ QDRRedirectWhenConnected($location, "topology");
return;
}
-
-
animate = true;
setupInitialUpdate();
QDRService.management.topology.startUpdating(true);
-
}
}
-TopologyController.$inject = ['QDRService', '$scope', '$log', '$rootScope', '$location', '$timeout', '$uibModal', '$sce'];
+TopologyController.$inject = [
+ "QDRService",
+ "$scope",
+ "$log",
+ "$rootScope",
+ "$location",
+ "$timeout",
+ "$uibModal",
+ "$sce"
+];
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/81e58b46/console/stand-alone/plugin/js/topology/svgUtils.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/topology/svgUtils.js b/console/stand-alone/plugin/js/topology/svgUtils.js
new file mode 100644
index 0000000..999bcf5
--- /dev/null
+++ b/console/stand-alone/plugin/js/topology/svgUtils.js
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2018 Red Hat Inc.
+ *
+ * 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.
+ */
+
+import { Nodes } from "./nodes.js";
+import { utils } from "../amqp/utilities.js";
+
+export function updateState(circle, selected_node) {
+ circle
+ .selectAll("circle")
+ .classed("highlighted", function (d) {
+ return d.highlighted;
+ })
+ .classed("selected", function (d) {
+ return d === selected_node;
+ })
+ .classed("fixed", function (d) {
+ return d.fixed ? d.fixed & 1 : false;
+ })
+ .classed("multiple", function (d) {
+ return d.normals && d.normals.length > 1;
+ });
+}
+
+export function appendCircle(g, urlPrefix) {
+ // add new circles and set their attr/class/behavior
+ return g
+ .append("svg:circle")
+ .attr("class", "node")
+ .attr("r", function (d) {
+ return Nodes.radius(d.nodeType);
+ })
+ .attr("fill", function (d) {
+ if (d.cdir === "both" && !utils.isConsole(d)) {
+ return "url(" + urlPrefix + "#half-circle)";
+ }
+ return null;
+ })
+ .classed("fixed", function (d) {
+ return d.fixed ? d.fixed & 1 : false;
+ })
+ .classed("normal", function (d) {
+ return d.nodeType == "normal" || utils.isConsole(d);
+ })
+ .classed("in", function (d) {
+ return d.cdir == "in";
+ })
+ .classed("out", function (d) {
+ return d.cdir == "out";
+ })
+ .classed("inout", function (d) {
+ return d.cdir == "both";
+ })
+ .classed("inter-router", function (d) {
+ return d.nodeType == "inter-router" || d.nodeType === "_topo";
+ })
+ .classed("on-demand", function (d) {
+ return d.nodeType == "on-demand";
+ })
+ .classed("edge", function (d) {
+ return d.nodeType === "edge" || d.nodeType === "_edge";
+ })
+ .classed("console", function (d) {
+ return utils.isConsole(d);
+ })
+ .classed("artemis", function (d) {
+ return utils.isArtemis(d);
+ })
+ .classed("qpid-cpp", function (d) {
+ return utils.isQpid(d);
+ })
+ .classed("route-container", function (d) {
+ return (
+ !utils.isArtemis(d) &&
+ !utils.isQpid(d) &&
+ d.nodeType === "route-container"
+ );
+ })
+ .classed("client", function (d) {
+ return d.nodeType === "normal" && !d.properties.console_identifier;
+ });
+}
+
+export function appendContent(g) {
+ // show node IDs
+ g.append("svg:text")
+ .attr("x", 0)
+ .attr("y", function (d) {
+ let y = 7;
+ if (utils.isArtemis(d)) y = 8;
+ else if (utils.isQpid(d)) y = 9;
+ else if (d.nodeType === "inter-router") y = 4;
+ else if (d.nodeType === "route-container") y = 5;
+ else if (d.nodeType === "edge" || d.nodeType === "_edge") y = 4;
+ return y;
+ })
+ .attr("class", "id")
+ .classed("console", function (d) {
+ return utils.isConsole(d);
+ })
+ .classed("normal", function (d) {
+ return d.nodeType === "normal";
+ })
+ .classed("on-demand", function (d) {
+ return d.nodeType === "on-demand";
+ })
+ .classed("edge", function (d) {
+ return d.nodeType === "edge";
+ })
+ .classed("edge", function (d) {
+ return d.nodeType === "_edge";
+ })
+ .classed("artemis", function (d) {
+ return utils.isArtemis(d);
+ })
+ .classed("qpid-cpp", function (d) {
+ return utils.isQpid(d);
+ })
+ .text(function (d) {
+ if (utils.isConsole(d)) {
+ return "\uf108"; // icon-desktop for a console
+ } else if (utils.isArtemis(d)) {
+ return "\ue900"; // custom font character
+ } else if (utils.isQpid(d)) {
+ return "\ue901"; // custom font character
+ } else if (d.nodeType === "route-container") {
+ return d.properties.product ?
+ d.properties.product[0].toUpperCase() :
+ "S";
+ } else if (d.nodeType === "normal") {
+ return "\uf109"; // icon-laptop for clients
+ } else if (d.nodeType === "edge" || d.nodeType === "_edge") {
+ return "Edge";
+ }
+ return d.name.length > 7 ?
+ d.name.substr(0, 3) + "..." + d.name.substr(d.name.length - 3, 3) :
+ d.name;
+ });
+}
+
+export function appendTitle(g) {
+ g.append("svg:title").text(function (d) {
+ return d.title();
+ });
+}
+
+// Generate a marker for each combination of:
+// start|end, ''|selected highlighted, and each possible node radius
+export function addDefs(svg) {
+ let sten = ["start", "end"];
+ let states = ["", "selected", "highlighted", "unknown"];
+ let radii = Nodes.discrete();
+ let defs = [];
+ for (let isten = 0; isten < sten.length; isten++) {
+ for (let istate = 0; istate < states.length; istate++) {
+ for (let iradii = 0; iradii < radii.length; iradii++) {
+ defs.push({
+ sten: sten[isten],
+ state: states[istate],
+ r: radii[iradii]
+ });
+ }
+ }
+ }
+ svg
+ .append("svg:defs")
+ .attr("class", "marker-defs")
+ .selectAll("marker")
+ .data(defs)
+ .enter()
+ .append("svg:marker")
+ .attr("id", function (d) {
+ return [d.sten, d.state, d.r].join("-");
+ })
+ .attr("viewBox", "0 -5 10 10")
+ .attr("refX", function (d) {
+ return Nodes.refX(d.sten, d.r);
+ })
+ .attr("markerWidth", 14)
+ .attr("markerHeight", 14)
+ .attr("markerUnits", "userSpaceOnUse")
+ .attr("orient", "auto")
+ .append("svg:path")
+ .attr("d", function (d) {
+ return d.sten === "end" ?
+ "M 0 -5 L 10 0 L 0 5 z" :
+ "M 10 -5 L 0 0 L 10 5 z";
+ });
+ addStyles(
+ sten, {
+ selected: "#33F",
+ highlighted: "#6F6",
+ unknown: "#888"
+ },
+ radii
+ );
+}
+export function addGradient(svg) {
+ // gradient for sender/receiver client
+ let grad = svg
+ .append("svg:defs")
+ .append("linearGradient")
+ .attr("id", "half-circle")
+ .attr("x1", "0%")
+ .attr("x2", "0%")
+ .attr("y1", "100%")
+ .attr("y2", "0%");
+ grad
+ .append("stop")
+ .attr("offset", "50%")
+ .style("stop-color", "#C0F0C0");
+ grad
+ .append("stop")
+ .attr("offset", "50%")
+ .style("stop-color", "#F0F000");
+}
+
+function addStyles(stend, stateColor, radii) {
+ // the <style>
+ let element = document.querySelector("style");
+ // Reference to the stylesheet
+ let sheet = element.sheet;
+
+ let states = Object.keys(stateColor);
+ // create styles for each combo of 'stend-state-radii'
+ for (let istend = 0; istend < stend.length; istend++) {
+ for (let istate = 0; istate < states.length; istate++) {
+ let selectors = [];
+ for (let iradii = 0; iradii < radii.length; iradii++) {
+ selectors.push(`#${stend[istend]}-${states[istate]}-${radii[iradii]}`);
+ }
+ let color = stateColor[states[istate]];
+ let sels = `${selectors.join(",")} {fill: ${color}; stroke: ${color};}`;
+ sheet.insertRule(sels, 0);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/81e58b46/console/stand-alone/plugin/js/topology/topoUtils.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/topology/topoUtils.js b/console/stand-alone/plugin/js/topology/topoUtils.js
index 71e5e4c..99e2c9a 100644
--- a/console/stand-alone/plugin/js/topology/topoUtils.js
+++ b/console/stand-alone/plugin/js/topology/topoUtils.js
@@ -18,36 +18,44 @@ under the License.
*/
/* global Set */
+import {
+ utils
+} from "../amqp/utilities.js";
// highlight the paths between the selected node and the hovered node
-function findNextHopNode(from, d, QDRService, selected_node, nodes) {
+function findNextHopNode(from, d, nodeInfo, selected_node, nodes) {
// d is the node that the mouse is over
// from is the selected_node ....
- if (!from)
- return null;
+ if (!from) return null;
- if (from == d)
- return selected_node;
+ if (from == d) return selected_node;
- let sInfo = QDRService.management.topology.nodeInfo()[from.key];
+ let sInfo = nodeInfo[from.key];
// find the hovered name in the selected name's .router.node results
- if (!sInfo['router.node'])
- return null;
- let aAr = sInfo['router.node'].attributeNames;
- let vAr = sInfo['router.node'].results;
+ if (!sInfo["router.node"]) return null;
+ let aAr = sInfo["router.node"].attributeNames;
+ let vAr = sInfo["router.node"].results;
for (let hIdx = 0; hIdx < vAr.length; ++hIdx) {
- let addrT = QDRService.utilities.valFor(aAr, vAr[hIdx], 'id');
- if (d.name && (addrT == d.name)) {
- let next = QDRService.utilities.valFor(aAr, vAr[hIdx], 'nextHop');
- return (next == null) ? nodes.nodeFor(addrT) : nodes.nodeFor(next);
+ let addrT = utils.valFor(aAr, vAr[hIdx], "id");
+ if (d.name && addrT == d.name) {
+ let next = utils.valFor(aAr, vAr[hIdx], "nextHop");
+ return next == null ? nodes.nodeFor(addrT) : nodes.nodeFor(next);
}
}
return null;
}
-export function nextHop(thisNode, d, nodes, links, QDRService, selected_node, cb) {
- if ((thisNode) && (thisNode != d)) {
- let target = findNextHopNode(thisNode, d, QDRService, selected_node, nodes);
+export function nextHop(
+ thisNode,
+ d,
+ nodes,
+ links,
+ nodeInfo,
+ selected_node,
+ cb
+) {
+ if (thisNode && thisNode != d) {
+ let target = findNextHopNode(thisNode, d, nodeInfo, selected_node, nodes);
if (target) {
let hnode = nodes.nodeFor(thisNode.name);
let hlLink = links.linkFor(hnode, target);
@@ -55,164 +63,177 @@ export function nextHop(thisNode, d, nodes, links, QDRService, selected_node, cb
if (cb) {
cb(hlLink, hnode, target);
}
- }
- else
- target = null;
+ } else target = null;
}
- nextHop(target, d, nodes, links, QDRService, selected_node, cb);
+ nextHop(target, d, nodes, links, nodeInfo, selected_node, cb);
}
}
let linkRateHistory = {};
-export function connectionPopupHTML (d, QDRService) {
+export function connectionPopupHTML(d, nodeInfo) {
if (!d) {
linkRateHistory = {};
return;
}
- let utils = QDRService.utilities;
// return all of onode's connections that connecto to right
let getConnsArray = function (onode, key, right) {
if (right.normals) {
// if we want connections between a router and a client[s]
let connIds = new Set();
- let connIndex = onode.connection.attributeNames.indexOf('identity');
- for (let n=0; n<right.normals.length; n++) {
+ let connIndex = onode.connection.attributeNames.indexOf("identity");
+ for (let n = 0; n < right.normals.length; n++) {
let normal = right.normals[n];
if (normal.key === key) {
connIds.add(normal.connectionId);
} else if (normal.alsoConnectsTo) {
- normal.alsoConnectsTo.forEach( function (ac2) {
- if (ac2.key === key)
- connIds.add(ac2.connectionId);
+ normal.alsoConnectsTo.forEach(function (ac2) {
+ if (ac2.key === key) connIds.add(ac2.connectionId);
});
}
}
- return onode.connection.results.filter( function (result) {
- return connIds.has(result[connIndex]);
- }).map( function (c) {
- return utils.flatten(onode.connection.attributeNames, c);
- });
- }
- else {
- // we want the connection between two routers
+ return onode.connection.results
+ .filter(function (result) {
+ return connIds.has(result[connIndex]);
+ })
+ .map(function (c) {
+ return utils.flatten(onode.connection.attributeNames, c);
+ });
+ } else {
+ // we want the connection between two routers
let container = utils.nameFromId(right.key);
- let containerIndex = onode.connection.attributeNames.indexOf('container');
- let roleIndex = onode.connection.attributeNames.indexOf('role');
- return onode.connection.results.filter( function (conn) {
- return conn[containerIndex] === container && conn[roleIndex] === 'inter-router';
- }).map( function (c) {
- return utils.flatten(onode.connection.attributeNames, c);
- });
+ let containerIndex = onode.connection.attributeNames.indexOf("container");
+ let roleIndex = onode.connection.attributeNames.indexOf("role");
+ return onode.connection.results
+ .filter(function (conn) {
+ return (
+ conn[containerIndex] === container &&
+ conn[roleIndex] === "inter-router"
+ );
+ })
+ .map(function (c) {
+ return utils.flatten(onode.connection.attributeNames, c);
+ });
}
};
// construct HTML to be used in a popup when the mouse is moved over a link.
// The HTML is sanitized elsewhere before it is displayed
let linksHTML = function (onode, conns) {
const max_links = 10;
- const fields = ['deliveryCount', 'undeliveredCount', 'unsettledCount', 'rejectedCount', 'releasedCount', 'modifiedCount'];
+ const fields = [
+ "deliveryCount",
+ "undeliveredCount",
+ "unsettledCount",
+ "rejectedCount",
+ "releasedCount",
+ "modifiedCount"
+ ];
// local function to determine if a link's connectionId is in any of the connections
let isLinkFor = function (connectionId, conns) {
- for (let c=0; c<conns.length; c++) {
- if (conns[c].identity === connectionId)
- return true;
+ for (let c = 0; c < conns.length; c++) {
+ if (conns[c].identity === connectionId) return true;
}
return false;
};
let fnJoin = function (ar, sepfn) {
- let out = '';
+ let out = "";
out = ar[0];
- for (let i=1; i<ar.length; i++) {
- let sep = sepfn(ar[i], i===ar.length-1);
- out += (sep[0] + sep[1]);
+ for (let i = 1; i < ar.length; i++) {
+ let sep = sepfn(ar[i], i === ar.length - 1);
+ out += sep[0] + sep[1];
}
return out;
};
// if the data for the line is from a client (small circle), we may have multiple connections
// loop through all links for this router and accumulate those belonging to the connection(s)
- let nodeLinks = onode['router.link'];
- if (!nodeLinks)
- return '';
+ let nodeLinks = onode["router.link"];
+ if (!nodeLinks) return "";
let links = [];
let hasAddress = false;
- for (let n=0; n<nodeLinks.results.length; n++) {
+ for (let n = 0; n < nodeLinks.results.length; n++) {
let link = utils.flatten(nodeLinks.attributeNames, nodeLinks.results[n]);
let allZero = true;
- if (link.linkType !== 'router-control') {
+ if (link.linkType !== "router-control") {
if (isLinkFor(link.connectionId, conns)) {
- if (link.owningAddr)
- hasAddress = true;
+ if (link.owningAddr) hasAddress = true;
if (link.name) {
- let rates = utils.rates(link, fields, linkRateHistory, link.name, 1);
+ let rates = utils.rates(
+ link,
+ fields,
+ linkRateHistory,
+ link.name,
+ 1
+ );
// replace the raw value with the rate
- for (let i=0; i<fields.length; i++) {
- if (rates[fields[i]] > 0)
- allZero = false;
+ for (let i = 0; i < fields.length; i++) {
+ if (rates[fields[i]] > 0) allZero = false;
link[fields[i]] = rates[fields[i]];
}
}
- if (!allZero)
- links.push(link);
+ if (!allZero) links.push(link);
}
}
}
// we may need to limit the number of links displayed, so sort descending by the sum of the field values
let sum = function (a) {
let s = 0;
- for (let i=0; i<fields.length; i++) {
+ for (let i = 0; i < fields.length; i++) {
s += a[fields[i]];
}
return s;
};
- links.sort( function (a, b) {
+ links.sort(function (a, b) {
let asum = sum(a);
let bsum = sum(b);
return asum < bsum ? 1 : asum > bsum ? -1 : 0;
});
- let HTMLHeading = '<h5>Rates (per second) for links</h5>';
+ let HTMLHeading = "<h5>Rates (per second) for links</h5>";
let HTML = '<table class="popupTable">';
// copy of fields since we may be prepending an address
let th = fields.slice();
let td = fields;
- th.unshift('dir');
- td.unshift('linkDir');
- th.push('priority');
- td.push('priority');
+ th.unshift("dir");
+ td.unshift("linkDir");
+ th.push("priority");
+ td.push("priority");
// add an address field if any of the links had an owningAddress
if (hasAddress) {
- th.unshift('address');
- td.unshift('owningAddr');
+ th.unshift("address");
+ td.unshift("owningAddr");
}
let rate_th = function (th) {
- let rth = th.map( function (t) {
- if (t.endsWith('Count'))
- t = t.replace('Count', 'Rate');
+ let rth = th.map(function (t) {
+ if (t.endsWith("Count")) t = t.replace("Count", "Rate");
return utils.humanify(t);
});
return rth;
};
- HTML += ('<tr class="header"><td>' + rate_th(th).join('</td><td>') + '</td></tr>');
+ HTML +=
+ '<tr class="header"><td>' + rate_th(th).join("</td><td>") + "</td></tr>";
// add rows to the table for each link
- for (let l=0; l<links.length; l++) {
- if (l>=max_links) {
+ for (let l = 0; l < links.length; l++) {
+ if (l >= max_links) {
HTMLHeading = `<h5>Rates (per second) for top ${max_links} links</h5>`;
break;
}
let link = links[l];
- let vals = td.map( function (f) {
- if (f === 'owningAddr') {
+ let vals = td.map(function (f) {
+ if (f === "owningAddr") {
let identity = utils.identity_clean(link.owningAddr);
return utils.addr_text(identity);
}
return link[f];
});
let joinedVals = fnJoin(vals, function (v1, last) {
- return [`</td><td${(isNaN(+v1) ? '': ' align="right"')}>`, last ? v1 : utils.pretty(v1 || '0', ',.2f')];
+ return [
+ `</td><td${isNaN(+v1) ? "" : ' align="right"'}>`,
+ last ? v1 : utils.pretty(v1 || "0", ",.2f")
+ ];
});
HTML += `<tr><td> ${joinedVals} </td></tr>`;
}
- return links.length > 0 ? `${HTMLHeading}${HTML}</table>` : '';
+ return links.length > 0 ? `${HTMLHeading}${HTML}</table>` : "";
};
let left, right;
@@ -228,43 +249,40 @@ export function connectionPopupHTML (d, QDRService) {
[left, right] = [right, left];
}
// left is a router. right is either a router or a client[s]
- let onode = QDRService.management.topology.nodeInfo()[left.key];
+ let onode = nodeInfo[left.key];
// find all the connections for left that go to right
let conns = getConnsArray(onode, left.key, right);
- let HTML = '';
- HTML += '<h5>Connection'+(conns.length > 1 ? 's' : '')+'</h5>';
- HTML += '<table class="popupTable"><tr class="header"><td>Security</td><td>Authentication</td><td>Tenant</td><td>Host</td>';
+ let HTML = "";
+ HTML += "<h5>Connection" + (conns.length > 1 ? "s" : "") + "</h5>";
+ HTML +=
+ '<table class="popupTable"><tr class="header"><td>Security</td><td>Authentication</td><td>Tenant</td><td>Host</td>';
- for (let c=0; c<Math.min(conns.length, 10); c++) {
- HTML += ('<tr><td>' + utils.connSecurity(conns[c]) + '</td>');
- HTML += ('<td>' + utils.connAuth(conns[c]) + '</td>');
- HTML += ('<td>' + (utils.connTenant(conns[c]) || '--') + '</td>');
- HTML += ('<td>' + conns[c].host + '</td>');
- HTML += '</tr>';
+ for (let c = 0; c < Math.min(conns.length, 10); c++) {
+ HTML += "<tr><td>" + utils.connSecurity(conns[c]) + "</td>";
+ HTML += "<td>" + utils.connAuth(conns[c]) + "</td>";
+ HTML += "<td>" + (utils.connTenant(conns[c]) || "--") + "</td>";
+ HTML += "<td>" + conns[c].host + "</td>";
+ HTML += "</tr>";
}
- HTML += '</table>';
+ HTML += "</table>";
HTML += linksHTML(onode, conns);
return HTML;
}
-export function addStyles (stend, stateColor, radii) {
- // the <style>
- let element = document.querySelector('style');
- // Reference to the stylesheet
- let sheet = element.sheet;
-
- let states = Object.keys(stateColor);
- // create styles for each combo of 'stend-state-radii'
- for (let istend=0; istend<stend.length; istend++) {
- for (let istate=0; istate<states.length; istate++) {
- let selectors = [];
- for (let iradii=0; iradii<radii.length; iradii++) {
- selectors.push(`#${stend[istend]}-${states[istate]}-${radii[iradii]}`);
- }
- let color = stateColor[states[istate]];
- let sels = `${selectors.join(',')} {fill: ${color}; stroke: ${color};}`;
- sheet.insertRule(sels, 0);
- }
+export function getSizes(QDRLog) {
+ const gap = 5;
+ let legendWidth = 194;
+ let topoWidth = $("#topology").width();
+ if (topoWidth < 768) legendWidth = 0;
+ let width = $("#topology").width() - gap - legendWidth;
+ let top = $("#topology").offset().top;
+ let height = window.innerHeight - top - gap;
+ if (width < 10) {
+ QDRLog.info(
+ `page width and height are abnormal w: ${width} h: ${height}`
+ );
+ return [0, 0];
}
+ return [width, height];
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[3/3] qpid-dispatch git commit: DISPATCH-1217 Treat node.fixed as
bitmap
Posted by ea...@apache.org.
DISPATCH-1217 Treat node.fixed as bitmap
Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/81e58b46
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/81e58b46
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/81e58b46
Branch: refs/heads/master
Commit: 81e58b4628173d1392d32dcfedce0312065bea41
Parents: e9b7bd5
Author: Ernest Allen <ea...@redhat.com>
Authored: Mon Dec 10 11:58:58 2018 -0500
Committer: Ernest Allen <ea...@redhat.com>
Committed: Mon Dec 10 11:58:58 2018 -0500
----------------------------------------------------------------------
console/stand-alone/plugin/css/dispatch.css | 10 -
.../stand-alone/plugin/html/qdrTopology.html | 179 ++-
console/stand-alone/plugin/js/amqp/utilities.js | 39 +-
.../plugin/js/dlgDetailController.js | 53 +-
.../stand-alone/plugin/js/topology/legend.js | 138 +++
console/stand-alone/plugin/js/topology/links.js | 54 +-
console/stand-alone/plugin/js/topology/map.js | 69 +-
console/stand-alone/plugin/js/topology/nodes.js | 99 +-
.../plugin/js/topology/qdrTopology.js | 1086 ++++++++----------
.../stand-alone/plugin/js/topology/svgUtils.js | 249 ++++
.../stand-alone/plugin/js/topology/topoUtils.js | 242 ++--
.../stand-alone/plugin/js/topology/traffic.js | 144 ++-
console/stand-alone/test/links.js | 54 +-
13 files changed, 1359 insertions(+), 1057 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/81e58b46/console/stand-alone/plugin/css/dispatch.css
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/css/dispatch.css b/console/stand-alone/plugin/css/dispatch.css
index f41754b..709ce43 100644
--- a/console/stand-alone/plugin/css/dispatch.css
+++ b/console/stand-alone/plugin/css/dispatch.css
@@ -1143,12 +1143,6 @@ svg {
/*height: 100%; */
}
- div#topologyForm .ngViewport, div#topologyForm .gridStyle {
- height: auto !important;
- min-height: initial !important;
- overflow: initial;
- }
-
div#multiple_details, div#link_details {
height: 300px;
width: 700px;
@@ -1819,10 +1813,6 @@ span.logo {
top: 10px;
}
- #topologyForm > div {
- width: auto;
- }
-
div.chartContainer {
width: auto;
margin-top: 1em;
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/81e58b46/console/stand-alone/plugin/html/qdrTopology.html
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/html/qdrTopology.html b/console/stand-alone/plugin/html/qdrTopology.html
index 5ba514b..0057fab 100644
--- a/console/stand-alone/plugin/html/qdrTopology.html
+++ b/console/stand-alone/plugin/html/qdrTopology.html
@@ -18,7 +18,7 @@ under the License.
-->
<style>
-@media (min-width: 768px) {
+ @media (min-width: 768px) {
.showLeft {
display: block;
}
@@ -27,6 +27,9 @@ under the License.
.showLeft {
display: none;
}
+ #backgroundMap {
+ display: none;
+ }
div.qdrTopology div.legend-container.page-menu {
top: 0;
right: auto;
@@ -35,7 +38,7 @@ under the License.
}
}
-#popover-div, #fixed-popup {
+#popover-div {
position: absolute;
z-index: 200;
border-radius: 4px;
@@ -53,41 +56,6 @@ under the License.
font-size: 10px;
}
-#topologyForm {
- border-right: 1px solid lightgray;
- border-bottom: 1px solid lightgray;
- padding: 2px;
- /* position: absolute; */
- background-color: #333333;
- width: 300px;
-}
-
- #topologyForm .infoGrid span {
- display: inline-block;
- width: 50%;
- height: 28px;
- padding: 3px 6px 2px 6px;
- border-right: 1px solid #666666;
- text-overflow: ellipsis;
- white-space: nowrap;
- overflow: hidden;
- }
-
- #topologyForm .infoGrid span:last-child {
- text-align: right;
- border-right: 0;
- }
- #topologyForm .infoGrid div {
- height: 28px;
- }
- #topologyForm .infoGrid div.odd {
- background-color: #444444;
- }
-
- #topologyForm .infoGrid div.listening-on {
- background-color: #336633;
- }
-
.legend-container {
position: absolute;
top: 1em;
@@ -135,8 +103,8 @@ under the License.
margin-bottom: 0;
padding-bottom: 0;
}
- /* the checkboxes for the addresses */
- #topo_legend ul li input[type=checkbox]:checked + label::before {
+/* the checkboxes for the addresses */
+#topo_legend ul li input[type=checkbox]:checked + label::before {
content:'\2713';
font-weight: bold;
font-size: 16px;
@@ -146,12 +114,12 @@ under the License.
position: absolute;
top: -8px;
left: -1px;
- }
- /* The aggregate addresses need a black checkbox on the white background */
- #topo_legend ul li input[type=checkbox]:checked + label.aggregate::before {
+}
+/* The aggregate addresses need a black checkbox on the white background */
+#topo_legend ul li input[type=checkbox]:checked + label.aggregate::before {
color: black;
- /* left: 1px; */
- }
+/* left: 1px; */
+}
#topo_legend ul.addresses button.btn-default {
background-image: none;
color: white;
@@ -159,8 +127,8 @@ under the License.
}
#topo_legend li.legend-sublist ul {
- margin-bottom: 0.5em;
- }
+ margin-bottom: 0.5em;
+}
#topo_legend li.legend-sublist ul.addresses{
max-height: 11.6em; /* show up to 4 addresses */
@@ -171,11 +139,11 @@ li.legend-sublist > ul ul {
margin-left: 1em;
}
-#popover-div h5, #fixed-popup h5 {
+#popover-div h5 {
margin-top: 1em;
margin-bottom: 0;
}
-#popover-div h5:first-of-type, #fixed-popup h5:first-of-type {
+#popover-div h5:first-of-type {
margin-top:0;
}
@@ -198,23 +166,23 @@ table.popupTable td {
}
.graticule {
- fill: none;
- stroke: #777;
- stroke-width: .5px;
- stroke-opacity: .5;
+ fill: none;
+ stroke: #777;
+ stroke-width: .5px;
+ stroke-opacity: .5;
}
g.geo path.land {
- fill: #dcedf7;
- stroke: #000;
- stroke-opacity: 1;
- stroke-width: 1px;
+ fill: #dcedf7;
+ stroke: #000;
+ stroke-opacity: 1;
+ stroke-width: 1px;
}
.boundary {
- fill: none;
- stroke: #333;
- stroke-width: 5px;
+ fill: none;
+ stroke: #333;
+ stroke-width: 5px;
}
.panel-group {
@@ -254,40 +222,44 @@ td.more-info {
<div class="legend-container page-menu navbar-collapse collapse">
<uib-accordion id="topo_legend" close-others="false">
<div uib-accordion-group class="panel-default" is-open="legend.status.optionsOpen" heading="Show Traffic">
- <ul class="options">
- <li class="legend-sublist" ng-hide="!legendOptions.showTraffic">
- <ul>
- <li><label>
- <input type='radio' ng-model="legendOptions.trafficType" value="dots" />
- Message path by address
- </label></li>
- <li>
- <ul class="addresses" ng-show="legendOptions.trafficType === 'dots'">
+ <ul class="options">
+ <li class="legend-sublist" ng-hide="!legendOptions.showTraffic">
+ <ul>
+ <li><label>
+ <input type='radio' ng-model="legendOptions.trafficType" value="dots" />
+ Message path by address
+ </label></li>
+ <li>
+ <ul class="addresses" ng-show="legendOptions.trafficType === 'dots'">
<li ng-repeat="(address, color) in addresses" class="legend-line">
- <checkbox style="background-color: {{addressColors[address]}};"
- title="{{address}}" ng-change="addressFilterChanged()"
- ng-model="addresses[address]" ng-mouseenter="enterLegend(address)" ng-mouseleave="leaveLegend()"></checkbox>
- <span class="legend-text" ng-mouseenter="enterLegend(address)" ng-mouseleave="leaveLegend()" ng-click="addressClick(address)" title="{{address}}">{{address | limitTo : 15}}{{address.length>15 ? '…' : ''}}</span>
+ <checkbox style="background-color: {{addressColors[address]}};" title="{{address}}"
+ ng-change="addressFilterChanged()" ng-model="addresses[address]"
+ ng-mouseenter="enterLegend(address)" ng-mouseleave="leaveLegend()"></checkbox>
+ <span class="legend-text" ng-mouseenter="enterLegend(address)" ng-mouseleave="leaveLegend()"
+ ng-click="addressClick(address)" title="{{address}}">{{address | limitTo :
+ 15}}{{address.length>15 ? '…' : ''}}</span>
</li>
- </ul>
- </li>
- </ul>
- <ul>
- <li><label>
- <input type='radio' ng-model="legendOptions.trafficType" value="congestion" />
- Link utilization
- </label></li>
- <li>
- <ul class="congestion" ng-show="legendOptions.trafficType === 'congestion'">
+ </ul>
+ </li>
+ </ul>
+ <ul>
+ <li><label>
+ <input type='radio' ng-model="legendOptions.trafficType" value="congestion" />
+ Link utilization
+ </label></li>
+ <li>
+ <ul class="congestion" ng-show="legendOptions.trafficType === 'congestion'">
<li>
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" preserveAspectRatio="xMidYMid meet" width="140" height="40">
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+ version="1.1" preserveAspectRatio="xMidYMid meet" width="140" height="40">
<defs>
- <linearGradient xmlns="http://www.w3.org/2000/svg" id="gradienta1bEihLEHL" gradientUnits="userSpaceOnUse" x1="0%" y1="0%" x2="100%" y2="0%">
- <stop style="stop-color: #000000;stop-opacity: 1" offset="0"/>
- <stop style="stop-color: #000000;stop-opacity: 1" offset="0.06"/>
- <stop style="stop-color: #00FF00;stop-opacity: 1" offset="0.333"/>
- <stop style="stop-color: #FFA500;stop-opacity: 1" offset="0.666"/>
- <stop style="stop-color: #FF0000;stop-opacity: 1" offset="1"/></linearGradient>
+ <linearGradient xmlns="http://www.w3.org/2000/svg" id="gradienta1bEihLEHL"
+ gradientUnits="userSpaceOnUse" x1="0%" y1="0%" x2="100%" y2="0%">
+ <stop style="stop-color: #999999;stop-opacity: 1" offset="0" />
+ <stop style="stop-color: #00FF00;stop-opacity: 1" offset="0.333" />
+ <stop style="stop-color: #FFA500;stop-opacity: 1" offset="0.666" />
+ <stop style="stop-color: #FF0000;stop-opacity: 1" offset="1" />
+ </linearGradient>
</defs>
<g>
<rect width="140" height="20" x="0" y="0" fill="url(#gradienta1bEihLEHL)"></rect>
@@ -295,34 +267,39 @@ td.more-info {
<text x="106" y="30" class="label">Busy</text>
</g>
</svg></li>
- </ul>
- </li>
- </ul>
- </li>
- </ul>
+ </ul>
+ </li>
+ </ul>
+ </li>
+ </ul>
</div>
<div uib-accordion-group class="panel-default" is-open="legend.status.legendOpen" heading="Legend">
<div id="topo_svg_legend"></div>
</div>
- <div uib-accordion-group class="panel-default" is-open="legend.status.mapOpen" heading="Background map">
+ <div id="backgroundMap" uib-accordion-group class="panel-default" is-open="legend.status.mapOpen" heading="Background map">
<div id="topo_mapOptions">
<div class="colorPicker">
<ul>
<li>
- <label><span class='map-label'>Land</span> <input id="areaColor" name="areaColor" type="color" ng-model="mapOptions.areaColor"/></label>
+ <label><span class='map-label'>Land</span> <input id="areaColor" name="areaColor" type="color"
+ ng-model="mapOptions.areaColor" /></label>
</li>
<li>
- <label><span class='map-label'>Ocean</span> <input id="oceanColor" name="oceanColor" type="color" ng-model="mapOptions.oceanColor"/></label>
+ <label><span class='map-label'>Ocean</span> <input id="oceanColor" name="oceanColor"
+ type="color" ng-model="mapOptions.oceanColor" /></label>
</li>
</ul>
</div>
</div>
</div>
- </uib-accordion>
+ </uib-accordion>
</div>
<div class="diagram">
- <div id="topology"><!-- d3 toplogy here --></div>
- <div id="crosssection"></div><div id="crosshtml" ng-bind-html="crosshtml"></div>
+ <div id="topology">
+ <!-- d3 toplogy here -->
+ </div>
+ <div id="crosssection"></div>
+ <div id="crosshtml" ng-bind-html="crosshtml"></div>
<div id="node_context_menu" class="contextMenu">
<ul>
@@ -344,4 +321,4 @@ td.more-info {
</script>
<script type="text/ng-template" id="titleCellTemplate.html">
<div title="{{row.entity.attributeValue}}" class="ui-grid-cell-contents">{{COL_FIELD CUSTOM_FILTERS | pretty}}</div>
-</script>
+</script>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/81e58b46/console/stand-alone/plugin/js/amqp/utilities.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/amqp/utilities.js b/console/stand-alone/plugin/js/amqp/utilities.js
index 517b4e1..34f1d7d 100644
--- a/console/stand-alone/plugin/js/amqp/utilities.js
+++ b/console/stand-alone/plugin/js/amqp/utilities.js
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Red Hat Inc.
+ * Copyright 2018 Red Hat Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
*/
/* global d3 */
-var ddd = typeof window === 'undefined' ? require ('d3') : d3;
+var ddd = typeof window === 'undefined' ? require('d3') : d3;
var utils = {
isAConsole: function (properties, connectionId, nodeType, key) {
@@ -40,7 +40,7 @@ var utils = {
if (!attributes || !result)
return {};
var flat = {};
- attributes.forEach(function(attr, i) {
+ attributes.forEach(function (attr, i) {
if (result && result.length > i)
flat[attr] = result[i];
});
@@ -48,9 +48,11 @@ var utils = {
},
flattenAll: function (entity, filter) {
if (!filter)
- filter = function (e) {return e;};
+ filter = function (e) {
+ return e;
+ };
let results = [];
- for (let i=0; i<entity.results.length; i++) {
+ for (let i = 0; i < entity.results.length; i++) {
let f = filter(this.flatten(entity.attributeNames, entity.results[i]));
if (f)
results.push(f);
@@ -72,7 +74,7 @@ var utils = {
addr_text: function (addr) {
if (!addr)
return '-';
- if (addr[0] === addr[0].toLowerCase())
+ if (addr[0] === addr[0].toLowerCase())
return addr;
if (addr[0] == 'M')
return addr.substring(2);
@@ -122,7 +124,7 @@ var utils = {
countsFor: function (aAr, vAr, key) {
let counts = {};
let idx = aAr.indexOf(key);
- for (let i=0; i<vAr.length; i++) {
+ for (let i = 0; i < vAr.length; i++) {
if (!counts[vAr[i][idx]])
counts[vAr[i][idx]] = 0;
counts[vAr[i][idx]]++;
@@ -139,7 +141,7 @@ var utils = {
var parts = id.split('/');
// remove $management
- parts.pop();
+ parts.pop();
// remove the area if present
if (parts[2] === '0')
@@ -171,18 +173,21 @@ var utils = {
list.shift();
}
let rates = {};
- list.push({date: new Date(), val: Object.assign({}, obj)});
+ list.push({
+ date: new Date(),
+ val: Object.assign({}, obj)
+ });
- for (let i=0; i<fields.length; i++) {
+ for (let i = 0; i < fields.length; i++) {
let cumulative = 0;
let field = fields[i];
- for (let j=0; j<list.length-1; j++) {
- let elapsed = list[j+1].date - list[j].date;
- let diff = list[j+1].val[field] - list[j].val[field];
+ for (let j = 0; j < list.length - 1; j++) {
+ let elapsed = list[j + 1].date - list[j].date;
+ let diff = list[j + 1].val[field] - list[j].val[field];
if (elapsed > 100)
- cumulative += diff/(elapsed / 1000);
+ cumulative += diff / (elapsed / 1000);
}
- rates[field] = list.length > 1 ? cumulative / (list.length-1) : 0;
+ rates[field] = list.length > 1 ? cumulative / (list.length - 1) : 0;
}
return rates;
},
@@ -216,4 +221,6 @@ var utils = {
}
};
-export { utils };
\ No newline at end of file
+export {
+ utils
+};
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/81e58b46/console/stand-alone/plugin/js/dlgDetailController.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/dlgDetailController.js b/console/stand-alone/plugin/js/dlgDetailController.js
index 1c9b45a..dba5d3b 100644
--- a/console/stand-alone/plugin/js/dlgDetailController.js
+++ b/console/stand-alone/plugin/js/dlgDetailController.js
@@ -33,7 +33,7 @@ export class DetailDialogController {
// count the number of characters in an array of strings
let countChars = function (ar) {
let count = 0;
- ar.forEach( a => count += a.length);
+ ar.forEach(a => count += a.length);
return count;
};
@@ -110,7 +110,7 @@ export class DetailDialogController {
};
// keep an array of column sizes
let updateSizes = function (fields, sizes, obj) {
- fields.forEach( function (key) {
+ fields.forEach(function (key) {
if (!sizes[key])
sizes[key] = utils.humanify(key).length;
sizes[key] = Math.max(sizes[key], utils.pretty(obj[key]).length);
@@ -128,8 +128,8 @@ export class DetailDialogController {
// queued function to get the .router info for an edge router
let q_getEdgeInfo = function (n, infoPerId, callback) {
let nodeId = utils.idFromName(n.container, '_edge');
- QDRService.management.topology.fetchEntities(nodeId,
- [{entity: 'router', attrs: []},
+ QDRService.management.topology.fetchEntities(nodeId,
+ [{ entity: 'router', attrs: [] },
],
function (results) {
let r = results[nodeId].router;
@@ -142,21 +142,22 @@ export class DetailDialogController {
callback(null);
});
};
- return new Promise( (function (resolve) {
+ return new Promise((function (resolve) {
let infoPerId = {};
// we are getting info for an edge router
if (d.nodeType === 'edge') {
// called for each expanded row to get further details about the edge router
$scope.detail.moreInfo = function (id) {
let nodeId = utils.idFromName(id, '_edge');
- QDRService.management.topology.fetchEntities(nodeId,
- [{entity: 'router.link', attrs: []},
- {entity: 'linkRoute', attrs: $scope.fields.linkRouteFields.cols},
- {entity: 'autoLink', attrs: $scope.fields.autoLinkFields.cols},
- {entity: 'address', attrs: []},
+ QDRService.management.topology.fetchEntities(nodeId,
+ [
+ { entity: 'router.link', attrs: [] },
+ { entity: 'linkRoute', attrs: $scope.fields.linkRouteFields.cols },
+ { entity: 'autoLink', attrs: $scope.fields.autoLinkFields.cols },
+ { entity: 'address', attrs: [] },
],
function (results) {
- $timeout( function () {
+ $timeout(function () {
// save the results (and sizes) for each entity requested
infoPerId[id].linkRouteSizes = {};
infoPerId[id].linkRoutes = utils.flattenAll(results[nodeId].linkRoute,
@@ -165,13 +166,13 @@ export class DetailDialogController {
return route;
});
infoPerId[id].autoLinkSizes = {};
- infoPerId[id].autoLinks = utils.flattenAll(results[nodeId].autoLink,
+ infoPerId[id].autoLinks = utils.flattenAll(results[nodeId].autoLink,
function (link) {
updateSizes($scope.fields.autoLinkFields.cols, infoPerId[id].autoLinkSizes, link);
return link;
});
infoPerId[id].addressSizes = {};
- infoPerId[id].addresses = utils.flattenAll(results[nodeId].address,
+ infoPerId[id].addresses = utils.flattenAll(results[nodeId].address,
function (addr) {
updateSizes($scope.fields.addressFields.cols, infoPerId[id].addressSizes, addr);
return addr;
@@ -182,7 +183,7 @@ export class DetailDialogController {
// async send up to 10 requests
let q = d3.queue(10);
- for (let n=0; n<d.normals.length; n++) {
+ for (let n = 0; n < d.normals.length; n++) {
q.defer(q_getEdgeInfo, d.normals[n], infoPerId);
if (expandedRows.has(d.normals[n].container)) {
$scope.detail.moreInfo(d.normals[n].container);
@@ -200,14 +201,14 @@ export class DetailDialogController {
});
} else {
// we are getting info for a group of clients or consoles
- $scope.detail.moreInfo = function () {};
+ $scope.detail.moreInfo = function () { };
let attrs = utils.copy($scope.fields.linkFields.cols);
attrs.unshift('connectionId');
- QDRService.management.topology.fetchEntities(d.key,
- [{entity: 'router.link', attrs: attrs}],
+ QDRService.management.topology.fetchEntities(d.key,
+ [{ entity: 'router.link', attrs: attrs }],
function (results) {
let links = results[d.key]['router.link'];
- for (let i=0; i<d.normals.length; i++) {
+ for (let i = 0; i < d.normals.length; i++) {
let n = d.normals[i];
let conn = {};
infoPerId[n.container] = conn;
@@ -231,7 +232,7 @@ export class DetailDialogController {
let count = d.normals.length;
let verb = count > 1 ? 'are' : 'is';
let preposition = d.cdir === 'in' ? 'to' : d.cdir === 'both' ? 'for' : 'from';
- let plural = count > 1 ? 's': '';
+ let plural = count > 1 ? 's' : '';
$scope.detail.template = 'clients.html';
$scope.detail.title = 'for client';
resolve({
@@ -242,16 +243,16 @@ export class DetailDialogController {
}
}));
};
-
+
let updateDetail = function () {
groupDetail.call(this)
- .then( function (det) {
- $timeout( function () {
+ .then(function (det) {
+ $timeout(function () {
$scope.detail.title = `for ${d.normals.length} ${$scope.detail.title}${d.normals.length > 1 ? 's' : ''}`;
$scope.detail.description = det.description;
- $scope.detail.infoPerId = Object.keys(det.infoPerId).map( function (id) {
+ $scope.detail.infoPerId = Object.keys(det.infoPerId).map(function (id) {
return det.infoPerId[id];
- }).sort( function (a, b) {
+ }).sort(function (a, b) {
return a.name > b.name ? 1 : -1;
});
});
@@ -266,7 +267,7 @@ DetailDialogController.$inject = ['QDRService', '$scope', '$timeout', '$uibModal
// SubTable directive
export class SubTable {
- constructor () {
+ constructor() {
this.restrict = 'E';
this.scope = {
sizes: '=sizes',
@@ -275,7 +276,7 @@ export class SubTable {
};
this.templateUrl = 'sub-table.html';
}
- link (scope) {
+ link(scope) {
scope.fieldWidth = function (val, sizes) {
if (!sizes)
return '10%';
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/81e58b46/console/stand-alone/plugin/js/topology/legend.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/topology/legend.js b/console/stand-alone/plugin/js/topology/legend.js
new file mode 100644
index 0000000..b3908c5
--- /dev/null
+++ b/console/stand-alone/plugin/js/topology/legend.js
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2018 Red Hat Inc.
+ *
+ * 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.
+ */
+
+/* global d3 */
+
+import { Nodes } from "./nodes.js";
+import { appendCircle, appendContent, appendTitle } from "./svgUtils.js";
+// for testing, window will be undefined
+var ddd = typeof window === 'undefined' ? require('d3') : d3;
+
+const lookFor = [
+ { role: "inter-router", title: "Router", text: "", cls: '' },
+ { role: "edge", title: "Router", text: "Edge", cls: 'edge' },
+ { role: "normal", title: "Console", text: "console", cls: 'console', props: { console_identifier: "Dispatch console" } },
+ { role: "normal", title: "Sender", text: "Sender", cls: 'client.in', cdir: "in" },
+ { role: "normal", title: "Receiver", text: "Receiver", cls: 'client.out', cdir: "out" },
+ { role: "normal", title: "Sender/Receiver", text: "Sender/Receiver", cls: 'client.inout', cdir: "both" },
+ { role: "route-container", title: "Qpid broker", text: "Qpid broker", cls: 'client.route-container', props: { product: "qpid-cpp" } },
+ { role: "route-container", title: "Artemis broker", text: "Artemis broker", cls: 'route-container', props: { product: "apache-activemp-artemis" } },
+ { role: "route-container", title: "Service", text: "Service", cls: 'route-container', props: { product: " External Service" } }
+];
+
+export class Legend {
+ constructor(svg, QDRLog, urlPrefix) {
+ this.svg = svg;
+ this.log = QDRLog;
+ this.urlPrefix = urlPrefix;
+ }
+
+ // create a new legend container svg
+ init() {
+ return ddd
+ .select("#topo_svg_legend")
+ .append("svg")
+ .attr("id", "svglegend")
+ .append("svg:g")
+ .attr(
+ "transform",
+ `translate(${Nodes.maxRadius()}, ${Nodes.maxRadius()})`
+ )
+ .selectAll("g");
+ }
+
+ // create or update the legend
+ update() {
+ let lsvg;
+ if (ddd.select("#topo_svg_legend svg").empty()) {
+ lsvg = this.init();
+ } else {
+ lsvg = ddd.select("#topo_svg_legend svg g").selectAll("g");
+ }
+ // add a node to legendNodes for each node type that is currently in the svg
+ let legendNodes = new Nodes(this.log);
+ lookFor.forEach(function (node, i) {
+ if (!node.cls || !this.svg.selectAll(`circle.${node.cls}`).empty()) {
+ let lnode = legendNodes.addUsing(
+ node.title,
+ node.text,
+ node.role,
+ undefined,
+ 0, 0, i, 0,
+ false,
+ node.props ? node.props : {}
+ );
+ if (node.cdir)
+ lnode.cdir = node.cdir;
+ }
+ }, this);
+
+ // determine the y coordinate of the last existing node in the legend
+ let cury = 0;
+ lsvg.each(function (d) {
+ cury += Nodes.radius(d.nodeType) * 2 + 10;
+ });
+
+ // associate the legendNodes with lsvg
+ lsvg = lsvg.data(legendNodes.nodes, function (d) {
+ return d.uid();
+ });
+
+ // add any new nodes
+ let legendEnter = lsvg
+ .enter()
+ .append("svg:g")
+ .attr("transform", function (d) {
+ let t = `translate(0, ${cury})`;
+ cury += Nodes.radius(d.nodeType) * 2 + 10;
+ return t;
+ });
+ appendCircle(legendEnter, this.urlPrefix);
+ appendContent(legendEnter);
+ appendTitle(legendEnter);
+ legendEnter.append("svg:text")
+ .attr("x", 35)
+ .attr("y", 6)
+ .attr("class", "label")
+ .text(function (d) {
+ return d.key;
+ });
+
+ // remove any nodes that dropped out of legendNodes
+ lsvg.exit().remove();
+
+ // position the legend based on it's size
+ let svgEl = document.getElementById("svglegend");
+ if (svgEl) {
+ let bb;
+ // firefox can throw an exception on getBBox on an svg element
+ try {
+ bb = svgEl.getBBox();
+ } catch (e) {
+ bb = {
+ y: 0,
+ height: 200,
+ x: 0,
+ width: 200
+ };
+ }
+ svgEl.style.height = bb.y + bb.height + "px";
+ svgEl.style.width = bb.x + bb.width + "px";
+ }
+ }
+}
+
+
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/81e58b46/console/stand-alone/plugin/js/topology/links.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/topology/links.js b/console/stand-alone/plugin/js/topology/links.js
index 16b086c..1260415 100644
--- a/console/stand-alone/plugin/js/topology/links.js
+++ b/console/stand-alone/plugin/js/topology/links.js
@@ -17,7 +17,9 @@ specific language governing permissions and limitations
under the License.
*/
-import { utils } from "../amqp/utilities.js";
+import {
+ utils
+} from "../amqp/utilities.js";
class Link {
constructor(source, target, dir, cls, uid) {
@@ -29,15 +31,13 @@ class Link {
this.uid = uid;
}
markerId(end) {
- let selhigh = this.highlighted
- ? "highlighted"
- : this.selected
- ? "selected"
- : "";
+ let selhigh = this.highlighted ?
+ "highlighted" :
+ (this.selected ?
+ "selected" :
+ "");
if (selhigh === "" && (!this.left && !this.right)) selhigh = "unknown";
- return `-${selhigh}-${
- end === "end" ? this.target.radius() : this.source.radius()
- }`;
+ return `-${selhigh}-${end === "end" ? this.target.radius() : this.source.radius()}`;
}
}
@@ -46,6 +46,9 @@ export class Links {
this.links = [];
this.logger = logger;
}
+ reset() {
+ this.links.length = 0;
+ }
getLinkSource(nodesIndex) {
for (let i = 0; i < this.links.length; ++i) {
if (this.links[i].target === nodesIndex) return i;
@@ -70,7 +73,7 @@ export class Links {
}
//this.logger.debug("creating new link (" + (links.length) + ") between " + nodes[_source].name + " and " + nodes[_target].name);
if (
- this.links.some(function(l) {
+ this.links.some(function (l) {
return l.uid === uid;
})
)
@@ -88,10 +91,8 @@ export class Links {
return null;
}
- getPosition(name, nodes, source, client, localStorage, height) {
- let position = localStorage[name]
- ? JSON.parse(localStorage[name])
- : undefined;
+ getPosition(name, nodes, source, client, height, localStorage) {
+ let position = localStorage[name] ? JSON.parse(localStorage[name]) : undefined;
if (typeof position == "undefined") {
position = {
x: Math.round(
@@ -109,10 +110,12 @@ export class Links {
nodes.get(source).y + 40 + Math.cos(client / (Math.PI * 2.0))
);
}
+ position.fixed = position.fixed ? true : false;
return position;
}
- initialize(nodeInfo, nodes, unknowns, localStorage, height) {
+ initialize(nodeInfo, nodes, unknowns, height, localStorage) {
+ this.reset();
let connectionsPerContainer = {};
let nodeIds = Object.keys(nodeInfo);
// collect connection info for each router
@@ -158,7 +161,10 @@ export class Links {
// create map of type:id:dir to [containers]
for (let container in connectionsPerContainer) {
let key = getKey(connectionsPerContainer[container]);
- if (!unique[key]) unique[key] = { c: [], nodes: [] };
+ if (!unique[key]) unique[key] = {
+ c: [],
+ nodes: []
+ };
unique[key].c.push(container);
}
for (let key in unique) {
@@ -176,8 +182,8 @@ export class Links {
nodes,
container.source,
container.resultsIndex,
- localStorage,
- height
+ height,
+ localStorage
);
let node = nodes.getOrCreateNode(
@@ -242,7 +248,7 @@ export class Links {
}
}
-var getContainerIndex = function(_id, nodeInfo) {
+var getContainerIndex = function (_id, nodeInfo) {
let nodeIndex = 0;
for (let id in nodeInfo) {
if (utils.nameFromId(id) === _id) return nodeIndex;
@@ -251,7 +257,7 @@ var getContainerIndex = function(_id, nodeInfo) {
return -1;
};
-var getLinkDir = function(connection, onode) {
+var getLinkDir = function (connection, onode) {
let links = onode["router.link"];
if (!links) {
return "unknown";
@@ -261,12 +267,12 @@ var getLinkDir = function(connection, onode) {
let typeIndex = links.attributeNames.indexOf("linkType");
let connectionIdIndex = links.attributeNames.indexOf("connectionId");
let dirIndex = links.attributeNames.indexOf("linkDir");
- links.results.forEach(function(linkResult) {
+ links.results.forEach(function (linkResult) {
if (
linkResult[typeIndex] === "endpoint" &&
linkResult[connectionIdIndex] === connection.identity
)
- if (linkResult[dirIndex] === "in") ++inCount;
+ if (linkResult[dirIndex] === "in")++inCount;
else ++outCount;
});
if (inCount > 0 && outCount > 0) return "both";
@@ -274,7 +280,7 @@ var getLinkDir = function(connection, onode) {
if (outCount > 0) return "out";
return "unknown";
};
-var getKey = function(containers) {
+var getKey = function (containers) {
let parts = [];
let connection = containers[0].connection;
let d = {
@@ -291,4 +297,4 @@ var getKey = function(containers) {
parts.push(`${container.source}-${container.linksDir}`);
}
return `${connectionType}:${parts.join(":")}`;
-};
+};
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/81e58b46/console/stand-alone/plugin/js/topology/map.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/topology/map.js b/console/stand-alone/plugin/js/topology/map.js
index 972d894..c03cfa5 100644
--- a/console/stand-alone/plugin/js/topology/map.js
+++ b/console/stand-alone/plugin/js/topology/map.js
@@ -30,8 +30,14 @@ export class BackgroundMap { // eslint-disable-line no-unused-vars
this.$scope = $scope;
this.initialized = false;
this.notify = notifyFn;
- $scope.mapOptions = angular.fromJson(localStorage[MAPOPTIONSKEY]) || {areaColor: defaultLandColor, oceanColor: defaultOceanColor};
- this.last = {translate: [0,0], scale: null};
+ $scope.mapOptions = angular.fromJson(localStorage[MAPOPTIONSKEY]) || {
+ areaColor: defaultLandColor,
+ oceanColor: defaultOceanColor
+ };
+ this.last = {
+ translate: [0, 0],
+ scale: null
+ };
}
updateLandColor(color) {
localStorage[MAPOPTIONSKEY] = JSON.stringify(this.$scope.mapOptions);
@@ -55,7 +61,7 @@ export class BackgroundMap { // eslint-disable-line no-unused-vars
}
init($scope, svg, width, height) {
- return new Promise( (function (resolve, reject) {
+ return new Promise((function (resolve, reject) {
if (this.initialized) {
resolve();
@@ -82,9 +88,9 @@ export class BackgroundMap { // eslint-disable-line no-unused-vars
// setup the projection with some defaults
this.projection = d3.geo.mercator()
- .rotate([this.rotate,0])
+ .rotate([this.rotate, 0])
.scale(1)
- .translate([width/2, height/2]);
+ .translate([width / 2, height / 2]);
// this path will hold the land coordinates once they are loaded
this.geoPath = d3.geo.path()
@@ -92,22 +98,26 @@ export class BackgroundMap { // eslint-disable-line no-unused-vars
// set up the scale extent and initial scale for the projection
var b = getMapBounds(this.projection, Math.max(maxnorth, maxsouth)),
- s = width/(b[1][0]-b[0][0]);
- this.scaleExtent = [s, 15*s];
+ s = width / (b[1][0] - b[0][0]);
+ this.scaleExtent = [s, 15 * s];
this.projection
.scale(this.scaleExtent[0]);
- this.lastProjection = angular.fromJson(localStorage[MAPPOSITIONKEY]) || {rotate: 20, scale: this.scaleExtent[0], translate: [width/2, height/2]};
+ this.lastProjection = angular.fromJson(localStorage[MAPPOSITIONKEY]) || {
+ rotate: 20,
+ scale: this.scaleExtent[0],
+ translate: [width / 2, height / 2]
+ };
this.zoom = d3.behavior.zoom()
.scaleExtent(this.scaleExtent)
.scale(this.projection.scale())
- .translate([0,0]) // not linked directly to projection
+ .translate([0, 0]) // not linked directly to projection
.on('zoom', this.zoomed.bind(this));
this.geo = svg.append('g')
.attr('class', 'geo')
- .style('opacity', this.$scope.legend.status.mapOpen ? '1': '0');
+ .style('opacity', this.$scope.legend.status.mapOpen ? '1' : '0');
this.geo.append('rect')
.attr('class', 'ocean')
@@ -115,13 +125,14 @@ export class BackgroundMap { // eslint-disable-line no-unused-vars
.attr('height', height)
.attr('fill', '#FFF');
- if (this.$scope.legend.status.mapOpen)
+ if (this.$scope.legend.status.mapOpen) {
this.svg.call(this.zoom)
.on('dblclick.zoom', null);
+ }
// async load of data file. calls resolve when this completes to let caller know
- d3.json('plugin/data/countries.json', function(error, world) {
- if (error)
+ d3.json('plugin/data/countries.json', function (error, world) {
+ if (error)
reject(error);
this.geo.append('path')
@@ -148,6 +159,8 @@ export class BackgroundMap { // eslint-disable-line no-unused-vars
setMapOpacity(opacity) {
opacity = opacity ? 1 : 0;
+ if (this.width && this.width < 768)
+ opacity = 0;
if (this.geo)
this.geo.style('opacity', opacity);
}
@@ -180,11 +193,11 @@ export class BackgroundMap { // eslint-disable-line no-unused-vars
}
zoomed() {
- if (d3.event && !this.$scope.current_node && !this.$scope.mousedown_node && this.$scope.legend.status.mapOpen) {
+ if (d3.event && !this.$scope.current_node && !this.$scope.mousedown_node && this.$scope.legend.status.mapOpen) {
let scale = d3.event.scale,
t = d3.event.translate,
- dx = t[0]-this.last.translate[0],
- dy = t[1]-this.last.translate[1],
+ dx = t[0] - this.last.translate[0],
+ dy = t[1] - this.last.translate[1],
yaw = this.projection.rotate()[0],
tp = this.projection.translate();
// zoomed
@@ -209,21 +222,21 @@ export class BackgroundMap { // eslint-disable-line no-unused-vars
dy = my - this.projection([0, lonlat[1]])[1];
// rotate the map so that the longitude under the mouse is where it was before the scale
- this.projection.rotate([yaw+dx ,0, 0]);
+ this.projection.rotate([yaw + dx, 0, 0]);
// translate the map so that the lattitude under the mouse is where it was before the scale
- this.projection.translate([tp[0], tp[1]+dy]);
+ this.projection.translate([tp[0], tp[1] + dy]);
} else {
// rotate instead of translate in the x direction
- this.projection.rotate([yaw+360.0*dx/this.width*this.scaleExtent[0]/scale, 0, 0]);
+ this.projection.rotate([yaw + 360.0 * dx / this.width * this.scaleExtent[0] / scale, 0, 0]);
// translate only in the y direction. don't translate beyond the max lattitude north or south
var bnorth = getMapBounds(this.projection, maxnorth),
bsouth = getMapBounds(this.projection, maxsouth);
- if (bnorth[0][1] + dy > 0)
+ if (bnorth[0][1] + dy > 0)
dy = -bnorth[0][1];
- else if (bsouth[1][1] + dy < this.height)
- dy = this.height-bsouth[1][1];
- this.projection.translate([tp[0],tp[1]+dy]);
+ else if (bsouth[1][1] + dy < this.height)
+ dy = this.height - bsouth[1][1];
+ this.projection.translate([tp[0], tp[1] + dy]);
}
this.last.scale = scale;
this.last.translate = t;
@@ -247,8 +260,8 @@ export class BackgroundMap { // eslint-disable-line no-unused-vars
// find the top left and bottom right of current projection
function getMapBounds(projection, maxlat) {
var yaw = projection.rotate()[0],
- xymax = projection([-yaw+180-1e-6,-maxlat]),
- xymin = projection([-yaw-180+1e-6, maxlat]);
-
- return [xymin,xymax];
-}
+ xymax = projection([-yaw + 180 - 1e-6, -maxlat]),
+ xymin = projection([-yaw - 180 + 1e-6, maxlat]);
+
+ return [xymin, xymax];
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/81e58b46/console/stand-alone/plugin/js/topology/nodes.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/topology/nodes.js b/console/stand-alone/plugin/js/topology/nodes.js
index 50c9a37..26ac9bc 100644
--- a/console/stand-alone/plugin/js/topology/nodes.js
+++ b/console/stand-alone/plugin/js/topology/nodes.js
@@ -17,7 +17,9 @@ specific language governing permissions and limitations
under the License.
*/
-import { utils } from "../amqp/utilities.js";
+import {
+ utils
+} from "../amqp/utilities.js";
/* global d3 Promise */
export class Node {
@@ -43,7 +45,7 @@ export class Node {
this.y = y;
this.id = nodeIndex;
this.resultIndex = resultIndex;
- this.fixed = !!+fixed;
+ this.fixed = fixed ? true : false;
this.cls = "";
this.container = connectionContainer;
this.isConsole = utils.isConsole(this);
@@ -71,11 +73,11 @@ export class Node {
}
toolTip(topology) {
return new Promise(
- function(resolve) {
+ function (resolve) {
if (this.nodeType === "normal" || this.nodeType === "edge") {
resolve(this.clientTooltip());
} else
- this.routerTooltip(topology).then(function(toolTip) {
+ this.routerTooltip(topology).then(function (toolTip) {
resolve(toolTip);
});
}.bind(this)
@@ -99,19 +101,23 @@ export class Node {
routerTooltip(topology) {
return new Promise(
- function(resolve) {
+ function (resolve) {
topology.ensureEntities(
this.key,
- [
- { entity: "listener", attrs: ["role", "port", "http"] },
- { entity: "router", attrs: ["name", "version", "hostName"] }
+ [{
+ entity: "listener",
+ attrs: ["role", "port", "http"]
+ },
+ {
+ entity: "router",
+ attrs: ["name", "version", "hostName"]
+ }
],
- function(foo, nodes) {
+ function (foo, nodes) {
// update all the router title text
let node = nodes[this.key];
const err = `<table class="popupTable"><tr><td>Error</td><td>Unable to get router info for ${
- this.key
- }</td></tr></table>`;
+ this.key}</td></tr></table>`;
if (!node) {
resolve(err);
return;
@@ -154,32 +160,43 @@ export class Node {
return nodeProperties[this.nodeType].radius;
}
uid() {
- if (!this.uuid) this.uuid = this.container;
+ if (!this.uuid)
+ this.uuid = `${this.container}`;
return this.normals ? `${this.uuid}-${this.normals.length}` : this.uuid;
}
setFixed(fixed) {
- if (!fixed) this.lat = this.lon = null;
- this.fixed = fixed;
+ if (!fixed & 1)
+ this.lat = this.lon = null;
+ this.fixed = fixed & 1 ? true : false;
}
}
const nodeProperties = {
// router types
"inter-router": {
radius: 28,
- refX: { end: 32, start: -19 },
+ refX: {
+ end: 32,
+ start: -19
+ },
linkDistance: [150, 70],
charge: [-1800, -900]
},
edge: {
radius: 20,
- refX: { end: 24, start: -12 },
+ refX: {
+ end: 24,
+ start: -12
+ },
linkDistance: [110, 55],
charge: [-1350, -900]
},
// generated nodes from connections. key is from connection.role
normal: {
radius: 15,
- refX: { end: 20, start: -7 },
+ refX: {
+ end: 20,
+ start: -7
+ },
linkDistance: [75, 40],
charge: [-900, -900]
}
@@ -240,7 +257,13 @@ export class Nodes {
gravity(d, nodeCount) {
return Nodes.forceScale(nodeCount, [0.0001, 0.1]);
}
-
+ setFixed(d, fixed) {
+ let n = this.find(d.container, d.properties, d.name);
+ if (n) {
+ n.fixed = fixed;
+ }
+ d.setFixed(fixed);
+ }
getLength() {
return this.nodes.length;
}
@@ -253,14 +276,6 @@ export class Nodes {
);
return undefined;
}
- setNodesFixed(name, b) {
- this.nodes.some(function(n) {
- if (n.name === name) {
- n.fixed(b);
- return true;
- }
- });
- }
nodeFor(name) {
for (let i = 0; i < this.nodes.length; ++i) {
if (this.nodes[i].name == name) return this.nodes[i];
@@ -268,7 +283,7 @@ export class Nodes {
return null;
}
nodeExists(connectionContainer) {
- return this.nodes.findIndex(function(node) {
+ return this.nodes.findIndex(function (node) {
return node.container === connectionContainer;
});
}
@@ -277,9 +292,12 @@ export class Nodes {
for (let i = 0; i < this.nodes.length; ++i) {
if (this.nodes[i].normals) {
if (
- this.nodes[i].normals.some(function(normal, j) {
+ this.nodes[i].normals.some(function (normal, j) {
if (normal.container === connectionContainer && i !== j) {
- normalInfo = { nodesIndex: i, normalsIndex: j };
+ normalInfo = {
+ nodesIndex: i,
+ normalsIndex: j
+ };
return true;
}
return false;
@@ -295,11 +313,11 @@ export class Nodes {
if (Object.prototype.toString.call(nodes) !== "[object Array]") {
nodes = [nodes];
}
- this.nodes.forEach(function(d) {
+ this.nodes.forEach(function (d) {
localStorage[d.name] = JSON.stringify({
x: Math.round(d.x),
y: Math.round(d.y),
- fixed: d.fixed & 1 ? 1 : 0
+ fixed: d.fixed
});
});
}
@@ -413,24 +431,23 @@ export class Nodes {
fixed,
properties
);
- this.nodes.push(obj);
- return obj;
+ return this.add(obj);
}
clearHighlighted() {
for (let i = 0; i < this.nodes.length; ++i) {
this.nodes[i].highlighted = false;
}
}
- initialize(nodeInfo, localStorage, width, height) {
+
+ initialize(nodeInfo, width, height, localStorage) {
+ this.nodes.length = 0;
let nodeCount = Object.keys(nodeInfo).length;
let yInit = 50;
let animate = false;
for (let id in nodeInfo) {
let name = utils.nameFromId(id);
// if we have any new nodes, animate the force graph to position them
- let position = localStorage[name]
- ? JSON.parse(localStorage[name])
- : undefined;
+ let position = localStorage[name] ? JSON.parse(localStorage[name]) : undefined;
if (!position) {
animate = true;
position = {
@@ -439,7 +456,7 @@ export class Nodes {
),
y: Math.round(
height / 2 +
- (Math.sin(this.nodes.length / (Math.PI * 2.0)) * height) / 4
+ (Math.sin(this.nodes.length / (Math.PI * 2.0)) * height) / 4
),
fixed: false
};
@@ -448,6 +465,7 @@ export class Nodes {
position.y = 200 - yInit;
yInit *= -1;
}
+ position.fixed = position.fixed ? true : false;
let parts = id.split("/");
this.addUsing(
id,
@@ -458,10 +476,9 @@ export class Nodes {
position.y,
name,
undefined,
- position.fixed,
- {}
+ position.fixed, {}
);
}
return animate;
}
-}
+}
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org