Commit 4562f31b by Torkel Ödegaard

merged with master

parents faa5199a 6911e184
......@@ -2,6 +2,9 @@ vNext
**New features or improvements**
- Allow [[..]] filter notation in all text panels (markdown/html/text) (Issue #511)
- New legend display option "Align as table" (Issue #136)
- New legend display option "Right side", will show legend to the right of the graph (Issue #556)
- Enhanced InfluxDB series aliasing (legend names) with pattern replacements (Issue #525)
**Changes**
- Use unix epoch for Graphite from/to for absolute time ranges (Closes #536)
......
......@@ -39,7 +39,6 @@ require.config({
'jquery.flot.stack': '../vendor/jquery/jquery.flot.stack',
'jquery.flot.stackpercent':'../vendor/jquery/jquery.flot.stackpercent',
'jquery.flot.time': '../vendor/jquery/jquery.flot.time',
'jquery.flot.byte': '../vendor/jquery/jquery.flot.byte',
modernizr: '../vendor/modernizr-2.6.1',
......@@ -80,7 +79,6 @@ require.config({
//
'jquery-ui': ['jquery'],
'jquery.flot': ['jquery'],
'jquery.flot.byte': ['jquery', 'jquery.flot'],
'jquery.flot.pie': ['jquery', 'jquery.flot'],
'jquery.flot.events': ['jquery', 'jquery.flot'],
'jquery.flot.selection':['jquery', 'jquery.flot'],
......
......@@ -18,6 +18,7 @@ function (angular, $, kbn, moment, _) {
var data, plot, annotations;
var hiddenData = {};
var dashboard = scope.dashboard;
var legendSideLastValue = null;
scope.$on('refresh',function() {
if (scope.otherPanelInFullscreenMode()) { return; }
......@@ -56,7 +57,7 @@ function (angular, $, kbn, moment, _) {
height = height - 32; // subtract panel title bar
if (scope.panel.legend.show) {
if (scope.panel.legend.show && !scope.panel.legend.rightSide) {
height = height - 21; // subtract one line legend
}
......@@ -136,6 +137,7 @@ function (angular, $, kbn, moment, _) {
yaxes: [],
xaxis: {},
grid: {
minBorderMargin: 0,
markings: [],
backgroundColor: null,
borderWidth: 0,
......@@ -162,9 +164,30 @@ function (angular, $, kbn, moment, _) {
addAnnotations(options);
configureAxisOptions(data, options);
plot = $.plot(elem, data, options);
// if legend is to the right delay plot draw a few milliseconds
// so the legend width calculation can be done
if (shouldDelayDraw(panel)) {
console.log('delay');
legendSideLastValue = panel.legend.rightSide;
setTimeout(function() {
plot = $.plot(elem, data, options);
addAxisLabels();
}, 50);
}
else {
plot = $.plot(elem, data, options);
addAxisLabels();
}
}
addAxisLabels();
function shouldDelayDraw(panel) {
if (panel.legend.rightSide) {
return true;
}
if (legendSideLastValue !== null && panel.legend.rightSide !== legendSideLastValue) {
return true;
}
return false;
}
function addTimeAxis(options) {
......@@ -354,8 +377,10 @@ function (angular, $, kbn, moment, _) {
url += scope.panel.fill !== 0 ? ('&areaAlpha=' + (scope.panel.fill/10).toFixed(1)) : '';
url += scope.panel.linewidth !== 0 ? '&lineWidth=' + scope.panel.linewidth : '';
url += scope.panel.legend.show ? '&hideLegend=false' : '&hideLegend=true';
url += scope.panel.grid.min !== null ? '&yMin=' + scope.panel.grid.min : '';
url += scope.panel.grid.max !== null ? '&yMax=' + scope.panel.grid.max : '';
url += scope.panel.grid.leftMin !== null ? '&yMin=' + scope.panel.grid.leftMin : '';
url += scope.panel.grid.leftMax !== null ? '&yMax=' + scope.panel.grid.leftMax : '';
url += scope.panel.grid.rightMin !== null ? '&yMin=' + scope.panel.grid.rightMin : '';
url += scope.panel.grid.rightMax !== null ? '&yMax=' + scope.panel.grid.rightMax : '';
url += scope.panel['x-axis'] ? '' : '&hideAxes=true';
url += scope.panel['y-axis'] ? '' : '&hideYAxis=true';
......
......@@ -33,46 +33,24 @@
<label class="small">Max / <a ng-click="toggleGridMinMax('rightMax')">Auto <i class="icon-star" ng-show="_.isNull(panel.grid.rightMax)"></i></a></label>
<input type="number" class="input-small" ng-model="panel.grid.rightMax" ng-change="render()" ng-model-onblur />
</div>
<div class="editor-option">
<label class="small">Label</label>
<input ng-change="get_data()" ng-model-onblur placeholder="" type="text" class="input-medium" ng-model="panel.rightYAxisLabel">
</div>
</div>
</div>
<div class="editor-row">
<div class="section">
<h5>Grid thresholds</h5>
<div class="editor-option">
<label class="small">Level1</label>
<input type="number" class="input-small" ng-model="panel.grid.threshold1" ng-change="render()" ng-model-onblur />
</div>
<div class="editor-option">
<label class="small">Color</label>
<spectrum-picker ng-model="panel.grid.threshold1Color" ng-change="render()" ></spectrum-picker>
</div>
<h5>Legend styles</h5>
<div class="editor-option">
<label class="small">Level2</label>
<input type="number" class="input-small" ng-model="panel.grid.threshold2" ng-change="render()" ng-model-onblur />
</div>
<div class="editor-option">
<label class="small">Color</label>
<spectrum-picker ng-model="panel.grid.threshold2Color" ng-change="render()" ></spectrum-picker>
<label class="small">Show Legend</label><input type="checkbox" ng-model="panel.legend.show" ng-checked="panel.legend.show" ng-change="render();">
</div>
<div class="editor-option">
<label class="small">Line mode</label><input type="checkbox" ng-model="panel.grid.thresholdLine" ng-checked="panel.grid.thresholdLine" ng-change="render();">
<label class="small">Include Values</label><input type="checkbox" ng-model="panel.legend.values" ng-checked="panel.legend.values" ng-change="render();">
</div>
</div>
<div class="section">
<h5>Legend</h5>
<div class="editor-option">
<label class="small">Show Legend</label><input type="checkbox" ng-model="panel.legend.show" ng-checked="panel.legend.show" ng-change="render();">
<label class="small">Align as table</label><input type="checkbox" ng-model="panel.legend.alignAsTable" ng-checked="panel.legend.alignAsTable">
</div>
<div class="editor-option">
<label class="small">Include Values</label><input type="checkbox" ng-model="panel.legend.values" ng-checked="panel.legend.values" ng-change="render();">
<label class="small">Right side</label><input type="checkbox" ng-model="panel.legend.rightSide" ng-change="render();" ng-checked="panel.legend.rightSide">
</div>
</div>
......@@ -101,6 +79,29 @@
</div>
<div class="section">
<h5>Grid thresholds</h5>
<div class="editor-option">
<label class="small">Level1</label>
<input type="number" class="input-small" ng-model="panel.grid.threshold1" ng-change="render()" ng-model-onblur />
</div>
<div class="editor-option">
<label class="small">Color</label>
<spectrum-picker ng-model="panel.grid.threshold1Color" ng-change="render()" ></spectrum-picker>
</div>
<div class="editor-option">
<label class="small">Level2</label>
<input type="number" class="input-small" ng-model="panel.grid.threshold2" ng-change="render()" ng-model-onblur />
</div>
<div class="editor-option">
<label class="small">Color</label>
<spectrum-picker ng-model="panel.grid.threshold2Color" ng-change="render()" ></spectrum-picker>
</div>
<div class="editor-option">
<label class="small">Line mode</label><input type="checkbox" ng-model="panel.grid.thresholdLine" ng-checked="panel.grid.thresholdLine" ng-change="render();">
</div>
</div>
<div class="section">
<h5>Show Axes</h5>
<div class="editor-option">
<label class="small">X-Axis</label><input type="checkbox" ng-model="panel['x-axis']" ng-checked="panel['x-axis']" ng-change="render()">
......
<span ng-show="panel.legend.show"
ng-class="{'pull-right': series.yaxis === 2, 'hidden-series': hiddenSeries[series.alias]}"
ng-repeat='series in legend'
class="histogram-legend">
<i class='icon-minus pointer'
ng-style="{color: series.color}"
bs-popover="'colorPopup.html'"
>
</i>
<span class='small histogram-legend-item'>
<section class="graph-legend" ng-class="{'graph-legend-table': panel.legend.alignAsTable}">
<div class="graph-legend-series"
ng-repeat='series in legend'
ng-class="{'pull-right': series.yaxis === 2, 'graph-legend-series-hidden': hiddenSeries[series.alias]}"
>
<div class="graph-legend-icon">
<i class='icon-minus pointer' ng-style="{color: series.color}" bs-popover="'colorPopup.html'">
</i>
</div>
<div class="graph-legend-alias small">
<a ng-click="toggleSeries(series, $event)" data-unique="1" data-placement="{{series.yaxis === 2 ? 'bottomRight' : 'bottomLeft'}}">
{{series.alias}}
</a>
<span ng-if="panel.legend.values">
<span ng-show="panel.legend.current">
&nbsp;&nbsp;Current: {{series.current}}&nbsp;
</span>
<span ng-show="panel.legend.min">
&nbsp;&nbsp;Min: {{series.min}}&nbsp;
</span>
<span ng-show="panel.legend.max">
&nbsp;&nbsp;Max: {{series.max}}&nbsp;
</span>
<span ng-show="panel.legend.total">
&nbsp;&nbsp;Total: {{series.total}}&nbsp;
</span>
<span ng-show="panel.legend.avg">
&nbsp;&nbsp;Avg: {{series.avg}}&nbsp;
</span>
</span>
</span>
</span>
</div>
<div class="graph-legend-value small" ng-show="panel.legend.values && panel.legend.current">
Current: {{series.current}}
</div>
<div class="graph-legend-value small" ng-show="panel.legend.values && panel.legend.min">
Min: {{series.min}}
</div>
<div class="graph-legend-value small" ng-show="panel.legend.values && panel.legend.max">
Max: {{series.max}}
</div>
<div class="graph-legend-value small" ng-show="panel.legend.values && panel.legend.total">
Total: {{series.total}}
</div>
<div class="graph-legend-value small" ng-show="panel.legend.values && panel.legend.avg">
Avg: {{series.avg}}
</div>
</div>
</section>
<script type="text/ng-template" id="colorPopup.html">
<div class="histogram-legend-popover">
<div class="graph-legend-popover">
<a class="close" ng-click="dismiss();" href="">×</a>
<div class="editor-row small" style="padding-bottom: 0;">
......
......@@ -3,21 +3,47 @@
style="min-height:{{panel.height || row.height}}"
ng-class="{'panel-fullscreen': fullscreen}">
<div style="position: relative">
<!-- <table style="width: 100%">
<tr>
<td style="width: 100%">
<div style="position: relative">
<div ng-if="datapointsWarning" class="datapoints-warning">
<span class="small" ng-show="!datapointsCount">No datapoints <tip>Can be caused by timezone mismatch between browser and graphite server</tip></span>
<span class="small" ng-show="datapointsOutside">Datapoints outside time range <tip>Can be caused by timezone mismatch between browser and graphite server</tip></span>
</div>
<div ng-if="datapointsWarning" class="datapoints-warning">
<span class="small" ng-show="!datapointsCount">No datapoints <tip>Can be caused by timezone mismatch between browser and graphite server</tip></span>
<span class="small" ng-show="datapointsOutside">Datapoints outside time range <tip>Can be caused by timezone mismatch between browser and graphite server</tip></span>
</div>
<div grafana-graph class="pointer histogram-chart">
</div>
<div grafana-graph class="pointer histogram-chart">
</div>
</div>
</div>
</td>
<td>
<div ng-if="panel.legend.show" ng-include="'app/panels/graph/legend.html'">
</div>
</td>
</tr>
</table> -->
<div class="graph-wrapper" ng-class="{'graph-legend-rightside': panel.legend.rightSide}">
<div class="graph-canvas-wrapper">
<div ng-if="panel.legend" class="grafana-legend-container">
<div ng-include="'app/panels/graph/legend.html'"></div>
<div ng-if="datapointsWarning" class="datapoints-warning">
<span class="small" ng-show="!datapointsCount">No datapoints <tip>Can be caused by timezone mismatch between browser and graphite server</tip></span>
<span class="small" ng-show="datapointsOutside">Datapoints outside time range <tip>Can be caused by timezone mismatch between browser and graphite server</tip></span>
</div>
<div grafana-graph class="pointer histogram-chart">
</div>
</div>
<div class="graph-legend-wrapper"
ng-if="panel.legend.show"
ng-include="'app/panels/graph/legend.html'">
</div>
</div>
<div class="clearfix"></div>
<div class="panel-full-edit-tabs" ng-if="editMode">
......
......@@ -25,7 +25,6 @@ define([
'jquery.flot.events',
'jquery.flot.selection',
'jquery.flot.time',
'jquery.flot.byte',
'jquery.flot.stack',
'jquery.flot.stackpercent'
],
......
......@@ -153,5 +153,18 @@
</div>
</div>
</div>
<div class="pull-left metrics-editor-help" style="margin-top: 30px;">
<div class="span6">
<span class="pointer">
<i class="icon-question-sign"></i> alias patterns:
</span>
<ul class="hide">
<li>$s = series name</li>
<li>$g = group by</li>
<li>$[0-9] part of series name for series names seperated by dots.</li>
<ul>
</div>
</div>
</div>
<div ng-include src="datasource.editorSrc"></div>
<div class="editor-row" style="margin-top: 20px">
<div class="editor-row" style="margin-top: 30px">
<button class="btn btn-success pull-right" ng-click="add_target(panel.target)">Add query</button>
<div class="btn-group pull-right" style="margin-right: 10px;">
......
define([
'underscore',
],
function (_) {
'use strict';
function InfluxSeries(options) {
this.seriesList = options.seriesList;
this.alias = options.alias;
this.groupByField = options.groupByField;
}
var p = InfluxSeries.prototype;
p.getTimeSeries = function() {
var output = [];
var self = this;
var i;
_.each(self.seriesList, function(series) {
var seriesName;
var timeCol = series.columns.indexOf('time');
var valueCol = 1;
var groupByCol = -1;
if (self.groupByField) {
groupByCol = series.columns.indexOf(self.groupByField);
}
// find value column
_.each(series.columns, function(column, index) {
if (column !== 'time' && column !== 'sequence_number' && column !== self.groupByField) {
valueCol = index;
}
});
var groups = {};
if (self.groupByField) {
groups = _.groupBy(series.points, function (point) {
return point[groupByCol];
});
}
else {
groups[series.columns[valueCol]] = series.points;
}
_.each(groups, function(groupPoints, key) {
var datapoints = [];
for (i = 0; i < groupPoints.length; i++) {
var metricValue = isNaN(groupPoints[i][valueCol]) ? null : groupPoints[i][valueCol];
datapoints[i] = [metricValue, groupPoints[i][timeCol]];
}
seriesName = series.name + '.' + key;
if (self.alias) {
seriesName = self.createNameForSeries(series.name, key);
}
output.push({ target: seriesName, datapoints: datapoints });
});
});
return output;
};
p.createNameForSeries = function(seriesName, groupByColValue) {
var name = this.alias
.replace('$s', seriesName);
var segments = seriesName.split('.');
for (var i = 0; i < segments.length; i++) {
if (segments[i].length > 0) {
name = name.replace('$' + i, segments[i]);
}
}
if (this.groupByField) {
name = name.replace('$g', groupByColValue);
}
return name;
};
return InfluxSeries;
});
\ No newline at end of file
define([
'angular',
'underscore',
'kbn'
'kbn',
'./influxSeries'
],
function (angular, _, kbn) {
function (angular, _, kbn, InfluxSeries) {
'use strict';
var module = angular.module('kibana.services');
......@@ -194,57 +195,14 @@ function (angular, _, kbn) {
return deferred.promise;
};
function handleInfluxQueryResponse(alias, groupByField, data) {
var output = [];
_.each(data, function(series) {
var seriesName;
var timeCol = series.columns.indexOf('time');
var valueCol = 1;
var groupByCol = -1;
if (groupByField) {
groupByCol = series.columns.indexOf(groupByField);
}
// find value column
_.each(series.columns, function(column, index) {
if (column !== 'time' && column !== 'sequence_number' && column !== groupByField) {
valueCol = index;
}
});
var groups = {};
if (groupByField) {
groups = _.groupBy(series.points, function (point) {
return point[groupByCol];
});
}
else {
groups[series.columns[valueCol]] = series.points;
}
_.each(groups, function(groupPoints, key) {
var datapoints = [];
for (var i = 0; i < groupPoints.length; i++) {
var metricValue = isNaN(groupPoints[i][valueCol]) ? null : groupPoints[i][valueCol];
datapoints[i] = [metricValue, groupPoints[i][timeCol]];
}
seriesName = alias ? alias : (series.name + '.' + key);
// if mulitple groups append key to alias
if (alias && groupByField) {
seriesName += key;
}
output.push({ target: seriesName, datapoints: datapoints });
});
function handleInfluxQueryResponse(alias, groupByField, seriesList) {
var influxSeries = new InfluxSeries({
seriesList: seriesList,
alias: alias,
groupByField: groupByField
});
return output;
return influxSeries.getTimeSeries();
}
function getTimeFilter(options) {
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
@import "submenu.less";
@import "graph.less";
@import "bootstrap-tagsinput.less";
.hide-controls {
......@@ -123,12 +124,6 @@
font-size: 12px;
}
.hidden-series {
a {
color: darken(@linkColor, 45%);
}
}
.panel-fullscreen {
z-index: 100;
display: block !important;
......@@ -154,57 +149,10 @@
right: -10000px;
}
// Graphite Graph Legends
.grafana-legend-container {
margin: 0 15px;
text-align: left;
position: relative;
top: 2px;
}
.grafana-legend-container .popover-content {
padding: 0;
}
.histogram-legend {
display:inline-block;
padding: 0 4px;
i {
position: relative;
top: 2px;
}
}
.histogram-legend-item {
display:inline-block;
}
.histogram-chart {
position:relative;
}
.histogram-legend-popover {
width: 200px;
label {
display: inline-block;
}
.btn {
padding: 1px 3px;
margin-right: 0px;
line-height: initial;
}
.close {
margin-right: 5px;
color: @linkColor;
opacity: 0.7;
text-shadow: none;
}
.editor-row {
padding: 5px;
}
}
.panel-full-edit-tabs {
margin-top: 10px;
min-height: 250px;
......@@ -520,3 +468,10 @@ select.grafana-target-segment-input {
padding-top: 15px;
text-align: left;
}
.metrics-editor-help:hover {
.hide {
display: block;
}
}
\ No newline at end of file
.graph-legend {
margin: 0 20px;
text-align: left;
position: relative;
top: 2px;
.popover-content {
padding: 0;
}
}
.graph-legend-icon {
position: relative;
top: 2px;
}
.graph-legend-series,
.graph-legend-icon,
.graph-legend-alias,
.graph-legend-value {
display: inline-block;
white-space: nowrap;
}
.graph-legend-series {
padding-left: 10px;
}
.graph-legend-value {
padding-left: 6px;
}
.graph-legend-table {
display: table;
.graph-legend-series {
display: table-row;
padding-left: 0;
&.pull-right {
float: none;
.graph-legend-alias::after {
content: 'y\00B2';
}
}
}
.graph-legend-alias {
display: table-cell;
white-space: nowrap;
}
.graph-legend-icon {
display: table-cell;
white-space: nowrap;
padding: 0 4px;
}
.graph-legend-value {
display: table-cell;
white-space: nowrap;
padding-left: 15px;
}
}
.graph-legend-rightside {
&.graph-wrapper {
display: table;
width: 100%;
}
.graph-canvas-wrapper {
display: table-cell;
width: 100%;
position: relative;
}
.graph-legend-wrapper {
display: table-cell;
vertical-align: top;
position: relative;
left: -4px;
}
.graph-legend {
margin: 0;
}
.graph-legend-series {
display: block;
padding-left: 0px;
}
.graph-legend-table .graph-legend-series {
display: table-row;
}
}
.graph-legend-series-hidden {
a {
color: darken(@linkColor, 45%);
}
}
.graph-legend-popover {
width: 200px;
label {
display: inline-block;
}
.btn {
padding: 1px 3px;
margin-right: 0px;
line-height: initial;
}
.close {
margin-right: 5px;
color: @linkColor;
opacity: 0.7;
text-shadow: none;
}
.editor-row {
padding: 5px;
}
}
define([
'services/influxdb/influxSeries'
], function(InfluxSeries) {
'use strict';
describe('when generating timeseries from influxdb response', function() {
describe('given two series', function() {
var series = new InfluxSeries({
seriesList: [
{
columns: ['time', 'mean', 'sequence_number'],
name: 'prod.server1.cpu',
points: [[1402596000, 10, 1], [1402596001, 12, 2]]
},
{
columns: ['time', 'mean', 'sequence_number'],
name: 'prod.server2.cpu',
points: [[1402596000, 15, 1], [1402596001, 16, 2]]
}
]
});
var result = series.getTimeSeries();
it('should generate two time series', function() {
expect(result.length).to.be(2);
expect(result[0].target).to.be('prod.server1.cpu.mean');
expect(result[0].datapoints[0][0]).to.be(10);
expect(result[0].datapoints[0][1]).to.be(1402596000);
expect(result[0].datapoints[1][0]).to.be(12);
expect(result[0].datapoints[1][1]).to.be(1402596001);
expect(result[1].target).to.be('prod.server2.cpu.mean');
expect(result[1].datapoints[0][0]).to.be(15);
expect(result[1].datapoints[0][1]).to.be(1402596000);
expect(result[1].datapoints[1][0]).to.be(16);
expect(result[1].datapoints[1][1]).to.be(1402596001);
});
});
describe('given an alias format', function() {
var series = new InfluxSeries({
seriesList: [
{
columns: ['time', 'mean', 'sequence_number'],
name: 'prod.server1.cpu',
points: [[1402596000, 10, 1], [1402596001, 12, 2]]
}
],
alias: '$s.testing'
});
var result = series.getTimeSeries();
it('should generate correct series name', function() {
expect(result[0].target).to.be('prod.server1.cpu.testing');
});
});
describe('given an alias format with segment numbers', function() {
var series = new InfluxSeries({
seriesList: [
{
columns: ['time', 'mean', 'sequence_number'],
name: 'prod.server1.cpu',
points: [[1402596000, 10, 1], [1402596001, 12, 2]]
}
],
alias: '$1.mean'
});
var result = series.getTimeSeries();
it('should generate correct series name', function() {
expect(result[0].target).to.be('server1.mean');
});
});
describe('given an alias format with group by field', function() {
var series = new InfluxSeries({
seriesList: [
{
columns: ['time', 'mean', 'host'],
name: 'prod.cpu',
points: [[1402596000, 10, 'A']]
}
],
groupByField: 'host',
alias: '$g.$1'
});
var result = series.getTimeSeries();
it('should generate correct series name', function() {
expect(result[0].target).to.be('A.cpu');
});
});
describe('given group by column', function() {
var series = new InfluxSeries({
seriesList: [
{
columns: ['time', 'mean', 'host'],
name: 'prod.cpu',
points: [
[1402596000, 10, 'A'],
[1402596001, 11, 'A'],
[1402596000, 5, 'B'],
[1402596001, 6, 'B'],
]
}
],
groupByField: 'host'
});
var result = series.getTimeSeries();
it('should generate two time series', function() {
expect(result.length).to.be(2);
expect(result[0].target).to.be('prod.cpu.A');
expect(result[0].datapoints[0][0]).to.be(10);
expect(result[0].datapoints[0][1]).to.be(1402596000);
expect(result[0].datapoints[1][0]).to.be(11);
expect(result[0].datapoints[1][1]).to.be(1402596001);
expect(result[1].target).to.be('prod.cpu.B');
expect(result[1].datapoints[0][0]).to.be(5);
expect(result[1].datapoints[0][1]).to.be(1402596000);
expect(result[1].datapoints[1][0]).to.be(6);
expect(result[1].datapoints[1][1]).to.be(1402596001);
});
});
});
});
......@@ -42,7 +42,6 @@ require.config({
'jquery.flot.stack': '../vendor/jquery/jquery.flot.stack',
'jquery.flot.stackpercent':'../vendor/jquery/jquery.flot.stackpercent',
'jquery.flot.time': '../vendor/jquery/jquery.flot.time',
'jquery.flot.byte': '../vendor/jquery/jquery.flot.byte',
modernizr: '../vendor/modernizr-2.6.1',
},
......@@ -75,7 +74,6 @@ require.config({
'jquery-ui': ['jquery'],
'jquery.flot': ['jquery'],
'jquery.flot.byte': ['jquery', 'jquery.flot'],
'jquery.flot.pie': ['jquery', 'jquery.flot'],
'jquery.flot.events': ['jquery', 'jquery.flot'],
'jquery.flot.selection':['jquery', 'jquery.flot'],
......@@ -96,7 +94,6 @@ require.config({
'bootstrap-tagsinput': ['jquery'],
timepicker: ['jquery', 'bootstrap'],
datepicker: ['jquery', 'bootstrap'],
}
......@@ -128,8 +125,10 @@ require([
'specs/gfunc-specs',
'specs/filterSrv-specs',
'specs/kbn-format-specs',
'specs/influxSeries-specs'
], function () {
window.__karma__.start();
});
});
(function ($) {
"use strict";
var options = {};
//Round to nearby lower multiple of base
function floorInBase(n, base) {
return base * Math.floor(n / base);
}
function init(plot) {
plot.hooks.processDatapoints.push(function (plot) {
$.each(plot.getAxes(), function(axisName, axis) {
var opts = axis.options;
if (opts.mode === "byte" || opts.mode === "byteRate") {
axis.tickGenerator = function (axis) {
var returnTicks = [],
tickSize = 2,
delta = axis.delta,
steps = 0,
tickMin = 0,
tickVal,
tickCount = 0;
//Set the reference for the formatter
if (opts.mode === "byteRate") {
axis.rate = true;
}
//Enforce maximum tick Decimals
if (typeof opts.tickDecimals === "number") {
axis.tickDecimals = opts.tickDecimals;
} else {
axis.tickDecimals = 2;
}
//Count the steps
while (Math.abs(delta) >= 1024) {
steps++;
delta /= 1024;
}
//Set the tick size relative to the remaining delta
while (tickSize <= 1024) {
if (delta <= tickSize) {
break;
}
tickSize *= 2;
}
//Tell flot the tickSize we've calculated
if (typeof opts.minTickSize !== "undefined" && tickSize < opts.minTickSize) {
axis.tickSize = opts.minTickSize;
} else {
axis.tickSize = tickSize * Math.pow(1024,steps);
}
//Calculate the new ticks
tickMin = floorInBase(axis.min, axis.tickSize);
do {
tickVal = tickMin + (tickCount++) * axis.tickSize;
returnTicks.push(tickVal);
} while (tickVal < axis.max);
return returnTicks;
};
axis.tickFormatter = function(size, axis) {
var ext, steps = 0;
while (Math.abs(size) >= 1024) {
steps++;
size /= 1024;
}
switch (steps) {
case 0: ext = " B"; break;
case 1: ext = " KB"; break;
case 2: ext = " MB"; break;
case 3: ext = " GB"; break;
case 4: ext = " TB"; break;
case 5: ext = " PB"; break;
case 6: ext = " EB"; break;
case 7: ext = " ZB"; break;
case 8: ext = " YB"; break;
}
if (typeof axis.rate !== "undefined") {
ext += "/s";
}
return (size.toFixed(axis.tickDecimals) + ext);
};
}
});
});
}
$.plot.plugins.push({
init: init,
options: options,
name: "byte",
version: "0.1"
});
})(jQuery);
\ No newline at end of file
/* Flot plugin for stacking data sets rather than overlyaing them.
Copyright (c) 2007-2013 IOLA and Ole Laursen.
Copyright (c) 2007-2014 IOLA and Ole Laursen.
Licensed under the MIT license.
The plugin assumes the data is sorted on x (or y if stacking horizontally).
......@@ -14,16 +14,16 @@ Two or more series are stacked when their "stack" attribute is set to the same
key (which can be any number or string or just "true"). To specify the default
stack, you can set the stack option like this:
series: {
stack: null/false, true, or a key (number/string)
}
series: {
stack: null/false, true, or a key (number/string)
}
You can also specify it for a single series, like this:
$.plot( $("#placeholder"), [{
data: [ ... ],
stack: true
}])
$.plot( $("#placeholder"), [{
data: [ ... ],
stack: true
}])
The stacking order is determined by the order of the data series in the array
(later series end up on top of the previous).
......@@ -39,21 +39,21 @@ charts or filled areas).
var options = {
series: { stack: null } // or number/string
};
function init(plot) {
function findMatchingSeries(s, allseries) {
var res = null;
for (var i = 0; i < allseries.length; ++i) {
if (s == allseries[i])
break;
if (allseries[i].stack == s.stack)
res = allseries[i];
}
return res;
}
function stackData(plot, s, datapoints) {
if (s.stack == null || s.stack === false)
return;
......@@ -118,7 +118,7 @@ charts or filled areas).
newpoints[l + accumulateOffset] += qy;
bottom = qy;
i += ps;
j += otherps;
}
......@@ -131,7 +131,7 @@ charts or filled areas).
newpoints.push(intery + qy);
for (m = 2; m < ps; ++m)
newpoints.push(points[i + m]);
bottom = qy;
bottom = qy;
}
j += otherps;
......@@ -142,22 +142,22 @@ charts or filled areas).
i += ps;
continue;
}
for (m = 0; m < ps; ++m)
newpoints.push(points[i + m]);
// we might be able to interpolate a point below,
// this can give us a better y
if (withlines && j > 0 && otherpoints[j - otherps] != null)
bottom = qy + (otherpoints[j - otherps + accumulateOffset] - qy) * (px - qx) / (otherpoints[j - otherps + keyOffset] - qx);
newpoints[l + accumulateOffset] += bottom;
i += ps;
}
fromgap = false;
if (l != newpoints.length && withbottom)
newpoints[l + 2] += bottom;
}
......@@ -175,10 +175,10 @@ charts or filled areas).
datapoints.points = newpoints;
}
plot.hooks.processDatapoints.push(stackData);
}
$.plot.plugins.push({
init: init,
options: options,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment