'];
+ function drawAxisLabels() {
+
+ $.each(allAxes(), function (_, axis) {
+ var box = axis.box,
+ legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis",
+ layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles,
+ font = axis.options.font || "flot-tick-label tickLabel",
+ tick, x, y, halign, valign;
+
+ // Remove text before checking for axis.show and ticks.length;
+ // otherwise plugins, like flot-tickrotor, that draw their own
+ // tick labels will end up with both theirs and the defaults.
+
+ surface.removeText(layer);
+
+ if (!axis.show || axis.ticks.length == 0)
+ return;
- var axes = allAxes();
- for (var j = 0; j < axes.length; ++j) {
- var axis = axes[j], box = axis.box;
- if (!axis.show)
- continue;
- //debug: html.push('
')
- html.push('
');
for (var i = 0; i < axis.ticks.length; ++i) {
- var tick = axis.ticks[i];
+
+ tick = axis.ticks[i];
if (!tick.label || tick.v < axis.min || tick.v > axis.max)
continue;
- var pos = {}, align;
-
if (axis.direction == "x") {
- align = "center";
- pos.left = Math.round(plotOffset.left + axis.p2c(tick.v) - axis.labelWidth/2);
- if (axis.position == "bottom")
- pos.top = box.top + box.padding;
- else
- pos.bottom = canvasHeight - (box.top + box.height - box.padding);
- }
- else {
- pos.top = Math.round(plotOffset.top + axis.p2c(tick.v) - axis.labelHeight/2);
+ halign = "center";
+ x = plotOffset.left + axis.p2c(tick.v);
+ if (axis.position == "bottom") {
+ y = box.top + box.padding;
+ } else {
+ y = box.top + box.height - box.padding;
+ valign = "bottom";
+ }
+ } else {
+ valign = "middle";
+ y = plotOffset.top + axis.p2c(tick.v);
if (axis.position == "left") {
- pos.right = canvasWidth - (box.left + box.width - box.padding)
- align = "right";
- }
- else {
- pos.left = box.left + box.padding;
- align = "left";
+ x = box.left + box.width - box.padding;
+ halign = "right";
+ } else {
+ x = box.left + box.padding;
}
}
- pos.width = axis.labelWidth;
-
- var style = ["position:absolute", "text-align:" + align ];
- for (var a in pos)
- style.push(a + ":" + pos[a] + "px")
-
- html.push('
' + tick.label + '
');
+ surface.addText(layer, x, y, tick.label, font, null, null, halign, valign);
}
- html.push('
');
- }
-
- html.push('
');
-
- placeholder.append(html.join(""));
+ });
}
function drawSeries(series) {
@@ -1713,18 +2236,18 @@
if (series.points.show)
drawSeriesPoints(series);
}
-
+
function drawSeriesLines(series) {
function plotLine(datapoints, xoffset, yoffset, axisx, axisy) {
var points = datapoints.points,
ps = datapoints.pointsize,
prevx = null, prevy = null;
-
+
ctx.beginPath();
for (var i = ps; i < points.length; i += ps) {
var x1 = points[i - ps], y1 = points[i - ps + 1],
x2 = points[i], y2 = points[i + 1];
-
+
if (x1 == null || x2 == null)
continue;
@@ -1787,7 +2310,7 @@
if (x1 != prevx || y1 != prevy)
ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset);
-
+
prevx = x2;
prevy = y2;
ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset);
@@ -1839,7 +2362,7 @@
continue;
// clip x values
-
+
// clip with xmin
if (x1 <= x2 && x1 < axisx.min) {
if (x2 < axisx.min)
@@ -1874,7 +2397,7 @@
ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom));
areaOpen = true;
}
-
+
// now first check the case where both is outside
if (y1 >= axisy.max && y2 >= axisy.max) {
ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max));
@@ -1886,7 +2409,7 @@
ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min));
continue;
}
-
+
// else it's a bit more complicated, there might
// be a flat maxed out rectangle first, then a
// triangular cutout or reverse; to find these
@@ -1895,7 +2418,7 @@
// clip the y values, without shortcutting, we
// go through all cases in turn
-
+
// clip with ymin
if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) {
x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
@@ -1922,7 +2445,7 @@
ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1));
// it goes to (x1, y1), but we fill that below
}
-
+
// fill triangular section, this sometimes result
// in redundant points if (x1, y1) hasn't changed
// from previous line to, but we just ignore that
@@ -1976,7 +2499,7 @@
var x = points[i], y = points[i + 1];
if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)
continue;
-
+
ctx.beginPath();
x = axisx.p2c(x);
y = axisy.p2c(y) + offset;
@@ -1985,7 +2508,7 @@
else
symbol(ctx, x, y, radius, shadow);
ctx.closePath();
-
+
if (fillStyle) {
ctx.fillStyle = fillStyle;
ctx.fill();
@@ -1993,7 +2516,7 @@
ctx.stroke();
}
}
-
+
ctx.save();
ctx.translate(plotOffset.left, plotOffset.top);
@@ -2001,6 +2524,15 @@
sw = series.shadowSize,
radius = series.points.radius,
symbol = series.points.symbol;
+
+ // If the user sets the line width to 0, we change it to a very
+ // small value. A line width of 0 seems to force the default of 1.
+ // Doing the conditional here allows the shadow setting to still be
+ // optional even with a lineWidth of 0.
+
+ if( lw == 0 )
+ lw = 0.0001;
+
if (lw > 0 && sw > 0) {
// draw shadow in two steps
var w = sw / 2;
@@ -2022,7 +2554,7 @@
ctx.restore();
}
- function drawBar(x, y, b, barLeft, barRight, offset, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) {
+ function drawBar(x, y, b, barLeft, barRight, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) {
var left, right, bottom, top,
drawLeft, drawRight, drawTop, drawBottom,
tmp;
@@ -2064,12 +2596,12 @@
drawTop = false;
}
}
-
+
// clip
if (right < axisx.min || left > axisx.max ||
top < axisy.min || bottom > axisy.max)
return;
-
+
if (left < axisx.min) {
left = axisx.min;
drawLeft = false;
@@ -2084,7 +2616,7 @@
bottom = axisy.min;
drawBottom = false;
}
-
+
if (top > axisy.max) {
top = axisy.max;
drawTop = false;
@@ -2094,16 +2626,11 @@
bottom = axisy.p2c(bottom);
right = axisx.p2c(right);
top = axisy.p2c(top);
-
+
// fill the bar
if (fillStyleCallback) {
- c.beginPath();
- c.moveTo(left, bottom);
- c.lineTo(left, top);
- c.lineTo(right, top);
- c.lineTo(right, bottom);
c.fillStyle = fillStyleCallback(bottom, top);
- c.fill();
+ c.fillRect(left, top, right - left, bottom - top)
}
// draw outline
@@ -2111,35 +2638,35 @@
c.beginPath();
// FIXME: inline moveTo is buggy with excanvas
- c.moveTo(left, bottom + offset);
+ c.moveTo(left, bottom);
if (drawLeft)
- c.lineTo(left, top + offset);
+ c.lineTo(left, top);
else
- c.moveTo(left, top + offset);
+ c.moveTo(left, top);
if (drawTop)
- c.lineTo(right, top + offset);
+ c.lineTo(right, top);
else
- c.moveTo(right, top + offset);
+ c.moveTo(right, top);
if (drawRight)
- c.lineTo(right, bottom + offset);
+ c.lineTo(right, bottom);
else
- c.moveTo(right, bottom + offset);
+ c.moveTo(right, bottom);
if (drawBottom)
- c.lineTo(left, bottom + offset);
+ c.lineTo(left, bottom);
else
- c.moveTo(left, bottom + offset);
+ c.moveTo(left, bottom);
c.stroke();
}
}
-
+
function drawSeriesBars(series) {
- function plotBars(datapoints, barLeft, barRight, offset, fillStyleCallback, axisx, axisy) {
+ function plotBars(datapoints, barLeft, barRight, fillStyleCallback, axisx, axisy) {
var points = datapoints.points, ps = datapoints.pointsize;
-
+
for (var i = 0; i < points.length; i += ps) {
if (points[i] == null)
continue;
- drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, offset, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth);
+ drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth);
}
}
@@ -2149,9 +2676,22 @@
// FIXME: figure out a way to add shadows (for instance along the right edge)
ctx.lineWidth = series.bars.lineWidth;
ctx.strokeStyle = series.color;
- var barLeft = series.bars.align == "left" ? 0 : -series.bars.barWidth/2;
+
+ var barLeft;
+
+ switch (series.bars.align) {
+ case "left":
+ barLeft = 0;
+ break;
+ case "right":
+ barLeft = -series.bars.barWidth;
+ break;
+ default:
+ barLeft = -series.bars.barWidth / 2;
+ }
+
var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top); } : null;
- plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, 0, fillStyleCallback, series.xaxis, series.yaxis);
+ plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, fillStyleCallback, series.xaxis, series.yaxis);
ctx.restore();
}
@@ -2162,27 +2702,66 @@
if (filloptions.fillColor)
return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor);
-
+
var c = $.color.parse(seriesColor);
c.a = typeof fill == "number" ? fill : 0.4;
c.normalize();
return c.toString();
}
-
- function insertLegend() {
- placeholder.find(".legend").remove();
- if (!options.legend.show)
+ function insertLegend() {
+
+ if (options.legend.container != null) {
+ $(options.legend.container).html("");
+ } else {
+ placeholder.find(".legend").remove();
+ }
+
+ if (!options.legend.show) {
return;
-
- var fragments = [], rowStarted = false,
+ }
+
+ var fragments = [], entries = [], rowStarted = false,
lf = options.legend.labelFormatter, s, label;
+
+ // Build a list of legend entries, with each having a label and a color
+
for (var i = 0; i < series.length; ++i) {
s = series[i];
- label = s.label;
- if (!label)
- continue;
-
+ if (s.label) {
+ label = lf ? lf(s.label, s) : s.label;
+ if (label) {
+ entries.push({
+ label: label,
+ color: s.color
+ });
+ }
+ }
+ }
+
+ // Sort the legend using either the default or a custom comparator
+
+ if (options.legend.sorted) {
+ if ($.isFunction(options.legend.sorted)) {
+ entries.sort(options.legend.sorted);
+ } else if (options.legend.sorted == "reverse") {
+ entries.reverse();
+ } else {
+ var ascending = options.legend.sorted != "descending";
+ entries.sort(function(a, b) {
+ return a.label == b.label ? 0 : (
+ (a.label < b.label) != ascending ? 1 : -1 // Logical XOR
+ );
+ });
+ }
+ }
+
+ // Generate markup for the list of entries, in their final order
+
+ for (var i = 0; i < entries.length; ++i) {
+
+ var entry = entries[i];
+
if (i % options.legend.noColumns == 0) {
if (rowStarted)
fragments.push('');
@@ -2190,16 +2769,15 @@
rowStarted = true;
}
- if (lf)
- label = lf(label, s);
-
fragments.push(
- '