3.3
Christian Veigl 14 years ago
parent dd6f4fb093
commit 699a08d954

@ -60,7 +60,6 @@ sub system : Local {
$c->stash->{ctx} = "system";
$c->stash->{plotdata} = \@plotdata;
$c->stash->{tz_offset_hours} = admin::Utils::tz_offset_hours();
return 1;
}
@ -81,7 +80,6 @@ sub voip : Local {
$c->stash->{ctx} = "voip";
$c->stash->{plotdata} = \@plotdata;
$c->stash->{tz_offset_hours} = admin::Utils::tz_offset_hours();
return 1;
}

@ -544,13 +544,4 @@ sub addel_iplist {
return 1;
}
# returns the timezone offset to GMT for
# localhost (hours only) as +HH
sub tz_offset_hours {
my $timezone_offset = strftime("%z", localtime());
$timezone_offset =~ s/.0$//;
return $timezone_offset;
}
1;

@ -1,6 +1,6 @@
/* Plugin for jQuery for working with colors.
*
* Version 1.1.
* Version 1.0.
*
* Inspiration from jQuery color animation plugin by John Resig.
*
@ -13,18 +13,15 @@
* console.log(c.r, c.g, c.b, c.a);
* $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)"
*
* Note that .scale() and .add() return the same modified object
* instead of making a new one.
*
* V. 1.1: Fix error handling so e.g. parsing an empty string does
* produce a color rather than just crashing.
* Note that .scale() and .add() work in-place instead of returning
* new objects.
*/
(function($) {
$.color = {};
(function() {
jQuery.color = {};
// construct color object with some convenient chainable helpers
$.color.make = function (r, g, b, a) {
jQuery.color.make = function (r, g, b, a) {
var o = {};
o.r = r || 0;
o.g = g || 0;
@ -64,7 +61,7 @@
};
o.clone = function () {
return $.color.make(o.r, o.b, o.g, o.a);
return jQuery.color.make(o.r, o.b, o.g, o.a);
};
return o.normalize();
@ -72,7 +69,7 @@
// extract CSS color property from element, going up in the DOM
// if it's "transparent"
$.color.extract = function (elem, css) {
jQuery.color.extract = function (elem, css) {
var c;
do {
c = elem.css(css).toLowerCase();
@ -81,20 +78,19 @@
if (c != '' && c != 'transparent')
break;
elem = elem.parent();
} while (!$.nodeName(elem.get(0), "body"));
} while (!jQuery.nodeName(elem.get(0), "body"));
// catch Safari's way of signalling transparent
if (c == "rgba(0, 0, 0, 0)")
c = "transparent";
return $.color.parse(c);
return jQuery.color.parse(c);
}
// parse CSS color string (like "rgb(10, 32, 43)" or "#fff"),
// returns color object, if parsing failed, you get black (0, 0,
// 0) out
$.color.parse = function (str) {
var res, m = $.color.make;
// returns color object
jQuery.color.parse = function (str) {
var res, m = jQuery.color.make;
// Look for rgb(num,num,num)
if (res = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))
@ -121,12 +117,11 @@
return m(parseInt(res[1]+res[1], 16), parseInt(res[2]+res[2], 16), parseInt(res[3]+res[3], 16));
// Otherwise, we're most likely dealing with a named color
var name = $.trim(str).toLowerCase();
var name = jQuery.trim(str).toLowerCase();
if (name == "transparent")
return m(255, 255, 255, 0);
else {
// default to black
res = lookupColors[name] || [0, 0, 0];
res = lookupColors[name];
return m(res[0], res[1], res[2]);
}
}
@ -175,5 +170,5 @@
silver:[192,192,192],
white:[255,255,255],
yellow:[255,255,0]
};
})(jQuery);
};
})();

