Commit 938928b8 authored by olau@iola.dk's avatar olau@iola.dk

Support for preventing event in setSelection and revamped coloredAreas as...

Support for preventing event in setSelection and revamped coloredAreas as markings with support for lines

git-svn-id: https://flot.googlecode.com/svn/trunk@91 1e0a6537-2640-0410-bfb7-f154510ff394
parent 20fe8fdb
......@@ -454,8 +454,7 @@ Customizing the grid
backgroundColor: color or null
tickColor: color
labelMargin: number
coloredAreas: array of areas or (fn: plot area -> array of areas)
coloredAreasColor: color
markings: array of markings or (fn: axes -> array of markings)
borderWidth: number
clickable: boolean
hoverable: boolean
......@@ -477,28 +476,37 @@ labels with CSS, e.g. to change the color. They have class "tickLabel".
"borderWidth" is the width of the border around the plot. Set it to 0
to disable the border.
"coloredAreas" is an array of areas that will be drawn on top of the
background. You can either specify an array of objects with { x1, y1,
x2, y2 } or a function that returns such an array given the plot area
as { xmin, xmax, ymin, ymax }. The default color of the areas are
"coloredAreasColor". You can override the color of individual areas by
specifying "color" in the area object.
"markings" is used to draw simple lines and rectangular areas in the
background of the plot. You can either specify an array of ranges on
the form { xaxis: { from, to }, yaxis: { from, to } } (secondary axis
coordinates with x2axis/y2axis) or with a function that returns such
an array given the axes for the plot in an object as the first
parameter.
Here's an example array:
You can set the color of markings by specifying "color" in the ranges
object. Here's an example array:
coloredAreas: [ { x1: 0, y1: 10, x2: 2, y2: 15, color: "#bb0000" }, ... ]
markings: [ { xaxis: { from: 0, to: 2 }, yaxis: { from: 10, to: 10 }, color: "#bb0000" }, ... ]
If you leave out one of the values, that value is assumed to go to the
border of the plot. So for example { x1: 0, x2: 2 } means an area that
extends from the top to the bottom of the plot in the x range 0-2.
border of the plot. So for example if you only specify { xaxis: {
from: 0, to: 2 } } it means an area that extends from the top to the
bottom of the plot in the x range 0-2.
A line is drawn if from and to are the same, e.g.
markings: [ { yaxis: { from: 1, to: 1 } }, ... ]
would draw a line parallel to the x axis at y = 1. You can control the
line width with "lineWidth" in the ranges objects.
An example function might look like this:
coloredAreas: function (plotarea) {
var areas = [];
for (var x = Math.floor(plotarea.xmin); x < plotarea.xmax; x += 2)
areas.push({ x1: x, x2: x + 1 });
return areas;
markings: function (axes) {
var markings = [];
for (var x = Math.floor(axes.xaxis.min); x < axes.xaxis.max; x += 2)
markings.push({ xaxis: { from: x, to: x + 1 } });
return markings;
}
......@@ -595,7 +603,7 @@ can call:
Clear the selection rectangle.
- setSelection(ranges)
- 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
......@@ -606,10 +614,10 @@ can call:
setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } });
setSelection will trigger the "plotselected" event when called so you
may have to do a bit of shortcircuiting to prevent an eternal loop
if you invoke setSelection inside a "plotselected" handler.
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.
- highlight(series, datapoint)
......
......@@ -4,7 +4,8 @@ Flot x.x
Backwards API change summary: Timestamps are now in UTC. Also
"selected" event -> becomes "plotselected" with new data, the
parameters for setSelection are now different (but backwards
compatibility hooks are in place).
compatibility hooks are in place), coloredAreas becomes markings with
a new interface (but backwards compatibility hooks are in place).
Interactivity: added a new "plothover" event and this and the
......@@ -19,9 +20,10 @@ Support for dual axis has been added (based on patch by someone who's
annoyed and /david). For each data series you can specify which axes
it belongs to, and there are two more axes, x2axis and y2axis, to
customize. This affects the "selected" event which has been renamed to
"plotselected" and spews out { xaxis: { from: -10, to: 20 } ... } and
"plotselected" and spews out { xaxis: { from: -10, to: 20 } ... },
setSelection in which the parameters are on a new form (backwards
compatible hooks are in place so old code shouldn't break).
compatible hooks are in place so old code shouldn't break) and
markings (formerly coloredAreas).
Timestamps in time mode are now displayed according to
UTC instead of the time zone of the visitor. This affects the way the
......@@ -35,6 +37,11 @@ Added support for specifying the size of tick labels (axis.labelWidth,
axis.labelHeight). Useful for specifying a max label size to keep
multiple plots aligned.
Markings, previously coloredAreas, are now specified as ranges on the
axes, like { xaxis: { from: 0, to: 10 }}. Furthermore you can now draw
horizontal/vertical lines by setting from and to to the same
coordinate (idea from line support patch by by Ryan Funduk).
The "fill" option can now be a number that specifies the opacity of
the fill.
......@@ -45,6 +52,9 @@ will take the other coordinate into account when scaling the axes
New option for bars "align". Set it to "center" to center the bars on
the value they represent.
setSelection now takes a second parameter which you can use to prevent
the method from firing the "plotselected" handler.
Using the "container" option in legend now overwrites the container
element instead of just appending to it (fixes infinite legend bug,
reported by several people, fix by Brad Dewey).
......
......@@ -29,9 +29,9 @@ $(function () {
d[i][0] += 60 * 60 * 1000;
// helper for returning the weekends in a period
function weekendAreas(plotarea) {
var areas = [];
var d = new Date(plotarea.xmin);
function weekendAreas(axes) {
var markings = [];
var d = new Date(axes.xaxis.min);
// go to the first Saturday
d.setUTCDate(d.getUTCDate() - ((d.getUTCDay() + 1) % 7))
d.setUTCSeconds(0);
......@@ -39,19 +39,19 @@ $(function () {
d.setUTCHours(0);
var i = d.getTime();
do {
// when we don't set y1 and y2 the rectangle
// automatically extends to infinity in those directions
areas.push({ x1: i, x2: i + 2 * 24 * 60 * 60 * 1000 });
// when we don't set yaxis the rectangle automatically
// extends to infinity upwards and downwards
markings.push({ xaxis: { from: i, to: i + 2 * 24 * 60 * 60 * 1000 } });
i += 7 * 24 * 60 * 60 * 1000;
} while (i < plotarea.xmax);
} while (i < axes.xaxis.max);
return areas;
return markings;
}
var options = {
xaxis: { mode: "time" },
selection: { mode: "x" },
grid: { coloredAreas: weekendAreas }
grid: { markings: weekendAreas }
};
var plot = $.plot($("#placeholder"), [d], options);
......@@ -65,7 +65,6 @@ $(function () {
});
// now connect the two
var internalSelection = false;
$("#placeholder").bind("plotselected", function (event, ranges) {
// do the zooming
......@@ -73,20 +72,13 @@ $(function () {
$.extend(true, {}, options, {
xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to }
}));
if (internalSelection)
return; // prevent eternal loop
internalSelection = true;
overview.setSelection(ranges);
internalSelection = false;
// don't fire event on the overview to prevent eternal loop
overview.setSelection(ranges, true);
});
$("#overview").bind("plotselected", function (event, ranges) {
if (internalSelection)
return;
internalSelection = true;
plot.setSelection(ranges);
internalSelection = false;
});
});
</script>
......
......@@ -63,7 +63,6 @@ $(function () {
});
// now connect the two
var internalSelection = false;
$("#placeholder").bind("plotselected", function (event, ranges) {
// clamp the zooming to prevent eternal zoom
......@@ -79,18 +78,11 @@ $(function () {
yaxis: { min: ranges.yaxis.from, max: ranges.yaxis.to }
}));
if (internalSelection)
return; // prevent eternal loop
internalSelection = true;
overview.setSelection(ranges);
internalSelection = false;
// don't fire event on the overview to prevent eternal loop
overview.setSelection(ranges, true);
});
$("#overview").bind("plotselected", function (event, ranges) {
if (internalSelection)
return;
internalSelection = true;
plot.setSelection(ranges);
internalSelection = false;
});
});
</script>
......
......@@ -11,100 +11,100 @@
// where series is either just the data as [ [x1, y1], [x2, y2], ... ]
// or { data: [ [x1, y1], [x2, y2], ... ], label: "some label" }
var series = [];
var options = {
var series = [],
options = {
// the color theme used for graphs
colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"],
legend: {
show: true,
noColumns: 1, // number of colums in legend table
labelFormatter: null, // fn: string -> string
labelBoxBorderColor: "#ccc", // border color for the little label boxes
container: null, // container (as jQuery object) to put legend in, null means default on top of graph
position: "ne", // position of default legend container within plot
margin: 5, // distance from grid edge to default legend container within plot
backgroundColor: null, // null means auto-detect
backgroundOpacity: 0.85 // set to 0 to avoid background
},
xaxis: {
mode: null, // null or "time"
min: null, // min. value to show, null means set automatically
max: null, // max. value to show, null means set automatically
autoscaleMargin: null, // margin in % to add if auto-setting min/max
ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks
tickFormatter: null, // fn: number -> string
labelWidth: null, // size of tick labels in pixels
labelHeight: null,
// mode specific options
tickDecimals: null, // no. of decimals, null means auto
tickSize: null, // number or [number, "unit"]
minTickSize: null, // number or [number, "unit"]
monthNames: null, // list of names of months
timeformat: null // format string to use
},
yaxis: {
autoscaleMargin: 0.02
},
x2axis: {
autoscaleMargin: null
},
y2axis: {
autoscaleMargin: 0.02
},
points: {
show: false,
radius: 3,
lineWidth: 2, // in pixels
fill: true,
fillColor: "#ffffff"
},
lines: {
show: false,
lineWidth: 2, // in pixels
fill: false,
fillColor: null
},
bars: {
show: false,
lineWidth: 2, // in pixels
barWidth: 1, // in units of the x axis
fill: true,
fillColor: null,
align: "left" // or "center"
},
grid: {
color: "#545454", // primary color used for outline and labels
backgroundColor: null, // null for transparent, else color
tickColor: "#dddddd", // color used for the ticks
labelMargin: 5, // in pixels
borderWidth: 2,
coloredAreas: null, // array of { x1, y1, x2, y2 } or fn: plot area -> areas
coloredAreasColor: "#f4f4f4",
// interactive stuff
clickable: false,
hoverable: false,
autoHighlight: true, // highlight in case mouse is near
mouseActiveRadius: 10 // how far the mouse can be away to activate an item
},
selection: {
mode: null, // one of null, "x", "y" or "xy"
color: "#e8cfac"
legend: {
show: true,
noColumns: 1, // number of colums in legend table
labelFormatter: null, // fn: string -> string
labelBoxBorderColor: "#ccc", // border color for the little label boxes
container: null, // container (as jQuery object) to put legend in, null means default on top of graph
position: "ne", // position of default legend container within plot
margin: 5, // distance from grid edge to default legend container within plot
backgroundColor: null, // null means auto-detect
backgroundOpacity: 0.85 // set to 0 to avoid background
},
xaxis: {
mode: null, // null or "time"
min: null, // min. value to show, null means set automatically
max: null, // max. value to show, null means set automatically
autoscaleMargin: null, // margin in % to add if auto-setting min/max
ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks
tickFormatter: null, // fn: number -> string
labelWidth: null, // size of tick labels in pixels
labelHeight: null,
// mode specific options
tickDecimals: null, // no. of decimals, null means auto
tickSize: null, // number or [number, "unit"]
minTickSize: null, // number or [number, "unit"]
monthNames: null, // list of names of months
timeformat: null // format string to use
},
yaxis: {
autoscaleMargin: 0.02
},
x2axis: {
autoscaleMargin: null
},
y2axis: {
autoscaleMargin: 0.02
},
points: {
show: false,
radius: 3,
lineWidth: 2, // in pixels
fill: true,
fillColor: "#ffffff"
},
lines: {
show: false,
lineWidth: 2, // in pixels
fill: false,
fillColor: null
},
bars: {
show: false,
lineWidth: 2, // in pixels
barWidth: 1, // in units of the x axis
fill: true,
fillColor: null,
align: "left" // or "center"
},
grid: {
color: "#545454", // primary color used for outline and labels
backgroundColor: null, // null for transparent, else color
tickColor: "#dddddd", // color used for the ticks
labelMargin: 5, // in pixels
borderWidth: 2,
markings: null, // array of ranges or fn: axes -> array of ranges
markingsColor: "#f4f4f4",
markingsLineWidth: 2,
// interactive stuff
clickable: false,
hoverable: false,
autoHighlight: true, // highlight in case mouse is near
mouseActiveRadius: 10 // how far the mouse can be away to activate an item
},
selection: {
mode: null, // one of null, "x", "y" or "xy"
color: "#e8cfac"
},
shadowSize: 4
},
shadowSize: 4
};
var canvas = null, // the canvas for the plot itself
overlay = null, // canvas for interactive stuff on top of plot
eventHolder = null, // jQuery object that events should be bound to
ctx = null, octx = null,
target = target_,
xaxis = {}, yaxis = {},
x2axis = {}, y2axis = {},
plotOffset = { left: 0, right: 0, top: 0, bottom: 0},
canvasWidth = 0, canvasHeight = 0,
plotWidth = 0, plotHeight = 0,
// dedicated to storing data for buggy standard compliance cases
workarounds = {};
canvas = null, // the canvas for the plot itself
overlay = null, // canvas for interactive stuff on top of plot
eventHolder = null, // jQuery object that events should be bound to
ctx = null, octx = null,
target = target_,
axes = { xaxis: {}, yaxis: {}, x2axis: {}, y2axis: {} },
plotOffset = { left: 0, right: 0, top: 0, bottom: 0},
canvasWidth = 0, canvasHeight = 0,
plotWidth = 0, plotHeight = 0,
// dedicated to storing data for buggy standard compliance cases
workarounds = {};
this.setData = setData;
this.setupGrid = setupGrid;
......@@ -114,7 +114,7 @@
this.getCanvas = function() { return canvas; };
this.getPlotOffset = function() { return plotOffset; };
this.getData = function() { return series; };
this.getAxes = function() { return { xaxis: xaxis, yaxis: yaxis, x2axis: x2axis, y2axis: y2axis }; };
this.getAxes = function() { return axes; };
this.highlight = highlight;
this.unhighlight = unhighlight;
......@@ -159,6 +159,10 @@
options.xaxis.ticks = options.xaxis.noTicks;
if (options.yaxis.noTicks && options.yaxis.ticks == null)
options.yaxis.ticks = options.yaxis.noTicks;
if (options.grid.coloredAreas)
options.grid.markings = options.grid.coloredAreas;
if (options.grid.coloredAreasColor)
options.grid.markingsColor = options.grid.coloredAreasColor;
}
function fillInSeriesOptions() {
......@@ -231,24 +235,27 @@
if (s.shadowSize == null)
s.shadowSize = options.shadowSize;
if (s.xaxis && s.xaxis == 2)
s.xaxis = x2axis;
s.xaxis = axes.x2axis;
else
s.xaxis = xaxis;
s.xaxis = axes.xaxis;
if (s.yaxis && s.yaxis == 2)
s.yaxis = y2axis;
s.yaxis = axes.y2axis;
else
s.yaxis = yaxis;
s.yaxis = axes.yaxis;
}
}
function processData() {
var topSentry = Number.POSITIVE_INFINITY,
bottomSentry = Number.NEGATIVE_INFINITY;
xaxis.datamin = yaxis.datamin = x2axis.datamin = y2axis.datamin = topSentry;
xaxis.datamax = yaxis.datamax = x2axis.datamax = y2axis.datamax = bottomSentry;
xaxis.used = yaxis.used = x2axis.used = y2axis.used = false;
bottomSentry = Number.NEGATIVE_INFINITY,
axis;
for (axis in axes) {
axes[axis].datamin = topSentry;
axes[axis].datamax = bottomSentry;
axes[axis].used = false;
}
for (var i = 0; i < series.length; ++i) {
var data = series[i].data,
axisx = series[i].xaxis,
......@@ -288,17 +295,12 @@
}
}
function setDefaultMinMax(axis) {
if (axis.datamin == topSentry)
axis.datamin = 0;
if (axis.datamax == bottomSentry)
axis.datamax = 1;
for (axis in axes) {
if (axes[axis].datamin == topSentry)
axes[axis].datamin = 0;
if (axes[axis].datamax == bottomSentry)
axes[axis].datamax = 1;
}
setDefaultMinMax(xaxis);
setDefaultMinMax(yaxis);
setDefaultMinMax(x2axis);
setDefaultMinMax(y2axis);
}
function constructCanvas() {
......@@ -347,7 +349,7 @@
prepareTickGeneration(axis, options);
setTicks(axis, options);
// add transformation helpers
if (axis == xaxis || axis == x2axis) {
if (axis == axes.xaxis || axis == axes.x2axis) {
// data point to canvas coordinate
axis.p2c = function (p) { return (p - axis.min) * axis.scale; };
// canvas coordinate to data point
......@@ -359,10 +361,8 @@
}
}
setupAxis(xaxis, options.xaxis);
setupAxis(yaxis, options.yaxis);
setupAxis(x2axis, options.x2axis);
setupAxis(y2axis, options.y2axis);
for (var axis in axes)
setupAxis(axes[axis], options[axis]);
setSpacing();
insertLabels();
......@@ -411,7 +411,7 @@
var noTicks;
if (typeof axisOptions.ticks == "number" && axisOptions.ticks > 0)
noTicks = axisOptions.ticks;
else if (axis == xaxis || axis == x2axis)
else if (axis == axes.xaxis || axis == axes.x2axis)
noTicks = canvasWidth / 100;
else
noTicks = canvasHeight / 60;
......@@ -786,10 +786,10 @@
}
}
measureXLabels(xaxis);
measureYLabels(yaxis);
measureXLabels(x2axis);
measureYLabels(y2axis);
measureXLabels(axes.xaxis);
measureYLabels(axes.yaxis);
measureXLabels(axes.x2axis);
measureYLabels(axes.y2axis);
// get the most space needed around the grid for things
// that may stick out
......@@ -799,25 +799,25 @@
plotOffset.left = plotOffset.right = plotOffset.top = plotOffset.bottom = maxOutset;
if (xaxis.labelHeight > 0)
plotOffset.bottom = Math.max(maxOutset, xaxis.labelHeight + options.grid.labelMargin);
if (yaxis.labelWidth > 0)
plotOffset.left = Math.max(maxOutset, yaxis.labelWidth + options.grid.labelMargin);
if (axes.xaxis.labelHeight > 0)
plotOffset.bottom = Math.max(maxOutset, axes.xaxis.labelHeight + options.grid.labelMargin);
if (axes.yaxis.labelWidth > 0)
plotOffset.left = Math.max(maxOutset, axes.yaxis.labelWidth + options.grid.labelMargin);
if (x2axis.labelHeight > 0)
plotOffset.top = Math.max(maxOutset, x2axis.labelHeight + options.grid.labelMargin);
if (axes.x2axis.labelHeight > 0)
plotOffset.top = Math.max(maxOutset, axes.x2axis.labelHeight + options.grid.labelMargin);
if (y2axis.labelWidth > 0)
plotOffset.right = Math.max(maxOutset, y2axis.labelWidth + options.grid.labelMargin);
if (axes.y2axis.labelWidth > 0)
plotOffset.right = Math.max(maxOutset, axes.y2axis.labelWidth + options.grid.labelMargin);
plotWidth = canvasWidth - plotOffset.left - plotOffset.right;
plotHeight = canvasHeight - plotOffset.bottom - plotOffset.top;
// precompute how much the axis is scaling a point in canvas space
xaxis.scale = plotWidth / (xaxis.max - xaxis.min);
yaxis.scale = plotHeight / (yaxis.max - yaxis.min);
x2axis.scale = plotWidth / (x2axis.max - x2axis.min);
y2axis.scale = plotHeight / (y2axis.max - y2axis.min);
axes.xaxis.scale = plotWidth / (axes.xaxis.max - axes.xaxis.min);
axes.yaxis.scale = plotHeight / (axes.yaxis.max - axes.yaxis.min);
axes.x2axis.scale = plotWidth / (axes.x2axis.max - axes.x2axis.min);
axes.y2axis.scale = plotHeight / (axes.y2axis.max - axes.y2axis.min);
}
function draw() {
......@@ -827,6 +827,35 @@
}
}
function extractRange(ranges, coord) {
var firstAxis = coord + "axis",
secondaryAxis = coord + "2axis",
axis, from, to, reverse;
if (ranges[firstAxis]) {
axis = axes[firstAxis];
from = ranges[firstAxis].from;
to = ranges[firstAxis].to;
}
else if (ranges[secondaryAxis]) {
axis = axes[secondaryAxis];
from = ranges[secondaryAxis].from;
to = ranges[secondaryAxis].to;
}
else {
// backwards-compat stuff - to be removed in future
axis = axes[firstAxis];
from = ranges[coord + "1"];
to = ranges[coord + "2"];
}
// auto-reverse as an added bonus
if (from != null && to != null && from > to)
return { from: to, to: from, axis: axis };
return { from: from, to: to, axis: axis };
}
function drawGrid() {
var i;
......@@ -840,44 +869,63 @@
ctx.fillRect(0, 0, plotWidth, plotHeight);
}
// draw colored areas
if (options.grid.coloredAreas) {
var areas = options.grid.coloredAreas;
if ($.isFunction(areas))
areas = areas({ xmin: xaxis.min, xmax: xaxis.max, ymin: yaxis.min, ymax: yaxis.max });
for (i = 0; i < areas.length; ++i) {
var a = areas[i];
// draw markings
if (options.grid.markings) {
var markings = options.grid.markings;
if ($.isFunction(markings))
// xmin etc. are backwards-compatible, to be removed in future
markings = markings({ xmin: axes.xaxis.min, xmax: axes.xaxis.max, ymin: axes.yaxis.min, ymax: axes.yaxis.max, xaxis: axes.xaxis, yaxis: axes.yaxis, x2axis: axes.x2axis, y2axis: axes.y2axis });
for (i = 0; i < markings.length; ++i) {
var m = markings[i],
xrange = extractRange(m, "x"),
yrange = extractRange(m, "y");
// fill in missing
if (xrange.from == null)
xrange.from = xrange.axis.min;
if (xrange.to == null)
xrange.to = xrange.axis.max;
if (yrange.from == null)
yrange.from = yrange.axis.min;
if (yrange.to == null)
yrange.to = yrange.axis.max;
// clip
if (a.x1 == null || a.x1 < xaxis.min)
a.x1 = xaxis.min;
if (a.x2 == null || a.x2 > xaxis.max)
a.x2 = xaxis.max;
if (a.y1 == null || a.y1 < yaxis.min)
a.y1 = yaxis.min;
if (a.y2 == null || a.y2 > yaxis.max)
a.y2 = yaxis.max;
var tmp;
if (a.x1 > a.x2) {
tmp = a.x1;
a.x1 = a.x2;
a.x2 = tmp;
}
if (a.y1 > a.y2) {
tmp = a.y1;
a.y1 = a.y2;
a.y2 = tmp;
}
if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max ||
yrange.to < yrange.axis.min || yrange.from > yrange.axis.max)
continue;
xrange.from = Math.max(xrange.from, xrange.axis.min);
xrange.to = Math.min(xrange.to, xrange.axis.max);
yrange.from = Math.max(yrange.from, yrange.axis.min);
yrange.to = Math.min(yrange.to, yrange.axis.max);
if (a.x1 >= xaxis.max || a.x2 <= xaxis.min || a.x1 == a.x2
|| a.y1 >= yaxis.max || a.y2 <= yaxis.min || a.y1 == a.y2)
if (xrange.from == xrange.to && yrange.from == yrange.to)
continue;
ctx.fillStyle = a.color || options.grid.coloredAreasColor;
ctx.fillRect(Math.floor(xaxis.p2c(a.x1)), Math.floor(yaxis.p2c(a.y2)),
Math.floor(xaxis.p2c(a.x2) - xaxis.p2c(a.x1)), Math.floor(yaxis.p2c(a.y1) - yaxis.p2c(a.y2)));
// then draw
xrange.from = xrange.axis.p2c(xrange.from);
xrange.to = xrange.axis.p2c(xrange.to);
yrange.from = yrange.axis.p2c(yrange.from);
yrange.to = yrange.axis.p2c(yrange.to);
if (xrange.from == xrange.to || yrange.from == yrange.to) {
// draw line
ctx.strokeStyle = m.color || options.grid.markingsColor;
ctx.lineWidth = m.lineWidth || options.grid.markingsLineWidth;
ctx.moveTo(Math.floor(xrange.from), Math.floor(yrange.from));
ctx.lineTo(Math.floor(xrange.to), Math.floor(yrange.to));
ctx.stroke();
}
else {
// fill area
ctx.fillStyle = m.color || options.grid.markingsColor;
ctx.fillRect(Math.floor(xrange.from),
Math.floor(yrange.to),
Math.floor(xrange.to - xrange.from),
Math.floor(yrange.from - yrange.to));
}
}
}
......@@ -885,41 +933,44 @@
ctx.lineWidth = 1;
ctx.strokeStyle = options.grid.tickColor;
ctx.beginPath();
var v;
for (i = 0; i < xaxis.ticks.length; ++i) {
v = xaxis.ticks[i].v;
if (v <= xaxis.min || v >= xaxis.max)
var v, axis = axes.xaxis;
for (i = 0; i < axis.ticks.length; ++i) {
v = axis.ticks[i].v;
if (v <= axis.min || v >= axes.xaxis.max)
continue; // skip those lying on the axes
ctx.moveTo(Math.floor(xaxis.p2c(v)) + ctx.lineWidth/2, 0);
ctx.lineTo(Math.floor(xaxis.p2c(v)) + ctx.lineWidth/2, plotHeight);
ctx.moveTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, 0);
ctx.lineTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, plotHeight);
}
for (i = 0; i < yaxis.ticks.length; ++i) {
v = yaxis.ticks[i].v;
if (v <= yaxis.min || v >= yaxis.max)
axis = axes.yaxis;
for (i = 0; i < axis.ticks.length; ++i) {
v = axis.ticks[i].v;
if (v <= axis.min || v >= axis.max)
continue;
ctx.moveTo(0, Math.floor(yaxis.p2c(v)) + ctx.lineWidth/2);
ctx.lineTo(plotWidth, Math.floor(yaxis.p2c(v)) + ctx.lineWidth/2);
ctx.moveTo(0, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
ctx.lineTo(plotWidth, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
}
for (i = 0; i < x2axis.ticks.length; ++i) {
v = x2axis.ticks[i].v;
if (v <= x2axis.min || v >= x2axis.max)
axis = axes.x2axis
for (i = 0; i < axis.ticks.length; ++i) {
v = axis.ticks[i].v;
if (v <= axis.min || v >= axis.max)
continue;
ctx.moveTo(Math.floor(x2axis.p2c(v)) + ctx.lineWidth/2, -5);
ctx.lineTo(Math.floor(x2axis.p2c(v)) + ctx.lineWidth/2, 5);
ctx.moveTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, -5);
ctx.lineTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, 5);
}
for (i = 0; i < y2axis.ticks.length; ++i) {
v = y2axis.ticks[i].v;
if (v <= y2axis.min || v >= y2axis.max)
axis = axes.y2axis;
for (i = 0; i < axis.ticks.length; ++i) {
v = axis.ticks[i].v;
if (v <= axis.min || v >= axis.max)
continue;
ctx.moveTo(plotWidth-5, Math.floor(y2axis.p2c(v)) + ctx.lineWidth/2);
ctx.lineTo(plotWidth+5, Math.floor(y2axis.p2c(v)) + ctx.lineWidth/2);
ctx.moveTo(plotWidth-5, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
ctx.lineTo(plotWidth+5, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
}
ctx.stroke();
......@@ -938,40 +989,33 @@
function insertLabels() {
target.find(".tickLabels").remove();
var i, tick;
var html = '<div class="tickLabels" style="font-size:smaller;color:' + options.grid.color + '">';
// do the x-axis
for (i = 0; i < xaxis.ticks.length; ++i) {
tick = xaxis.ticks[i];
if (!tick.label || tick.v < xaxis.min || tick.v > xaxis.max)
continue;
html += '<div style="position:absolute;top:' + (plotOffset.top + plotHeight + options.grid.labelMargin) + 'px;left:' + (plotOffset.left + xaxis.p2c(tick.v) - xaxis.labelWidth/2) + 'px;width:' + xaxis.labelWidth + 'px;text-align:center" class="tickLabel">' + tick.label + "</div>";
function addLabels(axis, labelGenerator) {
for (var i = 0; i < axis.ticks.length; ++i) {
var tick = axis.ticks[i];
if (!tick.label || tick.v < axis.min || tick.v > axis.max)
continue;
html += labelGenerator(tick, axis);
}
}
// do the y-axis
for (i = 0; i < yaxis.ticks.length; ++i) {
tick = yaxis.ticks[i];
if (!tick.label || tick.v < yaxis.min || tick.v > yaxis.max)
continue;
html += '<div style="position:absolute;top:' + (plotOffset.top + yaxis.p2c(tick.v) - yaxis.labelHeight/2) + 'px;right:' + (plotOffset.right + plotWidth + options.grid.labelMargin) + 'px;width:' + yaxis.labelWidth + 'px;text-align:right" class="tickLabel">' + tick.label + "</div>";
}
addLabels(axes.xaxis, function (tick, axis) {
return '<div style="position:absolute;top:' + (plotOffset.top + plotHeight + options.grid.labelMargin) + 'px;left:' + (plotOffset.left + axis.p2c(tick.v) - axis.labelWidth/2) + 'px;width:' + axis.labelWidth + 'px;text-align:center" class="tickLabel">' + tick.label + "</div>";
});
// do the x2-axis
for (i = 0; i < x2axis.ticks.length; ++i) {
tick = x2axis.ticks[i];
if (!tick.label || tick.v < x2axis.min || tick.v > x2axis.max)
continue;
html += '<div style="position:absolute;bottom:' + (plotOffset.bottom + plotHeight + options.grid.labelMargin) + 'px;left:' + (plotOffset.left + x2axis.p2c(tick.v) - x2axis.labelWidth/2) + 'px;width:' + x2axis.labelWidth + 'px;text-align:center" class="tickLabel">' + tick.label + "</div>";
}
// do the y2-axis
for (i = 0; i < y2axis.ticks.length; ++i) {
tick = y2axis.ticks[i];
if (!tick.label || tick.v < y2axis.min || tick.v > y2axis.max)
continue;
html += '<div style="position:absolute;top:' + (plotOffset.top + y2axis.p2c(tick.v) - y2axis.labelHeight/2) + 'px;left:' + (plotOffset.left + plotWidth + options.grid.labelMargin) +'px;width:' + y2axis.labelWidth + 'px;text-align:left" class="tickLabel">' + tick.label + "</div>";
}
addLabels(axes.yaxis, function (tick, axis) {
return '<div style="position:absolute;top:' + (plotOffset.top + axis.p2c(tick.v) - axis.labelHeight/2) + 'px;right:' + (plotOffset.right + plotWidth + options.grid.labelMargin) + 'px;width:' + axis.labelWidth + 'px;text-align:right" class="tickLabel">' + tick.label + "</div>";
});
addLabels(axes.x2axis, function (tick, axis) {
return '<div style="position:absolute;bottom:' + (plotOffset.bottom + plotHeight + options.grid.labelMargin) + 'px;left:' + (plotOffset.left + axis.p2c(tick.v) - axis.labelWidth/2) + 'px;width:' + axis.labelWidth + 'px;text-align:center" class="tickLabel">' + tick.label + "</div>";
});
addLabels(axes.y2axis, function (tick, axis) {
return '<div style="position:absolute;top:' + (plotOffset.top + axis.p2c(tick.v) - axis.labelHeight/2) + 'px;left:' + (plotOffset.left + plotWidth + options.grid.labelMargin) +'px;width:' + axis.labelWidth + 'px;text-align:left" class="tickLabel">' + tick.label + "</div>";
});
html += '</div>';
......@@ -1637,14 +1681,14 @@
canvasX = event.pageX - offset.left - plotOffset.left,
canvasY = event.pageY - offset.top - plotOffset.top;
if (xaxis.used)
pos.x = xaxis.c2p(canvasX);
if (yaxis.used)
pos.y = yaxis.c2p(canvasY);
if (x2axis.used)
pos.x2 = x2axis.c2p(canvasX);
if (y2axis.used)
pos.y2 = y2axis.c2p(canvasY);
if (axes.xaxis.used)
pos.x = axes.xaxis.c2p(canvasX);
if (axes.yaxis.used)
pos.y = axes.yaxis.c2p(canvasY);
if (axes.x2axis.used)
pos.x2 = axes.x2axis.c2p(canvasX);
if (axes.y2axis.used)
pos.y2 = axes.y2axis.c2p(canvasY);
var item = findNearbyItem(canvasX, canvasY);
......@@ -1787,19 +1831,19 @@
y2 = Math.min(selection.first.y, selection.second.y);
var r = {};
if (xaxis.used)
r.xaxis = { from: xaxis.c2p(x1), to: xaxis.c2p(x2) };
if (x2axis.used)
r.x2axis = { from: x2axis.c2p(x1), to: x2axis.c2p(x2) };
if (yaxis.used)
r.yaxis = { from: yaxis.c2p(y1), to: yaxis.c2p(y2) };
if (y2axis.used)
r.yaxis = { from: y2axis.c2p(y1), to: y2axis.c2p(y2) };
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.yaxis = { from: axes.y2axis.c2p(y1), to: axes.y2axis.c2p(y2) };
target.trigger("plotselected", [ r ]);
// backwards-compat stuff, to be removed in future
if (xaxis.used && yaxis.used)
if (axes.xaxis.used && axes.yaxis.used)
target.trigger("selected", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]);
}
......@@ -1866,34 +1910,19 @@
triggerRedrawOverlay();
}
}
function setSelection(ranges) {
var axis, from, to;
function setSelection(ranges, preventEvent) {
var range;
if (options.selection.mode == "y") {
selection.first.x = 0;
selection.second.x = plotWidth;
}
else {
if (ranges.yaxis) {
axis = xaxis;
from = ranges.xaxis.from;
to = ranges.xaxis.to;
}
else if (ranges.x2axis) {
axis = x2axis;
from = ranges.x2axis.from;
to = ranges.x2axis.to;
}
else {
// backwards-compat stuff - to be removed in future
axis = xaxis;
from = ranges.x1;
to = ranges.x2;
}
selection.first.x = axis.p2c(from);
selection.second.x = axis.p2c(to);
range = extractRange(ranges, "x");
selection.first.x = range.axis.p2c(range.from);
selection.second.x = range.axis.p2c(range.to);
}
if (options.selection.mode == "x") {
......@@ -1901,30 +1930,16 @@
selection.second.y = plotHeight;
}
else {
if (ranges.yaxis) {
axis = yaxis;
from = ranges.yaxis.from;
to = ranges.yaxis.to;
}
else if (ranges.y2axis) {
axis = y2axis;
from = ranges.y2axis.from;
to = ranges.y2axis.to;
}
else {
// backwards-compat stuff - to be removed in future
axis = yaxis;
from = ranges.y1;
to = ranges.y2;
}
selection.first.y = axis.p2c(from);
selection.second.y = axis.p2c(to);
range = extractRange(ranges, "y");
selection.first.y = range.axis.p2c(range.from);
selection.second.y = range.axis.p2c(range.to);
}
selection.show = true;
triggerRedrawOverlay();
triggerSelectedEvent();
if (!preventEvent)
triggerSelectedEvent();
}
function selectionIsSane() {
......@@ -1951,6 +1966,15 @@
return base * Math.floor(n / base);
}
function clamp(min, value, max) {
if (value < min)
return value;
else if (value > max)
return max;
else
return value;
}
// color helpers, inspiration from the jquery color animation
// plugin by John Resig
function Color (r, g, b, a) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment