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 <[email protected]> Authored: Mon Dec 10 11:58:58 2018 -0500 Committer: Ernest Allen <[email protected]> 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: [email protected] For additional commands, e-mail: [email protected]