@ -1 +1 @@
(function(b){b.color={};b.color.make=function(f,e,c,d){var h={};h.r=f||0;h.g=e||0;h.b=c||0;h.a=d!=null?d:1;h.add=function(k,j){for(var g=0;g<k.length;++g){h[k.charAt(g)]+=j}return h.normalize()};h.scale=function(k,j){for(var g=0;g<k.length;++g){h[k.charAt(g)]*=j}return h.normalize()};h.toString=function(){if(h.a>=1){return"rgb("+[h.r,h.g,h.b].join(",")+")"}else{return"rgba("+[h.r,h.g,h.b,h.a].join(",")+")"}};h.normalize=function(){function g(j,k,i){return k<j?j:(k>i?i:k)}h.r=g(0,parseInt(h.r),255);h.g=g(0,parseInt(h.g),255);h.b=g(0,parseInt(h.b),255);h.a=g(0,h.a,1);return h};h.clone=function(){return b.color.make(h.r,h.b,h.g,h.a)};return h.normalize()};b.color.extract=function(e,d){var f;do{f=e.css(d).toLowerCase();if(f!=""&&f!="transparent"){break}e=e.parent()}while(!b.nodeName(e.get(0),"body"));if(f=="rgba(0, 0, 0, 0)"){f="transparent"}return b.color.parse(f)};b.color.parse=function(f){var e,c=b.color.make;if(e=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(f)){return c(parseInt(e[1],10),parseInt(e[2],10),parseInt(e[3],10))}if(e=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(f)){return c(parseInt(e[1],10),parseInt(e[2],10),parseInt(e[3],10),parseFloat(e[4]))}if(e=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(f)){return c(parseFloat(e[1])*2.55,parseFloat(e[2])*2.55,parseFloat(e[3])*2.55)}if(e=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(f)){return c(parseFloat(e[1])*2.55,parseFloat(e[2])*2.55,parseFloat(e[3])*2.55,parseFloat(e[4]))}if(e=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(f)){return c(parseInt(e[1],16),parseInt(e[2],16),parseInt(e[3],16))}if(e=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(f)){return c(parseInt(e[1]+e[1],16),parseInt(e[2]+e[2],16),parseInt(e[3]+e[3],16))}var d=b.trim(f).toLowerCase();if(d=="transparent"){return c(255,255,255,0)}else{e=a[d]||[0,0,0];return c(e[0],e[1],e[2])}};var a={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery);
(function(){jQuery.color={};jQuery.color.make=function(E,D,B,C){var F={};F.r=E||0;F.g=D||0;F.b=B||0;F.a=C!=null?C:1;F.add=function(I,H){for(var G=0;G<I.length;++G){F[I.charAt(G)]+=H}return F.normalize()};F.scale=function(I,H){for(var G=0;G<I.length;++G){F[I.charAt(G)]*=H}return F.normalize()};F.toString=function(){if(F.a>=1){return"rgb("+[F.r,F.g,F.b].join(",")+")"}else{return"rgba("+[F.r,F.g,F.b,F.a].join(",")+")"}};F.normalize=function(){function G(I,J,H){return J<I?I:(J>H?H:J)}F.r=G(0,parseInt(F.r),255);F.g=G(0,parseInt(F.g),255);F.b=G(0,parseInt(F.b),255);F.a=G(0,F.a,1);return F};F.clone=function(){return jQuery.color.make(F.r,F.b,F.g,F.a)};return F.normalize()};jQuery.color.extract=function(C,B){var D;do{D=C.css(B).toLowerCase();if(D!=""&&D!="transparent"){break}C=C.parent()}while(!jQuery.nodeName(C.get(0),"body"));if(D=="rgba(0, 0, 0, 0)"){D="transparent"}return jQuery.color.parse(D)};jQuery.color.parse=function(E){var D,B=jQuery.color.make;if(D=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(E)){return B(parseInt(D[1],10),parseInt(D[2],10),parseInt(D[3],10))}if(D=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(E)){return B(parseInt(D[1],10),parseInt(D[2],10),parseInt(D[3],10),parseFloat(D[4]))}if(D=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(E)){return B(parseFloat(D[1])*2.55,parseFloat(D[2])*2.55,parseFloat(D[3])*2.55)}if(D=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(E)){return B(parseFloat(D[1])*2.55,parseFloat(D[2])*2.55,parseFloat(D[3])*2.55,parseFloat(D[4]))}if(D=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(E)){return B(parseInt(D[1],16),parseInt(D[2],16),parseInt(D[3],16))}if(D=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(E)){return B(parseInt(D[1]+D[1],16),parseInt(D[2]+D[2],16),parseInt(D[3]+D[3],16))}var C=jQuery.trim(E).toLowerCase();if(C=="transparent"){return B(255,255,255,0)}else{D=A[C];return B(D[0],D[1],D[2])}};var A={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})();

@ -1,5 +1,5 @@
/*
Flot plugin for showing crosshairs, thin lines, when the mouse hovers
Flot plugin for showing a crosshair, thin lines, when the mouse hovers
over the plot.
crosshair: {
@ -19,11 +19,10 @@ The plugin also adds four public methods:
- setCrosshair(pos)
Set the position of the crosshair. Note that this is cleared if
the user moves the mouse. "pos" is in coordinates of the plot and
should be on the form { x: xpos, y: ypos } (you can use x2/x3/...
if you're using multiple axes), which is coincidentally the same
format as what you get from a "plothover" event. If "pos" is null,
the crosshair is cleared.
the user moves the mouse. "pos" should be on the form { x: xpos,
y: ypos } (or x2 and y2 if you're using the secondary axes), which
is coincidentally the same format as what you get from a "plothover"
event. If "pos" is null, the crosshair is cleared.
- clearCrosshair()
@ -70,9 +69,10 @@ The plugin also adds four public methods:
if (!pos)
crosshair.x = -1;
else {
var o = plot.p2c(pos);
crosshair.x = Math.max(0, Math.min(o.left, plot.width()));
crosshair.y = Math.max(0, Math.min(o.top, plot.height()));
var axes = plot.getAxes();
crosshair.x = Math.max(0, Math.min(pos.x != null ? axes.xaxis.p2c(pos.x) : axes.x2axis.p2c(pos.x2), plot.width()));
crosshair.y = Math.max(0, Math.min(pos.y != null ? axes.yaxis.p2c(pos.y) : axes.y2axis.p2c(pos.y2), plot.height()));
}
plot.triggerRedrawOverlay();
@ -90,37 +90,31 @@ The plugin also adds four public methods:
crosshair.locked = false;
}
function onMouseOut(e) {
if (crosshair.locked)
return;
if (crosshair.x != -1) {
crosshair.x = -1;
plot.triggerRedrawOverlay();
}
}
function onMouseMove(e) {
if (crosshair.locked)
return;
if (plot.getSelection && plot.getSelection()) {
crosshair.x = -1; // hide the crosshair while selecting
return;
}
var offset = plot.offset();
crosshair.x = Math.max(0, Math.min(e.pageX - offset.left, plot.width()));
crosshair.y = Math.max(0, Math.min(e.pageY - offset.top, plot.height()));
plot.triggerRedrawOverlay();
}
plot.hooks.bindEvents.push(function (plot, eventHolder) {
if (!plot.getOptions().crosshair.mode)
return;
eventHolder.mouseout(onMouseOut);
eventHolder.mousemove(onMouseMove);
eventHolder.mouseout(function () {
if (crosshair.x != -1) {
crosshair.x = -1;
plot.triggerRedrawOverlay();
}
});
eventHolder.mousemove(function (e) {
if (plot.getSelection && plot.getSelection()) {
crosshair.x = -1; // hide the crosshair while selecting
return;
}
if (crosshair.locked)
return;
var offset = plot.offset();
crosshair.x = Math.max(0, Math.min(e.pageX - offset.left, plot.width()));
crosshair.y = Math.max(0, Math.min(e.pageY - offset.top, plot.height()));
plot.triggerRedrawOverlay();
});
});
plot.hooks.drawOverlay.push(function (plot, ctx) {
@ -151,11 +145,6 @@ The plugin also adds four public methods:
}
ctx.restore();
});
plot.hooks.shutdown.push(function (plot, eventHolder) {
eventHolder.unbind("mouseout", onMouseOut);
eventHolder.unbind("mousemove", onMouseMove);
});
}
$.plot.plugins.push({

@ -1 +1 @@
(function(b){var a={crosshair:{mode:null,color:"rgba(170, 0, 0, 0.80)",lineWidth:1}};function c(h){var j={x:-1,y:-1,locked:false};h.setCrosshair=function e(l){if(!l){j.x=-1}else{var k=h.p2c(l);j.x=Math.max(0,Math.min(k.left,h.width()));j.y=Math.max(0,Math.min(k.top,h.height()))}h.triggerRedrawOverlay()};h.clearCrosshair=h.setCrosshair;h.lockCrosshair=function f(k){if(k){h.setCrosshair(k)}j.locked=true};h.unlockCrosshair=function g(){j.locked=false};function d(k){if(j.locked){return}if(j.x!=-1){j.x=-1;h.triggerRedrawOverlay()}}function i(k){if(j.locked){return}if(h.getSelection&&h.getSelection()){j.x=-1;return}var l=h.offset();j.x=Math.max(0,Math.min(k.pageX-l.left,h.width()));j.y=Math.max(0,Math.min(k.pageY-l.top,h.height()));h.triggerRedrawOverlay()}h.hooks.bindEvents.push(function(l,k){if(!l.getOptions().crosshair.mode){return}k.mouseout(d);k.mousemove(i)});h.hooks.drawOverlay.push(function(m,k){var n=m.getOptions().crosshair;if(!n.mode){return}var l=m.getPlotOffset();k.save();k.translate(l.left,l.top);if(j.x!=-1){k.strokeStyle=n.color;k.lineWidth=n.lineWidth;k.lineJoin="round";k.beginPath();if(n.mode.indexOf("x")!=-1){k.moveTo(j.x,0);k.lineTo(j.x,m.height())}if(n.mode.indexOf("y")!=-1){k.moveTo(0,j.y);k.lineTo(m.width(),j.y)}k.stroke()}k.restore()});h.hooks.shutdown.push(function(l,k){k.unbind("mouseout",d);k.unbind("mousemove",i)})}b.plot.plugins.push({init:c,options:a,name:"crosshair",version:"1.0"})})(jQuery);
(function(B){var A={crosshair:{mode:null,color:"rgba(170, 0, 0, 0.80)",lineWidth:1}};function C(G){var H={x:-1,y:-1,locked:false};G.setCrosshair=function D(J){if(!J){H.x=-1}else{var I=G.getAxes();H.x=Math.max(0,Math.min(J.x!=null?I.xaxis.p2c(J.x):I.x2axis.p2c(J.x2),G.width()));H.y=Math.max(0,Math.min(J.y!=null?I.yaxis.p2c(J.y):I.y2axis.p2c(J.y2),G.height()))}G.triggerRedrawOverlay()};G.clearCrosshair=G.setCrosshair;G.lockCrosshair=function E(I){if(I){G.setCrosshair(I)}H.locked=true};G.unlockCrosshair=function F(){H.locked=false};G.hooks.bindEvents.push(function(J,I){if(!J.getOptions().crosshair.mode){return }I.mouseout(function(){if(H.x!=-1){H.x=-1;J.triggerRedrawOverlay()}});I.mousemove(function(K){if(J.getSelection&&J.getSelection()){H.x=-1;return }if(H.locked){return }var L=J.offset();H.x=Math.max(0,Math.min(K.pageX-L.left,J.width()));H.y=Math.max(0,Math.min(K.pageY-L.top,J.height()));J.triggerRedrawOverlay()})});G.hooks.drawOverlay.push(function(K,I){var L=K.getOptions().crosshair;if(!L.mode){return }var J=K.getPlotOffset();I.save();I.translate(J.left,J.top);if(H.x!=-1){I.strokeStyle=L.color;I.lineWidth=L.lineWidth;I.lineJoin="round";I.beginPath();if(L.mode.indexOf("x")!=-1){I.moveTo(H.x,0);I.lineTo(H.x,K.height())}if(L.mode.indexOf("y")!=-1){I.moveTo(0,H.y);I.lineTo(K.width(),H.y)}I.stroke()}I.restore()})}B.plot.plugins.push({init:C,options:A,name:"crosshair",version:"1.0"})})(jQuery);

@ -1,183 +0,0 @@
/*
Flot plugin for computing bottoms for filled line and bar charts.
The case: you've got two series that you want to fill the area
between. In Flot terms, you need to use one as the fill bottom of the
other. You can specify the bottom of each data point as the third
coordinate manually, or you can use this plugin to compute it for you.
In order to name the other series, you need to give it an id, like this
var dataset = [
{ data: [ ... ], id: "foo" } , // use default bottom
{ data: [ ... ], fillBetween: "foo" }, // use first dataset as bottom
];
$.plot($("#placeholder"), dataset, { line: { show: true, fill: true }});
As a convenience, if the id given is a number that doesn't appear as
an id in the series, it is interpreted as the index in the array
instead (so fillBetween: 0 can also mean the first series).
Internally, the plugin modifies the datapoints in each series. For
line series, extra data points might be inserted through
interpolation. Note that at points where the bottom line is not
defined (due to a null point or start/end of line), the current line
will show a gap too. The algorithm comes from the jquery.flot.stack.js
plugin, possibly some code could be shared.
*/
(function ($) {
var options = {
series: { fillBetween: null } // or number
};
function init(plot) {
function findBottomSeries(s, allseries) {
var i;
for (i = 0; i < allseries.length; ++i) {
if (allseries[i].id == s.fillBetween)
return allseries[i];
}
if (typeof s.fillBetween == "number") {
i = s.fillBetween;
if (i < 0 || i >= allseries.length)
return null;
return allseries[i];
}
return null;
}
function computeFillBottoms(plot, s, datapoints) {
if (s.fillBetween == null)
return;
var other = findBottomSeries(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,
withbottom = ps > 2 && datapoints.format[2].y,
withsteps = withlines && s.lines.steps,
fromgap = true,
i = 0, j = 0, l;
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];
py = points[i + 1];
qx = otherpoints[j];
qy = otherpoints[j + 1];
bottom = 0;
if (px == qx) {
for (m = 0; m < ps; ++m)
newpoints.push(points[i + m]);
//newpoints[l + 1] += 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 + 1] - py) * (qx - px) / (points[i - ps] - px);
newpoints.push(qx);
newpoints.push(intery)
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 + 1] - qy) * (px - qx) / (otherpoints[j - otherps] - qx);
//newpoints[l + 1] += 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(computeFillBottoms);
}
$.plot.plugins.push({
init: init,
options: options,
name: 'fillbetween',
version: '1.0'
});
})(jQuery);

@ -1 +0,0 @@
(function(b){var a={series:{fillBetween:null}};function c(f){function d(j,h){var g;for(g=0;g<h.length;++g){if(h[g].id==j.fillBetween){return h[g]}}if(typeof j.fillBetween=="number"){g=j.fillBetween;if(g<0||g>=h.length){return null}return h[g]}return null}function e(B,u,g){if(u.fillBetween==null){return}var p=d(u,B.getData());if(!p){return}var y=g.pointsize,E=g.points,h=p.datapoints.pointsize,x=p.datapoints.points,r=[],w,v,k,G,F,q,t=u.lines.show,o=y>2&&g.format[2].y,n=t&&u.lines.steps,D=true,C=0,A=0,z;while(true){if(C>=E.length){break}z=r.length;if(E[C]==null){for(m=0;m<y;++m){r.push(E[C+m])}C+=y}else{if(A>=x.length){if(!t){for(m=0;m<y;++m){r.push(E[C+m])}}C+=y}else{if(x[A]==null){for(m=0;m<y;++m){r.push(null)}D=true;A+=h}else{w=E[C];v=E[C+1];G=x[A];F=x[A+1];q=0;if(w==G){for(m=0;m<y;++m){r.push(E[C+m])}q=F;C+=y;A+=h}else{if(w>G){if(t&&C>0&&E[C-y]!=null){k=v+(E[C-y+1]-v)*(G-w)/(E[C-y]-w);r.push(G);r.push(k);for(m=2;m<y;++m){r.push(E[C+m])}q=F}A+=h}else{if(D&&t){C+=y;continue}for(m=0;m<y;++m){r.push(E[C+m])}if(t&&A>0&&x[A-h]!=null){q=F+(x[A-h+1]-F)*(w-G)/(x[A-h]-G)}C+=y}}D=false;if(z!=r.length&&o){r[z+2]=q}}}}if(n&&z!=r.length&&z>0&&r[z]!=null&&r[z]!=r[z-y]&&r[z+1]!=r[z-y+1]){for(m=0;m<y;++m){r[z+y+m]=r[z+m]}r[z+1]=r[z-y+1]}}g.points=r}f.hooks.processDatapoints.push(e)}b.plot.plugins.push({init:c,options:a,name:"fillbetween",version:"1.0"})})(jQuery);

@ -112,102 +112,101 @@ images (like Google Maps).
});
}
function drawSeries(plot, ctx, series) {
function draw(plot, ctx) {
var plotOffset = plot.getPlotOffset();
if (!series.images || !series.images.show)
return;
var points = series.datapoints.points,
ps = series.datapoints.pointsize;
for (var i = 0; i < points.length; i += ps) {
var img = points[i],
x1 = points[i + 1], y1 = points[i + 2],
x2 = points[i + 3], y2 = points[i + 4],
xaxis = series.xaxis, yaxis = series.yaxis,
tmp;
// actually we should check img.complete, but it
// appears to be a somewhat unreliable indicator in
// IE6 (false even after load event)
if (!img || img.width <= 0 || img.height <= 0)
continue;
if (x1 > x2) {
tmp = x2;
x2 = x1;
x1 = tmp;
}
if (y1 > y2) {
tmp = y2;
y2 = y1;
y1 = tmp;
}
// if the anchor is at the center of the pixel, expand the
// image by 1/2 pixel in each direction
if (series.images.anchor == "center") {
tmp = 0.5 * (x2-x1) / (img.width - 1);
x1 -= tmp;
x2 += tmp;
tmp = 0.5 * (y2-y1) / (img.height - 1);
y1 -= tmp;
y2 += tmp;
}
$.each(plot.getData(), function (i, series) {
var points = series.datapoints.points,
ps = series.datapoints.pointsize;
// clip
if (x1 == x2 || y1 == y2 ||
x1 >= xaxis.max || x2 <= xaxis.min ||
y1 >= yaxis.max || y2 <= yaxis.min)
continue;
var sx1 = 0, sy1 = 0, sx2 = img.width, sy2 = img.height;
if (x1 < xaxis.min) {
sx1 += (sx2 - sx1) * (xaxis.min - x1) / (x2 - x1);
x1 = xaxis.min;
}
for (var i = 0; i < points.length; i += ps) {
var img = points[i],
x1 = points[i + 1], y1 = points[i + 2],
x2 = points[i + 3], y2 = points[i + 4],
xaxis = series.xaxis, yaxis = series.yaxis,
tmp;
// actually we should check img.complete, but it
// appears to be a somewhat unreliable indicator in
// IE6 (false even after load event)
if (!img || img.width <= 0 || img.height <= 0)
continue;
if (x1 > x2) {
tmp = x2;
x2 = x1;
x1 = tmp;
}
if (y1 > y2) {
tmp = y2;
y2 = y1;
y1 = tmp;
}
// if the anchor is at the center of the pixel, expand the
// image by 1/2 pixel in each direction
if (series.images.anchor == "center") {
tmp = 0.5 * (x2-x1) / (img.width - 1);
x1 -= tmp;
x2 += tmp;
tmp = 0.5 * (y2-y1) / (img.height - 1);
y1 -= tmp;
y2 += tmp;
}
// clip
if (x1 == x2 || y1 == y2 ||
x1 >= xaxis.max || x2 <= xaxis.min ||
y1 >= yaxis.max || y2 <= yaxis.min)
continue;
var sx1 = 0, sy1 = 0, sx2 = img.width, sy2 = img.height;
if (x1 < xaxis.min) {
sx1 += (sx2 - sx1) * (xaxis.min - x1) / (x2 - x1);
x1 = xaxis.min;
}
if (x2 > xaxis.max) {
sx2 += (sx2 - sx1) * (xaxis.max - x2) / (x2 - x1);
x2 = xaxis.max;
}
if (x2 > xaxis.max) {
sx2 += (sx2 - sx1) * (xaxis.max - x2) / (x2 - x1);
x2 = xaxis.max;
}
if (y1 < yaxis.min) {
sy2 += (sy1 - sy2) * (yaxis.min - y1) / (y2 - y1);
y1 = yaxis.min;
}
if (y1 < yaxis.min) {
sy2 += (sy1 - sy2) * (yaxis.min - y1) / (y2 - y1);
y1 = yaxis.min;
}
if (y2 > yaxis.max) {
sy1 += (sy1 - sy2) * (yaxis.max - y2) / (y2 - y1);
y2 = yaxis.max;
}
x1 = xaxis.p2c(x1);
x2 = xaxis.p2c(x2);
y1 = yaxis.p2c(y1);
y2 = yaxis.p2c(y2);
// the transformation may have swapped us
if (x1 > x2) {
tmp = x2;
x2 = x1;
x1 = tmp;
}
if (y1 > y2) {
tmp = y2;
y2 = y1;
y1 = tmp;
}
if (y2 > yaxis.max) {
sy1 += (sy1 - sy2) * (yaxis.max - y2) / (y2 - y1);
y2 = yaxis.max;
}
x1 = xaxis.p2c(x1);
x2 = xaxis.p2c(x2);
y1 = yaxis.p2c(y1);
y2 = yaxis.p2c(y2);
// the transformation may have swapped us
if (x1 > x2) {
tmp = x2;
x2 = x1;
x1 = tmp;
}
if (y1 > y2) {
tmp = y2;
y2 = y1;
y1 = tmp;
}
tmp = ctx.globalAlpha;
ctx.globalAlpha *= series.images.alpha;
ctx.drawImage(img,
sx1, sy1, sx2 - sx1, sy2 - sy1,
x1 + plotOffset.left, y1 + plotOffset.top,
x2 - x1, y2 - y1);
ctx.globalAlpha = tmp;
}
tmp = ctx.globalAlpha;
ctx.globalAlpha *= series.images.alpha;
ctx.drawImage(img,
sx1, sy1, sx2 - sx1, sy2 - sy1,
x1 + plotOffset.left, y1 + plotOffset.top,
x2 - x1, y2 - y1);
ctx.globalAlpha = tmp;
}
});
}
function processRawData(plot, series, data, datapoints) {
@ -226,7 +225,7 @@ images (like Google Maps).
function init(plot) {
plot.hooks.processRawData.push(processRawData);
plot.hooks.drawSeries.push(drawSeries);
plot.hooks.draw.push(draw);
}
$.plot.plugins.push({

@ -1 +1 @@
(function(c){var a={series:{images:{show:false,alpha:1,anchor:"corner"}}};c.plot.image={};c.plot.image.loadDataImages=function(g,f,k){var j=[],h=[];var i=f.series.images.show;c.each(g,function(l,m){if(!(i||m.images.show)){return}if(m.data){m=m.data}c.each(m,function(n,o){if(typeof o[0]=="string"){j.push(o[0]);h.push(o)}})});c.plot.image.load(j,function(l){c.each(h,function(n,o){var m=o[0];if(l[m]){o[0]=l[m]}});k()})};c.plot.image.load=function(h,i){var g=h.length,f={};if(g==0){i({})}c.each(h,function(k,j){var l=function(){--g;f[j]=this;if(g==0){i(f)}};c("<img />").load(l).error(l).attr("src",j)})};function d(q,o,l){var m=q.getPlotOffset();if(!l.images||!l.images.show){return}var r=l.datapoints.points,n=l.datapoints.pointsize;for(var t=0;t<r.length;t+=n){var y=r[t],w=r[t+1],g=r[t+2],v=r[t+3],f=r[t+4],h=l.xaxis,u=l.yaxis,x;if(!y||y.width<=0||y.height<=0){continue}if(w>v){x=v;v=w;w=x}if(g>f){x=f;f=g;g=x}if(l.images.anchor=="center"){x=0.5*(v-w)/(y.width-1);w-=x;v+=x;x=0.5*(f-g)/(y.height-1);g-=x;f+=x}if(w==v||g==f||w>=h.max||v<=h.min||g>=u.max||f<=u.min){continue}var k=0,s=0,j=y.width,p=y.height;if(w<h.min){k+=(j-k)*(h.min-w)/(v-w);w=h.min}if(v>h.max){j+=(j-k)*(h.max-v)/(v-w);v=h.max}if(g<u.min){p+=(s-p)*(u.min-g)/(f-g);g=u.min}if(f>u.max){s+=(s-p)*(u.max-f)/(f-g);f=u.max}w=h.p2c(w);v=h.p2c(v);g=u.p2c(g);f=u.p2c(f);if(w>v){x=v;v=w;w=x}if(g>f){x=f;f=g;g=x}x=o.globalAlpha;o.globalAlpha*=l.images.alpha;o.drawImage(y,k,s,j-k,p-s,w+m.left,g+m.top,v-w,f-g);o.globalAlpha=x}}function b(i,f,g,h){if(!f.images.show){return}h.format=[{required:true},{x:true,number:true,required:true},{y:true,number:true,required:true},{x:true,number:true,required:true},{y:true,number:true,required:true}]}function e(f){f.hooks.processRawData.push(b);f.hooks.drawSeries.push(d)}c.plot.plugins.push({init:e,options:a,name:"image",version:"1.1"})})(jQuery);
(function(D){var B={series:{images:{show:false,alpha:1,anchor:"corner"}}};D.plot.image={};D.plot.image.loadDataImages=function(G,F,K){var J=[],H=[];var I=F.series.images.show;D.each(G,function(L,M){if(!(I||M.images.show)){return }if(M.data){M=M.data}D.each(M,function(N,O){if(typeof O[0]=="string"){J.push(O[0]);H.push(O)}})});D.plot.image.load(J,function(L){D.each(H,function(N,O){var M=O[0];if(L[M]){O[0]=L[M]}});K()})};D.plot.image.load=function(H,I){var G=H.length,F={};if(G==0){I({})}D.each(H,function(K,J){var L=function(){--G;F[J]=this;if(G==0){I(F)}};D("<img />").load(L).error(L).attr("src",J)})};function A(H,F){var G=H.getPlotOffset();D.each(H.getData(),function(O,P){var X=P.datapoints.points,I=P.datapoints.pointsize;for(var O=0;O<X.length;O+=I){var Q=X[O],M=X[O+1],V=X[O+2],K=X[O+3],T=X[O+4],W=P.xaxis,S=P.yaxis,N;if(!Q||Q.width<=0||Q.height<=0){continue}if(M>K){N=K;K=M;M=N}if(V>T){N=T;T=V;V=N}if(P.images.anchor=="center"){N=0.5*(K-M)/(Q.width-1);M-=N;K+=N;N=0.5*(T-V)/(Q.height-1);V-=N;T+=N}if(M==K||V==T||M>=W.max||K<=W.min||V>=S.max||T<=S.min){continue}var L=0,U=0,J=Q.width,R=Q.height;if(M<W.min){L+=(J-L)*(W.min-M)/(K-M);M=W.min}if(K>W.max){J+=(J-L)*(W.max-K)/(K-M);K=W.max}if(V<S.min){R+=(U-R)*(S.min-V)/(T-V);V=S.min}if(T>S.max){U+=(U-R)*(S.max-T)/(T-V);T=S.max}M=W.p2c(M);K=W.p2c(K);V=S.p2c(V);T=S.p2c(T);if(M>K){N=K;K=M;M=N}if(V>T){N=T;T=V;V=N}N=F.globalAlpha;F.globalAlpha*=P.images.alpha;F.drawImage(Q,L,U,J-L,R-U,M+G.left,V+G.top,K-M,T-V);F.globalAlpha=N}})}function C(I,F,G,H){if(!F.images.show){return }H.format=[{required:true},{x:true,number:true,required:true},{y:true,number:true,required:true},{x:true,number:true,required:true},{y:true,number:true,required:true}]}function E(F){F.hooks.processRawData.push(C);F.hooks.draw.push(A)}D.plot.plugins.push({init:E,options:B,name:"image",version:"1.1"})})(jQuery);

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -7,6 +7,20 @@ plot.zoomOut() and plot.pan(offset) so you easily can add custom
controls. It also fires a "plotpan" and "plotzoom" event when
something happens, useful for synchronizing plots.
Example usage:
plot = $.plot(...);
// zoom default amount in on the pixel (100, 200)
plot.zoom({ center: { left: 10, top: 20 } });
// zoom out again
plot.zoomOut({ center: { left: 10, top: 20 } });
// pan 100 pixels to the left and 20 down
plot.pan({ left: -100, top: 20 })
Options:
zoom: {
@ -17,66 +31,25 @@ Options:
pan: {
interactive: false
cursor: "move" // CSS mouse cursor value used when dragging, e.g. "pointer"
frameRate: 20
}
xaxis, yaxis, x2axis, y2axis: {
zoomRange: null // or [number, number] (min range, max range) or false
panRange: null // or [number, number] (min, max) or false
zoomRange: null // or [number, number] (min range, max range)
panRange: null // or [number, number] (min, max)
}
"interactive" enables the built-in drag/click behaviour. If you enable
interactive for pan, then you'll have a basic plot that supports
moving around; the same for zoom.
"amount" specifies the default amount to zoom in (so 1.5 = 150%)
relative to the current viewport.
"cursor" is a standard CSS mouse cursor string used for visual
feedback to the user when dragging.
"frameRate" specifies the maximum number of times per second the plot
will update itself while the user is panning around on it (set to null
to disable intermediate pans, the plot will then not update until the
mouse button is released).
"interactive" enables the built-in drag/click behaviour. "amount" is
the amount to zoom the viewport relative to the current range, so 1 is
100% (i.e. no change), 1.5 is 150% (zoom in), 0.7 is 70% (zoom out).
"zoomRange" is the interval in which zooming can happen, e.g. with
zoomRange: [1, 100] the zoom will never scale the axis so that the
difference between min and max is smaller than 1 or larger than 100.
You can set either end to null to ignore, e.g. [1, null]. If you set
zoomRange to false, zooming on that axis will be disabled.
You can set either of them to null to ignore.
"panRange" confines the panning to stay within a range, e.g. with
panRange: [-10, 20] panning stops at -10 in one end and at 20 in the
other. Either can be null, e.g. [-10, null]. If you set
panRange to false, panning on that axis will be disabled.
Example API usage:
plot = $.plot(...);
// zoom default amount in on the pixel (10, 20)
plot.zoom({ center: { left: 10, top: 20 } });
// zoom out again
plot.zoomOut({ center: { left: 10, top: 20 } });
// zoom 200% in on the pixel (10, 20)
plot.zoom({ amount: 2, center: { left: 10, top: 20 } });
// pan 100 pixels to the left and 20 down
plot.pan({ left: -100, top: 20 })
Here, "center" specifies where the center of the zooming should
happen. Note that this is defined in pixel space, not the space of the
data points (you can use the p2c helpers on the axes in Flot to help
you convert between these).
"amount" is the amount to zoom the viewport relative to the current
range, so 1 is 100% (i.e. no change), 1.5 is 150% (zoom in), 0.7 is
70% (zoom out). You can set the default in the options.
other. Either can be null.
*/
@ -119,79 +92,51 @@ Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-L
amount: 1.5 // how much to zoom relative to current position, 2 = 200% (zoom in), 0.5 = 50% (zoom out)
},
pan: {
interactive: false,
cursor: "move",
frameRate: 20
interactive: false
}
};
function init(plot) {
function onZoomClick(e, zoomOut) {
var c = plot.offset();
c.left = e.pageX - c.left;
c.top = e.pageY - c.top;
if (zoomOut)
plot.zoomOut({ center: c });
else
plot.zoom({ center: c });
}
function onMouseWheel(e, delta) {
onZoomClick(e, delta < 0);
return false;
}
var prevCursor = 'default', prevPageX = 0, prevPageY = 0,
panTimeout = null;
function onDragStart(e) {
if (e.which != 1) // only accept left-click
return false;
var c = plot.getPlaceholder().css('cursor');
if (c)
prevCursor = c;
plot.getPlaceholder().css('cursor', plot.getOptions().pan.cursor);
prevPageX = e.pageX;
prevPageY = e.pageY;
}
function onDrag(e) {
var frameRate = plot.getOptions().pan.frameRate;
if (panTimeout || !frameRate)
return;
panTimeout = setTimeout(function () {
plot.pan({ left: prevPageX - e.pageX,
top: prevPageY - e.pageY });
prevPageX = e.pageX;
prevPageY = e.pageY;
panTimeout = null;
}, 1 / frameRate * 1000);
}
function onDragEnd(e) {
if (panTimeout) {
clearTimeout(panTimeout);
panTimeout = null;
}
plot.getPlaceholder().css('cursor', prevCursor);
plot.pan({ left: prevPageX - e.pageX,
top: prevPageY - e.pageY });
}
function bindEvents(plot, eventHolder) {
var o = plot.getOptions();
if (o.zoom.interactive) {
eventHolder[o.zoom.trigger](onZoomClick);
eventHolder.mousewheel(onMouseWheel);
}
function clickHandler(e, zoomOut) {
var c = plot.offset();
c.left = e.pageX - c.left;
c.top = e.pageY - c.top;
if (zoomOut)
plot.zoomOut({ center: c });
else
plot.zoom({ center: c });
}
eventHolder[o.zoom.trigger](clickHandler);
eventHolder.mousewheel(function (e, delta) {
clickHandler(e, delta < 0);
return false;
});
}
if (o.pan.interactive) {
eventHolder.bind("dragstart", { distance: 10 }, onDragStart);
eventHolder.bind("drag", onDrag);
eventHolder.bind("dragend", onDragEnd);
var prevCursor = 'default', pageX = 0, pageY = 0;
eventHolder.bind("dragstart", { distance: 10 }, function (e) {
if (e.which != 1) // only accept left-click
return false;
eventHolderCursor = eventHolder.css('cursor');
eventHolder.css('cursor', 'move');
pageX = e.pageX;
pageY = e.pageY;
});
eventHolder.bind("drag", function (e) {
// unused at the moment, but we need it here to
// trigger the dragstart/dragend events
});
eventHolder.bind("dragend", function (e) {
eventHolder.css('cursor', prevCursor);
plot.pan({ left: pageX - e.pageX,
top: pageY - e.pageY });
});
}
}
@ -210,53 +155,51 @@ Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-L
if (!args)
args = {};
var c = args.center,
amount = args.amount || plot.getOptions().zoom.amount,
var axes = plot.getAxes(),
options = plot.getOptions(),
c = args.center,
amount = args.amount ? args.amount : options.zoom.amount,
w = plot.width(), h = plot.height();
if (!c)
c = { left: w / 2, top: h / 2 };
var xf = c.left / w,
x1 = c.left - xf * w / amount,
x2 = c.left + (1 - xf) * w / amount,
yf = c.top / h,
minmax = {
x: {
min: c.left - xf * w / amount,
max: c.left + (1 - xf) * w / amount
},
y: {
min: c.top - yf * h / amount,
max: c.top + (1 - yf) * h / amount
}
};
$.each(plot.getAxes(), function(_, axis) {
var opts = axis.options,
min = minmax[axis.direction].min,
max = minmax[axis.direction].max,
zr = opts.zoomRange;
y1 = c.top - yf * h / amount,
y2 = c.top + (1 - yf) * h / amount;
if (zr === false) // no zooming on this axis
function scaleAxis(min, max, name) {
var axis = axes[name],
axisOptions = options[name];
if (!axis.used)
return;
min = axis.c2p(min);
max = axis.c2p(max);
if (min > max) {
// make sure min < max
var tmp = min;
if (max < min) { // make sure min < max
var tmp = min
min = max;
max = tmp;
}
var range = max - min;
var range = max - min, zr = axisOptions.zoomRange;
if (zr &&
((zr[0] != null && range < zr[0]) ||
(zr[1] != null && range > zr[1])))
return;
opts.min = min;
opts.max = max;
});
axisOptions.min = min;
axisOptions.max = max;
}
scaleAxis(x1, x2, 'xaxis');
scaleAxis(x1, x2, 'x2axis');
scaleAxis(y1, y2, 'yaxis');
scaleAxis(y1, y2, 'y2axis');
plot.setupGrid();
plot.draw();
@ -266,45 +209,49 @@ Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-L
}
plot.pan = function (args) {
var delta = {
x: +args.left,
y: +args.top
};
if (isNaN(delta.x))
delta.x = 0;
if (isNaN(delta.y))
delta.y = 0;
$.each(plot.getAxes(), function (_, axis) {
var opts = axis.options,
min, max, d = delta[axis.direction];
var l = +args.left, t = +args.top,
axes = plot.getAxes(), options = plot.getOptions();
if (isNaN(l))
l = 0;
if (isNaN(t))
t = 0;
function panAxis(delta, name) {
var axis = axes[name],
axisOptions = options[name],
min, max;
if (!axis.used)
return;
min = axis.c2p(axis.p2c(axis.min) + d),
max = axis.c2p(axis.p2c(axis.max) + d);
min = axis.c2p(axis.p2c(axis.min) + delta),
max = axis.c2p(axis.p2c(axis.max) + delta);
var pr = opts.panRange;
if (pr === false) // no panning on this axis
return;
var pr = axisOptions.panRange;
if (pr) {
// check whether we hit the wall
if (pr[0] != null && pr[0] > min) {
d = pr[0] - min;
min += d;
max += d;
delta = pr[0] - min;
min += delta;
max += delta;
}
if (pr[1] != null && pr[1] < max) {
d = pr[1] - max;
min += d;
max += d;
delta = pr[1] - max;
min += delta;
max += delta;
}
}
opts.min = min;
opts.max = max;
});
axisOptions.min = min;
axisOptions.max = max;
}
panAxis(l, 'xaxis');
panAxis(l, 'x2axis');
panAxis(t, 'yaxis');
panAxis(t, 'y2axis');
plot.setupGrid();
plot.draw();
@ -312,25 +259,14 @@ Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-L
if (!args.preventEvent)
plot.getPlaceholder().trigger("plotpan", [ plot ]);
}
function shutdown(plot, eventHolder) {
eventHolder.unbind(plot.getOptions().zoom.trigger, onZoomClick);
eventHolder.unbind("mousewheel", onMouseWheel);
eventHolder.unbind("dragstart", onDragStart);
eventHolder.unbind("drag", onDrag);
eventHolder.unbind("dragend", onDragEnd);
if (panTimeout)
clearTimeout(panTimeout);
}
plot.hooks.bindEvents.push(bindEvents);
plot.hooks.shutdown.push(shutdown);
}
$.plot.plugins.push({
init: init,
options: options,
name: 'navigate',
version: '1.3'
version: '1.1'
});
})(jQuery);

