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/05/17 16:09:30 UTC
[5/6] qpid-dispatch git commit: DISPATCH-1000 Restore the html/css/js
files that were in the npm repository
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/dfd3cf7c/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
new file mode 100644
index 0000000..4c9c727
--- /dev/null
+++ b/console/stand-alone/plugin/html/tmplChartConfig.html
@@ -0,0 +1,85 @@
+<!--
+ 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
+-->
+
+<!--
+ This is the template for the graph dialog that is displayed. It uses the
+ dialogCtrl controller in qdrCharts.js.
+-->
+<div class="chartOptions">
+ <div class="modal-header">
+ <h3 class="modal-title">Chart {{chart.attr() | humanify}}</h3>
+ </div>
+ <div class="modal-body">
+ <div id="{{svgDivId}}" class="line-chart-pf"></div>
+
+ <uib-tabset>
+ <uib-tab heading="Type">
+ <legend>Chart type</legend>
+ <div>
+ <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>
+<!--
+ <div class="dlg-slider" ng-show="dialogChart.type=='rate'">
+ <span>Rate Window: {{rateWindow}} second{{rateWindow > 1 ? "s" : ""}}</span>
+ <div id="rateSlider"></div>
+ </div>
+-->
+ </div>
+ <div style="clear:both;"> </div>
+ </uib-tab>
+<!--
+ <uib-tab ng-hide="$parent.chart.aggregate()" heading="Colors">
+ <legend>Chart colors</legend>
+ <div>
+ <div class="colorPicker">
+ <label>Area: <input id="areaColor" name="areaColor" type="color" /></label>
+ </div>
+ </div>
+ <div style="clear:both;"> </div>
+ </uib-tab>
+-->
+ <uib-tab heading="Duration">
+ <legend>Chart duration</legend>
+ <div class="clearfix">
+ <div class="dlg-slider duration">
+ <span>Show data for past {{dialogChart.visibleDuration}} minute{{dialogChart.visibleDuration > 1 ? "s" : ""}}</span>
+ <div id="durationSlider"></div>
+ </div>
+ </div>
+ </uib-tab>
+ </uib-tabset>
+ </div>
+ <div class="modal-footer">
+ <div ng-hide="adding()">
+ <button class="btn btn-success" type="button" ng-click="apply()">Apply to existing chart</button>
+ <button class="btn btn-info" type="button" ng-click="copyToDashboard()">Create new chart</button>
+ <button class="btn btn-primary" type="button" ng-click="okClick()">Close</button>
+ </div>
+ <div ng-show="adding()">
+ <span ng-hide="isOnChartsPage()">
+ <button class="btn btn-success" type="button" ng-click="addChartsPage()"><i class="icon-bar-chart"></i> Add</button> this chart to the Charts page.
+ </span>
+ <span ng-show="isOnChartsPage()">
+ View the <button class="btn btn-success" type="button" ng-click="showChartsPage()"><i class="icon-bar-chart"></i> Charts</button> page.
+ </span>
+ <button class="btn btn-primary" type="button" ng-click="okClick()">Close</button>
+ </div>
+ </div>
+</div>
+
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/dfd3cf7c/console/stand-alone/plugin/html/tmplListChart.html
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/html/tmplListChart.html b/console/stand-alone/plugin/html/tmplListChart.html
new file mode 100644
index 0000000..3214187
--- /dev/null
+++ b/console/stand-alone/plugin/html/tmplListChart.html
@@ -0,0 +1,40 @@
+<!--
+ 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
+-->
+
+<!--
+ This is the template for the graph dialog that is displayed.
+-->
+<div class="modal-header">
+ <h3 class="modal-title">Chart {{chart.attr() | humanify}}</h3>
+</div>
+<div class="modal-body">
+ <p class="newChart">
+ <button ng-click="editChart()" title="Configure"><span class="fa-edit"></span></button>
+ </p><div style="clear:both"></div>
+ <div id="pfDialogChart" class="line-chart-pf"></div>
+</div>
+<div class="modal-footer">
+ <span ng-hide="isOnChartsPage()">
+ <button class="btn btn-success" type="button" ng-click="addChartsPage()"><i class="icon-bar-chart"></i> Add</button> this chart to the Charts page.
+ </span>
+ <span ng-show="isOnChartsPage()">
+ <button class="btn btn-danger" type="button" ng-click="delChartsPage()">Remove</button> this chart from the <button class="btn btn-success" type="button" ng-click="showChartsPage()"><i class="icon-bar-chart"></i> Charts</button> page.
+ </span>
+ <button class="btn btn-primary" type="button" ng-click="ok()">Close</button>
+</div>
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/dfd3cf7c/console/stand-alone/plugin/html/tmplListTree.html
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/html/tmplListTree.html b/console/stand-alone/plugin/html/tmplListTree.html
new file mode 100644
index 0000000..64797fa
--- /dev/null
+++ b/console/stand-alone/plugin/html/tmplListTree.html
@@ -0,0 +1,42 @@
+<!--
+ 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
+-->
+
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+ <ng-include src="'listGrid.html'"></ng-include>
+</div>
+<div class="qdr-attributes col-sm-3 col-md-2 col-sm-pull-9 col-md-pull-10 sidebar-pf sidebar-pf-left">
+ <div class="pane-wrapper">
+ <div class="pane-header-wrapper">
+ <div class="tree-header"><select ng-options="node as node.name for node in nodes" ng-model="currentNode" ng-change="selectNode(currentNode)"></select></div>
+ <div ng-hide="largeNetwork" class="expand-collapse">
+ <i class="icon-chevron-down clickable" title="Expand all nodes" ng-click="expandAll()"></i>
+ <i class="icon-chevron-up clickable" title="Unexpand all nodes" ng-click="contractAll()"></i>
+ </div>
+ </div>
+ <div class="pane-viewport">
+ <div class="pane-content">
+ <div class="treeContainer">
+ <div id="entityTree" onSelect="onTreeSelected" onRoot="onRootReady" hideRoot="true"></div>
+ <div ng-init="treeReady()"></div>
+ </div>
+ </div>
+ </div>
+ <div class="pane-bar"></div>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/dfd3cf7c/console/stand-alone/plugin/html/tmplOverviewTree.html
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/html/tmplOverviewTree.html b/console/stand-alone/plugin/html/tmplOverviewTree.html
new file mode 100644
index 0000000..d3f6e80
--- /dev/null
+++ b/console/stand-alone/plugin/html/tmplOverviewTree.html
@@ -0,0 +1,48 @@
+<!--
+ 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
+-->
+<div class="col-xs-9">
+ <ng-include src="'overviewGrid.html'"></ng-include>
+</div>
+
+<button type='button' class='navbar-toggle tree-menu-button' data-toggle="collapse" data-target=".overview-tree">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+
+<div class="overview-tree navbar-collapse collapse col-xs-3">
+ <div class="pane-wrapper">
+ <div class="pane-header-wrapper">
+ <div ng-hide="largeNetwork" class="expand-collapse">
+ <i class="icon-chevron-down clickable" title="Expand all nodes" ng-click="expandAll()"></i>
+ <i class="icon-chevron-up clickable" title="Unexpand all nodes" ng-click="contractAll()"></i>
+ </div>
+ </div>
+ <div class="pane-viewport">
+ <div class="pane-content">
+ <div class="treeContainer ng-scope">
+ <div id="overtree"></div>
+ </div>
+ </div>
+ </div>
+ <div class="pane-bar" ng-mousedown="startMoving($event)" ng-click="toggle()"></div>
+ </div>
+</div>
+<div ng-init="overviewLoaded()"></div>
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/dfd3cf7c/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
new file mode 100644
index 0000000..e8c240f
--- /dev/null
+++ b/console/stand-alone/plugin/js/dlgChartController.js
@@ -0,0 +1,204 @@
+/*
+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) {
+
+ // controller for the edit/configure chart dialog
+ QDR.module.controller('QDR.ChartDialogController', function($scope, QDRChartService, $location, $uibModalInstance, chart, updateTick, dashboard, adding) {
+ let dialogSvgChart = null;
+ $scope.svgDivId = 'dialogEditChart'; // the div id for the svg chart
+
+ let 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();
+
+ $scope.$watch('userTitle', function(newValue, oldValue) {
+ if (newValue !== oldValue) {
+ $scope.dialogChart.title(newValue);
+ dialogSvgChart.tick($scope.svgDivId);
+ }
+ });
+ $scope.$watch('dialogChart.areaColor', function (newValue, oldValue) {
+ if (newValue !== oldValue) {
+ if (dialogSvgChart)
+ dialogSvgChart.tick($scope.svgDivId);
+ }
+ });
+ $scope.$watch('dialogChart.lineColor', function (newValue, oldValue) {
+ if (newValue !== oldValue) {
+ if (dialogSvgChart)
+ dialogSvgChart.tick($scope.svgDivId);
+ }
+ });
+ $scope.$watch('dialogChart.type', function (newValue, oldValue) {
+ if (newValue !== oldValue) {
+ if (dialogSvgChart) {
+ dialogSvgChart.chart.visibleDuration = newValue === 'rate' ? 0.25 : 1;
+ dialogSvgChart.tick($scope.svgDivId);
+ }
+ }
+ });
+
+ // the stored rateWindow is in milliseconds, but the slider is in seconds
+ $scope.rateWindow = $scope.chart.rateWindow / 1000;
+
+ $scope.addChartsPage = function () {
+ QDRChartService.addDashboard(dialogSvgChart.chart);
+ };
+ $scope.delChartsPage = function () {
+ QDRChartService.delDashboard($scope.chart);
+ };
+
+ $scope.showChartsPage = function () {
+ cleanup();
+ $uibModalInstance.close(true);
+ $location.path(QDR.pluginRoot + '/charts');
+ };
+
+ var cleanup = function () {
+ if (updateTimer) {
+ clearTimeout(updateTimer);
+ updateTimer = null;
+ }
+ if (!$scope.isOnChartsPage())
+ QDRChartService.unRegisterChart($scope.dialogChart); // remove the chart
+ };
+ $scope.okClick = function () {
+ cleanup();
+ $uibModalInstance.close(true);
+ };
+
+ var initRateSlider = function () {
+ if (document.getElementById('rateSlider')) {
+ $( '#rateSlider' ).slider({
+ value: $scope.rateWindow,
+ min: 1,
+ max: 10,
+ step: 1,
+ slide: function( event, ui ) {
+ $scope.rateWindow = ui.value;
+ $scope.dialogChart.rateWindow = ui.value * 1000;
+ $scope.$apply();
+ if (dialogSvgChart)
+ dialogSvgChart.tick($scope.svgDivId);
+ }
+ });
+ } else {
+ setTimeout(initRateSlider, 100);
+ }
+ };
+ //initRateSlider();
+
+ var initDurationSlider = function () {
+ if (document.getElementById('durationSlider')) {
+ $( '#durationSlider' ).slider({
+ value: $scope.dialogChart.visibleDuration,
+ min: 0.25,
+ max: 10,
+ step: 0.25,
+ slide: function( event, ui ) {
+ $scope.visibleDuration = $scope.dialogChart.visibleDuration = ui.value;
+ $scope.$apply();
+ if (dialogSvgChart)
+ dialogSvgChart.tick($scope.svgDivId);
+ }
+ });
+ } else {
+ setTimeout(initDurationSlider, 100);
+ }
+ };
+ initDurationSlider();
+
+ $scope.adding = function () {
+ return adding;
+ };
+
+ $scope.isOnChartsPage = function () {
+ let chart = $scope.chart;
+ if (adding)
+ return QDRChartService.isAttrCharted(chart.nodeId(), chart.entity(), chart.name(), chart.attr(), chart.aggregate());
+ else
+ return $scope.chart.dashboard;
+ };
+
+ // handle the Apply button click
+ // update the dashboard chart's properties
+ $scope.apply = function () {
+ $scope.chart.areaColor = $scope.dialogChart.areaColor;
+ $scope.chart.lineColor = $scope.dialogChart.lineColor;
+ $scope.chart.type = $scope.dialogChart.type;
+ $scope.chart.rateWindow = $scope.rateWindow * 1000;
+ $scope.chart.title($scope.dialogChart.title());
+ $scope.chart.visibleDuration = $scope.dialogChart.visibleDuration;
+ QDRChartService.saveCharts();
+ if (typeof updateTick === 'function')
+ updateTick();
+ };
+
+ // add a new chart to the dashboard based on the current dialog settings
+ $scope.copyToDashboard = function () {
+ let chart = $scope.dialogChart.copy();
+ // set the new chart's dashboard state
+ QDRChartService.addDashboard(chart);
+ // notify the chart controller that it needs to display a new chart
+ dashboard.addChart(chart);
+ };
+
+ // update the chart on the popup dialog
+ var updateDialogChart = function () {
+ // draw the chart using the current data
+ if (dialogSvgChart)
+ dialogSvgChart.tick($scope.svgDivId);
+
+ // draw the chart again in 1 second
+ const updateRate = localStorage['updateRate'] ? localStorage['updateRate'] : 1000;
+ if (updateTimer)
+ clearTimeout(updateTimer);
+ updateTimer = setTimeout(updateDialogChart, updateRate);
+ };
+
+ var showChart = function () {
+ // ensure the div for our chart is loaded in the dom
+ let div = angular.element('#' + $scope.svgDivId);
+ if (!div.width()) {
+ 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()
+ })
+*/
+ if (updateTimer)
+ clearTimeout(updateTimer);
+ updateDialogChart();
+ };
+ showChart();
+ });
+ return QDR;
+
+} (QDR || {}));
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/dfd3cf7c/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
new file mode 100644
index 0000000..574ea07
--- /dev/null
+++ b/console/stand-alone/plugin/js/qdrChartService.js
@@ -0,0 +1,859 @@
+/*
+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 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));
+ };
+ }
+
+ // 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);
+ }
+ 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);
+ };
+ }
+
+ // 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);
+ }
+ }
+ }
+ }
+ 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;
+
+ 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();
+ }
+ }
+ }
+ });
+ }
+ }
+ };
+ 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);
+ }
+ });
+ }
+ },
+
+ // 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);
+ },
+
+ // 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);
+
+ // 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();
+ }
+ };
+ // 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'
+ };
+
+ }
+ 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>');
+ }
+ table += '</tbody></table>';
+ return table;
+ }
+ };
+
+ 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()]);
+ }
+ 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));
+ }
+ } 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;
+ }
+
+ // 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 || {}));
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/dfd3cf7c/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
new file mode 100644
index 0000000..b296de7
--- /dev/null
+++ b/console/stand-alone/plugin/js/qdrCharts.js
@@ -0,0 +1,160 @@
+/*
+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) {
+
+ /**
+ * @method ChartsController
+ *
+ * Controller that handles the QDR charts page
+ */
+ QDR.module.controller('QDR.ChartsController', function($scope, QDRService, QDRChartService, $uibModal, $location, $routeParams, $timeout) {
+
+ 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');
+ 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);
+ svgChart.zoomed = false;
+ $scope.svgCharts.push(svgChart);
+ });
+
+
+ // redraw the chart every update period
+ var updateCharts = function () {
+ $scope.svgCharts.forEach(function (svgChart) {
+ svgChart.tick(svgChart.chart.id()); // on this page we are using the chart.id() as the div id in which to render the chart
+ });
+ const updateRate = localStorage['updateRate'] ? localStorage['updateRate'] : 1000;
+ if (updateTimer) {
+ clearTimeout(updateTimer);
+ }
+ updateTimer = setTimeout(updateCharts, updateRate);
+ };
+
+ // called by ng-init in the html when the page is loaded
+ $scope.chartsLoaded = function () {
+ // ensure the div for our chart is loaded in the dom
+ let div = angular.element('.chartContainer');
+ if (!div.width()) {
+ setTimeout($scope.chartsLoaded, 100);
+ return;
+ }
+ // create an svg object for each chart
+ $scope.svgCharts.forEach ( function (c) {
+ c.generate(380);
+ QDRChartService.sendChartRequest(c.chart.request(), true);
+ });
+ if (updateTimer)
+ clearTimeout(updateTimer);
+ setTimeout(updateCharts);
+ };
+
+ $scope.zoomChart = function (chart) {
+ chart.zoomed = !chart.zoomed;
+ chart.zoom(chart.chart.id(), chart.zoomed);
+ };
+ $scope.showListPage = function () {
+ $location.path('/list');
+ };
+
+ $scope.hasCharts = function () {
+ return QDRChartService.numCharts() > 0;
+ };
+
+ $scope.editChart = function (chart) {
+ doDialog('tmplChartConfig.html', chart.chart);
+ };
+
+ $scope.delChart = function (chart) {
+ QDRChartService.unRegisterChart(chart.chart);
+ // remove from svgCharts
+ $scope.svgCharts.forEach(function (svgChart, i) {
+ if (svgChart === chart) {
+ delete $scope.svgCharts.splice(i, 1);
+ }
+ });
+ };
+
+ // 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);
+ $scope.svgCharts.push(nchart);
+ $timeout( function () {
+ nchart.generate();
+ QDRChartService.sendChartRequest(chart.request(), true);
+ });
+ };
+
+ $scope.$on('$destroy', function() {
+ if (updateTimer) {
+ clearTimeout(updateTimer);
+ updateTimer = null;
+ }
+ for (let i=$scope.svgCharts.length-1; i>=0; --i) {
+ delete $scope.svgCharts.splice(i, 1);
+ }
+ });
+
+ 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 () { return updateCharts; };
+ },
+ dashboard: function () {
+ return $scope;
+ },
+ adding: function () {
+ return false;
+ }
+ }
+ });
+ }
+
+ });
+
+
+ return QDR;
+
+}(QDR || {}));
+
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/dfd3cf7c/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
new file mode 100644
index 0000000..af06c55
--- /dev/null
+++ b/console/stand-alone/plugin/js/qdrGlobals.js
@@ -0,0 +1,46 @@
+/*
+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';
+
+var QDR = (function(QDR) {
+
+ 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;
+ })();
+
+ QDR.Core = {
+ notification: function (severity, msg) {
+ $.notify(msg, severity);
+ }
+ };
+
+ return QDR;
+
+} (QDR || {}));
\ 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