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/06/25 21:26:47 UTC
[1/8] qpid-dispatch git commit: DISPATCH-1049 Add console tests
Repository: qpid-dispatch
Updated Branches:
refs/heads/master af997544e -> b5deb0357
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/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
new file mode 100644
index 0000000..48d4cf8
--- /dev/null
+++ b/console/stand-alone/plugin/js/topology/topoUtils.js
@@ -0,0 +1,225 @@
+/*
+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.
+*/
+// highlight the paths between the selected node and the hovered node
+function findNextHopNode(from, d, QDRService, selected_node, nodes) {
+ // d is the node that the mouse is over
+ // from is the selected_node ....
+ if (!from)
+ return null;
+
+ if (from == d)
+ return selected_node;
+
+ let sInfo = QDRService.management.topology.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;
+ for (let hIdx = 0; hIdx < vAr.length; ++hIdx) {
+ let addrT = QDRService.utilities.valFor(aAr, vAr[hIdx], 'id');
+ if (addrT == d.name) {
+ let next = QDRService.utilities.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);
+ if (target) {
+ let hnode = nodes.nodeFor(thisNode.name);
+ let hlLink = links.linkFor(hnode, target);
+ if (hlLink) {
+ if (cb) {
+ cb(hlLink, hnode, target);
+ }
+ }
+ else
+ target = null;
+ }
+ nextHop(target, d, nodes, links, QDRService, selected_node, cb);
+ }
+}
+
+export function connectionPopupHTML (d, QDRService) {
+ let getConnsArray = function (d, conn) {
+ let conns = [conn];
+ if (d.cls === 'small') {
+ conns = [];
+ let normals = d.target.normals ? d.target.normals : d.source.normals;
+ for (let n=0; n<normals.length; n++) {
+ if (normals[n].resultIndex !== undefined) {
+ conns.push(QDRService.utilities.flatten(onode['connection'].attributeNames,
+ onode['connection'].results[normals[n].resultIndex]));
+ }
+ }
+ }
+ return conns;
+ };
+ // 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, conn, d) {
+ const max_links = 10;
+ const fields = ['undelivered', 'unsettled', 'rejected', 'released', 'modified'];
+ // 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;
+ }
+ return false;
+ };
+ let fnJoin = function (ar, sepfn) {
+ let out = '';
+ out = ar[0];
+ for (let i=1; i<ar.length; i++) {
+ let sep = sepfn(ar[i]);
+ out += (sep[0] + sep[1]);
+ }
+ return out;
+ };
+ let conns = getConnsArray(d, conn);
+ // 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 links = [];
+ let hasAddress = false;
+ for (let n=0; n<nodeLinks.results.length; n++) {
+ let link = QDRService.utilities.flatten(nodeLinks.attributeNames, nodeLinks.results[n]);
+ if (link.linkType !== 'router-control') {
+ if (isLinkFor(link.connectionId, conns)) {
+ if (link.owningAddr)
+ hasAddress = true;
+ links.push(link);
+ }
+ }
+ }
+ // we may need to limit the number of links displayed, so sort descending by the sum of the field values
+ links.sort( function (a, b) {
+ let asum = a.undeliveredCount + a.unsettledCount + a.rejectedCount + a.releasedCount + a.modifiedCount;
+ let bsum = b.undeliveredCount + b.unsettledCount + b.rejectedCount + b.releasedCount + b.modifiedCount;
+ return asum < bsum ? 1 : asum > bsum ? -1 : 0;
+ });
+ let HTMLHeading = '<h5>Links</h5>';
+ let HTML = '<table class="popupTable">';
+ // copy of fields since we may be prepending an address
+ let th = fields.slice();
+ // convert to actual attribute names
+ let td = fields.map( function (f) {return f + 'Count';});
+ th.unshift('dir');
+ td.unshift('linkDir');
+ // add an address field if any of the links had an owningAddress
+ if (hasAddress) {
+ th.unshift('address');
+ td.unshift('owningAddr');
+ }
+ HTML += ('<tr class="header"><td>' + 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) {
+ HTMLHeading = `<h4>Top ${max_links} Links</h4>`;
+ break;
+ }
+ let link = links[l];
+ let vals = td.map( function (f) {
+ if (f === 'owningAddr') {
+ let identity = QDRService.utilities.identity_clean(link.owningAddr);
+ return QDRService.utilities.addr_text(identity);
+ }
+ return link[f];
+ });
+ 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 += '</table>';
+ return HTMLHeading + HTML;
+ };
+ let left = d.left ? d.source : d.target;
+ // left is the connection with dir 'in'
+ let right = d.left ? d.target : d.source;
+ let onode = QDRService.management.topology.nodeInfo()[left.key];
+ let connSecurity = function (conn) {
+ if (!conn.isEncrypted)
+ return 'no-security';
+ if (conn.sasl === 'GSSAPI')
+ return 'Kerberos';
+ return conn.sslProto + '(' + conn.sslCipher + ')';
+ };
+ let connAuth = function (conn) {
+ if (!conn.isAuthenticated)
+ return 'no-auth';
+ let sasl = conn.sasl;
+ if (sasl === 'GSSAPI')
+ sasl = 'Kerberos';
+ else if (sasl === 'EXTERNAL')
+ sasl = 'x.509';
+ else if (sasl === 'ANONYMOUS')
+ return 'anonymous-user';
+ if (!conn.user)
+ return sasl;
+ return conn.user + '(' + sasl + ')';
+ };
+ let connTenant = function (conn) {
+ if (!conn.tenant) {
+ return '';
+ }
+ if (conn.tenant.length > 1)
+ return conn.tenant.replace(/\/$/, '');
+ };
+ // loop through all the connections for left, and find the one for right
+ let rightIndex = onode['connection'].results.findIndex( function (conn) {
+ return QDRService.utilities.valFor(onode['connection'].attributeNames, conn, 'container') === right.routerId;
+ });
+ if (rightIndex < 0) {
+ // we have a connection to a client/service
+ rightIndex = +left.resultIndex;
+ }
+ if (isNaN(rightIndex)) {
+ // we have a connection to a console
+ rightIndex = +right.resultIndex;
+ }
+ let HTML = '';
+ if (rightIndex >= 0) {
+ let conn = onode['connection'].results[rightIndex];
+ conn = QDRService.utilities.flatten(onode['connection'].attributeNames, conn);
+ let conns = getConnsArray(d, conn);
+ if (conns.length === 1) {
+ 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<conns.length; c++) {
+ HTML += ('<tr><td>' + connSecurity(conns[c]) + '</td>');
+ HTML += ('<td>' + connAuth(conns[c]) + '</td>');
+ HTML += ('<td>' + (connTenant(conns[c]) || '--') + '</td>');
+ HTML += ('<td>' + conns[c].host + '</td>');
+ HTML += '</tr>';
+ }
+ HTML += '</table>';
+ }
+ HTML += linksHTML(onode, conn, d);
+ }
+ return HTML;
+}
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/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
new file mode 100644
index 0000000..3778c4e
--- /dev/null
+++ b/console/stand-alone/plugin/js/topology/traffic.js
@@ -0,0 +1,445 @@
+/*
+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.
+*/
+
+/* global d3 Promise */
+
+import { ChordData } from '../chord/data.js';
+import { MIN_CHORD_THRESHOLD } from '../chord/matrix.js';
+import { nextHop } from './topoUtils.js';
+
+const transitionDuration = 1000;
+const CHORDFILTERKEY = 'chordFilter';
+
+export class Traffic { // eslint-disable-line no-unused-vars
+ constructor($scope, $timeout, QDRService, converter, radius, topology, 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.$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 {
+ 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 (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.nodes, self.traffic.QDRService.utilities.nameFromId(nodeId));
+ let connection = self.findResult(node, 'connection', 'identity', link.connectionId);
+ if (connection) {
+ let t = self.nodeIndexFor(self.traffic.topology.nodes.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(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(Math.max(0, Math.min(3, 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 (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();
+ });
+ };
+ }
+ remove() {
+ for (let id in 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);
+ return new Promise(function (resolve) {
+ return resolve();
+ });
+ }
+ toggleAddress(address) {
+ this.traffic.$scope.addresses[address] = !this.traffic.$scope.addresses[address];
+ return new Promise(function (resolve) {
+ return resolve();
+ });
+ }
+ 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.nodes, matrix.rows[r].egress);
+ let t = this.nodeIndexFor(this.traffic.topology.nodes.nodes, matrix.rows[r].ingress);
+ let address = matrix.getAddress(r, c);
+ if (r !== c) {
+ // accumulate the hops between the ingress and egress routers
+ nextHop(this.traffic.topology.nodes.nodes[f],
+ this.traffic.topology.nodes.nodes[t],
+ this.traffic.topology.nodes,
+ this.traffic.topology.links,
+ this.traffic.QDRService,
+ this.traffic.topology.nodes.nodes[f],
+ 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.nodes, f, val, true, address);
+ this.addClients(hops, this.traffic.topology.nodes.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
+ 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/b5deb035/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
deleted file mode 100644
index 3f1e5c2..0000000
--- a/console/stand-alone/plugin/js/topology/traffic.ts
+++ /dev/null
@@ -1,443 +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 */
-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/b5deb035/console/stand-alone/test/filter.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/test/filter.js b/console/stand-alone/test/filter.js
new file mode 100644
index 0000000..d72915c
--- /dev/null
+++ b/console/stand-alone/test/filter.js
@@ -0,0 +1,73 @@
+/*
+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.
+*/
+var assert = require('assert');
+import { aggregateAddresses, separateAddresses } from '../plugin/js/chord/filters.js';
+/* global describe it */
+
+describe('Filters', function() {
+ const MIN_VALUE = 1,
+ MAX_VALUE = 2;
+ const values = [
+ {ingress: 'A', egress: 'B', address: 'toB', messages: MIN_VALUE, key: 'BAtoB'},
+ {ingress: 'B', egress: 'A', address: 'toA', messages: MAX_VALUE, key: 'ABtoA'}
+ ];
+
+ describe('#aggregateAddresses', function() {
+ let m = aggregateAddresses(values, []);
+ it('should create a matrix', function() {
+ assert.ok(m.hasValues());
+ });
+ it('that has two rows', function() {
+ assert.equal(m.rows.length, 2);
+ });
+ it('and has 1 chord per router', function() {
+ assert.equal(m.getChordList().length, 2);
+ });
+ it('and contains all addresses when there is no filter', function () {
+ let minmax = m.getMinMax();
+ assert.ok(minmax[0] === MIN_VALUE && minmax[1] === MAX_VALUE);
+ });
+ it('should filter out an address', function () {
+ let m = aggregateAddresses(values, ['toB']);
+ // if the toB address was filtered, the min value in the matrix should be 2 (for the toA address)
+ assert.equal(m.getMinMax()[0], MAX_VALUE);
+ });
+ });
+ describe('#separateAddresses', function() {
+ let m = separateAddresses(values, []);
+ it('should create a matrix', function() {
+ assert.ok(m.hasValues());
+ });
+ it('that has a row per router/address combination', function() {
+ assert.equal(m.rows.length, 4);
+ });
+ it('and has 1 chord per router/address combination', function() {
+ assert.equal(m.getChordList().length, 4);
+ });
+ it('and contains all addresses when there is no filter', function () {
+ let minmax = m.getMinMax();
+ assert.ok(minmax[0] === MIN_VALUE && minmax[1] === MAX_VALUE);
+ });
+ it('should filter out an address', function () {
+ let m = separateAddresses(values, ['toB']);
+ // if the toB address was filtered, the min value in the matrix should be 2 (for the toA address)
+ assert.equal(m.getMinMax()[0], MAX_VALUE);
+ });
+ });
+});
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/test/links.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/test/links.js b/console/stand-alone/test/links.js
new file mode 100644
index 0000000..f07482f
--- /dev/null
+++ b/console/stand-alone/test/links.js
@@ -0,0 +1,82 @@
+/*
+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.
+*/
+/* global describe it before */
+var assert = require('assert');
+var fs = require('fs');
+var expect = require('chai').expect;
+import { Links } from '../plugin/js/topology/links.js';
+import { Nodes } from '../plugin/js/topology/nodes.js';
+import { QDRService } from '../plugin/js/qdrService.js';
+
+class Log {
+ constructor() {
+ }
+ log (msg) {}
+ debug (msg) {}
+ error (msg) {}
+ info (msg) {}
+ warn (msg) {}
+}
+var log = new Log();
+var loc = {protocol: function () { return 'http://';}};
+var timeout = {};
+var qdrService = new QDRService(log, timeout, loc);
+var links = new Links(qdrService, log);
+var nodes = new Nodes(qdrService, log);
+var nodeInfo;
+var unknowns = [];
+const width = 1024;
+const height = 768;
+
+before(function(done){
+ fs.readFile('./test/nodes.json', 'utf8', function(err, fileContents) {
+ if (err) throw err;
+ nodeInfo = JSON.parse(fileContents);
+ done();
+ });
+});
+
+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);
+ assert.equal(nodes.nodes.length, 6);
+ });
+ });
+
+});
+describe('Links', function() {
+ describe('#exists', function() {
+ it('should exist', function() {
+ expect(links).to.exist;
+ });
+ });
+ describe('#initializes', function() {
+ it('should initialize', function() {
+ links.initializeLinks(nodeInfo, nodes, unknowns, {}, width);
+ assert.equal(links.links.length, 10);
+ });
+ });
+
+});
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/test/matrix.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/test/matrix.js b/console/stand-alone/test/matrix.js
new file mode 100644
index 0000000..449273a
--- /dev/null
+++ b/console/stand-alone/test/matrix.js
@@ -0,0 +1,51 @@
+/*
+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.
+*/
+var assert = require('assert');
+import { valuesMatrix } from '../plugin/js/chord/matrix.js';
+/* global describe it */
+
+describe('Matrix', function() {
+ describe('#zeroInit', function() {
+ const ROW_COUNT = 10;
+ let matrix = new valuesMatrix(false);
+ matrix.zeroInit(ROW_COUNT);
+ it('should create the requested number of rows', function() {
+ assert.equal(matrix.rows.length, ROW_COUNT);
+ });
+ it('should create the requested number of cols per row', function() {
+ let allEqual = true;
+ matrix.rows.forEach( function (row) {
+ allEqual = allEqual && row.cols.length === ROW_COUNT;
+ });
+ assert.ok(allEqual);
+ });
+ });
+ describe('#hasValues', function () {
+ it('should not have any values to start', function () {
+ let matrix = new valuesMatrix(false);
+ assert.ok(!matrix.hasValues());
+ });
+ it('should have a value after adding one', function () {
+ let matrix = new valuesMatrix(false);
+ matrix.addRow('chordName', 'ingress', 'egress', 'address');
+ matrix.addValue(0, 0, {messages: 1234, address: 'address'});
+ assert.ok(matrix.hasValues());
+ });
+ });
+});
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/test/nodes.json
----------------------------------------------------------------------
diff --git a/console/stand-alone/test/nodes.json b/console/stand-alone/test/nodes.json
new file mode 100644
index 0000000..7aa7631
--- /dev/null
+++ b/console/stand-alone/test/nodes.json
@@ -0,0 +1 @@
+{"amqp:/_topo/0/D/$management":{"router.node":{"results":[["D","(self)"],["C",null],["A","C"],["B","C"],["E",null],["F",null]],"attributeNames":["id","nextHop"]},"connection":{"attributeNames":["name","identity","host","role","dir","container","sasl","isAuthenticated","user","isEncrypted","sslProto","sslCipher","properties","sslSsf","tenant","type","ssl","opened"],"results":[["connection/127.0.0.1:43430","3","127.0.0.1:43430","inter-router","in","C","ANONYMOUS",true,"anonymous",false,null,null,{"product":"qpid-dispatch-router","version":"1.2.0-SNAPSHOT"},0,null,"org.apache.qpid.dispatch.connection",false,true],["connection/0.0.0.0:2002","4","0.0.0.0:2002","inter-router","out","E","ANONYMOUS",true,null,false,null,null,{"product":"qpid-dispatch-router","version":"1.2.0-SNAPSHOT"},0,null,"org.apache.qpid.dispatch.connection",false,true],["connection/0.0.0.0:2003","5","0.0.0.0:2003","inter-router","out","F","ANONYMOUS",true,null,false,null,null,{"product":"qpid-dispatch-router","version
":"1.2.0-SNAPSHOT"},0,null,"org.apache.qpid.dispatch.connection",false,true],["connection/127.0.0.1","35","127.0.0.1","normal","in","3df1ec8f-c39d-ce46-bb71-939b171b9c3d",null,false,"anonymous",false,null,null,{"console_identifier":"Dispatch console"},0,null,"org.apache.qpid.dispatch.connection",false,true],["connection/127.0.0.1","37","127.0.0.1","normal","in","a3aa9724-cc48-b442-90e2-7ee59965825e",null,false,"anonymous",false,null,null,{"console_identifier":"Dispatch console"},0,null,"org.apache.qpid.dispatch.connection",false,true]]},"router.link":{"attributeNames":["name","identity","type","linkName","linkType","linkDir","owningAddr","capacity","peer","undeliveredCount","unsettledCount","deliveryCount","connectionId","adminStatus","operStatus","presettledCount","droppedPresettledCount","acceptedCount","rejectedCount","releasedCount","modifiedCount","ingressHistogram"],"results":[["qdlink.iiY_WYQqdKOZjjf","1","org.apache.qpid.dispatch.router.link","qdlink.iiY_WYQqdKOZjjf","router
-control","out","Lqdhello",250,null,0,0,69650,"3","enabled","up",69650,0,0,0,0,0,null],["qdlink.ad3E15bOZ4387CG","2","org.apache.qpid.dispatch.router.link","qdlink.ad3E15bOZ4387CG","router-control","in",null,250,null,0,0,69643,"3","enabled","up",69643,0,0,0,0,0,null],["qdlink.RfxRiUQpPAmV7ow","3","org.apache.qpid.dispatch.router.link","qdlink.RfxRiUQpPAmV7ow","inter-router","out",null,250,null,0,224,214732069,"3","enabled","up",0,0,214731845,0,0,0,null],["qdlink.0x5Gje+NwvMzFpo","4","org.apache.qpid.dispatch.router.link","qdlink.0x5Gje+NwvMzFpo","inter-router","in",null,250,null,0,232,214183382,"3","enabled","up",94314,0,214088836,0,0,0,null],["qdlink.g8g0G5os5DBBLZt","5","org.apache.qpid.dispatch.router.link","qdlink.g8g0G5os5DBBLZt","router-control","in",null,250,null,0,0,65399,"4","enabled","up",65399,0,0,0,0,0,null],["qdlink.GNu2meWTw_cQnmz","6","org.apache.qpid.dispatch.router.link","qdlink.GNu2meWTw_cQnmz","router-control","out","Lqdhello",250,null,0,0,73850,"4","enabled","up"
,73850,0,0,0,0,0,null],["qdlink.uCAaTkz40dYjyzL","7","org.apache.qpid.dispatch.router.link","qdlink.uCAaTkz40dYjyzL","inter-router","in",null,250,null,0,0,23717,"4","enabled","up",23717,0,0,0,0,0,null],["qdlink.7IApVdoiBsie1s4","8","org.apache.qpid.dispatch.router.link","qdlink.7IApVdoiBsie1s4","inter-router","out",null,250,null,0,0,23717,"4","enabled","up",0,0,23717,0,0,0,null],["qdlink.WhYVBxoYjPd28OH","9","org.apache.qpid.dispatch.router.link","qdlink.WhYVBxoYjPd28OH","router-control","in",null,250,null,0,0,65412,"5","enabled","up",65412,0,0,0,0,0,null],["qdlink.ZforTKcJOgvL1rw","10","org.apache.qpid.dispatch.router.link","qdlink.ZforTKcJOgvL1rw","router-control","out","Lqdhello",250,null,0,0,73851,"5","enabled","up",73851,0,0,0,0,0,null],["qdlink.g2xEaALSGR7GbpB","11","org.apache.qpid.dispatch.router.link","qdlink.g2xEaALSGR7GbpB","inter-router","in",null,250,null,0,223,214661483,"5","enabled","up",23729,0,214637531,0,0,0,null],["qdlink.gKc+W6yyLIBAvIH","12","org.apache.qpid.dis
patch.router.link","qdlink.gKc+W6yyLIBAvIH","inter-router","out",null,250,null,0,232,214112797,"5","enabled","up",0,0,214112565,0,0,0,null],["eef4b65e-88b0-ee48-ae83-714ff4b9483e","41","org.apache.qpid.dispatch.router.link","eef4b65e-88b0-ee48-ae83-714ff4b9483e","endpoint","out","Ltemp.s_bSvFZ+MBL6Tgt",250,null,0,0,817,"35","enabled","up",817,0,0,0,0,0,[227,210,58,58,58,58,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]],["d2636e9c-fa68-8245-991d-3d45a7d32f05","42","org.apache.qpid.dispatch.router.link","d2636e9c-fa68-8245-991d-3d45a7d32f05","endpoint","in",null,250,null,0,0,817,"35","enabled","up",0,0,817,0,0,0,null],["30a1137c-5f31-2b44-b02c-6701d89f50c7","43","org.apache.qpid.dispatch.router.link","30a1137c-5f31-2b44-b02c-6701d89f50c7","endpoint","out","Ltemp.M+hukZfxvBQl62r",250,null,0
,0,120,"37","enabled","up",120,0,0,0,0,0,[36,31,7,7,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]],["e524f82a-e2dd-854d-9e7f-620b0dad4e47","44","org.apache.qpid.dispatch.router.link","e524f82a-e2dd-854d-9e7f-620b0dad4e47","endpoint","in",null,250,null,0,2,123,"37","enabled","up",0,0,121,0,0,0,null]]},"router":{"attributeNames":["name","version","name"],"results":[["D","1.2.0-SNAPSHOT","D"]]},"listener":{"results":[["normal","5673",true],["inter-router","2001",false]],"attributeNames":["role","port","http"]}},"amqp:/_topo/0/C/$management":{"router.node":{"results":[["C","(self)"],["A",null],["B",null],["D",null],["E","D"],["F","D"]],"attributeNames":["id","nextHop"]},"connection":{"attributeNames":["name","identity","host","role","dir","container","sasl","isAuthenticated","user","isEn
crypted","sslProto","sslCipher","properties","sslSsf","tenant","type","ssl","opened"],"results":[["connection/127.0.0.1:42042","2","127.0.0.1:42042","inter-router","in","A","ANONYMOUS",true,"anonymous",false,null,null,{"product":"qpid-dispatch-router","version":"1.2.0-SNAPSHOT"},0,null,"org.apache.qpid.dispatch.connection",false,true],["connection/127.0.0.1:42044","3","127.0.0.1:42044","inter-router","in","B","ANONYMOUS",true,"anonymous",false,null,null,{"product":"qpid-dispatch-router","version":"1.2.0-SNAPSHOT"},0,null,"org.apache.qpid.dispatch.connection",false,true],["connection/0.0.0.0:2001","4","0.0.0.0:2001","inter-router","out","D","ANONYMOUS",true,null,false,null,null,{"product":"qpid-dispatch-router","version":"1.2.0-SNAPSHOT"},0,null,"org.apache.qpid.dispatch.connection",false,true]]},"router.link":{"attributeNames":["name","identity","type","linkName","linkType","linkDir","owningAddr","capacity","peer","undeliveredCount","unsettledCount","deliveryCount","connectionId","a
dminStatus","operStatus","presettledCount","droppedPresettledCount","acceptedCount","rejectedCount","releasedCount","modifiedCount","ingressHistogram"],"results":[["qdlink.m3KDAkh4XWFmTF4","1","org.apache.qpid.dispatch.router.link","qdlink.m3KDAkh4XWFmTF4","router-control","out","Lqdhello",250,null,0,0,73865,"2","enabled","up",73865,0,0,0,0,0,null],["qdlink.5A74B_72F2tpPTO","2","org.apache.qpid.dispatch.router.link","qdlink.5A74B_72F2tpPTO","router-control","in",null,250,null,0,0,65405,"2","enabled","up",65405,0,0,0,0,0,null],["qdlink.GwfDttXBw7jsEci","3","org.apache.qpid.dispatch.router.link","qdlink.GwfDttXBw7jsEci","inter-router","out",null,250,null,0,0,46871,"2","enabled","up",0,0,46871,0,0,0,null],["qdlink.ZDs9rTrIFNLMjmK","4","org.apache.qpid.dispatch.router.link","qdlink.ZDs9rTrIFNLMjmK","inter-router","in",null,250,null,0,0,46871,"2","enabled","up",46871,0,0,0,0,0,null],["qdlink.bqvnMED2goA3BuE","5","org.apache.qpid.dispatch.router.link","qdlink.bqvnMED2goA3BuE","router-cont
rol","out","Lqdhello",250,null,0,0,73866,"3","enabled","up",73866,0,0,0,0,0,null],["qdlink.yvvxh_IOqw4DsKe","6","org.apache.qpid.dispatch.router.link","qdlink.yvvxh_IOqw4DsKe","router-control","in",null,250,null,0,0,65420,"3","enabled","up",65420,0,0,0,0,0,null],["qdlink.MBLypLUMaK4ymNV","7","org.apache.qpid.dispatch.router.link","qdlink.MBLypLUMaK4ymNV","inter-router","out",null,250,null,0,214,214661484,"3","enabled","up",0,0,214661270,0,0,0,null],["qdlink.mGkw33coTYTwtn5","8","org.apache.qpid.dispatch.router.link","qdlink.mGkw33coTYTwtn5","inter-router","in",null,250,null,0,245,214112811,"3","enabled","up",23730,0,214088836,0,0,0,null],["qdlink.iiY_WYQqdKOZjjf","9","org.apache.qpid.dispatch.router.link","qdlink.iiY_WYQqdKOZjjf","router-control","in",null,250,null,0,0,69650,"4","enabled","up",69650,0,0,0,0,0,null],["qdlink.ad3E15bOZ4387CG","10","org.apache.qpid.dispatch.router.link","qdlink.ad3E15bOZ4387CG","router-control","out","Lqdhello",250,null,0,0,69643,"4","enabled","up",696
43,0,0,0,0,0,null],["qdlink.RfxRiUQpPAmV7ow","11","org.apache.qpid.dispatch.router.link","qdlink.RfxRiUQpPAmV7ow","inter-router","in",null,250,null,0,214,214732071,"4","enabled","up",0,0,214731857,0,0,0,null],["qdlink.0x5Gje+NwvMzFpo","12","org.apache.qpid.dispatch.router.link","qdlink.0x5Gje+NwvMzFpo","inter-router","out",null,250,null,0,245,214183397,"4","enabled","up",94316,0,214088836,0,0,0,null]]}},"amqp:/_topo/0/A/$management":{"connection":{"attributeNames":["name","identity","host","role","dir","container","sasl","isAuthenticated","user","isEncrypted","sslProto","sslCipher","properties","sslSsf","tenant","type","ssl","opened"],"results":[["connection/0.0.0.0:2000","2","0.0.0.0:2000","inter-router","out","C","ANONYMOUS",true,null,false,null,null,{"product":"qpid-dispatch-router","version":"1.2.0-SNAPSHOT"},0,null,"org.apache.qpid.dispatch.connection",false,true]]},"router.node":{"results":[["A","(self)"],["C",null],["B","C"],["D","C"],["E","C"],["F","C"]],"attributeNames":["i
d","nextHop"]},"router.link":{"attributeNames":["name","identity","type","linkName","linkType","linkDir","owningAddr","capacity","peer","undeliveredCount","unsettledCount","deliveryCount","connectionId","adminStatus","operStatus","presettledCount","droppedPresettledCount","acceptedCount","rejectedCount","releasedCount","modifiedCount","ingressHistogram"],"results":[["qdlink.m3KDAkh4XWFmTF4","1","org.apache.qpid.dispatch.router.link","qdlink.m3KDAkh4XWFmTF4","router-control","in",null,250,null,0,0,73865,"2","enabled","up",73865,0,0,0,0,0,null],["qdlink.5A74B_72F2tpPTO","2","org.apache.qpid.dispatch.router.link","qdlink.5A74B_72F2tpPTO","router-control","out","Lqdhello",250,null,0,0,65405,"2","enabled","up",65405,0,0,0,0,0,null],["qdlink.GwfDttXBw7jsEci","3","org.apache.qpid.dispatch.router.link","qdlink.GwfDttXBw7jsEci","inter-router","in",null,250,null,0,0,46871,"2","enabled","up",0,0,46871,0,0,0,null],["qdlink.ZDs9rTrIFNLMjmK","4","org.apache.qpid.dispatch.router.link","qdlink.ZDs9
rTrIFNLMjmK","inter-router","out",null,250,null,0,0,46870,"2","enabled","up",46870,0,0,0,0,0,null]]}},"amqp:/_topo/0/B/$management":{"connection":{"attributeNames":["name","identity","host","role","dir","container","sasl","isAuthenticated","user","isEncrypted","sslProto","sslCipher","properties","sslSsf","tenant","type","ssl","opened"],"results":[["connection/127.0.0.1:51142","3","127.0.0.1:51142","normal","in","e7bad48b-3f38-496f-bc3e-b2696e289b9f","ANONYMOUS",true,"anonymous",false,null,null,{},0,null,"org.apache.qpid.dispatch.connection",false,true],["connection/127.0.0.1:51140","2","127.0.0.1:51140","normal","in","33709f9b-70bd-4fc4-938a-74740b6d8c24","ANONYMOUS",true,"anonymous",false,null,null,{},0,null,"org.apache.qpid.dispatch.connection",false,true],["connection/0.0.0.0:2000","4","0.0.0.0:2000","inter-router","out","C","ANONYMOUS",true,null,false,null,null,{"product":"qpid-dispatch-router","version":"1.2.0-SNAPSHOT"},0,null,"org.apache.qpid.dispatch.connection",false,true]]
},"router.node":{"results":[["B","(self)"],["C",null],["A","C"],["D","C"],["E","C"],["F","C"]],"attributeNames":["id","nextHop"]},"router.link":{"attributeNames":["name","identity","type","linkName","linkType","linkDir","owningAddr","capacity","peer","undeliveredCount","unsettledCount","deliveryCount","connectionId","adminStatus","operStatus","presettledCount","droppedPresettledCount","acceptedCount","rejectedCount","releasedCount","modifiedCount","ingressHistogram"],"results":[["e7bad48b-3f38-496f-bc3e-b2696e289b9f-toF","1","org.apache.qpid.dispatch.router.link","e7bad48b-3f38-496f-bc3e-b2696e289b9f-toF","endpoint","in","M0toF",250,null,0,243,214089079,"3","enabled","up",0,0,214088836,0,0,0,null],["33709f9b-70bd-4fc4-938a-74740b6d8c24-toB","2","org.apache.qpid.dispatch.router.link","33709f9b-70bd-4fc4-938a-74740b6d8c24-toB","endpoint","out","M0toB",250,null,205,9,214637549,"2","enabled","up",0,0,214637540,0,0,0,[0,0,0,0,0,214637540,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]],["qdlink.bqvnMED2goA3BuE","3","org.apache.qpid.dispatch.router.link","qdlink.bqvnMED2goA3BuE","router-control","in",null,250,null,0,0,73865,"4","enabled","up",73865,0,0,0,0,0,null],["qdlink.yvvxh_IOqw4DsKe","4","org.apache.qpid.dispatch.router.link","qdlink.yvvxh_IOqw4DsKe","router-control","out","Lqdhello",250,null,0,0,65420,"4","enabled","up",65420,0,0,0,0,0,null],["qdlink.MBLypLUMaK4ymNV","5","org.apache.qpid.dispatch.router.link","qdlink.MBLypLUMaK4ymNV","inter-router","in",null,250,null,0,214,214661484,"4","enabled","up",0,0,214661270,0,0,0,null],["qdlink.mGkw33coTYTwtn5","6","org.apache.qpid.dispatch.router.link","qdlink.mGkw33coTYTwtn5","inter-router","out",null,250,null,0,245,214112810,"4","enabled","up",23729,0,214088836,0,0,0,null]]}},"amqp:/_topo/0/E/$management":{"connection":{"
attributeNames":["name","identity","host","role","dir","container","sasl","isAuthenticated","user","isEncrypted","sslProto","sslCipher","properties","sslSsf","tenant","type","ssl","opened"],"results":[["connection/127.0.0.1:32944","1","127.0.0.1:32944","inter-router","in","D","ANONYMOUS",true,"anonymous",false,null,null,{"product":"qpid-dispatch-router","version":"1.2.0-SNAPSHOT"},0,null,"org.apache.qpid.dispatch.connection",false,true]]},"router.node":{"results":[["E","(self)"],["D",null],["C","D"],["F","D"],["A","D"],["B","D"]],"attributeNames":["id","nextHop"]},"listener":{"results":[["normal","22004",false],["inter-router","2002",false]],"attributeNames":["role","port","http"]},"router":{"attributeNames":["name","version","name"],"results":[["E","1.2.0-SNAPSHOT","E"]]},"router.link":{"attributeNames":["name","identity","type","linkName","linkType","linkDir","owningAddr","capacity","peer","undeliveredCount","unsettledCount","deliveryCount","connectionId","adminStatus","operStatus
","presettledCount","droppedPresettledCount","acceptedCount","rejectedCount","releasedCount","modifiedCount","ingressHistogram"],"results":[["qdlink.g8g0G5os5DBBLZt","1","org.apache.qpid.dispatch.router.link","qdlink.g8g0G5os5DBBLZt","router-control","out","Lqdhello",250,null,0,0,65399,"1","enabled","up",65399,0,0,0,0,0,null],["qdlink.GNu2meWTw_cQnmz","2","org.apache.qpid.dispatch.router.link","qdlink.GNu2meWTw_cQnmz","router-control","in",null,250,null,0,0,73850,"1","enabled","up",73850,0,0,0,0,0,null],["qdlink.uCAaTkz40dYjyzL","3","org.apache.qpid.dispatch.router.link","qdlink.uCAaTkz40dYjyzL","inter-router","out",null,250,null,0,0,23717,"1","enabled","up",23717,0,0,0,0,0,null],["qdlink.7IApVdoiBsie1s4","4","org.apache.qpid.dispatch.router.link","qdlink.7IApVdoiBsie1s4","inter-router","in",null,250,null,0,0,23718,"1","enabled","up",0,0,23718,0,0,0,null]]}},"amqp:/_topo/0/F/$management":{"router.node":{"results":[["F","(self)"],["D",null],["C","D"],["E","D"],["A","D"],["B","D"]],"a
ttributeNames":["id","nextHop"]},"connection":{"attributeNames":["name","identity","host","role","dir","container","sasl","isAuthenticated","user","isEncrypted","sslProto","sslCipher","properties","sslSsf","tenant","type","ssl","opened"],"results":[["connection/127.0.0.1:57110","1","127.0.0.1:57110","inter-router","in","D","ANONYMOUS",true,"anonymous",false,null,null,{"product":"qpid-dispatch-router","version":"1.2.0-SNAPSHOT"},0,null,"org.apache.qpid.dispatch.connection",false,true],["connection/127.0.0.1:50830","2","127.0.0.1:50830","normal","in","776690cc-4dd4-4a21-8432-960ae8b5232a","ANONYMOUS",true,"anonymous",false,null,null,{},0,null,"org.apache.qpid.dispatch.connection",false,true],["connection/127.0.0.1:50832","3","127.0.0.1:50832","normal","in","dbd0eed0-6b6a-471b-bebf-267e237c6f61","ANONYMOUS",true,"anonymous",false,null,null,{},0,null,"org.apache.qpid.dispatch.connection",false,true]]},"router.link":{"attributeNames":["name","identity","type","linkName","linkType","linkD
ir","owningAddr","capacity","peer","undeliveredCount","unsettledCount","deliveryCount","connectionId","adminStatus","operStatus","presettledCount","droppedPresettledCount","acceptedCount","rejectedCount","releasedCount","modifiedCount","ingressHistogram"],"results":[["qdlink.WhYVBxoYjPd28OH","1","org.apache.qpid.dispatch.router.link","qdlink.WhYVBxoYjPd28OH","router-control","out","Lqdhello",250,null,0,0,65412,"1","enabled","up",65412,0,0,0,0,0,null],["qdlink.ZforTKcJOgvL1rw","2","org.apache.qpid.dispatch.router.link","qdlink.ZforTKcJOgvL1rw","router-control","in",null,250,null,0,0,73851,"1","enabled","up",73851,0,0,0,0,0,null],["qdlink.g2xEaALSGR7GbpB","3","org.apache.qpid.dispatch.router.link","qdlink.g2xEaALSGR7GbpB","inter-router","out",null,250,null,0,232,214661501,"1","enabled","up",23729,0,214637540,0,0,0,null],["qdlink.gKc+W6yyLIBAvIH","4","org.apache.qpid.dispatch.router.link","qdlink.gKc+W6yyLIBAvIH","inter-router","in",null,250,null,0,245,214112811,"1","enabled","up",0,0,
214112566,0,0,0,null],["776690cc-4dd4-4a21-8432-960ae8b5232a-toF","5","org.apache.qpid.dispatch.router.link","776690cc-4dd4-4a21-8432-960ae8b5232a-toF","endpoint","out","M0toF",250,null,236,9,214088845,"2","enabled","up",0,0,214088836,0,0,0,[0,0,0,0,0,214088836,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]],["dbd0eed0-6b6a-471b-bebf-267e237c6f61-toB","6","org.apache.qpid.dispatch.router.link","dbd0eed0-6b6a-471b-bebf-267e237c6f61-toB","endpoint","in","M0toB",250,null,0,232,214637772,"3","enabled","up",0,0,214637540,0,0,0,null]]}}}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/test/utilities.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/test/utilities.js b/console/stand-alone/test/utilities.js
new file mode 100644
index 0000000..56c751e
--- /dev/null
+++ b/console/stand-alone/test/utilities.js
@@ -0,0 +1,192 @@
+/*
+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.
+*/
+/* global describe it */
+var assert = require('assert');
+var expect = require('chai').expect;
+import { QDRService } from '../plugin/js/qdrService.js';
+
+class Log {
+ constructor() {
+ }
+ log (msg) {}
+ debug (msg) {}
+ error (msg) {}
+ info (msg) {}
+ warn (msg) {}
+}
+var log = new Log();
+var loc = {protocol: function () { return 'http://';}};
+var timeout = function (f) {f();};
+var qdrService = new QDRService(log, timeout, loc);
+
+describe('Management utilities', function() {
+ describe('#nameFromId', function() {
+ it('should extract name from id', function() {
+ let name = qdrService.utilities.nameFromId('amqp:/topo/0/routerName/$management');
+ assert.equal(name, 'routerName');
+ });
+ it('should extract name with / from id', function() {
+ let name = qdrService.utilities.nameFromId('amqp:/topo/0/router/Name/$management');
+ assert.equal(name, 'router/Name');
+ });
+ });
+ describe('#valFor', function() {
+ let aAr = ['name', 'value'];
+ let vAr = [['mary', 'lamb']];
+ it('should return correct value for key', function() {
+ let name = qdrService.utilities.valFor(aAr, vAr[0], 'name');
+ assert.equal(name, 'mary');
+ });
+ it('should return null if key is not found', function() {
+ let name = qdrService.utilities.valFor(aAr, vAr, 'address');
+ assert.equal(name, null);
+ });
+ });
+ describe('#pretty', function() {
+ it('should return unchanged if not a number', function() {
+ let val = qdrService.utilities.pretty('foo');
+ assert.equal(val, 'foo');
+ });
+ it('should add commas to numbers', function() {
+ let val = qdrService.utilities.pretty('1234');
+ assert.equal(val, '1,234');
+ });
+ });
+ describe('#humanify', function() {
+ it('should handle empty strings', function() {
+ let val = qdrService.utilities.humanify('');
+ assert.equal(val, '');
+ });
+ it('should handle undefined input', function() {
+ let val = qdrService.utilities.humanify();
+ assert.equal(val, undefined);
+ });
+ it('should capitalize the first letter', function() {
+ let val = qdrService.utilities.humanify('foo');
+ assert.equal(val, 'Foo');
+ });
+ it('should split on all capital letters', function() {
+ let val = qdrService.utilities.humanify('fooBarBaz');
+ assert.equal(val, 'Foo Bar Baz');
+ });
+ });
+ describe('#addr_class', function() {
+ it('should handle unknown address types', function() {
+ let val = qdrService.utilities.addr_class(' ');
+ assert.equal(val, 'unknown: ');
+ });
+ it('should handle undefined input', function() {
+ let val = qdrService.utilities.addr_class();
+ assert.equal(val, '-');
+ });
+ it('should identify mobile addresses', function() {
+ let val = qdrService.utilities.addr_class('Mfoo');
+ assert.equal(val, 'mobile');
+ });
+ it('should identify router addresses', function() {
+ let val = qdrService.utilities.addr_class('Rfoo');
+ assert.equal(val, 'router');
+ });
+ it('should identify area addresses', function() {
+ let val = qdrService.utilities.addr_class('Afoo');
+ assert.equal(val, 'area');
+ });
+ it('should identify local addresses', function() {
+ let val = qdrService.utilities.addr_class('Lfoo');
+ assert.equal(val, 'local');
+ });
+ it('should identify link-incoming C addresses', function() {
+ let val = qdrService.utilities.addr_class('Cfoo');
+ assert.equal(val, 'link-incoming');
+ });
+ it('should identify link-incoming E addresses', function() {
+ let val = qdrService.utilities.addr_class('Efoo');
+ assert.equal(val, 'link-incoming');
+ });
+ it('should identify link-outgoing D addresses', function() {
+ let val = qdrService.utilities.addr_class('Dfoo');
+ assert.equal(val, 'link-outgoing');
+ });
+ it('should identify link-outgoing F addresses', function() {
+ let val = qdrService.utilities.addr_class('Dfoo');
+ assert.equal(val, 'link-outgoing');
+ });
+ it('should identify topo addresses', function() {
+ let val = qdrService.utilities.addr_class('Tfoo');
+ assert.equal(val, 'topo');
+ });
+ });
+ describe('#addr_text', function() {
+ it('should handle undefined input', function() {
+ let val = qdrService.utilities.addr_text();
+ assert.equal(val, '-');
+ });
+ it('should identify mobile addresses', function() {
+ let val = qdrService.utilities.addr_text('M0foo');
+ assert.equal(val, 'foo');
+ });
+ it('should identify non-mobile addresses', function() {
+ let val = qdrService.utilities.addr_text('Rfoo');
+ assert.equal(val, 'foo');
+ });
+ });
+ describe('#identity_clean', function() {
+ it('should handle undefined input', function() {
+ let val = qdrService.utilities.identity_clean();
+ assert.equal(val, '-');
+ });
+ it('should handle identities with no /', function() {
+ let val = qdrService.utilities.identity_clean('foo');
+ assert.equal(val, 'foo');
+ });
+ it('should return everything after the 1st /', function() {
+ let val = qdrService.utilities.identity_clean('foo/bar');
+ assert.equal(val, 'bar');
+ });
+ });
+ describe('#copy', function() {
+ it('should handle undefined input', function() {
+ let val = qdrService.utilities.copy();
+ assert.equal(val, undefined);
+ });
+ it('should copy all object values instead making references', function() {
+ let input = {a: 'original value'};
+ let output = qdrService.utilities.copy(input);
+ input.a = 'changed value';
+ assert.equal(output.a, 'original value');
+ });
+ });
+ describe('#flatten', function() {
+ it('should return an object when passed undefined input', function() {
+ let val = qdrService.utilities.flatten();
+ assert.equal(typeof val, 'object');
+ });
+ it('and the returned object should be empty', function() {
+ let val = qdrService.utilities.flatten();
+ assert.equal(Object.keys(val).length, 0);
+ });
+ it('should flatten the arrays into an object', function() {
+ let attributes = ['first', 'second'];
+ let value = ['1st', '2nd'];
+ let val = qdrService.utilities.flatten(attributes, value);
+ assert.equal(val.second, '2nd');
+ });
+ });
+
+});
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/tsconfig.json
----------------------------------------------------------------------
diff --git a/console/stand-alone/tsconfig.json b/console/stand-alone/tsconfig.json
index 056e773..d89a961 100644
--- a/console/stand-alone/tsconfig.json
+++ b/console/stand-alone/tsconfig.json
@@ -1,7 +1,14 @@
{
"compilerOptions": {
- "allowJs": true,
- "checkJs": true,
+ "module": "commonjs",
+ "preserveConstEnums": true,
+ "newLine": "LF",
+ "target": "es5",
+ "moduleResolution": "node",
+ "noImplicitReturns": true,
+ "strict": true,
+ "declaration": true,
+ "declarationDir": "./typings",
"lib": [
"dom",
"dom.iterable",
@@ -10,22 +17,14 @@
"es7",
"esnext",
"esnext.asynciterable",
- "es2015.iterable",
- "es2017"
+ "es2015.iterable"
]
},
"compileOnSave": true,
"include": [
- "plugin/*"
+ "typings/*"
],
"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/b5deb035/console/stand-alone/vendor-js.txt
----------------------------------------------------------------------
diff --git a/console/stand-alone/vendor-js.txt b/console/stand-alone/vendor-js.txt
index 5af21f4..4e11d07 100644
--- a/console/stand-alone/vendor-js.txt
+++ b/console/stand-alone/vendor-js.txt
@@ -17,7 +17,7 @@
-- The following files get packaged into a single vendor.min.js
-node_modules/bluebird/js/browser/bluebird.min.js
+node_modules/babel-polyfill/dist/polyfill.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
@@ -26,18 +26,18 @@ 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-slider/src/slider.js
+node_modules/angular-ui-grid/ui-grid.min.js
node_modules/angular-ui-bootstrap/dist/ui-bootstrap.js
node_modules/angular-ui-bootstrap/dist/ui-bootstrap-tpls.js
+node_modules/angular-bootstrap-checkbox/angular-bootstrap-checkbox.js
+node_modules/bootstrap/dist/js/bootstrap.min.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
+node_modules/rhea/dist/rhea.js
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[4/8] qpid-dispatch git commit: DISPATCH-1049 Add console tests
Posted by ea...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/qdrList.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrList.js b/console/stand-alone/plugin/js/qdrList.js
index db3a43f..6dafe20 100644
--- a/console/stand-alone/plugin/js/qdrList.js
+++ b/console/stand-alone/plugin/js/qdrList.js
@@ -16,713 +16,673 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
-'use strict';
/* global angular d3 */
-/**
- * @module QDR
- */
-var QDR = (function(QDR) {
-
- /**
- * Controller for the main interface
- */
- QDR.module.controller('QDR.ListController', ['$scope', '$location', '$uibModal', '$filter', '$timeout', 'QDRService', 'QDRChartService', 'uiGridConstants', '$sce',
- function ($scope, $location, $uibModal, $filter, $timeout, QDRService, QDRChartService, uiGridConstants, $sce) {
-
- QDR.log.debug('QDR.ListControll started with location of ' + $location.path() + ' and connection of ' + QDRService.management.connection.is_connected());
- let updateIntervalHandle = undefined,
- updateInterval = 5000,
- last_updated = 0,
- updateNow = false,
- ListExpandedKey = 'QDRListExpanded',
- SelectedEntityKey = 'QDRSelectedEntity',
- ActivatedKey = 'QDRActivatedKey';
- $scope.details = {};
-
- $scope.tmplListTree = QDR.templatePath + 'tmplListTree.html';
- $scope.selectedEntity = localStorage[SelectedEntityKey] || 'address';
- $scope.ActivatedKey = localStorage[ActivatedKey] || null;
- if ($scope.selectedEntity == 'undefined')
- $scope.selectedEntity = undefined;
- $scope.selectedNode = localStorage['QDRSelectedNode'];
- $scope.selectedNodeId = localStorage['QDRSelectedNodeId'];
- $scope.selectedRecordName = localStorage['QDRSelectedRecordName'];
- $scope.nodes = [];
- $scope.currentNode = undefined;
- $scope.modes = [
- {
- content: '<a><i class="icon-list"></i> Attributes</a>',
- id: 'attributes',
- op: 'READ',
- title: 'View router attributes',
- isValid: function () { return true; }
- },
- {
- content: '<a><i class="icon-edit"></i> Update</a>',
- id: 'operations',
- op: 'UPDATE',
- title: 'Update this attribute',
- isValid: function () {
- return $scope.operations.indexOf(this.op) > -1;
- }
- },
- {
- content: '<a><i class="icon-plus"></i> Create</a>',
- id: 'operations',
- op: 'CREATE',
- title: 'Create a new attribute',
- isValid: function () { return $scope.operations.indexOf(this.op) > -1; }
- },
- {
- content: '<a><i class="icon-remove"></i> Delete</a>',
- id: 'delete',
- op: 'DELETE',
- title: 'Delete',
- isValid: function () { return $scope.operations.indexOf(this.op) > -1; }
- },
- {
- content: '<a><i class="icon-eye-open"></i> Fetch</a>',
- id: 'log',
- op: 'GET-LOG',
- title: 'Fetch recent log entries',
- isValid: function () { return ($scope.selectedEntity === 'log'); }
+import { QDRFolder, QDRLeaf, QDRCore, QDRLogger, QDRTemplatePath, QDRRedirectWhenConnected} from './qdrGlobals.js';
+
+export class ListController {
+ constructor(QDRService, QDRChartService, $scope, $log, $location, $uibModal, $filter, $timeout, uiGridConstants, $sce) {
+ this.controllerName = 'QDR.ListController';
+
+ let QDRLog = new QDRLogger($log, 'ListController');
+
+ QDRLog.debug('QDR.ListControll started with location of ' + $location.path() + ' and connection of ' + QDRService.management.connection.is_connected());
+ let updateIntervalHandle = undefined,
+ updateInterval = 5000,
+ last_updated = 0,
+ updateNow = false,
+ ListExpandedKey = 'QDRListExpanded',
+ SelectedEntityKey = 'QDRSelectedEntity',
+ ActivatedKey = 'QDRActivatedKey';
+ $scope.details = {};
+
+ $scope.tmplListTree = QDRTemplatePath + 'tmplListTree.html';
+ $scope.selectedEntity = localStorage[SelectedEntityKey] || 'address';
+ $scope.ActivatedKey = localStorage[ActivatedKey] || null;
+ if ($scope.selectedEntity == 'undefined')
+ $scope.selectedEntity = undefined;
+ $scope.selectedNode = localStorage['QDRSelectedNode'];
+ $scope.selectedNodeId = localStorage['QDRSelectedNodeId'];
+ $scope.selectedRecordName = localStorage['QDRSelectedRecordName'];
+ $scope.nodes = [];
+ $scope.currentNode = undefined;
+ $scope.modes = [
+ {
+ content: '<a><i class="icon-list"></i> Attributes</a>',
+ id: 'attributes',
+ op: 'READ',
+ title: 'View router attributes',
+ isValid: function () { return true; }
+ },
+ {
+ content: '<a><i class="icon-edit"></i> Update</a>',
+ id: 'operations',
+ op: 'UPDATE',
+ title: 'Update this attribute',
+ isValid: function () {
+ return $scope.operations.indexOf(this.op) > -1;
}
- ];
- $scope.operations = [];
- $scope.currentMode = $scope.modes[0];
- $scope.isModeSelected = function (mode) {
- return mode === $scope.currentMode;
- };
- $scope.fetchingLog = false;
- $scope.selectMode = function (mode) {
- $scope.currentMode = mode;
- if (mode.id === 'log') {
- $scope.logResults = [];
- $scope.fetchingLog = true;
- let entity; // undefined since it is not supported in the GET-LOG call
- QDRService.management.connection.sendMethod($scope.currentNode.id, entity, {}, $scope.currentMode.op)
- .then( function (response) {
- let statusCode = response.context.message.application_properties.statusCode;
- if (statusCode < 200 || statusCode >= 300) {
- QDR.Core.notification('error', response.context.message.statusDescription);
- QDR.log.error('Error ' + response.context.message.statusDescription);
- return;
- }
- $timeout( function () {
- $scope.fetchingLog = false;
- $scope.logResults = response.response.filter( function (entry) {
- return entry[0] === $scope.detailsObject.module;
- }).sort( function (a, b) {
- return b[5] - a[5];
- }).map( function (entry) {
- return {
- type: entry[1],
- message: entry[2],
- source: entry[3],
- line: entry[4],
- time: Date(entry[5]).toString()
- };
- });
+ },
+ {
+ content: '<a><i class="icon-plus"></i> Create</a>',
+ id: 'operations',
+ op: 'CREATE',
+ title: 'Create a new attribute',
+ isValid: function () { return $scope.operations.indexOf(this.op) > -1; }
+ },
+ {
+ content: '<a><i class="icon-remove"></i> Delete</a>',
+ id: 'delete',
+ op: 'DELETE',
+ title: 'Delete',
+ isValid: function () { return $scope.operations.indexOf(this.op) > -1; }
+ },
+ {
+ content: '<a><i class="icon-eye-open"></i> Fetch</a>',
+ id: 'log',
+ op: 'GET-LOG',
+ title: 'Fetch recent log entries',
+ isValid: function () { return ($scope.selectedEntity === 'log'); }
+ }
+ ];
+ $scope.operations = [];
+ $scope.currentMode = $scope.modes[0];
+ $scope.isModeSelected = function (mode) {
+ return mode === $scope.currentMode;
+ };
+ $scope.fetchingLog = false;
+ $scope.selectMode = function (mode) {
+ $scope.currentMode = mode;
+ if (mode.id === 'log') {
+ $scope.logResults = [];
+ $scope.fetchingLog = true;
+ let entity; // undefined since it is not supported in the GET-LOG call
+ QDRService.management.connection.sendMethod($scope.currentNode.id, entity, {}, $scope.currentMode.op)
+ .then( function (response) {
+ let statusCode = response.context.message.application_properties.statusCode;
+ if (statusCode < 200 || statusCode >= 300) {
+ QDRCore.notification('error', response.context.message.statusDescription);
+ QDRLog.error('Error ' + response.context.message.statusDescription);
+ return;
+ }
+ $timeout( function () {
+ $scope.fetchingLog = false;
+ $scope.logResults = response.response.filter( function (entry) {
+ return entry[0] === $scope.detailsObject.module;
+ }).sort( function (a, b) {
+ return b[5] - a[5];
+ }).map( function (entry) {
+ return {
+ type: entry[1],
+ message: entry[2],
+ source: entry[3],
+ line: entry[4],
+ time: Date(entry[5]).toString()
+ };
});
});
- }
- };
- $scope.isValid = function (mode) {
- return mode.isValid();
- };
-
- $scope.expandAll = function () {
- $('#entityTree').fancytree('getTree').visit(function(node){
- node.setExpanded(true);
- });
- };
- $scope.contractAll = function () {
- $('#entityTree').fancytree('getTree').visit(function(node){
- node.setExpanded(false);
- });
- };
-
- if (!QDRService.management.connection.is_connected()) {
- // we are not connected. we probably got here from a bookmark or manual page reload
- QDR.redirectWhenConnected($location, 'list');
- return;
+ });
}
-
- let excludedEntities = ['management', 'org.amqp.management', 'operationalEntity', 'entity', 'configurationEntity', 'dummy', 'console'];
- let aggregateEntities = ['router.address'];
-
- let classOverrides = {
- 'connection': function (row, nodeId) {
- let isConsole = QDRService.utilities.isAConsole (row.properties.value, row.identity.value, row.role.value, nodeId);
- return isConsole ? 'console' : row.role.value === 'inter-router' ? 'inter-router' : 'external';
- },
- 'router.link': function (row, nodeId) {
- let link = {nodeId: nodeId, connectionId: row.connectionId.value};
-
- let isConsole = QDRService.utilities.isConsole(QDRService.management.topology.getConnForLink(link));
- return isConsole ? 'console' : row.linkType.value;
- },
- 'router.address': function (row) {
- let identity = QDRService.utilities.identity_clean(row.identity.value);
- let address = QDRService.utilities.addr_text(identity);
- let cls = QDRService.utilities.addr_class(identity);
- if (address === '$management')
- cls = 'internal ' + cls;
- return cls;
+ };
+ $scope.isValid = function (mode) {
+ return mode.isValid();
+ };
+
+ $scope.expandAll = function () {
+ $('#entityTree').fancytree('getTree').visit(function(node){
+ node.setExpanded(true);
+ });
+ };
+ $scope.contractAll = function () {
+ $('#entityTree').fancytree('getTree').visit(function(node){
+ node.setExpanded(false);
+ });
+ };
+
+ if (!QDRService.management.connection.is_connected()) {
+ // we are not connected. we probably got here from a bookmark or manual page reload
+ QDRRedirectWhenConnected($location, 'list');
+ return;
+ }
+
+ let excludedEntities = ['management', 'org.amqp.management', 'operationalEntity', 'entity', 'configurationEntity', 'dummy', 'console'];
+ let aggregateEntities = ['router.address'];
+
+ let classOverrides = {
+ 'connection': function (row, nodeId) {
+ let isConsole = QDRService.utilities.isAConsole (row.properties.value, row.identity.value, row.role.value, nodeId);
+ return isConsole ? 'console' : row.role.value === 'inter-router' ? 'inter-router' : 'external';
+ },
+ 'router.link': function (row, nodeId) {
+ let link = {nodeId: nodeId, connectionId: row.connectionId.value};
+
+ let isConsole = QDRService.utilities.isConsole(QDRService.management.topology.getConnForLink(link));
+ return isConsole ? 'console' : row.linkType.value;
+ },
+ 'router.address': function (row) {
+ let identity = QDRService.utilities.identity_clean(row.identity.value);
+ let address = QDRService.utilities.addr_text(identity);
+ let cls = QDRService.utilities.addr_class(identity);
+ if (address === '$management')
+ cls = 'internal ' + cls;
+ return cls;
+ }
+ };
+
+ var lookupOperations = function () {
+ let ops = QDRService.management.schema().entityTypes[$scope.selectedEntity].operations.filter( function (op) { return op !== 'READ';});
+ $scope.operation = ops.length ? ops[0] : '';
+ return ops;
+ };
+ let entityTreeChildren = [];
+ let expandedList = angular.fromJson(localStorage[ListExpandedKey]) || [];
+ let saveExpanded = function () {
+ // save the list of entities that are expanded
+ let tree = $('#entityTree').fancytree('getTree');
+ let list = [];
+ tree.visit( function (tnode) {
+ if (tnode.isExpanded()) {
+ list.push(tnode.key);
}
- };
-
- var lookupOperations = function () {
- let ops = QDRService.management.schema().entityTypes[$scope.selectedEntity].operations.filter( function (op) { return op !== 'READ';});
- $scope.operation = ops.length ? ops[0] : '';
- return ops;
- };
- let entityTreeChildren = [];
- let expandedList = angular.fromJson(localStorage[ListExpandedKey]) || [];
- let saveExpanded = function () {
- // save the list of entities that are expanded
- let tree = $('#entityTree').fancytree('getTree');
- let list = [];
- tree.visit( function (tnode) {
- if (tnode.isExpanded()) {
- list.push(tnode.key);
- }
- });
- console.log('saving expanded list');
- console.log(list);
- localStorage[ListExpandedKey] = JSON.stringify(list);
- };
-
- var onTreeNodeBeforeActivate = function (event, data) {
- // if node is toplevel entity
- if (data.node.data.typeName === 'entity') {
- return false;
- /*
- // if the current active node is not this one and not one of its children
- let active = data.tree.getActiveNode();
- if (active && !data.node.isActive() && data.node.isExpanded()) { // there is an active node and it's not this one
- let any = false;
- let children = data.node.getChildren();
- if (children) {
- any = children.some( function (child) {
- return child.key === active.key;
- });
- }
- if (!any) // none of the clicked on node's children was active
- return false; // don't activate, just collapse this top level node
+ });
+ console.log('saving expanded list');
+ console.log(list);
+ localStorage[ListExpandedKey] = JSON.stringify(list);
+ };
+
+ var onTreeNodeBeforeActivate = function (event, data) {
+ // if node is toplevel entity
+ if (data.node.data.typeName === 'entity') {
+ return false;
+ /*
+ // if the current active node is not this one and not one of its children
+ let active = data.tree.getActiveNode();
+ if (active && !data.node.isActive() && data.node.isExpanded()) { // there is an active node and it's not this one
+ let any = false;
+ let children = data.node.getChildren();
+ if (children) {
+ any = children.some( function (child) {
+ return child.key === active.key;
+ });
}
- */
- }
- return true;
- };
- var onTreeNodeExpanded = function () {
- saveExpanded();
- updateExpandedEntities();
- };
- var onTreeNodeCollapsed = function () {
- saveExpanded();
- };
- // a tree node was selected
- var onTreeNodeActivated = function (event, data) {
- $scope.ActivatedKey = data.node.key;
- let selectedNode = data.node;
- $scope.selectedTreeNode = data.node;
- if ($scope.currentMode.id === 'operations')
- $scope.currentMode = $scope.modes[0];
- else if ($scope.currentMode.id === 'log')
- $scope.selectMode($scope.currentMode);
- else if ($scope.currentMode.id === 'delete') {
- // clicked on a tree node while on the delete screen -> switch to attribute screen
- $scope.currentMode = $scope.modes[0];
+ if (!any) // none of the clicked on node's children was active
+ return false; // don't activate, just collapse this top level node
}
- if (selectedNode.data.typeName === 'entity') {
- $scope.selectedEntity = selectedNode.key;
- $scope.operations = lookupOperations();
+ */
+ }
+ return true;
+ };
+ var onTreeNodeExpanded = function () {
+ saveExpanded();
+ updateExpandedEntities();
+ };
+ var onTreeNodeCollapsed = function () {
+ saveExpanded();
+ };
+ // a tree node was selected
+ var onTreeNodeActivated = function (event, data) {
+ $scope.ActivatedKey = data.node.key;
+ let selectedNode = data.node;
+ $scope.selectedTreeNode = data.node;
+ if ($scope.currentMode.id === 'operations')
+ $scope.currentMode = $scope.modes[0];
+ else if ($scope.currentMode.id === 'log')
+ $scope.selectMode($scope.currentMode);
+ else if ($scope.currentMode.id === 'delete') {
+ // clicked on a tree node while on the delete screen -> switch to attribute screen
+ $scope.currentMode = $scope.modes[0];
+ }
+ if (selectedNode.data.typeName === 'entity') {
+ $scope.selectedEntity = selectedNode.key;
+ $scope.operations = lookupOperations();
+ updateNow = true;
+ } else if (selectedNode.data.typeName === 'attribute') {
+ if (!selectedNode.parent)
+ return;
+ let sameEntity = $scope.selectedEntity === selectedNode.parent.key;
+ $scope.selectedEntity = selectedNode.parent.key;
+ $scope.operations = lookupOperations();
+ $scope.selectedRecordName = selectedNode.key;
+ updateDetails(selectedNode.data.details); // update the table on the right
+ if (!sameEntity) {
updateNow = true;
- } else if (selectedNode.data.typeName === 'attribute') {
- if (!selectedNode.parent)
- return;
- let sameEntity = $scope.selectedEntity === selectedNode.parent.key;
- $scope.selectedEntity = selectedNode.parent.key;
- $scope.operations = lookupOperations();
- $scope.selectedRecordName = selectedNode.key;
- updateDetails(selectedNode.data.details); // update the table on the right
- if (!sameEntity) {
- updateNow = true;
- }
- } else if (selectedNode.data.typeName === 'none') {
- $scope.selectedEntity = selectedNode.parent.key;
- $scope.selectedRecordName = $scope.selectedEntity;
- updateDetails(fromSchema($scope.selectedEntity));
- }
- };
- var getExpanded = function (tree) {
- let list = [];
- tree.visit( function (tnode) {
- if (tnode.isExpanded()) {
- list.push(tnode);
- }
- });
- return list;
- };
- // fill in an empty results recoord based on the entities schema
- var fromSchema = function (entityName) {
- let row = {};
- let schemaEntity = QDRService.management.schema().entityTypes[entityName];
- for (let attr in schemaEntity.attributes) {
- let entity = schemaEntity.attributes[attr];
- let value = '';
- if (angular.isDefined(entity['default'])) {
- if (entity['type'] === 'integer')
- value = parseInt(entity['default']); // some default values that are marked as integer are passed as string
- else
- value = entity['default'];
- }
- row[attr] = {
- value: value,
- type: entity.type,
- graph: false,
- title: entity.description,
- aggregate: false,
- aggregateTip: '',
- 'default': entity['default']
- };
}
- return row;
- };
- $scope.hasCreate = function () {
- let schemaEntity = QDRService.management.schema().entityTypes[$scope.selectedEntity];
- return (schemaEntity.operations.indexOf('CREATE') > -1);
- };
-
- var getActiveChild = function (node) {
- let active = node.children.filter(function (child) {
- return child.isActive();
- });
- if (active.length > 0)
- return active[0].key;
- return null;
- };
- // the data for the selected entity is available, populate the tree on the left
- var updateTreeChildren = function (entity, tableRows, expand) {
- let tree = $('#entityTree').fancytree('getTree'), node, newNode;
- if (tree && tree.getNodeByKey) {
- node = tree.getNodeByKey(entity);
+ } else if (selectedNode.data.typeName === 'none') {
+ $scope.selectedEntity = selectedNode.parent.key;
+ $scope.selectedRecordName = $scope.selectedEntity;
+ updateDetails(fromSchema($scope.selectedEntity));
+ }
+ };
+ var getExpanded = function (tree) {
+ let list = [];
+ tree.visit( function (tnode) {
+ if (tnode.isExpanded()) {
+ list.push(tnode);
}
- if (!tree || !node) {
- return;
+ });
+ return list;
+ };
+ // fill in an empty results recoord based on the entities schema
+ var fromSchema = function (entityName) {
+ let row = {};
+ let schemaEntity = QDRService.management.schema().entityTypes[entityName];
+ for (let attr in schemaEntity.attributes) {
+ let entity = schemaEntity.attributes[attr];
+ let value = '';
+ if (angular.isDefined(entity['default'])) {
+ if (entity['type'] === 'integer')
+ value = parseInt(entity['default']); // some default values that are marked as integer are passed as string
+ else
+ value = entity['default'];
}
- let wasActive = node.isActive();
- let wasExpanded = node.isExpanded();
- let activeChildKey = getActiveChild(node);
- node.removeChildren();
- if (tableRows.length == 0) {
- newNode = {
- extraClasses: 'no-data',
- typeName: 'none',
- title: 'no data',
- key: node.key + '.1'
- };
- node.addNode(newNode);
- if (expand) {
- updateDetails(fromSchema(entity));
- $scope.selectedRecordName = entity;
- }
- } else {
- let children = tableRows.map( function (row) {
- let addClass = entity;
- if (classOverrides[entity]) {
- addClass += ' ' + classOverrides[entity](row, $scope.currentNode.id);
- }
- let child = {
- typeName: 'attribute',
- extraClasses: addClass,
- tooltip: addClass,
- key: row.name.value,
- title: row.name.value,
- details: row
- };
- return child;
- });
- node.addNode(children);
+ row[attr] = {
+ value: value,
+ type: entity.type,
+ graph: false,
+ title: entity.description,
+ aggregate: false,
+ aggregateTip: '',
+ 'default': entity['default']
+ };
+ }
+ return row;
+ };
+ $scope.hasCreate = function () {
+ let schemaEntity = QDRService.management.schema().entityTypes[$scope.selectedEntity];
+ return (schemaEntity.operations.indexOf('CREATE') > -1);
+ };
+
+ var getActiveChild = function (node) {
+ let active = node.children.filter(function (child) {
+ return child.isActive();
+ });
+ if (active.length > 0)
+ return active[0].key;
+ return null;
+ };
+ // the data for the selected entity is available, populate the tree on the left
+ var updateTreeChildren = function (entity, tableRows, expand) {
+ let tree = $('#entityTree').fancytree('getTree'), node, newNode;
+ if (tree && tree.getNodeByKey) {
+ node = tree.getNodeByKey(entity);
+ }
+ if (!tree || !node) {
+ return;
+ }
+ let wasActive = node.isActive();
+ let wasExpanded = node.isExpanded();
+ let activeChildKey = getActiveChild(node);
+ node.removeChildren();
+ if (tableRows.length == 0) {
+ newNode = {
+ extraClasses: 'no-data',
+ typeName: 'none',
+ title: 'no data',
+ key: node.key + '.1'
+ };
+ node.addNode(newNode);
+ if (expand) {
+ updateDetails(fromSchema(entity));
+ $scope.selectedRecordName = entity;
}
- // top level node was expanded
- if (wasExpanded)
- node.setExpanded(true, {noAnimation: true, noEvents: true, noFocus: true});
- // if the parent node was active, but none of the children were active, active the 1st child
- if (wasActive) {
- if (!activeChildKey) {
- activeChildKey = node.children[0].key;
+ } else {
+ let children = tableRows.map( function (row) {
+ let addClass = entity;
+ if (classOverrides[entity]) {
+ addClass += ' ' + classOverrides[entity](row, $scope.currentNode.id);
}
- }
- if (!tree.getActiveNode())
- activeChildKey = $scope.ActivatedKey;
- // re-active the previously active child node
- if (activeChildKey) {
- newNode = tree.getNodeByKey(activeChildKey);
- // the node may not be there after the update
- if (newNode)
- newNode.setActive(true, {noFocus: true}); // fires the onTreeNodeActivated event for this node
- }
- //resizer();
- };
-
-
- var resizer = function () {
- // this forces the tree and the grid to be the size of the browser window.
- // the effect is that the tree and the grid will have vertical scroll bars if needed.
- // the alternative is to let the tree and grid determine the size of the page and have
- // the scroll bar on the window
- // don't allow HTML in the tree titles
- $('.fancytree-title').each( function () {
- let unsafe = $(this).html();
- $(this).html(unsafe.replace(/</g, '<').replace(/>/g, '>'));
+ let child = {
+ typeName: 'attribute',
+ extraClasses: addClass,
+ tooltip: addClass,
+ key: row.name.value,
+ title: row.name.value,
+ details: row
+ };
+ return child;
});
- let h = $scope.detailFields.length * 30 + 46;
- $('.ui-grid-viewport').height(h);
- $scope.details.excessRows = $scope.detailFields.length;
- $scope.gridApi.grid.handleWindowResize();
- $scope.gridApi.core.refresh();
- };
- $(window).resize(resizer);
-
- var schemaProps = function (entityName, key, currentNode) {
- let typeMap = {integer: 'number', string: 'text', path: 'text', boolean: 'boolean', map: 'textarea'};
-
- let entity = QDRService.management.schema().entityTypes[entityName];
- let value = entity.attributes[key];
- // skip identity and depricated fields
- if (!value)
- return {input: 'input', type: 'disabled', required: false, selected: '', rawtype: 'string', disabled: true, 'default': ''};
- let description = value.description || '';
- let val = value['default'];
- let disabled = (key == 'identity' || description.startsWith('Deprecated'));
- // special cases
- if (entityName == 'log' && key == 'module') {
- return {input: 'input', type: 'disabled', required: false, selected: '', rawtype: 'string', disabled: true, 'default': ''};
- }
- if (entityName === 'linkRoutePattern' && key === 'connector') {
- // turn input into a select. the values will be populated later
- value.type = [];
- // find all the connector names and populate the select
- QDRService.management.topology.fetchEntity(currentNode.id, 'connector', ['name'], function (nodeName, dotentity, response) {
- $scope.detailFields.some( function (field) {
- if (field.name === 'connector') {
- field.rawtype = response.results.map (function (result) {return result[0];});
- return true;
- }
- });
- });
+ node.addNode(children);
+ }
+ // top level node was expanded
+ if (wasExpanded)
+ node.setExpanded(true, {noAnimation: true, noEvents: true, noFocus: true});
+ // if the parent node was active, but none of the children were active, active the 1st child
+ if (wasActive) {
+ if (!activeChildKey) {
+ activeChildKey = node.children[0].key;
}
- return { name: key,
- humanName: QDRService.utilities.humanify(key),
- description:value.description,
- type: disabled ? 'disabled' : typeMap[value.type],
- rawtype: value.type,
- input: typeof value.type == 'string' ? value.type == 'boolean' ? 'boolean' : 'input'
- : 'select',
- selected: val ? val : undefined,
- 'default': value['default'],
- value: val,
- required: value.required,
- unique: value.unique,
- disabled: disabled
- };
- };
- $scope.getAttributeValue = function (attribute) {
- let value = attribute.attributeValue;
- if ($scope.currentMode.op === 'CREATE' && attribute.name === 'identity')
- value = '<assigned by system>';
- return value;
- };
-
- // update the table on the right
- var updateDetails = function (row) {
- let details = [];
- $scope.detailsObject = {};
- let attrs = Object.keys(row).sort();
- attrs.forEach( function (attr) {
- let changed = $scope.detailFields.filter(function (old) {
- return (old.name === attr) ? old.graph && old.rawValue != row[attr].value : false;
- });
- let schemaEntity = schemaProps($scope.selectedEntity, attr, $scope.currentNode);
- details.push( {
- attributeName: QDRService.utilities.humanify(attr),
- attributeValue: attr === 'port' ? row[attr].value : QDRService.utilities.pretty(row[attr].value),
- name: attr,
- changed: changed.length,
- rawValue: row[attr].value,
- graph: row[attr].graph,
- title: row[attr].title,
- chartExists: (QDRChartService.isAttrCharted($scope.currentNode.id, $scope.selectedEntity, row.name.value, attr)),
- aggchartExists: (QDRChartService.isAttrCharted($scope.currentNode.id, $scope.selectedEntity, row.name.value, attr, true)),
- aggregateValue: QDRService.utilities.pretty(row[attr].aggregate),
- aggregateTip: row[attr].aggregateTip,
-
- input: schemaEntity.input,
- type: schemaEntity.type,
- required: schemaEntity.required,
- unique: schemaEntity.unique,
- selected: schemaEntity.selected,
- rawtype: schemaEntity.rawtype,
- disabled: schemaEntity.disabled,
- 'default': schemaEntity['default']
+ }
+ if (!tree.getActiveNode())
+ activeChildKey = $scope.ActivatedKey;
+ // re-active the previously active child node
+ if (activeChildKey) {
+ newNode = tree.getNodeByKey(activeChildKey);
+ // the node may not be there after the update
+ if (newNode)
+ newNode.setActive(true, {noFocus: true}); // fires the onTreeNodeActivated event for this node
+ }
+ //resizer();
+ };
+
+
+ var resizer = function () {
+ // this forces the tree and the grid to be the size of the browser window.
+ // the effect is that the tree and the grid will have vertical scroll bars if needed.
+ // the alternative is to let the tree and grid determine the size of the page and have
+ // the scroll bar on the window
+ // don't allow HTML in the tree titles
+ $('.fancytree-title').each( function () {
+ let unsafe = $(this).html();
+ $(this).html(unsafe.replace(/</g, '<').replace(/>/g, '>'));
+ });
+ let h = $scope.detailFields.length * 30 + 46;
+ $('.ui-grid-viewport').height(h);
+ $scope.details.excessRows = $scope.detailFields.length;
+ $scope.gridApi.grid.handleWindowResize();
+ $scope.gridApi.core.refresh();
+
+ };
+ $(window).resize(resizer);
+
+ var schemaProps = function (entityName, key, currentNode) {
+ let typeMap = {integer: 'number', string: 'text', path: 'text', boolean: 'boolean', map: 'textarea'};
+
+ let entity = QDRService.management.schema().entityTypes[entityName];
+ let value = entity.attributes[key];
+ // skip identity and depricated fields
+ if (!value)
+ return {input: 'input', type: 'disabled', required: false, selected: '', rawtype: 'string', disabled: true, 'default': ''};
+ let description = value.description || '';
+ let val = value['default'];
+ let disabled = (key == 'identity' || description.startsWith('Deprecated'));
+ // special cases
+ if (entityName == 'log' && key == 'module') {
+ return {input: 'input', type: 'disabled', required: false, selected: '', rawtype: 'string', disabled: true, 'default': ''};
+ }
+ if (entityName === 'linkRoutePattern' && key === 'connector') {
+ // turn input into a select. the values will be populated later
+ value.type = [];
+ // find all the connector names and populate the select
+ QDRService.management.topology.fetchEntity(currentNode.id, 'connector', ['name'], function (nodeName, dotentity, response) {
+ $scope.detailFields.some( function (field) {
+ if (field.name === 'connector') {
+ field.rawtype = response.results.map (function (result) {return result[0];});
+ return true;
+ }
});
- $scope.detailsObject[attr] = row[attr].value;
});
- $scope.detailFields = details;
- aggregateColumn();
- resizer();
- };
-
- // called from html ng-style="getTableHeight()"
- $scope.getTableHeight = function () {
- return {
- height: (Math.max($scope.detailFields.length, 15) * 30 + 46) + 'px'
- };
- };
- var updateExpandedEntities = function () {
- let tree = $('#entityTree').fancytree('getTree');
- if (tree) {
- let q = d3.queue(10);
- let expanded = getExpanded(tree);
- expanded.forEach( function (node) {
- q.defer(q_updateTableData, node.key, node.key === $scope.selectedEntity);
- });
+ }
+ return {
+ name: key,
+ humanName: QDRService.utilities.humanify(key),
+ description:value.description,
+ type: disabled ? 'disabled' : typeMap[value.type],
+ rawtype: value.type,
+ input: typeof value.type == 'string' ? value.type == 'boolean' ? 'boolean' : 'input'
+ : 'select',
+ selected: val ? val : undefined,
+ 'default': value['default'],
+ value: val,
+ required: value.required,
+ unique: value.unique,
+ disabled: disabled
+ };
+ };
+ $scope.getAttributeValue = function (attribute) {
+ let value = attribute.attributeValue;
+ if ($scope.currentMode.op === 'CREATE' && attribute.name === 'identity')
+ value = '<assigned by system>';
+ return value;
+ };
+
+ // update the table on the right
+ var updateDetails = function (row) {
+ let details = [];
+ $scope.detailsObject = {};
+ let attrs = Object.keys(row).sort();
+ attrs.forEach( function (attr) {
+ let changed = $scope.detailFields.filter(function (old) {
+ return (old.name === attr) ? old.graph && old.rawValue != row[attr].value : false;
+ });
+ let schemaEntity = schemaProps($scope.selectedEntity, attr, $scope.currentNode);
+ details.push( {
+ attributeName: QDRService.utilities.humanify(attr),
+ attributeValue: attr === 'port' ? row[attr].value : QDRService.utilities.pretty(row[attr].value),
+ name: attr,
+ changed: changed.length,
+ rawValue: row[attr].value,
+ graph: row[attr].graph,
+ title: row[attr].title,
+ chartExists: (QDRChartService.isAttrCharted($scope.currentNode.id, $scope.selectedEntity, row.name.value, attr)),
+ aggchartExists: (QDRChartService.isAttrCharted($scope.currentNode.id, $scope.selectedEntity, row.name.value, attr, true)),
+ aggregateValue: QDRService.utilities.pretty(row[attr].aggregate),
+ aggregateTip: row[attr].aggregateTip,
+
+ input: schemaEntity.input,
+ type: schemaEntity.type,
+ required: schemaEntity.required,
+ unique: schemaEntity.unique,
+ selected: schemaEntity.selected,
+ rawtype: schemaEntity.rawtype,
+ disabled: schemaEntity.disabled,
+ 'default': schemaEntity['default']
+ });
+ $scope.detailsObject[attr] = row[attr].value;
+ });
+ $scope.detailFields = details;
+ aggregateColumn();
+ resizer();
+ };
+
+ // called from html ng-style="getTableHeight()"
+ $scope.getTableHeight = function () {
+ return {
+ height: (Math.max($scope.detailFields.length, 15) * 30 + 46) + 'px'
+ };
+ };
+ var updateExpandedEntities = function () {
+ let tree = $('#entityTree').fancytree('getTree');
+ if (tree) {
+ let q = d3.queue(10);
+ let expanded = getExpanded(tree);
+ expanded.forEach( function (node) {
+ q.defer(q_updateTableData, node.key, node.key === $scope.selectedEntity);
+ });
- q.await(function (error) {
- if (error)
- QDR.log.error(error.message);
+ q.await(function (error) {
+ if (error)
+ QDRLog.error(error.message);
- if (!tree.getActiveNode()) {
- if ($scope.ActivatedKey) {
- let node = tree.getNodeByKey($scope.ActivatedKey);
- if (node) {
- node.setActive(true, {noEvents: true});
- }
+ if (!tree.getActiveNode()) {
+ if ($scope.ActivatedKey) {
+ let node = tree.getNodeByKey($scope.ActivatedKey);
+ if (node) {
+ node.setActive(true, {noEvents: true});
}
- if (!tree.getActiveNode()) {
- let first = tree.getFirstChild();
- if (first) {
- let child = first.getFirstChild();
- if (child)
- first = child;
- }
- first.setActive(true);
+ }
+ if (!tree.getActiveNode()) {
+ let first = tree.getFirstChild();
+ if (first) {
+ let child = first.getFirstChild();
+ if (child)
+ first = child;
}
+ first.setActive(true);
}
+ }
- d3.selectAll('.ui-effects-placeholder').style('height', '0px');
- resizer();
+ d3.selectAll('.ui-effects-placeholder').style('height', '0px');
+ resizer();
- last_updated = Date.now();
- });
- }
- };
-
- // The selection dropdown (list of routers) was changed.
- $scope.selectNode = function(node) {
- $scope.selectedNode = node.name;
- $scope.selectedNodeId = node.id;
- $timeout( function () {
- setCurrentNode();
- updateNow = true;
+ last_updated = Date.now();
});
- };
+ }
+ };
- $scope.$watch('ActivatedKey', function(newValue, oldValue) {
- if (newValue !== oldValue) {
- localStorage[ActivatedKey] = $scope.ActivatedKey;
- }
- });
- $scope.$watch('selectedEntity', function(newValue, oldValue) {
- if (newValue !== oldValue) {
- localStorage['QDRSelectedEntity'] = $scope.selectedEntity;
- $scope.operations = lookupOperations();
- }
- });
- $scope.$watch('selectedNode', function(newValue, oldValue) {
- if (newValue !== oldValue) {
- localStorage['QDRSelectedNode'] = $scope.selectedNode;
- localStorage['QDRSelectedNodeId'] = $scope.selectedNodeId;
- }
- });
- $scope.$watch('selectedRecordName', function(newValue, oldValue) {
- if (newValue != oldValue) {
- localStorage['QDRSelectedRecordName'] = $scope.selectedRecordName;
- }
+ // The selection dropdown (list of routers) was changed.
+ $scope.selectNode = function(node) {
+ $scope.selectedNode = node.name;
+ $scope.selectedNodeId = node.id;
+ $timeout( function () {
+ setCurrentNode();
+ updateNow = true;
});
+ };
- /* Called periodically to refresh the data on the page */
- var q_updateTableData = function (entity, expand, callback) {
- // don't update the data when on the operations tabs
- if ($scope.currentMode.id !== 'attributes') {
- callback(null);
- return;
- }
- var gotNodeInfo = function (nodeName, dotentity, response) {
- let tableRows = [];
- let records = response.results;
- let aggregates = response.aggregates;
- let attributeNames = response.attributeNames;
- // If !attributeNmes then there was an error getting the records for this entity
- if (attributeNames) {
- let nameIndex = attributeNames.indexOf('name');
- let identityIndex = attributeNames.indexOf('identity');
- let ent = QDRService.management.schema().entityTypes[entity];
- for (let i=0; i<records.length; ++i) {
- let record = records[i];
- let aggregate = aggregates ? aggregates[i] : undefined;
- let row = {};
- let rowName;
- if (nameIndex > -1) {
- rowName = record[nameIndex];
- if (!rowName && identityIndex > -1) {
- rowName = record[nameIndex] = (dotentity + '/' + record[identityIndex]);
- }
- }
- if (!rowName) {
- let msg = 'response attributeNames did not contain a name field';
- QDR.log.error(msg);
- callback(Error(msg));
- return;
+ $scope.$watch('ActivatedKey', function(newValue, oldValue) {
+ if (newValue !== oldValue) {
+ localStorage[ActivatedKey] = $scope.ActivatedKey;
+ }
+ });
+ $scope.$watch('selectedEntity', function(newValue, oldValue) {
+ if (newValue !== oldValue) {
+ localStorage['QDRSelectedEntity'] = $scope.selectedEntity;
+ $scope.operations = lookupOperations();
+ }
+ });
+ $scope.$watch('selectedNode', function(newValue, oldValue) {
+ if (newValue !== oldValue) {
+ localStorage['QDRSelectedNode'] = $scope.selectedNode;
+ localStorage['QDRSelectedNodeId'] = $scope.selectedNodeId;
+ }
+ });
+ $scope.$watch('selectedRecordName', function(newValue, oldValue) {
+ if (newValue != oldValue) {
+ localStorage['QDRSelectedRecordName'] = $scope.selectedRecordName;
+ }
+ });
+
+ /* Called periodically to refresh the data on the page */
+ var q_updateTableData = function (entity, expand, callback) {
+ // don't update the data when on the operations tabs
+ if ($scope.currentMode.id !== 'attributes') {
+ callback(null);
+ return;
+ }
+ var gotNodeInfo = function (nodeName, dotentity, response) {
+ let tableRows = [];
+ let records = response.results;
+ let aggregates = response.aggregates;
+ let attributeNames = response.attributeNames;
+ // If !attributeNmes then there was an error getting the records for this entity
+ if (attributeNames) {
+ let nameIndex = attributeNames.indexOf('name');
+ let identityIndex = attributeNames.indexOf('identity');
+ let ent = QDRService.management.schema().entityTypes[entity];
+ for (let i=0; i<records.length; ++i) {
+ let record = records[i];
+ let aggregate = aggregates ? aggregates[i] : undefined;
+ let row = {};
+ let rowName;
+ if (nameIndex > -1) {
+ rowName = record[nameIndex];
+ if (!rowName && identityIndex > -1) {
+ rowName = record[nameIndex] = (dotentity + '/' + record[identityIndex]);
}
- for (let j=0; j<attributeNames.length; ++j) {
- let col = attributeNames[j];
- row[col] = {value: record[j], type: undefined, graph: false, title: '', aggregate: '', aggregateTip: ''};
- if (ent) {
- let att = ent.attributes[col];
- if (att) {
- row[col].type = att.type;
- row[col].graph = att.graph;
- row[col].title = att.description;
-
- if (aggregate) {
- if (att.graph) {
- row[col].aggregate = att.graph ? aggregate[j].sum : '';
- let tip = [];
- aggregate[j].detail.forEach( function (line) {
- tip.push(line);
- });
- row[col].aggregateTip = angular.toJson(tip);
- }
+ }
+ if (!rowName) {
+ let msg = 'response attributeNames did not contain a name field';
+ QDRLog.error(msg);
+ callback(Error(msg));
+ return;
+ }
+ for (let j=0; j<attributeNames.length; ++j) {
+ let col = attributeNames[j];
+ row[col] = {value: record[j], type: undefined, graph: false, title: '', aggregate: '', aggregateTip: ''};
+ if (ent) {
+ let att = ent.attributes[col];
+ if (att) {
+ row[col].type = att.type;
+ row[col].graph = att.graph;
+ row[col].title = att.description;
+
+ if (aggregate) {
+ if (att.graph) {
+ row[col].aggregate = att.graph ? aggregate[j].sum : '';
+ let tip = [];
+ aggregate[j].detail.forEach( function (line) {
+ tip.push(line);
+ });
+ row[col].aggregateTip = angular.toJson(tip);
}
}
}
}
- tableRows.push(row);
- }
- tableRows.sort( function (a, b) { return a.name.value.localeCompare(b.name.value); });
- selectRow({entity: dotentity, rows: tableRows, expand: expand});
- }
- callback(null); // let queue handler know we are done
- };
- // if this entity should show an aggregate column, send the request to get the info for this entity from all the nedes
- if (aggregateEntities.indexOf(entity) > -1) {
- let nodeIdList = QDRService.management.topology.nodeIdList();
- QDRService.management.topology.getMultipleNodeInfo(nodeIdList, entity, [], gotNodeInfo, $scope.selectedNodeId);
- } else {
- QDRService.management.topology.fetchEntity($scope.selectedNodeId, entity, [], gotNodeInfo);
- }
- };
-
- // tableRows are the records that were returned, this populates the left hand tree on the page
- var selectRow = function (info) {
- updateTreeChildren(info.entity, info.rows, info.expand);
- };
- $scope.detailFields = [];
-
- $scope.addToGraph = function(rowEntity) {
- let chart = QDRChartService.registerChart(
- {nodeId: $scope.selectedNodeId,
- entity: $scope.selectedEntity,
- name: $scope.selectedRecordName,
- attr: rowEntity.name,
- forceCreate: true});
-
- doDialog('tmplChartConfig.html', chart);
- };
-
- $scope.addAllToGraph = function(rowEntity) {
- let chart = QDRChartService.registerChart({
- nodeId: $scope.selectedNodeId,
- entity: $scope.selectedEntity,
- name: $scope.selectedRecordName,
- attr: rowEntity.name,
- type: 'rate',
- rateWindow: updateInterval,
- visibleDuration: 0.25,
- forceCreate: true,
- aggregate: true});
- doDialog('tmplChartConfig.html', chart);
- };
-
- // The ui-popover dynamic html
- $scope.aggregateTip = '';
- // disable popover tips for non-integer cells
- $scope.aggregateTipEnabled = function (row) {
- let tip = row.entity.aggregateTip;
- return (tip && tip.length) ? 'true' : 'false';
- };
- // convert the aggregate data into a table for the popover tip
- $scope.genAggregateTip = function (row) {
- let tip = row.entity.aggregateTip;
- if (tip && tip.length) {
- let data = angular.fromJson(tip);
- let table = '<table class=\'tiptable\'><tbody>';
- data.forEach (function (row) {
- table += '<tr>';
- table += '<td>' + row.node + '</td><td align=\'right\'>' + QDRService.utilities.pretty(row.val) + '</td>';
- table += '</tr>';
- });
- table += '</tbody></table>';
- $scope.aggregateTip = $sce.trustAsHtml(table);
- }
- };
- var aggregateColumn = function () {
- if ((aggregateEntities.indexOf($scope.selectedEntity) > -1 && $scope.detailCols.length != 3) ||
- (aggregateEntities.indexOf($scope.selectedEntity) == -1 && $scope.detailCols.length != 2)) {
- // column defs have to be reassigned and not spliced, so no push/pop
- $scope.detailCols = [
- {
- field: 'attributeName',
- displayName: 'Attribute',
- cellTemplate: '<div title="{{row.entity.title}}" class="listAttrName ui-grid-cell-contents">{{COL_FIELD CUSTOM_FILTERS | pretty}}<button ng-if="row.entity.graph" title="Click to view/add a graph" ng-click="grid.appScope.addToGraph(row.entity)" ng-class="{\'btn-success\': row.entity.chartExists}" class="btn"><i ng-class="{\'icon-bar-chart\': row.entity.graph == true }"></i></button></div>'
- },
- {
- field: 'attributeValue',
- displayName: 'Value',
- cellTemplate: '<div class="ui-grid-cell-contents" ng-class="{\'changed\': row.entity.changed == 1}">{{COL_FIELD CUSTOM_FILTERS | pretty}}</div>'
}
- ];
- if (aggregateEntities.indexOf($scope.selectedEntity) > -1) {
- $scope.detailCols.push(
- {
- width: '10%',
- field: 'aggregateValue',
- displayName: 'Aggregate',
- cellTemplate: '<div popover-enable="{{grid.appScope.aggregateTipEnabled(row)}}" uib-popover-html="grid.appScope.aggregateTip" popover-append-to-body="true" ng-mouseover="grid.appScope.genAggregateTip(row)" popover-trigger="\'mouseenter\'" class="listAggrValue ui-grid-cell-contents" ng-class="{\'changed\': row.entity.changed == 1}">{{COL_FIELD CUSTOM_FILTERS}} <button title="Click to view/add a graph" ng-if="row.entity.graph" ng-click="grid.appScope.addAllToGraph(row.entity)" ng-class="{\'btn-success\': row.entity.aggchartExists}" class="btn"><i ng-class="{\'icon-bar-chart\': row.entity.graph == true }"></i></button></div>',
- cellClass: 'aggregate'
- }
- );
+ tableRows.push(row);
}
+ tableRows.sort( function (a, b) { return a.name.value.localeCompare(b.name.value); });
+ selectRow({entity: dotentity, rows: tableRows, expand: expand});
}
- if ($scope.selectedRecordName === '')
- $scope.detailCols = [];
-
- $scope.details.columnDefs = $scope.detailCols;
- if ($scope.gridApi)
- $scope.gridApi.core.notifyDataChange( uiGridConstants.dataChange.COLUMN );
- };
-
- $scope.gridApi = undefined;
- // the table on the right of the page contains a row for each field in the selected record in the table on the left
- $scope.desiredTableHeight = 340;
- $scope.detailCols = [];
- $scope.details = {
- data: 'detailFields',
- columnDefs: [
+ callback(null); // let queue handler know we are done
+ };
+ // if this entity should show an aggregate column, send the request to get the info for this entity from all the nedes
+ if (aggregateEntities.indexOf(entity) > -1) {
+ let nodeIdList = QDRService.management.topology.nodeIdList();
+ QDRService.management.topology.getMultipleNodeInfo(nodeIdList, entity, [], gotNodeInfo, $scope.selectedNodeId);
+ } else {
+ QDRService.management.topology.fetchEntity($scope.selectedNodeId, entity, [], gotNodeInfo);
+ }
+ };
+
+ // tableRows are the records that were returned, this populates the left hand tree on the page
+ var selectRow = function (info) {
+ updateTreeChildren(info.entity, info.rows, info.expand);
+ };
+ $scope.detailFields = [];
+
+ $scope.addToGraph = function(rowEntity) {
+ let chart = QDRChartService.registerChart(
+ {nodeId: $scope.selectedNodeId,
+ entity: $scope.selectedEntity,
+ name: $scope.selectedRecordName,
+ attr: rowEntity.name,
+ forceCreate: true});
+
+ doDialog('tmplChartConfig.html', chart);
+ };
+
+ $scope.addAllToGraph = function(rowEntity) {
+ let chart = QDRChartService.registerChart({
+ nodeId: $scope.selectedNodeId,
+ entity: $scope.selectedEntity,
+ name: $scope.selectedRecordName,
+ attr: rowEntity.name,
+ type: 'rate',
+ rateWindow: updateInterval,
+ visibleDuration: 0.25,
+ forceCreate: true,
+ aggregate: true});
+ doDialog('tmplChartConfig.html', chart);
+ };
+
+ // The ui-popover dynamic html
+ $scope.aggregateTip = '';
+ // disable popover tips for non-integer cells
+ $scope.aggregateTipEnabled = function (row) {
+ let tip = row.entity.aggregateTip;
+ return (tip && tip.length) ? 'true' : 'false';
+ };
+ // convert the aggregate data into a table for the popover tip
+ $scope.genAggregateTip = function (row) {
+ let tip = row.entity.aggregateTip;
+ if (tip && tip.length) {
+ let data = angular.fromJson(tip);
+ let table = '<table class=\'tiptable\'><tbody>';
+ data.forEach (function (row) {
+ table += '<tr>';
+ table += '<td>' + row.node + '</td><td align=\'right\'>' + QDRService.utilities.pretty(row.val) + '</td>';
+ table += '</tr>';
+ });
+ table += '</tbody></table>';
+ $scope.aggregateTip = $sce.trustAsHtml(table);
+ }
+ };
+ var aggregateColumn = function () {
+ if ((aggregateEntities.indexOf($scope.selectedEntity) > -1 && $scope.detailCols.length != 3) ||
+ (aggregateEntities.indexOf($scope.selectedEntity) == -1 && $scope.detailCols.length != 2)) {
+ // column defs have to be reassigned and not spliced, so no push/pop
+ $scope.detailCols = [
{
field: 'attributeName',
displayName: 'Attribute',
@@ -733,216 +693,252 @@ var QDR = (function(QDR) {
displayName: 'Value',
cellTemplate: '<div class="ui-grid-cell-contents" ng-class="{\'changed\': row.entity.changed == 1}">{{COL_FIELD CUSTOM_FILTERS | pretty}}</div>'
}
- ],
- enableColumnResize: true,
- enableHorizontalScrollbar: 0,
- enableVerticalScrollbar: 0,
- multiSelect: false,
- jqueryUIDraggable: true,
- excessRows: 20,
- onRegisterApi: function(gridApi) {
- $scope.gridApi = gridApi;
+ ];
+ if (aggregateEntities.indexOf($scope.selectedEntity) > -1) {
+ $scope.detailCols.push(
+ {
+ width: '10%',
+ field: 'aggregateValue',
+ displayName: 'Aggregate',
+ cellTemplate: '<div popover-enable="{{grid.appScope.aggregateTipEnabled(row)}}" uib-popover-html="grid.appScope.aggregateTip" popover-append-to-body="true" ng-mouseover="grid.appScope.genAggregateTip(row)" popover-trigger="\'mouseenter\'" class="listAggrValue ui-grid-cell-contents" ng-class="{\'changed\': row.entity.changed == 1}">{{COL_FIELD CUSTOM_FILTERS}} <button title="Click to view/add a graph" ng-if="row.entity.graph" ng-click="grid.appScope.addAllToGraph(row.entity)" ng-class="{\'btn-success\': row.entity.aggchartExists}" class="btn"><i ng-class="{\'icon-bar-chart\': row.entity.graph == true }"></i></button></div>',
+ cellClass: 'aggregate'
+ }
+ );
}
- };
-
- $scope.$on('$destroy', function() {
- clearTimeout(updateIntervalHandle);
- $(window).off('resize', resizer);
- });
-
- function gotMethodResponse (entity, context) {
- let statusCode = context.message.application_properties.statusCode, note;
- if (statusCode < 200 || statusCode >= 300) {
- note = 'Failed to ' + $filter('Pascalcase')($scope.currentMode.op) + ' ' + entity + ': ' + context.message.application_properties.statusDescription;
- QDR.Core.notification('error', note);
- QDR.log.error('Error ' + note);
- } else {
- note = entity + ' ' + $filter('Pascalcase')($scope.currentMode.op) + 'd';
- QDR.Core.notification('success', note);
- QDR.log.debug('Success ' + note);
- $scope.selectMode($scope.modes[0]);
+ }
+ if ($scope.selectedRecordName === '')
+ $scope.detailCols = [];
+
+ $scope.details.columnDefs = $scope.detailCols;
+ if ($scope.gridApi)
+ $scope.gridApi.core.notifyDataChange( uiGridConstants.dataChange.COLUMN );
+ };
+
+ $scope.gridApi = undefined;
+ // the table on the right of the page contains a row for each field in the selected record in the table on the left
+ $scope.desiredTableHeight = 340;
+ $scope.detailCols = [];
+ $scope.details = {
+ data: 'detailFields',
+ columnDefs: [
+ {
+ field: 'attributeName',
+ displayName: 'Attribute',
+ cellTemplate: '<div title="{{row.entity.title}}" class="listAttrName ui-grid-cell-contents">{{COL_FIELD CUSTOM_FILTERS | pretty}}<button ng-if="row.entity.graph" title="Click to view/add a graph" ng-click="grid.appScope.addToGraph(row.entity)" ng-class="{\'btn-success\': row.entity.chartExists}" class="btn"><i ng-class="{\'icon-bar-chart\': row.entity.graph == true }"></i></button></div>'
+ },
+ {
+ field: 'attributeValue',
+ displayName: 'Value',
+ cellTemplate: '<div class="ui-grid-cell-contents" ng-class="{\'changed\': row.entity.changed == 1}">{{COL_FIELD CUSTOM_FILTERS | pretty}}</div>'
}
+ ],
+ enableColumnResize: true,
+ enableHorizontalScrollbar: 0,
+ enableVerticalScrollbar: 0,
+ multiSelect: false,
+ jqueryUIDraggable: true,
+ excessRows: 20,
+ onRegisterApi: function(gridApi) {
+ $scope.gridApi = gridApi;
}
- $scope.ok = function () {
- let attributes = {};
- $scope.detailFields.forEach( function (field) {
- let value = field.rawValue;
- if (field.input === 'input') {
- if (field.type === 'text' || field.type === 'disabled')
- value = field.attributeValue;
- } else if (field.input === 'select') {
- value = field.selected;
- } else if (field.input === 'boolean') {
- value = field.rawValue;
- }
- if (value === '')
- value = undefined;
-
- if ((value && value != field['default']) || field.required || (field.name === 'role')) {
- if (field.name !== 'identity')
- attributes[field.name] = value;
- }
- });
- QDRService.management.connection.sendMethod($scope.currentNode.id, $scope.selectedEntity, attributes, $scope.currentMode.op)
- .then(function (response) {gotMethodResponse($scope.selectedEntity, response.context);});
- };
- $scope.remove = function () {
- let identity = $scope.selectedTreeNode.data.details.identity.value;
- let attributes = {type: $scope.selectedEntity, identity: identity};
- QDRService.management.connection.sendMethod($scope.currentNode.id, $scope.selectedEntity, attributes, $scope.currentMode.op)
- .then(function (response) {gotMethodResponse($scope.selectedEntity, response.context);});
- };
-
- function doDialog(template, chart) {
-
- $uibModal.open({
- backdrop: true,
- keyboard: true,
- backdropClick: true,
- templateUrl: QDR.templatePath + template,
- controller: 'QDR.ChartDialogController',
- resolve: {
- chart: function() {
- return chart;
- },
- updateTick: function () {
- return function () {};
- },
- dashboard: function () {
- return $scope;
- },
- adding: function () {
- return true;
- }
- }
- }).result.then(function() {
- QDRChartService.unRegisterChart(chart);
- });
+ };
+
+ $scope.$on('$destroy', function() {
+ clearTimeout(updateIntervalHandle);
+ $(window).off('resize', resizer);
+ });
+
+ function gotMethodResponse (entity, context) {
+ let statusCode = context.message.application_properties.statusCode, note;
+ if (statusCode < 200 || statusCode >= 300) {
+ note = 'Failed to ' + $filter('Pascalcase')($scope.currentMode.op) + ' ' + entity + ': ' + context.message.application_properties.statusDescription;
+ QDRCore.notification('error', note);
+ QDRLog.error('Error ' + note);
+ } else {
+ note = entity + ' ' + $filter('Pascalcase')($scope.currentMode.op) + 'd';
+ QDRCore.notification('success', note);
+ QDRLog.debug('Success ' + note);
+ $scope.selectMode($scope.modes[0]);
}
- var setCurrentNode = function () {
- let currentNode;
- $scope.nodes.some( function (node, i) {
- if (node.name === $scope.selectedNode) {
- currentNode = $scope.nodes[i];
- return true;
- }
- });
- if ($scope.currentNode !== currentNode)
- $scope.currentNode = currentNode;
- };
+ }
+ $scope.ok = function () {
+ let attributes = {};
+ $scope.detailFields.forEach( function (field) {
+ let value = field.rawValue;
+ if (field.input === 'input') {
+ if (field.type === 'text' || field.type === 'disabled')
+ value = field.attributeValue;
+ } else if (field.input === 'select') {
+ value = field.selected;
+ } else if (field.input === 'boolean') {
+ value = field.rawValue;
+ }
+ if (value === '')
+ value = undefined;
- let treeReady = false;
- let serviceReady = false;
- $scope.largeNetwork = QDRService.management.topology.isLargeNetwork();
- if ($scope.largeNetwork)
- aggregateEntities = [];
-
- // called after we know for sure the schema is fetched and the routers are all ready
- QDRService.management.topology.addUpdatedAction('initList', function () {
- QDRService.management.topology.stopUpdating();
- QDRService.management.topology.delUpdatedAction('initList');
-
- $scope.nodes = QDRService.management.topology.nodeList().sort(function (a, b) { return a.name.toLowerCase() > b.name.toLowerCase();});
- // unable to get node list? Bail.
- if ($scope.nodes.length == 0) {
- $location.path('/' + QDR.pluginName + '/connect');
- $location.search('org', 'list');
+ if ((value && value != field['default']) || field.required || (field.name === 'role')) {
+ if (field.name !== 'identity')
+ attributes[field.name] = value;
}
- if (!angular.isDefined($scope.selectedNode)) {
- //QDR.log.debug("selectedNode was " + $scope.selectedNode);
- if ($scope.nodes.length > 0) {
- $scope.selectedNode = $scope.nodes[0].name;
- $scope.selectedNodeId = $scope.nodes[0].id;
- //QDR.log.debug("forcing selectedNode to " + $scope.selectedNode);
+ });
+ QDRService.management.connection.sendMethod($scope.currentNode.id, $scope.selectedEntity, attributes, $scope.currentMode.op)
+ .then(function (response) {gotMethodResponse($scope.selectedEntity, response.context);});
+ };
+ $scope.remove = function () {
+ let identity = $scope.selectedTreeNode.data.details.identity.value;
+ let attributes = {type: $scope.selectedEntity, identity: identity};
+ QDRService.management.connection.sendMethod($scope.currentNode.id, $scope.selectedEntity, attributes, $scope.currentMode.op)
+ .then(function (response) {gotMethodResponse($scope.selectedEntity, response.context);});
+ };
+
+ function doDialog(template, chart) {
+
+ $uibModal.open({
+ backdrop: true,
+ keyboard: true,
+ backdropClick: true,
+ templateUrl: QDRTemplatePath + template,
+ controller: 'QDR.ChartDialogController',
+ resolve: {
+ chart: function() {
+ return chart;
+ },
+ updateTick: function () {
+ return function () {};
+ },
+ dashboard: function () {
+ return $scope;
+ },
+ adding: function () {
+ return true;
}
}
- setCurrentNode();
- if ($scope.currentNode == undefined) {
- if ($scope.nodes.length > 0) {
- $scope.selectedNode = $scope.nodes[0].name;
- $scope.selectedNodeId = $scope.nodes[0].id;
- $scope.currentNode = $scope.nodes[0];
- }
+ }).result.then(function() {
+ QDRChartService.unRegisterChart(chart);
+ });
+ }
+ var setCurrentNode = function () {
+ let currentNode;
+ $scope.nodes.some( function (node, i) {
+ if (node.name === $scope.selectedNode) {
+ currentNode = $scope.nodes[i];
+ return true;
+ }
+ });
+ if ($scope.currentNode !== currentNode)
+ $scope.currentNode = currentNode;
+ };
+
+ let treeReady = false;
+ let serviceReady = false;
+ $scope.largeNetwork = QDRService.management.topology.isLargeNetwork();
+ if ($scope.largeNetwork)
+ aggregateEntities = [];
+
+ // called after we know for sure the schema is fetched and the routers are all ready
+ QDRService.management.topology.addUpdatedAction('initList', function () {
+ QDRService.management.topology.stopUpdating();
+ QDRService.management.topology.delUpdatedAction('initList');
+
+ $scope.nodes = QDRService.management.topology.nodeList().sort(function (a, b) { return a.name.toLowerCase() > b.name.toLowerCase();});
+ // unable to get node list? Bail.
+ if ($scope.nodes.length == 0) {
+ $location.path('/connect');
+ $location.search('org', 'list');
+ }
+ if (!angular.isDefined($scope.selectedNode)) {
+ //QDRLog.debug("selectedNode was " + $scope.selectedNode);
+ if ($scope.nodes.length > 0) {
+ $scope.selectedNode = $scope.nodes[0].name;
+ $scope.selectedNodeId = $scope.nodes[0].id;
+ //QDRLog.debug("forcing selectedNode to " + $scope.selectedNode);
+ }
+ }
+ setCurrentNode();
+ if ($scope.currentNode == undefined) {
+ if ($scope.nodes.length > 0) {
+ $scope.selectedNode = $scope.nodes[0].name;
+ $scope.selectedNodeId = $scope.nodes[0].id;
+ $scope.currentNode = $scope.nodes[0];
+ }
+ }
+ let sortedEntities = Object.keys(QDRService.management.schema().entityTypes).sort();
+ sortedEntities.forEach( function (entity) {
+ if (excludedEntities.indexOf(entity) == -1) {
+ if (!angular.isDefined($scope.selectedEntity))
+ $scope.selectedEntity = entity;
+ $scope.operations = lookupOperations();
+ let e = new QDRFolder(entity);
+ e.typeName = 'entity';
+ e.key = entity;
+ e.expanded = (expandedList.indexOf(entity) > -1);
+ let placeHolder = new QDRLeaf('loading...');
+ placeHolder.addClass = 'loading';
+ e.children = [placeHolder];
+ entityTreeChildren.push(e);
}
- let sortedEntities = Object.keys(QDRService.management.schema().entityTypes).sort();
- sortedEntities.forEach( function (entity) {
- if (excludedEntities.indexOf(entity) == -1) {
- if (!angular.isDefined($scope.selectedEntity))
- $scope.selectedEntity = entity;
- $scope.operations = lookupOperations();
- let e = new QDR.Folder(entity);
- e.typeName = 'entity';
- e.key = entity;
- e.expanded = (expandedList.indexOf(entity) > -1);
- let placeHolder = new QDR.Leaf('loading...');
- placeHolder.addClass = 'loading';
- e.children = [placeHolder];
- entityTreeChildren.push(e);
+ });
+ serviceReady = true;
+ initTree();
+ });
+ // called by ng-init="treeReady()" in tmplListTree.html
+ $scope.treeReady = function () {
+ treeReady = true;
+ initTree();
+ };
+
+ // this gets called once tree is initialized
+ var onTreeInitialized = function () {
+ updateExpandedEntities();
+ };
+
+ var initTree = function () {
+ if (!treeReady || !serviceReady)
+ return;
+ $('#entityTree').fancytree({
+ activate: onTreeNodeActivated,
+ expand: onTreeNodeExpanded,
+ collapse: onTreeNodeCollapsed,
+ beforeActivate: onTreeNodeBeforeActivate,
+ beforeSelect: function(event, data){
+ // A node is about to be selected: prevent this for folders:
+ if( data.node.isFolder() ){
+ return false;
}
- });
- serviceReady = true;
- initTree();
+ },
+ init: onTreeInitialized,
+ selectMode: 1,
+ autoCollapse: $scope.largeNetwork,
+ activeVisible: !$scope.largeNetwork,
+ clickFolderMode: 3,
+ debugLevel: 0,
+ extraClasses: {
+ expander: 'fa-angle',
+ connector: 'fancytree-no-connector'
+ },
+ source: entityTreeChildren
});
- // called by ng-init="treeReady()" in tmplListTree.html
- $scope.treeReady = function () {
- treeReady = true;
- initTree();
- };
-
- // this gets called once tree is initialized
- var onTreeInitialized = function () {
- updateExpandedEntities();
- };
+ };
+ QDRService.management.topology.ensureAllEntities({entity: 'connection'}, function () {
+ QDRService.management.topology.setUpdateEntities(['connection']);
+ // keep the list of routers up to date
+ QDRService.management.topology.startUpdating(true);
+ });
+
+ updateIntervalHandle = setInterval(function () {
+ if (!treeReady || !serviceReady)
+ return;
- var initTree = function () {
- if (!treeReady || !serviceReady)
- return;
- $('#entityTree').fancytree({
- activate: onTreeNodeActivated,
- expand: onTreeNodeExpanded,
- collapse: onTreeNodeCollapsed,
- beforeActivate: onTreeNodeBeforeActivate,
- beforeSelect: function(event, data){
- // A node is about to be selected: prevent this for folders:
- if( data.node.isFolder() ){
- return false;
- }
- },
- init: onTreeInitialized,
- selectMode: 1,
- autoCollapse: $scope.largeNetwork,
- activeVisible: !$scope.largeNetwork,
- clickFolderMode: 3,
- debugLevel: 0,
- extraClasses: {
- expander: 'fa-angle',
- connector: 'fancytree-no-connector'
- },
- source: entityTreeChildren
+ let now = Date.now();
+ if (((now - last_updated) >= updateInterval) || updateNow) {
+ updateNow = false;
+ $timeout( function () {
+ updateExpandedEntities();
+ resizer();
});
- };
- QDRService.management.topology.ensureAllEntities({entity: 'connection'}, function () {
- QDRService.management.topology.setUpdateEntities(['connection']);
- // keep the list of routers up to date
- QDRService.management.topology.startUpdating(true);
- });
-
- updateIntervalHandle = setInterval(function () {
- if (!treeReady || !serviceReady)
- return;
-
- let now = Date.now();
- if (((now - last_updated) >= updateInterval) || updateNow) {
- updateNow = false;
- $timeout( function () {
- updateExpandedEntities();
- resizer();
- });
- }
- }, 100);
-
- }]);
-
- return QDR;
+ }
+ }, 100);
-} (QDR || {}));
\ No newline at end of file
+ }
+}
+ListController.$inject = ['QDRService', 'QDRChartService', '$scope', '$log', '$location', '$uibModal', '$filter', '$timeout', 'uiGridConstants', '$sce'];
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/qdrListChart.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrListChart.js b/console/stand-alone/plugin/js/qdrListChart.js
deleted file mode 100644
index 711046e..0000000
--- a/console/stand-alone/plugin/js/qdrListChart.js
+++ /dev/null
@@ -1,146 +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 angular */
-
-/**
- * @module QDR
- */
-
-var QDR = (function(QDR) {
-
- QDR.module.controller('QDR.ListChartController', function ($scope, $uibModalInstance, $uibModal, $location, QDRService, QDRChartService, chart, nodeName) {
-
- $scope.chart = chart;
- $scope.dialogSvgChart = null;
- let updateTimer = null;
- $scope.svgDivId = 'dialogChart'; // the div id for the svg chart
-
- $scope.showChartsPage = function () {
- cleanup();
- $uibModalInstance.close(true);
- $location.path(QDR.pluginRoot + '/charts');
- };
-
- $scope.addHChart = function () {
- QDRChartService.addHDash($scope.chart);
- cleanup();
- $uibModalInstance.close(true);
- };
-
- $scope.addToDashboardLink = function () {
- let href = '#/' + QDR.pluginName + '/charts';
- let size = angular.toJson({
- size_x: 2,
- size_y: 2
- });
-
- let params = angular.toJson({chid: $scope.chart.id()});
- let title = 'Dispatch - ' + nodeName;
- return '/hawtio/#/dashboard/add?tab=dashboard' +
- '&href=' + encodeURIComponent(href) +
- '&routeParams=' + encodeURIComponent(params) +
- '&title=' + encodeURIComponent(title) +
- '&size=' + encodeURIComponent(size);
- };
-
-
- $scope.addChartsPage = function () {
- QDRChartService.addDashboard($scope.chart);
- };
-
- $scope.delChartsPage = function () {
- QDRChartService.delDashboard($scope.chart);
- };
-
- $scope.isOnChartsPage = function () {
- return $scope.chart.dashboard;
- };
-
- var showChart = function () {
- // we need a width and height before generating the chart
- let div = angular.element('#pfDialogChart');
- if (!div.width()) {
- setTimeout(showChart, 100);
- return;
- }
- $scope.pfDialogSvgChart = new QDRChartService.pfAreaChart($scope.chart, 'pfDialogChart');
- updateDialogChart();
- };
- showChart();
-
- var updateDialogChart = function () {
- if ($scope.pfDialogSvgChart) {
- $scope.pfDialogSvgChart.tick();
- }
- if (updateTimer)
- clearTimeout(updateTimer);
- updateTimer = setTimeout(updateDialogChart, 1000);
- };
-
- var cleanup = function () {
- if (updateTimer) {
- clearTimeout(updateTimer);
- updateTimer = null;
- }
- if (!$scope.chart.hdash && !$scope.chart.dashboard)
- QDRChartService.unRegisterChart($scope.chart); // remove the chart
-
- };
- $scope.ok = function () {
- cleanup();
- $uibModalInstance.close(true);
- };
-
- $scope.editChart = function () {
- doDialog('tmplChartConfig.html', chart);
- };
-
- function doDialog(template, chart) {
-
- $uibModal.open({
- backdrop: true,
- keyboard: true,
- backdropClick: true,
- templateUrl: QDR.templatePath + template,
- controller: 'QDR.ChartDialogController',
- resolve: {
- chart: function() {
- return chart;
- },
- updateTick: function () {
- return function () {};
- },
- dashboard: function () {
- return $scope;
- },
- adding: function () {
- return true;
- }
- }
- }).result.then(function() {
- $scope.ok();
- });
- }
-
- });
-
- return QDR;
-
-} (QDR || {}));
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[7/8] qpid-dispatch git commit: DISPATCH-1049 Add console tests
Posted by ea...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/package-lock.json
----------------------------------------------------------------------
diff --git a/console/stand-alone/package-lock.json b/console/stand-alone/package-lock.json
index a94c1cf..daacf41 100644
--- a/console/stand-alone/package-lock.json
+++ b/console/stand-alone/package-lock.json
@@ -10,11 +10,19 @@
"integrity": "sha1-z6I7xYQPkQTOMqZedNt+epdLvuE=",
"dev": true,
"requires": {
- "acorn": "5.6.1",
+ "acorn": "5.7.1",
"css": "2.2.3",
"normalize-path": "2.1.1",
"source-map": "0.5.7",
"through2": "2.0.3"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
}
},
"@gulp-sourcemaps/map-sources": {
@@ -33,12 +41,43 @@
"integrity": "sha512-mQjDxyOM1Cpocd+vm1kZBP7smwKZ4TNokFeds9LV7OZibmPJFEzY3+xZMrKfUdNT71lv8GoCPD6upKwHxubClw==",
"dev": true
},
+ "@types/mocha": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.2.tgz",
+ "integrity": "sha512-tfg9rh2qQhBW6SBqpvfqTgU6lHWGhQURoTrn7NeDF+CEkO9JGYbkzU23EXu6p3bnvDxLeeSX8ohAA23urvWeNw==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "10.3.4",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-10.3.4.tgz",
+ "integrity": "sha512-YMLlzdeNnAyLrQew39IFRkMacAR5BqKGIEei9ZjdHsIZtv+ZWKYTu1i7QJhetxQ9ReXx8w5f+cixdHZG3zgMQA==",
+ "dev": true
+ },
+ "JSONStream": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.3.tgz",
+ "integrity": "sha512-3Sp6WZZ/lXl+nTDoGpGWHEpTnnC6X5fnkolYZR6nwIfzbxxvA8utPWe1gCt7i0m9uVGsSz2IS8K8mJ7HmlduMg==",
+ "dev": true,
+ "requires": {
+ "jsonparse": "1.3.1",
+ "through": "2.3.8"
+ }
+ },
"acorn": {
- "version": "5.6.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.6.1.tgz",
- "integrity": "sha512-XH4o5BK5jmw9PzSGK7mNf+/xV+mPxQxGZoeC36OVsJZYV77JAG9NnI7T90hoUpI/C1TOfXWTvugRdZ9ZR3iE2Q==",
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz",
+ "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==",
"dev": true
},
+ "acorn-dynamic-import": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz",
+ "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==",
+ "dev": true,
+ "requires": {
+ "acorn": "5.7.1"
+ }
+ },
"acorn-jsx": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
@@ -56,6 +95,17 @@
}
}
},
+ "acorn-node": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.5.2.tgz",
+ "integrity": "sha512-krFKvw/d1F17AN3XZbybIUzEY4YEPNiGo05AfP3dBlfVKrMHETKpgjpuZkSF8qDNt9UkQcqj7am8yJLseklCMg==",
+ "dev": true,
+ "requires": {
+ "acorn": "5.7.1",
+ "acorn-dynamic-import": "3.0.0",
+ "xtend": "4.0.1"
+ }
+ },
"ajv": {
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
@@ -125,17 +175,17 @@
"integrity": "sha512-yzcHpPMLQl0232nDzm5P4iAFTFQ9dMw0QgFLuKYbDj9M0xJ62z0oudYD/Lvh1pWfRsukiytP4Xj6BHOSrSXP8A=="
},
"angular-ui-grid": {
- "version": "4.4.11",
- "resolved": "https://registry.npmjs.org/angular-ui-grid/-/angular-ui-grid-4.4.11.tgz",
- "integrity": "sha512-tOZrlgmmV8LXS5yDXxry53uibZxFOC3dNNUOH+5AA0SwVTg1B0rE4+4zdU7NfW6bKClshoPE7mik8/VrS1rXAQ==",
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/angular-ui-grid/-/angular-ui-grid-4.5.1.tgz",
+ "integrity": "sha512-asiuS57fEpakHjQ2R2po8FNFuSnc7B+Tj3j7FuroFBUQrjLnaF/5jNnpOVVBM2DzPcyOEjbE47bLk1PXG63fOQ==",
"requires": {
- "angular": "1.7.0"
+ "angular": "1.7.2"
},
"dependencies": {
"angular": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/angular/-/angular-1.7.0.tgz",
- "integrity": "sha512-3LboCLjrOuC7dWh953O0+dI3dJ7PexYRSCIrfqoN5qoHyja/wak3eWoxPKb2Sl2qwiPbrUV5KJXwgpUQ48McBQ=="
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/angular/-/angular-1.7.2.tgz",
+ "integrity": "sha512-JcKKJbBdybUsmQ6x1M3xWyTYQ/ioVKJhSByEAjqrhmlOfvMFdhfMqAx5KIo8rLGk4DFolYPcCSgssjgTVjCtRQ=="
}
}
},
@@ -193,10 +243,13 @@
"dev": true
},
"ansi-styles": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
- "dev": true
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "1.9.2"
+ }
},
"ansi-wrap": {
"version": "0.1.0",
@@ -239,13 +292,10 @@
}
},
"arr-diff": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
- "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
- "dev": true,
- "requires": {
- "arr-flatten": "1.1.0"
- }
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+ "dev": true
},
"arr-filter": {
"version": "1.1.2",
@@ -277,12 +327,24 @@
"integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
"dev": true
},
+ "array-differ": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz",
+ "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=",
+ "dev": true
+ },
"array-each": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz",
"integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=",
"dev": true
},
+ "array-filter": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz",
+ "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=",
+ "dev": true
+ },
"array-initial": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz",
@@ -318,6 +380,18 @@
}
}
},
+ "array-map": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
+ "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=",
+ "dev": true
+ },
+ "array-reduce": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz",
+ "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=",
+ "dev": true
+ },
"array-slice": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz",
@@ -370,6 +444,49 @@
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
"dev": true
},
+ "asn1.js": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
+ "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
+ "dev": true,
+ "requires": {
+ "bn.js": "4.11.8",
+ "inherits": "2.0.3",
+ "minimalistic-assert": "1.0.1"
+ }
+ },
+ "assert": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz",
+ "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=",
+ "dev": true,
+ "requires": {
+ "util": "0.10.3"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+ "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
+ "dev": true
+ },
+ "util": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+ "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.1"
+ }
+ }
+ }
+ },
+ "assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+ "dev": true
+ },
"assign-symbols": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
@@ -386,6 +503,14 @@
"once": "1.4.0",
"process-nextick-args": "1.0.7",
"stream-exhaust": "1.0.2"
+ },
+ "dependencies": {
+ "process-nextick-args": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+ "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
+ "dev": true
+ }
}
},
"async-each": {
@@ -418,6 +543,33 @@
"chalk": "1.1.3",
"esutils": "2.0.2",
"js-tokens": "3.0.2"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "2.2.1",
+ "escape-string-regexp": "1.0.5",
+ "has-ansi": "2.0.0",
+ "strip-ansi": "3.0.1",
+ "supports-color": "2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ }
}
},
"babel-core": {
@@ -455,6 +607,12 @@
"requires": {
"ms": "2.0.0"
}
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
}
}
},
@@ -472,6 +630,14 @@
"lodash": "4.17.10",
"source-map": "0.5.7",
"trim-right": "1.0.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
}
},
"babel-helper-builder-binary-assignment-operator-visitor": {
@@ -922,6 +1088,23 @@
"babel-types": "6.26.0"
}
},
+ "babel-polyfill": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz",
+ "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=",
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "core-js": "2.5.7",
+ "regenerator-runtime": "0.10.5"
+ },
+ "dependencies": {
+ "regenerator-runtime": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
+ "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg="
+ }
+ }
+ },
"babel-preset-env": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz",
@@ -960,6 +1143,38 @@
"semver": "5.5.0"
}
},
+ "babel-preset-es2015": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz",
+ "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-check-es2015-constants": "6.22.0",
+ "babel-plugin-transform-es2015-arrow-functions": "6.22.0",
+ "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0",
+ "babel-plugin-transform-es2015-block-scoping": "6.26.0",
+ "babel-plugin-transform-es2015-classes": "6.24.1",
+ "babel-plugin-transform-es2015-computed-properties": "6.24.1",
+ "babel-plugin-transform-es2015-destructuring": "6.23.0",
+ "babel-plugin-transform-es2015-duplicate-keys": "6.24.1",
+ "babel-plugin-transform-es2015-for-of": "6.23.0",
+ "babel-plugin-transform-es2015-function-name": "6.24.1",
+ "babel-plugin-transform-es2015-literals": "6.22.0",
+ "babel-plugin-transform-es2015-modules-amd": "6.24.1",
+ "babel-plugin-transform-es2015-modules-commonjs": "6.26.2",
+ "babel-plugin-transform-es2015-modules-systemjs": "6.24.1",
+ "babel-plugin-transform-es2015-modules-umd": "6.24.1",
+ "babel-plugin-transform-es2015-object-super": "6.24.1",
+ "babel-plugin-transform-es2015-parameters": "6.24.1",
+ "babel-plugin-transform-es2015-shorthand-properties": "6.24.1",
+ "babel-plugin-transform-es2015-spread": "6.22.0",
+ "babel-plugin-transform-es2015-sticky-regex": "6.24.1",
+ "babel-plugin-transform-es2015-template-literals": "6.22.0",
+ "babel-plugin-transform-es2015-typeof-symbol": "6.23.0",
+ "babel-plugin-transform-es2015-unicode-regex": "6.24.1",
+ "babel-plugin-transform-regenerator": "6.26.0"
+ }
+ },
"babel-register": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz",
@@ -979,7 +1194,6 @@
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
- "dev": true,
"requires": {
"core-js": "2.5.7",
"regenerator-runtime": "0.11.1"
@@ -1120,12 +1334,6 @@
"kind-of": "6.0.2"
}
},
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
- "dev": true
- },
"kind-of": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
@@ -1134,16 +1342,39 @@
}
}
},
+ "base64-js": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
+ "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==",
+ "dev": true
+ },
+ "beeper": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz",
+ "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=",
+ "dev": true
+ },
"binary-extensions": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz",
"integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=",
"dev": true
},
- "bluebird": {
- "version": "3.5.1",
- "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
- "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
+ "bl": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
+ "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
+ "dev": true,
+ "requires": {
+ "readable-stream": "2.3.6",
+ "safe-buffer": "5.1.2"
+ }
+ },
+ "bn.js": {
+ "version": "4.11.8",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
+ "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
+ "dev": true
},
"bootstrap": {
"version": "3.3.7",
@@ -1171,16 +1402,195 @@
"repeat-element": "1.1.2"
}
},
+ "brorand": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+ "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
+ "dev": true
+ },
+ "browser-pack": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz",
+ "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==",
+ "dev": true,
+ "requires": {
+ "JSONStream": "1.3.3",
+ "combine-source-map": "0.8.0",
+ "defined": "1.0.0",
+ "safe-buffer": "5.1.2",
+ "through2": "2.0.3",
+ "umd": "3.0.3"
+ }
+ },
+ "browser-resolve": {
+ "version": "1.11.3",
+ "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz",
+ "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==",
+ "dev": true,
+ "requires": {
+ "resolve": "1.1.7"
+ },
+ "dependencies": {
+ "resolve": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+ "dev": true
+ }
+ }
+ },
+ "browser-stdout": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+ "dev": true
+ },
+ "browserify": {
+ "version": "16.2.2",
+ "resolved": "https://registry.npmjs.org/browserify/-/browserify-16.2.2.tgz",
+ "integrity": "sha512-fMES05wq1Oukts6ksGUU2TMVHHp06LyQt0SIwbXIHm7waSrQmNBZePsU0iM/4f94zbvb/wHma+D1YrdzWYnF/A==",
+ "dev": true,
+ "requires": {
+ "JSONStream": "1.3.3",
+ "assert": "1.4.1",
+ "browser-pack": "6.1.0",
+ "browser-resolve": "1.11.3",
+ "browserify-zlib": "0.2.0",
+ "buffer": "5.1.0",
+ "cached-path-relative": "1.0.1",
+ "concat-stream": "1.6.2",
+ "console-browserify": "1.1.0",
+ "constants-browserify": "1.0.0",
+ "crypto-browserify": "3.12.0",
+ "defined": "1.0.0",
+ "deps-sort": "2.0.0",
+ "domain-browser": "1.2.0",
+ "duplexer2": "0.1.4",
+ "events": "2.1.0",
+ "glob": "7.1.2",
+ "has": "1.0.3",
+ "htmlescape": "1.1.1",
+ "https-browserify": "1.0.0",
+ "inherits": "2.0.3",
+ "insert-module-globals": "7.2.0",
+ "labeled-stream-splicer": "2.0.1",
+ "mkdirp": "0.5.1",
+ "module-deps": "6.1.0",
+ "os-browserify": "0.3.0",
+ "parents": "1.0.1",
+ "path-browserify": "0.0.1",
+ "process": "0.11.10",
+ "punycode": "1.4.1",
+ "querystring-es3": "0.2.1",
+ "read-only-stream": "2.0.0",
+ "readable-stream": "2.3.6",
+ "resolve": "1.8.1",
+ "shasum": "1.0.2",
+ "shell-quote": "1.6.1",
+ "stream-browserify": "2.0.1",
+ "stream-http": "2.8.3",
+ "string_decoder": "1.1.1",
+ "subarg": "1.0.0",
+ "syntax-error": "1.4.0",
+ "through2": "2.0.3",
+ "timers-browserify": "1.4.2",
+ "tty-browserify": "0.0.1",
+ "url": "0.11.0",
+ "util": "0.10.4",
+ "vm-browserify": "1.1.0",
+ "xtend": "4.0.1"
+ }
+ },
+ "browserify-aes": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+ "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+ "dev": true,
+ "requires": {
+ "buffer-xor": "1.0.3",
+ "cipher-base": "1.0.4",
+ "create-hash": "1.2.0",
+ "evp_bytestokey": "1.0.3",
+ "inherits": "2.0.3",
+ "safe-buffer": "5.1.2"
+ }
+ },
+ "browserify-cipher": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
+ "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
+ "dev": true,
+ "requires": {
+ "browserify-aes": "1.2.0",
+ "browserify-des": "1.0.1",
+ "evp_bytestokey": "1.0.3"
+ }
+ },
+ "browserify-des": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.1.tgz",
+ "integrity": "sha512-zy0Cobe3hhgpiOM32Tj7KQ3Vl91m0njwsjzZQK1L+JDf11dzP9qIvjreVinsvXrgfjhStXwUWAEpB9D7Gwmayw==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "1.0.4",
+ "des.js": "1.0.0",
+ "inherits": "2.0.3"
+ }
+ },
+ "browserify-rsa": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+ "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
+ "dev": true,
+ "requires": {
+ "bn.js": "4.11.8",
+ "randombytes": "2.0.6"
+ }
+ },
+ "browserify-sign": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
+ "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
+ "dev": true,
+ "requires": {
+ "bn.js": "4.11.8",
+ "browserify-rsa": "4.0.1",
+ "create-hash": "1.2.0",
+ "create-hmac": "1.1.7",
+ "elliptic": "6.4.0",
+ "inherits": "2.0.3",
+ "parse-asn1": "5.1.1"
+ }
+ },
+ "browserify-zlib": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+ "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+ "dev": true,
+ "requires": {
+ "pako": "1.0.6"
+ }
+ },
"browserslist": {
"version": "3.2.8",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz",
"integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==",
"dev": true,
"requires": {
- "caniuse-lite": "1.0.30000847",
+ "caniuse-lite": "1.0.30000856",
"electron-to-chromium": "1.3.48"
}
},
+ "buffer": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.1.0.tgz",
+ "integrity": "sha512-YkIRgwsZwJWTnyQrsBTWefizHh+8GYj3kbL1BTiAQ/9pwpino0G7B2gp5tx/FUBqUlvtxV85KNR3mwfAtv15Yw==",
+ "dev": true,
+ "requires": {
+ "base64-js": "1.3.0",
+ "ieee754": "1.1.12"
+ }
+ },
"buffer-equal": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz",
@@ -1193,6 +1603,12 @@
"integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==",
"dev": true
},
+ "buffer-xor": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+ "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
+ "dev": true
+ },
"bufferstreams": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/bufferstreams/-/bufferstreams-1.1.3.tgz",
@@ -1208,6 +1624,12 @@
"integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
"dev": true
},
+ "builtin-status-codes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
+ "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
+ "dev": true
+ },
"c3": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/c3/-/c3-0.4.23.tgz",
@@ -1231,16 +1653,14 @@
"to-object-path": "0.3.0",
"union-value": "1.0.0",
"unset-value": "1.0.0"
- },
- "dependencies": {
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
- "dev": true
- }
}
},
+ "cached-path-relative": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.1.tgz",
+ "integrity": "sha1-0JxLUoAKpMB44t2BqGmqyQ0uVOc=",
+ "dev": true
+ },
"caller-path": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
@@ -1263,22 +1683,34 @@
"dev": true
},
"caniuse-lite": {
- "version": "1.0.30000847",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000847.tgz",
- "integrity": "sha512-Weo+tRtVWcN2da782Ebx/27hFNEb+KP+uP6tdqAa+2S5bp1zOJhVH9tEpDygagrfvU4QjeuPwi/5VGsgT4SLaA==",
+ "version": "1.0.30000856",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000856.tgz",
+ "integrity": "sha512-x3mYcApHMQemyaHuH/RyqtKCGIYTgEA63fdi+VBvDz8xUSmRiVWTLeyKcoGQCGG6UPR9/+4qG4OKrTa6aSQRKg==",
"dev": true
},
+ "chai": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz",
+ "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=",
+ "dev": true,
+ "requires": {
+ "assertion-error": "1.1.0",
+ "check-error": "1.0.2",
+ "deep-eql": "3.0.1",
+ "get-func-name": "2.0.0",
+ "pathval": "1.1.0",
+ "type-detect": "4.0.8"
+ }
+ },
"chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
+ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
"dev": true,
"requires": {
- "ansi-styles": "2.2.1",
+ "ansi-styles": "3.2.1",
"escape-string-regexp": "1.0.5",
- "has-ansi": "2.0.0",
- "strip-ansi": "3.0.1",
- "supports-color": "2.0.0"
+ "supports-color": "5.4.0"
}
},
"chardet": {
@@ -1287,6 +1719,12 @@
"integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=",
"dev": true
},
+ "check-error": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
+ "dev": true
+ },
"chokidar": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
@@ -1302,9 +1740,30 @@
"is-glob": "2.0.1",
"path-is-absolute": "1.0.1",
"readdirp": "2.1.0"
- }
- },
- "circular-json": {
+ },
+ "dependencies": {
+ "glob-parent": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
+ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
+ "dev": true,
+ "requires": {
+ "is-glob": "2.0.1"
+ }
+ }
+ }
+ },
+ "cipher-base": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.3",
+ "safe-buffer": "5.1.2"
+ }
+ },
+ "circular-json": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
"integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
@@ -1330,12 +1789,6 @@
"requires": {
"is-descriptor": "0.1.6"
}
- },
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
- "dev": true
}
}
},
@@ -1346,6 +1799,14 @@
"dev": true,
"requires": {
"source-map": "0.5.7"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
}
},
"cli-cursor": {
@@ -1401,14 +1862,6 @@
"inherits": "2.0.3",
"process-nextick-args": "2.0.0",
"readable-stream": "2.3.6"
- },
- "dependencies": {
- "process-nextick-args": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
- "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
- "dev": true
- }
}
},
"co": {
@@ -1456,18 +1909,18 @@
}
},
"color-convert": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
- "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz",
+ "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==",
"dev": true,
"requires": {
- "color-name": "1.1.3"
+ "color-name": "1.1.1"
}
},
"color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz",
+ "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=",
"dev": true
},
"color-support": {
@@ -1476,6 +1929,32 @@
"integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
"dev": true
},
+ "combine-source-map": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz",
+ "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=",
+ "dev": true,
+ "requires": {
+ "convert-source-map": "1.1.3",
+ "inline-source-map": "0.6.2",
+ "lodash.memoize": "3.0.4",
+ "source-map": "0.5.7"
+ },
+ "dependencies": {
+ "convert-source-map": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz",
+ "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
"commander": {
"version": "2.15.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
@@ -1513,16 +1992,23 @@
"dev": true,
"requires": {
"source-map": "0.6.1"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
}
},
+ "console-browserify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+ "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
+ "dev": true,
+ "requires": {
+ "date-now": "0.1.4"
+ }
+ },
+ "constants-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
+ "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
+ "dev": true
+ },
"convert-source-map": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz",
@@ -1548,8 +2034,7 @@
"core-js": {
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
- "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==",
- "dev": true
+ "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw=="
},
"core-util-is": {
"version": "1.0.2",
@@ -1557,6 +2042,43 @@
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true
},
+ "create-ecdh": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
+ "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==",
+ "dev": true,
+ "requires": {
+ "bn.js": "4.11.8",
+ "elliptic": "6.4.0"
+ }
+ },
+ "create-hash": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+ "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "1.0.4",
+ "inherits": "2.0.3",
+ "md5.js": "1.3.4",
+ "ripemd160": "2.0.2",
+ "sha.js": "2.4.11"
+ }
+ },
+ "create-hmac": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+ "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "1.0.4",
+ "create-hash": "1.2.0",
+ "inherits": "2.0.3",
+ "ripemd160": "2.0.2",
+ "safe-buffer": "5.1.2",
+ "sha.js": "2.4.11"
+ }
+ },
"cross-spawn": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
@@ -1568,6 +2090,25 @@
"which": "1.3.1"
}
},
+ "crypto-browserify": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
+ "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
+ "dev": true,
+ "requires": {
+ "browserify-cipher": "1.0.1",
+ "browserify-sign": "4.0.4",
+ "create-ecdh": "4.0.3",
+ "create-hash": "1.2.0",
+ "create-hmac": "1.1.7",
+ "diffie-hellman": "5.0.3",
+ "inherits": "2.0.3",
+ "pbkdf2": "3.0.16",
+ "public-encrypt": "4.0.2",
+ "randombytes": "2.0.6",
+ "randomfill": "1.0.4"
+ }
+ },
"css": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/css/-/css-2.2.3.tgz",
@@ -1628,6 +2169,24 @@
"d3-time": "1.0.8"
}
},
+ "dargs": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/dargs/-/dargs-5.1.0.tgz",
+ "integrity": "sha1-7H6lDHhWTNNsnV7Bj2Yyn63ieCk=",
+ "dev": true
+ },
+ "date-now": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
+ "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
+ "dev": true
+ },
+ "dateformat": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz",
+ "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=",
+ "dev": true
+ },
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
@@ -1659,6 +2218,15 @@
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
"dev": true
},
+ "deep-eql": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
+ "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
+ "dev": true,
+ "requires": {
+ "type-detect": "4.0.8"
+ }
+ },
"deep-is": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
@@ -1695,7 +2263,7 @@
"dev": true,
"requires": {
"foreach": "2.0.5",
- "object-keys": "1.0.11"
+ "object-keys": "1.0.12"
}
},
"define-property": {
@@ -1737,12 +2305,6 @@
"kind-of": "6.0.2"
}
},
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
- "dev": true
- },
"kind-of": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
@@ -1751,6 +2313,12 @@
}
}
},
+ "defined": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
+ "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
+ "dev": true
+ },
"del": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz",
@@ -1763,6 +2331,51 @@
"p-map": "1.2.0",
"pify": "3.0.0",
"rimraf": "2.6.2"
+ },
+ "dependencies": {
+ "globby": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+ "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
+ "dev": true,
+ "requires": {
+ "array-union": "1.0.2",
+ "glob": "7.1.2",
+ "object-assign": "4.1.1",
+ "pify": "2.3.0",
+ "pinkie-promise": "2.0.1"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ }
+ }
+ }
+ }
+ },
+ "deps-sort": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz",
+ "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=",
+ "dev": true,
+ "requires": {
+ "JSONStream": "1.3.3",
+ "shasum": "1.0.2",
+ "subarg": "1.0.0",
+ "through2": "2.0.3"
+ }
+ },
+ "des.js": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
+ "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.3",
+ "minimalistic-assert": "1.0.1"
}
},
"detect-file": {
@@ -1786,18 +2399,40 @@
"integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=",
"dev": true
},
+ "detective": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/detective/-/detective-5.1.0.tgz",
+ "integrity": "sha512-TFHMqfOvxlgrfVzTEkNBSh9SvSNX/HfF4OFI2QFGCyPm02EsyILqnUeb5P6q7JZ3SFNTBL5t2sePRgrN4epUWQ==",
+ "dev": true,
+ "requires": {
+ "acorn-node": "1.5.2",
+ "defined": "1.0.0",
+ "minimist": "1.2.0"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "dev": true
+ }
+ }
+ },
"diff": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
"dev": true
},
- "dispatch-management": {
- "version": "0.1.21",
- "resolved": "https://registry.npmjs.org/dispatch-management/-/dispatch-management-0.1.21.tgz",
- "integrity": "sha512-BfD3w/61q4mshOKIQKpzS5NBZNiUkJgdL65soyBghsU+ICFgshXWxwHsIl4fPaTezR3IuHAaWqS4wGnzAfhDlg==",
+ "diffie-hellman": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+ "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
+ "dev": true,
"requires": {
- "d3-queue": "3.0.7"
+ "bn.js": "4.11.8",
+ "miller-rabin": "4.0.1",
+ "randombytes": "2.0.6"
}
},
"doctrine": {
@@ -1809,6 +2444,21 @@
"esutils": "2.0.2"
}
},
+ "domain-browser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
+ "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
+ "dev": true
+ },
+ "duplexer2": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+ "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "2.3.6"
+ }
+ },
"duplexify": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz",
@@ -1837,6 +2487,21 @@
"integrity": "sha1-07DYWTgUBE4JLs4hCPw6ya6kuQA=",
"dev": true
},
+ "elliptic": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz",
+ "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=",
+ "dev": true,
+ "requires": {
+ "bn.js": "4.11.8",
+ "brorand": "1.1.0",
+ "hash.js": "1.1.4",
+ "hmac-drbg": "1.0.1",
+ "inherits": "2.0.3",
+ "minimalistic-assert": "1.0.1",
+ "minimalistic-crypto-utils": "1.0.1"
+ }
+ },
"end-of-stream": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
@@ -1847,9 +2512,9 @@
}
},
"error-ex": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
- "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"dev": true,
"requires": {
"is-arrayish": "0.2.1"
@@ -1926,7 +2591,7 @@
"file-entry-cache": "2.0.0",
"functional-red-black-tree": "1.0.1",
"glob": "7.1.2",
- "globals": "11.5.0",
+ "globals": "11.7.0",
"ignore": "3.3.8",
"imurmurhash": "0.1.4",
"inquirer": "3.3.0",
@@ -1957,30 +2622,10 @@
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "1.9.1"
- }
- },
- "chalk": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
- "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "3.2.1",
- "escape-string-regexp": "1.0.5",
- "supports-color": "5.4.0"
- }
- },
"globals": {
- "version": "11.5.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.5.0.tgz",
- "integrity": "sha512-hYyf+kI8dm3nORsiiXUQigOU62hDLfJ9G01uyGMxhc6BKsircrUhC4uJPQPUSuq2GrTmiiEt7ewxlMdBewfmKQ==",
+ "version": "11.7.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz",
+ "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==",
"dev": true
},
"strip-ansi": {
@@ -1991,15 +2636,6 @@
"requires": {
"ansi-regex": "3.0.0"
}
- },
- "supports-color": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
- "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
- "dev": true,
- "requires": {
- "has-flag": "3.0.0"
- }
}
}
},
@@ -2025,7 +2661,7 @@
"integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==",
"dev": true,
"requires": {
- "acorn": "5.6.1",
+ "acorn": "5.7.1",
"acorn-jsx": "3.0.1"
}
},
@@ -2075,6 +2711,52 @@
"es5-ext": "0.10.45"
}
},
+ "events": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-2.1.0.tgz",
+ "integrity": "sha512-3Zmiobend8P9DjmKAty0Era4jV8oJ0yGYe2nJJAxgymF9+N8F2m0hhZiMoWtcfepExzNKZumFU3ksdQbInGWCg==",
+ "dev": true
+ },
+ "evp_bytestokey": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+ "dev": true,
+ "requires": {
+ "md5.js": "1.3.4",
+ "safe-buffer": "5.1.2"
+ }
+ },
+ "execa": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz",
+ "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "6.0.5",
+ "get-stream": "3.0.0",
+ "is-stream": "1.1.0",
+ "npm-run-path": "2.0.2",
+ "p-finally": "1.0.0",
+ "signal-exit": "3.0.2",
+ "strip-eof": "1.0.0"
+ },
+ "dependencies": {
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "1.0.4",
+ "path-key": "2.0.1",
+ "semver": "5.5.0",
+ "shebang-command": "1.2.0",
+ "which": "1.3.1"
+ }
+ }
+ }
+ },
"expand-brackets": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
@@ -2147,6 +2829,14 @@
"dev": true,
"requires": {
"is-extglob": "1.0.0"
+ },
+ "dependencies": {
+ "is-extglob": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+ "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
+ "dev": true
+ }
}
},
"fancy-log": {
@@ -2214,6 +2904,17 @@
"randomatic": "3.0.0",
"repeat-element": "1.1.2",
"repeat-string": "1.6.1"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+ "dev": true,
+ "requires": {
+ "isarray": "1.0.0"
+ }
+ }
}
},
"find-up": {
@@ -2238,12 +2939,6 @@
"resolve-dir": "1.0.1"
},
"dependencies": {
- "arr-diff": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
- "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
- "dev": true
- },
"array-unique": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
@@ -2468,12 +3163,6 @@
"kind-of": "6.0.2"
}
},
- "is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
- "dev": true
- },
"is-glob": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
@@ -2503,12 +3192,6 @@
}
}
},
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
- "dev": true
- },
"kind-of": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
@@ -2637,6 +3320,12 @@
"integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=",
"dev": true
},
+ "fork-stream": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz",
+ "integrity": "sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA=",
+ "dev": true
+ },
"fragment-cache": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
@@ -3209,12 +3898,30 @@
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
"dev": true
},
+ "get-assigned-identifiers": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz",
+ "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==",
+ "dev": true
+ },
"get-caller-file": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz",
"integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=",
"dev": true
},
+ "get-func-name": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
+ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
+ "dev": true
+ },
+ "get-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+ "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
+ "dev": true
+ },
"get-value": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
@@ -3243,19 +3950,42 @@
"requires": {
"glob-parent": "2.0.0",
"is-glob": "2.0.1"
+ },
+ "dependencies": {
+ "glob-parent": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
+ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
+ "dev": true,
+ "requires": {
+ "is-glob": "2.0.1"
+ }
+ }
}
},
"glob-parent": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
- "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
"dev": true,
"requires": {
- "is-glob": "2.0.1"
- }
- },
- "glob-stream": {
- "version": "6.1.0",
+ "is-glob": "3.1.0",
+ "path-dirname": "1.0.2"
+ },
+ "dependencies": {
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "2.1.1"
+ }
+ }
+ }
+ },
+ "glob-stream": {
+ "version": "6.1.0",
"resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz",
"integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=",
"dev": true,
@@ -3270,33 +4000,6 @@
"remove-trailing-separator": "1.1.0",
"to-absolute-glob": "2.0.2",
"unique-stream": "2.2.1"
- },
- "dependencies": {
- "glob-parent": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
- "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
- "dev": true,
- "requires": {
- "is-glob": "3.1.0",
- "path-dirname": "1.0.2"
- }
- },
- "is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
- "dev": true
- },
- "is-glob": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
- "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
- "dev": true,
- "requires": {
- "is-extglob": "2.1.1"
- }
- }
}
},
"glob-watcher": {
@@ -3341,27 +4044,6 @@
"integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
"dev": true
},
- "globby": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
- "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
- "dev": true,
- "requires": {
- "array-union": "1.0.2",
- "glob": "7.1.2",
- "object-assign": "4.1.1",
- "pify": "2.3.0",
- "pinkie-promise": "2.0.1"
- },
- "dependencies": {
- "pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
- "dev": true
- }
- }
- },
"glogg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.1.tgz",
@@ -3377,6 +4059,12 @@
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
"dev": true
},
+ "growl": {
+ "version": "1.10.5",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+ "dev": true
+ },
"gulp": {
"version": "github:gulpjs/gulp#71c094a51c7972d26f557899ddecab0210ef3776",
"dev": true,
@@ -3412,12 +4100,6 @@
"v8flags": "3.1.1",
"yargs": "7.1.0"
}
- },
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
- "dev": true
}
}
},
@@ -3475,6 +4157,17 @@
"plugin-error": "1.0.1"
}
},
+ "gulp-if": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/gulp-if/-/gulp-if-2.0.2.tgz",
+ "integrity": "sha1-pJe351cwBQQcqivIt92jyARE1ik=",
+ "dev": true,
+ "requires": {
+ "gulp-match": "1.0.3",
+ "ternary-stream": "2.0.1",
+ "through2": "2.0.3"
+ }
+ },
"gulp-insert": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/gulp-insert/-/gulp-insert-0.5.0.tgz",
@@ -3511,6 +4204,30 @@
}
}
},
+ "gulp-match": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/gulp-match/-/gulp-match-1.0.3.tgz",
+ "integrity": "sha1-kcfA1/Kb7NZgbVfYCn+Hdqh6uo4=",
+ "dev": true,
+ "requires": {
+ "minimatch": "3.0.4"
+ }
+ },
+ "gulp-mocha": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/gulp-mocha/-/gulp-mocha-6.0.0.tgz",
+ "integrity": "sha512-FfBldW5ttnDpKf4Sg6/BLOOKCCbr5mbixDGK1t02/8oSrTCwNhgN/mdszG3cuQuYNzuouUdw4EH/mlYtgUscPg==",
+ "dev": true,
+ "requires": {
+ "dargs": "5.1.0",
+ "execa": "0.10.0",
+ "mocha": "5.2.0",
+ "npm-run-path": "2.0.2",
+ "plugin-error": "1.0.1",
+ "supports-color": "5.4.0",
+ "through2": "2.0.3"
+ }
+ },
"gulp-ng-annotate": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/gulp-ng-annotate/-/gulp-ng-annotate-2.1.0.tgz",
@@ -3578,9 +4295,9 @@
}
},
"gulp-rename": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.2.3.tgz",
- "integrity": "sha512-CmdPM0BjJ105QCX1fk+j7NGhiN/1rCl9HLGss+KllBS/tdYadpjTxqdKyh/5fNV+M3yjT1MFz5z93bXdrTyzAw==",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.3.0.tgz",
+ "integrity": "sha512-nEuZB7/9i0IZ8AXORTizl2QLP9tcC9uWc/s329zElBLJw1CfOhmMXBxwVlCRKjDyrWuhVP0uBKl61KeQ32TiCg==",
"dev": true
},
"gulp-sourcemaps": {
@@ -3591,7 +4308,7 @@
"requires": {
"@gulp-sourcemaps/identity-map": "1.0.1",
"@gulp-sourcemaps/map-sources": "1.0.0",
- "acorn": "5.6.1",
+ "acorn": "5.7.1",
"convert-source-map": "1.5.1",
"css": "2.2.3",
"debug-fabulous": "1.1.0",
@@ -3600,14 +4317,17 @@
"source-map": "0.6.1",
"strip-bom-string": "1.0.0",
"through2": "2.0.3"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
+ }
+ },
+ "gulp-terser": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gulp-terser/-/gulp-terser-1.0.1.tgz",
+ "integrity": "sha512-UwesC0Lma+rk17pS/rFjVWN4zan2v/vRKDnLbZMIZJqP236yVlbzAPvD1VWDi5u3pCYKbLGoI3ynlgVat+7u9A==",
+ "dev": true,
+ "requires": {
+ "plugin-error": "1.0.1",
+ "terser": "3.7.6",
+ "through2": "2.0.3"
}
},
"gulp-tslint": {
@@ -3624,15 +4344,6 @@
"through": "2.3.8"
},
"dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "1.9.1"
- }
- },
"chalk": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz",
@@ -3643,15 +4354,6 @@
"escape-string-regexp": "1.0.5",
"supports-color": "5.4.0"
}
- },
- "supports-color": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
- "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
- "dev": true,
- "requires": {
- "has-flag": "3.0.0"
- }
}
}
},
@@ -3718,12 +4420,6 @@
"arr-union": "2.1.0",
"extend-shallow": "1.1.4"
}
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
}
}
},
@@ -3738,10 +4434,133 @@
"lodash": "4.17.10",
"make-error-cause": "1.2.2",
"through2": "2.0.3",
- "uglify-js": "3.4.0",
+ "uglify-js": "3.4.1",
"vinyl-sourcemaps-apply": "0.2.1"
}
},
+ "gulp-uglifyes": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/gulp-uglifyes/-/gulp-uglifyes-0.1.3.tgz",
+ "integrity": "sha512-p6EzPCBbPo+joHp85NxNcT/aPuodfb+UIYTsEKB8PuxAPwbiqhr3HBrpARqvLyO5VYYjREynnT6Tr++0YLRSUQ==",
+ "dev": true,
+ "requires": {
+ "gulp-util": "3.0.8",
+ "through2": "2.0.3",
+ "uglify-es": "3.3.9"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz",
+ "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==",
+ "dev": true
+ },
+ "uglify-es": {
+ "version": "3.3.9",
+ "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz",
+ "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==",
+ "dev": true,
+ "requires": {
+ "commander": "2.13.0",
+ "source-map": "0.6.1"
+ }
+ }
+ }
+ },
+ "gulp-util": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz",
+ "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=",
+ "dev": true,
+ "requires": {
+ "array-differ": "1.0.0",
+ "array-uniq": "1.0.3",
+ "beeper": "1.1.1",
+ "chalk": "1.1.3",
+ "dateformat": "2.2.0",
+ "fancy-log": "1.3.2",
+ "gulplog": "1.0.0",
+ "has-gulplog": "0.1.0",
+ "lodash._reescape": "3.0.0",
+ "lodash._reevaluate": "3.0.0",
+ "lodash._reinterpolate": "3.0.0",
+ "lodash.template": "3.6.2",
+ "minimist": "1.2.0",
+ "multipipe": "0.1.2",
+ "object-assign": "3.0.0",
+ "replace-ext": "0.0.1",
+ "through2": "2.0.3",
+ "vinyl": "0.5.3"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "2.2.1",
+ "escape-string-regexp": "1.0.5",
+ "has-ansi": "2.0.0",
+ "strip-ansi": "3.0.1",
+ "supports-color": "2.0.0"
+ }
+ },
+ "clone": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+ "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
+ "dev": true
+ },
+ "clone-stats": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz",
+ "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=",
+ "dev": true
+ },
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "dev": true
+ },
+ "object-assign": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz",
+ "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=",
+ "dev": true
+ },
+ "replace-ext": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz",
+ "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ },
+ "vinyl": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz",
+ "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=",
+ "dev": true,
+ "requires": {
+ "clone": "1.0.4",
+ "clone-stats": "0.0.1",
+ "replace-ext": "0.0.1"
+ }
+ }
+ }
+ },
"gulplog": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz",
@@ -3751,6 +4570,15 @@
"glogg": "1.0.1"
}
},
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "1.1.1"
+ }
+ },
"has-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
@@ -3790,14 +4618,6 @@
"get-value": "2.0.6",
"has-values": "1.0.0",
"isobject": "3.0.1"
- },
- "dependencies": {
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
- "dev": true
- }
}
},
"has-values": {
@@ -3841,6 +4661,43 @@
}
}
},
+ "hash-base": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
+ "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.3",
+ "safe-buffer": "5.1.2"
+ }
+ },
+ "hash.js": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.4.tgz",
+ "integrity": "sha512-A6RlQvvZEtFS5fLU43IDu0QUmBy+fDO9VMdTXvufKwIkt/rFfvICAViCax5fbDO4zdNzaC3/27ZhKUok5bAJyw==",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.3",
+ "minimalistic-assert": "1.0.1"
+ }
+ },
+ "he": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
+ "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
+ "dev": true
+ },
+ "hmac-drbg": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+ "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+ "dev": true,
+ "requires": {
+ "hash.js": "1.1.4",
+ "minimalistic-assert": "1.0.1",
+ "minimalistic-crypto-utils": "1.0.1"
+ }
+ },
"home-or-tmp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
@@ -3866,10 +4723,17 @@
"integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==",
"dev": true
},
- "html5shiv": {
- "version": "3.7.3",
- "resolved": "https://registry.npmjs.org/html5shiv/-/html5shiv-3.7.3.tgz",
- "integrity": "sha1-14qEo2e8uacQEA1XgCw4ewhGMdI="
+ "htmlescape": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz",
+ "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=",
+ "dev": true
+ },
+ "https-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
+ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
+ "dev": true
},
"iconv-lite": {
"version": "0.4.23",
@@ -3880,6 +4744,12 @@
"safer-buffer": "2.1.2"
}
},
+ "ieee754": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz",
+ "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==",
+ "dev": true
+ },
"ignore": {
"version": "3.3.8",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.8.tgz",
@@ -3914,6 +4784,23 @@
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"dev": true
},
+ "inline-source-map": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz",
+ "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=",
+ "dev": true,
+ "requires": {
+ "source-map": "0.5.7"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
"inquirer": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
@@ -3942,26 +4829,6 @@
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "1.9.1"
- }
- },
- "chalk": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
- "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "3.2.1",
- "escape-string-regexp": "1.0.5",
- "supports-color": "5.4.0"
- }
- },
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
@@ -3986,18 +4853,27 @@
"requires": {
"ansi-regex": "3.0.0"
}
- },
- "supports-color": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
- "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
- "dev": true,
- "requires": {
- "has-flag": "3.0.0"
- }
}
}
},
+ "insert-module-globals": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.0.tgz",
+ "integrity": "sha512-VE6NlW+WGn2/AeOMd496AHFYmE7eLKkUY6Ty31k4og5vmA3Fjuwe9v6ifH6Xx/Hz27QvdoMoviw1/pqWRB09Sw==",
+ "dev": true,
+ "requires": {
+ "JSONStream": "1.3.3",
+ "acorn-node": "1.5.2",
+ "combine-source-map": "0.8.0",
+ "concat-stream": "1.6.2",
+ "is-buffer": "1.1.6",
+ "path-is-absolute": "1.0.1",
+ "process": "0.11.10",
+ "through2": "2.0.3",
+ "undeclared-identifiers": "1.1.2",
+ "xtend": "4.0.1"
+ }
+ },
"interpret": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz",
@@ -4118,9 +4994,9 @@
"dev": true
},
"is-extglob": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
- "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
"dev": true
},
"is-finite": {
@@ -4148,6 +5024,14 @@
"dev": true,
"requires": {
"is-extglob": "1.0.0"
+ },
+ "dependencies": {
+ "is-extglob": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+ "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
+ "dev": true
+ }
}
},
"is-negated-glob": {
@@ -4213,14 +5097,6 @@
"dev": true,
"requires": {
"isobject": "3.0.1"
- },
- "dependencies": {
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
- "dev": true
- }
}
},
"is-posix-bracket": {
@@ -4256,6 +5132,12 @@
"integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
"dev": true
},
+ "is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+ "dev": true
+ },
"is-unc-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
@@ -4296,13 +5178,10 @@
"dev": true
},
"isobject": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
- "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
- "dev": true,
- "requires": {
- "isarray": "1.0.0"
- }
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
},
"jquery": {
"version": "3.3.1",
@@ -4315,9 +5194,9 @@
"integrity": "sha1-XAgV08xvkP9fqvWyaKbiO0ypBPo="
},
"jquery.fancytree": {
- "version": "2.28.1",
- "resolved": "https://registry.npmjs.org/jquery.fancytree/-/jquery.fancytree-2.28.1.tgz",
- "integrity": "sha512-Vs5ka+Zn0ldZtmtjHtzE/8bzN3RqFb2nbE+nS6ZUy4ISW6KOBGk2QYtLFA8HbPsgkqVnjoDIXvU7VI6CSQ/FmA==",
+ "version": "2.29.0",
+ "resolved": "https://registry.npmjs.org/jquery.fancytree/-/jquery.fancytree-2.29.0.tgz",
+ "integrity": "sha512-n08g4KxmXt1JNUK2D2kRxD7t1r2Nke5OhrMviqfBh5qdcJB1DWXYAZmHYEyVr/r3cpuyU6vy4gMDWHnh30v4vA==",
"requires": {
"jquery": "3.3.1"
}
@@ -4351,9 +5230,9 @@
"dev": true
},
"json-stable-stringify": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
- "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz",
+ "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=",
"dev": true,
"requires": {
"jsonify": "0.0.0"
@@ -4377,6 +5256,12 @@
"integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
"dev": true
},
+ "jsonparse": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
+ "dev": true
+ },
"just-debounce": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz",
@@ -4392,6 +5277,25 @@
"is-buffer": "1.1.6"
}
},
+ "labeled-stream-splicer": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.1.tgz",
+ "integrity": "sha512-MC94mHZRvJ3LfykJlTUipBqenZz1pacOZEMhhQ8dMGcDHs0SBE5GbsavUXV7YtP3icBW17W0Zy1I0lfASmo9Pg==",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.3",
+ "isarray": "2.0.4",
+ "stream-splicer": "2.0.0"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.4.tgz",
+ "integrity": "sha512-GMxXOiUirWg1xTKRipM0Ek07rX+ubx4nNVElTJdNLYmNO/2YrDkgJGw9CljXn+r4EWiDQg/8lsRdHyg2PJuUaA==",
+ "dev": true
+ }
+ }
+ },
"last-run": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz",
@@ -4452,7 +5356,7 @@
"is-plain-object": "2.0.4",
"object.map": "1.0.1",
"rechoir": "0.6.2",
- "resolve": "1.7.1"
+ "resolve": "1.8.1"
}
},
"load-json-file": {
@@ -4482,6 +5386,131 @@
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==",
"dev": true
},
+ "lodash._basecopy": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz",
+ "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=",
+ "dev": true
+ },
+ "lodash._basetostring": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz",
+ "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=",
+ "dev": true
+ },
+ "lodash._basevalues": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz",
+ "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=",
+ "dev": true
+ },
+ "lodash._getnative": {
+ "version": "3.9.1",
+ "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
+ "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=",
+ "dev": true
+ },
+ "lodash._isiterateecall": {
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz",
+ "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=",
+ "dev": true
+ },
+ "lodash._reescape": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz",
+ "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=",
+ "dev": true
+ },
+ "lodash._reevaluate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz",
+ "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=",
+ "dev": true
+ },
+ "lodash._reinterpolate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
+ "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=",
+ "dev": true
+ },
+ "lodash._root": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz",
+ "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=",
+ "dev": true
+ },
+ "lodash.escape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz",
+ "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=",
+ "dev": true,
+ "requires": {
+ "lodash._root": "3.0.1"
+ }
+ },
+ "lodash.isarguments": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
+ "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=",
+ "dev": true
+ },
+ "lodash.isarray": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
+ "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=",
+ "dev": true
+ },
+ "lodash.keys": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
+ "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=",
+ "dev": true,
+ "requires": {
+ "lodash._getnative": "3.9.1",
+ "lodash.isarguments": "3.1.0",
+ "lodash.isarray": "3.0.4"
+ }
+ },
+ "lodash.memoize": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz",
+ "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=",
+ "dev": true
+ },
+ "lodash.restparam": {
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz",
+ "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=",
+ "dev": true
+ },
+ "lodash.template": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz",
+ "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=",
+ "dev": true,
+ "requires": {
+ "lodash._basecopy": "3.0.1",
+ "lodash._basetostring": "3.0.1",
+ "lodash._basevalues": "3.0.0",
+ "lodash._isiterateecall": "3.0.9",
+ "lodash._reinterpolate": "3.0.0",
+ "lodash.escape": "3.2.0",
+ "lodash.keys": "3.1.2",
+ "lodash.restparam": "3.6.1",
+ "lodash.templatesettings": "3.1.1"
+ }
+ },
+ "lodash.templatesettings": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz",
+ "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=",
+ "dev": true,
+ "requires": {
+ "lodash._reinterpolate": "3.0.0",
+ "lodash.escape": "3.2.0"
+ }
+ },
"loose-envify": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
@@ -4571,16 +5600,10 @@
"requires": {
"findup-sync": "2.0.0",
"micromatch": "3.1.10",
- "resolve": "1.7.1",
+ "resolve": "1.8.1",
"stack-trace": "0.0.10"
},
"dependencies": {
- "arr-diff": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
- "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
- "dev": true
- },
"array-unique": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
@@ -4825,12 +5848,6 @@
}
}
},
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
- "dev": true
- },
"kind-of": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
@@ -4866,6 +5883,16 @@
"integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=",
"dev": true
},
+ "md5.js": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz",
+ "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=",
+ "dev": true,
+ "requires": {
+ "hash-base": "3.0.4",
+ "inherits": "2.0.3"
+ }
+ },
"memoizee": {
"version": "0.4.12",
"resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.12.tgz",
@@ -4888,6 +5915,15 @@
"integrity": "sha1-dTHjnUlJwoGma4xabgJl6LBYlNo=",
"dev": true
},
+ "merge-stream": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz",
+ "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "2.3.6"
+ }
+ },
"micromatch": {
"version": "2.3.11",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
@@ -4907,6 +5943,33 @@
"object.omit": "2.0.1",
"parse-glob": "3.0.4",
"regex-cache": "0.4.4"
+ },
+ "dependencies": {
+ "arr-diff": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
+ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "1.1.0"
+ }
+ },
+ "is-extglob": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+ "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
+ "dev": true
+ }
+ }
+ },
+ "miller-rabin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
+ "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+ "dev": true,
+ "requires": {
+ "bn.js": "4.11.8",
+ "brorand": "1.1.0"
}
},
"mimic-fn": {
@@ -4915,6 +5978,18 @@
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
"dev": true
},
+ "minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "dev": true
+ },
+ "minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+ "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
+ "dev": true
+ },
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -4960,11 +6035,97 @@
"minimist": "0.0.8"
}
},
+ "mocha": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz",
+ "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==",
+ "dev": true,
+ "requires": {
+ "browser-stdout": "1.3.1",
+ "commander": "2.15.1",
+ "debug": "3.1.0",
+ "diff": "3.5.0",
+ "escape-string-regexp": "1.0.5",
+ "glob": "7.1.2",
+ "growl": "1.10.5",
+ "he": "1.1.1",
+ "minimatch": "3.0.4",
+ "mkdirp": "0.5.1",
+ "supports-color": "5.4.0"
+ }
+ },
+ "module-deps": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.1.0.tgz",
+ "integrity": "sha512-NPs5N511VD1rrVJihSso/LiBShRbJALYBKzDW91uZYy7BpjnO4bGnZL3HjZ9yKcFdZUWwaYjDz9zxbuP7vKMuQ==",
+
<TRUNCATED>
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[5/8] qpid-dispatch git commit: DISPATCH-1049 Add console tests
Posted by ea...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/qdrChartService.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrChartService.js b/console/stand-alone/plugin/js/qdrChartService.js
index 574ea07..fc53520 100644
--- a/console/stand-alone/plugin/js/qdrChartService.js
+++ b/console/stand-alone/plugin/js/qdrChartService.js
@@ -16,844 +16,825 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
-'use strict';
/* global angular d3 c3 */
-/**
- * @module QDR
- */
-var QDR = (function(QDR) {
-
- // The QDR chart service handles periodic gathering data for charts and displaying the charts
- QDR.module.factory('QDRChartService', ['QDRService',
- function(QDRService) {
-
- let instance = 0; // counter for chart instances
- let bases = [];
- var findBase = function(name, attr, request) {
- for (let i = 0; i < bases.length; ++i) {
- let base = bases[i];
- if (base.equals(name, attr, request))
- return base;
- }
- return null;
- };
-
- function ChartBase(name, attr, request) {
- // the base chart attributes
- this.name = name; // the record's "name" field
- this.attr = attr; // the record's attr field to chart
- this.request = request; // the associated request that fetches the data
-
- // copy the savable properties to an object
- this.copyProps = function(o) {
- o.name = this.name;
- o.attr = this.attr;
- this.request.copyProps(o);
- };
-
- this.equals = function(name, attr, request) {
- return (this.name == name && this.attr == attr && this.request.equals(request));
- };
+import { QDRLogger } from './qdrGlobals.js';
+
+let instance = 0,
+ bases = [];
+
+class ChartBase {
+ constructor(name, attr, request) {
+ // the base chart attributes
+ this.name = name; // the record's "name" field
+ this.attr = attr; // the record's attr field to chart
+ this.request = request; // the associated request that fetches the data
+ // copy the savable properties to an object
+ this.copyProps = function (o) {
+ o.name = this.name;
+ o.attr = this.attr;
+ this.request.copyProps(o);
+ };
+ this.equals = function (name, attr, request) {
+ return (this.name == name && this.attr == attr && this.request.equals(request));
+ };
+ }
+}
+
+// Object that represents a visible chart
+// There can be multiple of these per ChartBase (eg. one rate and one value chart)
+class Chart {
+ constructor(opts, request, QDRService, QDRChartService) {
+ var findBase = function (name, attr, request) {
+ for (let i = 0; i < bases.length; ++i) {
+ let base = bases[i];
+ if (base.equals(name, attr, request))
+ return base;
}
-
- // Object that represents a visible chart
- // There can be multiple of these per ChartBase (eg. one rate and one value chart)
- function Chart(opts, request) { //name, attr, cinstance, request) {
-
- let base = findBase(opts.name, opts.attr, request);
- if (!base) {
- base = new ChartBase(opts.name, opts.attr, request);
- bases.push(base);
+ return null;
+ };
+ let base = findBase(opts.name, opts.attr, request);
+ if (!base) {
+ base = new ChartBase(opts.name, opts.attr, request);
+ bases.push(base);
+ }
+ this.base = base;
+ this.QDRService = QDRService;
+ this.QDRChartService = QDRChartService;
+ this.instance = angular.isDefined(opts.instance) ? opts.instance : ++instance;
+ this.dashboard = false; // is this chart on the dashboard page
+ this.hdash = false; // is this chart on the hawtio dashboard page
+ this.hreq = false; // has this hdash chart been requested
+ this.type = opts.type ? opts.type : 'value'; // value or rate
+ this.rateWindow = opts.rateWindow ? opts.rateWindow : 1000; // calculate the rate of change over this time interval. higher == smother graph
+ this.areaColor = '#32b9f3'; // the chart's area color when not an empty string
+ this.lineColor = '#058dc7'; // the chart's line color when not an empty string
+ this.visibleDuration = opts.visibleDuration ? opts.visibleDuration : opts.type === 'rate' ? 0.25 : 1; // number of minutes of data to show (<= base.duration)
+ this.userTitle = null; // user title overrides title()
+ this.hideLabel = opts.hideLabel;
+ this.hideLegend = opts.hideLegend;
+ // generate a unique id for this chart
+ this.id = function () {
+ let name = this.name();
+ let nameparts = name.split('/');
+ if (nameparts.length == 2)
+ name = nameparts[1];
+ let key = this.QDRService.utilities.nameFromId(this.request().nodeId) + this.request().entity + name + this.attr() + '_' + this.instance + '_' + (this.request().aggregate ? '1' : '0');
+ // remove all characters except letters,numbers, and _
+ return key.replace(/[^\w]/gi, '');
+ };
+ // copy the savable properties to an object
+ this.copyProps = function (o) {
+ o.type = this.type;
+ o.rateWindow = this.rateWindow;
+ o.areaColor = this.areaColor;
+ o.lineColor = this.lineColor;
+ o.visibleDuration = this.visibleDuration;
+ o.userTitle = this.userTitle;
+ o.dashboard = this.dashboard;
+ o.hdash = this.hdash;
+ o.instance = this.instance;
+ this.base.copyProps(o);
+ };
+ this.name = function (_) {
+ if (!arguments.length)
+ return this.base.name;
+ this.base.name = _;
+ return this;
+ };
+ this.attr = function (_) {
+ if (!arguments.length)
+ return this.base.attr;
+ this.base.attr = _;
+ return this;
+ };
+ this.nodeId = function (_) {
+ if (!arguments.length)
+ return this.base.request.nodeId;
+ this.base.request.nodeId = _;
+ return this;
+ };
+ this.entity = function (_) {
+ if (!arguments.length)
+ return this.base.request.entity;
+ this.base.request.entity = _;
+ return this;
+ };
+ this.aggregate = function (_) {
+ if (!arguments.length)
+ return this.base.request.aggregate;
+ this.base.request.aggregate = _;
+ return this;
+ };
+ this.request = function (_) {
+ if (!arguments.length)
+ return this.base.request;
+ this.base.request = _;
+ return this;
+ };
+ this.data = function () {
+ return this.base.request.data(this.base.name, this.base.attr); // refernce to chart's data array
+ };
+ this.interval = function (_) {
+ if (!arguments.length)
+ return this.base.request.interval;
+ this.base.request.interval = _;
+ return this;
+ };
+ this.duration = function (_) {
+ if (!arguments.length)
+ return this.base.request.duration;
+ this.base.request.duration = _;
+ return this;
+ };
+ this.router = function () {
+ return this.QDRService.utilities.nameFromId(this.nodeId());
+ };
+ this.title = function (_) {
+ let name = this.request().aggregate ? 'Aggregate' : this.QDRService.utilities.nameFromId(this.nodeId());
+ let computed = name +
+ ' ' + this.QDRService.utilities.humanify(this.attr()) +
+ ' - ' + this.name();
+ if (!arguments.length)
+ return this.userTitle || computed;
+ // don't store computed title in userTitle
+ if (_ === computed)
+ _ = null;
+ this.userTitle = _;
+ return this;
+ };
+ this.title_short = function () {
+ if (!arguments.length)
+ return this.userTitle || this.name();
+ return this;
+ };
+ this.copy = function () {
+ let chart = this.QDRChartService.registerChart({
+ nodeId: this.nodeId(),
+ entity: this.entity(),
+ name: this.name(),
+ attr: this.attr(),
+ interval: this.interval(),
+ forceCreate: true,
+ aggregate: this.aggregate(),
+ hdash: this.hdash
+ });
+ chart.type = this.type;
+ chart.areaColor = this.areaColor;
+ chart.lineColor = this.lineColor;
+ chart.rateWindow = this.rateWindow;
+ chart.visibleDuration = this.visibleDuration;
+ chart.userTitle = this.userTitle;
+ return chart;
+ };
+ // compare to a chart
+ this.equals = function (c) {
+ return (c.instance == this.instance &&
+ c.base.equals(this.base.name, this.base.attr, this.base.request) &&
+ c.type == this.type &&
+ c.rateWindow == this.rateWindow &&
+ c.areaColor == this.areaColor &&
+ c.lineColor == this.lineColor);
+ };
+ }
+}
+
+
+// Object that represents the management request to fetch and store data for multiple charts
+class ChartRequest {
+ constructor(opts) {
+ this.duration = opts.duration || 10; // number of minutes to keep the data
+ this.nodeId = opts.nodeId; // eg amqp:/_topo/0/QDR.A/$management
+ this.entity = opts.entity; // eg .router.address
+ // sorted since the responses will always be sorted
+ this.aggregate = opts.aggregate; // list of nodeIds for aggregate charts
+ this.datum = {}; // object containing array of arrays for each attr
+ // like {attr1: [[date,value],[date,value]...], attr2: [[date,value]...]}
+ this.interval = opts.interval || 1000; // number of milliseconds between updates to data
+ this.setTimeoutHandle = null; // used to cancel the next request
+ // allow override of normal request's management call to get data
+ this.override = opts.override; // call this instead of internal function to retreive data
+ this.overrideAttrs = opts.overrideAttrs;
+ this.data = function (name, attr) {
+ if (this.datum[name] && this.datum[name][attr])
+ return this.datum[name][attr];
+ return null;
+ };
+ this.addAttrName = function (name, attr) {
+ if (Object.keys(this.datum).indexOf(name) == -1) {
+ this.datum[name] = {};
+ }
+ if (Object.keys(this.datum[name]).indexOf(attr) == -1) {
+ this.datum[name][attr] = [];
+ }
+ };
+ this.addAttrName(opts.name, opts.attr);
+ this.copyProps = function (o) {
+ o.nodeId = this.nodeId;
+ o.entity = this.entity;
+ o.interval = this.interval;
+ o.aggregate = this.aggregate;
+ o.duration = this.duration;
+ };
+ this.removeAttr = function (name, attr) {
+ if (this.datum[name]) {
+ if (this.datum[name][attr]) {
+ delete this.datum[name][attr];
}
- this.base = base;
- this.instance = angular.isDefined(opts.instance) ? opts.instance : ++instance;
- this.dashboard = false; // is this chart on the dashboard page
- this.hdash = false; // is this chart on the hawtio dashboard page
- this.hreq = false; // has this hdash chart been requested
- this.type = opts.type ? opts.type : 'value'; // value or rate
- this.rateWindow = opts.rateWindow ? opts.rateWindow : 1000; // calculate the rate of change over this time interval. higher == smother graph
- this.areaColor = '#32b9f3'; // the chart's area color when not an empty string
- this.lineColor = '#058dc7'; // the chart's line color when not an empty string
- this.visibleDuration = opts.visibleDuration ? opts.visibleDuration : opts.type === 'rate' ? 0.25 : 1; // number of minutes of data to show (<= base.duration)
- this.userTitle = null; // user title overrides title()
- this.hideLabel = opts.hideLabel;
- this.hideLegend = opts.hideLegend;
-
- // generate a unique id for this chart
- this.id = function() {
- let name = this.name();
- let nameparts = name.split('/');
- if (nameparts.length == 2)
- name = nameparts[1];
- let key = QDRService.management.topology.nameFromId(this.request().nodeId) + this.request().entity + name + this.attr() + '_' + this.instance + '_' + (this.request().aggregate ? '1' : '0');
- // remove all characters except letters,numbers, and _
- return key.replace(/[^\w]/gi, '');
- };
- // copy the savable properties to an object
- this.copyProps = function(o) {
- o.type = this.type;
- o.rateWindow = this.rateWindow;
- o.areaColor = this.areaColor;
- o.lineColor = this.lineColor;
- o.visibleDuration = this.visibleDuration;
- o.userTitle = this.userTitle;
- o.dashboard = this.dashboard;
- o.hdash = this.hdash;
- o.instance = this.instance;
- this.base.copyProps(o);
- };
- this.name = function(_) {
- if (!arguments.length) return this.base.name;
- this.base.name = _;
- return this;
- };
- this.attr = function(_) {
- if (!arguments.length) return this.base.attr;
- this.base.attr = _;
- return this;
- };
- this.nodeId = function(_) {
- if (!arguments.length) return this.base.request.nodeId;
- this.base.request.nodeId = _;
- return this;
- };
- this.entity = function(_) {
- if (!arguments.length) return this.base.request.entity;
- this.base.request.entity = _;
- return this;
- };
- this.aggregate = function(_) {
- if (!arguments.length) return this.base.request.aggregate;
- this.base.request.aggregate = _;
- return this;
- };
- this.request = function(_) {
- if (!arguments.length) return this.base.request;
- this.base.request = _;
- return this;
- };
- this.data = function() {
- return this.base.request.data(this.base.name, this.base.attr); // refernce to chart's data array
- };
- this.interval = function(_) {
- if (!arguments.length) return this.base.request.interval;
- this.base.request.interval = _;
- return this;
- };
- this.duration = function(_) {
- if (!arguments.length) return this.base.request.duration;
- this.base.request.duration = _;
- return this;
- };
- this.router = function () {
- return QDRService.management.topology.nameFromId(this.nodeId());
- };
- this.title = function(_) {
- let name = this.request().aggregate ? 'Aggregate' : QDRService.management.topology.nameFromId(this.nodeId());
- let computed = name +
- ' ' + QDRService.utilities.humanify(this.attr()) +
- ' - ' + this.name();
- if (!arguments.length) return this.userTitle || computed;
-
- // don't store computed title in userTitle
- if (_ === computed)
- _ = null;
- this.userTitle = _;
- return this;
- };
- this.title_short = function() {
- if (!arguments.length) return this.userTitle || this.name();
- return this;
- };
- this.copy = function() {
- let chart = self.registerChart({
- nodeId: this.nodeId(),
- entity: this.entity(),
- name: this.name(),
- attr: this.attr(),
- interval: this.interval(),
- forceCreate: true,
- aggregate: this.aggregate(),
- hdash: this.hdash
- });
- chart.type = this.type;
- chart.areaColor = this.areaColor;
- chart.lineColor = this.lineColor;
- chart.rateWindow = this.rateWindow;
- chart.visibleDuration = this.visibleDuration;
- chart.userTitle = this.userTitle;
- return chart;
- };
- // compare to a chart
- this.equals = function(c) {
- return (c.instance == this.instance &&
- c.base.equals(this.base.name, this.base.attr, this.base.request) &&
- c.type == this.type &&
- c.rateWindow == this.rateWindow &&
- c.areaColor == this.areaColor &&
- c.lineColor == this.lineColor);
- };
}
-
- // Object that represents the management request to fetch and store data for multiple charts
- function ChartRequest(opts) { //nodeId, entity, name, attr, interval, aggregate) {
- this.duration = opts.duration || 10; // number of minutes to keep the data
- this.nodeId = opts.nodeId; // eg amqp:/_topo/0/QDR.A/$management
- this.entity = opts.entity; // eg .router.address
- // sorted since the responses will always be sorted
- this.aggregate = opts.aggregate; // list of nodeIds for aggregate charts
- this.datum = {}; // object containing array of arrays for each attr
- // like {attr1: [[date,value],[date,value]...], attr2: [[date,value]...]}
-
- this.interval = opts.interval || 1000; // number of milliseconds between updates to data
- this.setTimeoutHandle = null; // used to cancel the next request
-
- // allow override of normal request's management call to get data
- this.override = opts.override; // call this instead of internal function to retreive data
- this.overrideAttrs = opts.overrideAttrs;
-
- this.data = function(name, attr) {
- if (this.datum[name] && this.datum[name][attr])
- return this.datum[name][attr];
- return null;
- };
- this.addAttrName = function(name, attr) {
- if (Object.keys(this.datum).indexOf(name) == -1) {
- this.datum[name] = {};
- }
- if (Object.keys(this.datum[name]).indexOf(attr) == -1) {
- this.datum[name][attr] = [];
- }
- };
- this.addAttrName(opts.name, opts.attr);
-
- this.copyProps = function(o) {
- o.nodeId = this.nodeId;
- o.entity = this.entity;
- o.interval = this.interval;
- o.aggregate = this.aggregate;
- o.duration = this.duration;
- };
-
- this.removeAttr = function(name, attr) {
- if (this.datum[name]) {
- if (this.datum[name][attr]) {
- delete this.datum[name][attr];
- }
- }
- return this.attrs().length;
- };
-
- this.equals = function(r, entity, aggregate) {
- if (arguments.length == 3) {
- let o = {
- nodeId: r,
- entity: entity,
- aggregate: aggregate
- };
- r = o;
- }
- return (this.nodeId === r.nodeId && this.entity === r.entity && this.aggregate == r.aggregate);
- };
- this.names = function() {
- return Object.keys(this.datum);
- };
- this.attrs = function() {
- let attrs = {};
- Object.keys(this.datum).forEach(function(name) {
- Object.keys(this.datum[name]).forEach(function(attr) {
- attrs[attr] = 1;
- });
- }, this);
- return Object.keys(attrs);
- };
+ return this.attrs().length;
+ };
+ this.equals = function (r, entity, aggregate) {
+ if (arguments.length == 3) {
+ let o = {
+ nodeId: r,
+ entity: entity,
+ aggregate: aggregate
+ };
+ r = o;
}
-
- // Below here are the properties and methods available on QDRChartService
- let self = {
- charts: [], // list of charts to gather data for
- chartRequests: [], // the management request info (multiple charts can be driven off of a single request
-
- init: function() {
- self.loadCharts();
- QDRService.management.connection.addDisconnectAction(function() {
- self.charts.forEach(function(chart) {
- self.unRegisterChart(chart, true);
- });
- QDRService.management.connection.addConnectAction(self.init);
- });
- },
-
- findChartRequest: function(nodeId, entity, aggregate) {
- let ret = null;
- self.chartRequests.some(function(request) {
- if (request.equals(nodeId, entity, aggregate)) {
- ret = request;
- return true;
- }
- });
- return ret;
- },
-
- findCharts: function(opts) { //name, attr, nodeId, entity, hdash) {
- if (!opts.hdash)
- opts.hdash = false; // rather than undefined
- return self.charts.filter(function(chart) {
- return (chart.name() == opts.name &&
- chart.attr() == opts.attr &&
- chart.nodeId() == opts.nodeId &&
- chart.entity() == opts.entity &&
- chart.hdash == opts.hdash);
- });
- },
-
- delChartRequest: function(request) {
- for (let i = 0; i < self.chartRequests.length; ++i) {
- let r = self.chartRequests[i];
- if (request.equals(r)) {
- QDR.log.debug('removed request: ' + request.nodeId + ' ' + request.entity);
- self.chartRequests.splice(i, 1);
- self.stopCollecting(request);
- return;
- }
- }
- },
-
- delChart: function(chart, skipSave) {
- let foundBases = 0;
- for (let i = 0; i < self.charts.length; ++i) {
- let c = self.charts[i];
- if (c.base === chart.base)
- ++foundBases;
- if (c.equals(chart)) {
- self.charts.splice(i, 1);
- if (chart.dashboard && !skipSave)
- self.saveCharts();
- }
- }
- if (foundBases == 1) {
- let baseIndex = bases.indexOf(chart.base);
- bases.splice(baseIndex, 1);
- }
- },
-
- createChart: function (opts, request) {
- return new Chart(opts, request);
- },
- createChartRequest: function (opts) {
- let request = new ChartRequest(opts); //nodeId, entity, name, attr, interval, aggregate);
- request.creationTimestamp = opts.now;
- self.chartRequests.push(request);
- self.startCollecting(request);
- self.sendChartRequest(request, true);
- return request;
- },
- destroyChartRequest: function (request) {
- self.stopCollecting(request);
- self.delChartRequest(request);
- },
-
- registerChart: function(opts) { //nodeId, entity, name, attr, interval, instance, forceCreate, aggregate, hdash) {
- let request = self.findChartRequest(opts.nodeId, opts.entity, opts.aggregate);
- if (request) {
- // add any new attr or name to the list
- request.addAttrName(opts.name, opts.attr);
- } else {
- // the nodeId/entity did not already exist, so add a new request and chart
- QDR.log.debug('added new request: ' + opts.nodeId + ' ' + opts.entity);
- request = self.createChartRequest(opts);
- }
- let charts = self.findCharts(opts); //name, attr, nodeId, entity, hdash);
- let chart;
- if (charts.length == 0 || opts.forceCreate) {
- if (!opts.use_instance && opts.instance)
- delete opts.instance;
- chart = new Chart(opts, request); //opts.name, opts.attr, opts.instance, request);
- self.charts.push(chart);
- } else {
- chart = charts[0];
- }
- return chart;
- },
-
- // remove the chart for name/attr
- // if all attrs are gone for this request, remove the request
- unRegisterChart: function(chart, skipSave) {
- // remove the chart
-
- // TODO: how do we remove charts that were added to the hawtio dashboard but then removed?
- // We don't get a notification that they were removed. Instead, we could just stop sending
- // the request in the background and only send the request when the chart's tick() event is triggered
- //if (chart.hdash) {
- // chart.dashboard = false;
- // self.saveCharts();
- // return;
- //}
-
- for (let i = 0; i < self.charts.length; ++i) {
- let c = self.charts[i];
- if (chart.equals(c)) {
- let request = chart.request();
- self.delChart(chart, skipSave);
- if (request) {
- // see if any other charts use this attr
- for (let j = 0; j < self.charts.length; ++j) {
- let ch = self.charts[j];
- if (ch.attr() == chart.attr() && ch.request().equals(chart.request()))
- return;
- }
- // no other charts use this attr, so remove it
- if (request.removeAttr(chart.name(), chart.attr()) == 0) {
- self.destroyChartRequest(request);
- }
- }
+ return (this.nodeId === r.nodeId && this.entity === r.entity && this.aggregate == r.aggregate);
+ };
+ this.names = function () {
+ return Object.keys(this.datum);
+ };
+ this.attrs = function () {
+ let attrs = {};
+ Object.keys(this.datum).forEach(function (name) {
+ Object.keys(this.datum[name]).forEach(function (attr) {
+ attrs[attr] = 1;
+ });
+ }, this);
+ return Object.keys(attrs);
+ };
+ }
+}
+
+class AreaChart {
+ constructor(chart, chartId, defer, width, QDRService) {
+ if (!chart)
+ return;
+ this.QDRService = QDRService;
+ // reference to underlying chart
+ this.chart = chart;
+ // if this is an aggregate chart, show it stacked
+ this.stacked = chart.request().aggregate;
+ // the id of the html element that is bound to the chart. The svg will be a child of this
+ this.htmlId = chartId;
+ this.colorMap = {};
+ if (!defer)
+ this.generate(width);
+ }
+
+ // create the svg and bind it to the given div.id
+ generate (width) {
+ let chart = this.chart; // for access during chart callbacks
+ let self = this;
+
+ // an array of 10 colors
+ let colors = d3.scale.category10().range();
+ // list of router names. used to get the color index
+ let nameList = this.QDRService.management.topology.nodeNameList();
+ for (let i=0; i<nameList.length; i++) {
+ this.colorMap[nameList[i]] = colors[i % 10];
+ }
+ let nodeName = this.QDRService.utilities.nameFromId(this.chart.base.request.nodeId);
+ this.colorMap[nodeName] = this.chart.areaColor;
+
+ let c3ChartDefaults = $().c3ChartDefaults();
+ let singleAreaChartConfig = c3ChartDefaults.getDefaultSingleAreaConfig();
+ singleAreaChartConfig.bindto = '#' + this.htmlId;
+ singleAreaChartConfig.size = {
+ width: width || 400,
+ height: 200
+ };
+ singleAreaChartConfig.data = {
+ x: 'x', // x-axis is named x
+ columns: [[]],
+ type: 'area-spline'
+ };
+ singleAreaChartConfig.axis = {
+ x: {
+ type: 'timeseries',
+ tick: {
+ format: (function (d) {
+ let data = this.singleAreaChart.data.shown();
+ let first = data[0]['values'][0].x;
+
+ if (d - first == 0) {
+ return d3.timeFormat('%I:%M:%S')(d);
}
- }
- if (!skipSave)
- self.saveCharts();
- },
-
- stopCollecting: function(request) {
- if (request.setTimeoutHandle) {
- clearInterval(request.setTimeoutHandle);
- request.setTimeoutHandle = null;
- }
- },
-
- startCollecting: function(request) {
- request.setTimeoutHandle = setInterval(self.sendChartRequest, request.interval, request);
- },
- shouldRequest: function() {
- // see if any of the charts associated with this request have either dialog, dashboard, or hreq
- return self.charts.some(function(chart) {
- return (chart.dashboard || chart.hreq) || (!chart.dashboard && !chart.hdash);
- });
- },
- // send the request
- sendChartRequest: function(request) {
- if (request.busy)
- return;
- if (self.charts.length > 0 && !self.shouldRequest(request)) {
- return;
- }
- // ensure the response has the name field so we can associate the response values with the correct chart
- let attrs = request.attrs();
- if (attrs.indexOf('name') == -1)
- attrs.push('name');
-
- // this is called when the response is received
- var saveResponse = function(nodeId, entity, response) {
- request.busy = false;
- if (!response || !response.attributeNames)
- return;
- //QDR.log.debug("got chart results for " + nodeId + " " + entity);
- // records is an array that has data for all names
- let records = response.results;
- if (!records)
- return;
+ return d3.timeFormat('%M:%S')(d);
+ }).bind(this),
+ culling: {max: 4}
+ }
+ },
+ y: {
+ tick: {
+ format: function (d) { return d<1 ? d3.format('.2f')(d) : d3.format('.2s')(d); },
+ count: 5
+ }
+ }
+ };
- let now = new Date();
- let cutOff = new Date(now.getTime() - request.duration * 60 * 1000);
- // index of the "name" attr in the response
- let nameIndex = response.attributeNames.indexOf('name');
- if (nameIndex < 0)
- return;
+ if (!chart.hideLabel) {
+ singleAreaChartConfig.axis.x.label = {
+ text: chart.name(),
+ position: 'outer-right'
+ };
- let names = request.names();
- // for each record returned, find the name/attr for this request and save the data with this timestamp
- for (let i = 0; i < records.length; ++i) {
- let name = records[i][nameIndex];
- // if we want to store the values for some attrs for this name
- if (names.indexOf(name) > -1) {
- attrs.forEach(function(attr) {
- let attrIndex = response.attributeNames.indexOf(attr);
- if (records[i][attrIndex] !== undefined) {
- let data = request.data(name, attr); // get a reference to the data array
- if (data) {
-
- if (request.aggregate) {
- data.push([now, response.aggregates[i][attrIndex].sum, response.aggregates[i][attrIndex].detail]);
- } else {
- data.push([now, records[i][attrIndex]]);
- }
- // expire the old data
- while (data[0][0] < cutOff) {
- data.shift();
- }
- }
- }
- });
- }
- }
- };
- request.busy = true;
- // check for override of request
- if (request.override) {
- request.override(request, saveResponse);
- } else {
- // send the appropriate request
- if (request.aggregate) {
- let nodeList = QDRService.management.topology.nodeIdList();
- QDRService.management.topology.getMultipleNodeInfo(nodeList, request.entity, attrs, saveResponse, request.nodeId);
- } else {
- QDRService.management.topology.fetchEntity(request.nodeId, request.entity, attrs, saveResponse);
- }
- }
- },
-
- numCharts: function() {
- return self.charts.filter(function(chart) {
- return chart.dashboard;
- }).length;
- //return self.charts.length;
- },
-
- isAttrCharted: function(nodeId, entity, name, attr, aggregate) {
- let charts = self.findCharts({
- name: name,
- attr: attr,
- nodeId: nodeId,
- entity: entity
- });
- // if any of the matching charts are on the dashboard page, return true
- return charts.some(function(chart) {
- return (chart.dashboard && (aggregate ? chart.aggregate() : !chart.aggregate()));
- });
- },
-
- addHDash: function(chart) {
- chart.hdash = true;
- self.saveCharts();
- },
- delHDash: function(chart) {
- chart.hdash = false;
- self.saveCharts();
- },
- addDashboard: function(chart) {
- chart.dashboard = true;
- self.saveCharts();
- },
- delDashboard: function(chart) {
- chart.dashboard = false;
- self.saveCharts();
- },
- // save the charts to local storage
- saveCharts: function() {
- let minCharts = [];
-
- self.charts.forEach(function(chart) {
- let minChart = {};
- // don't save chart unless it is on the dashboard
- if (chart.dashboard || chart.hdash) {
- chart.copyProps(minChart);
- minCharts.push(minChart);
- }
- });
- localStorage['QDRCharts'] = angular.toJson(minCharts);
- },
- loadCharts: function() {
- let charts = angular.fromJson(localStorage['QDRCharts']);
- if (charts) {
- // get array of known ids
- let nodeList = QDRService.management.topology.nodeIdList();
- charts.forEach(function(chart) {
- // if this chart is not in the current list of nodes, skip
- if (nodeList.indexOf(chart.nodeId) >= 0) {
- if (!angular.isDefined(chart.instance)) {
- chart.instance = ++instance;
- }
- if (chart.instance >= instance)
- instance = chart.instance + 1;
- if (!chart.duration)
- chart.duration = 1;
- if (chart.nodeList)
- chart.aggregate = true;
- if (!chart.hdash)
- chart.hdash = false;
- if (!chart.dashboard)
- chart.dashboard = false;
- if (!chart.hdash && !chart.dashboard)
- chart.dashboard = true;
- if (chart.hdash && chart.dashboard)
- chart.dashboard = false;
- chart.forceCreate = true;
- chart.use_instance = true;
- let newChart = self.registerChart(chart); //chart.nodeId, chart.entity, chart.name, chart.attr, chart.interval, true, chart.aggregate);
- newChart.dashboard = chart.dashboard;
- newChart.hdash = chart.hdash;
- newChart.hreq = false;
- newChart.type = chart.type;
- newChart.rateWindow = chart.rateWindow;
- newChart.areaColor = chart.areaColor ? chart.areaColor : '#32b9f3';
- newChart.lineColor = chart.lineColor ? chart.lineColor : '#058dc7';
- newChart.duration(chart.duration);
- newChart.visibleDuration = chart.visibleDuration ? chart.visibleDuration : newChart.type === 'rate' ? 0.25 : 1;
- if (chart.userTitle)
- newChart.title(chart.userTitle);
- }
- });
+ }
+ singleAreaChartConfig.transition = {
+ duration: 0
+ };
+
+ singleAreaChartConfig.area = {
+ zerobased: false
+ };
+
+ singleAreaChartConfig.tooltip = {
+ contents: function (d) {
+ let d3f = ',';
+ if (chart.type === 'rate')
+ d3f = ',.2f';
+ let zPre = function (i) {
+ if (i < 10) {
+ i = '0' + i;
}
- },
-
- // constructor for a c3 area chart
- pfAreaChart: function (chart, chartId, defer, width) {
- if (!chart)
- return;
-
- // reference to underlying chart
- this.chart = chart;
-
- // if this is an aggregate chart, show it stacked
- this.stacked = chart.request().aggregate;
-
- // the id of the html element that is bound to the chart. The svg will be a child of this
- this.htmlId = chartId;
-
- // an array of 20 colors
- this.colors = d3.scale.category10().range();
-
- if (!defer)
- this.generate(width);
- },
+ return i;
+ };
+ let h = zPre(d[0].x.getHours());
+ let m = zPre(d[0].x.getMinutes());
+ let s = zPre(d[0].x.getSeconds());
+ let table = '<table class=\'dispatch-c3-tooltip\'> <tr><th colspan=\'2\' class=\'text-center\'><strong>'+h+':'+m+':'+s+'</strong></th></tr> <tbody>';
+ for (let i=0; i<d.length; i++) {
+ let c = self.colorMap[d[i].id];
+ let span = `<span class='chart-tip-legend' style='background-color: ${c};'> </span>` + d[i].id;
+ table += ('<tr><td>'+span+'<td>'+d3.format(d3f)(d[i].value)+'</td></tr>');
+ }
+ table += '</tbody></table>';
+ return table;
+ }
+ };
- // aggregate chart is based on pfAreaChart
- pfAggChart: function (chart, chartId, defer) {
- // inherit pfChart's properties, but force a defer
- self.pfAreaChart.call(this, chart, chartId, true);
+ singleAreaChartConfig.title = {
+ text: this.QDRService.utilities.humanify(this.chart.attr())
+ };
- // the request is for aggregate data, but the chart is for the sum and not the detail
- // Explanation: When the chart.request is aggregate, each data point is composed of 3 parts:
- // 1. the datetime stamp
- // 2. the sum of the value for all routers
- // 3. an object with each router's name and value for this data point
- // Normally, an aggregate chart shows lines for each of the routers and ignores the sum
- // For this chart, we want to chart the sum (the 2nd value), so we set stacked to false
- this.stacked = false;
+ singleAreaChartConfig.data.color = (color, d) => {
+ let c = this.colorMap[d.id];
+ return c ? c : color;
+ };
+ singleAreaChartConfig.color.pattern[0] = this.chart.areaColor;
- // let chart legends and tooltips show 'Total' instead of a router name
- this.aggregate = true;
+ //singleAreaChartConfig.data.colors = {};
+ //nameList.forEach( (name, i) => singleAreaChartConfig.data.colors[name] = this.colorMap[name] );
+ singleAreaChartConfig.data.colors = this.colormap;
- if (!defer)
- this.generate();
- }
+ if (!chart.hideLegend) {
+ singleAreaChartConfig.legend = {
+ show: true,
};
- // allow pfAggChart to inherit prototyped methods
- self.pfAggChart.prototype = Object.create(self.pfAreaChart.prototype);
- // except for the constructor
- self.pfAggChart.prototype.constructor = self.pfAggChart;
-
- // create the svg and bind it to the given div.id
- self.pfAreaChart.prototype.generate = function (width) {
- let chart = this.chart; // for access during chart callbacks
- let self = this;
-
- // list of router names. used to get the color index
- let nameList = QDRService.management.topology.nodeNameList();
-
- let c3ChartDefaults = $().c3ChartDefaults();
- let singleAreaChartConfig = c3ChartDefaults.getDefaultSingleAreaConfig();
- singleAreaChartConfig.bindto = '#' + this.htmlId;
- singleAreaChartConfig.size = {
- width: width || 400,
- height: 200
- };
- singleAreaChartConfig.data = {
- x: 'x', // x-axis is named x
- columns: [[]],
- type: 'area-spline'
- };
- singleAreaChartConfig.axis = {
- x: {
- type: 'timeseries',
- tick: {
- format: (function (d) {
- let data = this.singleAreaChart.data.shown();
- let first = data[0]['values'][0].x;
-
- if (d - first == 0) {
- return d3.timeFormat('%I:%M:%S')(d);
- }
- return d3.timeFormat('%M:%S')(d);
- }).bind(this),
- culling: {max: 4}
- }
- },
- y: {
- tick: {
- format: function (d) { return d<1 ? d3.format('.2f')(d) : d3.format('.2s')(d); },
- count: 5
- }
- }
- };
+ }
- if (!chart.hideLabel) {
- singleAreaChartConfig.axis.x.label = {
- text: chart.name(),
- position: 'outer-right'
- };
+ if (this.stacked) {
+ // create a stacked area chart
+ singleAreaChartConfig.data.groups = [this.QDRService.management.topology.nodeNameList()];
+ singleAreaChartConfig.data.order = function (t1, t2) { return t1.id < t2.id; };
+ }
+ this.singleAreaChart = c3.generate(singleAreaChartConfig);
+ }
+
+ chartData() {
+ let data = this.chart.data();
+ let nodeList = this.QDRService.management.topology.nodeNameList();
+
+ // oldest data point that should be visible
+ let now = new Date();
+ let visibleDate = new Date(now.getTime() - this.chart.visibleDuration * 60 * 1000);
+
+ let accessorSingle = function (d, d1, elapsed) {
+ return this.chart.type === 'rate' ? (d1[1] - d[1]) / elapsed : d[1];
+ };
+ let accessorStacked = function (d, d1, elapsed, i) {
+ return this.chart.type === 'rate' ? (d1[2][i].val - d[2][i].val) / elapsed : d[2][i].val;
+ };
+ let accessor = this.stacked ? accessorStacked : accessorSingle;
+
+ let dx = ['x'];
+ let dlines = [];
+ if (this.stacked) {
+ // for stacked, there is a line per router
+ nodeList.forEach( function (node) {
+ dlines.push([node]);
+ });
+ } else {
+ // for non-stacked, there is only one line
+ dlines.push([this.aggregate ? 'Total' : this.chart.router()]);
+ }
+ for (let i=0; i<data.length; i++) {
+ let d = data[i], elapsed = 1, d1;
+ if (d[0] >= visibleDate) {
+ if (this.chart.type === 'rate' && i < data.length-1) {
+ d1 = data[i+1];
+ elapsed = Math.max((d1[0] - d[0]) / 1000, 0.001); // number of seconds that elapsed
}
- singleAreaChartConfig.transition = {
- duration: 0
- };
-
- singleAreaChartConfig.area = {
- zerobased: false
- };
-
- singleAreaChartConfig.tooltip = {
- contents: function (d) {
- let d3f = ',';
- if (chart.type === 'rate')
- d3f = ',.2f';
- let zPre = function (i) {
- if (i < 10) {
- i = '0' + i;
- }
- return i;
- };
- let h = zPre(d[0].x.getHours());
- let m = zPre(d[0].x.getMinutes());
- let s = zPre(d[0].x.getSeconds());
- let table = '<table class=\'dispatch-c3-tooltip\'> <tr><th colspan=\'2\' class=\'text-center\'><strong>'+h+':'+m+':'+s+'</strong></th></tr> <tbody>';
- for (let i=0; i<d.length; i++) {
- let colorIndex = nameList.indexOf(d[i].id) % 10;
- let span = '<span class=\'chart-tip-legend\' style=\'background-color: '+self.colors[colorIndex]+';\'> </span>' + d[i].id;
- table += ('<tr><td>'+span+'<td>'+d3.format(d3f)(d[i].value)+'</td></tr>');
+ // don't push the last data point for a rate chart
+ if (this.chart.type !== 'rate' || i < data.length-1) {
+ dx.push(d[0]);
+ if (this.stacked) {
+ for (let nodeIndex=0; nodeIndex<nodeList.length; nodeIndex++) {
+ dlines[nodeIndex].push(accessor.call(this, d, d1, elapsed, nodeIndex));
}
- table += '</tbody></table>';
- return table;
+ } else {
+ dlines[0].push(accessor.call(this, d, d1, elapsed));
}
- };
-
- singleAreaChartConfig.title = {
- text: QDRService.utilities.humanify(this.chart.attr())
- };
-
- singleAreaChartConfig.data.colors = {};
- nameList.forEach( (function (r, i) {
- singleAreaChartConfig.data.colors[r] = this.colors[i % 10];
- }).bind(this));
-
- singleAreaChartConfig.data.color = (function (color, d) {
- let i = nameList.indexOf(d);
- return i >= 0 ? this.colors[i % 10] : color;
- }).bind(this);
-
- if (!chart.hideLegend) {
- singleAreaChartConfig.legend = {
- show: true,
- };
}
-
- if (this.stacked) {
- // create a stacked area chart
- singleAreaChartConfig.data.groups = [QDRService.management.topology.nodeNameList()];
- singleAreaChartConfig.data.order = function (t1, t2) { return t1.id < t2.id; };
- }
-
- this.singleAreaChart = c3.generate(singleAreaChartConfig);
- };
-
- // filter/modify the chart.data into data points for the svg
- /* the collected data looks like:
- [[date, val, [v1,v2,...]], [date, val, [v1,v2,...]],...]
- with date being the timestamp of the sample
- val being the total value
- and the [v1,v2,...] array being the component values for each router for stacked charts
-
- for stacked charts, the returned data looks like:
- [['x', date, date,...},
- ['R1', v1, v1,...},
- ['R2', v2, v2,...],
- ...]
-
- for non-stacked charts, the returned data looks like:
- ['x', date, date,...],
- ['R1', val, val,...]]
-
- for rate charts, all the values returned are the change per second between adjacent values
- */
- self.pfAreaChart.prototype.chartData = function() {
- let data = this.chart.data();
- let nodeList = QDRService.management.topology.nodeNameList();
-
- // oldest data point that should be visible
- let now = new Date();
- let visibleDate = new Date(now.getTime() - this.chart.visibleDuration * 60 * 1000);
-
- let accessorSingle = function (d, d1, elapsed) {
- return this.chart.type === 'rate' ? (d1[1] - d[1]) / elapsed : d[1];
- };
- let accessorStacked = function (d, d1, elapsed, i) {
- return this.chart.type === 'rate' ? (d1[2][i].val - d[2][i].val) / elapsed : d[2][i].val;
- };
- let accessor = this.stacked ? accessorStacked : accessorSingle;
-
- let dx = ['x'];
- let dlines = [];
- if (this.stacked) {
- // for stacked, there is a line per router
- nodeList.forEach( function (node) {
- dlines.push([node]);
- });
- } else {
- // for non-stacked, there is only one line
- dlines.push([this.aggregate ? 'Total' : this.chart.router()]);
+ }
+ }
+ let columns = [dx];
+ dlines.forEach( function (line) {
+ columns.push(line);
+ });
+ return columns;
+ }
+
+ // get the data for the chart and update it
+ tick () {
+ // can't draw charts that don't have data yet
+ if (!this.chart.data() || this.chart.data().length == 0 || !this.singleAreaChart) {
+ return;
+ }
+ let nodeName = this.QDRService.utilities.nameFromId(this.chart.base.request.nodeId);
+ this.colorMap[nodeName] = this.chart.areaColor;
+ this.singleAreaChart.data.colors(this.colorMap);
+
+ // update the chart title
+ // since there is no c3 api to get or set the chart title, we change the title directly using d3
+ let rate = '';
+ if (this.chart.type === 'rate')
+ rate = ' per second';
+ d3.select('#'+this.htmlId+' svg text.c3-title').text(this.QDRService.utilities.humanify(this.chart.attr()) + rate);
+
+ let d = this.chartData();
+ // load the new data
+ // using the c3.flow api causes the x-axis labels to jump around
+ this.singleAreaChart.load({
+ columns: d
+ });
+ }
+
+}
+
+// aggregate chart is based on pfAreaChart
+class AggChart extends AreaChart {
+ constructor(chart, chartId, defer, QDRService) {
+ // inherit pfChart's properties, but force a defer
+ super(chart, chartId, true, undefined, QDRService);
+ // the request is for aggregate data, but the chart is for the sum and not the detail
+ // Explanation: When the chart.request is aggregate, each data point is composed of 3 parts:
+ // 1. the datetime stamp
+ // 2. the sum of the value for all routers
+ // 3. an object with each router's name and value for this data point
+ // Normally, an aggregate chart shows lines for each of the routers and ignores the sum
+ // For this chart, we want to chart the sum (the 2nd value), so we set stacked to false
+ this.stacked = false;
+ // let chart legends and tooltips show 'Total' instead of a router name
+ this.aggregate = true;
+ if (!defer)
+ this.generate();
+ }
+}
+
+
+export class QDRChartService {
+ constructor(QDRService, $log) {
+ this.charts = [];
+ this.chartRequests = [];
+ this.QDRService = QDRService;
+ this.QDRLog = new QDRLogger($log, 'QDRChartService');
+ }
+
+ // Example service function
+ init () {
+ let self = this;
+ this.loadCharts();
+ this.QDRService.management.connection.addDisconnectAction(function() {
+ self.charts.forEach(function(chart) {
+ self.unRegisterChart(chart, true);
+ });
+ self.QDRService.management.connection.addConnectAction(self.init);
+ });
+ }
+ findChartRequest (nodeId, entity, aggregate) {
+ let ret = null;
+ this.chartRequests.some(function(request) {
+ if (request.equals(nodeId, entity, aggregate)) {
+ ret = request;
+ return true;
+ }
+ });
+ return ret;
+ }
+ findCharts (opts) { //name, attr, nodeId, entity, hdash) {
+ if (!opts.hdash)
+ opts.hdash = false; // rather than undefined
+ return this.charts.filter(function(chart) {
+ return (chart.name() == opts.name &&
+ chart.attr() == opts.attr &&
+ chart.nodeId() == opts.nodeId &&
+ chart.entity() == opts.entity &&
+ chart.hdash == opts.hdash);
+ });
+ }
+
+ delChartRequest (request) {
+ for (let i = 0; i < this.chartRequests.length; ++i) {
+ let r = this.chartRequests[i];
+ if (request.equals(r)) {
+ this.QDRLog.debug('removed request: ' + request.nodeId + ' ' + request.entity);
+ this.chartRequests.splice(i, 1);
+ this.stopCollecting(request);
+ return;
+ }
+ }
+ }
+
+ delChart (chart, skipSave) {
+ let foundBases = 0;
+ for (let i = 0; i < this.charts.length; ++i) {
+ let c = this.charts[i];
+ if (c.base === chart.base)
+ ++foundBases;
+ if (c.equals(chart)) {
+ this.charts.splice(i, 1);
+ if (chart.dashboard && !skipSave)
+ this.saveCharts();
+ }
+ }
+ if (foundBases == 1) {
+ let baseIndex = bases.indexOf(chart.base);
+ bases.splice(baseIndex, 1);
+ }
+ }
+
+ createChart (opts, request) {
+ return new Chart(opts, request, this.QDRService, this);
+ }
+ createChartRequest (opts) {
+ let request = new ChartRequest(opts); //nodeId, entity, name, attr, interval, aggregate);
+ request.creationTimestamp = opts.now;
+ this.chartRequests.push(request);
+ this.startCollecting(request);
+ this.sendChartRequest(request);
+ return request;
+ }
+ destroyChartRequest (request) {
+ this.stopCollecting(request);
+ this.delChartRequest(request);
+ }
+
+ registerChart (opts) { //nodeId, entity, name, attr, interval, instance, forceCreate, aggregate, hdash) {
+ let request = this.findChartRequest(opts.nodeId, opts.entity, opts.aggregate);
+ if (request) {
+ // add any new attr or name to the list
+ request.addAttrName(opts.name, opts.attr);
+ } else {
+ // the nodeId/entity did not already exist, so add a new request and chart
+ this.QDRLog.debug('added new request: ' + opts.nodeId + ' ' + opts.entity);
+ request = this.createChartRequest(opts);
+ }
+ let charts = this.findCharts(opts); //name, attr, nodeId, entity, hdash);
+ let chart;
+ if (charts.length == 0 || opts.forceCreate) {
+ if (!opts.use_instance && opts.instance)
+ delete opts.instance;
+ chart = new Chart(opts, request, this.QDRService, this); //opts.name, opts.attr, opts.instance, request);
+ this.charts.push(chart);
+ } else {
+ chart = charts[0];
+ }
+ return chart;
+ }
+
+ // remove the chart for name/attr
+ // if all attrs are gone for this request, remove the request
+ unRegisterChart (chart, skipSave) {
+ // remove the chart
+ for (let i = 0; i < this.charts.length; ++i) {
+ let c = this.charts[i];
+ if (chart.equals(c)) {
+ let request = chart.request();
+ this.delChart(chart, skipSave);
+ if (request) {
+ // see if any other charts use this attr
+ for (let j = 0; j < this.charts.length; ++j) {
+ let ch = this.charts[j];
+ if (ch.attr() == chart.attr() && ch.request().equals(chart.request()))
+ return;
+ }
+ // no other charts use this attr, so remove it
+ if (request.removeAttr(chart.name(), chart.attr()) == 0) {
+ this.destroyChartRequest(request);
+ }
}
- for (let i=0; i<data.length; i++) {
- let d = data[i], elapsed = 1, d1;
- if (d[0] >= visibleDate) {
- if (this.chart.type === 'rate' && i < data.length-1) {
- d1 = data[i+1];
- elapsed = Math.max((d1[0] - d[0]) / 1000, 0.001); // number of seconds that elapsed
- }
- // don't push the last data point for a rate chart
- if (this.chart.type !== 'rate' || i < data.length-1) {
- dx.push(d[0]);
- if (this.stacked) {
- for (let nodeIndex=0; nodeIndex<nodeList.length; nodeIndex++) {
- dlines[nodeIndex].push(accessor.call(this, d, d1, elapsed, nodeIndex));
+ }
+ }
+ if (!skipSave)
+ this.saveCharts();
+ }
+
+ stopCollecting (request) {
+ if (request.setTimeoutHandle) {
+ clearInterval(request.setTimeoutHandle);
+ request.setTimeoutHandle = null;
+ }
+ }
+
+ startCollecting (request) {
+ request.setTimeoutHandle = setInterval(this.sendChartRequest.bind(this), request.interval, request);
+ }
+ shouldRequest () {
+ // see if any of the charts associated with this request have either dialog, dashboard, or hreq
+ return this.charts.some(function(chart) {
+ return (chart.dashboard || chart.hreq) || (!chart.dashboard && !chart.hdash);
+ });
+ }
+ // send the request
+ sendChartRequest (request) {
+ if (request.busy)
+ return;
+ if (this.charts.length > 0 && !this.shouldRequest(request)) {
+ return;
+ }
+ // ensure the response has the name field so we can associate the response values with the correct chart
+ let attrs = request.attrs();
+ if (attrs.indexOf('name') == -1)
+ attrs.push('name');
+
+ // this is called when the response is received
+ var saveResponse = function(nodeId, entity, response) {
+ request.busy = false;
+ if (!response || !response.attributeNames)
+ return;
+ // records is an array that has data for all names
+ let records = response.results;
+ if (!records)
+ return;
+
+ let now = new Date();
+ let cutOff = new Date(now.getTime() - request.duration * 60 * 1000);
+ // index of the "name" attr in the response
+ let nameIndex = response.attributeNames.indexOf('name');
+ if (nameIndex < 0)
+ return;
+
+ let names = request.names();
+ // for each record returned, find the name/attr for this request and save the data with this timestamp
+ for (let i = 0; i < records.length; ++i) {
+ let name = records[i][nameIndex];
+ // if we want to store the values for some attrs for this name
+ if (names.indexOf(name) > -1) {
+ attrs.forEach(function(attr) {
+ let attrIndex = response.attributeNames.indexOf(attr);
+ if (records[i][attrIndex] !== undefined) {
+ let data = request.data(name, attr); // get a reference to the data array
+ if (data) {
+
+ if (request.aggregate) {
+ data.push([now, response.aggregates[i][attrIndex].sum, response.aggregates[i][attrIndex].detail]);
+ } else {
+ data.push([now, records[i][attrIndex]]);
+ }
+ // expire the old data
+ while (data[0][0] < cutOff) {
+ data.shift();
}
- } else {
- dlines[0].push(accessor.call(this, d, d1, elapsed));
}
}
- }
+ });
}
- let columns = [dx];
- dlines.forEach( function (line) {
- columns.push(line);
- });
- return columns;
- };
-
- // get the data for the chart and update it
- self.pfAreaChart.prototype.tick = function() {
- // can't draw charts that don't have data yet
- if (!this.chart.data() || this.chart.data().length == 0 || !this.singleAreaChart) {
- return;
+ }
+ };
+ request.busy = true;
+ // check for override of request
+ if (request.override) {
+ request.override(request, saveResponse);
+ } else {
+ // send the appropriate request
+ if (request.aggregate) {
+ let nodeList = this.QDRService.management.topology.nodeIdList();
+ this.QDRService.management.topology.getMultipleNodeInfo(nodeList, request.entity, attrs, saveResponse, request.nodeId);
+ } else {
+ this.QDRService.management.topology.fetchEntity(request.nodeId, request.entity, attrs, saveResponse);
+ }
+ }
+ }
+
+ numCharts () {
+ return this.charts.filter(function(chart) {
+ return chart.dashboard;
+ }).length;
+ //return this.charts.length;
+ }
+
+ isAttrCharted (nodeId, entity, name, attr, aggregate) {
+ let charts = this.findCharts({
+ name: name,
+ attr: attr,
+ nodeId: nodeId,
+ entity: entity
+ });
+ // if any of the matching charts are on the dashboard page, return true
+ return charts.some(function(chart) {
+ return (chart.dashboard && (aggregate ? chart.aggregate() : !chart.aggregate()));
+ });
+ }
+
+ addHDash (chart) {
+ chart.hdash = true;
+ this.saveCharts();
+ }
+ delHDash (chart) {
+ chart.hdash = false;
+ this.saveCharts();
+ }
+ addDashboard (chart) {
+ chart.dashboard = true;
+ this.saveCharts();
+ }
+ delDashboard (chart) {
+ chart.dashboard = false;
+ this.saveCharts();
+ }
+ // save the charts to local storage
+ saveCharts () {
+ let minCharts = [];
+
+ this.charts.forEach(function(chart) {
+ let minChart = {};
+ // don't save chart unless it is on the dashboard
+ if (chart.dashboard || chart.hdash) {
+ chart.copyProps(minChart);
+ minCharts.push(minChart);
+ }
+ });
+ localStorage['QDRCharts'] = angular.toJson(minCharts);
+ }
+ loadCharts () {
+ let charts = angular.fromJson(localStorage['QDRCharts']);
+ if (charts) {
+ let self = this;
+ // get array of known ids
+ let nodeList = this.QDRService.management.topology.nodeIdList();
+ charts.forEach(function(chart) {
+ // if this chart is not in the current list of nodes, skip
+ if (nodeList.indexOf(chart.nodeId) >= 0) {
+ if (!angular.isDefined(chart.instance)) {
+ chart.instance = ++instance;
+ }
+ if (chart.instance >= instance)
+ instance = chart.instance + 1;
+ if (!chart.duration)
+ chart.duration = 1;
+ if (chart.nodeList)
+ chart.aggregate = true;
+ if (!chart.hdash)
+ chart.hdash = false;
+ if (!chart.dashboard)
+ chart.dashboard = false;
+ if (!chart.hdash && !chart.dashboard)
+ chart.dashboard = true;
+ if (chart.hdash && chart.dashboard)
+ chart.dashboard = false;
+ chart.forceCreate = true;
+ chart.use_instance = true;
+ let newChart = self.registerChart(chart); //chart.nodeId, chart.entity, chart.name, chart.attr, chart.interval, true, chart.aggregate);
+ newChart.dashboard = chart.dashboard;
+ newChart.hdash = chart.hdash;
+ newChart.hreq = false;
+ newChart.type = chart.type;
+ newChart.rateWindow = chart.rateWindow;
+ newChart.areaColor = chart.areaColor ? chart.areaColor : '#32b9f3';
+ newChart.lineColor = chart.lineColor ? chart.lineColor : '#058dc7';
+ newChart.duration(chart.duration);
+ newChart.visibleDuration = chart.visibleDuration ? chart.visibleDuration : newChart.type === 'rate' ? 0.25 : 1;
+ if (chart.userTitle)
+ newChart.title(chart.userTitle);
}
-
- // update the chart title
- // since there is no c3 api to get or set the chart title, we change the title directly using d3
- let rate = '';
- if (this.chart.type === 'rate')
- rate = ' per second';
- d3.select('#'+this.htmlId+' svg text.c3-title').text(QDRService.utilities.humanify(this.chart.attr()) + rate);
-
- let d = this.chartData();
- // load the new data
- // using the c3.flow api causes the x-axis labels to jump around
- this.singleAreaChart.load({
- columns: d
- });
- };
-
- return self;
+ });
}
- ]);
-
- return QDR;
-}(QDR || {}));
+ }
+ // constructor for a c3 area chart
+ pfAreaChart (chart, chartId, defer, width) {
+ return new AreaChart(chart, chartId, defer, width, this.QDRService);
+ }
+
+ // aggregate chart is based on pfAreaChart
+ pfAggChart (chart, chartId, defer) {
+ return new AggChart(chart, chartId, defer, this.QDRService);
+ }
+}
+
+QDRChartService.$inject = ['QDRService', '$log'];
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/qdrCharts.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrCharts.js b/console/stand-alone/plugin/js/qdrCharts.js
index b296de7..0cffe4e 100644
--- a/console/stand-alone/plugin/js/qdrCharts.js
+++ b/console/stand-alone/plugin/js/qdrCharts.js
@@ -16,34 +16,27 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
-'use strict';
/* global angular */
-/**
- * @module QDR
- */
-var QDR = (function (QDR) {
+import { QDRTemplatePath, QDRRedirectWhenConnected } from './qdrGlobals.js';
- /**
- * @method ChartsController
- *
- * Controller that handles the QDR charts page
- */
- QDR.module.controller('QDR.ChartsController', function($scope, QDRService, QDRChartService, $uibModal, $location, $routeParams, $timeout) {
+export class ChartsController {
+ constructor(QDRService, QDRChartService, $scope, $location, $timeout, $routeParams, $uibModal) {
+ this.controllerName = 'QDR.ChartsController';
let updateTimer = null;
if (!QDRService.management.connection.is_connected()) {
// we are not connected. we probably got here from a bookmark or manual page reload
- QDR.redirectWhenConnected($location, 'charts');
+ QDRRedirectWhenConnected($location, 'charts');
return;
}
$scope.svgCharts = [];
// create an svg object for each chart
QDRChartService.charts.filter(function (chart) {return chart.dashboard;}).forEach(function (chart) {
- let svgChart = new QDRChartService.pfAreaChart(chart, chart.id(), true);
+ let svgChart = QDRChartService.pfAreaChart(chart, chart.id(), true);
svgChart.zoomed = false;
$scope.svgCharts.push(svgChart);
});
@@ -108,7 +101,7 @@ var QDR = (function (QDR) {
// called from dialog when we want to clone the dialog chart
// the chart argument here is a QDRChartService chart
$scope.addChart = function (chart) {
- let nchart = new QDRChartService.pfAreaChart(chart, chart.id(), true);
+ let nchart = QDRChartService.pfAreaChart(chart, chart.id(), true);
$scope.svgCharts.push(nchart);
$timeout( function () {
nchart.generate();
@@ -132,7 +125,7 @@ var QDR = (function (QDR) {
backdrop: true,
keyboard: true,
backdropClick: true,
- templateUrl: QDR.templatePath + template,
+ templateUrl: QDRTemplatePath + template,
controller: 'QDR.ChartDialogController',
resolve: {
chart: function() {
@@ -150,11 +143,7 @@ var QDR = (function (QDR) {
}
});
}
-
- });
-
-
- return QDR;
-
-}(QDR || {}));
+ }
+}
+ChartsController.$inject = ['QDRService', 'QDRChartService', '$scope', '$location', '$timeout', '$routeParams', '$uibModal'];
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/qdrGlobals.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrGlobals.js b/console/stand-alone/plugin/js/qdrGlobals.js
index af06c55..6fadf14 100644
--- a/console/stand-alone/plugin/js/qdrGlobals.js
+++ b/console/stand-alone/plugin/js/qdrGlobals.js
@@ -16,31 +16,43 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
-'use strict';
-var QDR = (function(QDR) {
+export var QDRFolder = (function () {
+ function Folder(title) {
+ this.title = title;
+ this.children = [];
+ this.folder = true;
+ }
+ return Folder;
+})();
+export var QDRLeaf = (function () {
+ function Leaf(title) {
+ this.title = title;
+ }
+ return Leaf;
+})();
- QDR.Folder = (function () {
- function Folder(title) {
- this.title = title;
- this.children = [];
- this.folder = true;
- }
- return Folder;
- })();
- QDR.Leaf = (function () {
- function Leaf(title) {
- this.title = title;
- }
- return Leaf;
- })();
+export var QDRCore = {
+ notification: function (severity, msg) {
+ $.notify(msg, severity);
+ }
+};
- QDR.Core = {
- notification: function (severity, msg) {
- $.notify(msg, severity);
- }
- };
+export class QDRLogger {
+ constructor($log, source) {
+ this.log = function (msg) { $log.log(`QDR-${source}: ${msg}`); };
+ this.debug = function (msg) { $log.debug(`QDR-${source}: ${msg}`); };
+ this.error = function (msg) { $log.error(`QDR-${source}: ${msg}`); };
+ this.info = function (msg) { $log.info(`QDR-${source}: ${msg}`); };
+ this.warn = function (msg) { $log.warn(`QDR-${source}: ${msg}`); };
+ }
+}
- return QDR;
+export const QDRTemplatePath = 'html/';
+export const QDR_SETTINGS_KEY = 'QDRSettings';
+export const QDR_LAST_LOCATION = 'QDRLastLocation';
-} (QDR || {}));
\ No newline at end of file
+export var QDRRedirectWhenConnected = function ($location, org) {
+ $location.path('/connect');
+ $location.search('org', org);
+};
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[3/8] qpid-dispatch git commit: DISPATCH-1049 Add console tests
Posted by ea...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/qdrOverview.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrOverview.js b/console/stand-alone/plugin/js/qdrOverview.js
index 2f784e7..536d93a 100644
--- a/console/stand-alone/plugin/js/qdrOverview.js
+++ b/console/stand-alone/plugin/js/qdrOverview.js
@@ -16,21 +16,15 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
-'use strict';
/* global angular d3 */
+import { QDRFolder, QDRLeaf, QDRCore, QDRLogger, QDRTemplatePath, QDRRedirectWhenConnected } from './qdrGlobals.js';
-/**
- * @module QDR
- */
-var QDR = (function (QDR) {
+export class OverviewController {
+ constructor(QDRService, $scope, $log, $location, $timeout, $uibModal) {
+ this.controllerName = 'QDR.OverviewController';
- /**
- *
- * Controller that handles the QDR overview page
- */
- QDR.module.controller('QDR.OverviewController', ['$scope', 'QDRService', '$location', '$timeout', '$uibModal', 'uiGridConstants', function($scope, QDRService, $location, $timeout, $uibModal) {
-
- QDR.log.debug('QDR.OverviewControll started with location of ' + $location.path() + ' and connection of ' + QDRService.management.connection.is_connected());
+ let QDRLog = new QDRLogger($log, 'OverviewController');
+ QDRLog.debug('QDR.OverviewControll started with location of ' + $location.path() + ' and connection of ' + QDRService.management.connection.is_connected());
let updateIntervalHandle = undefined;
const updateInterval = 5000;
@@ -46,14 +40,14 @@ var QDR = (function (QDR) {
content: '<i class="icon-list"></i> Attributes',
title: 'View the attribute values on your selection',
isValid: function () { return true; },
- href: function () { return '#/' + QDR.pluginName + '/attributes'; },
+ href: function () { return '#/attributes'; },
index: 0
},
{
content: '<i class="icon-leaf"></i> Operations',
title: 'Execute operations on your selection',
isValid: function () { return true; },
- href: function () { return '#/' + QDR.pluginName + '/operations'; },
+ href: function () { return '#/operations'; },
index: 1
}];
$scope.activeTab = $scope.subLevelTabs[0];
@@ -72,7 +66,7 @@ var QDR = (function (QDR) {
{title: 'Overview', name: 'Overview', right: false}
];
- $scope.tmplOverviewTree = QDR.templatePath + 'tmplOverviewTree.html';
+ $scope.tmplOverviewTree = QDRTemplatePath + 'tmplOverviewTree.html';
$scope.templates = [
{ name: 'Charts', url: 'overviewCharts.html'},
{ name: 'Routers', url: 'routers.html'},
@@ -188,7 +182,7 @@ var QDR = (function (QDR) {
if (nodes[node]['connection'].results[i][0] === 'inter-router')
++connections;
}
- let routerRow = {connections: connections, nodeId: node, id: QDRService.management.topology.nameFromId(node)};
+ let routerRow = {connections: connections, nodeId: node, id: QDRService.utilities.nameFromId(node)};
nodes[node]['router'].attributeNames.forEach( function (routerAttr, i) {
if (routerAttr !== 'routerId' && routerAttr !== 'id')
routerRow[routerAttr] = nodes[node]['router'].results[0][i];
@@ -441,7 +435,7 @@ var QDR = (function (QDR) {
}
return include;
});
- QDR.log.info('setting linkFields in updateLinkGrid');
+ QDRLog.info('setting linkFields in updateLinkGrid');
$scope.linkFields = filteredLinks;
expandGridToContent('Links', $scope.linkFields.length);
getLinkPagedData($scope.linkPagingOptions.pageSize, $scope.linkPagingOptions.currentPage);
@@ -594,7 +588,7 @@ var QDR = (function (QDR) {
var getAllLinkFields = function (completionCallbacks, selectionCallback) {
if (!$scope.linkFields) {
- QDR.log.debug('$scope.linkFields was not defined!');
+ QDRLog.debug('$scope.linkFields was not defined!');
return;
}
let nodeIds = QDRService.management.topology.nodeIdList();
@@ -611,10 +605,10 @@ var QDR = (function (QDR) {
if (elapsed < 0)
return 0;
let delivered = QDRService.utilities.valFor(response.attributeNames, result, 'deliveryCount') - oldname[0].rawDeliveryCount;
- //QDR.log.debug("elapsed " + elapsed + " delivered " + delivered)
+ //QDRLog.debug("elapsed " + elapsed + " delivered " + delivered)
return elapsed > 0 ? parseFloat(Math.round((delivered/elapsed) * 100) / 100).toFixed(2) : 0;
} else {
- //QDR.log.debug("unable to find old linkName")
+ //QDRLog.debug("unable to find old linkName")
return 0;
}
};
@@ -637,7 +631,7 @@ var QDR = (function (QDR) {
return QDRService.utilities.pretty(und + uns);
};
var getLinkName = function () {
- let namestr = QDRService.management.topology.nameFromId(nodeName);
+ let namestr = QDRService.utilities.nameFromId(nodeName);
return namestr + ':' + QDRService.utilities.valFor(response.attributeNames, result, 'identity');
};
var fixAddress = function () {
@@ -1050,8 +1044,8 @@ var QDR = (function (QDR) {
.then( function (results, context) {
let statusCode = context.message.application_properties.statusCode;
if (statusCode < 200 || statusCode >= 300) {
- QDR.Core.notification('error', context.message.statusDescription);
- QDR.log.error('Error ' + context.message.statusDescription);
+ QDRCore.notification('error', context.message.statusDescription);
+ QDRLog.error('Error ' + context.message.statusDescription);
}
});
};
@@ -1220,9 +1214,9 @@ var QDR = (function (QDR) {
}
});
d.result.then(function () {
- QDR.log.debug('d.open().then');
+ QDRLog.debug('d.open().then');
}, function () {
- QDR.log.debug('Modal dismissed at: ' + new Date());
+ QDRLog.debug('Modal dismissed at: ' + new Date());
});
}
@@ -1365,7 +1359,7 @@ var QDR = (function (QDR) {
let entry = allLogEntries[n];
entry.forEach( function (module) {
if (module.name === node.key) {
- module.nodeName = QDRService.management.topology.nameFromId(n);
+ module.nodeName = QDRService.utilities.nameFromId(n);
module.nodeId = n;
module.enable = logInfo.enable;
$scope.logModuleData.push(module);
@@ -1441,7 +1435,7 @@ var QDR = (function (QDR) {
};
if (!QDRService.management.connection.is_connected()) {
- QDR.redirectWhenConnected($location, 'overview');
+ QDRRedirectWhenConnected($location, 'overview');
return;
}
$scope.template = $scope.templates[0];
@@ -1530,7 +1524,7 @@ var QDR = (function (QDR) {
var showCharts = function () {
};
- let charts = new QDR.Folder('Charts');
+ let charts = new QDRFolder('Charts');
charts.info = {fn: showCharts};
charts.type = 'Charts'; // for the charts template
charts.key = 'Charts';
@@ -1538,7 +1532,7 @@ var QDR = (function (QDR) {
topLevelChildren.push(charts);
// create a routers tree branch
- let routers = new QDR.Folder('Routers');
+ let routers = new QDRFolder('Routers');
routers.type = 'Routers';
routers.info = {fn: allRouterInfo};
routers.expanded = (expandedNodeList.indexOf('Routers') > -1);
@@ -1549,8 +1543,8 @@ var QDR = (function (QDR) {
// called when the list of routers changes
var updateRouterTree = function (nodes) {
var worker = function (node) {
- let name = QDRService.management.topology.nameFromId(node);
- let router = new QDR.Leaf(name);
+ let name = QDRService.utilities.nameFromId(node);
+ let router = new QDRLeaf(name);
router.type = 'Router';
router.info = {fn: routerInfo};
router.nodeId = node;
@@ -1563,7 +1557,7 @@ var QDR = (function (QDR) {
};
// create an addresses tree branch
- let addresses = new QDR.Folder('Addresses');
+ let addresses = new QDRFolder('Addresses');
addresses.type = 'Addresses';
addresses.info = {fn: allAddressInfo};
addresses.expanded = (expandedNodeList.indexOf('Addresses') > -1);
@@ -1573,7 +1567,7 @@ var QDR = (function (QDR) {
topLevelChildren.push(addresses);
var updateAddressTree = function (addressFields) {
var worker = function (address) {
- let a = new QDR.Leaf(address.title);
+ let a = new QDRLeaf(address.title);
a.info = {fn: addressInfo};
a.key = address.uid;
a.fields = address;
@@ -1601,7 +1595,7 @@ var QDR = (function (QDR) {
};
$scope.filter = angular.fromJson(localStorage[FILTERKEY]) || {endpointsOnly: 'true', hideConsoles: true};
- let links = new QDR.Folder('Links');
+ let links = new QDRFolder('Links');
links.type = 'Links';
links.info = {fn: allLinkInfo};
links.expanded = (expandedNodeList.indexOf('Links') > -1);
@@ -1613,7 +1607,7 @@ var QDR = (function (QDR) {
// called both before the tree is created and whenever a background update is done
var updateLinkTree = function (linkFields) {
var worker = function (link) {
- let l = new QDR.Leaf(link.title);
+ let l = new QDRLeaf(link.title);
let isConsole = QDRService.utilities.isConsole(QDRService.management.topology.getConnForLink(link));
l.info = {fn: linkInfo};
l.key = link.uid;
@@ -1630,7 +1624,7 @@ var QDR = (function (QDR) {
updateLeaves(linkFields, 'Links', worker);
};
- let connections = new QDR.Folder('Connections');
+ let connections = new QDRFolder('Connections');
connections.type = 'Connections';
connections.info = {fn: allConnectionInfo};
connections.expanded = (expandedNodeList.indexOf('Connections') > -1);
@@ -1645,7 +1639,7 @@ var QDR = (function (QDR) {
if (connection.name === 'connection/' && connection.role === 'inter-router' && connection.host === '')
host = connection.container + ':' + connection.identity;
- let c = new QDR.Leaf(host);
+ let c = new QDRLeaf(host);
let isConsole = QDRService.utilities.isAConsole (connection.properties, connection.identity, connection.role, connection.routerId);
c.type = 'Connection';
c.info = {fn: connectionInfo};
@@ -1664,7 +1658,7 @@ var QDR = (function (QDR) {
var updateLogTree = function (logFields) {
var worker = function (log) {
- let l = new QDR.Leaf(log.name);
+ let l = new QDRLeaf(log.name);
l.type = 'Log';
l.info = {fn: logInfo};
l.key = log.name;
@@ -1678,7 +1672,7 @@ var QDR = (function (QDR) {
let htmlReady = false;
let dataReady = false;
$scope.largeNetwork = QDRService.management.topology.isLargeNetwork();
- let logs = new QDR.Folder('Logs');
+ let logs = new QDRFolder('Logs');
logs.type = 'Logs';
logs.info = {fn: allLogInfo};
logs.expanded = (expandedNodeList.indexOf('Logs') > -1);
@@ -1723,13 +1717,13 @@ var QDR = (function (QDR) {
// add placeholders for the top level tree nodes
let topLevelTreeNodes = [routers, addresses, links, connections, logs];
topLevelTreeNodes.forEach( function (parent) {
- let placeHolder = new QDR.Leaf('loading...');
+ let placeHolder = new QDRLeaf('loading...');
placeHolder.extraClasses = 'loading';
parent.children = [placeHolder];
});
var updateExpanded = function () {
- QDR.log.debug('updateExpandedEntities');
+ QDRLog.debug('updateExpandedEntities');
clearTimeout(updateIntervalHandle);
let tree = $('#overtree').fancytree('getTree');
@@ -1742,7 +1736,7 @@ var QDR = (function (QDR) {
});
q.await( function (error) {
if (error)
- QDR.log.error(error.message);
+ QDRLog.error(error.message);
// if there are no active nodes, activate the saved one
let tree = $('#overtree').fancytree('getTree');
@@ -1771,8 +1765,7 @@ var QDR = (function (QDR) {
clearTimeout(updateIntervalHandle);
$(window).off('resize', resizer);
});
- }]);
-
- return QDR;
-}(QDR || {}));
+ }
+}
+OverviewController.$inject = ['QDRService', '$scope', '$log', '$location', '$timeout', '$uibModal'];
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/qdrOverviewChartsController.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrOverviewChartsController.js b/console/stand-alone/plugin/js/qdrOverviewChartsController.js
index e47e597..a65d74c 100644
--- a/console/stand-alone/plugin/js/qdrOverviewChartsController.js
+++ b/console/stand-alone/plugin/js/qdrOverviewChartsController.js
@@ -16,14 +16,11 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
-'use strict';
/* global angular */
-/**
- * @module QDR
- */
-var QDR = (function(QDR) {
- QDR.module.controller('QDR.OverviewChartsController', function ($scope, QDRService, QDRChartService, $timeout) {
+export class OverviewChartsController {
+ constructor(QDRService, QDRChartService, $scope, $timeout) {
+ this.controllerName = 'QDR.OverviewChartsController';
$scope.overviewCharts = [];
let updateTimer;
@@ -90,7 +87,7 @@ var QDR = (function(QDR) {
];
$scope.overviewCharts = charts.map( function (chart) {
let c = QDRChartService.registerChart(chart);
- return new QDRChartService.pfAreaChart(c, c.id(), true);
+ return QDRChartService.pfAreaChart(c, c.id(), true);
});
@@ -128,7 +125,6 @@ var QDR = (function(QDR) {
QDRChartService.unRegisterChart(svg.chart);
});
});
- });
- return QDR;
-
-} (QDR || {}));
+ }
+}
+OverviewChartsController.$inject = ['QDRService', 'QDRChartService', '$scope', '$timeout'];
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/qdrOverviewLogsController.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrOverviewLogsController.js b/console/stand-alone/plugin/js/qdrOverviewLogsController.js
index 1e541a5..316605e 100644
--- a/console/stand-alone/plugin/js/qdrOverviewLogsController.js
+++ b/console/stand-alone/plugin/js/qdrOverviewLogsController.js
@@ -16,19 +16,19 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
-'use strict';
-/**
- * @module QDR
- */
-var QDR = (function(QDR) {
- QDR.module.controller('QDR.OverviewLogsController', function ($scope, $uibModalInstance, QDRService, $timeout, nodeName, nodeId, module, level) {
+import { QDRCore, QDRLogger } from './qdrGlobals.js';
+export class OverviewLogsController {
+ constructor(QDRService, $scope, $log, $uibModalInstance, $timeout, nodeName, nodeId, module, level) {
+ this.controllerName = 'QDR.OverviewLogsController';
+
+ let QDRLog = new QDRLogger($log, 'OverviewLogsController');
var gotLogInfo = function (nodeId, response, context) {
let statusCode = context.message.application_properties.statusCode;
if (statusCode < 200 || statusCode >= 300) {
- QDR.Core.notification('error', context.message.statusDescription);
- QDR.log.info('Error ' + context.message.statusDescription);
+ QDRCore.notification('error', context.message.statusDescription);
+ QDRLog.info('Error ' + context.message.statusDescription);
} else {
let levelLogs = response.filter( function (result) {
if (result[1] == null)
@@ -37,7 +37,7 @@ var QDR = (function(QDR) {
});
let logFields = levelLogs.map( function (result) {
return {
- nodeId: QDRService.management.topology.nameFromId(nodeId),
+ nodeId: QDRService.utilities.nameFromId(nodeId),
name: result[0],
type: result[1],
message: result[2],
@@ -64,7 +64,6 @@ var QDR = (function(QDR) {
$uibModalInstance.close(true);
};
- });
- return QDR;
-
-} (QDR || {}));
+ }
+}
+OverviewLogsController.$inject = ['QDRService', '$scope', '$log', '$uibModalInstance', '$timeout', 'nodeName', 'nodeId', 'module', 'level'];
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/qdrSchema.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrSchema.js b/console/stand-alone/plugin/js/qdrSchema.js
index 69569ea..4421636 100644
--- a/console/stand-alone/plugin/js/qdrSchema.js
+++ b/console/stand-alone/plugin/js/qdrSchema.js
@@ -16,19 +16,18 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
-'use strict';
-/**
- * @module QDR
- */
-var QDR = (function (QDR) {
+import { QDRRedirectWhenConnected} from './qdrGlobals.js';
+
+export class SchemaController {
+ constructor(QDRService, $scope, $location, $timeout) {
+ this.controllerName = 'QDR.SchemaController';
- QDR.module.controller('QDR.SchemaController', ['$scope', '$location', '$timeout', 'QDRService', function($scope, $location, $timeout, QDRService) {
if (!QDRService.management.connection.is_connected()) {
- QDR.redirectWhenConnected($location, 'schema');
+ QDRRedirectWhenConnected($location, 'schema');
return;
}
var onDisconnect = function () {
- $timeout( function () {QDR.redirectWhenConnected('schema');});
+ $timeout( function () {QDRRedirectWhenConnected('schema');});
};
// we are currently connected. setup a handler to get notified if we are ever disconnected
QDRService.management.connection.addDisconnectAction( onDisconnect );
@@ -77,7 +76,6 @@ var QDR = (function (QDR) {
QDRService.management.connection.delDisconnectAction( onDisconnect );
});
- }]);
-
- return QDR;
-}(QDR || {}));
+ }
+}
+SchemaController.$inject = ['QDRService', '$scope', '$location', '$timeout'];
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/qdrService.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrService.js b/console/stand-alone/plugin/js/qdrService.js
index f4effdd..8f13192 100644
--- a/console/stand-alone/plugin/js/qdrService.js
+++ b/console/stand-alone/plugin/js/qdrService.js
@@ -16,254 +16,95 @@ Licensed to the Apache Software Foundation (ASF) under one
specific language governing permissions and limitations
under the License.
*/
-'use strict';
/* global Promise */
-/**
- * @module QDR
- */
-var QDR = (function(QDR) {
-
- // The QDR service handles the connection to the router
- QDR.module.factory('QDRService', ['$timeout', '$location', function($timeout, $location) {
- let dm = require('dispatch-management');
- let self = {
- management: new dm.Management($location.protocol()),
- utilities: dm.Utilities,
-
- onReconnect: function () {
- self.management.connection.on('disconnected', self.onDisconnect);
- let org = localStorage[QDR.LAST_LOCATION] || '/overview';
- $timeout ( function () {
- $location.path(org);
- $location.search('org', null);
- $location.replace();
- });
- },
- onDisconnect: function () {
- self.management.connection.on('connected', self.onReconnect);
- $timeout( function () {
- $location.path('/connect');
- let curPath = $location.path();
- let parts = curPath.split('/');
- let org = parts[parts.length-1];
- if (org && org.length > 0 && org !== 'connect') {
- $location.search('org', org);
- } else {
- $location.search('org', null);
- }
- $location.replace();
- });
- },
-
- connect: function (connectOptions) {
- return new Promise ( function (resolve, reject) {
- self.management.connection.connect(connectOptions)
- .then( function (r) {
- // if we are ever disconnected, show the connect page and wait for a reconnect
- self.management.connection.on('disconnected', self.onDisconnect);
-
- self.management.getSchema()
+import { Management as dm } from '../../modules/management.js';
+import { utils } from '../../modules/utilities.js';
+
+import { QDR_LAST_LOCATION, QDRLogger} from './qdrGlobals.js';
+
+export class QDRService {
+ constructor($log, $timeout, $location) {
+ this.$timeout = $timeout;
+ this.$location = $location;
+ this.management = new dm($location.protocol());
+ this.utilities = utils;
+ this.QDRLog = new QDRLogger($log, 'QDRService');
+ }
+
+ // Example service function
+ onReconnect () {
+ this.management.connection.on('disconnected', this.onDisconnect);
+ let org = localStorage[QDR_LAST_LOCATION] || '/overview';
+ this.$timeout ( function () {
+ this.$location.path(org);
+ this.$location.search('org', null);
+ this.$location.replace();
+ });
+ }
+ onDisconnect () {
+ this.management.connection.on('connected', this.onReconnect);
+ this.$timeout( function () {
+ this.$location.path('/connect');
+ let curPath = this.$location.path();
+ let parts = curPath.split('/');
+ let org = parts[parts.length-1];
+ if (org && org.length > 0 && org !== 'connect') {
+ this.$location.search('org', org);
+ } else {
+ this.$location.search('org', null);
+ }
+ this.$location.replace();
+ });
+ }
+ connect (connectOptions) {
+ let self = this;
+ return new Promise ( function (resolve, reject) {
+ self.management.connection.connect(connectOptions)
+ .then( function (r) {
+ // if we are ever disconnected, show the connect page and wait for a reconnect
+ self.management.connection.on('disconnected', self.onDisconnect);
+
+ self.management.getSchema()
+ .then( function () {
+ self.QDRLog.info('got schema after connection');
+ self.management.topology.setUpdateEntities([]);
+ self.QDRLog.info('requesting a topology');
+ self.management.topology.get() // gets the list of routers
.then( function () {
- QDR.log.info('got schema after connection');
- self.management.topology.setUpdateEntities([]);
- QDR.log.info('requesting a topology');
- self.management.topology.get() // gets the list of routers
- .then( function () {
- QDR.log.info('got initial topology');
- let curPath = $location.path();
- let parts = curPath.split('/');
- let org = parts[parts.length-1];
- if (org === '' || org === 'connect') {
- org = localStorage[QDR.LAST_LOCATION] || QDR.pluginRoot + '/overview';
- }
- $timeout ( function () {
- $location.path(org);
- $location.search('org', null);
- $location.replace();
- });
- });
+ self.QDRLog.info('got initial topology');
+ let curPath = self.$location.path();
+ let parts = curPath.split('/');
+ let org = parts[parts.length-1];
+ if (org === '' || org === 'connect') {
+ org = localStorage[QDR_LAST_LOCATION] || '/overview';
+ }
+ self.$timeout ( function () {
+ self.$location.path(org);
+ self.$location.search('org', null);
+ self.$location.replace();
+ });
});
- resolve(r);
- }, function (e) {
- reject(e);
});
+ resolve(r);
+ }, function (e) {
+ reject(e);
});
- },
- disconnect: function () {
- self.management.connection.disconnect();
- delete self.management;
- self.management = new dm.Management($location.protocol());
- }
-
-
- };
-
- return self;
- }]);
-
- return QDR;
+ });
+ }
+ disconnect () {
+ this.management.connection.disconnect();
+ delete this.management;
+ this.management = new dm(this.$location.protocol());
+ }
+}
-}(QDR || {}));
+QDRService.$inject = ['$log', '$timeout', '$location'];
(function() {
console.dump = function(o) {
if (window.JSON && window.JSON.stringify)
- QDR.log.info(JSON.stringify(o, undefined, 2));
+ console.log(JSON.stringify(o, undefined, 2));
else
console.log(o);
};
-})();
-
-if (!String.prototype.startsWith) {
- String.prototype.startsWith = function (searchString, position) {
- return this.substr(position || 0, searchString.length) === searchString;
- };
-}
-
-if (!String.prototype.endsWith) {
- String.prototype.endsWith = function(searchString, position) {
- let subjectString = this.toString();
- if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
- position = subjectString.length;
- }
- position -= searchString.length;
- let lastIndex = subjectString.lastIndexOf(searchString, position);
- return lastIndex !== -1 && lastIndex === position;
- };
-}
-
-// https://tc39.github.io/ecma262/#sec-array.prototype.findIndex
-if (!Array.prototype.findIndex) {
- Object.defineProperty(Array.prototype, 'findIndex', {
- value: function(predicate) {
- // 1. Let O be ? ToObject(this value).
- if (this == null) {
- throw new TypeError('"this" is null or not defined');
- }
-
- let o = Object(this);
-
- // 2. Let len be ? ToLength(? Get(O, "length")).
- let len = o.length >>> 0;
-
- // 3. If IsCallable(predicate) is false, throw a TypeError exception.
- if (typeof predicate !== 'function') {
- throw new TypeError('predicate must be a function');
- }
-
- // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
- let thisArg = arguments[1];
-
- // 5. Let k be 0.
- let k = 0;
-
- // 6. Repeat, while k < len
- while (k < len) {
- // a. Let Pk be ! ToString(k).
- // b. Let kValue be ? Get(O, Pk).
- // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
- // d. If testResult is true, return k.
- let kValue = o[k];
- if (predicate.call(thisArg, kValue, k, o)) {
- return k;
- }
- // e. Increase k by 1.
- k++;
- }
-
- // 7. Return -1.
- return -1;
- }
- });
-}
-
-// https://tc39.github.io/ecma262/#sec-array.prototype.find
-if (!Array.prototype.find) {
- Object.defineProperty(Array.prototype, 'find', {
- value: function(predicate) {
- // 1. Let O be ? ToObject(this value).
- if (this == null) {
- throw new TypeError('"this" is null or not defined');
- }
-
- let o = Object(this);
-
- // 2. Let len be ? ToLength(? Get(O, "length")).
- let len = o.length >>> 0;
-
- // 3. If IsCallable(predicate) is false, throw a TypeError exception.
- if (typeof predicate !== 'function') {
- throw new TypeError('predicate must be a function');
- }
-
- // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
- let thisArg = arguments[1];
-
- // 5. Let k be 0.
- let k = 0;
-
- // 6. Repeat, while k < len
- while (k < len) {
- // a. Let Pk be ! ToString(k).
- // b. Let kValue be ? Get(O, Pk).
- // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
- // d. If testResult is true, return kValue.
- let kValue = o[k];
- if (predicate.call(thisArg, kValue, k, o)) {
- return kValue;
- }
- // e. Increase k by 1.
- k++;
- }
-
- // 7. Return undefined.
- return undefined;
- }
- });
-}
-
-// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
-if (!Array.prototype.fill) {
- Object.defineProperty(Array.prototype, 'fill', {
- value: function(value) {
-
- // Steps 1-2.
- if (this == null) {
- throw new TypeError('this is null or not defined');
- }
-
- var O = Object(this);
-
- // Steps 3-5.
- var len = O.length >>> 0;
-
- // Steps 6-7.
- var start = arguments[1];
- var relativeStart = start >> 0;
-
- // Step 8.
- var k = relativeStart < 0 ?
- Math.max(len + relativeStart, 0) :
- Math.min(relativeStart, len);
-
- // Steps 9-10.
- var end = arguments[2];
- var relativeEnd = end === undefined ?
- len : end >> 0;
-
- // Step 11.
- var final = relativeEnd < 0 ?
- Math.max(len + relativeEnd, 0) :
- Math.min(relativeEnd, len);
-
- // Step 12.
- while (k < final) {
- O[k] = value;
- k++;
- }
-
- // Step 13.
- return O;
- }
- });
-}
\ No newline at end of file
+})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/qdrSettings.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrSettings.js b/console/stand-alone/plugin/js/qdrSettings.js
index e73d13b..2d0e01b 100644
--- a/console/stand-alone/plugin/js/qdrSettings.js
+++ b/console/stand-alone/plugin/js/qdrSettings.js
@@ -16,29 +16,20 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
-'use strict';
/* global angular */
-/**
- * @module QDR
- */
-var QDR = (function(QDR) {
+import { QDR_SETTINGS_KEY, QDRLogger} from './qdrGlobals.js';
- /**
- * @method SettingsController
- * @param $scope
- * @param QDRServer
- *
- * Controller that handles the QDR settings page
- */
-
- QDR.module.controller('QDR.SettingsController', ['$scope', 'QDRService', 'QDRChartService', '$timeout', function($scope, QDRService, QDRChartService, $timeout) {
+export class SettingsController {
+ constructor(QDRService, QDRChartService, $scope, $log, $timeout) {
+ this.controllerName = 'QDR.SettingsController';
+ let QDRLog = new QDRLogger($log, 'SettingsController');
$scope.connecting = false;
$scope.connectionError = false;
$scope.connectionErrorText = undefined;
$scope.forms = {};
- $scope.formEntity = angular.fromJson(localStorage[QDR.SETTINGS_KEY]) || {
+ $scope.formEntity = angular.fromJson(localStorage[QDR_SETTINGS_KEY]) || {
address: '',
port: '',
username: '',
@@ -51,7 +42,7 @@ var QDR = (function(QDR) {
if (newValue !== oldValue) {
let pass = newValue.password;
newValue.password = '';
- localStorage[QDR.SETTINGS_KEY] = angular.toJson(newValue);
+ localStorage[QDR_SETTINGS_KEY] = angular.toJson(newValue);
newValue.password = pass;
}
}, true);
@@ -84,7 +75,7 @@ var QDR = (function(QDR) {
};
var doConnect = function() {
- QDR.log.info('doConnect called on connect page');
+ QDRLog.info('doConnect called on connect page');
if (!$scope.formEntity.address)
$scope.formEntity.address = 'localhost';
if (!$scope.formEntity.port)
@@ -116,65 +107,7 @@ var QDR = (function(QDR) {
failed(e);
});
};
- }]);
-
-
- QDR.module.directive('posint', function() {
- return {
- require: 'ngModel',
-
- link: function(scope, elem, attr, ctrl) {
- // input type number allows + and - but we don't want them so filter them out
- elem.bind('keypress', function(event) {
- let nkey = !event.charCode ? event.which : event.charCode;
- let skey = String.fromCharCode(nkey);
- let nono = '-+.,';
- if (nono.indexOf(skey) >= 0) {
- event.preventDefault();
- return false;
- }
- // firefox doesn't filter out non-numeric input. it just sets the ctrl to invalid
- if (/[!@#$%^&*()]/.test(skey) && event.shiftKey || // prevent shift numbers
- !( // prevent all but the following
- nkey <= 0 || // arrows
- nkey == 8 || // delete|backspace
- nkey == 13 || // enter
- (nkey >= 37 && nkey <= 40) || // arrows
- event.ctrlKey || event.altKey || // ctrl-v, etc.
- /[0-9]/.test(skey)) // numbers
- ) {
- event.preventDefault();
- return false;
- }
- });
- // check the current value of input
- var _isPortInvalid = function(value) {
- let port = value + '';
- let isErrRange = false;
- // empty string is valid
- if (port.length !== 0) {
- let n = ~~Number(port);
- if (n < 1 || n > 65535) {
- isErrRange = true;
- }
- }
- ctrl.$setValidity('range', !isErrRange);
- return isErrRange;
- };
-
- //For DOM -> model validation
- ctrl.$parsers.unshift(function(value) {
- return _isPortInvalid(value) ? undefined : value;
- });
-
- //For model -> DOM validation
- ctrl.$formatters.unshift(function(value) {
- _isPortInvalid(value);
- return value;
- });
- }
- };
- });
+ }
+}
+SettingsController.$inject = ['QDRService', 'QDRChartService', '$scope', '$log', '$timeout'];
- return QDR;
-}(QDR || {}));
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/qdrTopAddressesController.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrTopAddressesController.js b/console/stand-alone/plugin/js/qdrTopAddressesController.js
index 2c3d3a3..2c27dbc 100644
--- a/console/stand-alone/plugin/js/qdrTopAddressesController.js
+++ b/console/stand-alone/plugin/js/qdrTopAddressesController.js
@@ -16,14 +16,11 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
-'use strict';
/* global angular */
-/**
- * @module QDR
- */
-var QDR = (function(QDR) {
- QDR.module.controller('QDR.TopAddressesController', function ($scope, QDRService, $timeout) {
+export class TopAddressesController {
+ constructor(QDRService, $scope, $timeout) {
+ this.controllerName = 'QDR.TopAddressesController';
$scope.addressesData = [];
$scope.topAddressesGrid = {
@@ -214,7 +211,6 @@ var QDR = (function(QDR) {
clearInterval(timer);
});
- });
- return QDR;
-
-} (QDR || {}));
+ }
+}
+TopAddressesController.$inject = ['QDRService', '$scope', '$timeout'];
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/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
new file mode 100644
index 0000000..d7f4110
--- /dev/null
+++ b/console/stand-alone/plugin/js/topology/links.js
@@ -0,0 +1,216 @@
+/*
+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.
+*/
+
+class Link {
+ constructor(source, target, dir, cls, uid) {
+ this.source = source;
+ this.target = target;
+ this.left = dir != 'out';
+ this.right = (dir == 'out' || dir == 'both');
+ this.cls = cls;
+ this.uid = uid;
+ }
+}
+
+export class Links {
+ constructor(QDRService, logger) {
+ this.links = [];
+ this.QDRService = QDRService;
+ this.logger = logger;
+ }
+ getLinkSource (nodesIndex) {
+ for (let i=0; i<this.links.length; ++i) {
+ if (this.links[i].target === nodesIndex)
+ return i;
+ }
+ return -1;
+ }
+ getLink(_source, _target, dir, cls, uid) {
+ for (let i = 0; i < this.links.length; i++) {
+ let s = this.links[i].source,
+ t = this.links[i].target;
+ if (typeof this.links[i].source == 'object') {
+ s = s.id;
+ t = t.id;
+ }
+ if (s == _source && t == _target) {
+ return i;
+ }
+ // same link, just reversed
+ if (s == _target && t == _source) {
+ return -i;
+ }
+ }
+ //this.logger.debug("creating new link (" + (links.length) + ") between " + nodes[_source].name + " and " + nodes[_target].name);
+ if (this.links.some( function (l) { return l.uid === uid;}))
+ uid = uid + '.' + this.links.length;
+ return this.links.push(new Link(_source, _target, dir, cls, uid)) - 1;
+ }
+ linkFor (source, target) {
+ for (let i = 0; i < this.links.length; ++i) {
+ if ((this.links[i].source == source) && (this.links[i].target == target))
+ return this.links[i];
+ if ((this.links[i].source == target) && (this.links[i].target == source))
+ return this.links[i];
+ }
+ // the selected node was a client/broker
+ return null;
+ }
+
+
+ initializeLinks (nodeInfo, nodes, unknowns, localStorage, height) {
+ let animate = false;
+ let source = 0;
+ let client = 1.0;
+ for (let id in nodeInfo) {
+ let onode = nodeInfo[id];
+ if (!onode['connection'])
+ continue;
+ let conns = onode['connection'].results;
+ let attrs = onode['connection'].attributeNames;
+ //QDRLog.debug("external client parent is " + parent);
+ let normalsParent = {}; // 1st normal node for this parent
+
+ for (let j = 0; j < conns.length; j++) {
+ let connection = this.QDRService.utilities.flatten(attrs, conns[j]);
+ let role = connection.role;
+ let properties = connection.properties || {};
+ let dir = connection.dir;
+ if (role == 'inter-router') {
+ let connId = connection.container;
+ let target = getContainerIndex(connId, nodeInfo, this.QDRService);
+ if (target >= 0) {
+ this.getLink(source, target, dir, '', source + '-' + target);
+ }
+ } /* else if (role == "normal" || role == "on-demand" || role === "route-container")*/ {
+ // not an connection between routers, but an external connection
+ let name = this.QDRService.utilities.nameFromId(id) + '.' + connection.identity;
+
+ // if we have any new clients, animate the force graph to position them
+ let position = localStorage[name] ? JSON.parse(localStorage[name]) : undefined;
+ if ((typeof position == 'undefined')) {
+ animate = true;
+ position = {
+ x: Math.round(nodes.get(source).x + 40 * Math.sin(client / (Math.PI * 2.0))),
+ y: Math.round(nodes.get(source).y + 40 * Math.cos(client / (Math.PI * 2.0))),
+ fixed: false
+ };
+ //QDRLog.debug("new client pos (" + position.x + ", " + position.y + ")")
+ }// else QDRLog.debug("using previous location")
+ if (position.y > height) {
+ position.y = Math.round(nodes.get(source).y + 40 + Math.cos(client / (Math.PI * 2.0)));
+ }
+ let existingNodeIndex = nodes.nodeExists(connection.container);
+ let normalInfo = nodes.normalExists(connection.container);
+ let node = nodes.getOrCreateNode(id, name, role, nodeInfo, nodes.getLength(), position.x, position.y, connection.container, j, position.fixed, properties);
+ let nodeType = this.QDRService.utilities.isAConsole(properties, connection.identity, role, node.key) ? 'console' : 'client';
+ let cdir = getLinkDir(id, connection, onode, this.QDRService);
+ if (existingNodeIndex >= 0) {
+ // make a link between the current router (source) and the existing node
+ this.getLink(source, existingNodeIndex, dir, 'small', connection.name);
+ } else if (normalInfo.nodesIndex) {
+ // get node index of node that contained this connection in its normals array
+ let normalSource = this.getLinkSource(normalInfo.nodesIndex);
+ if (normalSource >= 0) {
+ if (cdir === 'unknown')
+ cdir = dir;
+ node.cdir = cdir;
+ nodes.add(node);
+ // create link from original node to the new node
+ this.getLink(this.links[normalSource].source, nodes.getLength()-1, cdir, 'small', connection.name);
+ // create link from this router to the new node
+ this.getLink(source, nodes.getLength()-1, cdir, 'small', connection.name);
+ // remove the old node from the normals list
+ nodes.get(normalInfo.nodesIndex).normals.splice(normalInfo.normalsIndex, 1);
+ }
+ } else if (role === 'normal') {
+ // normal nodes can be collapsed into a single node if they are all the same dir
+ if (cdir !== 'unknown') {
+ node.user = connection.user;
+ node.isEncrypted = connection.isEncrypted;
+ node.host = connection.host;
+ node.connectionId = connection.identity;
+ node.cdir = cdir;
+ // determine arrow direction by using the link directions
+ if (!normalsParent[nodeType+cdir]) {
+ normalsParent[nodeType+cdir] = node;
+ nodes.add(node);
+ node.normals = [node];
+ // now add a link
+ this.getLink(source, nodes.getLength() - 1, cdir, 'small', connection.name);
+ client++;
+ } else {
+ normalsParent[nodeType+cdir].normals.push(node);
+ }
+ } else {
+ node.id = nodes.getLength() - 1 + unknowns.length;
+ unknowns.push(node);
+ }
+ } else {
+ nodes.add(node);
+ // now add a link
+ this.getLink(source, nodes.getLength() - 1, dir, 'small', connection.name);
+ client++;
+ }
+ }
+ }
+ source++;
+ }
+ return animate;
+ }
+ clearHighlighted () {
+ for (let i = 0; i < this.links.length; ++i) {
+ this.links[i].highlighted = false;
+ }
+ }
+}
+
+var getContainerIndex = function (_id, nodeInfo, QDRService) {
+ let nodeIndex = 0;
+ for (let id in nodeInfo) {
+ if (QDRService.utilities.nameFromId(id) === _id)
+ return nodeIndex;
+ ++nodeIndex;
+ }
+ return -1;
+};
+
+var getLinkDir = function (id, connection, onode, QDRService) {
+ let links = onode['router.link'];
+ if (!links) {
+ return 'unknown';
+ }
+ let inCount = 0, outCount = 0;
+ links.results.forEach( function (linkResult) {
+ let link = QDRService.utilities.flatten(links.attributeNames, linkResult);
+ if (link.linkType === 'endpoint' && link.connectionId === connection.identity)
+ if (link.linkDir === 'in')
+ ++inCount;
+ else
+ ++outCount;
+ });
+ if (inCount > 0 && outCount > 0)
+ return 'both';
+ if (inCount > 0)
+ return 'in';
+ if (outCount > 0)
+ return 'out';
+ return 'unknown';
+};
+
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/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
new file mode 100644
index 0000000..45ef23f
--- /dev/null
+++ b/console/stand-alone/plugin/js/topology/nodes.js
@@ -0,0 +1,162 @@
+/*
+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.
+*/
+
+export class Node {
+ constructor(id, name, nodeType, properties, routerId, x, y, nodeIndex, resultIndex, fixed, connectionContainer) {
+ this.key = id;
+ this.name = name;
+ this.nodeType = nodeType;
+ this.properties = properties;
+ this.routerId = routerId;
+ this.x = x;
+ this.y = y;
+ this.id = nodeIndex;
+ this.resultIndex = resultIndex;
+ this.fixed = !!+fixed;
+ this.cls = '';
+ this.container = connectionContainer;
+ }
+}
+
+export class Nodes {
+ constructor(QDRService, logger) {
+ this.nodes = [];
+ this.QDRService = QDRService;
+ this.logger = logger;
+ }
+ getLength () {
+ return this.nodes.length;
+ }
+ get (index) {
+ if (index < this.getLength()) {
+ return this.nodes[index];
+ }
+ this.logger.error(`Attempted to get node[${index}] but there were only ${this.getLength()} 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];
+ }
+ return null;
+ }
+ nodeExists (connectionContainer) {
+ return this.nodes.findIndex( function (node) {
+ return node.container === connectionContainer;
+ });
+ }
+ normalExists (connectionContainer) {
+ let normalInfo = {};
+ for (let i=0; i<this.nodes.length; ++i) {
+ if (this.nodes[i].normals) {
+ if (this.nodes[i].normals.some(function (normal, j) {
+ if (normal.container === connectionContainer && i !== j) {
+ normalInfo = {nodesIndex: i, normalsIndex: j};
+ return true;
+ }
+ return false;
+ }))
+ break;
+ }
+ }
+ return normalInfo;
+ }
+ savePositions () {
+ 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,
+ });
+ });
+ }
+ find (connectionContainer, properties, name) {
+ properties = properties || {};
+ for (let i=0; i<this.nodes.length; ++i) {
+ if (this.nodes[i].name === name || this.nodes[i].container === connectionContainer) {
+ if (properties.product)
+ this.nodes[i].properties = properties;
+ return this.nodes[i];
+ }
+ }
+ return undefined;
+ }
+ getOrCreateNode (id, name, nodeType, nodeInfo, nodeIndex, x, y,
+ connectionContainer, resultIndex, fixed, properties) {
+ properties = properties || {};
+ let gotNode = this.find(connectionContainer, properties, name);
+ if (gotNode) {
+ return gotNode;
+ }
+ let routerId = this.QDRService.utilities.nameFromId(id);
+ return new Node(id, name, nodeType, properties, routerId, x, y,
+ nodeIndex, resultIndex, fixed, connectionContainer);
+ }
+ add (obj) {
+ this.nodes.push(obj);
+ return obj;
+ }
+ addUsing (id, name, nodeType, nodeInfo, nodeIndex, x, y,
+ connectContainer, resultIndex, fixed, properties) {
+ let obj = this.getOrCreateNode(id, name, nodeType, nodeInfo, nodeIndex, x, y,
+ connectContainer, resultIndex, fixed, properties);
+ this.nodes.push(obj);
+ return obj;
+ }
+ clearHighlighted () {
+ for (let i = 0; i<this.nodes.length; ++i) {
+ this.nodes[i].highlighted = false;
+ }
+ }
+ initialize (nodeInfo, localStorage, width, height) {
+ let nodeCount = Object.keys(nodeInfo).length;
+ let yInit = 50;
+ let animate = false;
+ for (let id in nodeInfo) {
+ let name = this.QDRService.utilities.nameFromId(id);
+ // if we have any new nodes, animate the force graph to position them
+ let position = localStorage[name] ? JSON.parse(localStorage[name]) : undefined;
+ if (!position) {
+ animate = true;
+ position = {
+ x: Math.round(width / 4 + ((width / 2) / nodeCount) * this.nodes.length),
+ y: Math.round(height / 2 + Math.sin(this.nodes.length / (Math.PI*2.0)) * height / 4),
+ fixed: false,
+ };
+ }
+ if (position.y > height) {
+ position.y = 200 - yInit;
+ yInit *= -1;
+ }
+ this.addUsing(id, name, 'inter-router', nodeInfo, this.nodes.length, position.x, position.y, name, undefined, position.fixed, {});
+ }
+ return animate;
+ }
+
+}
+
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[8/8] qpid-dispatch git commit: DISPATCH-1049 Add console tests
Posted by ea...@apache.org.
DISPATCH-1049 Add console tests
Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/b5deb035
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/b5deb035
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/b5deb035
Branch: refs/heads/master
Commit: b5deb03579a7dedd81a56f32baa3e5f4b5b57136
Parents: af99754
Author: Ernest Allen <ea...@redhat.com>
Authored: Mon Jun 25 17:25:50 2018 -0400
Committer: Ernest Allen <ea...@redhat.com>
Committed: Mon Jun 25 17:25:50 2018 -0400
----------------------------------------------------------------------
.gitignore | 2 +-
console/CMakeLists.txt | 207 +-
console/stand-alone/.babelrc | 21 +
console/stand-alone/gulpfile.js | 101 +-
console/stand-alone/index.html | 11 +-
console/stand-alone/main.js | 254 ++
console/stand-alone/modules/connection.js | 347 +++
console/stand-alone/modules/correlator.js | 50 +
console/stand-alone/modules/management.js | 63 +
console/stand-alone/modules/topology.js | 403 +++
console/stand-alone/modules/utilities.js | 115 +
console/stand-alone/package-lock.json | 2519 ++++++++++++++----
console/stand-alone/package.json | 24 +-
console/stand-alone/plugin/css/dispatch.css | 4 +-
console/stand-alone/plugin/html/qdrList.html | 53 +-
.../plugin/html/tmplChartConfig.html | 12 +-
console/stand-alone/plugin/js/chord/data.js | 234 +-
console/stand-alone/plugin/js/chord/filters.js | 52 +-
.../plugin/js/chord/layout/layout.js | 6 +-
console/stand-alone/plugin/js/chord/matrix.js | 3 +-
console/stand-alone/plugin/js/chord/qdrChord.js | 24 +-
.../plugin/js/chord/ribbon/ribbon.js | 4 +-
console/stand-alone/plugin/js/dispatchPlugin.js | 264 --
.../stand-alone/plugin/js/dlgChartController.js | 31 +-
console/stand-alone/plugin/js/navbar.js | 140 +-
.../stand-alone/plugin/js/posintDirective.js | 75 +
.../stand-alone/plugin/js/qdrChartService.js | 1607 ++++++-----
console/stand-alone/plugin/js/qdrCharts.js | 33 +-
console/stand-alone/plugin/js/qdrGlobals.js | 58 +-
console/stand-alone/plugin/js/qdrList.js | 1732 ++++++------
console/stand-alone/plugin/js/qdrListChart.js | 146 -
console/stand-alone/plugin/js/qdrOverview.js | 85 +-
.../plugin/js/qdrOverviewChartsController.js | 18 +-
.../plugin/js/qdrOverviewLogsController.js | 25 +-
console/stand-alone/plugin/js/qdrSchema.js | 22 +-
console/stand-alone/plugin/js/qdrService.js | 317 +--
console/stand-alone/plugin/js/qdrSettings.js | 89 +-
.../plugin/js/qdrTopAddressesController.js | 16 +-
console/stand-alone/plugin/js/topology/links.js | 216 ++
console/stand-alone/plugin/js/topology/nodes.js | 162 ++
.../plugin/js/topology/qdrTopology.js | 2512 +++++++----------
.../stand-alone/plugin/js/topology/topoUtils.js | 225 ++
.../stand-alone/plugin/js/topology/traffic.js | 445 ++++
.../stand-alone/plugin/js/topology/traffic.ts | 443 ---
console/stand-alone/test/filter.js | 73 +
console/stand-alone/test/links.js | 82 +
console/stand-alone/test/matrix.js | 51 +
console/stand-alone/test/nodes.json | 1 +
console/stand-alone/test/utilities.js | 192 ++
console/stand-alone/tsconfig.json | 23 +-
console/stand-alone/vendor-js.txt | 12 +-
51 files changed, 8155 insertions(+), 5449 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index 49a9c44..d7c32e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,7 +12,7 @@ tests/policy-1/policy-*.json
.metadata
.settings
console/test/topolgies/config-*
-.history
+.history/
.tox
.vscode
console/stand-alone/node_modules/
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/console/CMakeLists.txt b/console/CMakeLists.txt
index bfd9cd8..a55b572 100644
--- a/console/CMakeLists.txt
+++ b/console/CMakeLists.txt
@@ -20,101 +20,122 @@
##
## Add cmake option to choose whether to install stand-alone console
##
-option(CONSOLE_INSTALL "Build and install console (requires npm)" ON)
+option(CONSOLE_INSTALL "Build and install console (requires npm 5.2+)" ON)
if(CONSOLE_INSTALL)
- find_program(NPX_EXE npx DOC "Location of the npx task runner")
- if (NPX_EXE)
-
- set(CONSOLE_SOURCE_DIR "${CMAKE_SOURCE_DIR}/console/stand-alone")
- set(CONSOLE_BUILD_DIR "${CMAKE_BINARY_DIR}/console")
-
- ## Files needed to create the ${CONSOLE_ARTIFACTS}
- file (GLOB_RECURSE CONSOLE_JS_SOURCES ${CONSOLE_SOURCE_DIR}/plugin/js/*.js)
- file (GLOB_RECURSE CONSOLE_TS_SOURCES ${CONSOLE_SOURCE_DIR}/plugin/js/*.ts)
- set(CONSOLE_CSS_SOURCE ${CONSOLE_SOURCE_DIR}/plugin/css/dispatch.css)
- set(ALL_CONSOLE_SOURCES ${CONSOLE_JS_SOURCES} ${CONSOLE_TS_SOURCES} ${CONSOLE_CSS_SOURCE})
-
- ## Files created during the console build
- set(CONSOLE_ARTIFACTS
- ${CONSOLE_BUILD_DIR}/dist/js/dispatch.min.js
- ${CONSOLE_BUILD_DIR}/dist/js/vendor.min.js
- ${CONSOLE_BUILD_DIR}/dist/css/dispatch.min.css
- ${CONSOLE_BUILD_DIR}/dist/css/vendor.min.css
- )
-
- ## copy the build config files
- configure_file( ${CONSOLE_SOURCE_DIR}/package.json ${CONSOLE_BUILD_DIR}/ COPYONLY)
- configure_file( ${CONSOLE_SOURCE_DIR}/package-lock.json ${CONSOLE_BUILD_DIR}/ COPYONLY)
- configure_file( ${CONSOLE_SOURCE_DIR}/tslint.json ${CONSOLE_BUILD_DIR}/ COPYONLY)
- configure_file( ${CONSOLE_SOURCE_DIR}/gulpfile.js ${CONSOLE_BUILD_DIR}/ COPYONLY)
- configure_file( ${CONSOLE_SOURCE_DIR}/vendor-js.txt ${CONSOLE_BUILD_DIR}/ COPYONLY)
- configure_file( ${CONSOLE_SOURCE_DIR}/vendor-css.txt ${CONSOLE_BUILD_DIR}/ COPYONLY)
-
- ## Tell cmake how and when to build ${CONSOLE_ARTIFACTS}
- add_custom_command (
- OUTPUT ${CONSOLE_ARTIFACTS}
- COMMENT "Running console build"
- COMMAND npm install --loglevel=error
- COMMAND ${NPX_EXE} gulp --src ${CONSOLE_SOURCE_DIR}
- DEPENDS ${ALL_CONSOLE_SOURCES}
- WORKING_DIRECTORY ${CONSOLE_BUILD_DIR}/
- )
-
- ## Ensure ${CONSOLE_ARTIFACTS} is built on a make when needed
- add_custom_target(console ALL
- DEPENDS ${CONSOLE_ARTIFACTS}
- )
-
- ##
- ## Install the static and built console files
- ##
-
- ## Files copied to the root of the console's install dir
- set(BASE_FILES
- ${CONSOLE_SOURCE_DIR}/index.html
- ${CONSOLE_SOURCE_DIR}/favicon-32x32.png
- )
- ## Files copied to the css/ dir
- set(CSS_FONTS
- ${CONSOLE_SOURCE_DIR}/plugin/css/brokers.ttf
- ${CONSOLE_BUILD_DIR}/node_modules/angular-ui-grid/ui-grid.woff
- ${CONSOLE_BUILD_DIR}/node_modules/angular-ui-grid/ui-grid.ttf
- )
- ## Files copied to the fonts/ dir
- set(VENDOR_FONTS
- ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Regular-webfont.woff2
- ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Bold-webfont.woff2
- ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Light-webfont.woff2
- ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Semibold-webfont.woff2
- ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-BoldItalic-webfont.woff2
- ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Italic-webfont.woff2
- ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/fontawesome-webfont.woff2
- ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/fontawesome-webfont.eot
- ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/PatternFlyIcons-webfont.ttf
- ${CONSOLE_BUILD_DIR}/node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2
- )
-
- install(DIRECTORY ${CONSOLE_BUILD_DIR}/dist/
- DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}
- PATTERN "*.map" EXCLUDE
- )
- install(DIRECTORY ${CONSOLE_SOURCE_DIR}/plugin/html/
- DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/html
- FILES_MATCHING PATTERN "*.html"
- )
- install(FILES ${BASE_FILES}
- DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}
- )
- install(FILES ${CSS_FONTS}
- DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/css/
- )
- install(FILES ${VENDOR_FONTS}
- DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/fonts/
- )
- else(NPX_EXE)
- message(STATUS "Cannot build console, npm not found")
- endif(NPX_EXE)
+ find_program (NPM_EXECUTABLE npm DOC "Location of npm package manager")
+
+ if (NPM_EXECUTABLE)
+ execute_process(COMMAND ${NPM_EXECUTABLE} --version
+ OUTPUT_VARIABLE NPM_VERSION)
+ if(${NPM_VERSION} VERSION_EQUAL "5.2.0" OR ${NPM_VERSION} VERSION_GREATER "5.2.0")
+
+ find_program(NPX_EXE npx DOC "Location of the npx task runner")
+ if (NPX_EXE)
+
+ set(CONSOLE_SOURCE_DIR "${CMAKE_SOURCE_DIR}/console/stand-alone")
+ set(CONSOLE_BUILD_DIR "${CMAKE_BINARY_DIR}/console")
+
+ ## Files needed to create the ${CONSOLE_ARTIFACTS}
+ file (GLOB_RECURSE CONSOLE_JS_SOURCES ${CONSOLE_SOURCE_DIR}/plugin/js/*.js)
+ file (GLOB_RECURSE CONSOLE_TS_SOURCES ${CONSOLE_SOURCE_DIR}/plugin/js/*.ts)
+ file (GLOB_RECURSE CONSOLE_MODULE_SOURCES ${CONSOLE_SOURCE_DIR}/modules/*.js)
+ set(CONSOLE_CSS_SOURCE ${CONSOLE_SOURCE_DIR}/plugin/css/dispatch.css)
+ set(CONSOLE_MAIN ${CONSOLE_SOURCE_DIR}/main.js)
+ set(ALL_CONSOLE_SOURCES ${CONSOLE_MAIN} ${CONSOLE_MODULE_SOURCES} ${CONSOLE_JS_SOURCES} ${CONSOLE_TS_SOURCES} ${CONSOLE_CSS_SOURCE})
+
+ ## Files created during the console build
+ set(CONSOLE_ARTIFACTS
+ ${CONSOLE_BUILD_DIR}/dist/js/main.min.js
+ ${CONSOLE_BUILD_DIR}/dist/js/vendor.min.js
+ ${CONSOLE_BUILD_DIR}/dist/css/dispatch.min.css
+ ${CONSOLE_BUILD_DIR}/dist/css/vendor.min.css
+ )
+
+ ## copy the build config files
+ configure_file( ${CONSOLE_SOURCE_DIR}/package.json ${CONSOLE_BUILD_DIR}/ COPYONLY)
+ configure_file( ${CONSOLE_SOURCE_DIR}/package-lock.json ${CONSOLE_BUILD_DIR}/ COPYONLY)
+ configure_file( ${CONSOLE_SOURCE_DIR}/tslint.json ${CONSOLE_BUILD_DIR}/ COPYONLY)
+ configure_file( ${CONSOLE_SOURCE_DIR}/gulpfile.js ${CONSOLE_BUILD_DIR}/ COPYONLY)
+ configure_file( ${CONSOLE_SOURCE_DIR}/vendor-js.txt ${CONSOLE_BUILD_DIR}/ COPYONLY)
+ configure_file( ${CONSOLE_SOURCE_DIR}/vendor-css.txt ${CONSOLE_BUILD_DIR}/ COPYONLY)
+
+ ## Tell cmake how and when to build ${CONSOLE_ARTIFACTS}
+ add_custom_command (
+ OUTPUT ${CONSOLE_ARTIFACTS}
+ COMMENT "Running console build"
+ COMMAND npm install --loglevel=error
+ COMMAND ${NPX_EXE} gulp --src ${CONSOLE_SOURCE_DIR} --build "production"
+ DEPENDS ${ALL_CONSOLE_SOURCES}
+ WORKING_DIRECTORY ${CONSOLE_BUILD_DIR}/
+ )
+
+ ## Ensure ${CONSOLE_ARTIFACTS} is built on a make when needed
+ add_custom_target(console ALL
+ DEPENDS ${CONSOLE_ARTIFACTS}
+ )
+
+ ##
+ ## Install the static and built console files
+ ##
+
+ ## Files copied to the root of the console's install dir
+ set(BASE_FILES
+ ${CONSOLE_SOURCE_DIR}/index.html
+ ${CONSOLE_SOURCE_DIR}/favicon-32x32.png
+ )
+ ## Files copied to the css/ dir
+ set(CSS_FONTS
+ ${CONSOLE_SOURCE_DIR}/plugin/css/brokers.ttf
+ )
+ ## Files copied to the css/fonts/ dir
+ set(CSSFONTS_FONTS
+ ${CONSOLE_BUILD_DIR}/node_modules/angular-ui-grid/fonts/ui-grid.woff
+ ${CONSOLE_BUILD_DIR}/node_modules/angular-ui-grid/fonts/ui-grid.ttf
+ )
+ ## Files copied to the fonts/ dir
+ set(VENDOR_FONTS
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Regular-webfont.woff2
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Bold-webfont.woff2
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Light-webfont.woff2
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Semibold-webfont.woff2
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-SemiboldItalic-webfont.woff2
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-BoldItalic-webfont.woff2
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/OpenSans-Italic-webfont.woff2
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/fontawesome-webfont.woff2
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/fontawesome-webfont.eot
+ ${CONSOLE_BUILD_DIR}/node_modules/patternfly/dist/fonts/PatternFlyIcons-webfont.ttf
+ ${CONSOLE_BUILD_DIR}/node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2
+ )
+
+ install(DIRECTORY ${CONSOLE_BUILD_DIR}/dist/
+ DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}
+ PATTERN "*.map" EXCLUDE
+ )
+ install(DIRECTORY ${CONSOLE_SOURCE_DIR}/plugin/html/
+ DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/html
+ FILES_MATCHING PATTERN "*.html"
+ )
+ install(FILES ${BASE_FILES}
+ DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}
+ )
+ install(FILES ${CSS_FONTS}
+ DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/css/
+ )
+ install(FILES ${CSSFONTS_FONTS}
+ DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/css/fonts/
+ )
+ install(FILES ${VENDOR_FONTS}
+ DESTINATION ${CONSOLE_STAND_ALONE_INSTALL_DIR}/fonts/
+ )
+ else(NPX_EXE)
+ message(STATUS "Cannot build console, npx not found.")
+ endif(NPX_EXE)
+ else(${NPM_VERSION} VERSION_EQUAL "5.2.0" OR ${NPM_VERSION} VERSION_GREATER "5.2.0")
+ message(STATUS "Cannot build console. npm version 5.2 or greater is required.")
+ endif(${NPM_VERSION} VERSION_EQUAL "5.2.0" OR ${NPM_VERSION} VERSION_GREATER "5.2.0")
+ endif(NPM_EXECUTABLE)
+
endif(CONSOLE_INSTALL)
##
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/.babelrc
----------------------------------------------------------------------
diff --git a/console/stand-alone/.babelrc b/console/stand-alone/.babelrc
new file mode 100644
index 0000000..d67a300
--- /dev/null
+++ b/console/stand-alone/.babelrc
@@ -0,0 +1,21 @@
+/*
+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.
+*/
+{
+ "presets": ["es2015"]
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/gulpfile.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/gulpfile.js b/console/stand-alone/gulpfile.js
index 46531c3..0ddaf10 100644
--- a/console/stand-alone/gulpfile.js
+++ b/console/stand-alone/gulpfile.js
@@ -19,20 +19,26 @@ under the License.
`;
const gulp = require('gulp'),
- babel = require('gulp-babel'),
+ mocha = require('gulp-mocha'),
+ gulpif = require('gulp-if'),
+ rollup = require('rollup-stream'),
+ source = require('vinyl-source-stream'),
+ buffer = require('vinyl-buffer'),
concat = require('gulp-concat'),
uglify = require('gulp-uglify'),
+ terser = require('gulp-terser'),
+ babel = require('gulp-babel'),
ngAnnotate = require('gulp-ng-annotate'),
- rename = require('gulp-rename'),
cleanCSS = require('gulp-clean-css'),
del = require('del'),
eslint = require('gulp-eslint'),
maps = require('gulp-sourcemaps'),
insert = require('gulp-insert'),
+ rename = require('gulp-rename'),
fs = require('fs'),
tsc = require('gulp-typescript'),
- tslint = require('gulp-tslint');
- //tsProject = tsc.createProject('tsconfig.json');
+ tslint = require('gulp-tslint'),
+ through = require('through2');
// temp directory for converted typescript files
const built_ts = 'built_ts';
@@ -59,6 +65,7 @@ const arg = (argList => {
})(process.argv);
var src = arg.src ? arg.src + '/' : '';
+var production = (arg.build === 'production');
const paths = {
typescript: {
@@ -70,10 +77,14 @@ const paths = {
dest: 'dist/css/'
},
scripts: {
- src: [src + 'plugin/js/**/*.js', built_ts + '/**/*.js'],
+ src: [src + 'plugin/js/**/*.js'],
dest: 'dist/js/'
}
};
+var touch = through.obj(function(file, enc, done) {
+ var now = new Date;
+ fs.utimes(file.path, now, now, done);
+});
function clean() {
return del(['dist',built_ts ]);
@@ -110,20 +121,6 @@ function vendor_styles() {
.pipe(gulp.dest(paths.styles.dest));
}
-function scripts() {
- return gulp.src(paths.scripts.src, { sourcemaps: true })
- .pipe(babel({
- presets: [require.resolve('babel-preset-env')]
- }))
- .pipe(ngAnnotate())
- .pipe(maps.init())
- .pipe(uglify())
- .pipe(concat('dispatch.min.js'))
- .pipe(insert.prepend(license))
- .pipe(maps.write('./'))
- .pipe(gulp.dest(paths.scripts.dest));
-}
-
function vendor_scripts() {
var vendor_lines = fs.readFileSync('vendor-js.txt').toString().split('\n');
var vendor_files = vendor_lines.filter( function (line) {
@@ -134,7 +131,8 @@ function vendor_scripts() {
.pipe(uglify())
.pipe(concat('vendor.min.js'))
.pipe(maps.write('./'))
- .pipe(gulp.dest(paths.scripts.dest));
+ .pipe(gulp.dest(paths.scripts.dest))
+ .pipe(touch);
}
function watch() {
gulp.watch(paths.scripts.src, scripts);
@@ -167,10 +165,68 @@ function ts_lint() {
.pipe(tslint.report());
}
+function scripts() {
+ return rollup({
+ input: src + './main.js',
+ sourcemap: true,
+ format: 'es'
+ }).on('error', e => {
+ console.error(`${e.stack}`);
+ })
+
+ // point to the entry file and gives the name of the output file.
+ .pipe(source('main.min.js', src))
+
+ // buffer the output. most gulp plugins, including gulp-sourcemaps, don't support streams.
+ .pipe(buffer())
+
+ // tell gulp-sourcemaps to load the inline sourcemap produced by rollup-stream.
+ .pipe(maps.init({loadMaps: true}))
+ // transform the code further here.
+ /*
+ .pipe(babel(
+ {presets: [
+ ['env', {
+ targets: {
+ 'browsers': [
+ 'Chrome >= 52',
+ 'FireFox >= 44',
+ 'Safari >= 7',
+ 'Explorer 11',
+ 'last 4 Edge versions'
+ ]
+ },
+ useBuiltIns: true,
+ //debug: true
+ }],
+ 'es2015'
+ ],
+ 'ignore': [
+ 'node_modules'
+ ]
+ }
+ ))
+ */
+ .pipe(ngAnnotate())
+ //.pipe(gulpif(production, uglify()))
+ .pipe(gulpif(production, terser()))
+ .pipe(gulpif(production, insert.prepend(license)))
+ // write the sourcemap alongside the output file.
+ .pipe(maps.write('.'))
+
+ // and output to ./dist/main.js as normal.
+ .pipe(gulp.dest(paths.scripts.dest));
+}
+
+function test () {
+ return gulp.src(['test/**/*.js'], {read: false})
+ .pipe(mocha({require: ['babel-core/register'], exit: true}))
+ .on('error', console.error);
+}
+
var build = gulp.series(
clean, // removes the dist/ dir
- gulp.parallel(lint, ts_lint), // lints the .js, .ts files
- typescript, // converts .ts to .js
+ lint, // lints the .js
gulp.parallel(vendor_styles, vendor_scripts, styles, scripts), // uglify and concat
cleanup // remove .js that were converted from .ts
);
@@ -186,5 +242,6 @@ exports.tsc = typescript;
exports.scripts = scripts;
exports.styles = styles;
exports.vendor = vendor;
+exports.test = test;
gulp.task('default', build);
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/index.html
----------------------------------------------------------------------
diff --git a/console/stand-alone/index.html b/console/stand-alone/index.html
index 68070a3..1f08594 100644
--- a/console/stand-alone/index.html
+++ b/console/stand-alone/index.html
@@ -20,7 +20,6 @@ under the License.
<html xmlns:ng="https://angularjs.org">
<head>
-
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
@@ -76,9 +75,15 @@ under the License.
</div>
</div>
+<!-- <script type="module" src="js/main.min.js"></script> -->
<script type="text/javascript" src="js/vendor.min.js"></script>
-<script type="text/javascript" src="js/dispatch.min.js"></script>
-
+<script type="text/javascript" src="js/main.min.js"></script>
+<script defer nomodule>
+ var installError = document.getElementById('installError');
+ if (installError) {
+ installError.innerHTML = 'This browser is not supported because it does not support es-2015 modules. <a href="https://www.ecma-international.org/ecma-262/6.0/">https://www.ecma-international.org/ecma-262/6.0/</a><br/>Please use a different browser.';
+ }
+</script>
<script>
// If angular hasn't loaded a page after 1 second, display the error message
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/main.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/main.js b/console/stand-alone/main.js
new file mode 100644
index 0000000..ca69709
--- /dev/null
+++ b/console/stand-alone/main.js
@@ -0,0 +1,254 @@
+/*
+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.
+*/
+/* global angular d3 */
+
+/**
+ * @module QDR
+ * @main QDR
+ *
+ * The main entry point for the QDR module
+ *
+ */
+
+//import angular from 'angular';
+import { QDRLogger, QDRTemplatePath, QDR_LAST_LOCATION } from './plugin/js/qdrGlobals.js';
+import { QDRService } from './plugin/js/qdrService.js';
+import { QDRChartService } from './plugin/js/qdrChartService.js';
+import { NavBarController } from './plugin/js/navbar.js';
+import { OverviewController } from './plugin/js/qdrOverview.js';
+import { OverviewChartsController } from './plugin/js/qdrOverviewChartsController.js';
+import { OverviewLogsController } from './plugin/js/qdrOverviewLogsController.js';
+import { TopologyController } from './plugin/js/topology/qdrTopology.js';
+import { ChordController } from './plugin/js/chord/qdrChord.js';
+import { ListController } from './plugin/js/qdrList.js';
+import { TopAddressesController } from './plugin/js/qdrTopAddressesController.js';
+import { ChartDialogController } from './plugin/js/dlgChartController.js';
+import { SettingsController } from './plugin/js/qdrSettings.js';
+import { SchemaController } from './plugin/js/qdrSchema.js';
+import { ChartsController } from './plugin/js/qdrCharts.js';
+import { posint } from './plugin/js/posintDirective.js';
+
+(function(QDR) {
+
+ /**
+ * This plugin's angularjs module instance
+ */
+ QDR.module = angular.module('QDR', ['ngRoute', 'ngSanitize', 'ngResource', 'ui.bootstrap',
+ 'ui.grid', 'ui.grid.selection', 'ui.grid.autoResize', 'ui.grid.resizeColumns', 'ui.grid.saveState',
+ 'ui.slider', 'ui.checkbox']);
+
+ // set up the routing for this plugin
+ QDR.module.config(function($routeProvider) {
+ $routeProvider
+ .when('/', {
+ templateUrl: QDRTemplatePath + 'qdrOverview.html'
+ })
+ .when('/overview', {
+ templateUrl: QDRTemplatePath + 'qdrOverview.html'
+ })
+ .when('/topology', {
+ templateUrl: QDRTemplatePath + 'qdrTopology.html'
+ })
+ .when('/list', {
+ templateUrl: QDRTemplatePath + 'qdrList.html'
+ })
+ .when('/schema', {
+ templateUrl: QDRTemplatePath + 'qdrSchema.html'
+ })
+ .when('/charts', {
+ templateUrl: QDRTemplatePath + 'qdrCharts.html'
+ })
+ .when('/chord', {
+ templateUrl: QDRTemplatePath + 'qdrChord.html'
+ })
+ .when('/connect', {
+ templateUrl: QDRTemplatePath + 'qdrConnect.html'
+ });
+ });
+
+ QDR.module.config(function ($compileProvider) {
+ $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrome-extension|file|blob):/);
+ $compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrome-extension):/);
+ });
+
+ QDR.module.filter('to_trusted', ['$sce', function($sce){
+ return function(text) {
+ return $sce.trustAsHtml(text);
+ };
+ }]);
+
+ QDR.module.filter('humanify', ['QDRService', function (QDRService) {
+ return function (input) {
+ return QDRService.utilities.humanify(input);
+ };
+ }]);
+
+ QDR.module.filter('Pascalcase', function () {
+ return function (str) {
+ if (!str)
+ return '';
+ return str.replace(/(\w)(\w*)/g,
+ function(g0,g1,g2){return g1.toUpperCase() + g2.toLowerCase();});
+ };
+ });
+
+ QDR.module.filter('safePlural', function () {
+ return function (str) {
+ var es = ['x', 'ch', 'ss', 'sh'];
+ for (var i=0; i<es.length; ++i) {
+ if (str.endsWith(es[i]))
+ return str + 'es';
+ }
+ if (str.endsWith('y'))
+ return str.substr(0, str.length-2) + 'ies';
+ if (str.endsWith('s'))
+ return str;
+ return str + 's';
+ };
+ });
+
+ QDR.module.filter('pretty', function () {
+ return function (str) {
+ var formatComma = d3.format(',');
+ if (!isNaN(parseFloat(str)) && isFinite(str))
+ return formatComma(str);
+ return str;
+ };
+ });
+
+ // one-time initialization happens in the run function
+ // of our module
+ QDR.module.run( ['$rootScope', '$route', '$timeout', '$location', '$log', 'QDRService', 'QDRChartService', function ($rootScope, $route, $timeout, $location, $log, QDRService, QDRChartService) {
+ let QDRLog = new QDRLogger($log, 'main');
+ QDRLog.info('************* creating Dispatch Console ************');
+
+ var curPath = $location.path();
+ var org = curPath.substr(1);
+ if (org && org.length > 0 && org !== 'connect') {
+ $location.search('org', org);
+ } else {
+ $location.search('org', null);
+ }
+ QDR.queue = d3.queue;
+
+ if (!QDRService.management.connection.is_connected()) {
+ // attempt to connect to the host:port that served this page
+ var host = $location.host();
+ var port = $location.port();
+ var search = $location.search();
+ if (search.org) {
+ if (search.org === 'connect')
+ $location.search('org', 'overview');
+ }
+ var connectOptions = {address: host, port: port};
+ QDRLog.info('Attempting AMQP over websockets connection using address:port of browser ('+host+':'+port+')');
+ QDRService.management.connection.testConnect(connectOptions)
+ .then( function () {
+ // We didn't connect with reconnect: true flag.
+ // The reason being that if we used reconnect:true and the connection failed, rhea would keep trying. There
+ // doesn't appear to be a way to tell it to stop trying to reconnect.
+ QDRService.disconnect();
+ QDRLog.info('Connect succeeded. Using address:port of browser');
+ connectOptions.reconnect = true;
+ // complete the connection (create the sender/receiver)
+ QDRService.connect(connectOptions)
+ .then( function () {
+ // register a callback for when the node list is available (needed for loading saved charts)
+ QDRService.management.topology.addUpdatedAction('initChartService', function() {
+ QDRService.management.topology.delUpdatedAction('initChartService');
+ QDRChartService.init(); // initialize charting service after we are connected
+ });
+ // get the list of nodes
+ QDRService.management.topology.startUpdating(false);
+ });
+ }, function () {
+ QDRLog.info('failed to auto-connect to ' + host + ':' + port);
+ QDRLog.info('redirecting to connect page');
+ $timeout(function () {
+ $location.path('/connect');
+ $location.search('org', org);
+ $location.replace();
+ });
+ });
+ }
+
+ $rootScope.$on('$routeChangeSuccess', function() {
+ var path = $location.path();
+ if (path !== '/connect') {
+ localStorage[QDR_LAST_LOCATION] = path;
+ }
+ });
+ }]);
+
+ QDR.module.controller ('QDR.MainController', ['$scope', '$log', '$location', function ($scope, $log, $location) {
+ let QDRLog = new QDRLogger($log, 'MainController');
+ QDRLog.debug('started QDR.MainController with location.url: ' + $location.url());
+ QDRLog.debug('started QDR.MainController with window.location.pathname : ' + window.location.pathname);
+ $scope.topLevelTabs = [];
+ $scope.topLevelTabs.push({
+ id: 'qdr',
+ content: 'Qpid Dispatch Router Console',
+ title: 'Dispatch Router Console',
+ isValid: function() { return true; },
+ href: function() { return '#connect'; },
+ isActive: function() { return true; }
+ });
+ }]);
+
+ QDR.module.controller ('QDR.Core', function ($scope, $rootScope) {
+ $scope.alerts = [];
+ $scope.breadcrumb = {};
+ $scope.closeAlert = function(index) {
+ $scope.alerts.splice(index, 1);
+ };
+ $scope.$on('setCrumb', function(event, data) {
+ $scope.breadcrumb = data;
+ });
+ $scope.$on('newAlert', function(event, data) {
+ $scope.alerts.push(data);
+ $scope.$apply();
+ });
+ $scope.$on('clearAlerts', function () {
+ $scope.alerts = [];
+ $scope.$apply();
+ });
+ $scope.pageMenuClicked = function () {
+ $rootScope.$broadcast('pageMenuClicked');
+ };
+ });
+
+ QDR.module.controller('QDR.NavBarController', NavBarController);
+ QDR.module.controller('QDR.OverviewController', OverviewController);
+ QDR.module.controller('QDR.OverviewChartsController', OverviewChartsController);
+ QDR.module.controller('QDR.OverviewLogsController', OverviewLogsController);
+ QDR.module.controller('QDR.TopAddressesController', TopAddressesController);
+ QDR.module.controller('QDR.ChartDialogController', ChartDialogController);
+ QDR.module.controller('QDR.SettingsController', SettingsController);
+ QDR.module.controller('QDR.TopologyController', TopologyController);
+ QDR.module.controller('QDR.ChordController', ChordController);
+ QDR.module.controller('QDR.ListController', ListController);
+ QDR.module.controller('QDR.SchemaController', SchemaController);
+ QDR.module.controller('QDR.ChartsController', ChartsController);
+
+ QDR.module.service('QDRService', QDRService);
+ QDR.module.service('QDRChartService', QDRChartService);
+ QDR.module.directive('posint', posint);
+ // .directive('exampleDirective', () => new ExampleDirective);
+}({}));
+
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/modules/connection.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/modules/connection.js b/console/stand-alone/modules/connection.js
new file mode 100644
index 0000000..db21e01
--- /dev/null
+++ b/console/stand-alone/modules/connection.js
@@ -0,0 +1,347 @@
+/*
+ * Copyright 2017 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 Promise */
+
+const rhea = require('rhea');
+//import { on, websocket_connect, removeListener, once, connect } from 'rhea';
+import Correlator from './correlator.js';
+
+class ConnectionManager {
+ constructor(protocol) {
+ this.sender = undefined;
+ this.receiver = undefined;
+ this.connection = undefined;
+ this.version = undefined;
+ this.errorText = undefined;
+ this.protocol = protocol;
+ this.schema = undefined;
+ this.connectActions = [];
+ this.disconnectActions = [];
+ this.correlator = new Correlator();
+ this.on_message = (function (context) {
+ this.correlator.resolve(context);
+ }).bind(this);
+ this.on_disconnected = (function () {
+ this.errorText = 'Disconnected';
+ this.executeDisconnectActions(this.errorText);
+ }).bind(this);
+ this.on_connection_open = (function () {
+ this.executeConnectActions();
+ }).bind(this);
+ }
+ versionCheck(minVer) {
+ var verparts = this.version.split('.');
+ var minparts = minVer.split('.');
+ try {
+ for (var i = 0; i < minparts.length; ++i) {
+ if (parseInt(minVer[i] > parseInt(verparts[i])))
+ return false;
+ }
+ }
+ catch (e) {
+ return false;
+ }
+ return true;
+ }
+ addConnectAction(action) {
+ if (typeof action === 'function') {
+ this.delConnectAction(action);
+ this.connectActions.push(action);
+ }
+ }
+ addDisconnectAction(action) {
+ if (typeof action === 'function') {
+ this.delDisconnectAction(action);
+ this.disconnectActions.push(action);
+ }
+ }
+ delConnectAction(action) {
+ if (typeof action === 'function') {
+ var index = this.connectActions.indexOf(action);
+ if (index >= 0)
+ this.connectActions.splice(index, 1);
+ }
+ }
+ delDisconnectAction(action) {
+ if (typeof action === 'function') {
+ var index = this.disconnectActions.indexOf(action);
+ if (index >= 0)
+ this.disconnectActions.splice(index, 1);
+ }
+ }
+ executeConnectActions() {
+ this.connectActions.forEach(function (action) {
+ try {
+ action();
+ }
+ catch (e) {
+ // in case the page that registered the handler has been unloaded
+ }
+ });
+ this.connectActions = [];
+ }
+ executeDisconnectActions(message) {
+ this.disconnectActions.forEach(function (action) {
+ try {
+ action(message);
+ }
+ catch (e) {
+ // in case the page that registered the handler has been unloaded
+ }
+ });
+ this.disconnectActions = [];
+ }
+ on(eventType, fn) {
+ if (eventType === 'connected') {
+ this.addConnectAction(fn);
+ }
+ else if (eventType === 'disconnected') {
+ this.addDisconnectAction(fn);
+ }
+ else {
+ console.log('unknown event type ' + eventType);
+ }
+ }
+ setSchema(schema) {
+ this.schema = schema;
+ }
+ is_connected() {
+ return this.connection &&
+ this.sender &&
+ this.receiver &&
+ this.receiver.remote &&
+ this.receiver.remote.attach &&
+ this.receiver.remote.attach.source &&
+ this.receiver.remote.attach.source.address &&
+ this.connection.is_open();
+ }
+ disconnect() {
+ if (this.sender)
+ this.sender.close();
+ if (this.receiver)
+ this.receiver.close();
+ if (this.connection)
+ this.connection.close();
+ }
+ createSenderReceiver(options) {
+ return new Promise((function (resolve, reject) {
+ var timeout = options.timeout || 10000;
+ // set a timer in case the setup takes too long
+ var giveUp = (function () {
+ this.connection.removeListener('receiver_open', receiver_open);
+ this.connection.removeListener('sendable', sendable);
+ this.errorText = 'timed out creating senders and receivers';
+ reject(Error(this.errorText));
+ }).bind(this);
+ var timer = setTimeout(giveUp, timeout);
+ // register an event hander for when the setup is complete
+ var sendable = (function (context) {
+ clearTimeout(timer);
+ this.version = this.connection.properties ? this.connection.properties.version : '0.1.0';
+ // in case this connection dies
+ rhea.on('disconnected', this.on_disconnected);
+ // in case this connection dies and is then reconnected automatically
+ rhea.on('connection_open', this.on_connection_open);
+ // receive messages here
+ this.connection.on('message', this.on_message);
+ resolve(context);
+ }).bind(this);
+ this.connection.once('sendable', sendable);
+ // Now actually createt the sender and receiver.
+ // register an event handler for when the receiver opens
+ var receiver_open = (function () {
+ // once the receiver is open, create the sender
+ if (options.sender_address)
+ this.sender = this.connection.open_sender(options.sender_address);
+ else
+ this.sender = this.connection.open_sender();
+ }).bind(this);
+ this.connection.once('receiver_open', receiver_open);
+ // create a dynamic receiver
+ this.receiver = this.connection.open_receiver({ source: { dynamic: true } });
+ }).bind(this));
+ }
+ connect(options) {
+ return new Promise((function (resolve, reject) {
+ var finishConnecting = function () {
+ this.createSenderReceiver(options)
+ .then(function (results) {
+ resolve(results);
+ }, function (error) {
+ reject(error);
+ });
+ };
+ if (!this.connection) {
+ options.test = false; // if you didn't want a connection, you should have called testConnect() and not connect()
+ this.testConnect(options)
+ .then((function () {
+ finishConnecting.call(this);
+ }).bind(this), (function () {
+ // connect failed or timed out
+ this.errorText = 'Unable to connect';
+ this.executeDisconnectActions(this.errorText);
+ reject(Error(this.errorText));
+ }).bind(this));
+ }
+ else {
+ finishConnecting.call(this);
+ }
+ }).bind(this));
+ }
+ getReceiverAddress() {
+ return this.receiver.remote.attach.source.address;
+ }
+ // Try to connect using the options.
+ // if options.test === true -> close the connection if it succeeded and resolve the promise
+ // if the connection attempt fails or times out, reject the promise regardless of options.test
+ testConnect(options, callback) {
+ return new Promise((function (resolve, reject) {
+ var timeout = options.timeout || 10000;
+ var reconnect = options.reconnect || false; // in case options.reconnect is undefined
+ var baseAddress = options.address + ':' + options.port;
+ if (options.linkRouteAddress) {
+ baseAddress += ('/' + options.linkRouteAddress);
+ }
+ var wsprotocol = location.protocol === 'https:' ? 'wss' : 'ws';
+ if (this.connection) {
+ delete this.connection;
+ this.connection = null;
+ }
+ var ws = rhea.websocket_connect(WebSocket);
+ var c = {
+ connection_details: new ws(wsprotocol + '://' + baseAddress, ['binary']),
+ reconnect: reconnect,
+ properties: options.properties || { console_identifier: 'Dispatch console' }
+ };
+ if (options.hostname)
+ c.hostname = options.hostname;
+ if (options.username && options.username !== '') {
+ c.username = options.username;
+ }
+ if (options.password && options.password !== '') {
+ c.password = options.password;
+ }
+ // set a timeout
+ var disconnected = (function () {
+ clearTimeout(timer);
+ rhea.removeListener('disconnected', disconnected);
+ rhea.removeListener('connection_open', connection_open);
+ this.connection = null;
+ var rej = 'failed to connect';
+ if (callback)
+ callback({ error: rej });
+ reject(Error(rej));
+ }).bind(this);
+ var timer = setTimeout(disconnected, timeout);
+ // the event handler for when the connection opens
+ var connection_open = (function (context) {
+ clearTimeout(timer);
+ // prevent future disconnects from calling reject
+ rhea.removeListener('disconnected', disconnected);
+ // we were just checking. we don't really want a connection
+ if (options.test) {
+ context.connection.close();
+ this.connection = null;
+ }
+ else
+ this.on_connection_open();
+ var res = { context: context };
+ if (callback)
+ callback(res);
+ resolve(res);
+ }).bind(this);
+ // register an event handler for when the connection opens
+ rhea.once('connection_open', connection_open);
+ // register an event handler for if the connection fails to open
+ rhea.once('disconnected', disconnected);
+ // attempt the connection
+ this.connection = rhea.connect(c);
+ }).bind(this));
+ }
+ sendMgmtQuery(operation) {
+ return this.send([], '/$management', operation);
+ }
+ sendQuery(toAddr, entity, attrs, operation) {
+ operation = operation || 'QUERY';
+ var fullAddr = this._fullAddr(toAddr);
+ var body = { attributeNames: attrs || [] };
+ return this.send(body, fullAddr, operation, this.schema.entityTypes[entity].fullyQualifiedType);
+ }
+ send(body, to, operation, entityType) {
+ var application_properties = {
+ operation: operation,
+ type: 'org.amqp.management',
+ name: 'self'
+ };
+ if (entityType)
+ application_properties.entityType = entityType;
+ return this._send(body, to, application_properties);
+ }
+ sendMethod(toAddr, entity, attrs, operation, props) {
+ var fullAddr = this._fullAddr(toAddr);
+ var application_properties = {
+ operation: operation,
+ };
+ if (entity) {
+ application_properties.type = this.schema.entityTypes[entity].fullyQualifiedType;
+ }
+ if (attrs.name)
+ application_properties.name = attrs.name;
+ else if (attrs.identity)
+ application_properties.identity = attrs.identity;
+ if (props) {
+ for (var attrname in props) {
+ application_properties[attrname] = props[attrname];
+ }
+ }
+ return this._send(attrs, fullAddr, application_properties);
+ }
+ _send(body, to, application_properties) {
+ var _correlationId = this.correlator.corr();
+ var self = this;
+ return new Promise(function (resolve, reject) {
+ self.correlator.register(_correlationId, resolve, reject);
+ self.sender.send({
+ body: body,
+ to: to,
+ reply_to: self.receiver.remote.attach.source.address,
+ correlation_id: _correlationId,
+ application_properties: application_properties
+ });
+ });
+ }
+ _fullAddr(toAddr) {
+ var toAddrParts = toAddr.split('/');
+ toAddrParts.shift();
+ var fullAddr = toAddrParts.join('/');
+ return fullAddr;
+ }
+ availableQeueuDepth() {
+ return this.correlator.depth();
+ }
+}
+
+class ConnectionException {
+ constructor(message) {
+ this.message = message;
+ this.name = 'ConnectionException';
+ }
+}
+
+const _ConnectionManager = ConnectionManager;
+export { _ConnectionManager as ConnectionManager };
+const _ConnectionException = ConnectionException;
+export { _ConnectionException as ConnectionException };
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/modules/correlator.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/modules/correlator.js b/console/stand-alone/modules/correlator.js
new file mode 100644
index 0000000..bf34f93
--- /dev/null
+++ b/console/stand-alone/modules/correlator.js
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 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 { utils } from './utilities.js';
+
+class Correlator {
+ constructor() {
+ this._objects = {};
+ this._correlationID = 0;
+ this.maxCorrelatorDepth = 10;
+ }
+ corr() {
+ return ++(this._correlationID) + '';
+ }
+ // Associate this correlation id with the promise's resolve and reject methods
+ register(id, resolve, reject) {
+ this._objects[id] = { resolver: resolve, rejector: reject };
+ }
+ // Call the promise's resolve method.
+ // This is called by rhea's receiver.on('message') function
+ resolve(context) {
+ var correlationID = context.message.correlation_id;
+ // call the promise's resolve function with a copy of the rhea response (so we don't keep any references to internal rhea data)
+ this._objects[correlationID].resolver({ response: utils.copy(context.message.body), context: context });
+ delete this._objects[correlationID];
+ }
+ reject(id, error) {
+ this._objects[id].rejector(error);
+ delete this._objects[id];
+ }
+ // Return the number of requests that can be sent before we start queuing requests
+ depth() {
+ return Math.max(1, this.maxCorrelatorDepth - Object.keys(this._objects).length);
+ }
+}
+
+export default Correlator;
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/modules/management.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/modules/management.js b/console/stand-alone/modules/management.js
new file mode 100644
index 0000000..4b3bb32
--- /dev/null
+++ b/console/stand-alone/modules/management.js
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2015 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 Promise */
+
+import { ConnectionManager } from './connection.js';
+import Topology from './topology.js';
+
+export class Management {
+ constructor(protocol) {
+ this.connection = new ConnectionManager(protocol);
+ this.topology = new Topology(this.connection);
+ }
+ getSchema(callback) {
+ var self = this;
+ return new Promise(function (resolve, reject) {
+ self.connection.sendMgmtQuery('GET-SCHEMA')
+ .then(function (responseAndContext) {
+ var response = responseAndContext.response;
+ for (var entityName in response.entityTypes) {
+ var entity = response.entityTypes[entityName];
+ if (entity.deprecated) {
+ // deprecated entity
+ delete response.entityTypes[entityName];
+ }
+ else {
+ for (var attributeName in entity.attributes) {
+ var attribute = entity.attributes[attributeName];
+ if (attribute.deprecated) {
+ // deprecated attribute
+ delete response.entityTypes[entityName].attributes[attributeName];
+ }
+ }
+ }
+ }
+ self.connection.setSchema(response);
+ if (callback)
+ callback(response);
+ resolve(response);
+ }, function (error) {
+ if (callback)
+ callback(error);
+ reject(error);
+ });
+ });
+ }
+ schema() {
+ return this.connection.schema;
+ }
+}
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/modules/topology.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/modules/topology.js b/console/stand-alone/modules/topology.js
new file mode 100644
index 0000000..e208a6f
--- /dev/null
+++ b/console/stand-alone/modules/topology.js
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2015 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 Promise d3 */
+
+import { utils } from './utilities.js';
+
+class Topology {
+ constructor(connectionManager) {
+ this.connection = connectionManager;
+ this.updatedActions = {};
+ this.entities = []; // which entities to request each topology update
+ this.entityAttribs = {};
+ this._nodeInfo = {}; // info about all known nodes and entities
+ this.filtering = false; // filter out nodes that don't have connection info
+ this.timeout = 5000;
+ this.updateInterval = 5000;
+ this._getTimer = null;
+ this.updating = false;
+ }
+ addUpdatedAction(key, action) {
+ if (typeof action === 'function') {
+ this.updatedActions[key] = action;
+ }
+ }
+ delUpdatedAction(key) {
+ if (key in this.updatedActions)
+ delete this.updatedActions[key];
+ }
+ executeUpdatedActions(error) {
+ for (var action in this.updatedActions) {
+ this.updatedActions[action].apply(this, [error]);
+ }
+ }
+ setUpdateEntities(entities) {
+ this.entities = entities;
+ for (var i = 0; i < entities.length; i++) {
+ this.entityAttribs[entities[i]] = [];
+ }
+ }
+ addUpdateEntities(entityAttribs) {
+ if (Object.prototype.toString.call(entityAttribs) !== '[object Array]') {
+ entityAttribs = [entityAttribs];
+ }
+ for (var i = 0; i < entityAttribs.length; i++) {
+ var entity = entityAttribs[i].entity;
+ this.entityAttribs[entity] = entityAttribs[i].attrs || [];
+ }
+ }
+ on(eventName, fn, key) {
+ if (eventName === 'updated')
+ this.addUpdatedAction(key, fn);
+ }
+ unregister(eventName, key) {
+ if (eventName === 'updated')
+ this.delUpdatedAction(key);
+ }
+ nodeInfo() {
+ return this._nodeInfo;
+ }
+ get() {
+ return new Promise((function (resolve, reject) {
+ this.connection.sendMgmtQuery('GET-MGMT-NODES')
+ .then((function (response) {
+ response = response.response;
+ if (Object.prototype.toString.call(response) === '[object Array]') {
+ var workInfo = {};
+ // if there is only one node, it will not be returned
+ if (response.length === 0) {
+ var parts = this.connection.getReceiverAddress().split('/');
+ parts[parts.length - 1] = '$management';
+ response.push(parts.join('/'));
+ }
+ for (var i = 0; i < response.length; ++i) {
+ workInfo[response[i]] = {};
+ }
+ var gotResponse = function (nodeName, entity, response) {
+ workInfo[nodeName][entity] = response;
+ };
+ var q = d3.queue(this.connection.availableQeueuDepth());
+ for (var id in workInfo) {
+ for (var entity in this.entityAttribs) {
+ q.defer((this.q_fetchNodeInfo).bind(this), id, entity, this.entityAttribs[entity], q, gotResponse);
+ }
+ }
+ q.await((function () {
+ // filter out nodes that have no connection info
+ if (this.filtering) {
+ for (var id in workInfo) {
+ if (!(workInfo[id].connection)) {
+ this.flux = true;
+ delete workInfo[id];
+ }
+ }
+ }
+ this._nodeInfo = utils.copy(workInfo);
+ this.onDone(this._nodeInfo);
+ resolve(this._nodeInfo);
+ }).bind(this));
+ }
+ }).bind(this), function (error) {
+ reject(error);
+ });
+ }).bind(this));
+ }
+ onDone(result) {
+ clearTimeout(this._getTimer);
+ if (this.updating)
+ this._getTimer = setTimeout((this.get).bind(this), this.updateInterval);
+ this.executeUpdatedActions(result);
+ }
+ startUpdating(filter) {
+ this.stopUpdating();
+ this.updating = true;
+ this.filtering = filter;
+ this.get();
+ }
+ stopUpdating() {
+ this.updating = false;
+ if (this._getTimer) {
+ clearTimeout(this._getTimer);
+ this._getTimer = null;
+ }
+ }
+ fetchEntity(node, entity, attrs, callback) {
+ var results = {};
+ var gotResponse = function (nodeName, dotentity, response) {
+ results = response;
+ };
+ var q = d3.queue(this.connection.availableQeueuDepth());
+ q.defer((this.q_fetchNodeInfo).bind(this), node, entity, attrs, q, gotResponse);
+ q.await(function () {
+ callback(node, entity, results);
+ });
+ }
+ // called from d3.queue.defer so the last argument (callback) is supplied by d3
+ q_fetchNodeInfo(nodeId, entity, attrs, q, heartbeat, callback) {
+ this.getNodeInfo(nodeId, entity, attrs, q, function (nodeName, dotentity, response) {
+ heartbeat(nodeName, dotentity, response);
+ callback(null);
+ });
+ }
+ // get all the requested entities/attributes for a single router
+ fetchEntities(node, entityAttribs, doneCallback, resultCallback) {
+ var q = d3.queue(this.connection.availableQeueuDepth());
+ var results = {};
+ if (!resultCallback) {
+ resultCallback = function (nodeName, dotentity, response) {
+ if (!results[nodeName])
+ results[nodeName] = {};
+ results[nodeName][dotentity] = response;
+ };
+ }
+ var gotAResponse = function (nodeName, dotentity, response) {
+ resultCallback(nodeName, dotentity, response);
+ };
+ if (Object.prototype.toString.call(entityAttribs) !== '[object Array]') {
+ entityAttribs = [entityAttribs];
+ }
+ for (var i = 0; i < entityAttribs.length; ++i) {
+ var ea = entityAttribs[i];
+ q.defer((this.q_fetchNodeInfo).bind(this), node, ea.entity, ea.attrs || [], q, gotAResponse);
+ }
+ q.await(function () {
+ doneCallback(results);
+ });
+ }
+ // get all the requested entities for all known routers
+ fetchAllEntities(entityAttribs, doneCallback, resultCallback) {
+ var q = d3.queue(this.connection.availableQeueuDepth());
+ var results = {};
+ if (!resultCallback) {
+ resultCallback = function (nodeName, dotentity, response) {
+ if (!results[nodeName])
+ results[nodeName] = {};
+ results[nodeName][dotentity] = response;
+ };
+ }
+ var gotAResponse = function (nodeName, dotentity, response) {
+ resultCallback(nodeName, dotentity, response);
+ };
+ if (Object.prototype.toString.call(entityAttribs) !== '[object Array]') {
+ entityAttribs = [entityAttribs];
+ }
+ var nodes = Object.keys(this._nodeInfo);
+ for (var n = 0; n < nodes.length; ++n) {
+ for (var i = 0; i < entityAttribs.length; ++i) {
+ var ea = entityAttribs[i];
+ q.defer((this.q_fetchNodeInfo).bind(this), nodes[n], ea.entity, ea.attrs || [], q, gotAResponse);
+ }
+ }
+ q.await(function () {
+ doneCallback(results);
+ });
+ }
+ // enusre all the topology nones have all these entities
+ ensureAllEntities(entityAttribs, callback, extra) {
+ this.ensureEntities(Object.keys(this._nodeInfo), entityAttribs, callback, extra);
+ }
+ // ensure these nodes have all these entities. don't fetch unless forced to
+ ensureEntities(nodes, entityAttribs, callback, extra) {
+ if (Object.prototype.toString.call(entityAttribs) !== '[object Array]') {
+ entityAttribs = [entityAttribs];
+ }
+ if (Object.prototype.toString.call(nodes) !== '[object Array]') {
+ nodes = [nodes];
+ }
+ this.addUpdateEntities(entityAttribs);
+ var q = d3.queue(this.connection.availableQeueuDepth());
+ for (var n = 0; n < nodes.length; ++n) {
+ for (var i = 0; i < entityAttribs.length; ++i) {
+ var ea = entityAttribs[i];
+ // if we don'e already have the entity or we want to force a refresh
+ if (!this._nodeInfo[nodes[n]][ea.entity] || ea.force)
+ q.defer((this.q_ensureNodeInfo).bind(this), nodes[n], ea.entity, ea.attrs || [], q);
+ }
+ }
+ q.await(function () {
+ callback(extra);
+ });
+ }
+ addNodeInfo(id, entity, values) {
+ // save the results in the nodeInfo object
+ if (id) {
+ if (!(id in this._nodeInfo)) {
+ this._nodeInfo[id] = {};
+ }
+ // copy the values to allow garbage collection
+ this._nodeInfo[id][entity] = values;
+ }
+ }
+ isLargeNetwork() {
+ return Object.keys(this._nodeInfo).length >= 12;
+ }
+ getConnForLink(link) {
+ // find the connection for this link
+ var conns = this._nodeInfo[link.nodeId].connection;
+ var connIndex = conns.attributeNames.indexOf('identity');
+ var linkCons = conns.results.filter(function (conn) {
+ return conn[connIndex] === link.connectionId;
+ });
+ return utils.flatten(conns.attributeNames, linkCons[0]);
+ }
+ nodeNameList() {
+ var nl = [];
+ for (var id in this._nodeInfo) {
+ nl.push(utils.nameFromId(id));
+ }
+ return nl.sort();
+ }
+ nodeIdList() {
+ var nl = [];
+ for (var id in this._nodeInfo) {
+ //if (this._nodeInfo['connection'])
+ nl.push(id);
+ }
+ return nl.sort();
+ }
+ nodeList() {
+ var nl = [];
+ for (var id in this._nodeInfo) {
+ nl.push({
+ name: utils.nameFromId(id),
+ id: id
+ });
+ }
+ return nl;
+ }
+ // d3.queue'd function to make a management query for entities/attributes
+ q_ensureNodeInfo(nodeId, entity, attrs, q, callback) {
+ this.getNodeInfo(nodeId, entity, attrs, q, (function (nodeName, dotentity, response) {
+ this.addNodeInfo(nodeName, dotentity, response);
+ callback(null);
+ }).bind(this));
+ return {
+ abort: function () {
+ delete this._nodeInfo[nodeId];
+ }
+ };
+ }
+ getNodeInfo(nodeName, entity, attrs, q, callback) {
+ var timedOut = function (q) {
+ q.abort();
+ };
+ var atimer = setTimeout(timedOut, this.timeout, q);
+ this.connection.sendQuery(nodeName, entity, attrs)
+ .then(function (response) {
+ clearTimeout(atimer);
+ callback(nodeName, entity, response.response);
+ }, function () {
+ q.abort();
+ });
+ }
+ getMultipleNodeInfo(nodeNames, entity, attrs, callback, selectedNodeId, aggregate) {
+ var self = this;
+ if (typeof aggregate === 'undefined')
+ aggregate = true;
+ var responses = {};
+ var gotNodesResult = function (nodeName, dotentity, response) {
+ responses[nodeName] = response;
+ };
+ var q = d3.queue(this.connection.availableQeueuDepth());
+ nodeNames.forEach(function (id) {
+ q.defer((self.q_fetchNodeInfo).bind(self), id, entity, attrs, q, gotNodesResult);
+ });
+ q.await(function () {
+ if (aggregate)
+ self.aggregateNodeInfo(nodeNames, entity, selectedNodeId, responses, callback);
+ else {
+ callback(nodeNames, entity, responses);
+ }
+ });
+ }
+ quiesceLink(nodeId, name) {
+ var attributes = {
+ adminStatus: 'disabled',
+ name: name
+ };
+ return this.connection.sendMethod(nodeId, 'router.link', attributes, 'UPDATE');
+ }
+ aggregateNodeInfo(nodeNames, entity, selectedNodeId, responses, callback) {
+ // aggregate the responses
+ var self = this;
+ var newResponse = {};
+ var thisNode = responses[selectedNodeId];
+ newResponse.attributeNames = thisNode.attributeNames;
+ newResponse.results = thisNode.results;
+ newResponse.aggregates = [];
+ // initialize the aggregates
+ for (var i = 0; i < thisNode.results.length; ++i) {
+ // there is a result for each unique entity found (ie addresses, links, etc.)
+ var result = thisNode.results[i];
+ var vals = [];
+ // there is a val for each attribute in this entity
+ result.forEach(function (val) {
+ vals.push({
+ sum: val,
+ detail: []
+ });
+ });
+ newResponse.aggregates.push(vals);
+ }
+ var nameIndex = thisNode.attributeNames.indexOf('name');
+ var ent = self.connection.schema.entityTypes[entity];
+ var ids = Object.keys(responses);
+ ids.sort();
+ ids.forEach(function (id) {
+ var response = responses[id];
+ var results = response.results;
+ results.forEach(function (result) {
+ // find the matching result in the aggregates
+ var found = newResponse.aggregates.some(function (aggregate) {
+ if (aggregate[nameIndex].sum === result[nameIndex]) {
+ // result and aggregate are now the same record, add the graphable values
+ newResponse.attributeNames.forEach(function (key, i) {
+ if (ent.attributes[key] && ent.attributes[key].graph) {
+ if (id != selectedNodeId)
+ aggregate[i].sum += result[i];
+ }
+ aggregate[i].detail.push({
+ node: utils.nameFromId(id) + ':',
+ val: result[i]
+ });
+ });
+ return true; // stop looping
+ }
+ return false; // continute looking for the aggregate record
+ });
+ if (!found) {
+ // this attribute was not found in the aggregates yet
+ // because it was not in the selectedNodeId's results
+ var vals = [];
+ result.forEach(function (val) {
+ vals.push({
+ sum: val,
+ detail: [{
+ node: utils.nameFromId(id),
+ val: val
+ }]
+ });
+ });
+ newResponse.aggregates.push(vals);
+ }
+ });
+ });
+ callback(nodeNames, entity, newResponse);
+ }
+}
+
+export default Topology;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/modules/utilities.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/modules/utilities.js b/console/stand-alone/modules/utilities.js
new file mode 100644
index 0000000..328da38
--- /dev/null
+++ b/console/stand-alone/modules/utilities.js
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2015 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 */
+var ddd = typeof window === 'undefined' ? require ('d3') : d3;
+
+var utils = {
+ isAConsole: function (properties, connectionId, nodeType, key) {
+ return this.isConsole({
+ properties: properties,
+ connectionId: connectionId,
+ nodeType: nodeType,
+ key: key
+ });
+ },
+ isConsole: function (d) {
+ return (d && d.properties && d.properties.console_identifier === 'Dispatch console');
+ },
+ isArtemis: function (d) {
+ return (d.nodeType === 'route-container' || d.nodeType === 'on-demand') && (d.properties && d.properties.product === 'apache-activemq-artemis');
+ },
+
+ isQpid: function (d) {
+ return (d.nodeType === 'route-container' || d.nodeType === 'on-demand') && (d.properties && d.properties.product === 'qpid-cpp');
+ },
+ flatten: function (attributes, result) {
+ if (!attributes || !result)
+ return {};
+ var flat = {};
+ attributes.forEach(function(attr, i) {
+ if (result && result.length > i)
+ flat[attr] = result[i];
+ });
+ return flat;
+ },
+ copy: function (obj) {
+ if (obj)
+ return JSON.parse(JSON.stringify(obj));
+ },
+ identity_clean: function (identity) {
+ if (!identity)
+ return '-';
+ var pos = identity.indexOf('/');
+ if (pos >= 0)
+ return identity.substring(pos + 1);
+ return identity;
+ },
+ addr_text: function (addr) {
+ if (!addr)
+ return '-';
+ if (addr[0] == 'M')
+ return addr.substring(2);
+ else
+ return addr.substring(1);
+ },
+ addr_class: function (addr) {
+ if (!addr) return '-';
+ if (addr[0] == 'M') return 'mobile';
+ if (addr[0] == 'R') return 'router';
+ if (addr[0] == 'A') return 'area';
+ if (addr[0] == 'L') return 'local';
+ if (addr[0] == 'C') return 'link-incoming';
+ if (addr[0] == 'E') return 'link-incoming';
+ if (addr[0] == 'D') return 'link-outgoing';
+ if (addr[0] == 'F') return 'link-outgoing';
+ if (addr[0] == 'T') return 'topo';
+ return 'unknown: ' + addr[0];
+ },
+ humanify: function (s) {
+ if (!s || s.length === 0)
+ return s;
+ var t = s.charAt(0).toUpperCase() + s.substr(1).replace(/[A-Z]/g, ' $&');
+ return t.replace('.', ' ');
+ },
+ pretty: function (v) {
+ var formatComma = ddd.format(',');
+ if (!isNaN(parseFloat(v)) && isFinite(v))
+ return formatComma(v);
+ return v;
+ },
+ isMSIE: function () {
+ return (document.documentMode || /Edge/.test(navigator.userAgent));
+ },
+ valFor: function (aAr, vAr, key) {
+ var idx = aAr.indexOf(key);
+ if ((idx > -1) && (idx < vAr.length)) {
+ return vAr[idx];
+ }
+ return null;
+ },
+ // extract the name of the router from the router id
+ nameFromId: function (id) {
+ // the router id looks like 'amqp:/topo/0/routerName/$managemrnt'
+ var parts = id.split('/');
+ // handle cases where the router name contains a /
+ parts.splice(0, 3); // remove amqp, topo, 0
+ parts.pop(); // remove $management
+ return parts.join('/');
+ }
+
+};
+export { utils };
\ 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
[6/8] qpid-dispatch git commit: DISPATCH-1049 Add console tests
Posted by ea...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/package.json
----------------------------------------------------------------------
diff --git a/console/stand-alone/package.json b/console/stand-alone/package.json
index aa59f68..aad443e 100644
--- a/console/stand-alone/package.json
+++ b/console/stand-alone/package.json
@@ -4,7 +4,8 @@
"description": "Qpid Dispatch Router stand-alone console",
"main": "index.html",
"scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
+ "test": "mocha --require babel-core/register",
+ "tsc": "tsc"
},
"repository": {
"type": "git",
@@ -31,15 +32,13 @@
"angular-ui-bootstrap": "^2.5.6",
"angular-ui-grid": "^4.0.8",
"angular-ui-slider": "^0.4.0",
- "bluebird": "^3.5.1",
+ "babel-polyfill": "^6.26.0",
"bootstrap": "^3.3.7",
"c3": "^0.4.18",
"d3": "^3.5.14",
"d3-path": "^1.0.5",
"d3-queue": "^3.0.7",
"d3-time-format": "^2.1.1",
- "dispatch-management": "~0.1.21",
- "html5shiv": "^3.7.3",
"jquery": "^3.2.1",
"jquery-ui-dist": "^1.12.1",
"jquery.fancytree": "^2.26.0",
@@ -48,23 +47,38 @@
"rhea": "^0.2.13"
},
"devDependencies": {
+ "@types/mocha": "^5.2.2",
+ "@types/node": "^10.3.3",
"babel-core": "^6.26.3",
"babel-preset-env": "^1.7.0",
+ "babel-preset-es2015": "^6.24.1",
+ "browserify": "^16.2.2",
+ "chai": "^4.1.2",
"del": "^3.0.0",
"fs": "0.0.1-security",
+ "glob": "^7.1.2",
"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-if": "^2.0.2",
"gulp-insert": "^0.5.0",
+ "gulp-mocha": "^6.0.0",
"gulp-ng-annotate": "^2.1.0",
"gulp-rename": "^1.2.3",
"gulp-sourcemaps": "^2.6.4",
+ "gulp-terser": "^1.0.1",
"gulp-tslint": "^8.1.3",
"gulp-typescript": "^4.0.2",
"gulp-uglify": "^3.0.0",
+ "gulp-uglifyes": "^0.1.3",
+ "mocha": "^5.2.0",
+ "rollup-stream": "^1.24.1",
+ "through2": "^2.0.3",
"tslint": "^5.10.0",
- "typescript": "^2.9.1"
+ "typescript": "^2.9.1",
+ "vinyl-buffer": "^1.0.1",
+ "vinyl-source-stream": "^2.0.0"
}
}
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/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 38495ad..f76d421 100644
--- a/console/stand-alone/plugin/css/dispatch.css
+++ b/console/stand-alone/plugin/css/dispatch.css
@@ -975,10 +975,10 @@ i.red {
}
.qdrListActions div.delete {
- width: 20em;
+ width: 30em;
margin: auto;
border: 1px solid #eaeaea;
- height: 5em;
+ /* height: 5em; */
padding: 4em;
background-color: #fcfcfc;
}
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/html/qdrList.html
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/html/qdrList.html b/console/stand-alone/plugin/html/qdrList.html
index 64aa091..06faa43 100644
--- a/console/stand-alone/plugin/html/qdrList.html
+++ b/console/stand-alone/plugin/html/qdrList.html
@@ -18,16 +18,8 @@ under the License.
-->
<style>
-@media (min-width: 768px) {
- .list-grid {
+.list-grid {
padding-left: 300px;
- }
-}
-@media (max-width: 768px) {
- .list-grid {
- padding-left: 0;
- }
-
}
span.fancytree-icon {
@@ -51,11 +43,21 @@ span.fancytree-icon {
height: 25px;
width: 300px;
background-color: #333333;
+ border: 0;
}
#list-controller div.list-grid select {
background-color: white;
}
+
+.list-grid {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: calc(100% - 40px);
+ margin-left: 20px;
+}
+
</style>
<div id="list-controller" ng-controller="QDR.ListController">
@@ -71,23 +73,23 @@ span.fancytree-icon {
</div>
</div>
- <div class="list-grid col-xs-12">
+ <div class="list-grid">
<div class="row-fluid qdrListActions">
<ul class="nav nav-tabs">
<li ng-repeat="mode in modes" ng-show="isValid(mode)" ng-click="selectMode(mode)" ng-class="{active : isModeSelected(mode)}" title="{{mode.title}}" ng-bind-html="mode.content"> </li>
</ul>
- <h4>{{selectedRecordName}}</h4>
+ <h4>{{selectedRecordName | to_trusted}}</h4>
<div ng-show="currentMode.id === 'attributes'" class="selectedItems">
- <div ng-show="selectedRecordName === selectedEntity" class="no-content">There are no {{selectedEntity | safePlural}}</div>
+ <div ng-show="selectedRecordName === selectedEntity" class="no-content">There are no {{selectedEntity | safePlural | to_trusted}}</div>
<div id='details-grid' ng-hide="selectedRecordName === selectedEntity" ui-grid="details" ui-grid-resize-columns ui-grid-save-state
ui-grid-auto-resize ng-style="getTableHeight()"></div>
</div>
<div ng-show="currentMode.id === 'delete'">
<div class="delete" ng-show="selectedRecordName !== selectedEntity">
- <button class="btn btn-primary" ng-click="remove()">Delete</button> {{selectedRecordName}}
+ <button class="btn btn-primary" ng-click="remove()">Delete</button> {{selectedRecordName | to_trusted}}
</div>
<div ng-hide="selectedRecordName !== selectedEntity">
- There are no {{selectedEntity | safePlural}}
+ There are no {{selectedEntity | safePlural | to_trusted}}
</div>
</div>
<div class="operations" ng-show="currentMode.id === 'operations'">
@@ -98,7 +100,7 @@ span.fancytree-icon {
<th>Value</th>
</tr>
<tr title="{{attribute.title}}" ng-repeat="attribute in detailFields">
- <td><label for="{{attribute.name}}">{{attribute.name | humanify}}</label></td>
+ <td><label for="{{attribute.name}}">{{attribute.name | humanify | to_trusted}}</label></td>
<!-- we can't do <input type="{angular expression}"> because... jquery throws an exception because... -->
<td>
<div ng-if="attribute.input == 'input'">
@@ -106,7 +108,7 @@ span.fancytree-icon {
<div ng-if="attribute.type == 'number'"><input type="number" name="{{attribute.name}}" id="{{attribute.name}}" ng-model="attribute.rawValue" ng-required="attribute.required" ng-class="{required: attribute.required, unique: attribute.unique}" class="ui-widget-content ui-corner-all"/><span ng-if="attribute.required" title="required" class="required-indicator"></span><span ng-if="attribute.unique" title="unique" class="unique-indicator"></span></div>
<div ng-if="attribute.type == 'text'"><input type="text" name="{{attribute.name}}" id="{{attribute.name}}" ng-model="attribute.attributeValue" ng-required="attribute.required" ng-class="{required: attribute.required, unique: attribute.unique}" class="ui-widget-content ui-corner-all"/><span ng-if="attribute.required" title="required" class="required-indicator"></span><span ng-if="attribute.unique" title="unique" class="unique-indicator"></span></div>
<div ng-if="attribute.type == 'textarea'"><textarea name="{{attribute.name}}" id="{{attribute.name}}" ng-model="attribute.attributeValue" ng-required="attribute.required" ng-class="{required: attribute.required, unique: attribute.unique}" class="ui-widget-content ui-corner-all"></textarea><span ng-if="attribute.required" title="required" class="required-indicator"></span><span ng-if="attribute.unique" title="unique" class="unique-indicator"></span></div>
- <span ng-if="attribute.type == 'disabled'" >{{getAttributeValue(attribute)}}</span>
+ <span ng-if="attribute.type == 'disabled'" >{{getAttributeValue(attribute) | to_trusted}}</span>
</div>
<div ng-if="attribute.input == 'select'">
<select id="{{attribute.name}}" ng-model="attribute.selected" ng-required="attribute.required" ng-class="{required: attribute.required, unique: attribute.unique}" ng-options="item for item in attribute.rawtype track by item"></select>
@@ -118,7 +120,7 @@ span.fancytree-icon {
</div>
</td>
</tr>
- <tr><td></td><td><button class="btn btn-primary" type="button" ng-click="ok()">{{operation | Pascalcase}}</button></td></tr>
+ <tr><td></td><td><button class="btn btn-primary" type="button" ng-click="ok()">{{operation | Pascalcase | to_trusted}}</button></td></tr>
</table>
</fieldset>
</div>
@@ -139,10 +141,23 @@ span.fancytree-icon {
</tr>
</table>
</div>
- <div ng-if="logResults.length == 0 && !fetchingLog">No log entries for {{selectedRecordName}}</div>
- <div ng-if="fetchingLog">Fetching logs for {{selectedRecordName}}</div>
+ <div ng-if="logResults.length == 0 && !fetchingLog">No log entries for {{selectedRecordName | to_trusted}}</div>
+ <div ng-if="fetchingLog">Fetching logs for {{selectedRecordName | to_trusted}}</div>
</div>
</div>
</div>
</div>
+<style>
+ @media (min-width: 768px) {
+ .list-grid {
+ padding-left: 300px;
+ }
+ }
+ @media (max-width: 768px) {
+ .list-grid {
+ padding-left: 0;
+ }
+
+ }
+</style>
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/html/tmplChartConfig.html
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/html/tmplChartConfig.html b/console/stand-alone/plugin/html/tmplChartConfig.html
index 4c9c727..efd474e 100644
--- a/console/stand-alone/plugin/html/tmplChartConfig.html
+++ b/console/stand-alone/plugin/html/tmplChartConfig.html
@@ -31,7 +31,7 @@
<uib-tabset>
<uib-tab heading="Type">
<legend>Chart type</legend>
- <div>
+ <div class="clearfix">
<label><input type="radio" ng-model="dialogChart.type" value="value" /> Value Chart</label>
<label><input type="radio" ng-model="dialogChart.type" value="rate" /> Rate Chart</label>
<!--
@@ -41,19 +41,15 @@
</div>
-->
</div>
- <div style="clear:both;"> </div>
</uib-tab>
-<!--
- <uib-tab ng-hide="$parent.chart.aggregate()" heading="Colors">
+ <uib-tab ng-hide="chart.aggregate()" heading="Colors">
<legend>Chart colors</legend>
- <div>
+ <div class="clearfix">
<div class="colorPicker">
- <label>Area: <input id="areaColor" name="areaColor" type="color" /></label>
+ <label>Area: <input id="areaColor" name="areaColor" type="color" ng-model="dialogChart.areaColor"/></label>
</div>
</div>
- <div style="clear:both;"> </div>
</uib-tab>
--->
<uib-tab heading="Duration">
<legend>Chart duration</legend>
<div class="clearfix">
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/chord/data.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/chord/data.js b/console/stand-alone/plugin/js/chord/data.js
index 7f28a9c..45e68a7 100644
--- a/console/stand-alone/plugin/js/chord/data.js
+++ b/console/stand-alone/plugin/js/chord/data.js
@@ -17,136 +17,132 @@ specific language governing permissions and limitations
under the License.
*/
-'use strict';
-/* global angular Promise MIN_CHORD_THRESHOLD */
+/* global angular Promise */
+import { MIN_CHORD_THRESHOLD } from './matrix.js';
const SAMPLES = 3; // number of snapshots to use for rate calculations
-function ChordData (QDRService, isRate, converter) {
- this.QDRService = QDRService;
- this.last_matrix = undefined;
- this.last_values = {values: undefined, timestamp: undefined};
- this.rateValues = undefined;
- this.snapshots = []; // last N values used for calculating rate
- this.isRate = isRate;
- // fn to convert raw data to matrix
- this.converter = converter;
- // object that determines which addresses are excluded
- this.filter = [];
-}
-ChordData.prototype.setRate = function (isRate) {
- this.isRate = isRate;
-};
-ChordData.prototype.setConverter = function (converter) {
- this.converter = converter;
-};
-ChordData.prototype.setFilter = function (filter) {
- this.filter = filter;
-};
-ChordData.prototype.getAddresses = function () {
- let addresses = {};
- let outer = this.snapshots;
- if (outer.length === 0)
- outer = outer = [this.last_values];
- outer.forEach( function (snap) {
- snap.values.forEach( function (lv) {
- if (!(lv.address in addresses)) {
- addresses[lv.address] = this.filter.indexOf(lv.address) < 0;
- }
+class ChordData { // eslint-disable-line no-unused-vars
+ constructor(QDRService, isRate, converter) {
+ this.QDRService = QDRService;
+ this.last_matrix = undefined;
+ this.last_values = { values: undefined, timestamp: undefined };
+ this.rateValues = undefined;
+ this.snapshots = []; // last N values used for calculating rate
+ this.isRate = isRate;
+ // fn to convert raw data to matrix
+ this.converter = converter;
+ // object that determines which addresses are excluded
+ this.filter = [];
+ }
+ setRate(isRate) {
+ this.isRate = isRate;
+ }
+ setConverter(converter) {
+ this.converter = converter;
+ }
+ setFilter(filter) {
+ this.filter = filter;
+ }
+ getAddresses() {
+ let addresses = {};
+ let outer = this.snapshots;
+ if (outer.length === 0)
+ outer = outer = [this.last_values];
+ outer.forEach(function (snap) {
+ snap.values.forEach(function (lv) {
+ if (!(lv.address in addresses)) {
+ addresses[lv.address] = this.filter.indexOf(lv.address) < 0;
+ }
+ }, this);
}, this);
- }, this);
- return addresses;
-};
-ChordData.prototype.getRouters = function () {
- let routers = {};
- let outer = this.snapshots;
- if (outer.length === 0)
- outer = [this.last_values];
- outer.forEach( function (snap) {
- snap.values.forEach( function (lv) {
- routers[lv.egress] = true;
- routers[lv.ingress] = true;
+ return addresses;
+ }
+ getRouters() {
+ let routers = {};
+ let outer = this.snapshots;
+ if (outer.length === 0)
+ outer = [this.last_values];
+ outer.forEach(function (snap) {
+ snap.values.forEach(function (lv) {
+ routers[lv.egress] = true;
+ routers[lv.ingress] = true;
+ });
});
- });
- return Object.keys(routers).sort();
-};
-
-ChordData.prototype.applyFilter = function (filter) {
- if (filter)
- this.setFilter(filter);
-
- return new Promise( (function (resolve) {
- resolve(convert(this, this.last_values));
- }));
-};
-
-// construct a square matrix of the number of messages each router has egressed from each router
-ChordData.prototype.getMatrix = function () {
- let self = this;
- return new Promise( (function (resolve, reject) {
- // get the router.node and router.link info
- self.QDRService.management.topology.fetchAllEntities([
- {entity: 'router.node', attrs: ['id', 'index']},
- {entity: 'router.link', attrs: ['linkType', 'linkDir', 'owningAddr', 'ingressHistogram']}],
- function(results) {
- if (!results) {
- reject(Error('unable to fetch entities'));
- return;
- }
- // the raw data received from the rouers
- let values = [];
-
- // for each router in the network
- for (let nodeId in results) {
- // get a map of router ids to index into ingressHistogram for the links for this router.
- // each routers has a different order for the routers
- let ingressRouters = [];
- let routerNode = results[nodeId]['router.node'];
- let idIndex = routerNode.attributeNames.indexOf('id');
-
- // ingressRouters is an array of router names in the same order that the ingressHistogram values will be in
- for (let i=0; i<routerNode.results.length; i++) {
- ingressRouters.push(routerNode.results[i][idIndex]);
+ return Object.keys(routers).sort();
+ }
+ applyFilter(filter) {
+ if (filter)
+ this.setFilter(filter);
+ return new Promise((function (resolve) {
+ resolve(convert(this, this.last_values));
+ }));
+ }
+ // construct a square matrix of the number of messages each router has egressed from each router
+ getMatrix() {
+ let self = this;
+ return new Promise((function (resolve, reject) {
+ // get the router.node and router.link info
+ self.QDRService.management.topology.fetchAllEntities([
+ { entity: 'router.node', attrs: ['id', 'index'] },
+ { entity: 'router.link', attrs: ['linkType', 'linkDir', 'owningAddr', 'ingressHistogram'] }
+ ], function (results) {
+ if (!results) {
+ reject(Error('unable to fetch entities'));
+ return;
}
-
- // the name of the router we are working on
- let egressRouter = self.QDRService.management.topology.nameFromId(nodeId);
-
- // loop through the router links for this router looking for out/endpoint/non-console links
- let routerLinks = results[nodeId]['router.link'];
- for (let i=0; i<routerLinks.results.length; i++) {
- let link = self.QDRService.utilities.flatten(routerLinks.attributeNames, routerLinks.results[i]);
- // if the link is an outbound/enpoint/non console
- if (link.linkType === 'endpoint' && link.linkDir === 'out' && !link.owningAddr.startsWith('Ltemp.')) {
- // keep track of the raw egress values as well as their ingress and egress routers and the address
- for (let j=0; j<ingressRouters.length; j++) {
- let messages = link.ingressHistogram[j];
- if (messages) {
- values.push({ingress: ingressRouters[j],
- egress: egressRouter,
- address: self.QDRService.utilities.addr_text(link.owningAddr),
- messages: messages});
+ // the raw data received from the rouers
+ let values = [];
+ // for each router in the network
+ for (let nodeId in results) {
+ // get a map of router ids to index into ingressHistogram for the links for this router.
+ // each routers has a different order for the routers
+ let ingressRouters = [];
+ let routerNode = results[nodeId]['router.node'];
+ let idIndex = routerNode.attributeNames.indexOf('id');
+ // ingressRouters is an array of router names in the same order that the ingressHistogram values will be in
+ for (let i = 0; i < routerNode.results.length; i++) {
+ ingressRouters.push(routerNode.results[i][idIndex]);
+ }
+ // the name of the router we are working on
+ let egressRouter = self.QDRService.utilities.nameFromId(nodeId);
+ // loop through the router links for this router looking for out/endpoint/non-console links
+ let routerLinks = results[nodeId]['router.link'];
+ for (let i = 0; i < routerLinks.results.length; i++) {
+ let link = self.QDRService.utilities.flatten(routerLinks.attributeNames, routerLinks.results[i]);
+ // if the link is an outbound/enpoint/non console
+ if (link.linkType === 'endpoint' && link.linkDir === 'out' && !link.owningAddr.startsWith('Ltemp.')) {
+ // keep track of the raw egress values as well as their ingress and egress routers and the address
+ for (let j = 0; j < ingressRouters.length; j++) {
+ let messages = link.ingressHistogram[j];
+ if (messages) {
+ values.push({
+ ingress: ingressRouters[j],
+ egress: egressRouter,
+ address: self.QDRService.utilities.addr_text(link.owningAddr),
+ messages: messages
+ });
+ }
}
}
}
}
- }
- // values is an array of objects like [{ingress: 'xxx', egress: 'xxx', address: 'xxx', messages: ###}, ....]
-
- // convert the raw values array into a matrix object
- let matrix = convert(self, values);
+ // values is an array of objects like [{ingress: 'xxx', egress: 'xxx', address: 'xxx', messages: ###}, ....]
+ // convert the raw values array into a matrix object
+ let matrix = convert(self, values);
+ // resolve the promise
+ resolve(matrix);
+ });
+ }));
+ }
+ convertUsing(converter) {
+ let values = this.isRate ? this.rateValues : this.last_values.values;
+ // convert the values to a matrix using the requested converter and the current filter
+ return converter(values, this.filter);
+ }
+}
- // resolve the promise
- resolve(matrix);
- });
- }));
-};
-ChordData.prototype.convertUsing = function (converter) {
- let values = this.isRate ? this.rateValues : this.last_values.values;
- // convert the values to a matrix using the requested converter and the current filter
- return converter(values, this.filter);
-};
// Private functions
@@ -226,3 +222,5 @@ let convert = function (self, values) {
return matrix;
};
+
+export { ChordData };
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/chord/filters.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/chord/filters.js b/console/stand-alone/plugin/js/chord/filters.js
index 7bb68cc..d184221 100644
--- a/console/stand-alone/plugin/js/chord/filters.js
+++ b/console/stand-alone/plugin/js/chord/filters.js
@@ -16,11 +16,10 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
-'use strict';
-/* global valuesMatrix */
+import { valuesMatrix } from './matrix.js';
// this filter will show an arc per router with the addresses aggregated
-var aggregateAddresses = function (values, filter) { // eslint-disable-line no-unused-vars
+export const aggregateAddresses = function (values, filter) {
let m = new valuesMatrix(true);
values.forEach (function (value) {
if (filter.indexOf(value.address) < 0) {
@@ -40,53 +39,8 @@ var aggregateAddresses = function (values, filter) { // eslint-disable-line no-u
return m.sorted();
};
-// this filter will show an arc per router-address
-var _separateAddresses = function (values, filter) { // eslint-disable-line no-unused-vars
- let m = new valuesMatrix(false);
- values = values.filter( function (v) { return filter.indexOf(v.address) < 0;});
- if (values.length === 0)
- return m;
-
- let addresses = {}, routers = {};
- // get the list of routers and addresses in the data
- values.forEach( function (value) {
- addresses[value.address] = true;
- routers[value.ingress] = true;
- routers[value.egress] = true;
- });
- let saddresses = Object.keys(addresses).sort();
- let srouters = Object.keys(routers).sort();
- let alen = saddresses.length;
- // sanity check
- if (alen === 0)
- return m;
-
- /* Convert the data to a matrix */
-
- // initialize the matrix to have the correct ingress, egress, and address in each row and col
- m.zeroInit(saddresses.length * srouters.length);
- m.rows.forEach( function (row, r) {
- let egress = srouters[Math.floor(r/alen)];
- row.cols.forEach( function (col, c) {
- let ingress = srouters[Math.floor(c/alen)];
- let address = saddresses[c % alen];
- m.setRowCol(r, c, ingress, egress, address, 0);
- });
- });
- // set the values at each cell in the matrix
- for (let i=0, alen=saddresses.length, vlen=values.length; i<vlen; i++) {
- let value = values[i];
- let egressIndex = srouters.indexOf(value.egress);
- let ingressIndex = srouters.indexOf(value.ingress);
- let addressIndex = saddresses.indexOf(value.address);
- let row = egressIndex * alen + addressIndex;
- let col = ingressIndex * alen + addressIndex;
- m.setColMessages(row, col, value.messages);
- }
- return m;
-};
-let separateAddresses = function (values, filter) { // eslint-disable-line no-unused-vars
+export const separateAddresses = function (values, filter) {
let m = new valuesMatrix(false);
values.forEach( function (value) {
if (filter.indexOf(value.address) < 0) {
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/chord/layout/layout.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/chord/layout/layout.js b/console/stand-alone/plugin/js/chord/layout/layout.js
index e3d223f..907e9a8 100644
--- a/console/stand-alone/plugin/js/chord/layout/layout.js
+++ b/console/stand-alone/plugin/js/chord/layout/layout.js
@@ -16,11 +16,9 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
-'use strict';
/* global d3 */
-
-var qrdlayoutChord = function() { // eslint-disable-line no-unused-vars
+var qdrlayoutChord = function() { // eslint-disable-line no-unused-vars
var chord = {}, chords, groups, matrix, n, padding = 0, Ï„ = Math.PI*2, groupBy;
function relayout() {
groupBy = groupBy || d3.range(n);
@@ -145,3 +143,5 @@ let unique = function (arr) {
}
return Object.keys(counts).length;
};
+
+export { qdrlayoutChord };
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/chord/matrix.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/chord/matrix.js b/console/stand-alone/plugin/js/chord/matrix.js
index 3f10ddc..156deb3 100644
--- a/console/stand-alone/plugin/js/chord/matrix.js
+++ b/console/stand-alone/plugin/js/chord/matrix.js
@@ -16,7 +16,6 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
-'use strict';
/* global d3 */
const MIN_CHORD_THRESHOLD = 0.01;
@@ -212,3 +211,5 @@ let emptyMatrix = function (size) {
}
return matrix;
};
+
+export { MIN_CHORD_THRESHOLD, valuesMatrix };
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/chord/qdrChord.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/chord/qdrChord.js b/console/stand-alone/plugin/js/chord/qdrChord.js
index 91ee96a..edb1b3a 100644
--- a/console/stand-alone/plugin/js/chord/qdrChord.js
+++ b/console/stand-alone/plugin/js/chord/qdrChord.js
@@ -16,16 +16,21 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
-'use strict';
-/* global angular d3 separateAddresses aggregateAddresses ChordData qdrRibbon qrdlayoutChord */
+/* global angular d3 */
-var QDR = (function (QDR) {
- QDR.module.controller('QDR.ChordController', ['$scope', 'QDRService', '$location', '$timeout', '$sce', function($scope, QDRService, $location, $timeout, $sce) {
+import { QDRRedirectWhenConnected } from '../qdrGlobals.js';
+import { separateAddresses, aggregateAddresses } from './filters.js';
+import { ChordData } from './data.js';
+import { qdrRibbon } from './ribbon/ribbon.js';
+import { qdrlayoutChord } from './layout/layout.js';
+export class ChordController {
+ constructor(QDRService, $scope, $location, $timeout, $sce) {
+ this.controllerName = 'QDR.ChordController';
// if we get here and there is no connection, redirect to the connect page and then
// return here once we are connected
if (!QDRService.management.connection.is_connected()) {
- QDR.redirectWhenConnected($location, 'chord');
+ QDRRedirectWhenConnected($location, 'chord');
return;
}
@@ -441,7 +446,7 @@ var QDR = (function (QDR) {
// create a new chord layout so we can animate between the last one and this one
let groupBy = matrix.getGroupBy();
- let rechord = qrdlayoutChord().padding(ARCPADDING).groupBy(groupBy).matrix(matrixMessages);
+ let rechord = qdrlayoutChord().padding(ARCPADDING).groupBy(groupBy).matrix(matrixMessages);
// The chord layout has a function named .groups() that returns the
// data for the arcs. We decorate this data with a unique key.
@@ -831,8 +836,7 @@ var QDR = (function (QDR) {
});
}
let interval = setInterval(doUpdate, transitionDuration);
-
- }]);
- return QDR;
-} (QDR || {}));
+ }
+}
+ChordController.$inject = ['QDRService', '$scope', '$location', '$timeout', '$sce'];
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/chord/ribbon/ribbon.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/chord/ribbon/ribbon.js b/console/stand-alone/plugin/js/chord/ribbon/ribbon.js
index 604fa1e..fb6996f 100644
--- a/console/stand-alone/plugin/js/chord/ribbon/ribbon.js
+++ b/console/stand-alone/plugin/js/chord/ribbon/ribbon.js
@@ -16,9 +16,7 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
-'use strict';
/* global d3 */
-
const halfPI = Math.PI / 2.0;
const twoPI = Math.PI * 2.0;
@@ -163,3 +161,5 @@ let cpRatio = function (gap, x, y) {
let distScale = d3.scale.linear().domain([0, top/8, top/2, top]).range([0, .3, .4, .5]);
return distScale(dist);
};
+
+export { qdrRibbon };
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/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
deleted file mode 100644
index 47c8a60..0000000
--- a/console/stand-alone/plugin/js/dispatchPlugin.js
+++ /dev/null
@@ -1,264 +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 angular d3 */
-
-/**
- * @module QDR
- * @main QDR
- *
- * The main entry point for the QDR module
- *
- */
-var QDR = (function(QDR) {
-
- /**
- * @property pluginName
- * @type {string}
- *
- * The name of this plugin
- */
- QDR.pluginName = 'QDR';
- QDR.pluginRoot = '';
- QDR.isStandalone = true;
-
- /**
- * @property templatePath
- * @type {string}
- *
- * The top level path to this plugin's partials
- */
- QDR.templatePath = 'html/';
- /**
- * @property SETTINGS_KEY
- * @type {string}
- *
- * The key used to fetch our settings from local storage
- */
- QDR.SETTINGS_KEY = 'QDRSettings';
- QDR.LAST_LOCATION = 'QDRLastLocation';
-
- QDR.redirectWhenConnected = function ($location, org) {
- $location.path(QDR.pluginRoot + '/connect');
- $location.search('org', org);
- };
-
- /**
- * @property module
- * @type {object}
- *
- * This plugin's angularjs module instance
- */
- QDR.module = angular.module(QDR.pluginName, ['ngRoute', 'ngSanitize', 'ngResource', 'ui.bootstrap',
- 'ui.grid', 'ui.grid.selection', 'ui.grid.autoResize', 'ui.grid.resizeColumns', 'ui.grid.saveState', 'ui.slider', 'ui.checkbox']);
-
- // set up the routing for this plugin
- QDR.module.config(function($routeProvider) {
- $routeProvider
- .when('/', {
- templateUrl: QDR.templatePath + 'qdrOverview.html'
- })
- .when('/overview', {
- templateUrl: QDR.templatePath + 'qdrOverview.html'
- })
- .when('/topology', {
- templateUrl: QDR.templatePath + 'qdrTopology.html'
- })
- .when('/list', {
- templateUrl: QDR.templatePath + 'qdrList.html'
- })
- .when('/schema', {
- templateUrl: QDR.templatePath + 'qdrSchema.html'
- })
- .when('/charts', {
- templateUrl: QDR.templatePath + 'qdrCharts.html'
- })
- .when('/chord', {
- templateUrl: QDR.templatePath + 'qdrChord.html'
- })
- .when('/connect', {
- templateUrl: QDR.templatePath + 'qdrConnect.html'
- });
- });
-
- QDR.module.config(function ($compileProvider) {
- $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrome-extension|file|blob):/);
- $compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrome-extension):/);
- /* var cur = $compileProvider.urlSanitizationWhitelist();
- $compileProvider.urlSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|blob):/);
- cur = $compileProvider.urlSanitizationWhitelist();
-*/
- });
-
- QDR.module.filter('to_trusted', ['$sce', function($sce){
- return function(text) {
- return $sce.trustAsHtml(text);
- };
- }]);
-
- QDR.module.filter('humanify', function (QDRService) {
- return function (input) {
- return QDRService.utilities.humanify(input);
- };
- });
-
- QDR.module.filter('Pascalcase', function () {
- return function (str) {
- if (!str)
- return '';
- return str.replace(/(\w)(\w*)/g,
- function(g0,g1,g2){return g1.toUpperCase() + g2.toLowerCase();});
- };
- });
-
- QDR.module.filter('safePlural', function () {
- return function (str) {
- var es = ['x', 'ch', 'ss', 'sh'];
- for (var i=0; i<es.length; ++i) {
- if (str.endsWith(es[i]))
- return str + 'es';
- }
- if (str.endsWith('y'))
- return str.substr(0, str.length-2) + 'ies';
- if (str.endsWith('s'))
- return str;
- return str + 's';
- };
- });
-
- QDR.module.filter('pretty', function () {
- return function (str) {
- var formatComma = d3.format(',');
- if (!isNaN(parseFloat(str)) && isFinite(str))
- return formatComma(str);
- return str;
- };
- });
-
- QDR.logger = function ($log) {
- var log = $log;
-
- this.debug = function (msg) { msg = 'QDR: ' + msg; log.debug(msg);};
- this.error = function (msg) {msg = 'QDR: ' + msg; log.error(msg);};
- this.info = function (msg) {msg = 'QDR: ' + msg; log.info(msg);};
- this.warn = function (msg) {msg = 'QDR: ' + msg; log.warn(msg);};
-
- return this;
- };
- // one-time initialization happens in the run function
- // of our module
- QDR.module.run( ['$rootScope', '$route', '$timeout', '$location', '$log', 'QDRService', 'QDRChartService', function ($rootScope, $route, $timeout, $location, $log, QDRService, QDRChartService) {
- QDR.log = new QDR.logger($log);
- QDR.log.info('************* creating Dispatch Console ************');
- var curPath = $location.path();
- var org = curPath.substr(1);
- if (org && org.length > 0 && org !== 'connect') {
- $location.search('org', org);
- } else {
- $location.search('org', null);
- }
- QDR.queue = d3.queue;
-
- if (!QDRService.management.connection.is_connected()) {
- // attempt to connect to the host:port that served this page
- var host = $location.host();
- var port = $location.port();
- var search = $location.search();
- if (search.org) {
- if (search.org === 'connect')
- $location.search('org', 'overview');
- }
- var connectOptions = {address: host, port: port};
- QDR.log.info('Attempting AMQP over websockets connection using address:port of browser ('+host+':'+port+')');
- QDRService.management.connection.testConnect(connectOptions)
- .then( function () {
- // We didn't connect with reconnect: true flag.
- // The reason being that if we used reconnect:true and the connection failed, rhea would keep trying. There
- // doesn't appear to be a way to tell it to stop trying to reconnect.
- QDRService.disconnect();
- QDR.log.info('Connect succeeded. Using address:port of browser');
- connectOptions.reconnect = true;
- // complete the connection (create the sender/receiver)
- QDRService.connect(connectOptions)
- .then( function () {
- // register a callback for when the node list is available (needed for loading saved charts)
- QDRService.management.topology.addUpdatedAction('initChartService', function() {
- QDRService.management.topology.delUpdatedAction('initChartService');
- QDRChartService.init(); // initialize charting service after we are connected
- });
- // get the list of nodes
- QDRService.management.topology.startUpdating(false);
- });
- }, function () {
- QDR.log.info('failed to auto-connect to ' + host + ':' + port);
- QDR.log.info('redirecting to connect page');
- $timeout(function () {
- $location.path('/connect');
- $location.search('org', org);
- $location.replace();
- });
- });
- }
-
- $rootScope.$on('$routeChangeSuccess', function() {
- var path = $location.path();
- if (path !== '/connect') {
- localStorage[QDR.LAST_LOCATION] = path;
- }
- });
- }]);
-
- QDR.module.controller ('QDR.MainController', ['$scope', '$location', function ($scope, $location) {
- QDR.log.debug('started QDR.MainController with location.url: ' + $location.url());
- QDR.log.debug('started QDR.MainController with window.location.pathname : ' + window.location.pathname);
- $scope.topLevelTabs = [];
- $scope.topLevelTabs.push({
- id: 'qdr',
- content: 'Qpid Dispatch Router Console',
- title: 'Dispatch Router Console',
- isValid: function() { return true; },
- href: function() { return '#connect'; },
- isActive: function() { return true; }
- });
- }]);
-
- QDR.module.controller ('QDR.Core', function ($scope, $rootScope) {
- $scope.alerts = [];
- $scope.breadcrumb = {};
- $scope.closeAlert = function(index) {
- $scope.alerts.splice(index, 1);
- };
- $scope.$on('setCrumb', function(event, data) {
- $scope.breadcrumb = data;
- });
- $scope.$on('newAlert', function(event, data) {
- $scope.alerts.push(data);
- $scope.$apply();
- });
- $scope.$on('clearAlerts', function () {
- $scope.alerts = [];
- $scope.$apply();
- });
- $scope.pageMenuClicked = function () {
- $rootScope.$broadcast('pageMenuClicked');
- };
- });
-
- return QDR;
-}(QDR || {}));
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/dlgChartController.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/dlgChartController.js b/console/stand-alone/plugin/js/dlgChartController.js
index e8c240f..271baa2 100644
--- a/console/stand-alone/plugin/js/dlgChartController.js
+++ b/console/stand-alone/plugin/js/dlgChartController.js
@@ -16,15 +16,11 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
-'use strict';
/* global angular */
-/**
- * @module QDR
- */
-var QDR = (function(QDR) {
- // controller for the edit/configure chart dialog
- QDR.module.controller('QDR.ChartDialogController', function($scope, QDRChartService, $location, $uibModalInstance, chart, updateTick, dashboard, adding) {
+export class ChartDialogController {
+ constructor(QDRChartService, $scope, $location, $uibModalInstance, chart, updateTick, dashboard, adding) {
+ this.controllerName = 'QDR.ChartDialogController';
let dialogSvgChart = null;
$scope.svgDivId = 'dialogEditChart'; // the div id for the svg chart
@@ -41,8 +37,10 @@ var QDR = (function(QDR) {
});
$scope.$watch('dialogChart.areaColor', function (newValue, oldValue) {
if (newValue !== oldValue) {
- if (dialogSvgChart)
+ if (dialogSvgChart) {
+ dialogSvgChart.chart.areaColor = newValue;
dialogSvgChart.tick($scope.svgDivId);
+ }
}
});
$scope.$watch('dialogChart.lineColor', function (newValue, oldValue) {
@@ -73,7 +71,7 @@ var QDR = (function(QDR) {
$scope.showChartsPage = function () {
cleanup();
$uibModalInstance.close(true);
- $location.path(QDR.pluginRoot + '/charts');
+ $location.path('/charts');
};
var cleanup = function () {
@@ -185,20 +183,13 @@ var QDR = (function(QDR) {
setTimeout(showChart, 100);
return;
}
- dialogSvgChart = new QDRChartService.pfAreaChart($scope.dialogChart, $scope.svgDivId);
- /*
- $('input[name=areaColor]').val($scope.dialogChart.areaColor);
- $('input[name=areaColor]').on('input', function (e) {
- $scope.dialogChart.areaColor = $(this).val();
- updateDialogChart()
- })
-*/
+ dialogSvgChart = QDRChartService.pfAreaChart($scope.dialogChart, $scope.svgDivId, false, 550);
if (updateTimer)
clearTimeout(updateTimer);
updateDialogChart();
};
showChart();
- });
- return QDR;
-} (QDR || {}));
+ }
+}
+ChartDialogController.$inject = ['QDRChartService', '$scope', '$location', '$uibModalInstance', 'chart', 'updateTick', 'dashboard', 'adding'];
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/navbar.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/navbar.js b/console/stand-alone/plugin/js/navbar.js
index c17cd77..64d41c0 100644
--- a/console/stand-alone/plugin/js/navbar.js
+++ b/console/stand-alone/plugin/js/navbar.js
@@ -16,105 +16,92 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
-'use strict';
/* global angular */
-/**
- * @module QDR
- */
-var QDR = (function (QDR) {
+export class NavBarController {
+ constructor(QDRService, QDRChartService, $scope, $routeParams, $location) {
+ this.controllerName = 'QDR.NavBarController';
- QDR.breadcrumbs = [
- {
- content: '<i class="icon-power"></i> Connect',
- title: 'Connect to a router',
- isValid: function () { return true; },
- href: '#/connect',
- name: 'Connect'
- },
- {
- content: '<i class="pficon-home"></i> Overview',
- title: 'View router overview',
- isValid: function (QDRService) {return QDRService.management.connection.is_connected(); },
- href: '#/overview',
- name: 'Overview'
- },
- {
- content: '<i class="icon-list "></i> Entities',
- title: 'View the attributes of the router entities',
- isValid: function (QDRService) { return QDRService.management.connection.is_connected(); },
- href: '#/list',
- name: 'Entities'
- },
- {
- content: '<i class="code-branch"></i> Topology',
- title: 'View router network topology',
- isValid: function (QDRService) { return QDRService.management.connection.is_connected(); },
- href: '#/topology',
- name: 'Topology'
- },
- {
- content: '<i class="icon-bar-chart"></i> Charts',
- title: 'View charts',
- isValid: function (QDRService) { return QDRService.management.connection.is_connected(); },
- href: '#/charts',
- name: 'Charts'
- },
- {
- content: '<i class="chord-diagram"></i> Message Flow',
- title: 'Chord chart',
- isValid: function (QDRService) { return QDRService.management.connection.is_connected(); },
- href: '#/chord',
- name: 'Message Flow'
- },
- {
- content: '<i class="icon-schema"></i> Schema',
- title: 'View dispatch schema',
- isValid: function (QDRService) { return QDRService.management.connection.is_connected(); },
- href: '#/schema',
- name: 'Schema'
- }
- ];
- /**
- * @function NavBarController
- *
- * @param $scope
- * @param workspace
- *
- * The controller for this plugin's navigation bar
- *
- */
- QDR.module.controller('QDR.NavBarController', ['$rootScope', '$scope', 'QDRService', 'QDRChartService', '$routeParams', '$location', function($rootScope, $scope, QDRService, QDRChartService, $routeParams, $location) {
- $scope.breadcrumbs = QDR.breadcrumbs;
+ $scope.breadcrumbs = [
+ {
+ content: '<i class="icon-power"></i> Connect',
+ title: 'Connect to a router',
+ isValid: function () { return true; },
+ href: '#/connect',
+ name: 'Connect'
+ },
+ {
+ content: '<i class="pficon-home"></i> Overview',
+ title: 'View router overview',
+ isValid: function (QDRService) {return QDRService.management.connection.is_connected(); },
+ href: '#/overview',
+ name: 'Overview'
+ },
+ {
+ content: '<i class="icon-list "></i> Entities',
+ title: 'View the attributes of the router entities',
+ isValid: function (QDRService) { return QDRService.management.connection.is_connected(); },
+ href: '#/list',
+ name: 'Entities'
+ },
+ {
+ content: '<i class="code-branch"></i> Topology',
+ title: 'View router network topology',
+ isValid: function (QDRService) { return QDRService.management.connection.is_connected(); },
+ href: '#/topology',
+ name: 'Topology'
+ },
+ {
+ content: '<i class="icon-bar-chart"></i> Charts',
+ title: 'View charts',
+ isValid: function (QDRService) { return QDRService.management.connection.is_connected(); },
+ href: '#/charts',
+ name: 'Charts'
+ },
+ {
+ content: '<i class="chord-diagram"></i> Message Flow',
+ title: 'Chord chart',
+ isValid: function (QDRService) { return QDRService.management.connection.is_connected(); },
+ href: '#/chord',
+ name: 'Message Flow'
+ },
+ {
+ content: '<i class="icon-schema"></i> Schema',
+ title: 'View dispatch schema',
+ isValid: function (QDRService) { return QDRService.management.connection.is_connected(); },
+ href: '#/schema',
+ name: 'Schema'
+ }
+ ];
$scope.isValid = function(link) {
return link.isValid(QDRService, $location);
};
-
+
$scope.isActive = function(href) {
return href.split('#')[1] === $location.path();
};
-
+
$scope.isRight = function (link) {
return angular.isDefined(link.right);
};
-
+
$scope.hasChart = function (link) {
if (link.href == '#/charts') {
return QDRChartService.charts.some(function (c) { return c.dashboard; });
}
};
-
+
$scope.isDashboardable = function () {
return ($location.path().indexOf('schema') < 0 && $location.path().indexOf('connect') < 0);
};
-
+
$scope.addToDashboardLink = function () {
var href = '#' + $location.path();
var size = angular.toJson({
size_x: 2,
size_y: 2
});
-
+
var routeParams = angular.toJson($routeParams);
var title = 'Dispatch Router';
return '/hawtio/#/dashboard/add?tab=dashboard' +
@@ -123,9 +110,8 @@ var QDR = (function (QDR) {
'&title=' + encodeURIComponent(title) +
'&size=' + encodeURIComponent(size);
};
+ }
- }]);
-
- return QDR;
+}
+NavBarController.$inject = ['QDRService', 'QDRChartService', '$scope', '$routeParams', '$location'];
-} (QDR || {}));
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/console/stand-alone/plugin/js/posintDirective.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/posintDirective.js b/console/stand-alone/plugin/js/posintDirective.js
new file mode 100644
index 0000000..66b1aa0
--- /dev/null
+++ b/console/stand-alone/plugin/js/posintDirective.js
@@ -0,0 +1,75 @@
+/*
+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.
+*/
+export let posint = function() {
+ return {
+ require: 'ngModel',
+
+ link: function(scope, elem, attr, ctrl) {
+ // input type number allows + and - but we don't want them so filter them out
+ elem.bind('keypress', function(event) {
+ let nkey = !event.charCode ? event.which : event.charCode;
+ let skey = String.fromCharCode(nkey);
+ let nono = '-+.,';
+ if (nono.indexOf(skey) >= 0) {
+ event.preventDefault();
+ return false;
+ }
+ // firefox doesn't filter out non-numeric input. it just sets the ctrl to invalid
+ if (/[!@#$%^&*()]/.test(skey) && event.shiftKey || // prevent shift numbers
+ !( // prevent all but the following
+ nkey <= 0 || // arrows
+ nkey == 8 || // delete|backspace
+ nkey == 13 || // enter
+ (nkey >= 37 && nkey <= 40) || // arrows
+ event.ctrlKey || event.altKey || // ctrl-v, etc.
+ /[0-9]/.test(skey)) // numbers
+ ) {
+ event.preventDefault();
+ return false;
+ }
+ });
+ // check the current value of input
+ var _isPortInvalid = function(value) {
+ let port = value + '';
+ let isErrRange = false;
+ // empty string is valid
+ if (port.length !== 0) {
+ let n = ~~Number(port);
+ if (n < 1 || n > 65535) {
+ isErrRange = true;
+ }
+ }
+ ctrl.$setValidity('range', !isErrRange);
+ return isErrRange;
+ };
+
+ //For DOM -> model validation
+ ctrl.$parsers.unshift(function(value) {
+ return _isPortInvalid(value) ? undefined : value;
+ });
+
+ //For model -> DOM validation
+ ctrl.$formatters.unshift(function(value) {
+ _isPortInvalid(value);
+ return value;
+ });
+ }
+ };
+};
+
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[2/8] qpid-dispatch git commit: DISPATCH-1049 Add console tests
Posted by ea...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/b5deb035/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 15e73c4..9a48ba2 100644
--- a/console/stand-alone/plugin/js/topology/qdrTopology.js
+++ b/console/stand-alone/plugin/js/topology/qdrTopology.js
@@ -16,1593 +16,1091 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
-'use strict';
-/* global angular d3 separateAddresses Traffic */
+/* global angular d3 */
/**
* @module QDR
*/
-var QDR = (function(QDR) {
-
- /**
- * @method TopologyController
- *
- * Controller that handles the QDR topology page
- */
-
- QDR.module.controller('QDR.TopologyController', ['$scope', '$rootScope', 'QDRService', '$location', '$timeout', '$uibModal', '$sce',
- function($scope, $rootScope, QDRService, $location, $timeout, $uibModal, $sce) {
-
- const TOPOOPTIONSKEY = 'topoOptions';
- const radius = 25;
- const radiusNormal = 15;
-
- // - 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 = [];
- let links = [];
- let forceData = {nodes: nodes, links: links};
- let urlPrefix = $location.absUrl();
- urlPrefix = urlPrefix.split('#')[0];
- QDR.log.debug('started QDR.TopologyController with urlPrefix: ' + urlPrefix);
-
- $scope.legendOptions = angular.fromJson(localStorage[TOPOOPTIONSKEY]) || {showTraffic: false, trafficType: 'dots'};
- if (!$scope.legendOptions.trafficType)
- $scope.legendOptions.trafficType = 'dots';
- $scope.legend = {status: {legendOpen: true, optionsOpen: true}};
- $scope.legend.status.optionsOpen = $scope.legendOptions.showTraffic;
- let traffic = new Traffic($scope, $timeout, QDRService, separateAddresses,
- radius, forceData, nextHop, $scope.legendOptions.trafficType, urlPrefix);
-
- // the showTraaffic checkbox was just toggled (or initialized)
- $scope.$watch('legend.status.optionsOpen', function () {
- $scope.legendOptions.showTraffic = $scope.legend.status.optionsOpen;
- localStorage[TOPOOPTIONSKEY] = JSON.stringify($scope.legendOptions);
- if ($scope.legend.status.optionsOpen) {
- traffic.start();
- } else {
- traffic.stop();
- traffic.remove();
- restart();
- }
- });
- $scope.$watch('legendOptions.trafficType', function () {
- localStorage[TOPOOPTIONSKEY] = JSON.stringify($scope.legendOptions);
- if ($scope.legendOptions.showTraffic) {
- restart();
- traffic.setAnimationType($scope.legendOptions.trafficType, separateAddresses, radius);
- traffic.start();
- }
- });
-
- // mouse event vars
- let selected_node = null,
- selected_link = null,
- mousedown_link = null,
- mousedown_node = null,
- mouseover_node = null,
- mouseup_node = null,
- initial_mouse_down_position = null;
-
- $scope.schema = 'Not connected';
-
- $scope.contextNode = null; // node that is associated with the current context menu
- $scope.isRight = function(mode) {
- return mode.right;
- };
-
- var setNodesFixed = function (name, b) {
- nodes.some(function (n) {
- if (n.name === name) {
- n.fixed = b;
- return true;
- }
- });
- };
- $scope.setFixed = function(b) {
- if ($scope.contextNode) {
- $scope.contextNode.fixed = b;
- setNodesFixed($scope.contextNode.name, b);
- savePositions();
- }
+import { QDRLogger, QDRRedirectWhenConnected } 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 } from './topoUtils.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';
+ const radius = 25;
+ const radiusNormal = 15;
+
+ // - 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(QDRService, QDRLog);
+ let links = new Links(QDRService, QDRLog);
+ let forceData = {nodes: nodes, links: links};
+ // urlPrefix is used when referring to svg:defs
+ let urlPrefix = $location.absUrl();
+ urlPrefix = urlPrefix.split('#')[0];
+
+ $scope.legendOptions = angular.fromJson(localStorage[TOPOOPTIONSKEY]) || {showTraffic: false, trafficType: 'dots'};
+ if (!$scope.legendOptions.trafficType)
+ $scope.legendOptions.trafficType = 'dots';
+ $scope.legend = {status: {legendOpen: true, optionsOpen: true}};
+ $scope.legend.status.optionsOpen = $scope.legendOptions.showTraffic;
+ let traffic = new Traffic($scope, $timeout, QDRService, separateAddresses,
+ radius, forceData, $scope.legendOptions.trafficType, urlPrefix);
+
+ // the showTraaffic checkbox was just toggled (or initialized)
+ $scope.$watch('legend.status.optionsOpen', function () {
+ $scope.legendOptions.showTraffic = $scope.legend.status.optionsOpen;
+ localStorage[TOPOOPTIONSKEY] = JSON.stringify($scope.legendOptions);
+ if ($scope.legend.status.optionsOpen) {
+ traffic.start();
+ } else {
+ traffic.stop();
+ traffic.remove();
restart();
+ }
+ });
+ $scope.$watch('legendOptions.trafficType', function () {
+ localStorage[TOPOOPTIONSKEY] = JSON.stringify($scope.legendOptions);
+ if ($scope.legendOptions.showTraffic) {
+ restart();
+ traffic.setAnimationType($scope.legendOptions.trafficType, separateAddresses, radius);
+ traffic.start();
+ }
+ });
+
+ // mouse event vars
+ let selected_node = null,
+ selected_link = null,
+ mousedown_link = null,
+ mousedown_node = null,
+ mouseover_node = null,
+ mouseup_node = null,
+ initial_mouse_down_position = null;
+
+ $scope.schema = 'Not connected';
+
+ $scope.contextNode = null; // node that is associated with the current context menu
+ $scope.isRight = function(mode) {
+ return mode.right;
+ };
+
+ $scope.setFixed = function(b) {
+ if ($scope.contextNode) {
+ $scope.contextNode.fixed = b;
+ nodes.setNodesFixed($scope.contextNode.name, b);
+ nodes.savePositions();
+ }
+ restart();
+ };
+ $scope.isFixed = function() {
+ if (!$scope.contextNode)
+ return false;
+ return ($scope.contextNode.fixed & 1);
+ };
+
+ let mouseX, mouseY;
+ var relativeMouse = function () {
+ let offset = $('#main_container').offset();
+ return {left: (mouseX + $(document).scrollLeft()) - 1,
+ top: (mouseY + $(document).scrollTop()) - 1,
+ offset: offset
};
- $scope.isFixed = function() {
- if (!$scope.contextNode)
- return false;
- return ($scope.contextNode.fixed & 1);
- };
-
- let mouseX, mouseY;
- var relativeMouse = function () {
- let offset = $('#main_container').offset();
- return {left: (mouseX + $(document).scrollLeft()) - 1,
- top: (mouseY + $(document).scrollTop()) - 1,
- offset: offset
- };
- };
- // event handlers for popup context menu
- $(document).mousemove(function(e) {
- mouseX = e.clientX;
- mouseY = e.clientY;
- });
- $(document).mousemove();
- $(document).click(function() {
- $scope.contextNode = null;
- $('.contextMenu').fadeOut(200);
- });
-
- const radii = {
- 'inter-router': 25,
- 'normal': 15,
- 'on-demand': 15,
- 'route-container': 15,
- };
- let svg, lsvg;
- let force;
- let animate = false; // should the force graph organize itself when it is displayed
- let path, circle;
- let savedKeys = {};
- 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) {
- QDR.log.info('page width and height are abnormal w:' + width + ' height:' + height);
- return [0, 0];
- }
- return [width, height];
- };
- var resize = function() {
- if (!svg)
- return;
- let sizes = getSizes();
- width = sizes[0];
- height = sizes[1];
- if (width > 0) {
- // set attrs and 'resume' force
- svg.attr('width', width);
- svg.attr('height', height);
- force.size(sizes).resume();
- }
- $timeout(createLegend);
- };
-
- // the window is narrow and the page menu icon was clicked.
- // Re-create the legend
- $scope.$on('pageMenuClicked', function () {
- $timeout(createLegend);
- });
-
- window.addEventListener('resize', resize);
+ };
+ // event handlers for popup context menu
+ $(document).mousemove(e => {
+ mouseX = e.clientX;
+ mouseY = e.clientY;
+ });
+ $(document).mousemove();
+ $(document).click(function() {
+ $scope.contextNode = null;
+ $('.contextMenu').fadeOut(200);
+ });
+
+ const radii = {
+ 'inter-router': 25,
+ 'normal': 15,
+ 'on-demand': 15,
+ 'route-container': 15,
+ };
+ let svg, lsvg; // main svg and legend svg
+ let force;
+ let animate = false; // should the force graph organize itself when it is displayed
+ let path, circle;
+ let savedKeys = {};
+ 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();
width = sizes[0];
height = sizes[1];
- if (width <= 0 || height <= 0)
- return;
+ if (width > 0) {
+ // set attrs and 'resume' force
+ svg.attr('width', width);
+ svg.attr('height', height);
+ force.size(sizes).resume();
+ }
+ $timeout(createLegend);
+ };
+
+ // the window is narrow and the page menu icon was clicked.
+ // Re-create the legend
+ $scope.$on('pageMenuClicked', function () {
+ $timeout(createLegend);
+ });
+
+ window.addEventListener('resize', resize);
+ let sizes = getSizes();
+ width = sizes[0];
+ height = sizes[1];
+ if (width <= 0 || height <= 0)
+ return;
+
+ // vary the following force graph attributes based on nodeCount
+ // <= 6 routers returns min, >= 80 routers returns max, interpolate linearly
+ var forceScale = function(nodeCount, min, max) {
+ let count = Math.max(Math.min(nodeCount, 80), 6);
+ let x = d3.scale.linear()
+ .domain([6,80])
+ .range([min, max]);
+ //QDRLog.debug("forceScale(" + nodeCount + ", " + min + ", " + max + " returns " + x(count) + " " + x(nodeCount))
+ return x(count);
+ };
+ var linkDistance = function (d, nodeCount) {
+ if (d.target.nodeType === 'inter-router')
+ return forceScale(nodeCount, 150, 70);
+ return forceScale(nodeCount, 75, 40);
+ };
+ var charge = function (d, nodeCount) {
+ if (d.nodeType === 'inter-router')
+ return forceScale(nodeCount, -1800, -900);
+ return -900;
+ };
+ var gravity = function (d, nodeCount) {
+ return forceScale(nodeCount, 0.0001, 0.1);
+ };
+ // initialize the nodes and links array from the QDRService.topology._nodeInfo object
+ var initForceGraph = function() {
+ forceData.nodes = nodes = new Nodes(QDRService, QDRLog);
+ forceData.links = links = new Links(QDRService, QDRLog);
+ let nodeInfo = QDRService.management.topology.nodeInfo();
+ let nodeCount = Object.keys(nodeInfo).length;
+
+ let oldSelectedNode = selected_node;
+ let oldMouseoverNode = mouseover_node;
+ mouseover_node = null;
+ selected_node = null;
+ selected_link = null;
+
+ nodes.savePositions();
+ d3.select('#SVG_ID').remove();
+ svg = d3.select('#topology')
+ .append('svg')
+ .attr('id', 'SVG_ID')
+ .attr('width', width)
+ .attr('height', height);
+
+ // the legend
+ 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( ${(radii['inter-router'] + 2)},${(radii['inter-router'] + 2)})`)
+ .selectAll('g');
- var nodeExists = function (connectionContainer) {
- return nodes.findIndex( function (node) {
- return node.container === connectionContainer;
- });
- };
- var normalExists = function (connectionContainer) {
- let normalInfo = {};
- for (let i=0; i<nodes.length; ++i) {
- if (nodes[i].normals) {
- if (nodes[i].normals.some(function (normal, j) {
- if (normal.container === connectionContainer && i !== j) {
- normalInfo = {nodesIndex: i, normalsIndex: j};
- return true;
- }
- return false;
- }))
- break;
- }
- }
- return normalInfo;
- };
- var getLinkSource = function (nodesIndex) {
- for (let i=0; i<links.length; ++i) {
- if (links[i].target === nodesIndex)
- return i;
- }
- return -1;
- };
- var aNode = function(id, name, nodeType, nodeInfo, nodeIndex, x, y, connectionContainer, resultIndex, fixed, properties) {
- properties = properties || {};
- for (let i=0; i<nodes.length; ++i) {
- if (nodes[i].name === name || nodes[i].container === connectionContainer) {
- if (properties.product)
- nodes[i].properties = properties;
- return nodes[i];
+ // mouse event vars
+ mousedown_link = null;
+ mousedown_node = null;
+ mouseup_node = null;
+
+ // initialize the list of nodes
+ forceData.nodes = nodes = new Nodes(QDRService, QDRLog);
+ animate = nodes.initialize(nodeInfo, localStorage, width, height);
+ nodes.savePositions();
+
+ // initialize the list of links
+ let unknowns = [];
+ forceData.links = links = new Links(QDRService, QDRLog);
+ if (links.initializeLinks(nodeInfo, nodes, unknowns, localStorage, height)) {
+ animate = true;
+ }
+ $scope.schema = QDRService.management.schema();
+ // init D3 force layout
+ force = d3.layout.force()
+ .nodes(nodes.nodes)
+ .links(links.links)
+ .size([width, height])
+ .linkDistance(function(d) { return linkDistance(d, nodeCount); })
+ .charge(function(d) { return charge(d, nodeCount); })
+ .friction(.10)
+ .gravity(function(d) { return gravity(d, nodeCount); })
+ .on('tick', tick)
+ .on('end', function () {nodes.savePositions();})
+ .start();
+
+ // This section adds in the arrows
+ svg.append('svg:defs').attr('class', 'marker-defs').selectAll('marker')
+ .data(['end-arrow', 'end-arrow-selected', 'end-arrow-small', 'end-arrow-highlighted',
+ 'start-arrow', 'start-arrow-selected', 'start-arrow-small', 'start-arrow-highlighted'])
+ .enter().append('svg:marker')
+ .attr('id', function (d) { return d; })
+ .attr('viewBox', '0 -5 10 10')
+ .attr('refX', function (d) {
+ if (d.substr(0, 3) === 'end') {
+ return 24;
}
- }
- let routerId = QDRService.management.topology.nameFromId(id);
- return {
- key: id,
- name: name,
- nodeType: nodeType,
- properties: properties,
- routerId: routerId,
- x: x,
- y: y,
- id: nodeIndex,
- resultIndex: resultIndex,
- fixed: !!+fixed,
- cls: '',
- container: connectionContainer
- };
- };
-
- var getLinkDir = function (id, connection, onode) {
- let links = onode['router.link'];
- if (!links) {
- return 'unknown';
- }
- let inCount = 0, outCount = 0;
- links.results.forEach( function (linkResult) {
- let link = QDRService.utilities.flatten(links.attributeNames, linkResult);
- if (link.linkType === 'endpoint' && link.connectionId === connection.identity)
- if (link.linkDir === 'in')
- ++inCount;
- else
- ++outCount;
- });
- if (inCount > 0 && outCount > 0)
- return 'both';
- if (inCount > 0)
- return 'in';
- if (outCount > 0)
- return 'out';
- return 'unknown';
- };
-
- var savePositions = function () {
- nodes.forEach( function (d) {
- localStorage[d.name] = angular.toJson({
- x: Math.round(d.x),
- y: Math.round(d.y),
- fixed: (d.fixed & 1) ? 1 : 0,
- });
+ return d !== 'start-arrow-small' ? -14 : -24;})
+ .attr('markerWidth', 4)
+ .attr('markerHeight', 4)
+ .attr('orient', 'auto')
+ .classed('small', function (d) {return d.indexOf('small') > -1;})
+ .append('svg:path')
+ .attr('d', function (d) {
+ return d.substr(0, 3) === 'end' ? 'M 0 -5 L 10 0 L 0 5 z' : 'M 10 -5 L 0 0 L 10 5 z';
});
- };
-
- var initializeNodes = function (nodeInfo) {
- let nodeCount = Object.keys(nodeInfo).length;
- let yInit = 50;
- forceData.nodes = nodes = [];
- for (let id in nodeInfo) {
- let name = QDRService.management.topology.nameFromId(id);
- // if we have any new nodes, animate the force graph to position them
- let position = angular.fromJson(localStorage[name]);
- if (!angular.isDefined(position)) {
- animate = true;
- position = {
- x: Math.round(width / 4 + ((width / 2) / nodeCount) * nodes.length),
- y: Math.round(height / 2 + Math.sin(nodes.length / (Math.PI*2.0)) * height / 4),
- fixed: false,
- };
- //QDR.log.debug("new node pos (" + position.x + ", " + position.y + ")")
- }
- if (position.y > height) {
- position.y = 200 - yInit;
- yInit *= -1;
- }
- nodes.push(aNode(id, name, 'inter-router', nodeInfo, nodes.length, position.x, position.y, name, undefined, position.fixed));
- }
- };
- var initializeLinks = function (nodeInfo, unknowns) {
- forceData.links = links = [];
- let source = 0;
- let client = 1.0;
- for (let id in nodeInfo) {
- let onode = nodeInfo[id];
- if (!onode['connection'])
- continue;
- let conns = onode['connection'].results;
- let attrs = onode['connection'].attributeNames;
- //QDR.log.debug("external client parent is " + parent);
- let normalsParent = {}; // 1st normal node for this parent
-
- for (let j = 0; j < conns.length; j++) {
- let connection = QDRService.utilities.flatten(attrs, conns[j]);
- let role = connection.role;
- let properties = connection.properties || {};
- let dir = connection.dir;
- if (role == 'inter-router') {
- let connId = connection.container;
- let target = getContainerIndex(connId, nodeInfo);
- if (target >= 0) {
- getLink(source, target, dir, '', source + '-' + target);
- }
- } /* else if (role == "normal" || role == "on-demand" || role === "route-container")*/ {
- // not an connection between routers, but an external connection
- let name = QDRService.management.topology.nameFromId(id) + '.' + connection.identity;
-
- // if we have any new clients, animate the force graph to position them
- let position = angular.fromJson(localStorage[name]);
- if (!angular.isDefined(position)) {
- animate = true;
- position = {
- x: Math.round(nodes[source].x + 40 * Math.sin(client / (Math.PI * 2.0))),
- y: Math.round(nodes[source].y + 40 * Math.cos(client / (Math.PI * 2.0))),
- fixed: false
- };
- //QDR.log.debug("new client pos (" + position.x + ", " + position.y + ")")
- }// else QDR.log.debug("using previous location")
- if (position.y > height) {
- position.y = Math.round(nodes[source].y + 40 + Math.cos(client / (Math.PI * 2.0)));
- }
- let existingNodeIndex = nodeExists(connection.container);
- let normalInfo = normalExists(connection.container);
- let node = aNode(id, name, role, nodeInfo, nodes.length, position.x, position.y, connection.container, j, position.fixed, properties);
- let nodeType = QDRService.utilities.isAConsole(properties, connection.identity, role, node.key) ? 'console' : 'client';
- let cdir = getLinkDir(id, connection, onode);
- if (existingNodeIndex >= 0) {
- // make a link between the current router (source) and the existing node
- getLink(source, existingNodeIndex, dir, 'small', connection.name);
- } else if (normalInfo.nodesIndex) {
- // get node index of node that contained this connection in its normals array
- let normalSource = getLinkSource(normalInfo.nodesIndex);
- if (normalSource >= 0) {
- if (cdir === 'unknown')
- cdir = dir;
- node.cdir = cdir;
- nodes.push(node);
- // create link from original node to the new node
- getLink(links[normalSource].source, nodes.length-1, cdir, 'small', connection.name);
- // create link from this router to the new node
- getLink(source, nodes.length-1, cdir, 'small', connection.name);
- // remove the old node from the normals list
- nodes[normalInfo.nodesIndex].normals.splice(normalInfo.normalsIndex, 1);
- }
- } else if (role === 'normal') {
- // normal nodes can be collapsed into a single node if they are all the same dir
- if (cdir !== 'unknown') {
- node.user = connection.user;
- node.isEncrypted = connection.isEncrypted;
- node.host = connection.host;
- node.connectionId = connection.identity;
- node.cdir = cdir;
- // determine arrow direction by using the link directions
- if (!normalsParent[nodeType+cdir]) {
- normalsParent[nodeType+cdir] = node;
- nodes.push(node);
- node.normals = [node];
- // now add a link
- getLink(source, nodes.length - 1, cdir, 'small', connection.name);
- client++;
- } else {
- normalsParent[nodeType+cdir].normals.push(node);
- }
- } else {
- node.id = nodes.length - 1 + unknowns.length;
- unknowns.push(node);
- }
- } else {
- nodes.push(node);
- // now add a link
- getLink(source, nodes.length - 1, dir, 'small', connection.name);
- client++;
- }
- }
- }
- source++;
- }
- };
-
- // vary the following force graph attributes based on nodeCount
- // <= 6 routers returns min, >= 80 routers returns max, interpolate linearly
- var forceScale = function(nodeCount, min, max) {
- let count = nodeCount;
- if (nodeCount < 6) count = 6;
- if (nodeCount > 80) count = 80;
- let x = d3.scale.linear()
- .domain([6,80])
- .range([min, max]);
- //QDR.log.debug("forceScale(" + nodeCount + ", " + min + ", " + max + " returns " + x(count) + " " + x(nodeCount))
- return x(count);
- };
- var linkDistance = function (d, nodeCount) {
- if (d.target.nodeType === 'inter-router')
- return forceScale(nodeCount, 150, 70);
- return forceScale(nodeCount, 75, 40);
- };
- var charge = function (d, nodeCount) {
- if (d.nodeType === 'inter-router')
- return forceScale(nodeCount, -1800, -900);
- return -900;
- };
- var gravity = function (d, nodeCount) {
- return forceScale(nodeCount, 0.0001, 0.1);
- };
- // initialize the nodes and links array from the QDRService.topology._nodeInfo object
- var initForceGraph = function() {
- forceData.nodes = nodes = [];
- forceData.links = links = [];
- let nodeInfo = QDRService.management.topology.nodeInfo();
- let nodeCount = Object.keys(nodeInfo).length;
-
- let oldSelectedNode = selected_node;
- let oldMouseoverNode = mouseover_node;
- mouseover_node = null;
- selected_node = null;
- selected_link = null;
-
- savePositions();
- d3.select('#SVG_ID').remove();
- svg = d3.select('#topology')
- .append('svg')
- .attr('id', 'SVG_ID')
- .attr('width', width)
- .attr('height', height);
-
- // the legend
- 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(' + (radii['inter-router'] + 2) + ',' + (radii['inter-router'] + 2) + ')')
- .selectAll('g');
-
- // mouse event vars
- mousedown_link = null;
- mousedown_node = null;
- mouseup_node = null;
-
- // initialize the list of nodes
- initializeNodes(nodeInfo);
- savePositions();
-
- // initialize the list of links
- let unknowns = [];
- initializeLinks(nodeInfo, unknowns);
- $scope.schema = QDRService.management.schema();
- // init D3 force layout
- force = d3.layout.force()
- .nodes(nodes)
- .links(links)
- .size([width, height])
- .linkDistance(function(d) { return linkDistance(d, nodeCount); })
- .charge(function(d) { return charge(d, nodeCount); })
- .friction(.10)
- .gravity(function(d) { return gravity(d, nodeCount); })
- .on('tick', tick)
- .on('end', function () {savePositions();})
- .start();
-
- // This section adds in the arrows
- svg.append('svg:defs').attr('class', 'marker-defs').selectAll('marker')
- .data(['end-arrow', 'end-arrow-selected', 'end-arrow-small', 'end-arrow-highlighted',
- 'start-arrow', 'start-arrow-selected', 'start-arrow-small', 'start-arrow-highlighted'])
- .enter().append('svg:marker')
- .attr('id', function (d) { return d; })
- .attr('viewBox', '0 -5 10 10')
- .attr('refX', function (d) {
- if (d.substr(0, 3) === 'end') {
- return 24;
- }
- return d !== 'start-arrow-small' ? -14 : -24;})
- .attr('markerWidth', 4)
- .attr('markerHeight', 4)
- .attr('orient', 'auto')
- .classed('small', function (d) {return d.indexOf('small') > -1;})
- .append('svg:path')
- .attr('d', function (d) {
- return d.substr(0, 3) === 'end' ? 'M 0 -5 L 10 0 L 0 5 z' : 'M 10 -5 L 0 0 L 10 5 z';
- });
-
- // 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').selectAll('path'),
- circle = svg.append('svg:g').selectAll('g');
-
- // app starts here
- restart(false);
- force.start();
- if (oldSelectedNode) {
- d3.selectAll('circle.inter-router').classed('selected', function (d) {
- if (d.key === oldSelectedNode.key) {
- selected_node = d;
- return true;
- }
- return false;
- });
- }
- if (oldMouseoverNode && selected_node) {
- 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 () {
- nextHop(selected_node, d);
- restart();
- });
- }
- });
- }
-
- // if any clients don't yet have link directions, get the links for those nodes and restart the graph
- if (unknowns.length > 0)
- setTimeout(resolveUnknowns, 10, nodeInfo, unknowns);
-
- var continueForce = function (extra) {
- if (extra > 0) {
- --extra;
- force.start();
- setTimeout(continueForce, 100, extra);
+ // 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').selectAll('path'),
+ circle = svg.append('svg:g').selectAll('g');
+
+ // app starts here
+ restart(false);
+ force.start();
+ if (oldSelectedNode) {
+ d3.selectAll('circle.inter-router').classed('selected', function (d) {
+ if (d.key === oldSelectedNode.key) {
+ selected_node = d;
+ return true;
}
- };
- continueForce(forceScale(nodeCount, 0, 200)); // give graph time to settle down
- };
-
- var resolveUnknowns = function (nodeInfo, unknowns) {
- let unknownNodes = {};
- // collapse the unknown node.keys using an object
- for (let i=0; i<unknowns.length; ++i) {
- unknownNodes[unknowns[i].key] = 1;
- }
- unknownNodes = Object.keys(unknownNodes);
- //QDR.log.info("-- resolveUnknowns: ensuring .connection and .router.link are present for each node")
- QDRService.management.topology.ensureEntities(unknownNodes, [{entity: 'connection', force: true},
- {entity: 'router.link', attrs: ['linkType','connectionId','linkDir'], force: true}], function () {
- nodeInfo = QDRService.management.topology.nodeInfo();
- initializeLinks(nodeInfo, []);
- // collapse any router-container nodes that are duplicates
- animate = true;
- force.nodes(nodes).links(links).start();
- restart(false);
+ return false;
});
- };
-
- function getContainerIndex(_id, nodeInfo) {
- let nodeIndex = 0;
- for (let id in nodeInfo) {
- if (QDRService.management.topology.nameFromId(id) === _id)
- return nodeIndex;
- ++nodeIndex;
- }
- return -1;
- }
-
- function getLink(_source, _target, dir, cls, uid) {
- for (let i = 0; i < links.length; i++) {
- let s = links[i].source,
- t = links[i].target;
- if (typeof links[i].source == 'object') {
- s = s.id;
- t = t.id;
- }
- if (s == _source && t == _target) {
- return i;
- }
- // same link, just reversed
- if (s == _target && t == _source) {
- return -i;
- }
- }
- //QDR.log.debug("creating new link (" + (links.length) + ") between " + nodes[_source].name + " and " + nodes[_target].name);
- if (links.some( function (l) { return l.uid === uid;}))
- uid = uid + '.' + links.length;
- let link = {
- source: _source,
- target: _target,
- left: dir != 'out',
- right: (dir == 'out' || dir == 'both'),
- cls: cls,
- uid: uid,
- };
- return links.push(link) - 1;
}
-
-
- function resetMouseVars() {
- mousedown_node = null;
- mouseover_node = null;
- mouseup_node = null;
- mousedown_link = null;
- }
-
- // update force layout (called automatically each iteration)
- function tick() {
- circle.attr('transform', function(d) {
- let cradius;
- if (d.nodeType == 'inter-router') {
- cradius = d.left ? radius + 8 : radius;
- } else {
- cradius = d.left ? radiusNormal + 18 : radiusNormal;
+ if (oldMouseoverNode && selected_node) {
+ 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();
+ });
}
- d.x = Math.max(d.x, radiusNormal * 2);
- d.y = Math.max(d.y, radiusNormal * 2);
- d.x = Math.max(0, Math.min(width - cradius, d.x));
- d.y = Math.max(0, Math.min(height - cradius, d.y));
- return 'translate(' + d.x + ',' + d.y + ')';
});
+ }
- // draw directed edges with proper padding from node centers
- path.attr('d', function(d) {
- let sourcePadding, targetPadding, r;
-
- r = d.target.nodeType === 'inter-router' ? radius : radiusNormal - 18;
- sourcePadding = targetPadding = 0;
- let dtx = Math.max(targetPadding, Math.min(width - r, d.target.x)),
- dty = Math.max(targetPadding, Math.min(height - r, d.target.y)),
- dsx = Math.max(sourcePadding, Math.min(width - r, d.source.x)),
- dsy = Math.max(sourcePadding, Math.min(height - r, d.source.y));
-
- let deltaX = dtx - dsx,
- deltaY = dty - dsy,
- dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
- if (dist == 0)
- dist = 0.001;
- let normX = deltaX / dist,
- normY = deltaY / dist;
- let sourceX = dsx + (sourcePadding * normX),
- sourceY = dsy + (sourcePadding * normY),
- targetX = dtx - (targetPadding * normX),
- targetY = dty - (targetPadding * normY);
- sourceX = Math.max(0, sourceX);
- sourceY = Math.max(0, sourceY);
- targetX = Math.max(0, targetX);
- targetY = Math.max(0, targetY);
-
- return 'M' + sourceX + ',' + sourceY + 'L' + targetX + ',' + targetY;
- })
- .attr('id', function (d) {
- return ['path', d.source.index, d.target.index].join('-');
- });
+ // if any clients don't yet have link directions, get the links for those nodes and restart the graph
+ if (unknowns.length > 0)
+ setTimeout(resolveUnknowns, 10, nodeInfo, unknowns);
- if (!animate) {
- animate = true;
- force.stop();
+ var continueForce = function (extra) {
+ if (extra > 0) {
+ --extra;
+ force.start();
+ setTimeout(continueForce, 100, extra);
}
+ };
+ continueForce(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.
+ // The router.link info is needed to determine which direction the arrows between routers should point.
+ // 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 node.keys using an object
+ for (let i=0; i<unknowns.length; ++i) {
+ unknownNodes[unknowns[i].key] = 1;
}
+ unknownNodes = Object.keys(unknownNodes);
+ //QDRLog.info("-- resolveUnknowns: ensuring .connection and .router.link are present for each node")
+ QDRService.management.topology.ensureEntities(unknownNodes, [{entity: 'connection', force: true},
+ {entity: 'router.link', attrs: ['linkType','connectionId','linkDir'], force: true}], function () {
+ nodeInfo = QDRService.management.topology.nodeInfo();
+ forceData.links = links = new Links(QDRService, QDRLog);
+ links.initializeLinks(nodeInfo, nodes, [], localStorage, height);
+ animate = true;
+ force.nodes(nodes.nodes).links(links.links).start();
+ restart(false);
+ });
+ };
- // highlight the paths between the selected node and the hovered node
- function findNextHopNode(from, d) {
- // d is the node that the mouse is over
- // from is the selected_node ....
- if (!from)
- return null;
-
- if (from == d)
- return selected_node;
-
- //QDR.log.debug("finding nextHop from: " + from.name + " to " + d.name);
- let sInfo = QDRService.management.topology.nodeInfo()[from.key];
+ function resetMouseVars() {
+ mousedown_node = null;
+ mouseover_node = null;
+ mouseup_node = null;
+ mousedown_link = null;
+ }
- if (!sInfo) {
- QDR.log.warn('unable to find topology node info for ' + from.key);
- return null;
+ // update force layout (called automatically each iteration)
+ function tick() {
+ circle.attr('transform', function(d) {
+ let cradius;
+ if (d.nodeType == 'inter-router') {
+ cradius = d.left ? radius + 8 : radius;
+ } else {
+ cradius = d.left ? radiusNormal + 18 : radiusNormal;
}
+ d.x = Math.max(d.x, radiusNormal * 2);
+ d.y = Math.max(d.y, radiusNormal * 2);
+ d.x = Math.max(0, Math.min(width - cradius, d.x));
+ d.y = Math.max(0, Math.min(height - cradius, d.y));
+ return `translate(${d.x},${d.y})`;
+ });
- // 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;
- for (let hIdx = 0; hIdx < vAr.length; ++hIdx) {
- let addrT = QDRService.utilities.valFor(aAr, vAr[hIdx], 'id');
- if (addrT == d.name) {
- //QDR.log.debug("found " + d.name + " at " + hIdx);
- let nextHop = QDRService.utilities.valFor(aAr, vAr[hIdx], 'nextHop');
- //QDR.log.debug("nextHop was " + nextHop);
- return (nextHop == null) ? nodeFor(addrT) : nodeFor(nextHop);
- }
- }
- return null;
- }
+ // draw directed edges with proper padding from node centers
+ path.attr('d', function(d) {
+ let sourcePadding, targetPadding, r;
+
+ r = d.target.nodeType === 'inter-router' ? radius : radiusNormal - 18;
+ sourcePadding = targetPadding = 0;
+ let dtx = Math.max(targetPadding, Math.min(width - r, d.target.x)),
+ dty = Math.max(targetPadding, Math.min(height - r, d.target.y)),
+ dsx = Math.max(sourcePadding, Math.min(width - r, d.source.x)),
+ dsy = Math.max(sourcePadding, Math.min(height - r, d.source.y));
+
+ let deltaX = dtx - dsx,
+ deltaY = dty - dsy,
+ dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
+ if (dist == 0)
+ dist = 0.001;
+ let normX = deltaX / dist,
+ normY = deltaY / dist;
+ let sourceX = dsx + (sourcePadding * normX),
+ sourceY = dsy + (sourcePadding * normY),
+ targetX = dtx - (targetPadding * normX),
+ targetY = dty - (targetPadding * normY);
+ sourceX = Math.max(0, sourceX);
+ sourceY = Math.max(0, sourceY);
+ targetX = Math.max(0, targetX);
+ targetY = Math.max(0, targetY);
+
+ return `M${sourceX},${sourceY}L${targetX},${targetY}`;
+ })
+ .attr('id', function (d) {
+ return ['path', d.source.index, d.target.index].join('-');
+ });
- function nodeFor(name) {
- for (let i = 0; i < nodes.length; ++i) {
- if (nodes[i].name == name)
- return nodes[i];
- }
- return null;
+ if (!animate) {
+ animate = true;
+ force.stop();
}
+ }
- function linkFor(source, target) {
- for (let i = 0; i < links.length; ++i) {
- if ((links[i].source == source) && (links[i].target == target))
- return links[i];
- if ((links[i].source == target) && (links[i].target == source))
- return links[i];
- }
- // the selected node was a client/broker
- return null;
- }
+ 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);
+ 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');
+ 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');
- }
+ }
- function clerAllHighlights() {
- for (let i = 0; i < links.length; ++i) {
- links[i]['highlighted'] = false;
- }
- for (let i = 0; i<nodes.length; ++i) {
- nodes[i]['highlighted'] = false;
- }
- }
- // takes the nodes and links array of objects and adds svg elements for everything that hasn't already
- // been added
- function restart(start) {
- if (!circle)
- return;
- circle.call(force.drag);
-
- // path (link) group
- path = path.data(links, function(d) {return d.uid;});
-
- // update existing links
- path.classed('selected', function(d) {
- return d === selected_link;
- })
- .classed('highlighted', function(d) {
- return d.highlighted;
- });
- if (!$scope.legend.status.optionsOpen || $scope.legendOptions.trafficType === 'dots') {
- path
- .attr('marker-start', function(d) {
- let sel = d === selected_link ? '-selected' : (d.cls === 'small' ? '-small' : '');
- if (d.highlighted)
- sel = '-highlighted';
- return d.left ? 'url(' + urlPrefix + '#start-arrow' + sel + ')' : '';
- })
- .attr('marker-end', function(d) {
- let sel = d === selected_link ? '-selected' : (d.cls === 'small' ? '-small' : '');
- if (d.highlighted)
- sel = '-highlighted';
- return d.right ? 'url(' + urlPrefix + '#end-arrow' + sel + ')' : '';
- });
- }
- // add new links. if a link with a new uid is found in the data, add a new path
- path.enter().append('svg:path')
- .attr('class', 'link')
+ function clearAllHighlights() {
+ links.clearHighlighted();
+ nodes.clearHighlighted();
+ }
+ // takes the nodes and links array of objects and adds svg elements for everything that hasn't already
+ // been added
+ function restart(start) {
+ if (!circle)
+ return;
+ circle.call(force.drag);
+
+ // path (link) group
+ path = path.data(links.links, function(d) {return d.uid;});
+
+ // update existing links
+ path.classed('selected', function(d) {
+ return d === selected_link;
+ })
+ .classed('highlighted', function(d) {
+ return d.highlighted;
+ });
+ if (!$scope.legend.status.optionsOpen || $scope.legendOptions.trafficType === 'dots') {
+ path
.attr('marker-start', function(d) {
let sel = d === selected_link ? '-selected' : (d.cls === 'small' ? '-small' : '');
- return d.left ? 'url(' + urlPrefix + '#start-arrow' + sel + ')' : '';
+ if (d.highlighted)
+ sel = '-highlighted';
+ return d.left ? `url(${urlPrefix}#start-arrow${sel})` : '';
})
.attr('marker-end', function(d) {
let sel = d === selected_link ? '-selected' : (d.cls === 'small' ? '-small' : '');
- return d.right ? 'url(' + urlPrefix + '#end-arrow' + sel + ')' : '';
- })
- .classed('small', function(d) {
- return d.cls == 'small';
- })
- .on('mouseover', function(d) { // mouse over a path
- let event = d3.event;
- mousedown_link = d;
- selected_link = mousedown_link;
- let updateTooltip = function () {
- $timeout(function () {
- $scope.trustedpopoverContent = $sce.trustAsHtml(connectionPopupHTML(d));
- if (selected_link)
- displayTooltip(event);
- });
- };
- // update the contents of the popup tooltip each time the data is polled
- QDRService.management.topology.addUpdatedAction('connectionPopupHTML', updateTooltip);
- QDRService.management.topology.ensureAllEntities(
- [{ entity: 'router.link', force: true},{entity: 'connection'}], function () {
- updateTooltip();
- });
- // show the tooltip
- updateTooltip();
- restart();
-
- })
- .on('mouseout', function() { // mouse out of a path
- QDRService.management.topology.delUpdatedAction('connectionPopupHTML');
- d3.select('#popover-div')
- .style('display', 'none');
- selected_link = null;
- restart();
- })
- // left click a path
- .on('click', function () {
- d3.event.stopPropagation();
- clearPopups();
+ if (d.highlighted)
+ sel = '-highlighted';
+ return d.right ? `url(${urlPrefix}#end-arrow${sel})` : '';
});
- // remove old links
- path.exit().remove();
+ }
+ // add new links. if a link with a new uid is found in the data, add a new path
+ path.enter().append('svg:path')
+ .attr('class', 'link')
+ .attr('marker-start', function(d) {
+ let sel = d === selected_link ? '-selected' : (d.cls === 'small' ? '-small' : '');
+ return d.left ? `url(${urlPrefix}#start-arrow${sel})` : '';
+ })
+ .attr('marker-end', function(d) {
+ let sel = d === selected_link ? '-selected' : (d.cls === 'small' ? '-small' : '');
+ return d.right ? `url(${urlPrefix}#end-arrow${sel})` : '';
+ })
+ .classed('small', function(d) {
+ return d.cls == 'small';
+ })
+ .on('mouseover', function(d) { // mouse over a path
+ let event = d3.event;
+ mousedown_link = d;
+ selected_link = mousedown_link;
+ let updateTooltip = function () {
+ $timeout(function () {
+ $scope.trustedpopoverContent = $sce.trustAsHtml(connectionPopupHTML(d, QDRService));
+ if (selected_link)
+ displayTooltip(event);
+ });
+ };
+ // update the contents of the popup tooltip each time the data is polled
+ QDRService.management.topology.addUpdatedAction('connectionPopupHTML', updateTooltip);
+ QDRService.management.topology.ensureAllEntities(
+ [{ entity: 'router.link', force: true},{entity: 'connection'}], function () {
+ updateTooltip();
+ });
+ // show the tooltip
+ updateTooltip();
+ restart();
+
+ })
+ .on('mouseout', function() { // mouse out of a path
+ QDRService.management.topology.delUpdatedAction('connectionPopupHTML');
+ d3.select('#popover-div')
+ .style('display', 'none');
+ selected_link = null;
+ restart();
+ })
+ // left click a path
+ .on('click', function () {
+ d3.event.stopPropagation();
+ clearPopups();
+ });
+ // remove old links
+ path.exit().remove();
- // circle (node) group
- // nodes are known by id
- circle = circle.data(nodes, function(d) {
- return d.name;
+ // circle (node) group
+ // nodes are known by id
+ circle = circle.data(nodes.nodes, function(d) {
+ return d.name;
+ });
+
+ // 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 & 1;
});
- // 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 & 1;
+ // 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; });
+
+ appendCircle(g)
+ .on('mouseover', function(d) { // mouseover a circle
+ QDRService.management.topology.delUpdatedAction('connectionPopupHTML');
+ if (d.nodeType === 'normal') {
+ showClientTooltip(d, d3.event);
+ } else
+ showRouterTooltip(d, d3.event);
+ if (d === mousedown_node)
+ return;
+ // enlarge target node
+ 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();
});
-
- // 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; });
-
- appendCircle(g)
- .on('mouseover', function(d) { // mouseover a circle
- QDRService.management.topology.delUpdatedAction('connectionPopupHTML');
- if (d.nodeType === 'normal') {
- showClientTooltip(d, d3.event);
- } else
- showRouterTooltip(d, d3.event);
- if (d === mousedown_node)
- return;
- // enlarge target node
- d3.select(this).attr('transform', 'scale(1.1)');
- if (!selected_node) {
- return;
- }
- // highlight the next-hop route from the selected node to this node
- clerAllHighlights();
- // 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
- nextHop(selected_node, d);
- restart();
- });
- })
- .on('mouseout', function() { // mouse out for a circle
- // unenlarge target node
- d3.select('#popover-div')
- .style('display', 'none');
- d3.select(this).attr('transform', '');
- clerAllHighlights();
- mouseover_node = null;
+ })
+ .on('mouseout', function() { // mouse out for a circle
+ // unenlarge target node
+ 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
+ if (d3.event.button !== 0) { // ignore all but left button
+ return;
+ }
+ mousedown_node = d;
+ // mouse position relative to svg
+ initial_mouse_down_position = d3.mouse(this.parentNode.parentNode.parentNode).slice();
+ })
+ .on('mouseup', function(d) { // mouse up for circle
+ if (!mousedown_node)
+ return;
+
+ selected_link = null;
+ // unenlarge target node
+ d3.select(this).attr('transform', '');
+
+ // check for drag
+ mouseup_node = d;
+
+ let mySvg = this.parentNode.parentNode.parentNode;
+ // 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.fixed = true;
+ nodes.setNodesFixed(d.name, true);
+ resetMouseVars();
restart();
- })
- .on('mousedown', function(d) { // mouse down for circle
- if (d3.event.button !== 0) { // ignore all but left button
- return;
- }
- mousedown_node = d;
- // mouse position relative to svg
- initial_mouse_down_position = d3.mouse(this.parentNode.parentNode.parentNode).slice();
- })
- .on('mouseup', function(d) { // mouse up for circle
- if (!mousedown_node)
- return;
-
- selected_link = null;
- // unenlarge target node
- d3.select(this).attr('transform', '');
-
- // check for drag
- mouseup_node = d;
-
- let mySvg = this.parentNode.parentNode.parentNode;
- // 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.fixed = true;
- setNodesFixed(d.name, true);
- resetMouseVars();
- restart();
- return;
- }
+ return;
+ }
- // if this node was selected, unselect it
- if (mousedown_node === selected_node) {
- selected_node = null;
- } else {
- if (d.nodeType !== 'normal' && d.nodeType !== 'on-demand')
- selected_node = mousedown_node;
- }
- clerAllHighlights();
- mousedown_node = null;
- if (!$scope.$$phase) $scope.$apply();
- restart(false);
+ // if this node was selected, unselect it
+ if (mousedown_node === selected_node) {
+ selected_node = null;
+ } else {
+ if (d.nodeType !== 'normal' && d.nodeType !== 'on-demand')
+ selected_node = mousedown_node;
+ }
+ clearAllHighlights();
+ mousedown_node = null;
+ if (!$scope.$$phase) $scope.$apply();
+ restart(false);
- })
- .on('dblclick', function(d) { // circle
- if (d.fixed) {
- d.fixed = false;
- setNodesFixed(d.name, false);
- restart(); // redraw the node without a dashed line
- force.start(); // let the nodes move to a new position
- }
- })
- .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 () {
- $scope.contextNode = d;
+ })
+ .on('dblclick', function(d) { // circle
+ if (d.fixed) {
+ d.fixed = false;
+ nodes.setNodesFixed(d.name, false);
+ restart(); // redraw the node without a dashed line
+ force.start(); // let the nodes move to a new position
+ }
+ })
+ .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'
});
- })
- .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';
- if (QDR.isStandalone)
- window.location = $location.protocol() + '://localhost:8161/hawtio' + artemisPath;
- else
- $location.path(artemisPath);
- }
- return;
- }
- d3.event.stopPropagation();
+ $timeout( function () {
+ $scope.contextNode = d;
});
-
- appendContent(g);
- //appendTitle(g);
-
- // 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) {
- let g = d3.select(this);
- g.append('svg:text')
- .attr('x', radiusNormal + 3)
- .attr('y', Math.floor(radiusNormal / 2))
- .attr('class', 'subtext')
- .text('x ' + d.normals.length);
+ })
+ .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;
+ }
+ return;
+ }
+ d3.event.stopPropagation();
});
- // 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 (!mousedown_node || !selected_node)
- return;
+ appendContent(g);
+ //appendTitle(g);
- if (!start)
- return;
- // set the graph in motion
- //QDR.log.debug("mousedown_node is " + mousedown_node);
- force.start();
+ // remove old nodes
+ circle.exit().remove();
- }
- let createLegend = function () {
- // dynamically create the legend based on which node types are present
- // the legend
- 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(' + (radii['inter-router'] + 2) + ',' + (radii['inter-router'] + 2) + ')')
- .selectAll('g');
- let legendNodes = [];
- legendNodes.push(aNode('Router', '', 'inter-router', '', undefined, 0, 0, 0, 0, false, {}));
-
- if (!svg.selectAll('circle.console').empty()) {
- legendNodes.push(aNode('Console', '', 'normal', '', undefined, 1, 0, 0, 0, false, {
- console_identifier: 'Dispatch console'
- }));
- }
- if (!svg.selectAll('circle.client.in').empty()) {
- let node = aNode('Sender', '', 'normal', '', undefined, 2, 0, 0, 0, false, {});
- node.cdir = 'in';
- legendNodes.push(node);
- }
- if (!svg.selectAll('circle.client.out').empty()) {
- let node = aNode('Receiver', '', 'normal', '', undefined, 3, 0, 0, 0, false, {});
- node.cdir = 'out';
- legendNodes.push(node);
- }
- if (!svg.selectAll('circle.client.inout').empty()) {
- let node = aNode('Sender/Receiver', '', 'normal', '', undefined, 4, 0, 0, 0, false, {});
- node.cdir = 'both';
- legendNodes.push(node);
- }
- if (!svg.selectAll('circle.qpid-cpp').empty()) {
- legendNodes.push(aNode('Qpid broker', '', 'route-container', '', undefined, 5, 0, 0, 0, false, {
- product: 'qpid-cpp'
- }));
- }
- if (!svg.selectAll('circle.artemis').empty()) {
- legendNodes.push(aNode('Artemis broker', '', 'route-container', '', undefined, 6, 0, 0, 0, false,
- {product: 'apache-activemq-artemis'}));
- }
- if (!svg.selectAll('circle.route-container').empty()) {
- legendNodes.push(aNode('Service', '', 'route-container', 'external-service', undefined, 7, 0, 0, 0, false,
- {product: ' External Service'}));
- }
- lsvg = lsvg.data(legendNodes, function(d) {
- return d.key;
- });
- let lg = lsvg.enter().append('svg:g')
- .attr('transform', function(d, i) {
- // 45px between lines and add 10px space after 1st line
- return 'translate(0, ' + (45 * i + (i > 0 ? 10 : 0)) + ')';
- });
-
- 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 radii[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 & 1;
- })
- .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';
- })
- .classed('on-demand', function(d) {
- return d.nodeType == 'on-demand';
- })
- .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
+ // 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) {
+ let g = d3.select(this);
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;
- 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('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
- return d.name.length > 7 ? d.name.substr(0, 6) + '...' : d.name;
- });
- };
- let appendTitle = function(g) {
- g.append('svg:title').text(function(d) {
- return generateTitle(d);
- });
- };
+ .attr('x', radiusNormal + 3)
+ .attr('y', Math.floor(radiusNormal / 2))
+ .attr('class', 'subtext')
+ .text('x ' + 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);
- let generateTitle = function (d) {
- let x = '';
- if (d.normals && d.normals.length > 1)
- x = ' x ' + d.normals.length;
- if (QDRService.utilities.isConsole(d))
- return 'Dispatch console' + x;
- else if (QDRService.utilities.isArtemis(d))
- return 'Broker - Artemis' + x;
- else if (d.properties.product == 'qpid-cpp')
- return 'Broker - qpid-cpp' + x;
- else if (d.cdir === 'in')
- return 'Sender' + x;
- else if (d.cdir === 'out')
- return 'Receiver' + x;
- else if (d.cdir === 'both')
- return 'Sender/Receiver' + x;
- else if (d.nodeType === 'normal')
- return 'client' + x;
- else if (d.nodeType === 'on-demand')
- return 'broker';
- else if (d.properties.product) {
- return d.properties.product;
- }
- else {
- return '';
- }
- };
+ if (!mousedown_node || !selected_node)
+ return;
- let showClientTooltip = function (d, event) {
- let type = generateTitle(d);
- let title = '<table class="popupTable"><tr><td>Type</td><td>' + type + '</td></tr>';
- if (!d.normals || d.normals.length < 2)
- title += ('<tr><td>Host</td><td>' + d.host + '</td></tr>');
- title += '</table>';
- showToolTip(title, event);
- };
+ if (!start)
+ return;
+ // set the graph in motion
+ //QDRLog.debug("mousedown_node is " + mousedown_node);
+ force.start();
- let showRouterTooltip = function (d, event) {
- QDRService.management.topology.ensureEntities(d.key, [
- {entity: 'listener', attrs: ['role', 'port', 'http']},
- {entity: 'router', attrs: ['name', 'version', 'hostName']}
- ], function () {
- // update all the router title text
- let nodes = QDRService.management.topology.nodeInfo();
- let node = nodes[d.key];
- let listeners = node['listener'];
- let router = node['router'];
- let r = QDRService.utilities.flatten(router.attributeNames, router.results[0]);
- let title = '<table class="popupTable">';
- title += ('<tr><td>Router</td><td>' + r.name + '</td></tr>');
- if (r.hostName)
- title += ('<tr><td>Host Name</td><td>' + r.hostHame + '</td></tr>');
- title += ('<tr><td>Version</td><td>' + r.version + '</td></tr>');
- let ports = [];
- for (let l=0; l<listeners.results.length; l++) {
- let listener = QDRService.utilities.flatten(listeners.attributeNames, listeners.results[l]);
- if (listener.role === 'normal') {
- ports.push(listener.port+'');
- }
- }
- if (ports.length > 0) {
- title += ('<tr><td>Ports</td><td>' + ports.join(', ') + '</td></tr>');
- }
- title += '</table>';
- showToolTip(title, event);
+ }
+ let createLegend = function () {
+ // dynamically create the legend based on which node types are present
+ // the legend
+ 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(${(radii['inter-router'] + 2)},${(radii['inter-router'] + 2)})`)
+ .selectAll('g');
+ let legendNodes = new Nodes(QDRService, QDRLog);
+ legendNodes.addUsing('Router', '', 'inter-router', '', undefined, 0, 0, 0, 0, false, {});
+
+ if (!svg.selectAll('circle.console').empty()) {
+ legendNodes.addUsing('Console', 'Console', 'normal', '', undefined, 0, 0, 1, 0, false, {
+ console_identifier: 'Dispatch console'
});
- };
- let showToolTip = function (title, event) {
- // show the tooltip
- $timeout ( function () {
- $scope.trustedpopoverContent = $sce.trustAsHtml(title);
- displayTooltip(event);
+ }
+ if (!svg.selectAll('circle.client.in').empty()) {
+ legendNodes.addUsing('Sender', 'Sender', 'normal', '', undefined, 0, 0, 2, 0, false, {}).cdir = 'in';
+ }
+ if (!svg.selectAll('circle.client.out').empty()) {
+ legendNodes.addUsing('Receiver', 'Receiver', 'normal', '', undefined, 0, 0, 3, 0, false, {}).cdir = 'out';
+ }
+ if (!svg.selectAll('circle.client.inout').empty()) {
+ legendNodes.addUsing('Sender/Receiver', 'Sender/Receiver', 'normal', '', undefined, 0, 0, 4, 0, false, {}).cdir = 'both';
+ }
+ if (!svg.selectAll('circle.qpid-cpp').empty()) {
+ legendNodes.addUsing('Qpid broker', 'Qpid broker', 'route-container', '', undefined, 0, 0, 5, 0, false, {
+ product: 'qpid-cpp'
});
- };
-
- 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'));
+ }
+ if (!svg.selectAll('circle.artemis').empty()) {
+ legendNodes.addUsing('Artemis broker', 'Artemis broker', 'route-container', '', undefined, 0, 0, 6, 0, false,
+ {product: 'apache-activemq-artemis'});
+ }
+ if (!svg.selectAll('circle.route-container').empty()) {
+ legendNodes.addUsing('Service', 'Service', 'route-container', 'external-service', undefined, 0, 0, 7, 0, false,
+ {product: ' External Service'});
+ }
+ lsvg = lsvg.data(legendNodes.nodes, function(d) {
+ return d.key;
+ });
+ let lg = lsvg.enter().append('svg:g')
+ .attr('transform', function(d, i) {
+ // 45px between lines and add 10px space after 1st line
+ return 'translate(0, ' + (45 * i + (i > 0 ? 10 : 0)) + ')';
});
- };
- function nextHop(thisNode, d, cb) {
- if ((thisNode) && (thisNode != d)) {
- let target = findNextHopNode(thisNode, d);
- //QDR.log.debug("highlight link from node ");
- //console.dump(nodeFor(selected_node.name));
- //console.dump(target);
- if (target) {
- let hnode = nodeFor(thisNode.name);
- let hlLink = linkFor(hnode, target);
- //QDR.log.debug("need to highlight");
- //console.dump(hlLink);
- if (hlLink) {
- if (cb) {
- cb(hlLink, hnode, target);
- } else {
- hlLink['highlighted'] = true;
- hnode['highlighted'] = true;
- }
- }
- else
- target = null;
- }
- nextHop(target, d, cb);
- }
- if (thisNode == d && !cb) {
- let hnode = nodeFor(thisNode.name);
- hnode['highlighted'] = true;
+ 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';
}
-
- 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 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 radii[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 & 1;
+ })
+ .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';
+ })
+ .classed('on-demand', function(d) {
+ return d.nodeType == 'on-demand';
+ })
+ .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 routers = nodes.filter( function (node) {
- return node.nodeType === 'inter-router';
+ };
+ 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;
+ 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('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
+ return d.name.length > 7 ? d.name.substr(0, 6) + '...' : d.name;
});
- if (routers.length > cnodes.length) {
- return -1;
- }
-
-
- if (cnodes.length != Object.keys(savedKeys).length) {
- 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++) {
- let key = cnodes[i];
- // if this node isn't in the saved node list
- if (!savedKeys.hasOwnProperty(key))
- return 1;
- // if the number of connections for this node chaanged
- if (!nodeInfo[key]['connection'])
- return -1;
- if (nodeInfo[key]['connection'].results.length != savedKeys[key]) {
- return -1;
+ };
+ let appendTitle = function(g) {
+ g.append('svg:title').text(function(d) {
+ return generateTitle(d);
+ });
+ };
+
+ let generateTitle = function (d) {
+ let x = '';
+ if (d.normals && d.normals.length > 1)
+ x = ' x ' + d.normals.length;
+ if (QDRService.utilities.isConsole(d))
+ return 'Dispatch console' + x;
+ else if (QDRService.utilities.isArtemis(d))
+ return 'Broker - Artemis' + x;
+ else if (d.properties.product == 'qpid-cpp')
+ return 'Broker - qpid-cpp' + x;
+ else if (d.cdir === 'in')
+ return 'Sender' + x;
+ else if (d.cdir === 'out')
+ return 'Receiver' + x;
+ else if (d.cdir === 'both')
+ return 'Sender/Receiver' + x;
+ else if (d.nodeType === 'normal')
+ return 'client' + x;
+ else if (d.nodeType === 'on-demand')
+ return 'broker';
+ else if (d.properties.product) {
+ return d.properties.product;
+ }
+ else {
+ return '';
+ }
+ };
+
+ let showClientTooltip = function (d, event) {
+ let type = generateTitle(d);
+ let title = `<table class="popupTable"><tr><td>Type</td><td>${type}</td></tr>`;
+ if (!d.normals || d.normals.length < 2)
+ title += ('<tr><td>Host</td><td>' + d.host + '</td></tr>');
+ title += '</table>';
+ showToolTip(title, event);
+ };
+
+ let showRouterTooltip = function (d, event) {
+ QDRService.management.topology.ensureEntities(d.key, [
+ {entity: 'listener', attrs: ['role', 'port', 'http']},
+ {entity: 'router', attrs: ['name', 'version', 'hostName']}
+ ], function () {
+ // update all the router title text
+ let nodes = QDRService.management.topology.nodeInfo();
+ let node = nodes[d.key];
+ let listeners = node['listener'];
+ let router = node['router'];
+ let r = QDRService.utilities.flatten(router.attributeNames, router.results[0]);
+ let title = '<table class="popupTable">';
+ title += ('<tr><td>Router</td><td>' + r.name + '</td></tr>');
+ if (r.hostName)
+ title += ('<tr><td>Host Name</td><td>' + r.hostHame + '</td></tr>');
+ title += ('<tr><td>Version</td><td>' + r.version + '</td></tr>');
+ let ports = [];
+ for (let l=0; l<listeners.results.length; l++) {
+ let listener = QDRService.utilities.flatten(listeners.attributeNames, listeners.results[l]);
+ if (listener.role === 'normal') {
+ ports.push(listener.port+'');
}
}
- return 0;
- }
-
- function saveChanged() {
- savedKeys = {};
- 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 (ports.length > 0) {
+ title += ('<tr><td>Ports</td><td>' + ports.join(', ') + '</td></tr>');
}
- }
- // we are about to leave the page, save the node positions
- $rootScope.$on('$locationChangeStart', function() {
- //QDR.log.debug("locationChangeStart");
- savePositions();
+ title += '</table>';
+ showToolTip(title, event);
});
- // When the DOM element is removed from the page,
- // AngularJS will trigger the $destroy event on
- // the scope
- $scope.$on('$destroy', function() {
- //QDR.log.debug("scope on destroy");
- savePositions();
- QDRService.management.topology.setUpdateEntities([]);
- QDRService.management.topology.stopUpdating();
- QDRService.management.topology.delUpdatedAction('normalsStats');
- QDRService.management.topology.delUpdatedAction('topology');
- QDRService.management.topology.delUpdatedAction('connectionPopupHTML');
-
- d3.select('#SVG_ID').remove();
- window.removeEventListener('resize', resize);
- traffic.stop();
+ };
+ let showToolTip = function (title, event) {
+ // show the tooltip
+ $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'));
+ });
+ };
+
+ 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 routers = nodes.nodes.filter( function (node) {
+ return node.nodeType === 'inter-router';
+ });
+ if (routers.length > cnodes.length) {
+ return -1;
+ }
- function handleInitialUpdate() {
- // we only need to update connections during steady-state
- QDRService.management.topology.setUpdateEntities(['connection']);
- // we currently have all entities available on all routers
- save
<TRUNCATED>
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org