File diff suppressed because one or more lines are too long

@ -1,750 +0,0 @@
/*
Flot plugin for rendering pie charts. The plugin assumes the data is
coming is as a single data value for each series, and each of those
values is a positive value or zero (negative numbers don't make
any sense and will cause strange effects). The data values do
NOT need to be passed in as percentage values because it
internally calculates the total and percentages.
* Created by Brian Medendorp, June 2009
* Updated November 2009 with contributions from: btburnett3, Anthony Aragues and Xavi Ivars
* Changes:
2009-10-22: lineJoin set to round
2009-10-23: IE full circle fix, donut
2009-11-11: Added basic hover from btburnett3 - does not work in IE, and center is off in Chrome and Opera
2009-11-17: Added IE hover capability submitted by Anthony Aragues
2009-11-18: Added bug fix submitted by Xavi Ivars (issues with arrays when other JS libraries are included as well)
Available options are:
series: {
pie: {
show: true/false
radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto'
innerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect
startAngle: 0-2 factor of PI used for starting angle (in radians) i.e 3/2 starts at the top, 0 and 2 have the same result
tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show)
offset: {
top: integer value to move the pie up or down
left: integer value to move the pie left or right, or 'auto'
},
stroke: {
color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#FFF')
width: integer pixel width of the stroke
},
label: {
show: true/false, or 'auto'
formatter: a user-defined function that modifies the text/style of the label text
radius: 0-1 for percentage of fullsize, or a specified pixel length
background: {
color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#000')
opacity: 0-1
},
threshold: 0-1 for the percentage value at which to hide labels (if they're too small)
},
combine: {
threshold: 0-1 for the percentage value at which to combine slices (if they're too small)
color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#CCC'), if null, the plugin will automatically use the color of the first slice to be combined
label: any text value of what the combined slice should be labeled
}
highlight: {
opacity: 0-1
}
}
}
More detail and specific examples can be found in the included HTML file.
*/
(function ($)
{
function init(plot) // this is the "body" of the plugin
{
var canvas = null;
var target = null;
var maxRadius = null;
var centerLeft = null;
var centerTop = null;
var total = 0;
var redraw = true;
var redrawAttempts = 10;
var shrink = 0.95;
var legendWidth = 0;
var processed = false;
var raw = false;
// interactive variables
var highlights = [];
// add hook to determine if pie plugin in enabled, and then perform necessary operations
plot.hooks.processOptions.push(checkPieEnabled);
plot.hooks.bindEvents.push(bindEvents);
// check to see if the pie plugin is enabled
function checkPieEnabled(plot, options)
{
if (options.series.pie.show)
{
//disable grid
options.grid.show = false;
// set labels.show
if (options.series.pie.label.show=='auto')
if (options.legend.show)
options.series.pie.label.show = false;
else
options.series.pie.label.show = true;
// set radius
if (options.series.pie.radius=='auto')
if (options.series.pie.label.show)
options.series.pie.radius = 3/4;
else
options.series.pie.radius = 1;
// ensure sane tilt
if (options.series.pie.tilt>1)
options.series.pie.tilt=1;
if (options.series.pie.tilt<0)
options.series.pie.tilt=0;
// add processData hook to do transformations on the data
plot.hooks.processDatapoints.push(processDatapoints);
plot.hooks.drawOverlay.push(drawOverlay);
// add draw hook
plot.hooks.draw.push(draw);
}
}
// bind hoverable events
function bindEvents(plot, eventHolder)
{
var options = plot.getOptions();
if (options.series.pie.show && options.grid.hoverable)
eventHolder.unbind('mousemove').mousemove(onMouseMove);
if (options.series.pie.show && options.grid.clickable)
eventHolder.unbind('click').click(onClick);
}
// debugging function that prints out an object
function alertObject(obj)
{
var msg = '';
function traverse(obj, depth)
{
if (!depth)
depth = 0;
for (var i = 0; i < obj.length; ++i)
{
for (var j=0; j<depth; j++)
msg += '\t';
if( typeof obj[i] == "object")
{ // its an object
msg += ''+i+':\n';
traverse(obj[i], depth+1);
}
else
{ // its a value
msg += ''+i+': '+obj[i]+'\n';
}
}
}
traverse(obj);
alert(msg);
}
function calcTotal(data)
{
for (var i = 0; i < data.length; ++i)
{
var item = parseFloat(data[i].data[0][1]);
if (item)
total += item;
}
}
function processDatapoints(plot, series, data, datapoints)
{
if (!processed)
{
processed = true;
canvas = plot.getCanvas();
target = $(canvas).parent();
options = plot.getOptions();
plot.setData(combine(plot.getData()));
}
}
function setupPie()
{
legendWidth = target.children().filter('.legend').children().width();
// calculate maximum radius and center point
maxRadius = Math.min(canvas.width,(canvas.height/options.series.pie.tilt))/2;
centerTop = (canvas.height/2)+options.series.pie.offset.top;
centerLeft = (canvas.width/2);
if (options.series.pie.offset.left=='auto')
if (options.legend.position.match('w'))
centerLeft += legendWidth/2;
else
centerLeft -= legendWidth/2;
else
centerLeft += options.series.pie.offset.left;
if (centerLeft<maxRadius)
centerLeft = maxRadius;
else if (centerLeft>canvas.width-maxRadius)
centerLeft = canvas.width-maxRadius;
}
function fixData(data)
{
for (var i = 0; i < data.length; ++i)
{
if (typeof(data[i].data)=='number')
data[i].data = [[1,data[i].data]];
else if (typeof(data[i].data)=='undefined' || typeof(data[i].data[0])=='undefined')
{
if (typeof(data[i].data)!='undefined' && typeof(data[i].data.label)!='undefined')
data[i].label = data[i].data.label; // fix weirdness coming from flot
data[i].data = [[1,0]];
}
}
return data;
}
function combine(data)
{
data = fixData(data);
calcTotal(data);
var combined = 0;
var numCombined = 0;
var color = options.series.pie.combine.color;
var newdata = [];
for (var i = 0; i < data.length; ++i)
{
// make sure its a number
data[i].data[0][1] = parseFloat(data[i].data[0][1]);
if (!data[i].data[0][1])
data[i].data[0][1] = 0;
if (data[i].data[0][1]/total<=options.series.pie.combine.threshold)
{
combined += data[i].data[0][1];
numCombined++;
if (!color)
color = data[i].color;
}
else
{
newdata.push({
data: [[1,data[i].data[0][1]]],
color: data[i].color,
label: data[i].label,
angle: (data[i].data[0][1]*(Math.PI*2))/total,
percent: (data[i].data[0][1]/total*100)
});
}
}
if (numCombined>0)
newdata.push({
data: [[1,combined]],
color: color,
label: options.series.pie.combine.label,
angle: (combined*(Math.PI*2))/total,
percent: (combined/total*100)
});
return newdata;
}
function draw(plot, newCtx)
{
if (!target) return; // if no series were passed
ctx = newCtx;
setupPie();
var slices = plot.getData();
var attempts = 0;
while (redraw && attempts<redrawAttempts)
{
redraw = false;
if (attempts>0)
maxRadius *= shrink;
attempts += 1;
clear();
if (options.series.pie.tilt<=0.8)
drawShadow();
drawPie();
}
if (attempts >= redrawAttempts) {
clear();
target.prepend('<div class="error">Could not draw pie with labels contained inside canvas</div>');
}
if ( plot.setSeries && plot.insertLegend )
{
plot.setSeries(slices);
plot.insertLegend();
}
// we're actually done at this point, just defining internal functions at this point
function clear()
{
ctx.clearRect(0,0,canvas.width,canvas.height);
target.children().filter('.pieLabel, .pieLabelBackground').remove();
}
function drawShadow()
{
var shadowLeft = 5;
var shadowTop = 15;
var edge = 10;
var alpha = 0.02;
// set radius
if (options.series.pie.radius>1)
var radius = options.series.pie.radius;
else
var radius = maxRadius * options.series.pie.radius;
if (radius>=(canvas.width/2)-shadowLeft || radius*options.series.pie.tilt>=(canvas.height/2)-shadowTop || radius<=edge)
return; // shadow would be outside canvas, so don't draw it
ctx.save();
ctx.translate(shadowLeft,shadowTop);
ctx.globalAlpha = alpha;
ctx.fillStyle = '#000';
// center and rotate to starting position
ctx.translate(centerLeft,centerTop);
ctx.scale(1, options.series.pie.tilt);
//radius -= edge;
for (var i=1; i<=edge; i++)
{
ctx.beginPath();
ctx.arc(0,0,radius,0,Math.PI*2,false);
ctx.fill();
radius -= i;
}
ctx.restore();
}
function drawPie()
{
startAngle = Math.PI*options.series.pie.startAngle;
// set radius
if (options.series.pie.radius>1)
var radius = options.series.pie.radius;
else
var radius = maxRadius * options.series.pie.radius;
// center and rotate to starting position
ctx.save();
ctx.translate(centerLeft,centerTop);
ctx.scale(1, options.series.pie.tilt);
//ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera
// draw slices
ctx.save();
var currentAngle = startAngle;
for (var i = 0; i < slices.length; ++i)
{
slices[i].startAngle = currentAngle;
drawSlice(slices[i].angle, slices[i].color, true);
}
ctx.restore();
// draw slice outlines
ctx.save();
ctx.lineWidth = options.series.pie.stroke.width;
currentAngle = startAngle;
for (var i = 0; i < slices.length; ++i)
drawSlice(slices[i].angle, options.series.pie.stroke.color, false);
ctx.restore();
// draw donut hole
drawDonutHole(ctx);
// draw labels
if (options.series.pie.label.show)
drawLabels();
// restore to original state
ctx.restore();
function drawSlice(angle, color, fill)
{
if (angle<=0)
return;
if (fill)
ctx.fillStyle = color;
else
{
ctx.strokeStyle = color;
ctx.lineJoin = 'round';
}
ctx.beginPath();
if (Math.abs(angle - Math.PI*2) > 0.000000001)
ctx.moveTo(0,0); // Center of the pie
else if ($.browser.msie)
angle -= 0.0001;
//ctx.arc(0,0,radius,0,angle,false); // This doesn't work properly in Opera
ctx.arc(0,0,radius,currentAngle,currentAngle+angle,false);
ctx.closePath();
//ctx.rotate(angle); // This doesn't work properly in Opera
currentAngle += angle;
if (fill)
ctx.fill();
else
ctx.stroke();
}
function drawLabels()
{
var currentAngle = startAngle;
// set radius
if (options.series.pie.label.radius>1)
var radius = options.series.pie.label.radius;
else
var radius = maxRadius * options.series.pie.label.radius;
for (var i = 0; i < slices.length; ++i)
{
if (slices[i].percent >= options.series.pie.label.threshold*100)
drawLabel(slices[i], currentAngle, i);
currentAngle += slices[i].angle;
}
function drawLabel(slice, startAngle, index)
{
if (slice.data[0][1]==0)
return;
// format label text
var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter;
if (lf)
text = lf(slice.label, slice);
else
text = slice.label;
if (plf)
text = plf(text, slice);
var halfAngle = ((startAngle+slice.angle) + startAngle)/2;
var x = centerLeft + Math.round(Math.cos(halfAngle) * radius);
var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt;
var html = '<span class="pieLabel" id="pieLabel'+index+'" style="position:absolute;top:' + y + 'px;left:' + x + 'px;">' + text + "</span>";
target.append(html);
var label = target.children('#pieLabel'+index);
var labelTop = (y - label.height()/2);
var labelLeft = (x - label.width()/2);
label.css('top', labelTop);
label.css('left', labelLeft);
// check to make sure that the label is not outside the canvas
if (0-labelTop>0 || 0-labelLeft>0 || canvas.height-(labelTop+label.height())<0 || canvas.width-(labelLeft+label.width())<0)
redraw = true;
if (options.series.pie.label.background.opacity != 0) {
// put in the transparent background separately to avoid blended labels and label boxes
var c = options.series.pie.label.background.color;
if (c == null) {
c = slice.color;
}
var pos = 'top:'+labelTop+'px;left:'+labelLeft+'px;';
$('<div class="pieLabelBackground" style="position:absolute;width:' + label.width() + 'px;height:' + label.height() + 'px;' + pos +'background-color:' + c + ';"> </div>').insertBefore(label).css('opacity', options.series.pie.label.background.opacity);
}
} // end individual label function
} // end drawLabels function
} // end drawPie function
} // end draw function
// Placed here because it needs to be accessed from multiple locations
function drawDonutHole(layer)
{
// draw donut hole
if(options.series.pie.innerRadius > 0)
{
// subtract the center
layer.save();
innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius;
layer.globalCompositeOperation = 'destination-out'; // this does not work with excanvas, but it will fall back to using the stroke color
layer.beginPath();
layer.fillStyle = options.series.pie.stroke.color;
layer.arc(0,0,innerRadius,0,Math.PI*2,false);
layer.fill();
layer.closePath();
layer.restore();
// add inner stroke
layer.save();
layer.beginPath();
layer.strokeStyle = options.series.pie.stroke.color;
layer.arc(0,0,innerRadius,0,Math.PI*2,false);
layer.stroke();
layer.closePath();
layer.restore();
// TODO: add extra shadow inside hole (with a mask) if the pie is tilted.
}
}
//-- Additional Interactive related functions --
function isPointInPoly(poly, pt)
{
for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1]))
&& (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0])
&& (c = !c);
return c;
}
function findNearbySlice(mouseX, mouseY)
{
var slices = plot.getData(),
options = plot.getOptions(),
radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
for (var i = 0; i < slices.length; ++i)
{
var s = slices[i];
if(s.pie.show)
{
ctx.save();
ctx.beginPath();
ctx.moveTo(0,0); // Center of the pie
//ctx.scale(1, options.series.pie.tilt); // this actually seems to break everything when here.
ctx.arc(0,0,radius,s.startAngle,s.startAngle+s.angle,false);
ctx.closePath();
x = mouseX-centerLeft;
y = mouseY-centerTop;
if(ctx.isPointInPath)
{
if (ctx.isPointInPath(mouseX-centerLeft, mouseY-centerTop))
{
//alert('found slice!');
ctx.restore();
return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i};
}
}
else
{
// excanvas for IE doesn;t support isPointInPath, this is a workaround.
p1X = (radius * Math.cos(s.startAngle));
p1Y = (radius * Math.sin(s.startAngle));
p2X = (radius * Math.cos(s.startAngle+(s.angle/4)));
p2Y = (radius * Math.sin(s.startAngle+(s.angle/4)));
p3X = (radius * Math.cos(s.startAngle+(s.angle/2)));
p3Y = (radius * Math.sin(s.startAngle+(s.angle/2)));
p4X = (radius * Math.cos(s.startAngle+(s.angle/1.5)));
p4Y = (radius * Math.sin(s.startAngle+(s.angle/1.5)));
p5X = (radius * Math.cos(s.startAngle+s.angle));
p5Y = (radius * Math.sin(s.startAngle+s.angle));
arrPoly = [[0,0],[p1X,p1Y],[p2X,p2Y],[p3X,p3Y],[p4X,p4Y],[p5X,p5Y]];
arrPoint = [x,y];
// TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt?
if(isPointInPoly(arrPoly, arrPoint))
{
ctx.restore();
return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i};
}
}
ctx.restore();
}
}
return null;
}
function onMouseMove(e)
{
triggerClickHoverEvent('plothover', e);
}
function onClick(e)
{
triggerClickHoverEvent('plotclick', e);
}
// trigger click or hover event (they send the same parameters so we share their code)
function triggerClickHoverEvent(eventname, e)
{
var offset = plot.offset(),
canvasX = parseInt(e.pageX - offset.left),
canvasY = parseInt(e.pageY - offset.top),
item = findNearbySlice(canvasX, canvasY);
if (options.grid.autoHighlight)
{
// clear auto-highlights
for (var i = 0; i < highlights.length; ++i)
{
var h = highlights[i];
if (h.auto == eventname && !(item && h.series == item.series))
unhighlight(h.series);
}
}
// highlight the slice
if (item)
highlight(item.series, eventname);
// trigger any hover bind events
var pos = { pageX: e.pageX, pageY: e.pageY };
target.trigger(eventname, [ pos, item ]);
}
function highlight(s, auto)
{
if (typeof s == "number")
s = series[s];
var i = indexOfHighlight(s);
if (i == -1)
{
highlights.push({ series: s, auto: auto });
plot.triggerRedrawOverlay();
}
else if (!auto)
highlights[i].auto = false;
}
function unhighlight(s)
{
if (s == null)
{
highlights = [];
plot.triggerRedrawOverlay();
}
if (typeof s == "number")
s = series[s];
var i = indexOfHighlight(s);
if (i != -1)
{
highlights.splice(i, 1);
plot.triggerRedrawOverlay();
}
}
function indexOfHighlight(s)
{
for (var i = 0; i < highlights.length; ++i)
{
var h = highlights[i];
if (h.series == s)
return i;
}
return -1;
}
function drawOverlay(plot, octx)
{
//alert(options.series.pie.radius);
var options = plot.getOptions();
//alert(options.series.pie.radius);
var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
octx.save();
octx.translate(centerLeft, centerTop);
octx.scale(1, options.series.pie.tilt);
for (i = 0; i < highlights.length; ++i)
drawHighlight(highlights[i].series);
drawDonutHole(octx);
octx.restore();
function drawHighlight(series)
{
if (series.angle < 0) return;
//octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString();
octx.fillStyle = "rgba(255, 255, 255, "+options.series.pie.highlight.opacity+")"; // this is temporary until we have access to parseColor
octx.beginPath();
if (Math.abs(series.angle - Math.PI*2) > 0.000000001)
octx.moveTo(0,0); // Center of the pie
octx.arc(0,0,radius,series.startAngle,series.startAngle+series.angle,false);
octx.closePath();
octx.fill();
}
}
} // end init (plugin body)
// define pie specific options and their default values
var options = {
series: {
pie: {
show: false,
radius: 'auto', // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value)
innerRadius:0, /* for donut */
startAngle: 3/2,
tilt: 1,
offset: {
top: 0,
left: 'auto'
},
stroke: {
color: '#FFF',
width: 1
},
label: {
show: 'auto',
formatter: function(label, slice){
return '<div style="font-size:x-small;text-align:center;padding:2px;color:'+slice.color+';">'+label+'<br/>'+Math.round(slice.percent)+'%</div>';
}, // formatter function
radius: 1, // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value)
background: {
color: null,
opacity: 0
},
threshold: 0 // percentage at which to hide the label (i.e. the slice is too narrow)
},
combine: {
threshold: -1, // percentage at which to combine little slices into one larger slice
color: null, // color to give the new slice (auto-generated if null)
label: 'Other' // label to give the new slice
},
highlight: {
//color: '#FFF', // will add this functionality once parseColor is available
opacity: 0.5
}
}
}
};
$.plot.plugins.push({
init: init,
options: options,
name: "pie",
version: "1.0"
});
})(jQuery);

