http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/d98ae37e/console/hawtio/src/main/webapp/plugin/js/qdrChartService.js ---------------------------------------------------------------------- diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrChartService.js b/console/hawtio/src/main/webapp/plugin/js/qdrChartService.js deleted file mode 100644 index 47cb47d..0000000 --- a/console/hawtio/src/main/webapp/plugin/js/qdrChartService.js +++ /dev/null @@ -1,1111 +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. -*/ -/** - * @module QDR - */ -var QDR = (function(QDR) { - - // The QDR chart service handles periodic gathering data for charts and displaying the charts - QDR.module.factory("QDRChartService", ['$rootScope', 'QDRService', '$http', '$resource', '$location', - function($rootScope, QDRService, $http, $resource, $location) { - - var instance = 0; // counter for chart instances - var bases = []; - var findBase = function (name, attr, request) { - for (var i=0; i<bases.length; ++i) { - var 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)); - } - }; - - // 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) { - - var base = findBase(opts.name, opts.attr, request); - if (!base) { - base = new ChartBase(opts.name, opts.attr, request); - bases.push(base); - } - 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 = "#cbe7f3"; // 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 : 10; // number of minutes of data to show (<= base.duration) - this.userTitle = null; // user title overrides title() - - // generate a unique id for this chart - this.id = function () { - var name = this.name() - var nameparts = name.split('/'); - if (nameparts.length == 2) - name = nameparts[1]; - var key = QDRService.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.title = function (_) { - var name = this.request().aggregate ? 'Aggregate' : QDRService.nameFromId(this.nodeId()); - var computed = name + - " " + QDRService.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 () { - var 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 - // copy the savable properties to an object - - 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) { - var 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 () { - var attrs = {} - Object.keys(this.datum).forEach( function (name) { - Object.keys(this.datum[name]).forEach( function (attr) { - attrs[attr] = 1; - }) - }, this) - return Object.keys(attrs); - } - }; - - // Below here are the properties and methods available on QDRChartService - var 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(); - }, - - findChartRequest: function (nodeId, entity, aggregate) { - var 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 (var i=0; i<self.chartRequests.length; ++i) { - var 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) { - var foundBases = 0; - for (var i=0; i<self.charts.length; ++i) { - var c = self.charts[i]; - if (c.base === chart.base) - ++foundBases; - if (c.equals(chart)) { - self.charts.splice(i, 1); - if (chart.dashboard) - self.saveCharts(); - } - } - if (foundBases == 1) { - var baseIndex = bases.indexOf(chart.base) - bases.splice(baseIndex, 1); - } - }, - - registerChart: function (opts) { //nodeId, entity, name, attr, interval, instance, forceCreate, aggregate, hdash) { - var 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 = new ChartRequest(opts); //nodeId, entity, name, attr, interval, aggregate); - self.chartRequests.push(request); - self.startCollecting(request); - } - var charts = self.findCharts(opts); //name, attr, nodeId, entity, hdash); - var 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) { - // 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 (var i=0; i<self.charts.length; ++i) { - var c = self.charts[i]; - if (chart.equals(c)) { - var request = chart.request(); - self.delChart(chart); - if (request) { - // see if any other charts use this attr - for (var i=0; i<self.charts.length; ++i) { - var c = self.charts[i]; - if (c.attr() == chart.attr() && c.request().equals(chart.request())) - return; - } - // no other charts use this attr, so remove it - if (request.removeAttr(chart.name(), chart.attr()) == 0) { - self.stopCollecting(request); - self.delChartRequest(request); - } - } - } - } - self.saveCharts(); - - }, - - stopCollecting: function (request) { - if (request.setTimeoutHandle) { - clearTimeout(request.setTimeoutHandle); - request.setTimeoutHandle = null; - } - }, - - startCollecting: function (request) { - // Using setTimeout instead of setInterval because the response may take longer than interval - request.setTimeoutHandle = setTimeout(self.sendChartRequest, request.interval, request); - }, - shouldRequest: function (request) { - // 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, once) { - if (!once && !self.shouldRequest(request)) { - request.setTimeoutHandle = setTimeout(self.sendChartRequest, request.interval, request) - return; - } - - // ensure the response has the name field so we can associate the response values with the correct chart - var attrs = request.attrs(); - attrs.push("name"); - - // this is called when the response is received - var saveResponse = function (nodeId, entity, response) { - if (!response || !response.attributeNames) - return; - //QDR.log.debug("got chart results for " + nodeId + " " + entity); - // records is an array that has data for all names - var records = response.results; - if (!records) - return; - - var now = new Date(); - var cutOff = new Date(now.getTime() - request.duration * 60 * 1000); - // index of the "name" attr in the response - var nameIndex = response.attributeNames.indexOf("name"); - if (nameIndex < 0) - return; - - var names = request.names(); - // for each record returned, find the name/attr for this request and save the data with this timestamp - for (var i=0; i<records.length; ++i) { - var 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) { - var data = request.data(name, attr) // get a reference to the data array - if (data) { - var attrIndex = response.attributeNames.indexOf(attr) - 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(); - } - } - }) - } - } - } - if (request.aggregate) { - var nodeList = QDRService.nodeIdList() - QDRService.getMultipleNodeInfo(nodeList, request.entity, attrs, saveResponse, request.nodeId); - } else { - QDRService.getNodeInfo(request.nodeId, request.entity, attrs, saveResponse); - } - // it is now safe to schedule another request - if (once) - return; - request.setTimeoutHandle = setTimeout(self.sendChartRequest, request.interval, request) - }, - - numCharts: function () { - return self.charts.filter( function (chart) { return chart.dashboard }).length; - //return self.charts.length; - }, - - isAttrCharted: function (nodeId, entity, name, attr) { - var 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) }); - }, - - addHDash: function (chart) { - chart.hdash = true; - self.saveCharts(); - /* - if (!chart.hdash) { - var dashChart = self.registerChart(chart.nodeId(), chart.entity(), - chart.name(), chart.attr(), chart.interval(), true, chart.aggregate(), true); - dashChart.dashboard = true; - dashChart.hdash = false; - chart.dashboard = false; - 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 () { - var charts = []; - var minCharts = []; - - self.charts.forEach(function (chart) { - var 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 () { - var charts = angular.fromJson(localStorage["QDRCharts"]); - if (charts) { - var nodeList = QDRService.nodeList().map( function (node) { - return node.id; - }) - charts.forEach(function (chart) { - if (nodeList.indexOf(chart.nodeId) >= 0) { - if (!chart.interval) - chart.interval = 1000; - if (!chart.duration) - chart.duration = 10; - if (chart.nodeList) - chart.aggregate = true; - var newChart = self.registerChart(chart.nodeId, chart.entity, chart.name, chart.attr, chart.interval, true, chart.aggregate); - newChart.dashboard = true; // we only save the dashboard charts - newChart.type = chart.type; - newChart.rateWindow = chart.rateWindow; - newChart.areaColor = chart.areaColor ? chart.areaColor : "#c0e0ff"; - newChart.lineColor = chart.lineColor ? chart.lineColor : "#4682b4"; - newChart.duration(chart.duration); - newChart.visibleDuration = chart.visibleDuration ? chart.visibleDuration : 10; - if (chart.userTitle) - newChart.title(chart.userTitle); - } - }) - } - }, - loadCharts: function () { - var charts = angular.fromJson(localStorage["QDRCharts"]); - if (charts) { - // get array of known ids - var nodeList = QDRService.nodeList().map( function (node) { - return node.id; - }) - 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 = 10; - 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; - var 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 : "#cbe7f3"; - newChart.lineColor = chart.lineColor ? chart.lineColor : "#058dc7"; - newChart.duration(chart.duration); - newChart.visibleDuration = chart.visibleDuration ? chart.visibleDuration : 10; - if (chart.userTitle) - newChart.title(chart.userTitle); - } - }) - } - }, - - AreaChart: function (chart) { - if (!chart) - return; - - // if this is an aggregate chart, show it stacked - var stacked = chart.request().aggregate; - this.chart = chart; // reference to underlying chart - this.svgchart = null; - this.url = $location.absUrl(); - - // callback function. called by svgchart when binding data - // the variable 'this' refers to the svg and not the AreaChart, - // but since we are still in the scope of the AreaChart we have access to the passed in chart argument - this.chartData = function () { - - var now = new Date(); - var visibleDate = new Date(now.getTime() - chart.visibleDuration * 60 * 1000); - var data = chart.data(); - var nodeList = QDRService.nodeIdList(); - - if (chart.type == "rate") { - var rateData = []; - var datalen = data.length; - k = 0; // inner loop optimization - for (var i=0; i<datalen; ++i) { - var d = data[i]; - if (d[0] >= visibleDate) { - for (var j=k+1; j<datalen; ++j) { - var d1 = data[j]; - if (d1[0] - d[0] >= chart.rateWindow) { // rateWindow is the timespan to calculate rates - var elapsed = Math.max((d1[0] - d[0]) / 1000, 1); // number of seconds that elapsed - var rd = [d1[0],(d1[1] - d[1])/elapsed] - k = j; // start here next time - // this is a stacked (aggregate) chart - if (stacked) { - var detail = []; - nodeList.forEach( function (node, nodeIndex) { - if (d1[2][nodeIndex] && d[2][nodeIndex]) - detail.push({node: QDRService.nameFromId(node), val: (d1[2][nodeIndex].val- d[2][nodeIndex].val)/elapsed}) - }) - rd.push(detail) - } - rateData.push(rd); - break; - } - } - } - } - // we need at least a point to chart - if (rateData.length == 0) { - rateData[0] = [chart.data()[0][0],0,[{node:'',val:0}]]; - } - return rateData; - } - if (chart.visibleDuration != chart.duration()) { - return data.filter(function (d) { return d[0]>=visibleDate}); - } else - return data; - } - - this.zoom = function (id, zoom) { - if (this.svgchart) { - this.svgchart.attr("zoom", zoom) - d3.select('#' + id) - .data([this.chartData()]) - .call(this.svgchart) - } - } - - // called by the controller on the page that displays the chart - // called whenever the controller wants to redraw the chart - // note: the data is collected independently of how often the chart is redrawn - this.tick = function (id) { - - // can't draw charts that don't have data yet - if (this.chart.data().length == 0) { - return; - } - - // if we haven't created the svg yet - if (!this.svgchart) { - - // make sure the dom element exists on the page - var div = angular.element('#' + id); - if (!div) - return; - - var width = div.width(); - var height = div.height(); - - // make sure the dom element has a size. otherwise we wouldn't see anything anyway - if (!width) - return; - - var tooltipGenerator; - // stacked charts have a different tooltip - if (stacked) { - tooltipGenerator = function (d, color, format) { - var html = "<table class='fo-table'><tbody><tr class='fo-title'>"+ - "<td align='center' colspan='2' nowrap>Time: "+d[0].toTimeString().substring(0, 8)+"</td></tr>" - d[2].forEach( function (detail) { - html += "<tr class='detail'><td align='right' nowrap>" - + detail.node - + "<div class='fo-table-legend' style='background-color: "+color(detail.node)+"'></div>" - + "</td><td>"+format(detail.val)+"</td></tr>" - }) - html += "</tbody></table>" - return html; - } - } else { - tooltipGenerator = function (d, color, format) { - var html = "<table class='fo-table'><tbody><tr class='fo-title'>"+ - "<td align='center'>Time</td><td align='center'>Value</td></tr><tr><td>" + - d[0].toTimeString().substring(0, 8) + - "</td><td>" + - format(d[1]) + - "</td></tr></tbody></table>" - return html; - } - } - // create and initialize the chart - this.svgchart = self.timeSeriesStackedChart(id, width, height, - QDRService.humanify(this.chart.attr()), - this.chart.name(), - QDRService.nameFromId(this.chart.nodeId()), - this.chart.entity(), - stacked, - this.chart.visibleDuration) - .tooltipGenerator(tooltipGenerator); - - } - // in case the chart properties have changed, set the new props - this.svgchart - .attr("type", this.chart.type) - .attr("areaColor", this.chart.areaColor) - .attr("lineColor", this.chart.lineColor) - .attr("url", this.url) - .attr("title", this.chart.userTitle); - - // bind the new data and update the chart - d3.select('#' + id) // the div id on the page/dialog - .data([this.chartData()]) - .call(this.svgchart); // the charting function - } - }, - - timeSeriesStackedChart: function (id, width, height, attrName, name, node, entity, stacked, visibleDuration) { - var margin = {top: 20, right: 18, bottom: 10, left: 15} - // attrs that can be changed after the chart is created by using - // chart.attr(<attrname>, <attrvalue>); - var attrs = { - attrName: attrName, // like Deliveries to Container. Put at top of chart - name: name, // like router.address/qdrhello Put at bottom of chart with node - node: node, // put at bottom of chart with name - entity: entity, // like .router.address Not used atm - title: "", // user title overrides the node and name at the bottom of the chart - url: "", // needed to reference filters and clip because of angular's location service - type: "value", // value or rate - areaColor: "", // can be set for non-stacked charts - lineColor: "", // can be set for non-stacked charts - zoom: false, // should the y-axis range start at 0 or the min data value - visibleDuration: visibleDuration - } - var width = width - margin.left - margin.right, - height = height - margin.top - margin.bottom, - yAxisTransitionDuration = 0 - - var x = d3.time.scale() - var y = d3.scale.linear() - .rangeRound([height, 0]); - // The x-accessor for the path generator; xScale * xValue. - var X = function (d) { return x(d[0]) } - // The x-accessor for the path generator; yScale * yValue. - var Y = function Y(d) { return y(d[1]) } - - var xAxis = d3.svg.axis().scale(x).orient("bottom") - .outerTickSize(6) - .innerTickSize(-(height-margin.top-margin.bottom)) - .tickPadding(2) - .ticks(d3.time.minutes, 2) - var yAxis = d3.svg.axis().scale(y).orient("right") - .outerTickSize(8) - .innerTickSize(-(width-margin.left-margin.right)) - .tickPadding(10) - .ticks(3) - .tickFormat(function(d) { return formatValue(d)}) - - var tooltipGenerator = function (d, color, format) {return ""}; // should be overridden to set an appropriate tooltip - var formatValue = d3.format(".2s"); - var formatPrecise = d3.format(","); - var bisectDate = d3.bisector(function(d) { return d[0]; }).left; - var line = d3.svg.line(); - - var stack = d3.layout.stack() - .offset("zero") - .values(function (d) { return d.values; }) - .x(function (d) { return x(d.date); }) - .y(function (d) { return d.value; }); - - var area = d3.svg.area() - if (stacked) { - area.interpolate("cardinal") - .x(function (d) { return x(d.date); }) - .y0(function (d) { return y(d.y0); }) - .y1(function (d) { return y(d.y0 + d.y); }); - } else { - area.interpolate("basis").x(X).y1(Y) - line.x(X).y(Y) - } - var color = d3.scale.category20(); - - var sv = d3.select("#"+id).append("svg") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) - var svg = sv - .append("g") - .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); - - sv.append("linearGradient") - .attr("id", id) //"temperature-gradient") - .attr("gradientUnits", "userSpaceOnUse") - .attr("x1", 0).attr("y1", height *.5 ) - .attr("x2", 0).attr("y2", height * 1.2) - .selectAll("stop") - .data([ - {offset: "0%", opacity: 1}, - {offset: "100%", opacity: 0} - ]) - .enter().append("stop") - .attr("offset", function(d) { return d.offset; }) - .attr("stop-opacity", function(d) { return d.opacity; }) - .attr("stop-color", function(d) { return "#cbe7f3" }); -/* - var clip = svg.append("defs").append("svg:clipPath") - .attr("id", "clip") - .append("svg:rect") - .attr("id", "clip-rect") - .attr("x", "0") - .attr("y", "0") - .attr("width", width) - .attr("height", height); -*/ - // we want all our areas to appear before the axiis - svg.append("g") - .attr("class", "section-container") - - svg.append("g") - .attr("class", "x axis") - - svg.append("g") - .attr("class", "y axis") - - svg.append("text").attr("class", "title") - .attr("x", (width / 2) - (margin.left + margin.right) / 2) - .attr("y", 0 - (margin.top / 2)) - .attr("text-anchor", "middle") - .text(attrs.attrName); - - svg.append("text").attr("class", "legend") - .attr("x", (width / 2) - (margin.left + margin.right) / 2) - .attr("y", height + (margin.bottom / 2) ) - .attr("text-anchor", "middle") - .text(!stacked ? attrs.node + " " + attrs.name : attrs.name); - - var focus = sv.append("g") - .attr("class", "focus") - .style("display", "none"); - - focus.append("circle") - .attr("r", 4.5); - - var focusg = focus.append("g"); - focusg.append("rect") - .attr("class", "mo-guide y") - .attr("width", 1) - .attr("height", height - (margin.top + margin.bottom)); - focusg.append("rect") - .attr("class", "mo-guide x") - .attr("width", width - (margin.left + margin.right)) - .attr("height", 1); - focus.append("foreignObject") - .attr('class', 'svg-tooltip') - .append("xhtml:span"); -/* - var transition = d3.select({}).transition() - .duration(2000) - .ease("linear"); -*/ - function chart(selection) { - selection.each(function(data) { - - var seriesArr = [] - if (stacked) { - var detailNames = data[0][2].map(function (detail){ return detail.node }) - var revNames = angular.copy(detailNames).reverse(); - color.domain(revNames); - - var series = {}; - detailNames.forEach(function (name) { - series[name] = {name: name, values:[]}; - seriesArr.unshift(series[name]); // insert at beginning - }); - - data.forEach(function (d) { - detailNames.map(function (name, i) { - series[name].values.push({date: d[0], value: d[2][i] ? d[2][i].val : 0}); - }); - }); - - // this decorates seriesArr with x,y,and y0 properties - stack(seriesArr); - } - - var extent = d3.extent(data, function(d) {return d[0];}); - //var points = data.length; - //var futureDate = new Date(data[points-1][0].getTime() - attrs.visibleDuration * 60 * 1000); - //extent = [futureDate, data[points-1][0]] - x.domain(extent) - .range([0, width - margin.left - margin.right]); - - // Update the y-scale. - var min = attrs.zoom ? 0 : d3.min(data, function(d) {return d[1]}) *.99; - var max = d3.max(data, function(d) {return d[1]}) * 1.01; - var mean = d3.mean(data, function(d) {return d[1]}); - //max = max * 1.01; - var diff = (max - min); - if (diff == 0) { - max = max + 1; - diff = 1; - } - var ratio = mean != 0 ? diff / mean : 1; - if (ratio < .05) - formatValue = d3.format(".3s") - - if (stacked) { - y.domain([min, max]) - .range([height - margin.top - margin.bottom, 0]); - } else { - y - .domain([min, max]) - .range([height - margin.top - margin.bottom, 0]); - } - if (attrs.type == "rate") { - area.interpolate("basis"); // rate charts look better smoothed - line.interpolate("basis"); - } - else { - area.interpolate("linear"); // don't smooth value charts - line.interpolate("linear"); - } - - // adjust the xaxis based on the range of x values (domain) - var timeSpan = (extent[1] - extent[0]) / (1000 * 60); // number of minutes - if (timeSpan < 1.5) - xAxis.ticks(d3.time.seconds, 10); - else if (timeSpan < 3) - xAxis.ticks(d3.time.seconds, 30); - else if (timeSpan < 8) - xAxis.ticks(d3.time.minutes, 1); - else - xAxis.ticks(d3.time.minutes, 2); - - // adjust the number of yaxis ticks based on the range of y values - if (formatValue(min) === formatValue(max)) - yAxis.ticks(2); - - var container = svg.select('.section-container'); - container.selectAll('.series').remove(); - if (stacked) { - y.domain([Math.min(min, 0), d3.max(seriesArr, function (c) { - return d3.max(c.values, function (d) { return d.y0 + d.y; }); - })]); - - // creates a .series g path for each section in the detail - // since we don't get more sections this selection is only run once - var series = container.selectAll(".series") - .data(seriesArr) - - series.enter().append("g") - .attr("class", "series") - .append("path") - .attr("class", "streamPath") - .style("fill", function (d) { return color(d.name); }) - .style("stroke", "grey"); - - series.exit().remove() - - // each time the data is updated, update each section - container.selectAll(".series .streamPath").data(seriesArr) - .attr("d", function (d) { return area(d.values); }) - } else { - var series = container.selectAll(".series") - .data([data], function(d) { return d; }) - - var g = series.enter().append("g") - .attr("class", "series") - - g.append("path") - .attr("class", "area") - .style("fill", "url(" + attrs.url + "#" + id + ") " + attrs.areaColor) //temperature-gradient)") - .attr("d", area.y0(y.range()[0])) - .attr("transform", null); - - g.append("path") - .attr("class", "line") - .style("stroke", attrs.lineColor) - .attr("d", line) -/* -debugger; - g.transition() - .duration(2000) - .attr("transform", "translate(-4)"); -*/ - series.exit().remove() - - sv.selectAll("stop") - .attr("stop-color", attrs.areaColor) - - } - // Update the x-axis. - svg.select(".x.axis") - .attr("transform", "translate(0," + (height - margin.top - margin.bottom + 1) + ")") - .call(xAxis); - - svg.select(".y.axis") - .transition().duration(yAxisTransitionDuration) // animate the y axis - .attr("transform", "translate(" + (width - margin.right - margin.left) + ",0)") - .call(yAxis); - yAxisTransitionDuration = 1000 // only do a transition after the chart is 1st drawn - - // TODO: fix this - // need to recreate this every update... not sure why - var overlay = sv.select(".overlay"); - if (!overlay.empty()) - overlay.remove(); - sv.append("rect") - .attr("class", "overlay") - .attr("width", width) - .attr("height", height) - .on("mouseover", function () {focus.style("display", null)}) - .on("mouseout", function () {focus.style("display", "none")}) - .on("mousemove", mousemove) - - function mousemove() { - var x0 = x.invert(d3.mouse(this)[0] - margin.left); - var i = bisectDate(data, x0, 1); - if (i < data.length && i > 0) { - var d0 = data[i - 1]; - var d1 = data[i]; - // set d to the data that is closest to the mouse position - var d = x0 - d0[0] > d1[0] - x0 ? d1 : d0; - focus.attr("transform", "translate(" + (x(d[0]) + margin.left) + "," + (y(d[1]) + margin.top) + ")"); - - var tipFormat = formatPrecise; - if (attrs.type === "rate") - tipFormat = d3.format(".2n") - // set the tooltip html and position it - focus.select('.svg-tooltip span') - .html(tooltipGenerator(d, color, tipFormat)) - - var foBounds = focus.select('table')[0][0].getBoundingClientRect(); - var mx = x(d[0]); // mouse x - var my = y(d[1]); // mouse y - - // perfer to put the tooltip in the nw corner relative to the focus circle - var foy = -foBounds.height; - var fox = -foBounds.width; - // off the left side - if (mx - foBounds.width - margin.left < 0) - fox = 0; - // above the top - if (my - foBounds.height - margin.top < 0) - foy = 0; - // won't fit above or below, just put it at bottom - if (my + foBounds.height > height) - foy = -(foBounds.height - (height - my)); - - focus.select('.svg-tooltip') - .attr('x', fox).attr('y', foy); - - // position the guide lines - focus.select(".mo-guide.y") - .attr("y", -my); - focus.select(".mo-guide.x") - .attr("x", -mx); - - } else { - focus.attr("transform", "translate(-10,-10)"); - } - } - - }) - - - } - chart.attr = function (attrName, value) { - if (arguments.length < 2) - return arguments.length == 1 ? attrs[attrName] : chart; - if (angular.isDefined(attrs[attrName])) - attrs[attrName] = value; - return chart; - } - chart.tooltipGenerator = function (_) { - tooltipGenerator = _; - return chart; - } - - return chart; - } - } - return self; - }]); - - return QDR; -}(QDR || {})); diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrChartService.js b/console/hawtio/src/main/webapp/plugin/js/qdrChartService.js new file mode 120000 index 0000000..dc5df4c --- /dev/null +++ b/console/hawtio/src/main/webapp/plugin/js/qdrChartService.js @@ -0,0 +1 @@ +../../../../../../stand-alone/plugin/js/qdrChartService.js \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/d98ae37e/console/hawtio/src/main/webapp/plugin/js/qdrCharts.js ---------------------------------------------------------------------- diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrCharts.js b/console/hawtio/src/main/webapp/plugin/js/qdrCharts.js index f37018f..30a9b99 100644 --- a/console/hawtio/src/main/webapp/plugin/js/qdrCharts.js +++ b/console/hawtio/src/main/webapp/plugin/js/qdrCharts.js @@ -27,16 +27,21 @@ var QDR = (function (QDR) { * Controller that handles displaying a chart on the hawtio dashboard page. Only one chart is displayed per instance of this * page */ - QDR.module.controller("QDR.ChartsController", function($scope, QDRService, QDRChartService, $dialog, $location, localStorage, $routeParams) { + QDR.module.controller("QDR.ChartsController", function($scope, QDRService, QDRChartService, $dialog, $location, $routeParams) { var updateTimer = null; if (!QDRService.connected && !$routeParams.chid) { // we are not connected. we probably got here from a bookmark or manual page reload - $location.path("/" + QDR.pluginName + "/connect") - $location.search('org', "charts"); + QDRService.redirectWhenConnected("charts"); return; } + // we are currently connected. setup a handler to get notified if we are ever disconnected + QDRService.addDisconnectAction( function () { + QDRService.redirectWhenConnected("charts") + $scope.$apply(); + }) + $scope.svgCharts = []; // create an svg object for each chart @@ -74,7 +79,7 @@ var QDR = (function (QDR) { // the link that the above login prompt will display $scope.loginHref = QDR.pluginName + "/connect"; - // call by ng-init in the html when the page is loaded + // called by ng-init in the html when the page is loaded $scope.chartsLoaded = function () { $scope.svgCharts.forEach(function (svgChart) { QDRChartService.sendChartRequest(svgChart.chart.request(), true); @@ -169,7 +174,7 @@ var QDR = (function (QDR) { }); // the edit chart properties dialog - QDR.module.controller("QDR.ChartDialogController", function($scope, QDRChartService, $location, dialog, $rootScope, localStorage, chart, updateTick) { + QDR.module.controller("QDR.ChartDialogController", function($scope, QDRChartService, $location, dialog, $rootScope, chart, updateTick) { UI.colors[0] = "#cbe7f3" UI.colors[1] = "#058dc7" @@ -178,7 +183,7 @@ var QDR = (function (QDR) { var dialogSvgChart = null; $scope.svgDivId = "dialogChart"; // the div id for the svg chart - var updateTimer = null; + var updateTimer = null; $scope.chart = chart; // the underlying chart object from the dashboard $scope.dialogChart = $scope.chart.copy(); // the chart object for this dialog $scope.userTitle = $scope.chart.title(); @@ -191,8 +196,9 @@ var QDR = (function (QDR) { }) $scope.$watch("dialogChart.areaColor", function (newValue, oldValue) { if (newValue !== oldValue) { - if (dialogSvgChart) + if (dialogSvgChart) { dialogSvgChart.tick($scope.svgDivId); + } } }) $scope.$watch("dialogChart.lineColor", function (newValue, oldValue) { @@ -219,8 +225,8 @@ var QDR = (function (QDR) { QDRChartService.unRegisterChart($scope.dialogChart); // remove the chart } $scope.okClick = function () { - cleanup(); - dialog.close(); + cleanup(); + dialog.close(); }; var initRateSlider = function () { @@ -283,9 +289,9 @@ var QDR = (function (QDR) { // update the chart on the popup dialog var updateDialogChart = function () { // draw the chart using the current data - if (dialogSvgChart) + if (dialogSvgChart) { dialogSvgChart.tick($scope.svgDivId); - + } // draw the chart again in 1 second var updateRate = localStorage['updateRate'] ? localStorage['updateRate'] : 5000; if (updateTimer) @@ -301,6 +307,16 @@ var QDR = (function (QDR) { return; } dialogSvgChart = new QDRChartService.AreaChart($scope.dialogChart); + $('input[name=lineColor]').val($scope.dialogChart.lineColor); + $('input[name=areaColor]').val($scope.dialogChart.areaColor); + $('input[name=areaColor]').on('input', function (e) { + $scope.dialogChart.areaColor = $(this).val(); + updateDialogChart() + }) + $('input[name=lineColor]').on('input', function (e) { + $scope.dialogChart.lineColor = $(this).val(); + updateDialogChart() + }) if (updateTimer) clearTimeout(updateTimer); updateDialogChart(); http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/d98ae37e/console/hawtio/src/main/webapp/plugin/js/qdrList.js ---------------------------------------------------------------------- diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrList.js b/console/hawtio/src/main/webapp/plugin/js/qdrList.js deleted file mode 100644 index 7aa808e..0000000 --- a/console/hawtio/src/main/webapp/plugin/js/qdrList.js +++ /dev/null @@ -1,811 +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. -*/ -/** - * @module QDR - */ -var QDR = (function(QDR) { - - /** - * @method ListController - * @param $scope - * @param QDRService - * - * Controller for the main interface - */ - QDR.module.controller("QDR.ListController", ['$scope', '$location', '$dialog', '$filter', 'localStorage', 'QDRService', 'QDRChartService', - function ($scope, $location, $dialog, $filter, localStorage, QDRService, QDRChartService) { - - var updateIntervalHandle = undefined; - var updateInterval = 5000; - var ListExpandedKey = "QDRListExpanded"; - $scope.details = {}; - - $scope.selectedEntity = localStorage['QDRSelectedEntity'] || "address"; - $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-chart"></i> Chart</a>', - id: 'charts', - op: 'graph', - title: "Graph attributes", - isValid: function () { return false; return $scope.detailFields.some( function (field ) { - return field.graph; - })} - }, - { - 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.selectMode = function (mode) { - $scope.currentMode = mode; - if (mode.id === 'log') { - $scope.logResults = "getting recent log entries..."; - QDRService.sendMethod($scope.currentNode.id, $scope.selectedEntity, {}, $scope.currentMode.op, function (nodeName, entity, response, context) { - $scope.logResults = 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.$apply(); - }) - } - } - $scope.isValid = function (mode) { - return mode.isValid() - } - - if (!QDRService.connected) { - // we are not connected. we probably got here from a bookmark or manual page reload - QDRService.redirectWhenConnected("list"); - return; - } - // we are currently connected. setup a handler to get notified if we are ever disconnected - QDRService.addDisconnectAction( function () { - QDRService.redirectWhenConnected("list") - $scope.$apply(); - }) - - $scope.nodes = QDRService.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 (!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); - } - } - var setCurrentNode = function () { - $scope.nodes.some( function (node, i) { - if (node.name === $scope.selectedNode) { - $scope.currentNode = $scope.nodes[i] - 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]; - } - } - - var excludedEntities = ["management", "org.amqp.management", "operationalEntity", "entity", "configurationEntity", "dummy", "console"]; - var aggregateEntities = ["router.address"]; - var classOverrides = { - "connection": function (row) { - return row.role.value; - }, - "router.link": function (row) { - return row.linkType.value; - } - } - - var lookupOperations = function () { - var ops = QDRService.schema.entityTypes[$scope.selectedEntity].operations.filter( function (op) { return op !== 'READ'}); - $scope.operation = ops.length ? ops[0] : ""; - return ops; - } - - var entityTreeChildren = []; - var expandedList = angular.fromJson(localStorage[ListExpandedKey]) || []; - var sortedEntities = Object.keys(QDRService.schema.entityTypes).sort(); - sortedEntities.forEach( function (entity) { - if (excludedEntities.indexOf(entity) == -1) { - if (!angular.isDefined($scope.selectedEntity)) { - $scope.selectedEntity = entity; - $scope.operations = lookupOperations() - } - var e = new Folder(entity) - e.typeName = "entity" - e.key = entity - e.expand = (expandedList.indexOf(entity) > -1) - var placeHolder = new Folder("loading...") - placeHolder.addClass = "loading" - e.children = [placeHolder] - entityTreeChildren.push(e) - } - }) - $scope.treeReady = function () { - $('#entityTree').dynatree({ - onActivate: onTreeSelected, - onExpand: onTreeNodeExpanded, - selectMode: 1, - activeVisible: false, - children: entityTreeChildren - }) - restartUpdate(); // start getting the data now that the tree is created - updateExpandedEntities(); - }; - var onTreeNodeExpanded = function (expanded, node) { - // save the list of entities that are expanded - var tree = $("#entityTree").dynatree("getTree"); - var list = []; - tree.visit( function (tnode) { - if (tnode.isExpanded()) { - list.push(tnode.data.key) - } - }) - localStorage[ListExpandedKey] = JSON.stringify(list) - - if (expanded) - onTreeSelected(node); - } - // a tree node was selected - var onTreeSelected = function (selectedNode) { - 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.data.key; - $scope.operations = lookupOperations() - } else if (selectedNode.data.typeName === 'attribute') { - $scope.selectedEntity = selectedNode.parent.data.key; - $scope.operations = lookupOperations() - $scope.selectedRecordName = selectedNode.data.key; - updateDetails(selectedNode.data.details); // update the table on the right - $("#entityTree").dynatree("getRoot").visit(function(node){ - node.select(false); - }); - selectedNode.select(); - } - $scope.$apply(); - } - // fill in an empty results recoord based on the entities schema - var fromSchema = function (entityName) { - var row = {} - var schemaEntity = QDRService.schema.entityTypes[entityName] - for (attr in schemaEntity.attributes) { - var entity = schemaEntity.attributes[attr] - var value = "" - if (angular.isDefined(entity['default'])) - 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 () { - var schemaEntity = QDRService.schema.entityTypes[$scope.selectedEntity] - return (schemaEntity.operations.indexOf("CREATE") > -1) - } - - var stopUpdating = function () { - if (angular.isDefined(updateIntervalHandle)) { - clearInterval(updateIntervalHandle); - } - updateIntervalHandle = undefined; - } - - // the data for the selected entity is available, populate the tree - var updateEntityChildren = function (entity, tableRows, expand) { - var tree = $("#entityTree").dynatree("getTree"); - if (!tree.getNodeByKey) { - return stopUpdating() - } - var node = tree.getNodeByKey(entity) - var updatedDetails = false; - var scrollTreeDiv = $('.qdr-attributes.pane.left .pane-viewport') - var scrollTop = scrollTreeDiv.scrollTop(); - node.removeChildren(); - if (tableRows.length == 0) { - node.addChild({ - addClass: "no-data", - typeName: "none", - title: "no data", - key: node.data.key + ".1" - }) - if (expand) { - updateDetails(fromSchema(entity)); - $scope.selectedRecordName = entity; - } - } else { - tableRows.forEach( function (row) { - var addClass = entity; - if (classOverrides[entity]) { - addClass += " " + classOverrides[entity](row); - } - var child = { - typeName: "attribute", - addClass: addClass, - key: row.name.value, - title: row.name.value, - details: row - } - if (row.name.value === $scope.selectedRecordName) { - if (expand) - updateDetails(row); // update the table on the right - child.select = true; - updatedDetails = true; - } - node.addChild(child) - }) - } - // if the selectedRecordName was not found, select the 1st one - if (expand && !updatedDetails && tableRows.length > 0) { - var row = tableRows[0]; - $scope.selectedRecordName = row.name.value; - var node = tree.getNodeByKey($scope.selectedRecordName); - node.select(true); - updateDetails(row) // update the table on the right - } - scrollTreeDiv.scrollTop(scrollTop) - } - - var schemaProps = function (entityName, key, currentNode) { - var typeMap = {integer: 'number', string: 'text', path: 'text', boolean: 'boolean', map: 'textarea'}; - - var entity = QDRService.schema.entityTypes[entityName] - var value = entity.attributes[key] - // skip identity and depricated fields - if (!value) - return {input: 'input', type: 'disabled', required: false, selected: "", rawtype: 'string', disabled: true, 'default': ''} - var description = value.description || "" - var val = value['default']; - var 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.getNodeInfo(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; - } - }) - }); - } - return { name: key, - humanName: QDRService.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) { - var 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) { - var details = []; - $scope.detailsObject = {}; - var attrs = Object.keys(row).sort(); - attrs.forEach( function (attr) { - var changed = $scope.detailFields.filter(function (old) { - return (old.name === attr) ? old.graph && old.rawValue != row[attr].value : false; - }) - var schemaEntity = schemaProps($scope.selectedEntity, attr, $scope.currentNode) - details.push( { - attributeName: QDRService.humanify(attr), - attributeValue: attr === 'port' ? row[attr].value : QDRService.pretty(row[attr].value), - name: attr, - changed: changed.length, - rawValue: row[attr].value, - graph: row[attr].graph, - title: row[attr].title, - aggregateValue: QDRService.pretty(row[attr].aggregate), - aggregateTip: row[attr].aggregateTip, - - input: schemaEntity.input, - type: schemaEntity.type, - required: schemaEntity.required, - selected: schemaEntity.selected, - rawtype: schemaEntity.rawtype, - disabled: schemaEntity.disabled, - 'default': schemaEntity['default'] - }) - $scope.detailsObject[attr] = row[attr].value; - }) - setTimeout(applyDetails, 1, details) - } - - var applyDetails = function (details) { - $scope.detailFields = details; - aggregateColumn(); - $scope.$apply(); - // ng-grid bug? the entire table doesn't always draw unless a reflow is triggered; - $(window).trigger('resize'); - } - - var restartUpdate = function () { - stopUpdating(); - updateTableData($scope.selectedEntity, true); - updateIntervalHandle = setInterval(updateExpandedEntities, updateInterval); - } - var updateExpandedEntities = function () { - var tree = $("#entityTree").dynatree("getTree"); - if (tree.visit) { - tree.visit( function (node) { - if (node.isExpanded()) { - updateTableData(node.data.key, node.data.key === $scope.selectedEntity) - } - }) - } else { - stopUpdating(); - } - } - - $scope.selectNode = function(node) { - $scope.selectedNode = node.name; - $scope.selectedNodeId = node.id; - setCurrentNode(); - restartUpdate(); - }; - $scope.$watch('selectedEntity', function(newValue, oldValue) { - if (newValue !== oldValue) { - localStorage['QDRSelectedEntity'] = $scope.selectedEntity; - restartUpdate(); - $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 updateTableData = function (entity, expand) { - if (!QDRService.connected) { - // we are no longer connected. bail back to the connect page - $location.path("/" + QDR.pluginName + "/connect") - $location.search('org', "list"); - return; - } - // don't update the data when on the operations tab - if ($scope.currentMode.id === 'operations') { - return; - } - - var gotNodeInfo = function (nodeName, dotentity, response) { - var tableRows = []; - var records = response.results; - var aggregates = response.aggregates; - var attributeNames = response.attributeNames; - // If !attributeNmes then there was an error getting the records for this entity - if (attributeNames) { - var nameIndex = attributeNames.indexOf("name"); - var identityIndex = attributeNames.indexOf("identity"); - var ent = QDRService.schema.entityTypes[entity]; - for (var i=0; i<records.length; ++i) { - var record = records[i]; - var aggregate = aggregates ? aggregates[i] : undefined; - var row = {}; - var rowName; - if (nameIndex > -1) { - rowName = record[nameIndex]; - if (!rowName && identityIndex > -1) { - rowName = record[nameIndex] = (dotentity + '/' + record[identityIndex]) - } - } - if (!rowName) { - QDR.log.error("response attributeNames did not contain a name field"); - console.dump(response.attributeNames); - return; - } - for (var j=0; j<attributeNames.length; ++j) { - var col = attributeNames[j]; - row[col] = {value: record[j], type: undefined, graph: false, title: '', aggregate: '', aggregateTip: ''}; - if (ent) { - var 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 : ''; - var 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) }) - setTimeout(selectRow, 0, {entity: dotentity, rows: tableRows, expand: expand}); - } - // 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) { - var nodeInfo = QDRService.topology.nodeInfo(); - QDRService.getMultipleNodeInfo(Object.keys(nodeInfo), entity, [], gotNodeInfo, $scope.selectedNodeId); - } else { - QDRService.getNodeInfo($scope.selectedNodeId, entity, [], gotNodeInfo); - } - }; - - // tableRows are the records that were returned, this populates the left hand table on the page - var selectRow = function (info) { - updateEntityChildren(info.entity, info.rows, info.expand); - fixTooltips(); - } - - var titleFromAlt = function (alt) { - if (alt && alt.length) { - var data = angular.fromJson(alt); - var table = "<table class='tiptable'><tbody>"; - data.forEach (function (row) { - table += "<tr>"; - table += "<td>" + row.node + "</td><td align='right'>" + QDRService.pretty(row.val) + "</td>"; - table += "</tr>" - }) - table += "</tbody></table>" - return table; - } - return ''; - } - var fixTooltips = function () { - if ($('.hastip').length == 0) { - setTimeout(fixTooltips, 100); - return; - } - $('.hastip').each( function (i, tip) { - var tipset = tip.getAttribute('tipset') - if (!tipset) { - $(tip).tipsy({html: true, className: 'subTip', opacity: 1, title: function () { - return titleFromAlt(this.getAttribute('alt')) - } }); - tip.setAttribute('tipset', true) - } else { - var title = titleFromAlt(tip.getAttribute('alt')) - tip.setAttribute('original-title', title) - } - }) - } - - $scope.detailFields = []; - - $scope.addToGraph = function(rowEntity) { - var chart = QDRChartService.registerChart( - {nodeId: $scope.selectedNodeId, - entity: "." + $scope.selectedEntity, - name: $scope.selectedRecordName, - attr: rowEntity.name, - forceCreate: true}); - doDialog("template-from-script.html", chart); - } - - $scope.addAllToGraph = function(rowEntity) { - var chart = QDRChartService.registerChart({ - nodeId: $scope.selectedNodeId, - entity: $scope.selectedEntity, - name: $scope.selectedRecordName, - attr: rowEntity.name, - type: "rate", - rateWindow: udateInterval, - visibleDuration: 1, - forceCreate: true, - aggregate: true}); - doDialog("template-from-script.html", chart); - } - - $scope.detailCols = []; - 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">{{row.entity[col.field]}}<i ng-if="row.entity.graph" ng-click="addToGraph(row.entity)" ng-class="{\'icon-bar-chart\': row.entity.graph == true }"></i></div>' - }, - { - field: 'attributeValue', - displayName: 'Value', - cellTemplate: '<div class="ngCellText" ng-class="{\'changed\': row.entity.changed == 1}"><span>{{row.getProperty(col.field)}}</span></div>' - } - ] - if (aggregateEntities.indexOf($scope.selectedEntity) > -1) { - $scope.detailCols.push( - { - width: '10%', - field: 'aggregateValue', - displayName: 'Aggregate', - cellTemplate: '<div class="hastip" alt="{{row.entity.aggregateTip}}"><span ng-class="{\'changed\': row.entity.changed == 1}">{{row.entity[col.field]}}</span><i ng-if="row.entity.graph" ng-click="addAllToGraph(row.entity)" ng-class="{\'icon-bar-chart\': row.entity.graph == true }"></i></div>', - cellClass: 'aggregate' - } - ) - } - } - if ($scope.selectedRecordName === "") - $scope.detailCols = []; - } - - // 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.details = { - data: 'detailFields', - columnDefs: "detailCols", - enableColumnResize: true, - multiSelect: false, - beforeSelectionChange: function() { - return false; - } - }; - $scope.$on("$destroy", function( event ) { - //QDR.log.debug("scope destroyed for qdrList"); - stopUpdating(); - }); - - function gotMethodResponse (nodeName, entity, response, context) { - var statusCode = context.message.application_properties.statusCode; - if (statusCode < 200 || statusCode >= 300) { - Core.notification('error', context.message.application_properties.statusDescription); - //QDR.log.debug(context.message.application_properties.statusDescription) - } else { - var note = entity + " " + $filter('Pascalcase')($scope.currentMode.op) + "d" - Core.notification('success', note); - $scope.selectMode($scope.modes[0]); - restartUpdate(); - } - } - $scope.ok = function () { - var attributes = {} - $scope.detailFields.forEach( function (field) { - var 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.sendMethod($scope.currentNode.id, $scope.selectedEntity, attributes, $scope.currentMode.op, gotMethodResponse) - } - $scope.remove = function () { - var attributes = {type: $scope.selectedEntity, name: $scope.selectedRecordName} - QDRService.sendMethod($scope.currentNode.id, $scope.selectedEntity, attributes, $scope.currentMode.op, gotMethodResponse) - } - - function doDialog(template, chart) { - var d = $dialog.dialog({ - backdrop: true, - keyboard: true, - backdropClick: true, - templateUrl: template, - controller: "QDR.ListChartController", - resolve: { - chart: function() { - return chart - }, - nodeName: function () { - return $scope.selectedNode - } - } - }); - - d.open().then(function(result) { console.log("d.open().then"); }); - - }; - }]); - - QDR.module.controller('QDR.ListChartController', function ($scope, dialog, $dialog, $location, localStorage, QDRChartService, chart, nodeName) { - $scope.chart = chart; - $scope.dialogSvgChart = null; - var updateTimer = null; - $scope.svgDivId = "dialogChart"; // the div id for the svg chart - - $scope.showChartsPage = function () { - cleanup(); - dialog.close(true); - $location.path("/" + QDR.pluginName + "/charts"); - }; - - $scope.addHChart = function () { - QDRChartService.addHDash($scope.chart); - cleanup(); - dialog.close(true); - } - - $scope.addToDashboardLink = function () { - var href = "#/" + QDR.pluginName + "/charts"; - var size = angular.toJson({ - size_x: 2, - size_y: 2 - }); - - var params = angular.toJson({chid: $scope.chart.id()}); - var 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 () { - // the chart divs are generated by angular and aren't available immediately - var div = angular.element("#" + $scope.svgDivId); - if (!div.width()) { - setTimeout(showChart, 100); - return; - } - dialogSvgChart = new QDRChartService.AreaChart($scope.chart); - $scope.dialogSvgChart = dialogSvgChart; - updateDialogChart(); - } - showChart(); - - var updateDialogChart = function () { - if ($scope.dialogSvgChart) - $scope.dialogSvgChart.tick($scope.svgDivId); - if (updateTimer) - clearTimeout(updateTimer) - updateTimer = setTimeout(updateDialogChart, 1000); - } - - var cleanup = function () { - if (updateTimer) { - clearTimeout(updateTimer); - updateTimer = null; - } - if (!$scope.chart.hdash) - QDRChartService.unRegisterChart($scope.chart); // remove the chart - - } - $scope.ok = function () { - cleanup(); - dialog.close(true); - //dialogService.cancel("myDialog"); - }; - - }); - return QDR; - -} (QDR || {})); -// messaging-ci-01.mw.lab.eng.bos.redhat.com diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrList.js b/console/hawtio/src/main/webapp/plugin/js/qdrList.js new file mode 120000 index 0000000..3955db5 --- /dev/null +++ b/console/hawtio/src/main/webapp/plugin/js/qdrList.js @@ -0,0 +1 @@ +../../../../../../stand-alone/plugin/js/qdrList.js \ No newline at end of file http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/d98ae37e/console/hawtio/src/main/webapp/plugin/js/qdrListChart.js ---------------------------------------------------------------------- diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrListChart.js b/console/hawtio/src/main/webapp/plugin/js/qdrListChart.js new file mode 120000 index 0000000..d1fcf4a --- /dev/null +++ b/console/hawtio/src/main/webapp/plugin/js/qdrListChart.js @@ -0,0 +1 @@ +../../../../../../stand-alone/plugin/js/qdrListChart.js \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org