Repository: qpid-dispatch
Updated Branches:
  refs/heads/master 43b3b2e03 -> 0310ac224


http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0310ac22/console/stand-alone/package.json
----------------------------------------------------------------------
diff --git a/console/stand-alone/package.json b/console/stand-alone/package.json
index 23731e3..aa59f68 100644
--- a/console/stand-alone/package.json
+++ b/console/stand-alone/package.json
@@ -45,6 +45,26 @@
     "jquery.fancytree": "^2.26.0",
     "notifyjs-browser": "^0.4.2",
     "patternfly": "^3.30.0",
-    "rhea": "^0.2.10"
+    "rhea": "^0.2.13"
+  },
+  "devDependencies": {
+    "babel-core": "^6.26.3",
+    "babel-preset-env": "^1.7.0",
+    "del": "^3.0.0",
+    "fs": "0.0.1-security",
+    "gulp": "github:gulpjs/gulp#4.0",
+    "gulp-babel": "^7.0.1",
+    "gulp-clean-css": "^3.9.4",
+    "gulp-concat": "^2.6.1",
+    "gulp-eslint": "^4.0.2",
+    "gulp-insert": "^0.5.0",
+    "gulp-ng-annotate": "^2.1.0",
+    "gulp-rename": "^1.2.3",
+    "gulp-sourcemaps": "^2.6.4",
+    "gulp-tslint": "^8.1.3",
+    "gulp-typescript": "^4.0.2",
+    "gulp-uglify": "^3.0.0",
+    "tslint": "^5.10.0",
+    "typescript": "^2.9.1"
   }
 }

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0310ac22/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 6478620..38495ad 100644
--- a/console/stand-alone/plugin/css/dispatch.css
+++ b/console/stand-alone/plugin/css/dispatch.css
@@ -22,7 +22,7 @@ main-display > .span8 {
 }
 
 ul.qdrListNodes > li > span {
-  padding: 6px 20px; 6px; 6px;
+  padding: 6px 20px 6px 6px;
   display: block;
 }
 

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0310ac22/console/stand-alone/plugin/js/dispatchPlugin.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/dispatchPlugin.js 
b/console/stand-alone/plugin/js/dispatchPlugin.js
index c39cb9b..47c8a60 100644
--- a/console/stand-alone/plugin/js/dispatchPlugin.js
+++ b/console/stand-alone/plugin/js/dispatchPlugin.js
@@ -39,20 +39,12 @@ var QDR = (function(QDR) {
   QDR.isStandalone = true;
 
   /**
-   * @property log
-   * @type {Logging.Logger}
-   *
-   * This plugin's logger instance
-   */
-  //HIO QDR.log = Logger.get(QDR.pluginName);
-  /**
    * @property templatePath
    * @type {string}
    *
    * The top level path to this plugin's partials
    */
-  QDR.srcBase = 'plugin/';
-  QDR.templatePath = QDR.srcBase + 'html/';
+  QDR.templatePath = 'html/';
   /**
    * @property SETTINGS_KEY
    * @type {string}

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0310ac22/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 74ad417..15e73c4 100644
--- a/console/stand-alone/plugin/js/topology/qdrTopology.js
+++ b/console/stand-alone/plugin/js/topology/qdrTopology.js
@@ -1509,7 +1509,7 @@ var QDR = (function(QDR) {
           // add rows to the table for each link
           for (let l=0; l<links.length; l++) {
             if (l>=max_links) {
-              HTMLHeading = '<h4>Top ' + max_links + ' Links</h4>';
+              HTMLHeading = `<h4>Top ${max_links} Links</h4>`;
               break;
             }
             let link = links[l];
@@ -1523,7 +1523,7 @@ var QDR = (function(QDR) {
             let joinedVals = fnJoin(vals, function (v1) {
               return ['</td><td' + (isNaN(+v1) ? '': ' align="right"') + '>', 
QDRService.utilities.pretty(v1 || '0')];
             });
-            HTML += ('<tr><td>' + joinedVals + '</td></tr>');
+            HTML += `<tr><td> ${joinedVals} </td></tr>`;
           }
           HTML += '</table>';
           return HTMLHeading + HTML;

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0310ac22/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
deleted file mode 100644
index e52df67..0000000
--- a/console/stand-alone/plugin/js/topology/traffic.js
+++ /dev/null
@@ -1,440 +0,0 @@
-/*
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you 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.
-*/
-'use strict';
-
-/* global d3 ChordData MIN_CHORD_THRESHOLD Promise */
-
-const transitionDuration = 1000;
-const CHORDFILTERKEY = 'chordFilter';
-
-function Traffic ($scope, $timeout, QDRService, converter, radius, topology, 
nextHop, type, prefix) {
-  $scope.addressColors = {};
-  this.QDRService = QDRService;
-  this.type = type; // moving dots or colored path
-  this.prefix = prefix; // url prefix used in svg url()s
-  this.topology = topology; // contains the list of router nodes
-  this.nextHop = nextHop; // fn that returns the route through the network 
between two routers
-  this.$scope = $scope;
-  this.$timeout = $timeout;
-  // internal variables
-  this.interval = null; // setInterval handle
-  this.setAnimationType(type, converter, radius);
-}
-
-// stop updating the traffic data
-Traffic.prototype.stop = function () {
-  if (this.interval) {
-    clearInterval(this.interval);
-    this.interval = null;
-  }
-};
-// start updating the traffic data
-Traffic.prototype.start = function () {
-  this.doUpdate();
-  this.interval = setInterval(this.doUpdate.bind(this), transitionDuration);
-};
-// remove any animations that are in progress
-Traffic.prototype.remove = function() {
-  if (this.vis)
-    this.vis.remove();
-};
-// called when one of the address checkboxes is toggled
-Traffic.prototype.setAnimationType = function (type, converter, radius) {
-  this.stop();
-  this.remove();
-  this.type = type;
-  this.vis = type === 'dots' ? new Dots(this, converter, radius) :
-    new Congestion(this);
-};
-// called periodically to refresh the traffic flow
-Traffic.prototype.doUpdate = function () {
-  this.vis.doUpdate();
-};
-
-/* Base class for congestion and dots visualizations */
-function TrafficAnimation (traffic) {
-  this.traffic = traffic;
-}
-TrafficAnimation.prototype.nodeIndexFor = function (nodes, name) {
-  for (let i=0; i<nodes.length; i++) {
-    let node = nodes[i];
-    if (node.container === name)
-      return i;
-  }
-  return -1;
-};
-
-/* Color the links between router to show how heavily used the links are. */
-function Congestion (traffic) {
-  TrafficAnimation.call(this, traffic);
-  this.init_markerDef();
-}
-Congestion.prototype = Object.create(TrafficAnimation.prototype);
-Congestion.prototype.constructor = Congestion;
-
-Congestion.prototype.init_markerDef = function () {
-  this.custom_markers_def = d3.select('#SVG_ID').select('defs.custom-markers');
-  if (this.custom_markers_def.empty()) {
-    this.custom_markers_def = 
d3.select('#SVG_ID').append('svg:defs').attr('class', 'custom-markers');
-  }
-};
-Congestion.prototype.findResult = function (node, entity, attribute, value) {
-  let attrIndex = node[entity].attributeNames.indexOf(attribute);
-  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(node[entity].attributeNames, 
node[entity].results[i]);
-      }
-    }
-  }
-  return null;
-};
-Congestion.prototype.doUpdate = function () {
-  let self = this;
-  this.traffic.QDRService.management.topology.ensureAllEntities(
-    [{ entity: 'router.link', force: true},{entity: 'connection'}], function 
() {
-      let links = {};
-      let nodeInfo = self.traffic.QDRService.management.topology.nodeInfo();
-      // accumulate all the inter-router links in an object
-      // keyed by the svgs path id
-      for (let nodeId in nodeInfo) {
-        let node = nodeInfo[nodeId];
-        let nodeLinks = node['router.link'];
-        for (let n=0; n<nodeLinks.results.length; n++) {
-          let link = 
self.traffic.QDRService.utilities.flatten(nodeLinks.attributeNames, 
nodeLinks.results[n]);
-          if (link.linkType !== 'router-control') {
-            let f = self.nodeIndexFor(self.traffic.topology.nodes, 
-              self.traffic.QDRService.management.topology.nameFromId(nodeId));
-            let connection = self.findResult(node, 'connection', 'identity', 
link.connectionId);
-            if (connection) {
-              let t = self.nodeIndexFor(self.traffic.topology.nodes, 
connection.container);
-              let little = Math.min(f, t);
-              let big = Math.max(f, t);
-              let key = ['#path', little, big].join('-');
-              if (!links[key])
-                links[key] = [];
-              links[key].push(link);
-            }
-          }
-        }
-      }
-      // accumulate the colors/directions to be used
-      let colors = {};
-      for (let key in links) {
-        let congestion = self.congestion(links[key]);
-        let path = d3.select(key);
-        if (path && !path.empty()) {
-          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};
-          path
-            .attr('stroke', congestion)
-            .classed('traffic', true)
-            .attr('marker-start', function(d) {
-              return d.left ? 'url(' + self.traffic.prefix + '#' + id + ')' : 
'';
-            })
-            .attr('marker-end', function(d) {
-              return d.right ? 'url(' + self.traffic.prefix + '#' + id + ')' : 
'';
-            });
-        }
-      }
-
-      // create the svg:def that holds the custom markers
-      self.init_markerDef();
-
-      let colorKeys = Object.keys(colors);
-      let custom_markers = self.custom_markers_def.selectAll('marker')
-        .data(colorKeys, function (d) {return d;});
-
-      custom_markers.enter().append('svg:marker')
-        .attr('id', function (d) { return d; })
-        .attr('viewBox', '0 -5 10 10')
-        .attr('refX', function (d) {
-          return colors[d].dir === 'end' ? 24 : (colors[d].small) ? -24 : -14;
-        })
-        .attr('markerWidth', 4)
-        .attr('markerHeight', 4)
-        .attr('orient', 'auto')
-        .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';
-        });
-      custom_markers.exit().remove();
-
-    });
-};
-Congestion.prototype.congestion = function (links) {
-  let v = 0;
-  for (let l=0; l<links.length; l++) {
-    let link = links[l];
-    v = Math.max(v, (link.undeliveredCount+link.unsettledCount)/link.capacity);
-  }
-  return this.fillColor(v);
-};
-Congestion.prototype.fillColor = function (v) {
-  let color = d3.scale.linear().domain([0, 1, 2, 3])
-    .interpolate(d3.interpolateHcl)
-    .range([d3.rgb('#CCCCCC'), d3.rgb('#00FF00'), d3.rgb('#FFA500'), 
d3.rgb('#FF0000')]);
-  return color(v);
-};
-Congestion.prototype.remove = function () {
-  d3.select('#SVG_ID').selectAll('path.traffic')
-    .classed('traffic', false);
-  d3.select('#SVG_ID').select('defs.custom-markers')
-    .selectAll('marker').remove();
-};
-
-/* Create animated dots moving along the links between routers
-   to show message flow */
-function Dots (traffic, converter, radius) {
-  TrafficAnimation.call(this, traffic);
-  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.chordData = new ChordData(this.traffic.QDRService, true, converter); // 
gets ingressHistogram data
-  this.chordData.setFilter(this.excludedAddresses);
-  traffic.$scope.addresses = {};
-  this.chordData.getMatrix().then(function () {
-    this.traffic.$timeout(function () {
-      this.traffic.$scope.addresses = this.chordData.getAddresses();
-      for (let address in this.traffic.$scope.addresses) {
-        this.fillColor(address);
-      }
-    }.bind(this));
-  }.bind(this));
-  // colors
-  this.colorGen = d3.scale.category10();
-  let self = this;
-  // event notification that an address checkbox has changed
-  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) {
-    // 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 () {
-    self.unFadeAll();
-  };
-  // clicked on the address name. toggle the address checkbox
-  traffic.$scope.addressClick = function (address) {
-    self.toggleAddress(address)
-      .then(function () {
-        self.updateAddresses();
-      });
-  };
-}
-Dots.prototype = Object.create(TrafficAnimation.prototype);
-Dots.prototype.constructor = Dots;
-
-Dots.prototype.remove = function () {
-  for (let id in this.lastFlows) {
-    d3.select('#SVG_ID').selectAll('circle.flow' + id).remove();
-  }
-  this.lastFlows = {};
-};
-Dots.prototype.updateAddresses = function () {
-  this.excludedAddresses = [];
-  for (let address in this.traffic.$scope.addresses) {
-    if (!this.traffic.$scope.addresses[address])
-      this.excludedAddresses.push(address);
-  }
-  localStorage[CHORDFILTERKEY] = JSON.stringify(this.excludedAddresses);
-  if (this.chordData)
-    this.chordData.setFilter(this.excludedAddresses);
-  return new Promise(function (resolve) {
-    return resolve();
-  });
-};
-Dots.prototype.toggleAddress = function (address) {
-  this.traffic.$scope.addresses[address] = 
!this.traffic.$scope.addresses[address];
-  return new Promise(function (resolve) {
-    return resolve();
-  });
-};
-Dots.prototype.fadeOtherAddresses = function (address) {
-  d3.selectAll('circle.flow').classed('fade', function (d) {
-    return d.address !== address;
-  });
-};
-Dots.prototype.unFadeAll = function () {
-  d3.selectAll('circle.flow').classed('fade', false);
-};
-Dots.prototype.doUpdate = function () {
-  let self = this;
-  // 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 () {
-    // get the ingressHistogram data for all routers
-    self.chordData.getMatrix().then(self.render.bind(self), function (e) {
-      console.log('Could not get message histogram' + e);
-    });
-  });
-};
-Dots.prototype.render = function (matrix) {
-  this.traffic.$timeout(function () {
-    this.traffic.$scope.addresses = this.chordData.getAddresses();
-  }.bind(this));
-  // get the rate of message flow between routers
-  let hops = {}; // every hop between routers that is involved in message flow
-  let matrixMessages = matrix.matrixMessages();
-  // the fastest traffic rate gets 3 times as many dots as the slowest
-  let minmax = matrix.getMinMax();
-  let flowScale = d3.scale.linear().domain(minmax).range([1, 1.75]);
-  // row is ingress router, col is egress router. Value at [row][col] is the 
rate
-  matrixMessages.forEach(function (row, r) {
-    row.forEach(function (val, c) {
-      if (val > MIN_CHORD_THRESHOLD) {
-        // translate between matrix row/col and node index
-        let f = this.nodeIndexFor(this.traffic.topology.nodes, 
matrix.rows[r].egress);
-        let t = this.nodeIndexFor(this.traffic.topology.nodes, 
matrix.rows[r].ingress);
-        let address = matrix.getAddress(r, c);
-        if (r !== c) {
-          // accumulate the hops between the ingress and egress routers
-          this.traffic.nextHop(this.traffic.topology.nodes[f], 
this.traffic.topology.nodes[t], function (link, fnode, tnode) {
-            let key = '-' + link.uid;
-            let back = fnode.index < tnode.index;
-            if (!hops[key])
-              hops[key] = [];
-            hops[key].push({ val: val, back: back, address: address });
-          });
-        }
-        // Find the senders connected to nodes[f] and the receivers connected 
to nodes[t]
-        // and add their links to the animation
-        this.addClients(hops, this.traffic.topology.nodes, f, val, true, 
address);
-        this.addClients(hops, this.traffic.topology.nodes, t, val, false, 
address);
-      }
-    }.bind(this));
-  }.bind(this));
-  // for each link between routers that has traffic, start an animation
-  let keep = {};
-  for (let id in hops) {
-    let hop = hops[id];
-    for (let h = 0; h < hop.length; h++) {
-      let ahop = hop[h];
-      let flowId = id + '-' + this.addressIndex(this, ahop.address) + 
(ahop.back ? 'b' : '');
-      let path = d3.select('#path' + id);
-      // start the animation. If the animation is already running, this will 
have no effect
-      this.startAnimation(path, flowId, ahop, flowScale(ahop.val));
-      keep[flowId] = true;
-    }
-  }
-  // remove any existing animations that we don't have data for anymore
-  for (let id in this.lastFlows) {
-    if (this.lastFlows[id] && !keep[id]) {
-      this.lastFlows[id] = 0;
-      d3.select('#SVG_ID').selectAll('circle.flow' + id).remove();
-    }
-  }
-};
-// animate the d3 selection (flow) along the given path
-Dots.prototype.animateFlow = function (flow, path, count, back, rate) {
-  let self = this;
-  let l = path.node().getTotalLength();
-  flow.transition()
-    .ease('easeLinear')
-    .duration(l * 10 / rate)
-    .attrTween('transform', self.translateDots(self.radius, path, count, back))
-    .each('end', function () { self.animateFlow(flow, path, count, back, 
rate); });
-};
-// create dots along the path between routers
-Dots.prototype.startAnimation = function (path, id, hop, rate) {
-  if (!path.node())
-    return;
-  this.animateDots(path, id, hop, rate);
-};
-Dots.prototype.animateDots = function (path, id, hop, rate) {
-  let back = hop.back, address = hop.address;
-  // the density of dots is determined by the rate of this traffic relative to 
the other traffic
-  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 };
-  }
-  // keep track of the number of dots for each link. If the length of the link 
is changed,
-  // re-create the animation
-  if (!this.lastFlows[id])
-    this.lastFlows[id] = len;
-  else {
-    if (this.lastFlows[id] !== len) {
-      this.lastFlows[id] = len;
-      d3.select('#SVG_ID').selectAll('circle.flow' + id).remove();
-    }
-  }
-  let flow = d3.select('#SVG_ID').selectAll('circle.flow' + id)
-    .data(dots, function (d) { return d.i + d.address; });
-  let circles = flow
-    .enter().append('circle')
-    .attr('class', 'flow flow' + id)
-    .attr('fill', this.fillColor(address))
-    .attr('r', 5);
-  this.animateFlow(circles, path, dots.length, back, rate);
-  flow.exit()
-    .remove();
-};
-Dots.prototype.fillColor = function (n) {
-  if (!(n in this.traffic.$scope.addressColors)) {
-    let ci = Object.keys(this.traffic.$scope.addressColors).length;
-    this.traffic.$scope.addressColors[n] = this.colorGen(ci);
-  }
-  return this.traffic.$scope.addressColors[n];
-};
-Dots.prototype.addClients = function (hops, nodes, f, val, sender, address) {
-  let cdir = sender ? 'out' : 'in';
-  for (let n=0; n<nodes.length; n++) {
-    let node = nodes[n];
-    if (node.normals && node.key === nodes[f].key && node.cdir === cdir) {
-      let key = ['',f,n].join('-');
-      if (!hops[key])
-        hops[key] = [];
-      hops[key].push({val: val, back: node.cdir === 'in', address: address});
-      return;
-    }
-  }
-};
-Dots.prototype.addressIndex = function (vis, address) {
-  return Object.keys(vis.traffic.$scope.addresses).indexOf(address);
-};
-// calculate the translation for each dot along the path
-Dots.prototype.translateDots = function (radius, path, count, back) {
-  let pnode = path.node();
-  // will be called for each element in the flow selection (for each dot)
-  return function(d) {
-    // will be called with t going from 0 to 1 for each dot
-    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;
-      if (back)
-        f = 1 - f;
-      // l needs to be calculated each tick because the path's length might be 
changed during the animation
-      let l = pnode.getTotalLength();
-      let p = pnode.getPointAtLength(f * l);
-      return 'translate(' + p.x + ',' + p.y + ')';
-    };
-  };
-};

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0310ac22/console/stand-alone/plugin/js/topology/traffic.ts
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/topology/traffic.ts 
b/console/stand-alone/plugin/js/topology/traffic.ts
new file mode 100644
index 0000000..3f1e5c2
--- /dev/null
+++ b/console/stand-alone/plugin/js/topology/traffic.ts
@@ -0,0 +1,443 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.
+*/
+'use strict';
+
+/* global d3 ChordData MIN_CHORD_THRESHOLD Promise */
+declare var d3: any;
+declare var ChordData: any;
+declare var MIN_CHORD_THRESHOLD: number;
+
+const transitionDuration = 1000;
+const CHORDFILTERKEY = 'chordFilter';
+
+class Traffic { // eslint-disable-line no-unused-vars
+  [x: string]: any;
+  constructor($scope, $timeout, QDRService, converter, radius, topology, 
nextHop, type, prefix) {
+    $scope.addressColors = {};
+    this.QDRService = QDRService;
+    this.type = type; // moving dots or colored path
+    this.prefix = prefix; // url prefix used in svg url()s
+    this.topology = topology; // contains the list of router nodes
+    this.nextHop = nextHop; // fn that returns the route through the network 
between two routers
+    this.$scope = $scope;
+    this.$timeout = $timeout;
+    // internal variables
+    this.interval = null; // setInterval handle
+    this.setAnimationType(type, converter, radius);
+  }
+  // stop updating the traffic data
+  stop() {
+    if (this.interval) {
+      clearInterval(this.interval);
+      this.interval = null;
+    }
+  }
+  // start updating the traffic data
+  start() {
+    this.doUpdate();
+    this.interval = setInterval(this.doUpdate.bind(this), transitionDuration);
+  }
+  // remove any animations that are in progress
+  remove() {
+    if (this.vis) {
+      this.vis.remove();
+    }
+  }
+  // called when one of the address checkboxes is toggled
+  setAnimationType(type, converter, radius) {
+    this.stop();
+    this.remove();
+    this.type = type;
+    this.vis = type === 'dots' ? new Dots(this, converter, radius) :
+      new Congestion(this);
+  }
+  // called periodically to refresh the traffic flow
+  doUpdate() {
+    this.vis.doUpdate();
+  }
+}
+
+
+/* Base class for congestion and dots visualizations */
+class TrafficAnimation {
+  [x: string]: any;
+  constructor(traffic) {
+    this.traffic = traffic;
+  }
+  nodeIndexFor(nodes, name) {
+    for (let i = 0; i < nodes.length; i++) {
+      let node = nodes[i];
+      if (node.container === name) {
+        return i;
+      }
+    }
+    return -1;
+  }
+}
+
+/* Color the links between router to show how heavily used the links are. */
+class Congestion extends TrafficAnimation {
+  constructor(traffic) {
+    super(traffic);
+    this.init_markerDef();
+  }
+  init_markerDef() {
+    this.custom_markers_def = 
d3.select('#SVG_ID').select('defs.custom-markers');
+    if (this.custom_markers_def.empty()) {
+      this.custom_markers_def = 
d3.select('#SVG_ID').append('svg:defs').attr('class', 'custom-markers');
+    }
+  }
+  findResult(node, entity, attribute, value) {
+    let attrIndex = node[entity].attributeNames.indexOf(attribute);
+    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(node[entity].attributeNames, 
node[entity].results[i]);
+        }
+      }
+    }
+    return null;
+  }
+  doUpdate() {
+    let self = this;
+    this.traffic.QDRService.management.topology.ensureAllEntities(
+      [{ entity: 'router.link', force: true }, { entity: 'connection' }], 
function () {
+      let links = {};
+      let nodeInfo = self.traffic.QDRService.management.topology.nodeInfo();
+      // accumulate all the inter-router links in an object
+      // keyed by the svgs path id
+      for (const nodeId of Object.keys(nodeInfo)) {
+        let node = nodeInfo[nodeId];
+        let nodeLinks = node['router.link'];
+        for (let n = 0; n < nodeLinks.results.length; n++) {
+          let link = 
self.traffic.QDRService.utilities.flatten(nodeLinks.attributeNames, 
nodeLinks.results[n]);
+          if (link.linkType !== 'router-control') {
+            let f = self.nodeIndexFor(self.traffic.topology.nodes, 
self.traffic.QDRService.management.topology.nameFromId(nodeId));
+            let connection = self.findResult(node, 'connection', 'identity', 
link.connectionId);
+            if (connection) {
+              let t = self.nodeIndexFor(self.traffic.topology.nodes, 
connection.container);
+              let little = Math.min(f, t);
+              let big = Math.max(f, t);
+              let key = ['#path', little, big].join('-');
+              if (!links[key]) {
+                links[key] = [];
+              }
+              links[key].push(link);
+            }
+          }
+        }
+      }
+      // accumulate the colors/directions to be used
+      let colors = {};
+      for (const key of Object.keys(links)) {
+        let congestion = self.congestion(links[key]);
+        let path = d3.select(key);
+        if (path && !path.empty()) {
+          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 };
+          path
+            .attr('stroke', congestion)
+            .classed('traffic', true)
+            .attr('marker-start', function (d) {
+              return d.left ? 'url(' + self.traffic.prefix + '#' + id + ')' : 
'';
+            })
+            .attr('marker-end', function (d) {
+              return d.right ? 'url(' + self.traffic.prefix + '#' + id + ')' : 
'';
+            });
+        }
+      }
+      // create the svg:def that holds the custom markers
+      self.init_markerDef();
+      let colorKeys = Object.keys(colors);
+      let custom_markers = self.custom_markers_def.selectAll('marker')
+        .data(colorKeys, function (d) { return d; });
+      custom_markers.enter().append('svg:marker')
+        .attr('id', function (d) { return d; })
+        .attr('viewBox', '0 -5 10 10')
+        .attr('refX', function (d) {
+          return colors[d].dir === 'end' ? 24 : (colors[d].small) ? -24 : -14;
+        })
+        .attr('markerWidth', 4)
+        .attr('markerHeight', 4)
+        .attr('orient', 'auto')
+        .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';
+        });
+      custom_markers.exit().remove();
+    });
+  }
+  congestion(links) {
+    let v = 0;
+    for (let l = 0; l < links.length; l++) {
+      let link = links[l];
+      v = Math.max(v, (link.undeliveredCount + link.unsettledCount) / 
link.capacity);
+    }
+    return this.fillColor(v);
+  }
+  fillColor(v) {
+    let color = d3.scale.linear().domain([0, 1, 2, 3])
+      .interpolate(d3.interpolateHcl)
+      .range([d3.rgb('#CCCCCC'), d3.rgb('#00FF00'), d3.rgb('#FFA500'), 
d3.rgb('#FF0000')]);
+    return color(v);
+  }
+  remove() {
+    d3.select('#SVG_ID').selectAll('path.traffic')
+      .classed('traffic', false);
+    d3.select('#SVG_ID').select('defs.custom-markers')
+      .selectAll('marker').remove();
+  }
+}
+
+/* Create animated dots moving along the links between routers
+   to show message flow */
+class Dots extends TrafficAnimation {
+  constructor(traffic, converter, radius) {
+    super(traffic);
+    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.chordData = new ChordData(this.traffic.QDRService, true, converter); 
// gets ingressHistogram data
+    this.chordData.setFilter(this.excludedAddresses);
+    traffic.$scope.addresses = {};
+    this.chordData.getMatrix().then(function () {
+      this.traffic.$timeout(function () {
+        this.traffic.$scope.addresses = this.chordData.getAddresses();
+        for (const address of Object.keys(this.traffic.$scope.addresses)) {
+          this.fillColor(address);
+        }
+      }.bind(this));
+    }.bind(this));
+    // colors
+    this.colorGen = d3.scale.category10();
+    let self = this;
+    // event notification that an address checkbox has changed
+    traffic.$scope.addressFilterChanged = function () {
+      self.updateAddresses();
+      // 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) {
+      // 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 () {
+      self.unFadeAll();
+    };
+    // clicked on the address name. toggle the address checkbox
+    traffic.$scope.addressClick = function (address) {
+      self.toggleAddress(address);
+      self.updateAddresses();
+    };
+  }
+  remove() {
+    for (const id of Object.keys(this.lastFlows)) {
+      d3.select('#SVG_ID').selectAll('circle.flow' + id).remove();
+    }
+    this.lastFlows = {};
+  }
+  updateAddresses() {
+    this.excludedAddresses = [];
+    for (let address in this.traffic.$scope.addresses) {
+      if (!this.traffic.$scope.addresses[address]) {
+        this.excludedAddresses.push(address);
+      }
+    }
+    localStorage[CHORDFILTERKEY] = JSON.stringify(this.excludedAddresses);
+    if (this.chordData) {
+      this.chordData.setFilter(this.excludedAddresses);
+    }
+  }
+  toggleAddress(address) {
+    this.traffic.$scope.addresses[address] = 
!this.traffic.$scope.addresses[address];
+  }
+  fadeOtherAddresses(address) {
+    d3.selectAll('circle.flow').classed('fade', function (d) {
+      return d.address !== address;
+    });
+  }
+  unFadeAll() {
+    d3.selectAll('circle.flow').classed('fade', false);
+  }
+  doUpdate() {
+    let self = this;
+    // 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 () {
+      // get the ingressHistogram data for all routers
+      self.chordData.getMatrix().then(self.render.bind(self), function (e) {
+        console.log('Could not get message histogram' + e);
+      });
+    });
+  }
+  render(matrix) {
+    this.traffic.$timeout(function () {
+      this.traffic.$scope.addresses = this.chordData.getAddresses();
+    }.bind(this));
+    // get the rate of message flow between routers
+    let hops = {}; // every hop between routers that is involved in message 
flow
+    let matrixMessages = matrix.matrixMessages();
+    // the fastest traffic rate gets 3 times as many dots as the slowest
+    let minmax = matrix.getMinMax();
+    let flowScale = d3.scale.linear().domain(minmax).range([1, 1.75]);
+    // row is ingress router, col is egress router. Value at [row][col] is the 
rate
+    matrixMessages.forEach(function (row, r) {
+      row.forEach(function (val, c) {
+        if (val > MIN_CHORD_THRESHOLD) {
+          // translate between matrix row/col and node index
+          let f = this.nodeIndexFor(this.traffic.topology.nodes, 
matrix.rows[r].egress);
+          let t = this.nodeIndexFor(this.traffic.topology.nodes, 
matrix.rows[r].ingress);
+          let address = matrix.getAddress(r, c);
+          if (r !== c) {
+            // accumulate the hops between the ingress and egress routers
+            this.traffic.nextHop(this.traffic.topology.nodes[f], 
this.traffic.topology.nodes[t], function (link, fnode, tnode) {
+              let key = '-' + link.uid;
+              let back = fnode.index < tnode.index;
+              if (!hops[key]) {
+                hops[key] = [];
+              }
+              hops[key].push({ val: val, back: back, address: address });
+            });
+          }
+          // Find the senders connected to nodes[f] and the receivers 
connected to nodes[t]
+          // and add their links to the animation
+          this.addClients(hops, this.traffic.topology.nodes, f, val, true, 
address);
+          this.addClients(hops, this.traffic.topology.nodes, t, val, false, 
address);
+        }
+      }.bind(this));
+    }.bind(this));
+    // for each link between routers that has traffic, start an animation
+    let keep = {};
+    for (const id of Object.keys(hops)) {
+      let hop = hops[id];
+      for (let h = 0; h < hop.length; h++) {
+        let ahop = hop[h];
+        let flowId = id + '-' + this.addressIndex(this, ahop.address) + 
(ahop.back ? 'b' : '');
+        let path = d3.select('#path' + id);
+        // start the animation. If the animation is already running, this will 
have no effect
+        this.startAnimation(path, flowId, ahop, flowScale(ahop.val));
+        keep[flowId] = true;
+      }
+    }
+    // remove any existing animations that we don't have data for anymore
+    for (let id in this.lastFlows) {
+      if (this.lastFlows[id] && !keep[id]) {
+        this.lastFlows[id] = 0;
+        d3.select('#SVG_ID').selectAll('circle.flow' + id).remove();
+      }
+    }
+  }
+  // animate the d3 selection (flow) along the given path
+  animateFlow(flow, path, count, back, rate) {
+    let self = this;
+    let l = path.node().getTotalLength();
+    flow.transition()
+      .ease('easeLinear')
+      .duration(l * 10 / rate)
+      .attrTween('transform', self.translateDots(self.radius, path, count, 
back))
+      .each('end', function () { self.animateFlow(flow, path, count, back, 
rate); });
+  }
+  // create dots along the path between routers
+  startAnimation(path, id, hop, rate) {
+    if (!path.node()) {
+      return;
+    }
+    this.animateDots(path, id, hop, rate);
+  }
+  animateDots(path, id, hop, rate) {
+    let back = hop.back, address = hop.address;
+    // the density of dots is determined by the rate of this traffic relative 
to the other traffic
+    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 };
+    }
+    // keep track of the number of dots for each link. If the length of the 
link is changed,
+    // re-create the animation
+    if (!this.lastFlows[id]) {
+      this.lastFlows[id] = len;
+    } else {
+      if (this.lastFlows[id] !== len) {
+        this.lastFlows[id] = len;
+        d3.select('#SVG_ID').selectAll('circle.flow' + id).remove();
+      }
+    }
+    let flow = d3.select('#SVG_ID').selectAll('circle.flow' + id)
+      .data(dots, function (d) { return d.i + d.address; });
+    let circles = flow
+      .enter().append('circle')
+      .attr('class', 'flow flow' + id)
+      .attr('fill', this.fillColor(address))
+      .attr('r', 5);
+    this.animateFlow(circles, path, dots.length, back, rate);
+    flow.exit()
+      .remove();
+  }
+  fillColor(n) {
+    if (!(n in this.traffic.$scope.addressColors)) {
+      let ci = Object.keys(this.traffic.$scope.addressColors).length;
+      this.traffic.$scope.addressColors[n] = this.colorGen(ci);
+    }
+    return this.traffic.$scope.addressColors[n];
+  }
+  addClients(hops, nodes, f, val, sender, address) {
+    let cdir = sender ? 'out' : 'in';
+    for (let n = 0; n < nodes.length; n++) {
+      let node = nodes[n];
+      if (node.normals && node.key === nodes[f].key && node.cdir === cdir) {
+        let key = ['', f, n].join('-');
+        if (!hops[key]) {
+          hops[key] = [];
+        }
+        hops[key].push({ val: val, back: node.cdir === 'in', address: address 
});
+        return;
+      }
+    }
+  }
+  addressIndex(vis, address) {
+    return Object.keys(vis.traffic.$scope.addresses).indexOf(address);
+  }
+  // calculate the translation for each dot along the path
+  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) {
+      // will be called with t going from 0 to 1 for each dot
+      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;
+        if (back) {
+          f = 1 - f;
+        }
+        // l needs to be calculated each tick because the path's length might 
be changed during the animation
+        let l = pnode.getTotalLength();
+        let p = pnode.getPointAtLength(f * l);
+        return 'translate(' + p.x + ',' + p.y + ')';
+      };
+    };
+  }
+}
+
+

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0310ac22/console/stand-alone/tsconfig.json
----------------------------------------------------------------------
diff --git a/console/stand-alone/tsconfig.json 
b/console/stand-alone/tsconfig.json
new file mode 100644
index 0000000..056e773
--- /dev/null
+++ b/console/stand-alone/tsconfig.json
@@ -0,0 +1,31 @@
+{
+  "compilerOptions": {
+    "allowJs": true,
+    "checkJs": true,
+    "lib": [
+      "dom",
+      "dom.iterable",
+      "es5",
+      "es6",
+      "es7",
+      "esnext",
+      "esnext.asynciterable",
+      "es2015.iterable",
+      "es2017"
+    ]
+  },
+  "compileOnSave": true,
+  "include": [
+    "plugin/*"
+  ],
+  "exclude": [
+    "node_modules/*"
+  ],
+  "typeAcquisition": {
+    "enable": true
+  },
+  "files": [
+    "plugin/**/*.ts"
+  ]
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0310ac22/console/stand-alone/tslint.json
----------------------------------------------------------------------
diff --git a/console/stand-alone/tslint.json b/console/stand-alone/tslint.json
new file mode 100644
index 0000000..ad645d3
--- /dev/null
+++ b/console/stand-alone/tslint.json
@@ -0,0 +1,64 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.
+*/
+{
+  "rules": {
+    "class-name": true,
+    "curly": true,
+    "eofline": false,
+    "forin": true,
+    "indent": [true, 4],
+    "label-position": true,
+    "max-line-length": [true, 140],
+    "no-arg": true,
+    "no-bitwise": true,
+    "no-console": [true,
+      "debug",
+      "info",
+      "time",
+      "timeEnd",
+      "trace"
+    ],
+    "no-construct": true,
+    "no-debugger": true,
+    "no-duplicate-variable": true,
+    "no-empty": true,
+    "no-eval": true,
+    "no-string-literal": false,
+    "no-trailing-whitespace": true,
+    "no-unused-variable": false,
+    //"no-use-before-declare": true,
+    "one-line": [true,
+      "check-open-brace",
+      "check-catch",
+      "check-else",
+      "check-whitespace"
+    ],
+    "quotemark": [true, "single"],
+    "radix": true,
+    "semicolon": true,
+    "triple-equals": [true, "allow-null-check"],
+    "variable-name": false,
+    "whitespace": [true,
+      "check-branch",
+      "check-decl",
+      "check-operator",
+      "check-separator"
+    ]
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0310ac22/console/stand-alone/vendor-css.txt
----------------------------------------------------------------------
diff --git a/console/stand-alone/vendor-css.txt 
b/console/stand-alone/vendor-css.txt
new file mode 100644
index 0000000..333e38c
--- /dev/null
+++ b/console/stand-alone/vendor-css.txt
@@ -0,0 +1,25 @@
+- Licensed to the Apache Software Foundation (ASF) under one
+- or more contributor license agreements.  See the NOTICE file
+- distributed with this work for additional information
+- regarding copyright ownership.  The ASF licenses this file
+- to you 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.
+
+-- The following files get packaged into a single vendor.min.css
+
+node_modules/jquery-ui-dist/jquery-ui.css
+node_modules/patternfly/dist/css/patternfly.min.css
+node_modules/patternfly/dist/css/patternfly-additions.min.css
+node_modules/jquery.fancytree/dist/skin-bootstrap-n/ui.fancytree.css
+node_modules/c3/c3.css
+node_modules/angular-ui-grid/ui-grid.css

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0310ac22/console/stand-alone/vendor-js.txt
----------------------------------------------------------------------
diff --git a/console/stand-alone/vendor-js.txt 
b/console/stand-alone/vendor-js.txt
new file mode 100644
index 0000000..5af21f4
--- /dev/null
+++ b/console/stand-alone/vendor-js.txt
@@ -0,0 +1,43 @@
+- Licensed to the Apache Software Foundation (ASF) under one
+- or more contributor license agreements.  See the NOTICE file
+- distributed with this work for additional information
+- regarding copyright ownership.  The ASF licenses this file
+- to you 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.
+
+-- The following files get packaged into a single vendor.min.js
+
+node_modules/bluebird/js/browser/bluebird.min.js
+node_modules/jquery/dist/jquery.min.js
+node_modules/jquery-ui-dist/jquery-ui.min.js
+node_modules/jquery.fancytree/dist/jquery.fancytree-all.min.js
+node_modules/angular/angular.min.js
+node_modules/angular-animate/angular-animate.min.js
+node_modules/angular-sanitize/angular-sanitize.min.js
+node_modules/angular-route/angular-route.min.js
+node_modules/angular-resource/angular-resource.min.js
+node_modules/bootstrap/dist/js/bootstrap.min.js
+node_modules/angular-ui-bootstrap/dist/ui-bootstrap.js
+node_modules/angular-ui-bootstrap/dist/ui-bootstrap-tpls.js
+node_modules/d3/d3.min.js
+node_modules/d3-queue/build/d3-queue.min.js
+node_modules/d3-time/build/d3-time.min.js
+node_modules/d3-time-format/build/d3-time-format.min.js
+node_modules/d3-path/build/d3-path.min.js
+node_modules/c3/c3.min.js
+node_modules/angular-ui-slider/src/slider.js
+node_modules/angular-ui-grid/ui-grid.min.js
+node_modules/angular-bootstrap-checkbox/angular-bootstrap-checkbox.js
+node_modules/notifyjs-browser/dist/notify.js
+node_modules/patternfly/dist/js/patternfly.min.js
+node_modules/dispatch-management/dist/dispatch-management.min.js


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to