File diff suppressed because one or more lines are too long

@ -1,60 +0,0 @@
/*
Flot plugin for automatically redrawing plots when the placeholder
size changes, e.g. on window resizes.
It works by listening for changes on the placeholder div (through the
jQuery resize event plugin) - if the size changes, it will redraw the
plot.
There are no options. If you need to disable the plugin for some
plots, you can just fix the size of their placeholders.
*/
/* Inline dependency:
* jQuery resize event - v1.1 - 3/14/2010
* http://benalman.com/projects/jquery-resize-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($,h,c){var a=$([]),e=$.resize=$.extend($.resize,{}),i,k="setTimeout",j="resize",d=j+"-special-event",b="delay",f="throttleWindow";e[b]=250;e[f]=true;$.event.special[j]={setup:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.add(l);$.data(this,d,{w:l.width(),h:l.height()});if(a.length===1){g()}},teardown:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.not(l);l.removeData(d);if(!a.length){clearTimeout(i)}},add:function(l){if(!e[f]&&this[k]){return false}var n;function m(s,o,p){var q=$(this),r=$.data(this,d);r.w=o!==c?o:q.width();r.h=p!==c?p:q.height();n.apply(this,arguments)}if($.isFunction(l)){n=l;return m}else{n=l.handler;l.handler=m}}};function g(){i=h[k](function(){a.each(function(){var n=$(this),m=n.width(),l=n.height(),o=$.data(this,d);if(m!==o.w||l!==o.h){n.trigger(j,[o.w=m,o.h=l])}});g()},e[b])}})(jQuery,this);
(function ($) {
var options = { }; // no options
function init(plot) {
function onResize() {
var placeholder = plot.getPlaceholder();
// somebody might have hidden us and we can't plot
// when we don't have the dimensions
if (placeholder.width() == 0 || placeholder.height() == 0)
return;
plot.resize();
plot.setupGrid();
plot.draw();
}
function bindEvents(plot, eventHolder) {
plot.getPlaceholder().resize(onResize);
}
function shutdown(plot, eventHolder) {
plot.getPlaceholder().unbind("resize", onResize);
}
plot.hooks.bindEvents.push(bindEvents);
plot.hooks.shutdown.push(shutdown);
}
$.plot.plugins.push({
init: init,
options: options,
name: 'resize',
version: '1.0'
});
})(jQuery);

@ -1 +0,0 @@
(function(n,p,u){var w=n([]),s=n.resize=n.extend(n.resize,{}),o,l="setTimeout",m="resize",t=m+"-special-event",v="delay",r="throttleWindow";s[v]=250;s[r]=true;n.event.special[m]={setup:function(){if(!s[r]&&this[l]){return false}var a=n(this);w=w.add(a);n.data(this,t,{w:a.width(),h:a.height()});if(w.length===1){q()}},teardown:function(){if(!s[r]&&this[l]){return false}var a=n(this);w=w.not(a);a.removeData(t);if(!w.length){clearTimeout(o)}},add:function(b){if(!s[r]&&this[l]){return false}var c;function a(d,h,g){var f=n(this),e=n.data(this,t);e.w=h!==u?h:f.width();e.h=g!==u?g:f.height();c.apply(this,arguments)}if(n.isFunction(b)){c=b;return a}else{c=b.handler;b.handler=a}}};function q(){o=p[l](function(){w.each(function(){var d=n(this),a=d.width(),b=d.height(),c=n.data(this,t);if(a!==c.w||b!==c.h){d.trigger(m,[c.w=a,c.h=b])}});q()},s[v])}})(jQuery,this);(function(b){var a={};function c(f){function e(){var h=f.getPlaceholder();if(h.width()==0||h.height()==0){return}f.resize();f.setupGrid();f.draw()}function g(i,h){i.getPlaceholder().resize(e)}function d(i,h){i.getPlaceholder().unbind("resize",e)}f.hooks.bindEvents.push(g);f.hooks.shutdown.push(d)}b.plot.plugins.push({init:c,options:a,name:"resize",version:"1.0"})})(jQuery);

@ -8,22 +8,20 @@ The plugin defines the following options:
color: color
}
Selection support is enabled by setting the mode to one of "x", "y" or
You enable selection support 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).
where both ranges can be specified. "color" is color of the selection.
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,
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 one extra 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, ...
// similar for yaxis, secondary axes are in x2axis
// and y2axis if present
});
The "plotselected" event is only fired when the user has finished
@ -39,19 +37,17 @@ 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:
form as returned in the "plotselected" event. If the selection
mode is "x", you should put in either an xaxis (or x2axis) object,
if the mode is "y" you need to put in an yaxis (or y2axis) object
and both xaxis/x2axis and yaxis/y2axis 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.
"plotselected" handler, pass true as the second parameter.
- clearSelection(preventEvent)
@ -81,13 +77,11 @@ The plugin allso adds the following methods to the plot object:
// make this plugin much slimmer.
var savedhandlers = {};
var mouseUpHandler = null;
function onMouseMove(e) {
if (selection.active) {
updateSelection(e);
plot.getPlaceholder().trigger("plotselecting", [ getSelection() ]);
updateSelection(e);
}
}
@ -111,24 +105,18 @@ The plugin allso adds the following methods to the plot object:
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);
$(document).one("mouseup", onMouseUp);
}
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
// no more draggy-dee-drag
selection.active = false;
updateSelection(e);
@ -147,13 +135,21 @@ The plugin allso adds the following methods to the plot object:
if (!selectionIsSane())
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) };
}
});
var x1 = Math.min(selection.first.x, selection.second.x),
x2 = Math.max(selection.first.x, selection.second.x),
y1 = Math.max(selection.first.y, selection.second.y),
y2 = Math.min(selection.first.y, selection.second.y);
var r = {};
var axes = plot.getAxes();
if (axes.xaxis.used)
r.xaxis = { from: axes.xaxis.c2p(x1), to: axes.xaxis.c2p(x2) };
if (axes.x2axis.used)
r.x2axis = { from: axes.x2axis.c2p(x1), to: axes.x2axis.c2p(x2) };
if (axes.yaxis.used)
r.yaxis = { from: axes.yaxis.c2p(y1), to: axes.yaxis.c2p(y2) };
if (axes.y2axis.used)
r.y2axis = { from: axes.y2axis.c2p(y1), to: axes.y2axis.c2p(y2) };
return r;
}
@ -163,12 +159,13 @@ The plugin allso adds the following methods to the plot object:
plot.getPlaceholder().trigger("plotselected", [ r ]);
// backwards-compat stuff, to be removed in future
if (r.xaxis && r.yaxis)
var axes = plot.getAxes();
if (axes.xaxis.used && axes.yaxis.used)
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);
return value < min? min: (value > max? max: value);
}
function setSelectionPos(pos, e) {
@ -179,10 +176,10 @@ The plugin allso adds the following methods to the plot object:
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();
pos.x = pos == selection.first? 0: plot.width();
if (o.selection.mode == "x")
pos.y = pos == selection.first ? 0 : plot.height();
pos.y = pos == selection.first? 0: plot.height();
}
function updateSelection(pos) {
@ -207,53 +204,19 @@ The plugin allso adds the following methods to the plot object:
}
}
// 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();
var axis, range, axes = plot.getAxes();
var 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);
axis = ranges["xaxis"]? axes["xaxis"]: (ranges["x2axis"]? axes["x2axis"]: axes["xaxis"]);
range = ranges["xaxis"] || ranges["x2axis"] || { from:ranges["x1"], to:ranges["x2"] }
selection.first.x = axis.p2c(Math.min(range.from, range.to));
selection.second.x = axis.p2c(Math.max(range.from, range.to));
}
if (o.selection.mode == "x") {
@ -261,15 +224,15 @@ The plugin allso adds the following methods to the plot object:
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);
axis = ranges["yaxis"]? axes["yaxis"]: (ranges["y2axis"]? axes["y2axis"]: axes["yaxis"]);
range = ranges["yaxis"] || ranges["y2axis"] || { from:ranges["y1"], to:ranges["y2"] }
selection.first.y = axis.p2c(Math.min(range.from, range.to));
selection.second.y = axis.p2c(Math.max(range.from, range.to));
}
selection.show = true;
plot.triggerRedrawOverlay();
if (!preventEvent && selectionIsSane())
if (!preventEvent)
triggerSelectedEvent();
}
@ -285,10 +248,11 @@ The plugin allso adds the following methods to the plot object:
plot.hooks.bindEvents.push(function(plot, eventHolder) {
var o = plot.getOptions();
if (o.selection.mode != null) {
if (o.selection.mode != null)
eventHolder.mousemove(onMouseMove);
if (o.selection.mode != null)
eventHolder.mousedown(onMouseDown);
}
});
@ -319,15 +283,6 @@ The plugin allso adds the following methods to the plot object:
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({
@ -339,6 +294,6 @@ The plugin allso adds the following methods to the plot object:
}
},
name: 'selection',
version: '1.1'
version: '1.0'
});
})(jQuery);

@ -1 +1 @@
(function(a){function b(k){var p={first:{x:-1,y:-1},second:{x:-1,y:-1},show:false,active:false};var m={};var r=null;function e(s){if(p.active){l(s);k.getPlaceholder().trigger("plotselecting",[g()])}}function n(s){if(s.which!=1){return}document.body.focus();if(document.onselectstart!==undefined&&m.onselectstart==null){m.onselectstart=document.onselectstart;document.onselectstart=function(){return false}}if(document.ondrag!==undefined&&m.ondrag==null){m.ondrag=document.ondrag;document.ondrag=function(){return false}}d(p.first,s);p.active=true;r=function(t){j(t)};a(document).one("mouseup",r)}function j(s){r=null;if(document.onselectstart!==undefined){document.onselectstart=m.onselectstart}if(document.ondrag!==undefined){document.ondrag=m.ondrag}p.active=false;l(s);if(f()){i()}else{k.getPlaceholder().trigger("plotunselected",[]);k.getPlaceholder().trigger("plotselecting",[null])}return false}function g(){if(!f()){return null}var u={},t=p.first,s=p.second;a.each(k.getAxes(),function(v,w){if(w.used){var y=w.c2p(t[w.direction]),x=w.c2p(s[w.direction]);u[v]={from:Math.min(y,x),to:Math.max(y,x)}}});return u}function i(){var s=g();k.getPlaceholder().trigger("plotselected",[s]);if(s.xaxis&&s.yaxis){k.getPlaceholder().trigger("selected",[{x1:s.xaxis.from,y1:s.yaxis.from,x2:s.xaxis.to,y2:s.yaxis.to}])}}function h(t,u,s){return u<t?t:(u>s?s:u)}function d(w,t){var v=k.getOptions();var u=k.getPlaceholder().offset();var s=k.getPlotOffset();w.x=h(0,t.pageX-u.left-s.left,k.width());w.y=h(0,t.pageY-u.top-s.top,k.height());if(v.selection.mode=="y"){w.x=w==p.first?0:k.width()}if(v.selection.mode=="x"){w.y=w==p.first?0:k.height()}}function l(s){if(s.pageX==null){return}d(p.second,s);if(f()){p.show=true;k.triggerRedrawOverlay()}else{q(true)}}function q(s){if(p.show){p.show=false;k.triggerRedrawOverlay();if(!s){k.getPlaceholder().trigger("plotunselected",[])}}}function c(s,w){var t,y,z,A,x=k.getAxes();for(var u in x){t=x[u];if(t.direction==w){A=w+t.n+"axis";if(!s[A]&&t.n==1){A=w+"axis"}if(s[A]){y=s[A].from;z=s[A].to;break}}}if(!s[A]){t=w=="x"?k.getXAxes()[0]:k.getYAxes()[0];y=s[w+"1"];z=s[w+"2"]}if(y!=null&&z!=null&&y>z){var v=y;y=z;z=v}return{from:y,to:z,axis:t}}function o(t,s){var v,u,w=k.getOptions();if(w.selection.mode=="y"){p.first.x=0;p.second.x=k.width()}else{u=c(t,"x");p.first.x=u.axis.p2c(u.from);p.second.x=u.axis.p2c(u.to)}if(w.selection.mode=="x"){p.first.y=0;p.second.y=k.height()}else{u=c(t,"y");p.first.y=u.axis.p2c(u.from);p.second.y=u.axis.p2c(u.to)}p.show=true;k.triggerRedrawOverlay();if(!s&&f()){i()}}function f(){var s=5;return Math.abs(p.second.x-p.first.x)>=s&&Math.abs(p.second.y-p.first.y)>=s}k.clearSelection=q;k.setSelection=o;k.getSelection=g;k.hooks.bindEvents.push(function(t,s){var u=t.getOptions();if(u.selection.mode!=null){s.mousemove(e);s.mousedown(n)}});k.hooks.drawOverlay.push(function(v,D){if(p.show&&f()){var t=v.getPlotOffset();var s=v.getOptions();D.save();D.translate(t.left,t.top);var z=a.color.parse(s.selection.color);D.strokeStyle=z.scale("a",0.8).toString();D.lineWidth=1;D.lineJoin="round";D.fillStyle=z.scale("a",0.4).toString();var B=Math.min(p.first.x,p.second.x),A=Math.min(p.first.y,p.second.y),C=Math.abs(p.second.x-p.first.x),u=Math.abs(p.second.y-p.first.y);D.fillRect(B,A,C,u);D.strokeRect(B,A,C,u);D.restore()}});k.hooks.shutdown.push(function(t,s){s.unbind("mousemove",e);s.unbind("mousedown",n);if(r){a(document).unbind("mouseup",r)}})}a.plot.plugins.push({init:b,options:{selection:{mode:null,color:"#e8cfac"}},name:"selection",version:"1.1"})})(jQuery);
(function(A){function B(J){var O={first:{x:-1,y:-1},second:{x:-1,y:-1},show:false,active:false};var L={};function D(Q){if(O.active){J.getPlaceholder().trigger("plotselecting",[F()]);K(Q)}}function M(Q){if(Q.which!=1){return }document.body.focus();if(document.onselectstart!==undefined&&L.onselectstart==null){L.onselectstart=document.onselectstart;document.onselectstart=function(){return false}}if(document.ondrag!==undefined&&L.ondrag==null){L.ondrag=document.ondrag;document.ondrag=function(){return false}}C(O.first,Q);O.active=true;A(document).one("mouseup",I)}function I(Q){if(document.onselectstart!==undefined){document.onselectstart=L.onselectstart}if(document.ondrag!==undefined){document.ondrag=L.ondrag}O.active=false;K(Q);if(E()){H()}else{J.getPlaceholder().trigger("plotunselected",[]);J.getPlaceholder().trigger("plotselecting",[null])}return false}function F(){if(!E()){return null}var R=Math.min(O.first.x,O.second.x),Q=Math.max(O.first.x,O.second.x),T=Math.max(O.first.y,O.second.y),S=Math.min(O.first.y,O.second.y);var U={};var V=J.getAxes();if(V.xaxis.used){U.xaxis={from:V.xaxis.c2p(R),to:V.xaxis.c2p(Q)}}if(V.x2axis.used){U.x2axis={from:V.x2axis.c2p(R),to:V.x2axis.c2p(Q)}}if(V.yaxis.used){U.yaxis={from:V.yaxis.c2p(T),to:V.yaxis.c2p(S)}}if(V.y2axis.used){U.y2axis={from:V.y2axis.c2p(T),to:V.y2axis.c2p(S)}}return U}function H(){var Q=F();J.getPlaceholder().trigger("plotselected",[Q]);var R=J.getAxes();if(R.xaxis.used&&R.yaxis.used){J.getPlaceholder().trigger("selected",[{x1:Q.xaxis.from,y1:Q.yaxis.from,x2:Q.xaxis.to,y2:Q.yaxis.to}])}}function G(R,S,Q){return S<R?R:(S>Q?Q:S)}function C(U,R){var T=J.getOptions();var S=J.getPlaceholder().offset();var Q=J.getPlotOffset();U.x=G(0,R.pageX-S.left-Q.left,J.width());U.y=G(0,R.pageY-S.top-Q.top,J.height());if(T.selection.mode=="y"){U.x=U==O.first?0:J.width()}if(T.selection.mode=="x"){U.y=U==O.first?0:J.height()}}function K(Q){if(Q.pageX==null){return }C(O.second,Q);if(E()){O.show=true;J.triggerRedrawOverlay()}else{P(true)}}function P(Q){if(O.show){O.show=false;J.triggerRedrawOverlay();if(!Q){J.getPlaceholder().trigger("plotunselected",[])}}}function N(R,Q){var T,S,U=J.getAxes();var V=J.getOptions();if(V.selection.mode=="y"){O.first.x=0;O.second.x=J.width()}else{T=R.xaxis?U.xaxis:(R.x2axis?U.x2axis:U.xaxis);S=R.xaxis||R.x2axis||{from:R.x1,to:R.x2};O.first.x=T.p2c(Math.min(S.from,S.to));O.second.x=T.p2c(Math.max(S.from,S.to))}if(V.selection.mode=="x"){O.first.y=0;O.second.y=J.height()}else{T=R.yaxis?U.yaxis:(R.y2axis?U.y2axis:U.yaxis);S=R.yaxis||R.y2axis||{from:R.y1,to:R.y2};O.first.y=T.p2c(Math.min(S.from,S.to));O.second.y=T.p2c(Math.max(S.from,S.to))}O.show=true;J.triggerRedrawOverlay();if(!Q){H()}}function E(){var Q=5;return Math.abs(O.second.x-O.first.x)>=Q&&Math.abs(O.second.y-O.first.y)>=Q}J.clearSelection=P;J.setSelection=N;J.getSelection=F;J.hooks.bindEvents.push(function(R,Q){var S=R.getOptions();if(S.selection.mode!=null){Q.mousemove(D)}if(S.selection.mode!=null){Q.mousedown(M)}});J.hooks.drawOverlay.push(function(T,Y){if(O.show&&E()){var R=T.getPlotOffset();var Q=T.getOptions();Y.save();Y.translate(R.left,R.top);var U=A.color.parse(Q.selection.color);Y.strokeStyle=U.scale("a",0.8).toString();Y.lineWidth=1;Y.lineJoin="round";Y.fillStyle=U.scale("a",0.4).toString();var W=Math.min(O.first.x,O.second.x),V=Math.min(O.first.y,O.second.y),X=Math.abs(O.second.x-O.first.x),S=Math.abs(O.second.y-O.first.y);Y.fillRect(W,V,X,S);Y.strokeRect(W,V,X,S);Y.restore()}})}A.plot.plugins.push({init:B,options:{selection:{mode:null,color:"#e8cfac"}},name:"selection",version:"1.0"})})(jQuery);

@ -1,14 +1,8 @@
/*
Flot plugin for stacking data sets, i.e. putting them on top of each
other, for accumulative graphs.
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).
other, for accumulative graphs. Note that the plugin assumes the data
is sorted on x. Also 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
@ -20,15 +14,15 @@ specify the default stack, you can set
or specify it for a specific series
$.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).
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).
inserted through interpolation. For bar charts, the second y value is
also adjusted.
*/
(function ($) {
@ -57,20 +51,15 @@ adjusted (e.g for bar charts or filled areas).
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),
withlines = s.lines.show, withbars = s.bars.show,
withsteps = withlines && s.lines.steps,
fromgap = true,
keyOffset = horizontal ? 1 : 0,
accumulateOffset = horizontal ? 0 : 1,
i = 0, j = 0, l;
while (true) {
@ -79,40 +68,27 @@ adjusted (e.g for bar charts or filled areas).
l = newpoints.length;
if (points[i] == null) {
// copy gaps
if (j >= otherpoints.length
|| otherpoints[j] == null
|| points[i] == null) {
// degenerate cases
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];
px = points[i];
py = points[i + 1];
qx = otherpoints[j];
qy = otherpoints[j + 1];
bottom = 0;
if (px == qx) {
for (m = 0; m < ps; ++m)
newpoints.push(points[i + m]);
newpoints[l + accumulateOffset] += qy;
newpoints[l + 1] += qy;
bottom = qy;
i += ps;
@ -122,9 +98,9 @@ adjusted (e.g for bar charts or filled areas).
// 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);
intery = py + (points[i - ps + 1] - py) * (qx - px) / (points[i - ps] - px);
newpoints.push(qx);
newpoints.push(intery + qy);
newpoints.push(intery + qy)
for (m = 2; m < ps; ++m)
newpoints.push(points[i + m]);
bottom = qy;
@ -132,29 +108,21 @@ adjusted (e.g for bar charts or filled areas).
j += otherps;
}
else { // px < qx
if (fromgap && withlines) {
// if we come from a gap, we just skip this point
i += ps;
continue;
}
else {
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);
if (withlines && j > 0 && otherpoints[j - ps] != null)
bottom = qy + (otherpoints[j - ps + 1] - qy) * (px - qx) / (otherpoints[j - ps] - qx);
newpoints[l + accumulateOffset] += bottom;
newpoints[l + 1] += bottom;
i += ps;
}
fromgap = false;
if (l != newpoints.length && withbottom)
if (l != newpoints.length && withbars)
newpoints[l + 2] += bottom;
}
@ -168,7 +136,7 @@ adjusted (e.g for bar charts or filled areas).
newpoints[l + 1] = newpoints[l - ps + 1];
}
}
datapoints.points = newpoints;
}
@ -179,6 +147,6 @@ adjusted (e.g for bar charts or filled areas).
init: init,
options: options,
name: 'stack',
version: '1.2'
version: '1.0'
});
})(jQuery);

