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