mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
got flot graph showing!
This commit is contained in:
parent
50e42c8bdd
commit
bbcc1ef9ee
@ -43,6 +43,9 @@ require.config({
|
||||
|
||||
modernizr: '../vendor/modernizr-2.6.1',
|
||||
elasticjs: '../vendor/elasticjs/elastic-angular-client',
|
||||
|
||||
'ts-widget': '../vendor/timeserieswidget/jquery.tswidget',
|
||||
'ts-graphite-helpers': '../vendor/timeserieswidget/graphite_helpers'
|
||||
},
|
||||
shim: {
|
||||
underscore: {
|
||||
@ -91,7 +94,16 @@ require.config({
|
||||
timepicker: ['jquery', 'bootstrap'],
|
||||
datepicker: ['jquery', 'bootstrap'],
|
||||
|
||||
elasticjs: ['angular', '../vendor/elasticjs/elastic']
|
||||
elasticjs: ['angular', '../vendor/elasticjs/elastic'],
|
||||
|
||||
'ts-widget': [
|
||||
'jquery',
|
||||
'jquery.flot',
|
||||
'jquery.flot.selection',
|
||||
'jquery.flot.stack',
|
||||
'jquery.flot.time',
|
||||
'ts-graphite-helpers'
|
||||
]
|
||||
},
|
||||
waitSeconds: 60,
|
||||
});
|
||||
|
@ -50,7 +50,8 @@
|
||||
"group": [
|
||||
"default"
|
||||
],
|
||||
"type": "graph"
|
||||
"type": "graph",
|
||||
"someprop": "hej from config"
|
||||
}
|
||||
],
|
||||
"notice": true
|
||||
|
@ -1,9 +1,13 @@
|
||||
<div ng-controller='graph' ng-init="init()">
|
||||
<h2>{{saySomething}}</h2>
|
||||
<h2>{{panel.someprop}}</h2>
|
||||
|
||||
<ul>
|
||||
<li>{{panel.graphiteUrl}}</li>
|
||||
<li>{{panel.targets}}</li>
|
||||
</ul>
|
||||
|
||||
<mychart ng-model='data'></mychart>
|
||||
<div class="chart_container flot" id="chart_container_flot">
|
||||
<div class="chart" id="chart_flot" height="300px" width="700px"></div>
|
||||
<div class="legend" id="legend_flot_simple"></div>
|
||||
</div>
|
||||
</div>
|
@ -1,21 +1,11 @@
|
||||
/** @scratch /panels/5
|
||||
* include::panels/text.asciidoc[]
|
||||
*/
|
||||
|
||||
/** @scratch /panels/text/0
|
||||
* == text
|
||||
* Status: *Stable*
|
||||
*
|
||||
* The text panel is used for displaying static text formated as markdown, sanitized html or as plain
|
||||
* text.
|
||||
*
|
||||
*/
|
||||
define([
|
||||
'jquery',
|
||||
'angular',
|
||||
'app',
|
||||
'underscore'
|
||||
'underscore',
|
||||
'ts-widget'
|
||||
],
|
||||
function (angular, app, _) {
|
||||
function ($, angular, app, _, timeseriesWidget) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('kibana.panels.graph', []);
|
||||
@ -40,4 +30,50 @@ function (angular, app, _) {
|
||||
|
||||
});
|
||||
|
||||
angular
|
||||
.module('kibana.directives')
|
||||
.directive('mychart', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function (scope, elem, attrs) {
|
||||
var tsData = {
|
||||
graphite_url: 'http://localhost:3030/data',
|
||||
from: '-24hours',
|
||||
until: 'now',
|
||||
height: '300',
|
||||
width: '740',
|
||||
targets: [
|
||||
{
|
||||
name: 'series 1',
|
||||
color: '#CC6699',
|
||||
target: 'random1',
|
||||
}
|
||||
],
|
||||
title: 'horizontal title',
|
||||
vtitle: 'vertical title',
|
||||
drawNullAsZero: false,
|
||||
legend: { container: '#legend_flot_simple', noColumns: 1 },
|
||||
};
|
||||
$("#chart_flot").graphiteFlot(tsData, function(err) {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
console.log('asd');
|
||||
$(elem).html('NJEEEJ!');
|
||||
/*// If the data changes somehow, update it in the chart
|
||||
scope.$watch('data', function(v){
|
||||
if(!chart){
|
||||
chart = $.plot(elem, v , options);
|
||||
elem.show();
|
||||
}else{
|
||||
chart.setData(v);
|
||||
chart.setupGrid();
|
||||
chart.draw();
|
||||
}
|
||||
});*/
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
});
|
@ -38,21 +38,9 @@ function (Settings) {
|
||||
* dashboard, but this list is used in the "add panel" interface.
|
||||
*/
|
||||
panel_names: [
|
||||
'histogram',
|
||||
'map',
|
||||
'pie',
|
||||
'table',
|
||||
'filtering',
|
||||
'timepicker',
|
||||
'graph',
|
||||
'text',
|
||||
'hits',
|
||||
'dashcontrol',
|
||||
'column',
|
||||
'trends',
|
||||
'bettermap',
|
||||
'query',
|
||||
'terms',
|
||||
'sparklines'
|
||||
'column'
|
||||
]
|
||||
});
|
||||
});
|
||||
|
29
src/vendor/timeserieswidget/graphite_helpers.js
vendored
Normal file
29
src/vendor/timeserieswidget/graphite_helpers.js
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
String.prototype.graphiteGlob = function(glob) {
|
||||
var regex = '^';
|
||||
for (var i = 0; i < glob.length; i++ ) {
|
||||
var c = glob.charAt(i);
|
||||
switch (c) {
|
||||
case '*':
|
||||
regex += '[^\.]+';
|
||||
break;
|
||||
case '.':
|
||||
regex += '\\.';
|
||||
break;
|
||||
default:
|
||||
regex += c;
|
||||
}
|
||||
}
|
||||
regex += '$';
|
||||
return this.match(regex);
|
||||
}
|
||||
/*
|
||||
if (!"stats.dfs4.timer".graphiteGlob('stats.*.timer')) {
|
||||
console.log('fail 1');
|
||||
}
|
||||
if ("stats.dfs4.timer".graphiteGlob('statsd.*.timer')) {
|
||||
console.log('fail 2');
|
||||
}
|
||||
if ("stats.dfs4.foo.timer".graphiteGlob('stats.*.timer')) {
|
||||
console.log('fail 3');
|
||||
}
|
||||
*/
|
416
src/vendor/timeserieswidget/jquery.flot.axislabels.js
vendored
Normal file
416
src/vendor/timeserieswidget/jquery.flot.axislabels.js
vendored
Normal file
@ -0,0 +1,416 @@
|
||||
/*
|
||||
Axis Labels Plugin for flot.
|
||||
http://github.com/markrcote/flot-axislabels
|
||||
|
||||
Original code is Copyright (c) 2010 Xuan Luo.
|
||||
Original code was released under the GPLv3 license by Xuan Luo, September 2010.
|
||||
Original code was rereleased under the MIT license by Xuan Luo, April 2012.
|
||||
|
||||
Improvements by Mark Cote.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
(function ($) {
|
||||
var options = { };
|
||||
|
||||
function canvasSupported() {
|
||||
return !!document.createElement('canvas').getContext;
|
||||
}
|
||||
|
||||
function canvasTextSupported() {
|
||||
if (!canvasSupported()) {
|
||||
return false;
|
||||
}
|
||||
var dummy_canvas = document.createElement('canvas');
|
||||
var context = dummy_canvas.getContext('2d');
|
||||
return typeof context.fillText == 'function';
|
||||
}
|
||||
|
||||
function css3TransitionSupported() {
|
||||
var div = document.createElement('div');
|
||||
return typeof div.style.MozTransition != 'undefined' // Gecko
|
||||
|| typeof div.style.OTransition != 'undefined' // Opera
|
||||
|| typeof div.style.webkitTransition != 'undefined' // WebKit
|
||||
|| typeof div.style.transition != 'undefined';
|
||||
}
|
||||
|
||||
|
||||
function AxisLabel(axisName, position, padding, plot, opts) {
|
||||
this.axisName = axisName;
|
||||
this.position = position;
|
||||
this.padding = padding;
|
||||
this.plot = plot;
|
||||
this.opts = opts;
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
}
|
||||
|
||||
|
||||
CanvasAxisLabel.prototype = new AxisLabel();
|
||||
CanvasAxisLabel.prototype.constructor = CanvasAxisLabel;
|
||||
function CanvasAxisLabel(axisName, position, padding, plot, opts) {
|
||||
AxisLabel.prototype.constructor.call(this, axisName, position, padding,
|
||||
plot, opts);
|
||||
}
|
||||
|
||||
CanvasAxisLabel.prototype.calculateSize = function() {
|
||||
if (!this.opts.axisLabelFontSizePixels)
|
||||
this.opts.axisLabelFontSizePixels = 14;
|
||||
if (!this.opts.axisLabelFontFamily)
|
||||
this.opts.axisLabelFontFamily = 'sans-serif';
|
||||
|
||||
var textWidth = this.opts.axisLabelFontSizePixels + this.padding;
|
||||
var textHeight = this.opts.axisLabelFontSizePixels + this.padding;
|
||||
if (this.position == 'left' || this.position == 'right') {
|
||||
this.width = this.opts.axisLabelFontSizePixels + this.padding;
|
||||
this.height = 0;
|
||||
} else {
|
||||
this.width = 0;
|
||||
this.height = this.opts.axisLabelFontSizePixels + this.padding;
|
||||
}
|
||||
};
|
||||
|
||||
CanvasAxisLabel.prototype.draw = function(box) {
|
||||
var ctx = this.plot.getCanvas().getContext('2d');
|
||||
ctx.save();
|
||||
ctx.font = this.opts.axisLabelFontSizePixels + 'px ' +
|
||||
this.opts.axisLabelFontFamily;
|
||||
var width = ctx.measureText(this.opts.axisLabel).width;
|
||||
var height = this.opts.axisLabelFontSizePixels;
|
||||
var x, y, angle = 0;
|
||||
if (this.position == 'top') {
|
||||
x = box.left + box.width/2 - width/2;
|
||||
y = box.top + height*0.72;
|
||||
} else if (this.position == 'bottom') {
|
||||
x = box.left + box.width/2 - width/2;
|
||||
y = box.top + box.height - height*0.72;
|
||||
} else if (this.position == 'left') {
|
||||
x = box.left + height*0.72;
|
||||
y = box.height/2 + box.top + width/2;
|
||||
angle = -Math.PI/2;
|
||||
} else if (this.position == 'right') {
|
||||
x = box.left + box.width - height*0.72;
|
||||
y = box.height/2 + box.top - width/2;
|
||||
angle = Math.PI/2;
|
||||
}
|
||||
ctx.translate(x, y);
|
||||
ctx.rotate(angle);
|
||||
ctx.fillText(this.opts.axisLabel, 0, 0);
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
|
||||
HtmlAxisLabel.prototype = new AxisLabel();
|
||||
HtmlAxisLabel.prototype.constructor = HtmlAxisLabel;
|
||||
function HtmlAxisLabel(axisName, position, padding, plot, opts) {
|
||||
AxisLabel.prototype.constructor.call(this, axisName, position,
|
||||
padding, plot, opts);
|
||||
}
|
||||
|
||||
HtmlAxisLabel.prototype.calculateSize = function() {
|
||||
var elem = $('<div class="axisLabels" style="position:absolute;">' +
|
||||
this.opts.axisLabel + '</div>');
|
||||
this.plot.getPlaceholder().append(elem);
|
||||
// store height and width of label itself, for use in draw()
|
||||
this.labelWidth = elem.outerWidth(true);
|
||||
this.labelHeight = elem.outerHeight(true);
|
||||
elem.remove();
|
||||
|
||||
this.width = this.height = 0;
|
||||
if (this.position == 'left' || this.position == 'right') {
|
||||
this.width = this.labelWidth + this.padding;
|
||||
} else {
|
||||
this.height = this.labelHeight + this.padding;
|
||||
}
|
||||
};
|
||||
|
||||
HtmlAxisLabel.prototype.draw = function(box) {
|
||||
this.plot.getPlaceholder().find('#' + this.axisName + 'Label').remove();
|
||||
var elem = $('<div id="' + this.axisName +
|
||||
'Label" " class="axisLabels" style="position:absolute;">'
|
||||
+ this.opts.axisLabel + '</div>');
|
||||
this.plot.getPlaceholder().append(elem);
|
||||
if (this.position == 'top') {
|
||||
elem.css('left', box.left + box.width/2 - this.labelWidth/2 + 'px');
|
||||
elem.css('top', box.top + 'px');
|
||||
} else if (this.position == 'bottom') {
|
||||
elem.css('left', box.left + box.width/2 - this.labelWidth/2 + 'px');
|
||||
elem.css('top', box.top + box.height - this.labelHeight + 'px');
|
||||
} else if (this.position == 'left') {
|
||||
elem.css('top', box.top + box.height/2 - this.labelHeight/2 + 'px');
|
||||
elem.css('left', box.left + 'px');
|
||||
} else if (this.position == 'right') {
|
||||
elem.css('top', box.top + box.height/2 - this.labelHeight/2 + 'px');
|
||||
elem.css('left', box.left + box.width - this.labelWidth + 'px');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
CssTransformAxisLabel.prototype = new HtmlAxisLabel();
|
||||
CssTransformAxisLabel.prototype.constructor = CssTransformAxisLabel;
|
||||
function CssTransformAxisLabel(axisName, position, padding, plot, opts) {
|
||||
HtmlAxisLabel.prototype.constructor.call(this, axisName, position,
|
||||
padding, plot, opts);
|
||||
}
|
||||
|
||||
CssTransformAxisLabel.prototype.calculateSize = function() {
|
||||
HtmlAxisLabel.prototype.calculateSize.call(this);
|
||||
this.width = this.height = 0;
|
||||
if (this.position == 'left' || this.position == 'right') {
|
||||
this.width = this.labelHeight + this.padding;
|
||||
} else {
|
||||
this.height = this.labelHeight + this.padding;
|
||||
}
|
||||
};
|
||||
|
||||
CssTransformAxisLabel.prototype.transforms = function(degrees, x, y) {
|
||||
var stransforms = {
|
||||
'-moz-transform': '',
|
||||
'-webkit-transform': '',
|
||||
'-o-transform': '',
|
||||
'-ms-transform': ''
|
||||
};
|
||||
if (x != 0 || y != 0) {
|
||||
var stdTranslate = ' translate(' + x + 'px, ' + y + 'px)';
|
||||
stransforms['-moz-transform'] += stdTranslate;
|
||||
stransforms['-webkit-transform'] += stdTranslate;
|
||||
stransforms['-o-transform'] += stdTranslate;
|
||||
stransforms['-ms-transform'] += stdTranslate;
|
||||
}
|
||||
if (degrees != 0) {
|
||||
var rotation = degrees / 90;
|
||||
var stdRotate = ' rotate(' + degrees + 'deg)';
|
||||
stransforms['-moz-transform'] += stdRotate;
|
||||
stransforms['-webkit-transform'] += stdRotate;
|
||||
stransforms['-o-transform'] += stdRotate;
|
||||
stransforms['-ms-transform'] += stdRotate;
|
||||
}
|
||||
var s = 'top: 0; left: 0; ';
|
||||
for (var prop in stransforms) {
|
||||
if (stransforms[prop]) {
|
||||
s += prop + ':' + stransforms[prop] + ';';
|
||||
}
|
||||
}
|
||||
s += ';';
|
||||
return s;
|
||||
};
|
||||
|
||||
CssTransformAxisLabel.prototype.calculateOffsets = function(box) {
|
||||
var offsets = { x: 0, y: 0, degrees: 0 };
|
||||
if (this.position == 'bottom') {
|
||||
offsets.x = box.left + box.width/2 - this.labelWidth/2;
|
||||
offsets.y = box.top + box.height - this.labelHeight;
|
||||
} else if (this.position == 'top') {
|
||||
offsets.x = box.left + box.width/2 - this.labelWidth/2;
|
||||
offsets.y = box.top;
|
||||
} else if (this.position == 'left') {
|
||||
offsets.degrees = -90;
|
||||
offsets.x = box.left - this.labelWidth/2 + this.labelHeight/2;
|
||||
offsets.y = box.height/2 + box.top;
|
||||
} else if (this.position == 'right') {
|
||||
offsets.degrees = 90;
|
||||
offsets.x = box.left + box.width - this.labelWidth/2
|
||||
- this.labelHeight/2;
|
||||
offsets.y = box.height/2 + box.top;
|
||||
}
|
||||
return offsets;
|
||||
};
|
||||
|
||||
CssTransformAxisLabel.prototype.draw = function(box) {
|
||||
this.plot.getPlaceholder().find("." + this.axisName + "Label").remove();
|
||||
var offsets = this.calculateOffsets(box);
|
||||
var elem = $('<div class="axisLabels ' + this.axisName +
|
||||
'Label" style="position:absolute; ' +
|
||||
'color: ' + this.opts.color + '; ' +
|
||||
this.transforms(offsets.degrees, offsets.x, offsets.y) +
|
||||
'">' + this.opts.axisLabel + '</div>');
|
||||
this.plot.getPlaceholder().append(elem);
|
||||
};
|
||||
|
||||
|
||||
IeTransformAxisLabel.prototype = new CssTransformAxisLabel();
|
||||
IeTransformAxisLabel.prototype.constructor = IeTransformAxisLabel;
|
||||
function IeTransformAxisLabel(axisName, position, padding, plot, opts) {
|
||||
CssTransformAxisLabel.prototype.constructor.call(this, axisName,
|
||||
position, padding,
|
||||
plot, opts);
|
||||
this.requiresResize = false;
|
||||
}
|
||||
|
||||
IeTransformAxisLabel.prototype.transforms = function(degrees, x, y) {
|
||||
// I didn't feel like learning the crazy Matrix stuff, so this uses
|
||||
// a combination of the rotation transform and CSS positioning.
|
||||
var s = '';
|
||||
if (degrees != 0) {
|
||||
var rotation = degrees/90;
|
||||
while (rotation < 0) {
|
||||
rotation += 4;
|
||||
}
|
||||
s += ' filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=' + rotation + '); ';
|
||||
// see below
|
||||
this.requiresResize = (this.position == 'right');
|
||||
}
|
||||
if (x != 0) {
|
||||
s += 'left: ' + x + 'px; ';
|
||||
}
|
||||
if (y != 0) {
|
||||
s += 'top: ' + y + 'px; ';
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
IeTransformAxisLabel.prototype.calculateOffsets = function(box) {
|
||||
var offsets = CssTransformAxisLabel.prototype.calculateOffsets.call(
|
||||
this, box);
|
||||
// adjust some values to take into account differences between
|
||||
// CSS and IE rotations.
|
||||
if (this.position == 'top') {
|
||||
// FIXME: not sure why, but placing this exactly at the top causes
|
||||
// the top axis label to flip to the bottom...
|
||||
offsets.y = box.top + 1;
|
||||
} else if (this.position == 'left') {
|
||||
offsets.x = box.left;
|
||||
offsets.y = box.height/2 + box.top - this.labelWidth/2;
|
||||
} else if (this.position == 'right') {
|
||||
offsets.x = box.left + box.width - this.labelHeight;
|
||||
offsets.y = box.height/2 + box.top - this.labelWidth/2;
|
||||
}
|
||||
return offsets;
|
||||
};
|
||||
|
||||
IeTransformAxisLabel.prototype.draw = function(box) {
|
||||
CssTransformAxisLabel.prototype.draw.call(this, box);
|
||||
if (this.requiresResize) {
|
||||
var elem = this.plot.getPlaceholder().find("." + this.axisName + "Label");
|
||||
// Since we used CSS positioning instead of transforms for
|
||||
// translating the element, and since the positioning is done
|
||||
// before any rotations, we have to reset the width and height
|
||||
// in case the browser wrapped the text (specifically for the
|
||||
// y2axis).
|
||||
elem.css('width', this.labelWidth);
|
||||
elem.css('height', this.labelHeight);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function init(plot) {
|
||||
// This is kind of a hack. There are no hooks in Flot between
|
||||
// the creation and measuring of the ticks (setTicks, measureTickLabels
|
||||
// in setupGrid() ) and the drawing of the ticks and plot box
|
||||
// (insertAxisLabels in setupGrid() ).
|
||||
//
|
||||
// Therefore, we use a trick where we run the draw routine twice:
|
||||
// the first time to get the tick measurements, so that we can change
|
||||
// them, and then have it draw it again.
|
||||
var secondPass = false;
|
||||
|
||||
var axisLabels = {};
|
||||
var axisOffsetCounts = { left: 0, right: 0, top: 0, bottom: 0 };
|
||||
|
||||
var defaultPadding = 2; // padding between axis and tick labels
|
||||
plot.hooks.draw.push(function (plot, ctx) {
|
||||
var hasAxisLabels = false;
|
||||
if (!secondPass) {
|
||||
// MEASURE AND SET OPTIONS
|
||||
$.each(plot.getAxes(), function(axisName, axis) {
|
||||
var opts = axis.options // Flot 0.7
|
||||
|| plot.getOptions()[axisName]; // Flot 0.6
|
||||
if (!opts || !opts.axisLabel || !axis.show)
|
||||
return;
|
||||
|
||||
hasAxisLabels = true;
|
||||
var renderer = null;
|
||||
|
||||
if (!opts.axisLabelUseHtml &&
|
||||
navigator.appName == 'Microsoft Internet Explorer') {
|
||||
var ua = navigator.userAgent;
|
||||
var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
|
||||
if (re.exec(ua) != null) {
|
||||
rv = parseFloat(RegExp.$1);
|
||||
}
|
||||
if (rv >= 9 && !opts.axisLabelUseCanvas && !opts.axisLabelUseHtml) {
|
||||
renderer = CssTransformAxisLabel;
|
||||
} else if (!opts.axisLabelUseCanvas && !opts.axisLabelUseHtml) {
|
||||
renderer = IeTransformAxisLabel;
|
||||
} else if (opts.axisLabelUseCanvas) {
|
||||
renderer = CanvasAxisLabel;
|
||||
} else {
|
||||
renderer = HtmlAxisLabel;
|
||||
}
|
||||
} else {
|
||||
if (opts.axisLabelUseHtml || (!css3TransitionSupported() && !canvasTextSupported()) && !opts.axisLabelUseCanvas) {
|
||||
renderer = HtmlAxisLabel;
|
||||
} else if (opts.axisLabelUseCanvas || !css3TransitionSupported()) {
|
||||
renderer = CanvasAxisLabel;
|
||||
} else {
|
||||
renderer = CssTransformAxisLabel;
|
||||
}
|
||||
}
|
||||
|
||||
var padding = opts.axisLabelPadding === undefined ?
|
||||
defaultPadding : opts.axisLabelPadding;
|
||||
|
||||
axisLabels[axisName] = new renderer(axisName,
|
||||
axis.position, padding,
|
||||
plot, opts);
|
||||
|
||||
// flot interprets axis.labelHeight and .labelWidth as
|
||||
// the height and width of the tick labels. We increase
|
||||
// these values to make room for the axis label and
|
||||
// padding.
|
||||
|
||||
axisLabels[axisName].calculateSize();
|
||||
|
||||
// AxisLabel.height and .width are the size of the
|
||||
// axis label and padding.
|
||||
axis.labelHeight += axisLabels[axisName].height;
|
||||
axis.labelWidth += axisLabels[axisName].width;
|
||||
opts.labelHeight = axis.labelHeight;
|
||||
opts.labelWidth = axis.labelWidth;
|
||||
});
|
||||
// if there are axis labels re-draw with new label widths and heights
|
||||
if (hasAxisLabels) {
|
||||
secondPass = true;
|
||||
plot.setupGrid();
|
||||
plot.draw();
|
||||
}
|
||||
} else {
|
||||
// DRAW
|
||||
$.each(plot.getAxes(), function(axisName, axis) {
|
||||
var opts = axis.options // Flot 0.7
|
||||
|| plot.getOptions()[axisName]; // Flot 0.6
|
||||
if (!opts || !opts.axisLabel || !axis.show)
|
||||
return;
|
||||
|
||||
axisLabels[axisName].draw(axis.box);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$.plot.plugins.push({
|
||||
init: init,
|
||||
options: options,
|
||||
name: 'axisLabels',
|
||||
version: '2.0b0'
|
||||
});
|
||||
})(jQuery);
|
2980
src/vendor/timeserieswidget/jquery.flot.js
vendored
Normal file
2980
src/vendor/timeserieswidget/jquery.flot.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
360
src/vendor/timeserieswidget/jquery.flot.selection.js
vendored
Normal file
360
src/vendor/timeserieswidget/jquery.flot.selection.js
vendored
Normal file
@ -0,0 +1,360 @@
|
||||
/* Flot plugin for selecting regions of a plot.
|
||||
|
||||
Copyright (c) 2007-2013 IOLA and Ole Laursen.
|
||||
Licensed under the MIT license.
|
||||
|
||||
The plugin supports these options:
|
||||
|
||||
selection: {
|
||||
mode: null or "x" or "y" or "xy",
|
||||
color: color,
|
||||
shape: "round" or "miter" or "bevel",
|
||||
minSize: number of pixels
|
||||
}
|
||||
|
||||
Selection support is enabled by setting the mode to one of "x", "y" or "xy".
|
||||
In "x" mode, the user will only be able to specify the x range, similarly for
|
||||
"y" mode. For "xy", the selection becomes a rectangle where both ranges can be
|
||||
specified. "color" is color of the selection (if you need to change the color
|
||||
later on, you can get to it with plot.getOptions().selection.color). "shape"
|
||||
is the shape of the corners of the selection.
|
||||
|
||||
"minSize" is the minimum size a selection can be in pixels. This value can
|
||||
be customized to determine the smallest size a selection can be and still
|
||||
have the selection rectangle be displayed. When customizing this value, the
|
||||
fact that it refers to pixels, not axis units must be taken into account.
|
||||
Thus, for example, if there is a bar graph in time mode with BarWidth set to 1
|
||||
minute, setting "minSize" to 1 will not make the minimum selection size 1
|
||||
minute, but rather 1 pixel. Note also that setting "minSize" to 0 will prevent
|
||||
"plotunselected" events from being fired when the user clicks the mouse without
|
||||
dragging.
|
||||
|
||||
When selection support is enabled, a "plotselected" event will be emitted on
|
||||
the DOM element you passed into the plot function. The event handler gets a
|
||||
parameter with the ranges selected on the axes, like this:
|
||||
|
||||
placeholder.bind( "plotselected", function( event, ranges ) {
|
||||
alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to)
|
||||
// similar for yaxis - with multiple axes, the extra ones are in
|
||||
// x2axis, x3axis, ...
|
||||
});
|
||||
|
||||
The "plotselected" event is only fired when the user has finished making the
|
||||
selection. A "plotselecting" event is fired during the process with the same
|
||||
parameters as the "plotselected" event, in case you want to know what's
|
||||
happening while it's happening,
|
||||
|
||||
A "plotunselected" event with no arguments is emitted when the user clicks the
|
||||
mouse to remove the selection. As stated above, setting "minSize" to 0 will
|
||||
destroy this behavior.
|
||||
|
||||
The plugin allso adds the following methods to the plot object:
|
||||
|
||||
- setSelection( ranges, preventEvent )
|
||||
|
||||
Set the selection rectangle. The passed in ranges is on the same form as
|
||||
returned in the "plotselected" event. If the selection mode is "x", you
|
||||
should put in either an xaxis range, if the mode is "y" you need to put in
|
||||
an yaxis range and both xaxis and yaxis if the selection mode is "xy", like
|
||||
this:
|
||||
|
||||
setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } });
|
||||
|
||||
setSelection will trigger the "plotselected" event when called. If you don't
|
||||
want that to happen, e.g. if you're inside a "plotselected" handler, pass
|
||||
true as the second parameter. If you are using multiple axes, you can
|
||||
specify the ranges on any of those, e.g. as x2axis/x3axis/... instead of
|
||||
xaxis, the plugin picks the first one it sees.
|
||||
|
||||
- clearSelection( preventEvent )
|
||||
|
||||
Clear the selection rectangle. Pass in true to avoid getting a
|
||||
"plotunselected" event.
|
||||
|
||||
- getSelection()
|
||||
|
||||
Returns the current selection in the same format as the "plotselected"
|
||||
event. If there's currently no selection, the function returns null.
|
||||
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
function init(plot) {
|
||||
var selection = {
|
||||
first: { x: -1, y: -1}, second: { x: -1, y: -1},
|
||||
show: false,
|
||||
active: false
|
||||
};
|
||||
|
||||
// FIXME: The drag handling implemented here should be
|
||||
// abstracted out, there's some similar code from a library in
|
||||
// the navigation plugin, this should be massaged a bit to fit
|
||||
// the Flot cases here better and reused. Doing this would
|
||||
// make this plugin much slimmer.
|
||||
var savedhandlers = {};
|
||||
|
||||
var mouseUpHandler = null;
|
||||
|
||||
function onMouseMove(e) {
|
||||
if (selection.active) {
|
||||
updateSelection(e);
|
||||
|
||||
plot.getPlaceholder().trigger("plotselecting", [ getSelection() ]);
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseDown(e) {
|
||||
if (e.which != 1) // only accept left-click
|
||||
return;
|
||||
|
||||
// cancel out any text selections
|
||||
document.body.focus();
|
||||
|
||||
// prevent text selection and drag in old-school browsers
|
||||
if (document.onselectstart !== undefined && savedhandlers.onselectstart == null) {
|
||||
savedhandlers.onselectstart = document.onselectstart;
|
||||
document.onselectstart = function () { return false; };
|
||||
}
|
||||
if (document.ondrag !== undefined && savedhandlers.ondrag == null) {
|
||||
savedhandlers.ondrag = document.ondrag;
|
||||
document.ondrag = function () { return false; };
|
||||
}
|
||||
|
||||
setSelectionPos(selection.first, e);
|
||||
|
||||
selection.active = true;
|
||||
|
||||
// this is a bit silly, but we have to use a closure to be
|
||||
// able to whack the same handler again
|
||||
mouseUpHandler = function (e) { onMouseUp(e); };
|
||||
|
||||
$(document).one("mouseup", mouseUpHandler);
|
||||
}
|
||||
|
||||
function onMouseUp(e) {
|
||||
mouseUpHandler = null;
|
||||
|
||||
// revert drag stuff for old-school browsers
|
||||
if (document.onselectstart !== undefined)
|
||||
document.onselectstart = savedhandlers.onselectstart;
|
||||
if (document.ondrag !== undefined)
|
||||
document.ondrag = savedhandlers.ondrag;
|
||||
|
||||
// no more dragging
|
||||
selection.active = false;
|
||||
updateSelection(e);
|
||||
|
||||
if (selectionIsSane())
|
||||
triggerSelectedEvent();
|
||||
else {
|
||||
// this counts as a clear
|
||||
plot.getPlaceholder().trigger("plotunselected", [ ]);
|
||||
plot.getPlaceholder().trigger("plotselecting", [ null ]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getSelection() {
|
||||
if (!selectionIsSane())
|
||||
return null;
|
||||
|
||||
if (!selection.show) return null;
|
||||
|
||||
var r = {}, c1 = selection.first, c2 = selection.second;
|
||||
$.each(plot.getAxes(), function (name, axis) {
|
||||
if (axis.used) {
|
||||
var p1 = axis.c2p(c1[axis.direction]), p2 = axis.c2p(c2[axis.direction]);
|
||||
r[name] = { from: Math.min(p1, p2), to: Math.max(p1, p2) };
|
||||
}
|
||||
});
|
||||
return r;
|
||||
}
|
||||
|
||||
function triggerSelectedEvent() {
|
||||
var r = getSelection();
|
||||
|
||||
plot.getPlaceholder().trigger("plotselected", [ r ]);
|
||||
|
||||
// backwards-compat stuff, to be removed in future
|
||||
if (r.xaxis && r.yaxis)
|
||||
plot.getPlaceholder().trigger("selected", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]);
|
||||
}
|
||||
|
||||
function clamp(min, value, max) {
|
||||
return value < min ? min: (value > max ? max: value);
|
||||
}
|
||||
|
||||
function setSelectionPos(pos, e) {
|
||||
var o = plot.getOptions();
|
||||
var offset = plot.getPlaceholder().offset();
|
||||
var plotOffset = plot.getPlotOffset();
|
||||
pos.x = clamp(0, e.pageX - offset.left - plotOffset.left, plot.width());
|
||||
pos.y = clamp(0, e.pageY - offset.top - plotOffset.top, plot.height());
|
||||
|
||||
if (o.selection.mode == "y")
|
||||
pos.x = pos == selection.first ? 0 : plot.width();
|
||||
|
||||
if (o.selection.mode == "x")
|
||||
pos.y = pos == selection.first ? 0 : plot.height();
|
||||
}
|
||||
|
||||
function updateSelection(pos) {
|
||||
if (pos.pageX == null)
|
||||
return;
|
||||
|
||||
setSelectionPos(selection.second, pos);
|
||||
if (selectionIsSane()) {
|
||||
selection.show = true;
|
||||
plot.triggerRedrawOverlay();
|
||||
}
|
||||
else
|
||||
clearSelection(true);
|
||||
}
|
||||
|
||||
function clearSelection(preventEvent) {
|
||||
if (selection.show) {
|
||||
selection.show = false;
|
||||
plot.triggerRedrawOverlay();
|
||||
if (!preventEvent)
|
||||
plot.getPlaceholder().trigger("plotunselected", [ ]);
|
||||
}
|
||||
}
|
||||
|
||||
// function taken from markings support in Flot
|
||||
function extractRange(ranges, coord) {
|
||||
var axis, from, to, key, axes = plot.getAxes();
|
||||
|
||||
for (var k in axes) {
|
||||
axis = axes[k];
|
||||
if (axis.direction == coord) {
|
||||
key = coord + axis.n + "axis";
|
||||
if (!ranges[key] && axis.n == 1)
|
||||
key = coord + "axis"; // support x1axis as xaxis
|
||||
if (ranges[key]) {
|
||||
from = ranges[key].from;
|
||||
to = ranges[key].to;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// backwards-compat stuff - to be removed in future
|
||||
if (!ranges[key]) {
|
||||
axis = coord == "x" ? plot.getXAxes()[0] : plot.getYAxes()[0];
|
||||
from = ranges[coord + "1"];
|
||||
to = ranges[coord + "2"];
|
||||
}
|
||||
|
||||
// auto-reverse as an added bonus
|
||||
if (from != null && to != null && from > to) {
|
||||
var tmp = from;
|
||||
from = to;
|
||||
to = tmp;
|
||||
}
|
||||
|
||||
return { from: from, to: to, axis: axis };
|
||||
}
|
||||
|
||||
function setSelection(ranges, preventEvent) {
|
||||
var axis, range, o = plot.getOptions();
|
||||
|
||||
if (o.selection.mode == "y") {
|
||||
selection.first.x = 0;
|
||||
selection.second.x = plot.width();
|
||||
}
|
||||
else {
|
||||
range = extractRange(ranges, "x");
|
||||
|
||||
selection.first.x = range.axis.p2c(range.from);
|
||||
selection.second.x = range.axis.p2c(range.to);
|
||||
}
|
||||
|
||||
if (o.selection.mode == "x") {
|
||||
selection.first.y = 0;
|
||||
selection.second.y = plot.height();
|
||||
}
|
||||
else {
|
||||
range = extractRange(ranges, "y");
|
||||
|
||||
selection.first.y = range.axis.p2c(range.from);
|
||||
selection.second.y = range.axis.p2c(range.to);
|
||||
}
|
||||
|
||||
selection.show = true;
|
||||
plot.triggerRedrawOverlay();
|
||||
if (!preventEvent && selectionIsSane())
|
||||
triggerSelectedEvent();
|
||||
}
|
||||
|
||||
function selectionIsSane() {
|
||||
var minSize = plot.getOptions().selection.minSize;
|
||||
return Math.abs(selection.second.x - selection.first.x) >= minSize &&
|
||||
Math.abs(selection.second.y - selection.first.y) >= minSize;
|
||||
}
|
||||
|
||||
plot.clearSelection = clearSelection;
|
||||
plot.setSelection = setSelection;
|
||||
plot.getSelection = getSelection;
|
||||
|
||||
plot.hooks.bindEvents.push(function(plot, eventHolder) {
|
||||
var o = plot.getOptions();
|
||||
if (o.selection.mode != null) {
|
||||
eventHolder.mousemove(onMouseMove);
|
||||
eventHolder.mousedown(onMouseDown);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
plot.hooks.drawOverlay.push(function (plot, ctx) {
|
||||
// draw selection
|
||||
if (selection.show && selectionIsSane()) {
|
||||
var plotOffset = plot.getPlotOffset();
|
||||
var o = plot.getOptions();
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(plotOffset.left, plotOffset.top);
|
||||
|
||||
var c = $.color.parse(o.selection.color);
|
||||
|
||||
ctx.strokeStyle = c.scale('a', 0.8).toString();
|
||||
ctx.lineWidth = 1;
|
||||
ctx.lineJoin = o.selection.shape;
|
||||
ctx.fillStyle = c.scale('a', 0.4).toString();
|
||||
|
||||
var x = Math.min(selection.first.x, selection.second.x) + 0.5,
|
||||
y = Math.min(selection.first.y, selection.second.y) + 0.5,
|
||||
w = Math.abs(selection.second.x - selection.first.x) - 1,
|
||||
h = Math.abs(selection.second.y - selection.first.y) - 1;
|
||||
|
||||
ctx.fillRect(x, y, w, h);
|
||||
ctx.strokeRect(x, y, w, h);
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
});
|
||||
|
||||
plot.hooks.shutdown.push(function (plot, eventHolder) {
|
||||
eventHolder.unbind("mousemove", onMouseMove);
|
||||
eventHolder.unbind("mousedown", onMouseDown);
|
||||
|
||||
if (mouseUpHandler)
|
||||
$(document).unbind("mouseup", mouseUpHandler);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
$.plot.plugins.push({
|
||||
init: init,
|
||||
options: {
|
||||
selection: {
|
||||
mode: null, // one of null, "x", "y" or "xy"
|
||||
color: "#e8cfac",
|
||||
shape: "round", // one of "round", "miter", or "bevel"
|
||||
minSize: 5 // minimum number of pixels
|
||||
}
|
||||
},
|
||||
name: 'selection',
|
||||
version: '1.1'
|
||||
});
|
||||
})(jQuery);
|
188
src/vendor/timeserieswidget/jquery.flot.stack.js
vendored
Normal file
188
src/vendor/timeserieswidget/jquery.flot.stack.js
vendored
Normal file
@ -0,0 +1,188 @@
|
||||
/* Flot plugin for stacking data sets rather than overlyaing them.
|
||||
|
||||
Copyright (c) 2007-2013 IOLA and Ole Laursen.
|
||||
Licensed under the MIT license.
|
||||
|
||||
The plugin assumes the data is sorted on x (or y if stacking horizontally).
|
||||
For line charts, it is assumed that if a line has an undefined gap (from a
|
||||
null point), then the line above it should have the same gap - insert zeros
|
||||
instead of "null" if you want another behaviour. This also holds for the start
|
||||
and end of the chart. Note that stacking a mix of positive and negative values
|
||||
in most instances doesn't make sense (so it looks weird).
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
You can also specify it for a single series, like this:
|
||||
|
||||
$.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).
|
||||
|
||||
Internally, the plugin modifies the datapoints in each series, adding an
|
||||
offset to the y value. For line series, extra data points are inserted through
|
||||
interpolation. If there's a second y value, it's also adjusted (e.g for bar
|
||||
charts or filled areas).
|
||||
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
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;
|
||||
|
||||
var other = findMatchingSeries(s, plot.getData());
|
||||
if (!other)
|
||||
return;
|
||||
|
||||
var ps = datapoints.pointsize,
|
||||
points = datapoints.points,
|
||||
otherps = other.datapoints.pointsize,
|
||||
otherpoints = other.datapoints.points,
|
||||
newpoints = [],
|
||||
px, py, intery, qx, qy, bottom,
|
||||
withlines = s.lines.show,
|
||||
horizontal = s.bars.horizontal,
|
||||
withbottom = ps > 2 && (horizontal ? datapoints.format[2].x : datapoints.format[2].y),
|
||||
withsteps = withlines && s.lines.steps,
|
||||
fromgap = true,
|
||||
keyOffset = horizontal ? 1 : 0,
|
||||
accumulateOffset = horizontal ? 0 : 1,
|
||||
i = 0, j = 0, l, m;
|
||||
|
||||
while (true) {
|
||||
if (i >= points.length)
|
||||
break;
|
||||
|
||||
l = newpoints.length;
|
||||
|
||||
if (points[i] == null) {
|
||||
// copy gaps
|
||||
for (m = 0; m < ps; ++m)
|
||||
newpoints.push(points[i + m]);
|
||||
i += ps;
|
||||
}
|
||||
else if (j >= otherpoints.length) {
|
||||
// for lines, we can't use the rest of the points
|
||||
if (!withlines) {
|
||||
for (m = 0; m < ps; ++m)
|
||||
newpoints.push(points[i + m]);
|
||||
}
|
||||
i += ps;
|
||||
}
|
||||
else if (otherpoints[j] == null) {
|
||||
// oops, got a gap
|
||||
for (m = 0; m < ps; ++m)
|
||||
newpoints.push(null);
|
||||
fromgap = true;
|
||||
j += otherps;
|
||||
}
|
||||
else {
|
||||
// cases where we actually got two points
|
||||
px = points[i + keyOffset];
|
||||
py = points[i + accumulateOffset];
|
||||
qx = otherpoints[j + keyOffset];
|
||||
qy = otherpoints[j + accumulateOffset];
|
||||
bottom = 0;
|
||||
|
||||
if (px == qx) {
|
||||
for (m = 0; m < ps; ++m)
|
||||
newpoints.push(points[i + m]);
|
||||
|
||||
newpoints[l + accumulateOffset] += qy;
|
||||
bottom = qy;
|
||||
|
||||
i += ps;
|
||||
j += otherps;
|
||||
}
|
||||
else if (px > qx) {
|
||||
// we got past point below, might need to
|
||||
// insert interpolated extra point
|
||||
if (withlines && i > 0 && points[i - ps] != null) {
|
||||
intery = py + (points[i - ps + accumulateOffset] - py) * (qx - px) / (points[i - ps + keyOffset] - px);
|
||||
newpoints.push(qx);
|
||||
newpoints.push(intery + qy);
|
||||
for (m = 2; m < ps; ++m)
|
||||
newpoints.push(points[i + m]);
|
||||
bottom = qy;
|
||||
}
|
||||
|
||||
j += otherps;
|
||||
}
|
||||
else { // px < qx
|
||||
if (fromgap && withlines) {
|
||||
// if we come from a gap, we just skip this point
|
||||
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;
|
||||
}
|
||||
|
||||
// maintain the line steps invariant
|
||||
if (withsteps && l != newpoints.length && l > 0
|
||||
&& newpoints[l] != null
|
||||
&& newpoints[l] != newpoints[l - ps]
|
||||
&& newpoints[l + 1] != newpoints[l - ps + 1]) {
|
||||
for (m = 0; m < ps; ++m)
|
||||
newpoints[l + ps + m] = newpoints[l + m];
|
||||
newpoints[l + 1] = newpoints[l - ps + 1];
|
||||
}
|
||||
}
|
||||
|
||||
datapoints.points = newpoints;
|
||||
}
|
||||
|
||||
plot.hooks.processDatapoints.push(stackData);
|
||||
}
|
||||
|
||||
$.plot.plugins.push({
|
||||
init: init,
|
||||
options: options,
|
||||
name: 'stack',
|
||||
version: '1.2'
|
||||
});
|
||||
})(jQuery);
|
424
src/vendor/timeserieswidget/jquery.flot.time.js
vendored
Normal file
424
src/vendor/timeserieswidget/jquery.flot.time.js
vendored
Normal file
@ -0,0 +1,424 @@
|
||||
/* Pretty handling of time axes.
|
||||
|
||||
Copyright (c) 2007-2013 IOLA and Ole Laursen.
|
||||
Licensed under the MIT license.
|
||||
|
||||
Set axis.mode to "time" to enable. See the section "Time series data" in
|
||||
API.txt for details.
|
||||
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
var options = {
|
||||
xaxis: {
|
||||
timezone: null, // "browser" for local to the client or timezone for timezone-js
|
||||
timeformat: null, // format string to use
|
||||
twelveHourClock: false, // 12 or 24 time in time mode
|
||||
monthNames: null // list of names of months
|
||||
}
|
||||
};
|
||||
|
||||
// round to nearby lower multiple of base
|
||||
|
||||
function floorInBase(n, base) {
|
||||
return base * Math.floor(n / base);
|
||||
}
|
||||
|
||||
// Returns a string with the date d formatted according to fmt.
|
||||
// A subset of the Open Group's strftime format is supported.
|
||||
|
||||
function formatDate(d, fmt, monthNames, dayNames) {
|
||||
|
||||
if (typeof d.strftime == "function") {
|
||||
return d.strftime(fmt);
|
||||
}
|
||||
|
||||
var leftPad = function(n, pad) {
|
||||
n = "" + n;
|
||||
pad = "" + (pad == null ? "0" : pad);
|
||||
return n.length == 1 ? pad + n : n;
|
||||
};
|
||||
|
||||
var r = [];
|
||||
var escape = false;
|
||||
var hours = d.getHours();
|
||||
var isAM = hours < 12;
|
||||
|
||||
if (monthNames == null) {
|
||||
monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||||
}
|
||||
|
||||
if (dayNames == null) {
|
||||
dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||
}
|
||||
|
||||
var hours12;
|
||||
|
||||
if (hours > 12) {
|
||||
hours12 = hours - 12;
|
||||
} else if (hours == 0) {
|
||||
hours12 = 12;
|
||||
} else {
|
||||
hours12 = hours;
|
||||
}
|
||||
|
||||
for (var i = 0; i < fmt.length; ++i) {
|
||||
|
||||
var c = fmt.charAt(i);
|
||||
|
||||
if (escape) {
|
||||
switch (c) {
|
||||
case 'a': c = "" + dayNames[d.getDay()]; break;
|
||||
case 'b': c = "" + monthNames[d.getMonth()]; break;
|
||||
case 'd': c = leftPad(d.getDate()); break;
|
||||
case 'e': c = leftPad(d.getDate(), " "); break;
|
||||
case 'H': c = leftPad(hours); break;
|
||||
case 'I': c = leftPad(hours12); break;
|
||||
case 'l': c = leftPad(hours12, " "); break;
|
||||
case 'm': c = leftPad(d.getMonth() + 1); break;
|
||||
case 'M': c = leftPad(d.getMinutes()); break;
|
||||
// quarters not in Open Group's strftime specification
|
||||
case 'q':
|
||||
c = "" + (Math.floor(d.getMonth() / 3) + 1); break;
|
||||
case 'S': c = leftPad(d.getSeconds()); break;
|
||||
case 'y': c = leftPad(d.getFullYear() % 100); break;
|
||||
case 'Y': c = "" + d.getFullYear(); break;
|
||||
case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break;
|
||||
case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break;
|
||||
case 'w': c = "" + d.getDay(); break;
|
||||
}
|
||||
r.push(c);
|
||||
escape = false;
|
||||
} else {
|
||||
if (c == "%") {
|
||||
escape = true;
|
||||
} else {
|
||||
r.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return r.join("");
|
||||
}
|
||||
|
||||
// To have a consistent view of time-based data independent of which time
|
||||
// zone the client happens to be in we need a date-like object independent
|
||||
// of time zones. This is done through a wrapper that only calls the UTC
|
||||
// versions of the accessor methods.
|
||||
|
||||
function makeUtcWrapper(d) {
|
||||
|
||||
function addProxyMethod(sourceObj, sourceMethod, targetObj, targetMethod) {
|
||||
sourceObj[sourceMethod] = function() {
|
||||
return targetObj[targetMethod].apply(targetObj, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
var utc = {
|
||||
date: d
|
||||
};
|
||||
|
||||
// support strftime, if found
|
||||
|
||||
if (d.strftime != undefined) {
|
||||
addProxyMethod(utc, "strftime", d, "strftime");
|
||||
}
|
||||
|
||||
addProxyMethod(utc, "getTime", d, "getTime");
|
||||
addProxyMethod(utc, "setTime", d, "setTime");
|
||||
|
||||
var props = ["Date", "Day", "FullYear", "Hours", "Milliseconds", "Minutes", "Month", "Seconds"];
|
||||
|
||||
for (var p = 0; p < props.length; p++) {
|
||||
addProxyMethod(utc, "get" + props[p], d, "getUTC" + props[p]);
|
||||
addProxyMethod(utc, "set" + props[p], d, "setUTC" + props[p]);
|
||||
}
|
||||
|
||||
return utc;
|
||||
};
|
||||
|
||||
// select time zone strategy. This returns a date-like object tied to the
|
||||
// desired timezone
|
||||
|
||||
function dateGenerator(ts, opts) {
|
||||
if (opts.timezone == "browser") {
|
||||
return new Date(ts);
|
||||
} else if (!opts.timezone || opts.timezone == "utc") {
|
||||
return makeUtcWrapper(new Date(ts));
|
||||
} else if (typeof timezoneJS != "undefined" && typeof timezoneJS.Date != "undefined") {
|
||||
var d = new timezoneJS.Date();
|
||||
// timezone-js is fickle, so be sure to set the time zone before
|
||||
// setting the time.
|
||||
d.setTimezone(opts.timezone);
|
||||
d.setTime(ts);
|
||||
return d;
|
||||
} else {
|
||||
return makeUtcWrapper(new Date(ts));
|
||||
}
|
||||
}
|
||||
|
||||
// map of app. size of time units in milliseconds
|
||||
|
||||
var timeUnitSize = {
|
||||
"second": 1000,
|
||||
"minute": 60 * 1000,
|
||||
"hour": 60 * 60 * 1000,
|
||||
"day": 24 * 60 * 60 * 1000,
|
||||
"month": 30 * 24 * 60 * 60 * 1000,
|
||||
"quarter": 3 * 30 * 24 * 60 * 60 * 1000,
|
||||
"year": 365.2425 * 24 * 60 * 60 * 1000
|
||||
};
|
||||
|
||||
// the allowed tick sizes, after 1 year we use
|
||||
// an integer algorithm
|
||||
|
||||
var baseSpec = [
|
||||
[1, "second"], [2, "second"], [5, "second"], [10, "second"],
|
||||
[30, "second"],
|
||||
[1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"],
|
||||
[30, "minute"],
|
||||
[1, "hour"], [2, "hour"], [4, "hour"],
|
||||
[8, "hour"], [12, "hour"],
|
||||
[1, "day"], [2, "day"], [3, "day"],
|
||||
[0.25, "month"], [0.5, "month"], [1, "month"],
|
||||
[2, "month"]
|
||||
];
|
||||
|
||||
// we don't know which variant(s) we'll need yet, but generating both is
|
||||
// cheap
|
||||
|
||||
var specMonths = baseSpec.concat([[3, "month"], [6, "month"],
|
||||
[1, "year"]]);
|
||||
var specQuarters = baseSpec.concat([[1, "quarter"], [2, "quarter"],
|
||||
[1, "year"]]);
|
||||
|
||||
function init(plot) {
|
||||
plot.hooks.processDatapoints.push(function (plot, series, datapoints) {
|
||||
$.each(plot.getAxes(), function(axisName, axis) {
|
||||
|
||||
var opts = axis.options;
|
||||
|
||||
if (opts.mode == "time") {
|
||||
axis.tickGenerator = function(axis) {
|
||||
|
||||
var ticks = [];
|
||||
var d = dateGenerator(axis.min, opts);
|
||||
var minSize = 0;
|
||||
|
||||
// make quarter use a possibility if quarters are
|
||||
// mentioned in either of these options
|
||||
|
||||
var spec = (opts.tickSize && opts.tickSize[1] ===
|
||||
"quarter") ||
|
||||
(opts.minTickSize && opts.minTickSize[1] ===
|
||||
"quarter") ? specQuarters : specMonths;
|
||||
|
||||
if (opts.minTickSize != null) {
|
||||
if (typeof opts.tickSize == "number") {
|
||||
minSize = opts.tickSize;
|
||||
} else {
|
||||
minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]];
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < spec.length - 1; ++i) {
|
||||
if (axis.delta < (spec[i][0] * timeUnitSize[spec[i][1]]
|
||||
+ spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2
|
||||
&& spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var size = spec[i][0];
|
||||
var unit = spec[i][1];
|
||||
|
||||
// special-case the possibility of several years
|
||||
|
||||
if (unit == "year") {
|
||||
|
||||
// if given a minTickSize in years, just use it,
|
||||
// ensuring that it's an integer
|
||||
|
||||
if (opts.minTickSize != null && opts.minTickSize[1] == "year") {
|
||||
size = Math.floor(opts.minTickSize[0]);
|
||||
} else {
|
||||
|
||||
var magn = Math.pow(10, Math.floor(Math.log(axis.delta / timeUnitSize.year) / Math.LN10));
|
||||
var norm = (axis.delta / timeUnitSize.year) / magn;
|
||||
|
||||
if (norm < 1.5) {
|
||||
size = 1;
|
||||
} else if (norm < 3) {
|
||||
size = 2;
|
||||
} else if (norm < 7.5) {
|
||||
size = 5;
|
||||
} else {
|
||||
size = 10;
|
||||
}
|
||||
|
||||
size *= magn;
|
||||
}
|
||||
|
||||
// minimum size for years is 1
|
||||
|
||||
if (size < 1) {
|
||||
size = 1;
|
||||
}
|
||||
}
|
||||
|
||||
axis.tickSize = opts.tickSize || [size, unit];
|
||||
var tickSize = axis.tickSize[0];
|
||||
unit = axis.tickSize[1];
|
||||
|
||||
var step = tickSize * timeUnitSize[unit];
|
||||
|
||||
if (unit == "second") {
|
||||
d.setSeconds(floorInBase(d.getSeconds(), tickSize));
|
||||
} else if (unit == "minute") {
|
||||
d.setMinutes(floorInBase(d.getMinutes(), tickSize));
|
||||
} else if (unit == "hour") {
|
||||
d.setHours(floorInBase(d.getHours(), tickSize));
|
||||
} else if (unit == "month") {
|
||||
d.setMonth(floorInBase(d.getMonth(), tickSize));
|
||||
} else if (unit == "quarter") {
|
||||
d.setMonth(3 * floorInBase(d.getMonth() / 3,
|
||||
tickSize));
|
||||
} else if (unit == "year") {
|
||||
d.setFullYear(floorInBase(d.getFullYear(), tickSize));
|
||||
}
|
||||
|
||||
// reset smaller components
|
||||
|
||||
d.setMilliseconds(0);
|
||||
|
||||
if (step >= timeUnitSize.minute) {
|
||||
d.setSeconds(0);
|
||||
} else if (step >= timeUnitSize.hour) {
|
||||
d.setMinutes(0);
|
||||
} else if (step >= timeUnitSize.day) {
|
||||
d.setHours(0);
|
||||
} else if (step >= timeUnitSize.day * 4) {
|
||||
d.setDate(1);
|
||||
} else if (step >= timeUnitSize.month * 2) {
|
||||
d.setMonth(floorInBase(d.getMonth(), 3));
|
||||
} else if (step >= timeUnitSize.quarter * 2) {
|
||||
d.setMonth(floorInBase(d.getMonth(), 6));
|
||||
} else if (step >= timeUnitSize.year) {
|
||||
d.setMonth(0);
|
||||
}
|
||||
|
||||
var carry = 0;
|
||||
var v = Number.NaN;
|
||||
var prev;
|
||||
|
||||
do {
|
||||
|
||||
prev = v;
|
||||
v = d.getTime();
|
||||
ticks.push(v);
|
||||
|
||||
if (unit == "month" || unit == "quarter") {
|
||||
if (tickSize < 1) {
|
||||
|
||||
// a bit complicated - we'll divide the
|
||||
// month/quarter up but we need to take
|
||||
// care of fractions so we don't end up in
|
||||
// the middle of a day
|
||||
|
||||
d.setDate(1);
|
||||
var start = d.getTime();
|
||||
d.setMonth(d.getMonth() +
|
||||
(unit == "quarter" ? 3 : 1));
|
||||
var end = d.getTime();
|
||||
d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize);
|
||||
carry = d.getHours();
|
||||
d.setHours(0);
|
||||
} else {
|
||||
d.setMonth(d.getMonth() +
|
||||
tickSize * (unit == "quarter" ? 3 : 1));
|
||||
}
|
||||
} else if (unit == "year") {
|
||||
d.setFullYear(d.getFullYear() + tickSize);
|
||||
} else {
|
||||
d.setTime(v + step);
|
||||
}
|
||||
} while (v < axis.max && v != prev);
|
||||
|
||||
return ticks;
|
||||
};
|
||||
|
||||
axis.tickFormatter = function (v, axis) {
|
||||
|
||||
var d = dateGenerator(v, axis.options);
|
||||
|
||||
// first check global format
|
||||
|
||||
if (opts.timeformat != null) {
|
||||
return formatDate(d, opts.timeformat, opts.monthNames, opts.dayNames);
|
||||
}
|
||||
|
||||
// possibly use quarters if quarters are mentioned in
|
||||
// any of these places
|
||||
|
||||
var useQuarters = (axis.options.tickSize &&
|
||||
axis.options.tickSize[1] == "quarter") ||
|
||||
(axis.options.minTickSize &&
|
||||
axis.options.minTickSize[1] == "quarter");
|
||||
|
||||
var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
|
||||
var span = axis.max - axis.min;
|
||||
var suffix = (opts.twelveHourClock) ? " %p" : "";
|
||||
var hourCode = (opts.twelveHourClock) ? "%I" : "%H";
|
||||
var fmt;
|
||||
|
||||
if (t < timeUnitSize.minute) {
|
||||
fmt = hourCode + ":%M:%S" + suffix;
|
||||
} else if (t < timeUnitSize.day) {
|
||||
if (span < 2 * timeUnitSize.day) {
|
||||
fmt = hourCode + ":%M" + suffix;
|
||||
} else {
|
||||
fmt = "%b %d " + hourCode + ":%M" + suffix;
|
||||
}
|
||||
} else if (t < timeUnitSize.month) {
|
||||
fmt = "%b %d";
|
||||
} else if ((useQuarters && t < timeUnitSize.quarter) ||
|
||||
(!useQuarters && t < timeUnitSize.year)) {
|
||||
if (span < timeUnitSize.year) {
|
||||
fmt = "%b";
|
||||
} else {
|
||||
fmt = "%b %Y";
|
||||
}
|
||||
} else if (useQuarters && t < timeUnitSize.year) {
|
||||
if (span < timeUnitSize.year) {
|
||||
fmt = "Q%q";
|
||||
} else {
|
||||
fmt = "Q%q %Y";
|
||||
}
|
||||
} else {
|
||||
fmt = "%Y";
|
||||
}
|
||||
|
||||
var rt = formatDate(d, fmt, opts.monthNames, opts.dayNames);
|
||||
|
||||
return rt;
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$.plot.plugins.push({
|
||||
init: init,
|
||||
options: options,
|
||||
name: 'time',
|
||||
version: '1.0'
|
||||
});
|
||||
|
||||
// Time-axis support used to be in Flot core, which exposed the
|
||||
// formatDate function on the plot object. Various plugins depend
|
||||
// on the function, so we need to re-expose it here.
|
||||
|
||||
$.plot.formatDate = formatDate;
|
||||
|
||||
})(jQuery);
|
838
src/vendor/timeserieswidget/jquery.tswidget.js
vendored
Normal file
838
src/vendor/timeserieswidget/jquery.tswidget.js
vendored
Normal file
@ -0,0 +1,838 @@
|
||||
function strip_ending_slash(str) {
|
||||
if(str.substr(-1) == '/') {
|
||||
return str.substr(0, str.length - 1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function truncate_str(str) {
|
||||
if (str.length >= 147) {
|
||||
return str.substring(0, 148) + "...";
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
function build_graphite_options(options, raw) {
|
||||
raw = raw || false;
|
||||
var clean_options = [];
|
||||
internal_options = ['_t'];
|
||||
graphite_options = ['target', 'targets', 'from', 'until', 'rawData', 'format'];
|
||||
graphite_png_options = ['areaMode', 'width', 'height', 'template', 'margin', 'bgcolor',
|
||||
'fgcolor', 'fontName', 'fontSize', 'fontBold', 'fontItalic',
|
||||
'yMin', 'yMax', 'colorList', 'title', 'vtitle', 'lineMode',
|
||||
'lineWith', 'hideLegend', 'hideAxes', 'hideGrid', 'minXstep',
|
||||
'majorGridlineColor', 'minorGridLineColor', 'minorY',
|
||||
'thickness', 'min', 'max', 'tz'];
|
||||
|
||||
if(raw) {
|
||||
options['format'] = 'json';
|
||||
} else {
|
||||
// use random parameter to force image refresh
|
||||
options["_t"] = options["_t"] || Math.random();
|
||||
}
|
||||
|
||||
$.each(options, function (key, value) {
|
||||
if(raw) {
|
||||
if ($.inArray(key, graphite_options) == -1) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if ($.inArray(key, graphite_options) == -1 && $.inArray(key, graphite_png_options) == -1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (key === "targets") {
|
||||
$.each(value, function (index, value) {
|
||||
if (raw) {
|
||||
// it's normally pointless to use alias() in raw mode, because we apply an alias (name) ourself
|
||||
// in the client rendering step. we just need graphite to return the target.
|
||||
// but graphite sometimes alters the name of the target in the returned data
|
||||
// (https://github.com/graphite-project/graphite-web/issues/248)
|
||||
// so we need a good string identifier and set it using alias() (which graphite will honor)
|
||||
// so that we recognize the returned output. simplest is just to include the target spec again
|
||||
// though this duplicates a lot of info in the url.
|
||||
clean_options.push("target=alias(" + encodeURIComponent(value.target) + ",'" + value.target +"')");
|
||||
} else {
|
||||
clean_options.push("target=alias(color(" +encodeURIComponent(value.target + ",'" + value.color) +"'),'" + value.name +"')");
|
||||
}
|
||||
});
|
||||
} else if (value !== null) {
|
||||
clean_options.push(key + "=" + encodeURIComponent(value));
|
||||
}
|
||||
});
|
||||
return clean_options;
|
||||
}
|
||||
|
||||
// build url for an image. but GET url's are limited in length, so if you have many/long params, some may be missing.
|
||||
// could be made smarter for example to favor non-target options because we usually don't want to loose any of those.
|
||||
function build_graphite_url(options) {
|
||||
var limit = 2000; // http://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers
|
||||
var url = options.graphite_url + "?";
|
||||
|
||||
options = build_graphite_options(options, false);
|
||||
$.map(options, function(option) {
|
||||
if (url.length + option.length < limit) {
|
||||
url += '&' + option;
|
||||
}
|
||||
});
|
||||
return url.replace(/\?&/, "?");
|
||||
}
|
||||
|
||||
function build_anthracite_url(options) {
|
||||
url = strip_ending_slash(options.anthracite_url) + '/events/json';
|
||||
if ('events_query' in options) {
|
||||
url += '?q=' + options['events_query'];
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
function find_definition (target_graphite, options) {
|
||||
var matching_i = undefined;
|
||||
for (var cfg_i = 0; cfg_i < options.targets.length && matching_i == undefined; cfg_i++) {
|
||||
// string match (no globbing)
|
||||
if(options.targets[cfg_i].target == target_graphite.target) {
|
||||
matching_i = cfg_i;
|
||||
}
|
||||
// glob match?
|
||||
else if(target_graphite.target.graphiteGlob(options.targets[cfg_i].target)) {
|
||||
matching_i = cfg_i;
|
||||
}
|
||||
}
|
||||
if (matching_i == undefined) {
|
||||
console.error ("internal error: could not figure out which target_option target_graphite '" +
|
||||
target_graphite.target + "' comes from");
|
||||
return [];
|
||||
}
|
||||
return options.targets[matching_i];
|
||||
}
|
||||
|
||||
(function ($) {
|
||||
/*
|
||||
from graphite-web-0.9.9/graphTemplates.conf.example:
|
||||
|
||||
[default]
|
||||
background = black
|
||||
foreground = white
|
||||
majorLine = white
|
||||
minorLine = grey
|
||||
lineColors = blue,green,red,purple,brown,yellow,aqua,grey,magenta,pink,gold,rose
|
||||
fontName = Sans
|
||||
fontSize = 10
|
||||
fontBold = False
|
||||
fontItalic = False
|
||||
|
||||
definitions below are from http://graphite.readthedocs.org/en/1.0/url-api.html
|
||||
*/
|
||||
var default_graphite_options = {
|
||||
'bgcolor': '#000000', // background color of the graph
|
||||
'fgcolor' : '#ffffff', // title, legend text, and axis labels
|
||||
'majorLine': '#ffffff',
|
||||
'minorLine': '#afafaf'
|
||||
}
|
||||
var default_tswidget_options = {
|
||||
'events_color': '#ccff66',
|
||||
'es_events_color': '#ff0066',
|
||||
'events_text_color': '#5C991F'
|
||||
}
|
||||
|
||||
$.fn.graphite = function (options) {
|
||||
if (options === "update") {
|
||||
$.fn.graphite.update(this, arguments[1]);
|
||||
return this;
|
||||
}
|
||||
|
||||
// Initialize plugin //
|
||||
options = options || {};
|
||||
var settings = $.extend({}, $.fn.graphite.defaults, options);
|
||||
|
||||
return this.each(function () {
|
||||
$this = $(this);
|
||||
|
||||
$this.data("graphOptions", settings);
|
||||
$.fn.graphite.render($this, settings);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
$.fn.graphite.render = function($img, options) {
|
||||
$img.attr("src", build_graphite_url(options));
|
||||
$img.attr("height", options.height);
|
||||
$img.attr("width", options.width);
|
||||
};
|
||||
|
||||
$.fn.graphite.update = function($img, options) {
|
||||
options = options || {};
|
||||
$img.each(function () {
|
||||
$this = $(this);
|
||||
var settings = $.extend({}, $this.data("graphOptions"), options);
|
||||
$this.data("graphOptions", settings);
|
||||
$.fn.graphite.render($this, settings);
|
||||
});
|
||||
};
|
||||
|
||||
// note: graphite json output is a list of dicts like:
|
||||
// {"datapoints": [...], "target": "<metricname>" }
|
||||
// if you did alias(series, "foo") then "target" will contain the alias
|
||||
// (loosing the metricname which is bad, esp. when you had a glob with an alias, then you don't know what's what)
|
||||
// rickshaw: options.series is a list of dicts like:
|
||||
// { name: "alias", color: "foo", data: [{x: (...), y: (...)} , ...]}
|
||||
// we basically tell users to use this dict, with extra 'target' to specify graphite target string
|
||||
// flot: d = [[<ts>, <val>], (...)]
|
||||
// plot ($(..), [d], ..)
|
||||
$.fn.graphiteRick = function (options, on_error) {
|
||||
options = options || {};
|
||||
var settings = $.extend({}, default_graphite_options, default_tswidget_options, $.fn.graphite.defaults, options);
|
||||
|
||||
return this.each(function () {
|
||||
$this = $(this);
|
||||
$this.data("graphOptions", settings);
|
||||
$.fn.graphiteRick.render(this, settings, on_error);
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.graphiteFlot = function (options, on_error) {
|
||||
if ('zoneFileBasePath' in options) {
|
||||
timezoneJS.timezone.zoneFileBasePath = options['zoneFileBasePath'];
|
||||
timezoneJS.timezone.init();
|
||||
}
|
||||
options = options || {};
|
||||
var settings = $.extend({}, default_graphite_options, default_tswidget_options, $.fn.graphite.defaults, options);
|
||||
|
||||
return this.each(function () {
|
||||
$this = $(this);
|
||||
$this.data("graphOptions", settings);
|
||||
$.fn.graphiteFlot.render(this, settings, on_error);
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.graphiteHighcharts = function (options, on_error) {
|
||||
if ('zoneFileBasePath' in options) {
|
||||
timezoneJS.timezone.zoneFileBasePath = options['zoneFileBasePath'];
|
||||
timezoneJS.timezone.init();
|
||||
}
|
||||
options = options || {};
|
||||
var settings = $.extend({}, default_graphite_options, default_tswidget_options, $.fn.graphite.defaults, options);
|
||||
|
||||
return this.each(function () {
|
||||
$this = $(this);
|
||||
$this.data("graphOptions", settings);
|
||||
$.fn.graphiteHighcharts.render(this, settings, on_error);
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.graphiteFlot.render = function(div, options, on_error) {
|
||||
var id = div.getAttribute('id');
|
||||
$div = $(div);
|
||||
$div.height(options.height);
|
||||
$div.width(options.width);
|
||||
var events = [];
|
||||
var es_events = [];
|
||||
var all_targets = [];
|
||||
var add_targets = function(response_data) {
|
||||
for (var res_i = 0; res_i < response_data.length; res_i++) {
|
||||
var target = find_definition(response_data[res_i], options);
|
||||
target.label = target.name; // flot wants 'label'
|
||||
target.data = [];
|
||||
var nulls = 0;
|
||||
var non_nulls = 0;
|
||||
for (var i in response_data[res_i].datapoints) {
|
||||
if(response_data[res_i].datapoints[i][0] == null) {
|
||||
nulls++;
|
||||
if('drawNullAsZero' in options && options['drawNullAsZero']) {
|
||||
response_data[res_i].datapoints[i][0] = 0;
|
||||
} else {
|
||||
// don't tell flot about null values, it prevents adjacent non-null values from
|
||||
// being rendered correctly
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
non_nulls++;
|
||||
}
|
||||
target.data.push([response_data[res_i].datapoints[i][1] * 1000, response_data[res_i].datapoints[i][0]]);
|
||||
}
|
||||
if (nulls/non_nulls > 0.3) {
|
||||
console.log("warning: rendered target contains " + nulls + " null values, " + non_nulls + " non_nulls");
|
||||
}
|
||||
all_targets.push(target);
|
||||
}
|
||||
}
|
||||
|
||||
var drawFlot = function(es_data, anthracite_data) {
|
||||
|
||||
// default config state modifiers (you can override them in your config objects)
|
||||
var states = {
|
||||
'stacked': {
|
||||
'series': {'stack': true, 'lines': {'show': true, 'lineWidth': 0, 'fill': 1}},
|
||||
},
|
||||
'lines': {
|
||||
// flot lib wants 0 or null. not false o_O
|
||||
'series': {'stack': null, 'lines': { 'show': true, 'lineWidth': 0.6, 'fill': false }}
|
||||
}
|
||||
};
|
||||
if(!('states' in options)) {
|
||||
options['states'] = {};
|
||||
}
|
||||
$.extend(options['states'], states);
|
||||
|
||||
function suffixFormatterSI(val, axis) {
|
||||
range = axis.max - axis.min;
|
||||
lowest = Math.min (range,val);
|
||||
if (lowest >= Math.pow(10,12))
|
||||
return (val / Math.pow(10,12)).toFixed(axis.tickDecimals) + " T";
|
||||
if (lowest >= Math.pow(10,9))
|
||||
return (val / Math.pow(10,9)).toFixed(axis.tickDecimals) + " G";
|
||||
if (lowest >= Math.pow(10,6))
|
||||
return (val / Math.pow(10,6)).toFixed(axis.tickDecimals) + " M";
|
||||
if (lowest >= Math.pow(10,3))
|
||||
return (val / Math.pow(10,3)).toFixed(axis.tickDecimals) + " k";
|
||||
return val.toFixed(axis.tickDecimals);
|
||||
}
|
||||
function suffixFormatterBinary(val, axis) {
|
||||
range = axis.max - axis.min;
|
||||
lowest = Math.min (range,val);
|
||||
if (lowest >= Math.pow(2,40))
|
||||
return (val / Math.pow(2,40)).toFixed(axis.tickDecimals) + " Ti";
|
||||
if (lowest >= Math.pow(2,30))
|
||||
return (val / Math.pow(2,30)).toFixed(axis.tickDecimals) + " Gi";
|
||||
if (lowest >= Math.pow(2,20))
|
||||
return (val / Math.pow(2,20)).toFixed(axis.tickDecimals) + " Mi";
|
||||
if (lowest >= Math.pow(2,10))
|
||||
return (val / Math.pow(2,10)).toFixed(axis.tickDecimals) + " Ki";
|
||||
return val.toFixed(axis.tickDecimals);
|
||||
}
|
||||
|
||||
var buildFlotOptions = function(options) {
|
||||
// xaxis color = title color and horizontal lines in grid
|
||||
// yaxis color = vtitle color and vertical lines in grid
|
||||
// xaxis tickcolor = override vertical lines in grid
|
||||
// yaxis tickcolor = override horizontal lines in grid
|
||||
// note: flot doesn't distinguish between major and minor line
|
||||
// so i use minor, because graphite uses very thin lines which make them look less intense,
|
||||
// in flot they seem to be a bit thicker, so generally make them less intense to have them look similar.
|
||||
// although they still look more intense than in graphite though.
|
||||
// tuning tickLength doesn't seem to help (and no lineWidth for axis..). maybe a graphite bug
|
||||
options['tickColor'] = options['minorLine'];
|
||||
options['xaxis'] = options['xaxis'] || {};
|
||||
$.extend(options['xaxis'], { color: options['fgcolor'], tickColor: options['tickColor'], mode: 'time'});
|
||||
if ('tz' in options) {
|
||||
options['xaxis']['timezone'] = options['tz'];
|
||||
}
|
||||
options['yaxis'] = options['yaxis'] || {};
|
||||
$.extend(options['yaxis'], { color: options['fgcolor'], tickColor: options['tickColor'], tickFormatter: suffixFormatterSI});
|
||||
if('suffixes' in options) {
|
||||
if(options['suffixes'] == 'binary') {
|
||||
options['yaxis']['tickFormatter'] = suffixFormatterBinary;
|
||||
} else if(!options['suffixes']) {
|
||||
delete options['yaxis']['tickFormatter'];
|
||||
}
|
||||
}
|
||||
if('title' in options) {
|
||||
options['xaxes'] = [{axisLabel: options['title']}];
|
||||
}
|
||||
if('vtitle' in options) {
|
||||
options['yaxes'] = [{position: 'left', axisLabel: options['vtitle']}];
|
||||
}
|
||||
for (i = 0; i < options['targets'].length; i++ ) {
|
||||
options['targets'][i]['color'] = options['targets'][i]['color'];
|
||||
}
|
||||
if(!('grid' in options)) {
|
||||
options['grid'] = {};
|
||||
}
|
||||
if(options['hover_details']) {
|
||||
options['grid']['hoverable'] = true;
|
||||
options['grid']['autoHighlight'] = true; // show datapoint being hilighted. true by default but hardcode to make sure
|
||||
}
|
||||
if(!('markings' in options['grid'])) {
|
||||
options['grid']['markings'] = [];
|
||||
}
|
||||
if(!('selection' in options)) {
|
||||
options['selection'] = {
|
||||
'mode': "xy"
|
||||
};
|
||||
}
|
||||
|
||||
for (var i = 0; i < events.length; i++) {
|
||||
x = events[i].date * 1000;
|
||||
options['grid']['markings'].push({ color: options['events_color'], lineWidth: 1, xaxis: { from: x, to: x} });
|
||||
}
|
||||
// custom es_events loop
|
||||
for (var i = 0; i < es_events.length; i++) {
|
||||
x = Date.parse(es_events[i]['_source']['@timestamp']);
|
||||
options['grid']['markings'].push({ color: options['es_events_color'], lineWidth: 1, xaxis: { from: x, to: x} });
|
||||
}
|
||||
state = options['state'] || 'lines';
|
||||
return $.extend(options, options['states'][state]);
|
||||
}
|
||||
var plot = $.plot(div, all_targets, buildFlotOptions(options));
|
||||
$div.bind('plotselected', function (event, ranges) {
|
||||
// clamp the zooming to prevent eternal zoom
|
||||
|
||||
if (ranges.xaxis.to - ranges.xaxis.from < 0.00001) {
|
||||
ranges.xaxis.to = ranges.xaxis.from + 0.00001;
|
||||
}
|
||||
|
||||
if (ranges.yaxis.to - ranges.yaxis.from < 0.00001) {
|
||||
ranges.yaxis.to = ranges.yaxis.from + 0.00001;
|
||||
}
|
||||
|
||||
// do the zooming
|
||||
zoomed_options = buildFlotOptions(options);
|
||||
zoomed_options['xaxis']['min'] = ranges.xaxis.from;
|
||||
zoomed_options['xaxis']['max'] = ranges.xaxis.to;
|
||||
zoomed_options['yaxis']['min'] = ranges.yaxis.from;
|
||||
zoomed_options['yaxis']['max'] = ranges.yaxis.to;
|
||||
plot = $.plot(div, all_targets, zoomed_options);
|
||||
});
|
||||
// add labels
|
||||
var o;
|
||||
/*for (var i = 0; i < events.length; i++) {
|
||||
o = plot.pointOffset({ x: events[i].date * 1000, y: 0});
|
||||
msg = '<div style="position:absolute;left:' + (o.left) + 'px;top:' + ( o.top + 35 ) + 'px;';
|
||||
msg += 'color:' + options['events_text_color'] + ';font-size:smaller">';
|
||||
msg += '<b>' + events[i].type + '</b></br>';
|
||||
msg += events[i].desc
|
||||
msg += '</div>';
|
||||
$div.append(msg);
|
||||
}
|
||||
for (var i = 0; i < es_events.length; i++) {
|
||||
o = plot.pointOffset({ x: Date.parse(es_events[i]['_source']['@timestamp']), y: 0});
|
||||
msg = '<div style="background-color:#40FF00;position:absolute;left:' + (o.left) + 'px;top:' + ( o.top + 35 ) + 'px;';
|
||||
msg += 'color:' + '#FF0066' + ';font-size:smaller">';
|
||||
msg += '<b>tags</b>: ' + es_events[i]['_source']['@tags'].join(' ') + '</br>';
|
||||
msg += "<b>env</b>: " + es_events[i]['_source']['@fields']['environment'] + '</br>';
|
||||
msg += "<b>msg</b>: " + es_events[i]['_source']['@message'] + '</br>';
|
||||
msg += '</div>';
|
||||
$div.append(msg);
|
||||
}*/
|
||||
if (options['line_stack_toggle']) {
|
||||
var form = document.getElementById(options['line_stack_toggle']);
|
||||
if(options['state'] == 'stacked') {
|
||||
lines_checked = '';
|
||||
stacked_checked = ' checked';
|
||||
} else {
|
||||
lines_checked = ' checked';
|
||||
stacked_checked = '';
|
||||
}
|
||||
form.innerHTML= '<input type="radio" name="offset" id="lines" value="lines"'+ lines_checked +'>' +
|
||||
'<label class="lines" for="lines">lines</label>' +
|
||||
'<br/><input type="radio" name="offset" id="stacked" value="stacked"' + stacked_checked + '>' +
|
||||
'<label class="stack" for="stack">stack</label>';
|
||||
|
||||
form.addEventListener('change', function(e) {
|
||||
var mode = e.target.value;
|
||||
options['state'] = mode;
|
||||
$.plot(div, all_targets, buildFlotOptions(options));
|
||||
}, false);
|
||||
}
|
||||
function showTooltip(x, y, contents) {
|
||||
$("<div id='tooltip_" + id + "'>" + contents + "</div>").css({
|
||||
position: "absolute",
|
||||
display: "none",
|
||||
top: y + 5,
|
||||
left: x + 5,
|
||||
border: "1px solid #fdd",
|
||||
padding: "2px",
|
||||
"background-color": "#fee",
|
||||
opacity: 0.80
|
||||
}).appendTo("body").fadeIn(200);
|
||||
}
|
||||
var previousPoint = null;
|
||||
$(div).bind("plothover", function (event, pos, item) {
|
||||
if (item) {
|
||||
if (previousPoint != item.dataIndex) {
|
||||
previousPoint = item.dataIndex;
|
||||
$("#tooltip_" + id).remove();
|
||||
var x = item.datapoint[0],
|
||||
y = item.datapoint[1].toFixed(2);
|
||||
var date = new Date(x);
|
||||
showTooltip(item.pageX, item.pageY,
|
||||
"Series: " + item.series.label +
|
||||
"<br/>Local Time: " + date.toLocaleString() +
|
||||
"<br/>UTC Time: " + date.toUTCString() + ")" +
|
||||
"<br/>Value: " + y);
|
||||
}
|
||||
} else {
|
||||
$("#tooltip_" + id).remove();
|
||||
previousPoint = null;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
data = build_graphite_options(options, true);
|
||||
var requests = [];
|
||||
requests.push($.ajax({
|
||||
accepts: {text: 'application/json'},
|
||||
cache: false,
|
||||
dataType: 'json',
|
||||
url: options['graphite_url'],
|
||||
type: "POST",
|
||||
data: data.join('&'),
|
||||
success: function(data, textStatus, jqXHR ) {
|
||||
if(data.length == 0 ) {
|
||||
console.warn("no data in graphite response");
|
||||
}
|
||||
add_targets(data);
|
||||
},
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
on_error("Failed to do graphite POST request to " + truncate_str(options['graphite_url']) +
|
||||
": " + textStatus + ": " + errorThrown);
|
||||
}
|
||||
}));
|
||||
if('anthracite_url' in options){
|
||||
anthracite_url = build_anthracite_url(options, true);
|
||||
requests.push($.ajax({
|
||||
accepts: {text: 'application/json'},
|
||||
cache: false,
|
||||
dataType: 'json',
|
||||
url: anthracite_url,
|
||||
success: function(data, textStatus, jqXHR ) {
|
||||
events = data.events;
|
||||
},
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
on_error("Failed to do anthracite GET request to " + truncate_str(anthracite_url) +
|
||||
": " + textStatus + ": " + errorThrown);
|
||||
}
|
||||
}));
|
||||
}
|
||||
if('es_url' in options){
|
||||
requests.push($.ajax({
|
||||
accepts: {text: 'application/json'},
|
||||
cache: false,
|
||||
dataType: 'json',
|
||||
jsonp: 'json',
|
||||
url: options['es_url'],
|
||||
success: function(data, textStatus, jqXHR ) { es_events = data.hits.hits },
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
on_error("Failed to do elasticsearch request to " + truncate_str(options['es_url']) +
|
||||
": " + textStatus + ": " + errorThrown);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
$.when.apply($, requests).done(drawFlot);
|
||||
};
|
||||
|
||||
$.fn.graphiteRick.render = function(div, options, on_error) {
|
||||
$div = $(div);
|
||||
$div.attr("height", options.height);
|
||||
$div.attr("width", options.width);
|
||||
var drawRick = function(resp_graphite) {
|
||||
// note that resp_graphite.length can be != options.targets.length. let's call:
|
||||
// * target_graphite a targetstring as returned by graphite
|
||||
// * target_option a targetstring configuration
|
||||
// if a target_option contains * graphite will return all matches separately unless you use something to aggregate like sumSeries()
|
||||
// we must render all target_graphite's, but we must merge in the config from the corresponding target_option.
|
||||
// example: for a target_graphite 'stats.foo.bar' we must find a target_option 'stats.foo.bar' *or*
|
||||
// anything that causes graphite to match it, such as 'stats.*.bar' (this would be a bit cleaner if graphite's json
|
||||
// would include also the originally specified target string)
|
||||
// note that this code assumes each target_graphite can only be originating from one target_option,
|
||||
// in some unlikely cases this is not correct (there might be overlap between different target_options with globs)
|
||||
// but in that case I don't see why taking the settings of any of the possible originating target_options wouldn't be fine.
|
||||
var all_targets = [];
|
||||
if(resp_graphite.length == 0 ) {
|
||||
console.warn("no data in graphite response");
|
||||
}
|
||||
for (var res_i = 0; res_i < resp_graphite.length; res_i++) {
|
||||
var target = find_definition(resp_graphite[res_i], options);
|
||||
target.data = [];
|
||||
for (var i in resp_graphite[res_i].datapoints) {
|
||||
target.data[i] = { x: resp_graphite[res_i].datapoints[i][1], y: resp_graphite[res_i].datapoints[i][0] || 0 };
|
||||
}
|
||||
all_targets.push(target);
|
||||
}
|
||||
options['element'] = div;
|
||||
options['series'] = all_targets
|
||||
for (i = 0; i < options['targets'].length; i++ ) {
|
||||
options['targets'][i]['color'] = options['targets'][i]['color'];
|
||||
}
|
||||
var graph = new Rickshaw.Graph(options);
|
||||
if(options['x_axis']) {
|
||||
var x_axis = new Rickshaw.Graph.Axis.Time( { graph: graph } );
|
||||
}
|
||||
if(options['y_axis']) {
|
||||
var y_axis = new Rickshaw.Graph.Axis.Y( {
|
||||
graph: graph,
|
||||
orientation: 'left',
|
||||
tickFormat: Rickshaw.Fixtures.Number.formatKMBT,
|
||||
element: document.getElementById(options['y_axis']),
|
||||
});
|
||||
}
|
||||
if(options['hover_details']) {
|
||||
var hoverDetail = new Rickshaw.Graph.HoverDetail( {
|
||||
graph: graph
|
||||
} );
|
||||
}
|
||||
var setRickshawOptions = function (options, graph) {
|
||||
if ('state' in options && options['state'] == 'stacked') {
|
||||
graph.setRenderer('stack');
|
||||
graph.offset = 'zero';
|
||||
}
|
||||
else { // 'state' is lines
|
||||
graph.setRenderer('line');
|
||||
graph.offset = 'zero';
|
||||
}
|
||||
return graph;
|
||||
}
|
||||
graph = setRickshawOptions(options, graph);
|
||||
graph.render();
|
||||
if (options['legend']) {
|
||||
var legend = new Rickshaw.Graph.Legend({
|
||||
graph: graph,
|
||||
element: document.getElementById(options['legend'])
|
||||
});
|
||||
if(options['legend_toggle']) {
|
||||
var shelving = new Rickshaw.Graph.Behavior.Series.Toggle({
|
||||
graph: graph,
|
||||
legend: legend
|
||||
});
|
||||
}
|
||||
if(options['legend_reorder']) {
|
||||
var order = new Rickshaw.Graph.Behavior.Series.Order({
|
||||
graph: graph,
|
||||
legend: legend
|
||||
});
|
||||
}
|
||||
if(options['legend_highlight']) {
|
||||
var highlighter = new Rickshaw.Graph.Behavior.Series.Highlight({
|
||||
graph: graph,
|
||||
legend: legend
|
||||
});
|
||||
}
|
||||
}
|
||||
if (options['line_stack_toggle']) {
|
||||
var form = document.getElementById(options['line_stack_toggle']);
|
||||
if(!options['renderer'] || options['renderer'] == 'area') {
|
||||
lines_checked = '';
|
||||
stack_checked = ' checked';
|
||||
} else {
|
||||
lines_checked = ' checked';
|
||||
stack_checked = '';
|
||||
}
|
||||
form.innerHTML= '<input type="radio" name="mode" id="lines" value="lines"'+ lines_checked +'>' +
|
||||
'<label class="lines" for="lines">lines</label>' +
|
||||
'<br/><input type="radio" name="mode" id="stacked" value="stacked"' + stack_checked + '>' +
|
||||
'<label class="stack" for="stacked">stacked</label>';
|
||||
|
||||
form.addEventListener('change', function(e) {
|
||||
options['state'] = e.target.value;
|
||||
graph = setRickshawOptions(options, graph);
|
||||
graph.render();
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
data = build_graphite_options(options, true);
|
||||
$.ajax({
|
||||
accepts: {text: 'application/json'},
|
||||
cache: false,
|
||||
dataType: 'json',
|
||||
type: 'POST',
|
||||
data: data.join('&'),
|
||||
url: options['graphite_url'],
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
on_error("Failed to do graphite POST request to " + truncate_str(options['graphite_url']) +
|
||||
": " + textStatus + ": " + errorThrown);
|
||||
}
|
||||
}).done(drawRick);
|
||||
};
|
||||
|
||||
$.fn.graphiteHighcharts.render = function(div, options, on_error) {
|
||||
var id = div.getAttribute('id');
|
||||
$div = $(div);
|
||||
$div.height(options.height);
|
||||
$div.width(options.width);
|
||||
var drawHighcharts = function(resp_graphite) {
|
||||
var hsoptions = {
|
||||
chart: {
|
||||
renderTo: id,
|
||||
type: 'area',
|
||||
zoomType: 'xy',
|
||||
backgroundColor: options.bgcolor,
|
||||
animation: false
|
||||
},
|
||||
exporting: {
|
||||
enabled: false
|
||||
},
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
legend: {
|
||||
borderWidth: 0,
|
||||
useHTML: true,
|
||||
itemHoverStyle: {
|
||||
color: 'red',
|
||||
},
|
||||
itemStyle: {
|
||||
color: options.fgcolor
|
||||
}
|
||||
},
|
||||
plotOptions: {
|
||||
line: {
|
||||
lineWidth: 0.8,
|
||||
marker: {
|
||||
enabled: false
|
||||
},
|
||||
},
|
||||
spline: {
|
||||
lineWidth: 0.8,
|
||||
marker: {
|
||||
enabled: false
|
||||
},
|
||||
},
|
||||
area: {
|
||||
stacking: 'normal',
|
||||
marker: {
|
||||
enabled: false
|
||||
},
|
||||
lineWidth: 0.8
|
||||
},
|
||||
areaspline: {
|
||||
stacking: 'normal',
|
||||
marker: {
|
||||
enabled: false
|
||||
},
|
||||
lineWidth: 0.8
|
||||
}
|
||||
},
|
||||
title: {
|
||||
text: options.title,
|
||||
style: {
|
||||
color: options.fgcolor
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'datetime',
|
||||
tickPixelInterval: 50,
|
||||
labels: {
|
||||
rotation: -45,
|
||||
align: 'right'
|
||||
},
|
||||
lineColor: '#777',
|
||||
tickColor: '#777',
|
||||
maxPadding: 0.01,
|
||||
minPadding: 0.01,
|
||||
gridLineWidth: 0.2
|
||||
},
|
||||
yAxis: {
|
||||
gridLineColor: 'rgba(255, 255, 255, .3)',
|
||||
minorGridLineColor: 'rgba(255,255,255,0.1)',
|
||||
title: {
|
||||
text: options.vtitle,
|
||||
useHTML: true
|
||||
},
|
||||
maxPadding: 0.01,
|
||||
minPadding: 0.01
|
||||
},
|
||||
tooltip: {
|
||||
enabled: options.hover_details,
|
||||
crosshairs:[{width:1, color:'#ccc'},{width:1, color:'#ccc'}],
|
||||
borderWidth: 0,
|
||||
backgroundColor: {
|
||||
linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
|
||||
stops: [
|
||||
[0, 'rgba(96, 96, 96, .8)'],
|
||||
[1, 'rgba(16, 16, 16, .8)']
|
||||
]
|
||||
},
|
||||
style: {
|
||||
color: '#FFF'
|
||||
},
|
||||
useHTML: true,
|
||||
formatter: function() {
|
||||
return "Series: " + this.series.name +
|
||||
"<br/>Local Time: " + Highcharts.dateFormat('%A %B %e %Y %H:%M', this.x) +
|
||||
"<br/>Value: " + Highcharts.numberFormat(this.y, 2);
|
||||
}
|
||||
},
|
||||
series: []
|
||||
};
|
||||
for (var res_i = 0; res_i < resp_graphite.length; res_i++) {
|
||||
var target = find_definition(resp_graphite[res_i], options);
|
||||
var hstarget = {
|
||||
data: [],
|
||||
events: {
|
||||
click: function() {
|
||||
var q;
|
||||
if($.isArray(this.graphite_metric)) {
|
||||
q = '^' + this.options.graphite_metric.join('$|^') + '$';
|
||||
} else {
|
||||
q = '^' + this.options.graphite_metric + '$';
|
||||
}
|
||||
window.location = "/inspect/" + q;
|
||||
}
|
||||
},
|
||||
type: "line",
|
||||
animation: false
|
||||
};
|
||||
if (options.legend && options.legend.labelFormatter) {
|
||||
hstarget.name = options.legend.labelFormatter(target.name);
|
||||
}
|
||||
hstarget.graphite_metric = target.graphite_metric;
|
||||
if (options["series"] && options["series"].stack) {
|
||||
hstarget.type = "area";
|
||||
}
|
||||
for (var i in resp_graphite[res_i].datapoints) {
|
||||
hstarget.data.push([
|
||||
resp_graphite[res_i].datapoints[i][1] * 1000,
|
||||
resp_graphite[res_i].datapoints[i][0]
|
||||
]);
|
||||
}
|
||||
hsoptions.series.push(hstarget)
|
||||
}
|
||||
|
||||
var hschart = new Highcharts.Chart(hsoptions);
|
||||
if (options['line_stack_toggle'])
|
||||
{
|
||||
var form = document.getElementById(options['line_stack_toggle']);
|
||||
var optionshtml = '';
|
||||
|
||||
if (options["series"] && options["series"].stack)
|
||||
{
|
||||
optionshtml += '<option value="stack">stack</option>';
|
||||
optionshtml += '<option value="line">lines</option>';
|
||||
} else {
|
||||
optionshtml += '<option value="line">lines</option>';
|
||||
optionshtml += '<option value="stack">stack</option>';
|
||||
}
|
||||
form.innerHTML = '<select>' + optionshtml + '</select>';
|
||||
|
||||
$("select", form).change(function() {
|
||||
for (var i in hsoptions.series) {
|
||||
var series = hsoptions.series[i];
|
||||
series.stack = i;
|
||||
if (this.value == "stack") {
|
||||
series.type = "area";
|
||||
series.stack = 1;
|
||||
} else {
|
||||
series.type = this.value;
|
||||
}
|
||||
}
|
||||
hschart = new Highcharts.Chart(hsoptions);
|
||||
});
|
||||
}
|
||||
};
|
||||
data = build_graphite_options(options, true);
|
||||
$.ajax({
|
||||
accepts: {text: 'application/json'},
|
||||
cache: false,
|
||||
dataType: 'json',
|
||||
url: options['graphite_url'],
|
||||
type: "POST",
|
||||
data: data.join('&'),
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
on_error("Failed to do graphite POST request to " + truncate_str(options['graphite_url']) +
|
||||
": " + textStatus + ": " + errorThrown);
|
||||
}
|
||||
}).done(drawHighcharts);
|
||||
};
|
||||
// Default settings.
|
||||
// Override with the options argument for per-case setup
|
||||
// or set $.fn.graphite.defaults.<value> for global changes
|
||||
$.fn.graphite.defaults = {
|
||||
from: "-1hour",
|
||||
height: "300",
|
||||
until: "now",
|
||||
graphite_url: "/render/",
|
||||
width: "940"
|
||||
};
|
||||
|
||||
}(jQuery));
|
Loading…
Reference in New Issue
Block a user