@ -1 +1 @@
(function(b){var a={series:{stack:null}};function c(f){function d(k,j){var h=null;for(var g=0;g<j.length;++g){if(k==j[g]){break}if(j[g].stack==k.stack){h=j[g]}}return h}function e(C,v,g){if(v.stack==null){return}var p=d(v,C.getData());if(!p){return}var z=g.pointsize,F=g.points,h=p.datapoints.pointsize,y=p.datapoints.points,t=[],x,w,k,J,I,r,u=v.lines.show,G=v.bars.horizontal,o=z>2&&(G?g.format[2].x:g.format[2].y),n=u&&v.lines.steps,E=true,q=G?1:0,H=G?0:1,D=0,B=0,A;while(true){if(D>=F.length){break}A=t.length;if(F[D]==null){for(m=0;m<z;++m){t.push(F[D+m])}D+=z}else{if(B>=y.length){if(!u){for(m=0;m<z;++m){t.push(F[D+m])}}D+=z}else{if(y[B]==null){for(m=0;m<z;++m){t.push(null)}E=true;B+=h}else{x=F[D+q];w=F[D+H];J=y[B+q];I=y[B+H];r=0;if(x==J){for(m=0;m<z;++m){t.push(F[D+m])}t[A+H]+=I;r=I;D+=z;B+=h}else{if(x>J){if(u&&D>0&&F[D-z]!=null){k=w+(F[D-z+H]-w)*(J-x)/(F[D-z+q]-x);t.push(J);t.push(k+I);for(m=2;m<z;++m){t.push(F[D+m])}r=I}B+=h}else{if(E&&u){D+=z;continue}for(m=0;m<z;++m){t.push(F[D+m])}if(u&&B>0&&y[B-h]!=null){r=I+(y[B-h+H]-I)*(x-J)/(y[B-h+q]-J)}t[A+H]+=r;D+=z}}E=false;if(A!=t.length&&o){t[A+2]+=r}}}}if(n&&A!=t.length&&A>0&&t[A]!=null&&t[A]!=t[A-z]&&t[A+1]!=t[A-z+1]){for(m=0;m<z;++m){t[A+z+m]=t[A+m]}t[A+1]=t[A-z+1]}}g.points=t}f.hooks.processDatapoints.push(e)}b.plot.plugins.push({init:c,options:a,name:"stack",version:"1.2"})})(jQuery);
(function(B){var A={series:{stack:null}};function C(F){function D(J,I){var H=null;for(var G=0;G<I.length;++G){if(J==I[G]){break}if(I[G].stack==J.stack){H=I[G]}}return H}function E(W,P,G){if(P.stack==null){return }var L=D(P,W.getData());if(!L){return }var T=G.pointsize,Y=G.points,H=L.datapoints.pointsize,S=L.datapoints.points,N=[],R,Q,I,a,Z,M,O=P.lines.show,K=P.bars.show,J=O&&P.lines.steps,X=0,V=0,U;while(true){if(X>=Y.length){break}U=N.length;if(V>=S.length||S[V]==null||Y[X]==null){for(m=0;m<T;++m){N.push(Y[X+m])}X+=T}else{R=Y[X];Q=Y[X+1];a=S[V];Z=S[V+1];M=0;if(R==a){for(m=0;m<T;++m){N.push(Y[X+m])}N[U+1]+=Z;M=Z;X+=T;V+=H}else{if(R>a){if(O&&X>0&&Y[X-T]!=null){I=Q+(Y[X-T+1]-Q)*(a-R)/(Y[X-T]-R);N.push(a);N.push(I+Z);for(m=2;m<T;++m){N.push(Y[X+m])}M=Z}V+=H}else{for(m=0;m<T;++m){N.push(Y[X+m])}if(O&&V>0&&S[V-T]!=null){M=Z+(S[V-T+1]-Z)*(R-a)/(S[V-T]-a)}N[U+1]+=M;X+=T}}if(U!=N.length&&K){N[U+2]+=M}}if(J&&U!=N.length&&U>0&&N[U]!=null&&N[U]!=N[U-T]&&N[U+1]!=N[U-T+1]){for(m=0;m<T;++m){N[U+T+m]=N[U+m]}N[U+1]=N[U-T+1]}}G.points=N}F.hooks.processDatapoints.push(E)}B.plot.plugins.push({init:C,options:A,name:"stack",version:"1.0"})})(jQuery);

@ -1,70 +0,0 @@
/*
Flot plugin that adds some extra symbols for plotting points.
The symbols are accessed as strings through the standard symbol
choice:
series: {
points: {
symbol: "square" // or "diamond", "triangle", "cross"
}
}
*/
(function ($) {
function processRawData(plot, series, datapoints) {
// we normalize the area of each symbol so it is approximately the
// same as a circle of the given radius
var handlers = {
square: function (ctx, x, y, radius, shadow) {
// pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2
var size = radius * Math.sqrt(Math.PI) / 2;
ctx.rect(x - size, y - size, size + size, size + size);
},
diamond: function (ctx, x, y, radius, shadow) {
// pi * r^2 = 2s^2 => s = r * sqrt(pi/2)
var size = radius * Math.sqrt(Math.PI / 2);
ctx.moveTo(x - size, y);
ctx.lineTo(x, y - size);
ctx.lineTo(x + size, y);
ctx.lineTo(x, y + size);
ctx.lineTo(x - size, y);
},
triangle: function (ctx, x, y, radius, shadow) {
// pi * r^2 = 1/2 * s^2 * sin (pi / 3) => s = r * sqrt(2 * pi / sin(pi / 3))
var size = radius * Math.sqrt(2 * Math.PI / Math.sin(Math.PI / 3));
var height = size * Math.sin(Math.PI / 3);
ctx.moveTo(x - size/2, y + height/2);
ctx.lineTo(x + size/2, y + height/2);
if (!shadow) {
ctx.lineTo(x, y - height/2);
ctx.lineTo(x - size/2, y + height/2);
}
},
cross: function (ctx, x, y, radius, shadow) {
// pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2
var size = radius * Math.sqrt(Math.PI) / 2;
ctx.moveTo(x - size, y - size);
ctx.lineTo(x + size, y + size);
ctx.moveTo(x - size, y + size);
ctx.lineTo(x + size, y - size);
}
}
var s = series.points.symbol;
if (handlers[s])
series.points.symbol = handlers[s];
}
function init(plot) {
plot.hooks.processDatapoints.push(processRawData);
}
$.plot.plugins.push({
init: init,
name: 'symbols',
version: '1.0'
});
})(jQuery);

@ -1 +0,0 @@
(function(b){function a(h,e,g){var d={square:function(k,j,n,i,m){var l=i*Math.sqrt(Math.PI)/2;k.rect(j-l,n-l,l+l,l+l)},diamond:function(k,j,n,i,m){var l=i*Math.sqrt(Math.PI/2);k.moveTo(j-l,n);k.lineTo(j,n-l);k.lineTo(j+l,n);k.lineTo(j,n+l);k.lineTo(j-l,n)},triangle:function(l,k,o,j,n){var m=j*Math.sqrt(2*Math.PI/Math.sin(Math.PI/3));var i=m*Math.sin(Math.PI/3);l.moveTo(k-m/2,o+i/2);l.lineTo(k+m/2,o+i/2);if(!n){l.lineTo(k,o-i/2);l.lineTo(k-m/2,o+i/2)}},cross:function(k,j,n,i,m){var l=i*Math.sqrt(Math.PI)/2;k.moveTo(j-l,n-l);k.lineTo(j+l,n+l);k.moveTo(j-l,n+l);k.lineTo(j+l,n-l)}};var f=e.points.symbol;if(d[f]){e.points.symbol=d[f]}}function c(d){d.hooks.processDatapoints.push(a)}b.plot.plugins.push({init:c,name:"symbols",version:"1.0"})})(jQuery);

@ -1,146 +0,0 @@
/*
* jquery.flot.tooltip
*
* desc: create tooltip with values of hovered point on the graph,
support many series, time mode,
you can set custom tip content (also with use of HTML tags) and precision of values
* version: 0.4.2
* author: Krzysztof Urbas @krzysu [myviews.pl]
* website: https://github.com/krzysu/flot.tooltip
*
* released under MIT License, 2011
*/
(function ($) {
var options = {
tooltip: false, //boolean
tooltipOpts: {
content: "%s | X: %x | Y: %y.2", //%s -> series label, %x -> X value, %y -> Y value, %x.2 -> precision of X value
dateFormat: "%y-%0m-%0d",
shifts: {
x: 10,
y: 20
},
defaultTheme: true
}
};
function init(plot) {
var tipPosition = {x: 0, y: 0};
var opts = plot.getOptions();
function updateTooltipPosition(pos) {
tipPosition.x = pos.x;
tipPosition.y = pos.y;
};
function onMouseMove(e) {
var pos = {x: 0, y: 0};
pos.x = e.pageX;
pos.y = e.pageY;
updateTooltipPosition(pos);
};
function timestampToDate(tmst) {
var theDate = new Date(tmst);
return $.plot.formatDate(theDate, opts.tooltipOpts.dateFormat);
};
plot.hooks.bindEvents.push(function (plot, eventHolder) {
var to = opts.tooltipOpts;
var placeholder = plot.getPlaceholder();
var $tip;
if (opts.tooltip === false) return;
if( $('#flotTip').length > 0 ){
$tip = $('#flotTip');
}
else {
$tip = $('<div />').attr('id', 'flotTip');
$tip.appendTo('body').hide().css({position: 'absolute'});
if(to.defaultTheme) {
$tip.css({
'background': '#fff',
'z-index': '100',
'padding': '0.4em 0.6em',
'border-radius': '0.5em',
'font-size': '0.8em',
'border': '1px solid #111'
});
}
}
$(placeholder).bind("plothover", function (event, pos, item) {
if (item) {
var tipText;
if(opts.xaxis.mode === "time" || opts.xaxes[0].mode === "time") {
tipText = stringFormat(to.content, item, timestampToDate);
}
else {
tipText = stringFormat(to.content, item);
}
$tip.html( tipText ).css({left: tipPosition.x + to.shifts.x, top: tipPosition.y + to.shifts.y}).show();
}
else {
$tip.hide().html('');
}
});
eventHolder.mousemove(onMouseMove);
});
function stringFormat(content, item, fnct) {
var seriesPattern = /%s/;
var xPattern = /%x\.{0,1}(\d{0,})/;
var yPattern = /%y\.{0,1}(\d{0,})/;
//series match
if( typeof(item.series.label) !== 'undefined' ) {
content = content.replace(seriesPattern, item.series.label);
}
// xVal match
if( typeof(fnct) === 'function' ) {
content = content.replace(xPattern, fnct(item.datapoint[0]) );
}
else {
content = adjustValPrecision(xPattern, content, item.datapoint[0]);
}
// yVal match
content = adjustValPrecision(yPattern, content, item.datapoint[1]);
return content;
};
function adjustValPrecision(pattern, content, value) {
var precision;
if( content.match(pattern) !== 'null' ) {
if(RegExp.$1 !== '') {
precision = RegExp.$1;
value = value.toFixed(precision)
}
content = content.replace(pattern, value);
}
return content;
};
}
$.plot.plugins.push({
init: init,
options: options,
name: 'tooltip',
version: '0.4'
});
})(jQuery);

@ -1,3 +0,0 @@
(function(c){c.plot.plugins.push({init:function(g){var h,i;function l(a){var b=0,d=0,b=a.pageX,d=a.pageY;h=b;i=d}function m(a){return c.plot.formatDate(new Date(a),f.tooltipOpts.dateFormat)}function j(a,b,d){var c=/%s/,e=/%x\.{0,1}(\d{0,})/;typeof b.series.label!=="undefined"&&(a=a.replace(c,b.series.label));a=typeof d==="function"?a.replace(e,d(b.datapoint[0])):k(e,a,b.datapoint[0]);return a=k(/%y\.{0,1}(\d{0,})/,a,b.datapoint[1])}function k(a,b,d){var c;if(b.match(a)!=="null"){if(RegExp.$1!=="")c=
RegExp.$1,d=d.toFixed(c);b=b.replace(a,d)}return b}h=0;i=0;var f=g.getOptions();g.hooks.bindEvents.push(function(a,b){var d=f.tooltipOpts,g=a.getPlaceholder(),e;f.tooltip!==!1&&(c("#flotTip").length>0?e=c("#flotTip"):(e=c("<div />").attr("id","flotTip"),e.appendTo("body").hide().css({position:"absolute"}),d.defaultTheme&&e.css({background:"#fff","z-index":"100",padding:"0.4em 0.6em","border-radius":"0.5em","font-size":"0.8em",border:"1px solid #111"})),c(g).bind("plothover",function(a,b,c){c?(a=f.xaxis.mode===
"time"||f.xaxes[0].mode==="time"?j(d.content,c,m):j(d.content,c),e.html(a).css({left:h+d.shifts.x,top:i+d.shifts.y}).show()):e.hide().html("")}),b.mousemove(l))})},options:{tooltip:!1,tooltipOpts:{content:"%s | X: %x | Y: %y.2",dateFormat:"%y-%0m-%0d",shifts:{x:10,y:20},defaultTheme:!0}},name:"tooltip",version:"0.4"})})(jQuery);

@ -1,146 +0,0 @@
/*
* jquery.flot.tooltip
*
* desc: create tooltip with values of hovered point on the graph,
support many series, time mode,
you can set custom tip content (also with use of HTML tags) and precision of values
* version: 0.4.2
* author: Krzysztof Urbas @krzysu [myviews.pl]
* website: https://github.com/krzysu/flot.tooltip
*
* released under MIT License, 2011
*/
(function ($) {
var options = {
tooltip: false, //boolean
tooltipOpts: {
content: "%s | X: %x | Y: %y.2", //%s -> series label, %x -> X value, %y -> Y value, %x.2 -> precision of X value
dateFormat: "%y-%0m-%0d",
shifts: {
x: 10,
y: 20
},
defaultTheme: true
}
};
function init(plot) {
var tipPosition = {x: 0, y: 0};
var opts = plot.getOptions();
function updateTooltipPosition(pos) {
tipPosition.x = pos.x;
tipPosition.y = pos.y;
};
function onMouseMove(e) {
var pos = {x: 0, y: 0};
pos.x = e.pageX;
pos.y = e.pageY;
updateTooltipPosition(pos);
};
function timestampToDate(tmst) {
var theDate = new Date(tmst);
return $.plot.formatDate(theDate, opts.tooltipOpts.dateFormat);
};
plot.hooks.bindEvents.push(function (plot, eventHolder) {
var to = opts.tooltipOpts;
var placeholder = plot.getPlaceholder();
var $tip;
if (opts.tooltip === false) return;
if( $('#flotTip').length > 0 ){
$tip = $('#flotTip');
}
else {
$tip = $('<div />').attr('id', 'flotTip');
$tip.appendTo('body').hide().css({position: 'absolute'});
if(to.defaultTheme) {
$tip.css({
'background': '#fff',
'z-index': '100',
'padding': '0.4em 0.6em',
'border-radius': '0.5em',
'font-size': '0.8em',
'border': '1px solid #111'
});
}
}
$(placeholder).bind("plothover", function (event, pos, item) {
if (item) {
var tipText;
if(opts.xaxis.mode === "time" || opts.xaxes[0].mode === "time") {
tipText = stringFormat(to.content, item, timestampToDate);
}
else {
tipText = stringFormat(to.content, item);
}
$tip.html( tipText ).css({left: tipPosition.x + to.shifts.x, top: tipPosition.y + to.shifts.y}).show();
}
else {
$tip.hide().html('');
}
});
eventHolder.mousemove(onMouseMove);
});
function stringFormat(content, item, fnct) {
var seriesPattern = /%s/;
var xPattern = /%x\.{0,1}(\d{0,})/;
var yPattern = /%y\.{0,1}(\d{0,})/;
//series match
if( typeof(item.series.label) !== 'undefined' ) {
content = content.replace(seriesPattern, item.series.label);
}
// xVal match
if( typeof(fnct) === 'function' ) {
content = content.replace(xPattern, fnct(item.datapoint[0]) );
}
else {
content = adjustValPrecision(xPattern, content, item.datapoint[0]);
}
// yVal match
content = adjustValPrecision(yPattern, content, item.datapoint[1]);
return content;
};
function adjustValPrecision(pattern, content, value) {
var precision;
if( content.match(pattern) !== 'null' ) {
if(RegExp.$1 !== '') {
precision = RegExp.$1;
value = value.toFixed(precision)
}
content = content.replace(pattern, value);
}
return content;
};
}
$.plot.plugins.push({
init: init,
options: options,
name: 'tooltip',
version: '0.4'
});
})(jQuery);

@ -1,3 +0,0 @@
(function(c){c.plot.plugins.push({init:function(g){var h,i;function l(a){var b=0,d=0,b=a.pageX,d=a.pageY;h=b;i=d}function m(a){return c.plot.formatDate(new Date(a),f.tooltipOpts.dateFormat)}function j(a,b,d){var c=/%s/,e=/%x\.{0,1}(\d{0,})/;typeof b.series.label!=="undefined"&&(a=a.replace(c,b.series.label));a=typeof d==="function"?a.replace(e,d(b.datapoint[0])):k(e,a,b.datapoint[0]);return a=k(/%y\.{0,1}(\d{0,})/,a,b.datapoint[1])}function k(a,b,d){var c;if(b.match(a)!=="null"){if(RegExp.$1!=="")c=
RegExp.$1,d=d.toFixed(c);b=b.replace(a,d)}return b}h=0;i=0;var f=g.getOptions();g.hooks.bindEvents.push(function(a,b){var d=f.tooltipOpts,g=a.getPlaceholder(),e;f.tooltip!==!1&&(c("#flotTip").length>0?e=c("#flotTip"):(e=c("<div />").attr("id","flotTip"),e.appendTo("body").hide().css({position:"absolute"}),d.defaultTheme&&e.css({background:"#fff","z-index":"100",padding:"0.4em 0.6em","border-radius":"0.5em","font-size":"0.8em",border:"1px solid #111"})),c(g).bind("plothover",function(a,b,c){c?(a=f.xaxis.mode===
"time"||f.xaxes[0].mode==="time"?j(d.content,c,m):j(d.content,c),e.html(a).css({left:h+d.shifts.x,top:i+d.shifts.y}).show()):e.hide().html("")}),b.mousemove(l))})},options:{tooltip:!1,tooltipOpts:{content:"%s | X: %x | Y: %y.2",dateFormat:"%y-%0m-%0d",shifts:{x:10,y:20},defaultTheme:!0}},name:"tooltip",version:"0.4"})})(jQuery);

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -60,6 +60,7 @@ RRDRRAFilterDS.prototype.getElFast = function(row_idx,ds_idx) {
}
}
// --------------------------------------------------
function RRDFilterDS(rrd_file,ds_id_list) {
this.rrd_file=rrd_file;
@ -74,7 +75,7 @@ function RRDFilterDS(rrd_file,ds_id_list) {
this.ds_list.push(new_ds);
}
}
RRDFilterDS.prototype.getMinStep = function() {return this.rrd_file.getMinStep();}
RRDFilterDS.prototype.getMinSteps = function() {return this.rrd_file.getMinSteps();}
RRDFilterDS.prototype.getLastUpdate = function() {return this.rrd_file.getLastUpdate();}
RRDFilterDS.prototype.getNrDSs = function() {return this.ds_list.length;}
@ -125,25 +126,12 @@ RRDFilterDS.prototype.getRRA = function(idx) {return new RRDRRAFilterDS(this.rrd
// computeResult(val_list) - val_list contains the values of the requested DSs (in the same order)
// Example class that implements the interface:
// function DoNothing(ds_name) { //Leaves the DS alone.
// this.getName = function() {return ds_name;}
// this.getDSNames = function() {return [ds1_name];}
// this.computeResult = function(val_list) {return val_list[0];}
// }
// function sumDS(ds1_name,ds2_name) { //Sums the two DSs.
// this.getName = function() {return ds1_name+"+"+ds2_name;}
// this.getDSNames = function() {return [ds1_name,ds2_name];}
// function sumDS(ds1,ds2) {
// this.getName = function() {return ds1+"+"+ds2;}
// this.getDSNames = function() {return [ds1,ds2];}
// this.computeResult = function(val_list) {return val_list[0]+val_list[1];}
// }
//
// So to add a summed DS of your 1st and second DS:
// var ds0_name = rrd_data.getDS(0).getName();
// var ds1_name = rrd_data.getDS(1).getName();
// rrd_data = new RRDFilterOp(rrd_data, [new DoNothing(ds0_name),
// DoNothing(ds1_name), sumDS(ds0_name, ds1_name]);
////////////////////////////////////////////////////////////////////
//Private
function RRDDSFilterOp(rrd_file,op_obj,my_idx) {
this.rrd_file=rrd_file;
this.op_obj=op_obj;
@ -166,8 +154,7 @@ RRDDSFilterOp.prototype.getMax = function() {return undefined;}
RRDDSFilterOp.prototype.getRealDSList = function() { return this.ds_idx_list;}
RRDDSFilterOp.prototype.computeResult = function(val_list) {return this.op_obj.computeResult(val_list);}
// ------ --------------------------------------------
//Private
// --------------------------------------------------
function RRDRRAFilterOp(rrd_rra,ds_list) {
this.rrd_rra=rrd_rra;
this.ds_list=ds_list;
@ -203,7 +190,6 @@ RRDRRAFilterOp.prototype.getElFast = function(row_idx,ds_idx) {
}
// --------------------------------------------------
//Public
function RRDFilterOp(rrd_file,op_obj_list) {
this.rrd_file=rrd_file;
this.ds_list=[];
@ -211,8 +197,9 @@ function RRDFilterOp(rrd_file,op_obj_list) {
this.ds_list.push(new RRDDSFilterOp(rrd_file,op_obj_list[i],i));
}
}
RRDFilterOp.prototype.getMinStep = function() {return this.rrd_file.getMinStep();}
RRDFilterOp.prototype.getMinSteps = function() {return this.rrd_file.getMinSteps();}
RRDFilterOp.prototype.getLastUpdate = function() {return this.rrd_file.getLastUpdate();}
RRDFilterOp.prototype.getNrDSs = function() {return this.ds_list.length;}
RRDFilterOp.prototype.getDSNames = function() {
var ds_names=[];
@ -253,131 +240,3 @@ RRDFilterOp.prototype.getNrRRAs = function() {return this.rrd_file.getNrRRAs();}
RRDFilterOp.prototype.getRRAInfo = function(idx) {return this.rrd_file.getRRAInfo(idx);}
RRDFilterOp.prototype.getRRA = function(idx) {return new RRDRRAFilterOp(this.rrd_file.getRRA(idx),this.ds_list);}
// ================================================================
// Shift RRAs in rra_list by the integer shift_int (in seconds).
// Only change is getLastUpdate - this takes care of everything.
// Example: To shift the first three 3 RRAs in the file by one hour,
// rrd_data = new RRAFilterShift(rra_data, 3600, [0,1,2]);
function RRAFilterShift(rrd_file, shift_int, rra_list) {
this.rrd_file = rrd_file;
this.shift_int = shift_int;
this.rra_list = rra_list;
this.shift_in_seconds = this.shift_int*3600; //number of steps needed to move 1 hour
}
RRAFilterShift.prototype.getMinStep = function() {return this.rrd_file.getMinStep();}
RRAFilterShift.prototype.getLastUpdate = function() {return this.rrd_file.getLastUpdate()+this.shift_in_seconds;}
RRAFilterShift.prototype.getNrDSs = function() {return this.rrd_file.getNrDSs();}
RRAFilterShift.prototype.getDSNames = function() {return this.rrd_file.getDSNames();}
RRAFilterShift.prototype.getDS = function(id) {return this.rrd_file.getDS(id);}
RRAFilterShift.prototype.getNrRRAs = function() {return this.rra_list.length;}
RRAFilterShift.prototype.getRRAInfo = function(idx) {return this.rrd_file.getRRAInfo(idx);}
RRAFilterShift.prototype.getRRA = function(idx) {return this.rrd_file.getRRA(idx);}
// ================================================================
// Filter RRAs by using a user provided filter object
// The object must implement the following interface
// getIdx() - Index of RRA to use
// getStep() - new step size (return null to use step size of RRA specified by getIdx()
/* Example classes that implements the interface:
*
* //This RRA Filter object leaves the original RRA unchanged.
*
* function RRADoNothing(rra_idx) {
* this.getIdx = function() {return rra_idx;}
* this.getStep = function() {return null;}
* }
*
* // This Filter creates a new RRA with a different step size
* // based on another RRA, whose data the new RRA averages.
* // rra_idx should be index of RRA with largest step size
* // that doesn't exceed new step size.
*
* function RRA_Avg(rra_idx,new_step_in_seconds) {
* this.getIdx = function() {return rra_idx;}
* this.getStep = function() {return new_step_in_seconds;}
* }
* //For example, if you have two RRAs, one with a 5 second step,
* //and another with a 60 second step, and you'd like a 30 second step,
* //rrd_data = new RRDRRAFilterShift(rrd_data,[new RRADoNothing(0), new RRDDoNothing(1),new RRA_Avg(1,30)];)
*/
//Private Function
function RRAInfoFilterAvg(rrd_file, rra, op_obj, idx) {
this.rrd_file = rrd_file;
this.op_obj = op_obj;
this.base_rra = rrd_file.getRRA(this.op_obj.getIdx());
this.rra = rra;
this.idx = idx;
var scaler = 1;
if (this.op_obj.getStep()!=null) {scaler = this.op_obj.getStep()/this.base_rra.getStep();}
this.scaler = scaler;
}
RRAInfoFilterAvg.prototype.getIdx = function() {return this.idx;}
RRAInfoFilterAvg.prototype.getNrRows = function() {return this.rra.getNrRows();} //draw info from RRAFilterAvg
RRAInfoFilterAvg.prototype.getStep = function() {return this.rra.getStep();}
RRAInfoFilterAvg.prototype.getCFName = function() {return this.rra.getCFName();}
RRAInfoFilterAvg.prototype.getPdpPerRow = function() {return this.rrd_file.getRRAInfo(this.op_obj.getIdx()).getPdpPerRow()*this.scaler;}
//---------------------------------------------------------------------------
//Private Function
function RRAFilterAvg(rrd_file, op_obj) {
this.rrd_file = rrd_file;
this.op_obj = op_obj;
this.base_rra = rrd_file.getRRA(op_obj.getIdx());
var scaler=1;
if (op_obj.getStep()!=null) {scaler = op_obj.getStep()/this.base_rra.getStep();}
this.scaler = Math.floor(scaler);
//document.write(this.scaler+",");
}
RRAFilterAvg.prototype.getIdx = function() {return this.op_obj.getIdx();}
RRAFilterAvg.prototype.getCFName = function() {return this.base_rra.getCFName();}
RRAFilterAvg.prototype.getNrRows = function() {return Math.floor(this.base_rra.getNrRows()/this.scaler);}
RRAFilterAvg.prototype.getNrDSs = function() {return this.base_rra.getNrDSs();}
RRAFilterAvg.prototype.getStep = function() {
if(this.op_obj.getStep()!=null) {
return this.op_obj.getStep();
} else { return this.base_rra.getStep();}
}
RRAFilterAvg.prototype.getEl = function(row,ds) {
var sum=0;
for(var i=0;i<this.scaler;i++) {
sum += this.base_rra.getEl((this.scaler*row)+i,ds);
}
return sum/this.scaler;
}
RRAFilterAvg.prototype.getElFast = function(row,ds) {
var sum=0;
for(var i=0;i<this.scaler;i++) {
sum += this.base_rra.getElFast((this.scaler*row)+i,ds);
}
return sum/this.scaler;
}
//----------------------------------------------------------------------------
//Public function - use this one for RRA averaging
function RRDRRAFilterAvg(rrd_file, op_obj_list) {
this.rrd_file = rrd_file;
this.op_obj_list = op_obj_list;
this.rra_list=[];
for (var i=0; i<op_obj_list.length; i++) {
this.rra_list.push(new RRAFilterAvg(rrd_file,op_obj_list[i]));
}
}
RRDRRAFilterAvg.prototype.getMinStep = function() {return this.rrd_file.getMinStep();} //other RRA steps change, not min
RRDRRAFilterAvg.prototype.getLastUpdate = function() {return this.rrd_file.getLastUpdate();}
RRDRRAFilterAvg.prototype.getNrDSs = function() {return this.rrd_file.getNrDSs();} //DSs unchanged
RRDRRAFilterAvg.prototype.getDSNames = function() {return this.rrd_file.getDSNames();}
RRDRRAFilterAvg.prototype.getDS = function(id) {return this.rrd_file.getDS(id);}
RRDRRAFilterAvg.prototype.getNrRRAs = function() {return this.rra_list.length;}
RRDRRAFilterAvg.prototype.getRRAInfo = function(idx) {
if ((idx>=0) && (idx<this.rra_list.length)) {
return new RRAInfoFilterAvg(this.rrd_file, this.rra_list[idx],this.op_obj_list[idx],idx);
} else {return this.rrd_file.getRRAInfo(0);}
}
RRDRRAFilterAvg.prototype.getRRA = function(idx) {
if ((idx>=0) && (idx<this.rra_list.length)) {
return this.rra_list[idx];
}
}

@ -17,99 +17,37 @@
*
*/
/*
* Local dependencies:
* rrdFlotSupport.py
*
* External dependencies:
* [Flot]/jquery.py
* [Flot]/jquery.flot.js
* [Flot]/jquery.flot.selection.js
*/
/* graph_options defaults (see Flot docs for details)
* {
* legend: { position:"nw",noColumns:3},
* lines: { show:true },
* yaxis: { autoscaleMargin: 0.20}
* }
*
* ds_graph_options is a dictionary of DS_name,
* with each element being a graph_option
* The defaults for each element are
* {
* title: label or ds_name // this is what is displayed in the checkboxes
* checked: first_ds_in_list? // boolean
* label: title or ds_name // this is what is displayed in the legend
* color: ds_index // see Flot docs for details
* lines: { show:true } // see Flot docs for details
* yaxis: 1 // can be 1 or 2
* stack: 'none' // other options are 'positive' and 'negative'
* }
*
* //overwrites other defaults; mostly used for linking via the URL
* rrdflot_defaults defaults (see Flot docs for details)
* {
* legend: "Top" //Starting location of legend. Options are:
* // "Top","Bottom","TopRight","BottomRight","None".
* num_cb_rows: 12 //How many rows of DS checkboxes per column.
* use_elem_buttons: false //To be used in conjunction with num_cb_rows: This option
* // creates a button above every column, which selects
* // every element in the column.
* multi_ds: false //"true" appends the name of the aggregation function to the
* // name of the DS element.
* multi_rra: false //"true" appends the name of the RRA consolidation function (CF)
* // (AVERAGE, MIN, MAX or LAST) to the name of the RRA. Useful
* // for RRAs over the same interval with different CFs.
* use_checked_DSs: false //Use the list checked_DSs below.
* checked_DSs: [] //List of elements to be checked by default when graph is loaded.
* // Overwrites graph options.
* use_rra: false //Whether to use the rra index specified below.
* rra: 0 //RRA (rra index in rrd) to be selected when graph is loaded.
* use_windows: false //Whether to use the window zoom specifications below.
* window_min: 0 //Sets minimum for window zoom. X-axis usually in unix time.
* window_max: 0 //Sets maximum for window zoom.
* graph_height: "300px" //Height of main graph.
* graph_width: "500px" //Width of main graph.
* scale_height: "110px" //Height of small scaler graph.
* scale_width: "250px" //Width of small scaler graph.
* timezone: 0 //timezone.
* }
*/
var local_checked_DSs = [];
var selected_rra = 0;
var window_min=0;
var window_max=0;
var elem_group=null;
var timezone_shift=0;
function suffixFormatter(val, axis) {
if (val > 1000000)
return (val / 1000000).toFixed(axis.tickDecimals) + " MB";
else if (val > 1000)
return (val / 1000).toFixed(axis.tickDecimals) + " kB";
else
return val.toFixed(axis.tickDecimals) + " B";
}
function rrdFlot(html_id, rrd_file, graph_options, ds_graph_options, rrdflot_defaults) {
function rrdFlot(html_id, rrd_file, graph_options, ds_graph_options, si_suffix) {
if(si_suffix==null)
this.si_suffix = false;
else
this.si_suffix = si_suffix;
this.html_id=html_id;
this.rrd_file=rrd_file;
this.graph_options=graph_options;
if (rrdflot_defaults==null) {
this.rrdflot_defaults=new Object(); // empty object, just not to be null
} else {
this.rrdflot_defaults=rrdflot_defaults;
}
if (ds_graph_options==null) {
this.ds_graph_options=new Object(); // empty object, just not to be null
this.ds_graph_options=new Object();
} else {
this.ds_graph_options=ds_graph_options;
}
this.selection_range=new rrdFlotSelection();
graph_info={};
this.createHTML();
this.populateRes();
this.populateDScb();
this.drawFlotGraph()
this.drawFlotGraph();
}
// ===============================================
// Create the HTML tags needed to host the graphs
rrdFlot.prototype.createHTML = function() {
var rf_this=this; // use obj inside other functions
@ -120,250 +58,113 @@ rrdFlot.prototype.createHTML = function() {
this.graph_id=this.html_id+"_graph";
this.scale_id=this.html_id+"_scale";
this.legend_sel_id=this.html_id+"_legend_sel";
this.time_sel_id=this.html_id+"_time_sel";
this.elem_group_id=this.html_id+"_elem_group";
// First clean up anything in the element
while (base_el.lastChild!=null) base_el.removeChild(base_el.lastChild);
// Now create the layout
var external_table=document.createElement("Table");
// Header two: resulution select and DS selection title
var rowHeader=external_table.insertRow(-1);
var cellRes=rowHeader.insertCell(-1);
cellRes.colSpan=3;
cellRes.appendChild(document.createTextNode("Resolution:"));
var forRes=document.createElement("Select");
forRes.id=this.res_id;
//forRes.onChange= this.callback_res_changed;
forRes.onChange= this.callback_res_changed;
forRes.onchange= function () {rf_this.callback_res_changed();};
cellRes.appendChild(forRes);
var cellDSTitle=rowHeader.insertCell(-1);
cellDSTitle.appendChild(document.createTextNode("Select elements to plot:"));
var cellScaleReset=rowHeader.insertCell(-1);
cellScaleReset.vAlign="center";
cellScaleReset.appendChild(document.createTextNode(" "));
var elScaleReset=document.createElement("input");
elScaleReset.type = "button";
elScaleReset.value = "Reset selection";
elScaleReset.onclick = function () {rf_this.callback_scale_reset();}
cellScaleReset.appendChild(elScaleReset);
// Graph row: main graph and DS selection block
var rowGraph=external_table.insertRow(-1);
var cellGraph=rowGraph.insertCell(-1);
cellGraph.colSpan=3;
var elGraph=document.createElement("Div");
if(this.rrdflot_defaults.graph_width!=null) {
elGraph.style.width=this.rrdflot_defaults.graph_width;
} else {elGraph.style.width="500px";}
if(this.rrdflot_defaults.graph_height!=null) {
elGraph.style.height=this.rrdflot_defaults.graph_height;
} else {elGraph.style.height="300px";}
elGraph.style.width="670px";
elGraph.style.height="200px";
elGraph.id=this.graph_id;
cellGraph.appendChild(elGraph);
var cellDScb=rowGraph.insertCell(-1);
cellDScb.vAlign="top";
var formDScb=document.createElement("Form");
formDScb.id=this.ds_cb_id;
formDScb.onchange= function () {rf_this.callback_ds_cb_changed();};
cellDScb.appendChild(formDScb);
// Scale row: scaled down selection graph
var rowScale=external_table.insertRow(-1);
var cellScaleLegend=rowScale.insertCell(-1);
cellScaleLegend.vAlign="top";
cellScaleLegend.appendChild(document.createTextNode("Legend:"));
cellScaleLegend.appendChild(document.createElement('br'));
var forScaleLegend=document.createElement("Select");
forScaleLegend.id=this.legend_sel_id;
forScaleLegend.appendChild(new Option("Top","nw",this.rrdflot_defaults.legend=="Top"));
forScaleLegend.appendChild(new Option("Bottom","sw",this.rrdflot_defaults.legend=="Bottom"));
forScaleLegend.appendChild(new Option("TopRight","ne",this.rrdflot_defaults.legend=="TopRight"));
forScaleLegend.appendChild(new Option("BottomRight","se",this.rrdflot_defaults.legend=="BottomRight"));
forScaleLegend.appendChild(new Option("None","None",this.rrdflot_defaults.legend=="None"));
forScaleLegend.onchange= function () {rf_this.callback_legend_changed();};
cellScaleLegend.appendChild(forScaleLegend);
cellScaleLegend.appendChild(document.createElement('br'));
cellScaleLegend.appendChild(document.createTextNode("Timezone:"));
cellScaleLegend.appendChild(document.createElement('br'));
var timezone=document.createElement("Select");
timezone.id=this.time_sel_id;
var timezones = ["+12","+11","+10","+9","+8","+7","+6","+5","+4","+3","+2","+1","0",
"-1","-2","-3","-4","-5","-6","-7","-8","-9","-10","-11","-12"];
for(var j=0; j<24; j++) {
timezone.appendChild(new Option(timezones[j],timezones[j],this.rrdflot_defaults.timezone==timezones[j]));
}
timezone.onchange= function () {rf_this.callback_timezone_changed();};
cellScaleLegend.appendChild(timezone);
var cellScale=rowScale.insertCell(-1);
cellScale.align="right";
cellScale.colSpan=2;
var elScale=document.createElement("Div");
if(this.rrdflot_defaults.scale_width!=null) {
elScale.style.width=this.rrdflot_defaults.scale_width;
} else {elScale.style.width="250px";}
if(this.rrdflot_defaults.scale_height!=null) {
elScale.style.height=this.rrdflot_defaults.scale_height;
} else {elScale.style.height="110px";}
elScale.style.width="670px";
elScale.style.height="80px";
elScale.id=this.scale_id;
cellScale.appendChild(elScale);
var cellScaleReset=rowScale.insertCell(-1);
cellScaleReset.vAlign="top";
cellScaleReset.appendChild(document.createTextNode(" "));
cellScaleReset.appendChild(document.createElement('br'));
var elScaleReset=document.createElement("input");
elScaleReset.type = "button";
elScaleReset.value = "Reset selection";
elScaleReset.onclick = function () {rf_this.callback_scale_reset();}
cellScaleReset.appendChild(elScaleReset);
base_el.appendChild(external_table);
};
// ======================================
// Populate RRA and RD info
rrdFlot.prototype.populateRes = function() {
var form_el=document.getElementById(this.res_id);
// First clean up anything in the element
while (form_el.lastChild!=null) form_el.removeChild(form_el.lastChild);
// now populate with RRA info
var nrRRAs=this.rrd_file.getNrRRAs();
for (var i=0; i<nrRRAs; i++) {
var rra=this.rrd_file.getRRAInfo(i);
if(rra.getCFName() != "AVERAGE")
continue;
var step=rra.getStep();
var rows=rra.getNrRows();
var period=step*rows;
var rra_label=rfs_format_time(step)+" ("+rfs_format_time(period)+" total)";
var rra_label=rfs_format_time(period) + " - " + rfs_format_time(step) + " steps";
form_el.appendChild(new Option(rra_label,i));
}
if(this.rrdflot_defaults.use_rra) {form_el.selectedIndex = this.rrdflot_defaults.rra;}
};
rrdFlot.prototype.populateDScb = function() {
var rf_this=this; // use obj inside other functions
var form_el=document.getElementById(this.ds_cb_id);
//Create a table within a table to arrange
// checkbuttons into two or more columns
var table_el=document.createElement("Table");
var row_el=table_el.insertRow(-1);
row_el.vAlign="top";
var cell_el=null; // will define later
if (this.rrdflot_defaults.num_cb_rows==null) {
this.rrdflot_defaults.num_cb_rows=12;
}
// now populate with DS info
while (form_el.lastChild!=null) form_el.removeChild(form_el.lastChild);
var nrDSs=this.rrd_file.getNrDSs();
var elem_group_number = 0;
for (var i=0; i<nrDSs; i++) {
if ((i%this.rrdflot_defaults.num_cb_rows)==0) { // one column every x DSs
if(this.rrdflot_defaults.use_element_buttons) {
cell_el=row_el.insertCell(-1); //make next element column
if(nrDSs>this.rrdflot_defaults.num_cb_rows) { //if only one column, no need for a button
elem_group_number = (i/this.rrdflot_defaults.num_cb_rows)+1;
var elGroupSelect = document.createElement("input");
elGroupSelect.type = "button";
elGroupSelect.value = "Group "+elem_group_number;
elGroupSelect.onclick = (function(e) { //lambda function!!
return function() {rf_this.callback_elem_group_changed(e);};})(elem_group_number);
cell_el.appendChild(elGroupSelect);
cell_el.appendChild(document.createElement('br')); //add space between the two
}
} else {
//just make next element column
cell_el=row_el.insertCell(-1);
}
}
var ds=this.rrd_file.getDS(i);
if (this.rrdflot_defaults.multi_ds) { //null==false in boolean ops
var name=ds.getName()+"-"+ds.getType();
var name2=ds.getName();
}
else {var name=ds.getName(); var name2=ds.getName();}
var title=name;
if(this.rrdflot_defaults.use_checked_DSs) {
if(this.rrdflot_defaults.checked_DSs.length==0) {
var checked=(i==0); // only first checked by default
} else{checked=false;}
} else {var checked=(i==0);}
var name=ds.getName();
var title=name;
var checked=1;
if (this.ds_graph_options[name]!=null) {
var dgo=this.ds_graph_options[name];
if (dgo['title']!=null) {
// if the user provided the title, use it
title=dgo['title'];
} else if (dgo['label']!=null) {
// use label as a second choiceit
title=dgo['label'];
} // else leave the ds name
if(this.rrdflot_defaults.use_checked_DSs) {
if(this.rrdflot_defaults.checked_DSs.length==0) {
// if the user provided the title, use it
checked=dgo['checked'];
}
} else {
if (dgo['checked']!=null) {
checked=dgo['checked'];
}
}
if (dgo['checked']!=null) {
checked=dgo['checked'];
}
}
if(this.rrdflot_defaults.use_checked_DSs) {
if(this.rrdflot_defaults.checked_DSs==null) {continue;}
for(var j=0;j<this.rrdflot_defaults.checked_DSs.length;j++){
if (name==this.rrdflot_defaults.checked_DSs[j]) {checked=true;}
}
}
var cb_el = document.createElement("input");
cb_el.type = "checkbox";
cb_el.name = "ds";
cb_el.value = name2;
cb_el.checked = cb_el.defaultChecked = checked;
cell_el.appendChild(cb_el);
cell_el.appendChild(document.createTextNode(title));
cell_el.appendChild(document.createElement('br'));
}
form_el.appendChild(table_el);
};
// ======================================
//
rrdFlot.prototype.drawFlotGraph = function() {
// Res contains the RRA idx
var oSelect=document.getElementById(this.res_id);
var rra_idx=Number(oSelect.options[oSelect.selectedIndex].value);
selected_rra=rra_idx;
if(this.rrdflot_defaults.use_rra) {
oSelect.options[oSelect.selectedIndex].value = this.rrdflot_defaults.rra;
rra_idx = this.rrdflot_defaults.rra;
}
// now get the list of selected DSs
var ds_positive_stack_list=[];
var ds_negative_stack_list=[];
var ds_single_list=[];
var ds_colors={};
var oCB=document.getElementById(this.ds_cb_id);
var nrDSs=oCB.ds.length;
local_checked_DSs=[];
if (oCB.ds.length>0) {
for (var i=0; i<oCB.ds.length; i++) {
if (oCB.ds[i].checked==true) {
var ds_name=oCB.ds[i].value;
var nrDSs=this.rrd_file.getNrDSs();
for (var i=0; i<nrDSs; i++) {
var ds_name=this.rrd_file.getDS(i).getName();
var ds_stack_type='none';
local_checked_DSs.push(ds_name);;
if (this.ds_graph_options[ds_name]!=null) {
var dgo=this.ds_graph_options[ds_name];
if (dgo['stack']!=null) {
@ -378,116 +179,58 @@ rrdFlot.prototype.drawFlotGraph = function() {
ds_single_list.push(ds_name);
}
ds_colors[ds_name]=i;
}
}
} else { // single element is not treated as an array
if (oCB.ds.checked==true) {
// no sense trying to stack a single element
var ds_name=oCB.ds.value;
ds_single_list.push(ds_name);
ds_colors[ds_name]=0;
local_checked_DSs.push(ds_name);
}
}
var timeSelect=document.getElementById(this.time_sel_id);
timezone_shift=timeSelect.options[timeSelect.selectedIndex].value;
}
// then extract RRA data about those DSs
var flot_obj=rrdRRAStackFlotObj(this.rrd_file,rra_idx,
ds_positive_stack_list,ds_negative_stack_list,ds_single_list,
timezone_shift*3600);
ds_positive_stack_list,ds_negative_stack_list,ds_single_list);
// fix the colors, based on the position in the RRD
for (var i=0; i<flot_obj.data.length; i++) {
var name=flot_obj.data[i].label; // at this point, label is the ds_name
var color=ds_colors[name]; // default color as defined above
var name=flot_obj.data[i].label;
var color=ds_colors[name];
if (this.ds_graph_options[name]!=null) {
var dgo=this.ds_graph_options[name];
if (dgo['color']!=null) {
color=dgo['color'];
}
if (dgo['label']!=null) {
// if the user provided the label, use it
flot_obj.data[i].label=dgo['label'];
} else if (dgo['title']!=null) {
// use title as a second choice
flot_obj.data[i].label=dgo['title'];
} // else use the ds name
}
if (dgo['lines']!=null) {
// if the user provided the label, use it
flot_obj.data[i].lines=dgo['lines'];
}
if (dgo['yaxis']!=null) {
// if the user provided the label, use it
flot_obj.data[i].yaxis=dgo['yaxis'];
}
}
flot_obj.data[i].color=color;
}
// finally do the real plotting
this.bindFlotGraph(flot_obj);
};
// ======================================
// Bind the graphs to the HTML tags
rrdFlot.prototype.bindFlotGraph = function(flot_obj) {
var rf_this=this; // use obj inside other functions
var rf_this=this;
// Legend
var oSelect=document.getElementById(this.legend_sel_id);
var legend_id=oSelect.options[oSelect.selectedIndex].value;
var graph_jq_id="#"+this.graph_id;
var scale_jq_id="#"+this.scale_id;
fmt_cb = this.si_suffix ? suffixFormatter : null;
var graph_options = {
legend: {show:false, position:"nw",noColumns:3},
legend: {show:false, position:"nw",noColumns:5, backgroundOpacity: 0.5 },
lines: {show:true},
xaxis: { mode: "time" },
yaxis: { autoscaleMargin: 0.20},
yaxis: { autoscaleMargin: 0.20, tickFormatter: fmt_cb },
selection: { mode: "x" },
tooltip: true,
tooltipOpts: { content: "<h4>%s</h4> Value: %y.3" },
grid: { hoverable: true },
};
if (this.graph_options!=null) {
if (typeof(this.graph_options.tooltip)=='boolean'&&this.graph_options.tooltip==true) {
//nothing
}
else if(typeof(this.graph_options.tooltip)=='boolean'&&this.graph_options.tooltip==false) {
graph_options.grid.hoverable=false;
graph_options.tooltip=false;
}
else if(typeof(this.graph_options.tooltip)=='undefined') {
//defaults to true
}
else {
graph_options.grid.hoverable=false;
graph_options.tooltip=false;
}
}
if (legend_id=="None") {
// do nothing
} else {
graph_options.legend.show=true;
graph_options.legend.position=legend_id;
}
graph_options.legend.show=true;
if (this.selection_range.isSet()) {
var selection_range=this.selection_range.getFlotRanges();
if(this.rrdflot_defaults.use_windows) {
graph_options.xaxis.min = this.rrdflot_defaults.window_min;
graph_options.xaxis.max = this.rrdflot_defaults.window_max;
} else {
graph_options.xaxis.min=selection_range.xaxis.from;
graph_options.xaxis.max=selection_range.xaxis.to;
}
} else if(this.rrdflot_defaults.use_windows) {
graph_options.xaxis.min = this.rrdflot_defaults.window_min;
graph_options.xaxis.max = this.rrdflot_defaults.window_max;
} else {
graph_options.xaxis.min=flot_obj.min;
graph_options.xaxis.max=flot_obj.max;
@ -499,7 +242,7 @@ rrdFlot.prototype.bindFlotGraph = function(flot_obj) {
graph_options.legend.position=this.graph_options.legend.position;
}
if (this.graph_options.legend.noColumns!=null) {
gcale_data=flot_data;
graph_options.legend.noColumns=this.graph_options.legend.noColumns;
}
}
if (this.graph_options.yaxis!=null) {
@ -515,70 +258,47 @@ rrdFlot.prototype.bindFlotGraph = function(flot_obj) {
var scale_options = {
legend: {show:false},
lines: {show:true},
xaxis: {mode: "time", min:flot_obj.min, max:flot_obj.max },
xaxis: { mode: "time", min:flot_obj.min, max:flot_obj.max },
yaxis: { tickFormatter: fmt_cb },
selection: { mode: "x" },
};
//this.selection_range.selection_min=flot_obj.min;
//this.selection_range.selection_max=flot_obj.max;
var flot_data=flot_obj.data;
var graph_data=this.selection_range.trim_flot_data(flot_data);
var scale_data=flot_data;
this.graph = $.plot($(graph_jq_id), graph_data, graph_options);
this.scale = $.plot($(scale_jq_id), scale_data, scale_options);
if(this.rrdflot_defaults.use_windows) {
ranges = {};
ranges.xaxis = [];
ranges.xaxis.from = this.rrdflot_defaults.window_min;
ranges.xaxis.to = this.rrdflot_defaults.window_max;
rf_this.scale.setSelection(ranges,true);
window_min = ranges.xaxis.from;
window_max = ranges.xaxis.to;
}
if (this.selection_range.isSet()) {
this.scale.setSelection(this.selection_range.getFlotRanges(),true); //don't fire event, no need
this.scale.setSelection(this.selection_range.getFlotRanges(),true);
}
// now connect the two
$(graph_jq_id).unbind("plotselected"); // but first remove old function
$(graph_jq_id).unbind("plotselected");
$(graph_jq_id).bind("plotselected", function (event, ranges) {
// do the zooming
rf_this.selection_range.setFromFlotRanges(ranges);
graph_options.xaxis.min=ranges.xaxis.from;
graph_options.xaxis.max=ranges.xaxis.to;
window_min = ranges.xaxis.from;
window_max = ranges.xaxis.to;
rf_this.graph = $.plot($(graph_jq_id), rf_this.selection_range.trim_flot_data(flot_data), graph_options);
// don't fire event on the scale to prevent eternal loop
rf_this.scale.setSelection(ranges, true); //puts the transparent window on minigraph
rf_this.scale.setSelection(ranges, true);
});
$(scale_jq_id).unbind("plotselected"); //same here
$(scale_jq_id).unbind("plotselected");
$(scale_jq_id).bind("plotselected", function (event, ranges) {
rf_this.graph.setSelection(ranges);
});
// only the scale has a selection
// so when that is cleared, redraw also the graph
$(scale_jq_id).bind("plotunselected", function() {
rf_this.selection_range.reset();
graph_options.xaxis.min=flot_obj.min;
graph_options.xaxis.max=flot_obj.max;
rf_this.graph = $.plot($(graph_jq_id), rf_this.selection_range.trim_flot_data(flot_data), graph_options);
window_min = 0;
window_max = 0;
});
};
// callback functions that are called when one of the selections changes
rrdFlot.prototype.callback_res_changed = function() {
this.rrdflot_defaults.use_rra = false;
this.drawFlotGraph();
};
@ -594,35 +314,3 @@ rrdFlot.prototype.callback_legend_changed = function() {
this.drawFlotGraph();
};
rrdFlot.prototype.callback_timezone_changed = function() {
this.drawFlotGraph();
};
rrdFlot.prototype.callback_elem_group_changed = function(num) { //,window_min,window_max) {
var oCB=document.getElementById(this.ds_cb_id);
var nrDSs=oCB.ds.length;
if (oCB.ds.length>0) {
for (var i=0; i<oCB.ds.length; i++) {
if(Math.floor(i/this.rrdflot_defaults.num_cb_rows)==num-1) {oCB.ds[i].checked=true; }
else {oCB.ds[i].checked=false;}
}
}
this.drawFlotGraph()
};
function getGraphInfo() {
var graph_info = {};
graph_info['dss'] = local_checked_DSs;
graph_info['rra'] = selected_rra;
graph_info['window_min'] = window_min;
graph_info['window_max'] = window_max;
graph_info['timezone'] = timezone_shift;
return graph_info;
};
function resetWindow() {
window_min = 0;
window_max = 0;
};

@ -60,13 +60,9 @@
* }
*/
function rrdFlotMatrix(html_id, rrd_files, ds_list, graph_options, rrd_graph_options,rrdflot_defaults) {
function rrdFlotMatrix(html_id, rrd_files, ds_list, graph_options, rrd_graph_options) {
this.html_id=html_id;
this.rrd_files=rrd_files;
if (rrdflot_defaults==null) {
this.rrdflot_defaults=new Object();
}
else {this.rrdflot_defaults=rrdflot_defaults;}
if (ds_list==null) {
this.ds_list=[];
var rrd_file=this.rrd_files[0][1]; // get the first one... they are all the same
@ -164,11 +160,11 @@ rrdFlotMatrix.prototype.createHTML = function() {
cellScaleLegend.appendChild(document.createElement('br'));
var forScaleLegend=document.createElement("Select");
forScaleLegend.id=this.legend_sel_id;
forScaleLegend.appendChild(new Option("Top","nw",this.rrdflot_defaults.legend=="Top"));
forScaleLegend.appendChild(new Option("Bottom","sw",this.rrdflot_defaults.legend=="Bottom"));
forScaleLegend.appendChild(new Option("TopRight","ne",this.rrdflot_defaults.legend=="TopRight"));
forScaleLegend.appendChild(new Option("BottomRight","se",this.rrdflot_defaults.legend=="BottomRight"));
forScaleLegend.appendChild(new Option("None","None",this.rrdflot_defaults.legend=="None"));
forScaleLegend.appendChild(new Option("Top","nw"));
forScaleLegend.appendChild(new Option("Bottom","sw"));
forScaleLegend.appendChild(new Option("TopRight","ne"));
forScaleLegend.appendChild(new Option("BottomRight","se"));
forScaleLegend.appendChild(new Option("None","None"));
forScaleLegend.onchange= function () {rf_this.callback_legend_changed();};
cellScaleLegend.appendChild(forScaleLegend);
@ -375,6 +371,7 @@ rrdFlotMatrix.prototype.bindFlotGraph = function(flot_obj) {
// Legend
var oSelect=document.getElementById(this.legend_sel_id);
var legend_id=oSelect.options[oSelect.selectedIndex].value;
var graph_jq_id="#"+this.graph_id;
var scale_jq_id="#"+this.scale_id;
@ -384,27 +381,8 @@ rrdFlotMatrix.prototype.bindFlotGraph = function(flot_obj) {
xaxis: { mode: "time" },
yaxis: { autoscaleMargin: 0.20},
selection: { mode: "x" },
tooltip: true,
tooltipOpts: { content: "<h4>%s</h4> Value: %y.3" },
grid: { hoverable: true },
};
if (this.graph_options!=null) {
if (typeof(this.graph_options.tooltip)=='boolean'&&this.graph_options.tooltip==true) {
//nothing
}
else if(typeof(this.graph_options.tooltip)=='boolean'&&this.graph_options.tooltip==false) {
graph_options.grid.hoverable=false;
graph_options.tooltip=false;
}
else if(typeof(this.graph_options.tooltip)=='undefined') {
//defaults to true
}
else {
graph_options.grid.hoverable=false;
graph_options.tooltip=false;
}
}
if (legend_id=="None") {
// do nothing

@ -99,7 +99,7 @@ function rrdRRA2FlotObj(rrd_file,rra_idx,ds_list,want_ds_labels,want_rounding) {
// of the stack is invalid
function rrdRRAStackFlotObj(rrd_file,rra_idx,
ds_positive_stack_list,ds_negative_stack_list,ds_single_list,
timestamp_shift, want_ds_labels,want_rounding,one_undefined_enough) {
want_ds_labels,want_rounding,one_undefined_enough) {
var rra=rrd_file.getRRA(rra_idx);
var rra_rows=rra.getNrRows();
var last_update=rrd_file.getLastUpdate();
@ -115,7 +115,7 @@ function rrdRRAStackFlotObj(rrd_file,rra_idx,
var first_el=last_update-(rra_rows-1)*step;
var out_el={data:[], min:(first_el+timestamp_shift)*1000.0, max:(last_update+timestamp_shift)*1000.0};
var out_el={data:[], min:first_el*1000.0, max:last_update*1000.0};
// first the stacks stack
var stack_els=[ds_positive_stack_list,ds_negative_stack_list];
@ -164,7 +164,7 @@ function rrdRRAStackFlotObj(rrd_file,rra_idx,
}
// fill the flot data
for (var id=0; id<tmp_nr_ids; id++) {
tmp_flot_els[id].data.push([(timestamp+timestamp_shift)*1000.0,ds_vals[id]]);
tmp_flot_els[id].data.push([timestamp*1000.0,ds_vals[id]]);
}
}
} // end if
@ -190,7 +190,7 @@ function rrdRRAStackFlotObj(rrd_file,rra_idx,
for (var i=0;i<rra_rows;i++) {
var el=rra.getEl(i,ds_idx);
if (el!=undefined) {
flot_series.push([(timestamp+timestamp_shift)*1000.0,el]);
flot_series.push([timestamp*1000.0,el]);
}
timestamp+=step;
} // end for
@ -347,38 +347,10 @@ rrdFlotSelection.prototype.trim_flot_data = function(flot_data) {
rrdFlotSelection.prototype.trim_data = function(data_list) {
if (this.selection_min==null) return data_list; // no selection => no filtering
var out_data=[];
for (var i=0; i<data_list.length; i++) {
if (data_list[i]==null) continue; // protect
//data_list[i][0]+=3550000*5;
var nr=data_list[i][0]; //date in unix time
if ((nr>=this.selection_min) && (nr<=this.selection_max)) {
out_data.push(data_list[i]);
}
}
return out_data;
};
// Given an array of flot lines, limit to the selection
rrdFlotSelection.prototype.trim_flot_timezone_data = function(flot_data,shift) {
var out_data=[];
for (var i=0; i<flot_data.length; i++) {
var data_el=flot_data[i];
out_data.push({label : data_el.label, data:this.trim_timezone_data(data_el.data,shift), color:data_el.color, lines:data_el.lines, yaxis:data_el.yaxis});
}
return out_data;
};
// Limit to selection the flot series data element
rrdFlotSelection.prototype.trim_timezone_data = function(data_list,shift) {
if (this.selection_min==null) return data_list; // no selection => no filtering
var out_data=[];
for (var i=0; i<data_list.length; i++) {
if (data_list[i]==null) continue; // protect
var nr=data_list[i][0]+shift;
var nr=data_list[i][0];
if ((nr>=this.selection_min) && (nr<=this.selection_max)) {
out_data.push(data_list[i]);
}
@ -386,9 +358,8 @@ rrdFlotSelection.prototype.trim_timezone_data = function(data_list,shift) {
return out_data;
};
// ======================================
// Miscelaneous helper functions
// Miscelabeous helper functions
// ======================================
function rfs_format_time(s) {

@ -30,15 +30,14 @@
<script type="text/javascript">
function update_fname(rrd_data_arr, args) {
var graph_opts = {};
var ds_graph_opts = {};
var rrdflot_defaults = {timezone:"[% tz_offset_hours %]"};
if (rrd_data_arr.length == 1)
var f = new rrdFlot(args['plot_id'],rrd_data_arr[0],graph_opts,ds_graph_opts,rrdflot_defaults);
else {
var t = new RRDFileSum(rrd_data_arr, false);
var f = new rrdFlot(args['plot_id'],rrd_data_arr[0],graph_opts,ds_graph_opts,rrdflot_defaults);
}
var graph_opts={};
var ds_graph_opts={};
if (rrd_data_arr.length == 1)
var f=new rrdFlot(args['plot_id'],rrd_data_arr[0],graph_opts,ds_graph_opts, args['si_suffix']);
else {
var t = new RRDFileSum(rrd_data_arr, false);
var f=new rrdFlot(args['plot_id'],t,graph_opts,ds_graph_opts, args['si_suffix']);
}
}
function update_fname_handler(bf, args) {
@ -51,7 +50,7 @@
alert("File "+fname+" is not a valid RRD archive!");
}
if (rrd_data!=undefined) {
output.push(rrd_data);
output.push(rrd_data);
if (output.length >= args['rrd_count']) {
update_fname(output, args);
}
@ -59,14 +58,14 @@
}
function fname_update(fname, plot_id, si_suffix) {
var o = new Array();
var o = new Array();
for (var i = 0; i < fname.length; i++) {
try {
FetchBinaryURLAsync(fname[i], update_fname_handler, {plot_id: plot_id,
si_suffix: si_suffix, rrd_count: fname.length, name: fname[i],
output: o});
} catch (err) {
alert("Failed loading "+fname[i]+"\n"+err);
alert("Failed loading "+fname[i]+"\n"+err);
}
}
}

Loading…
Cancel
Save