Commit 0d15280a authored by olau@iola.dk's avatar olau@iola.dk

Implemented plugin system, introduced experimental hooks system (three hooks...

Implemented plugin system, introduced experimental hooks system (three hooks defined at the moment), moved thresholding to plugin, new stack plugin for stacking charts, refactoring of data processing to support plugin writing, moved series specific global options, changed semantics of arguments to plothover event to reflect the situation with data transformations

git-svn-id: https://flot.googlecode.com/svn/trunk@157 1e0a6537-2640-0410-bfb7-f154510ff394
parent b1b1bde0
......@@ -40,7 +40,7 @@ E.g.
[ [1, 3], [2, 14.01], [3.5, 3.14] ]
Note that to simplify the internal logic in Flot both the x and y
values must be numbers, even if specifying time series (see below for
values must be numbers (even if specifying time series, see below for
how to do this). This is a common problem because you might retrieve
data from the database and serialize them directly to JSON without
noticing the wrong type. If you're getting mysterious errors, double
......@@ -49,7 +49,7 @@ check that you're inputting numbers and not strings.
If a null is specified as a point or if one of the coordinates is null
or couldn't be converted to a number, the point is ignored when
drawing. As a special case, a null value for lines is interpreted as a
line segment end, i.e. the point before and after the null value are
line segment end, i.e. the points before and after the null value are
not connected.
Lines and points take two coordinates. For bars, you can specify a
......@@ -64,7 +64,6 @@ The format of a single series object is as follows:
lines: specific lines options
bars: specific bars options
points: specific points options
threshold: specific threshold options
xaxis: 1 or 2
yaxis: 1 or 2
clickable: boolean
......@@ -100,7 +99,7 @@ E.g., you can use this to make a dual axis plot by specifying
"clickable" and "hoverable" can be set to false to disable
interactivity for specific series if interactivity is turned on in
plot, see below.
the plot, see below.
The rest of the options are all documented below as they are the same
as the default options passed in via the options parameter in the plot
......@@ -120,8 +119,10 @@ All options are completely optional. They are documented individually
below, to change them you just specify them in an object, e.g.
var options = {
series: {
lines: { show: true },
points: { show: true }
}
};
$.plot(placeholder, data, options);
......@@ -412,6 +413,7 @@ been produced with two days in-between.
Customizing the data series
===========================
series: {
lines, points, bars: {
show: boolean
lineWidth: number
......@@ -433,26 +435,29 @@ Customizing the data series
steps: boolean
}
colors: [ color1, color2, ... ]
shadowSize: number
threshold: {
below: number
color: color
}
colors: [ color1, color2, ... ]
The options inside "series: {}" are copied to each of the series. So
you can specify that all series should have bars by putting it in the
global options, or override it for individual series by specifying
bars in a particular the series object in the array of data.
The most important options are "lines", "points" and "bars" that
specifies whether and how lines, points and bars should be shown for
specify whether and how lines, points and bars should be shown for
each data series. In case you don't specify anything at all, Flot will
default to showing lines (you can turn this off with
lines: { show: false}). You can specify the various types
independently of each other, and Flot will happily draw each of them
in turn, e.g.
in turn (this is probably only useful for lines and points), e.g.
var options = {
series: {
lines: { show: true, fill: true, fillColor: "rgba(255, 255, 255, 0.8)" },
points: { show: true, fill: false }
}
};
"lineWidth" is the thickness of the line or outline in pixels. You can
......@@ -481,7 +486,11 @@ bars first.
For lines, "steps" specifies whether two adjacent data points are
connected with a straight (possibly diagonal) line or with first a
horizontal and then a vertical line.
horizontal and then a vertical line. Note that this transforms the
data by adding extra points.
"shadowSize" is the default size of shadows in pixels. Set it to 0 to
remove shadows.
The "colors" array specifies a default color theme to get colors for
the data series from. You can specify as many colors as you like, like
......@@ -492,13 +501,6 @@ this:
If there are more data series than colors, Flot will try to generate
extra colors by lightening and darkening colors in the theme.
"shadowSize" is the default size of shadows in pixels. Set it to 0 to
remove shadows.
"threshold" specifies that the data points below "below" should be
drawn with the specified color. This makes it easy to mark points
below 0, e.g. for budget data.
Customizing the grid
====================
......@@ -595,7 +597,7 @@ You can use "plotclick" and "plothover" events like this:
The item object in this example is either null or a nearby object on the form:
item: {
datapoint: the point as you specified it in the data, e.g. [0, 2]
datapoint: the point, e.g. [0, 2]
dataIndex: the index of the point in the data array
series: the series object
seriesIndex: the index of the series
......@@ -606,10 +608,12 @@ For instance, if you have specified the data like this
$.plot($("#placeholder"), [ { label: "Foo", data: [[0, 10], [7, 3]] } ], ...);
and the mouse is near the point (7, 3), "datapoint" is the [7, 3] we
specified, "dataIndex" will be 1, "series" is a normalized series
object with among other things the "Foo" label in series.label and the
color in series.color, and "seriesIndex" is 0.
and the mouse is near the point (7, 3), "datapoint" is [7, 3],
"dataIndex" will be 1, "series" is a normalized series object with
among other things the "Foo" label in series.label and the color in
series.color, and "seriesIndex" is 0. Note that plugins and options
that transform the data can shift the indexes from what you specified
in the original data array.
If you use the above events to update some other information and want
to clear out that info in case the mouse goes away, you'll probably
......@@ -746,12 +750,17 @@ can call:
Highlight a specific datapoint in the data series. You can either
specify the actual objects, e.g. if you got them from a
"plotclick" event, or you can specify the indices, e.g.
highlight(1, 3) to highlight the fourth point in the second series.
highlight(1, 3) to highlight the fourth point in the second series
(remember, zero-based indexing).
- unhighlight(series, datapoint) or unhighlight()
- unhighlight(series, datapoint)
Remove the highlighting of the point, same parameters as
highlight.
Remove the highlighting of the point, same parameters as highlight.
If you call unhighlight with no parameters, e.g. as
plot.unhighlight(), all current highlights are removed.
- setData(data)
......@@ -788,7 +797,7 @@ Flot to keep track of its state, so be careful.
- getData()
Returns an array of the data series currently used on normalized
Returns an array of the data series currently used in normalized
form with missing settings filled in according to the global
options. So for instance to find out what color Flot has assigned
to the data series, you could do this:
......@@ -797,6 +806,10 @@ Flot to keep track of its state, so be careful.
for (var i = 0; i < series.length; ++i)
alert(series[i].color);
A notable other interesting field besides color is datapoints
which has a field "points" with the normalized data points in a
flat array.
- getAxes()
......@@ -820,4 +833,100 @@ Flot to keep track of its state, so be careful.
placed at (left, top), its center will be at the top-most, left
corner of the grid.
- getOptions()
Gets the options for the plot, in a normalized format with default
values filled in.
Hooks
=====
In addition to the public methods, the Plot object also has some hooks
that can be used to modify the plotting process. You can install a
callback function at various points in the process, the function then
gets access to the internal data structures in Flot.
Here's an overview of the phases Flot goes through:
1. Plugin initialization, parsing options
2. Constructing the canvas used for drawing
3. Set data: parsing data specification, calculating colors,
copying raw data points into internal format,
normalizing them, finding max/min for axis auto-scaling
4. Grid setup: calculating axis spacing, ticks, inserting tick
labels, the legend
5. Draw: drawing the grid, drawing each of the series in turn
6. Setting up event handling for interactive features
7. Responding to events, if any
The hooks are simple arrays. They are available on the "hooks"
sub-object on the Plot object with the names mentioned below, e.g.
var plot = $.plot(...);
function f() { alert("hello!")};
plot.hooks.processDatapoints.push(f);
All hooks get the plot object as first parameter.
Currently available hooks (when in doubt, check the Flot source):
- processOptions [phase 1]
function(plot, options)
Called after Flot has parsed and merged options. Rarely useful, but
does allow customizations beyond simple merging of default values.
- processRawData [phase 3]
function(plot, series, data, datapoints)
Called before Flot copies and normalizes the raw data for the given
series. If the function fills in datapoints.points with normalized
points and sets datapoints.pointsize to the size of the points,
Flot will skip the copying/normalization step for this series.
- processDatapoints [phase 3]
function(plot, series, datapoints)
Called after normalization of the given series but before finding
min/max of the data points. This hook is useful for implementing data
transformations. "datapoints" contains the normalized data points in
a flat array as datapoints.points with the size of a single point
given in datapoints.pointsize. Here's a simple transform that
multiplies all y coordinates by 2:
function multiply(plot, series, datapoints) {
var points = datapoints.points, ps = datapoints.pointsize;
for (var i = 0; i < points.length; i += ps)
points[i + 1] *= 2;
}
Note that you must leave datapoints in a good condition as Flot
doesn't check it or do any normalization on it afterwards.
Plugins
-------
Plugins extend the functionality of Flot. To use a plugin, simply
include its Javascript file after Flot in the HTML page.
The plugin registers itself in the global array $.plot.plugins. When
you make a new plot object with $.plot, Flot goes through this array
calling the "init" function of each plugin and merging default options
from its "option" attribute. The init function gets a reference to the
plot object created and uses this to register hooks and add new public
methods if needed.
See the PLUGINS.txt file for details on how to write a plugin.
Flot 0.x
--------
API changes:
In the global options specified in the $.plot command,
"lines", "points", "bars" and "shadowSize" have been moved to a
sub-object called "series", i.e.
$.plot(placeholder, data, { lines: { show: true }})
becomes
$.plot(placeholder, data, { series: { lines: { show: true }}})
All future series-specific options will go into this sub-object (to
simplify plugin writing). Backward-compatibility hooks are in place,
so old code should not break.
"plothover" no longer provides the original data point, but instead a
normalized one, since there may be no corresponding original point.
Changes:
- Added support for disabling interactivity for specific data series
......@@ -8,10 +28,11 @@ Changes:
- Flot now calls $() on the placeholder and optional legend container
passed in so you can specify DOM elements or CSS expressions to make
it easier to use Flot with libraries like Prototype or Mootools.
it easier to use Flot with libraries like Prototype or Mootools or
through raw JSON from Ajax responses.
- A new "plotselecting" event is now emitted while the user is making
selection.
a selection.
- Added a new crosshairs feature for tracing the mouse position on the
axes, enable with crosshair { mode: "x"} (see the new tracking
......@@ -49,10 +70,6 @@ Changes:
set to true connects the points with horizontal/vertical steps
instead of diagonal lines.
- Thresholding: you can set a threshold and a color, and the data
points below that threshold will then get the color. Useful for
marking data below 0, for instance.
- The legend labelFormatter now passes the series in addition to just
the label (suggestion by Vincent Lemeltier).
......@@ -62,6 +79,22 @@ Changes:
don't have to start from the axis. This can be used to make stacked
bars.
- Plugin system: register an init method in the $.flot.plugins array
to get started, see PLUGINS.txt for details on how to write plugins.
- Hooks: you can register functions that are called while Flot is
crunching the data and doing the plot. This can be used to modify
Flot without changing the source, useful for writing plugins. At
this point only a few hooks are defined.
- Threshold plugin: you can set a threshold and a color, and the data
points below that threshold will then get the color. Useful for
marking data below 0, for instance.
- Stack plugin: you can specify a stack key for each series to have
them summed. This is useful for drawing additive/cumulative graphs
with bars and (currently unfilled) lines.
Bug fixes:
- Fixed two corner-case bugs when drawing filled curves (report and
......
Writing plugins
---------------
To make a new plugin, create an init function and a set of options (if
needed), stuff it into an object and put it in the $.plot.plugins
array. For example:
function myCoolPluginInit(plot) { plot.coolstring = "Hello!" };
var myCoolOptions = { coolstuff: { show: true } }
$.plot.plugins.push({ init: myCoolPluginInit, options: myCoolOptions });
// now when $.plot is called, the returned object will have the
// attribute "coolstring"
Now, given that the plugin might run in many different places, it's
a good idea to avoid leaking names. We can avoid this by wrapping the
above lines in an anonymous function which we call immediately, like
this: (function () { inner code ... })(). To make it even more robust
in case $ is not bound to jQuery but some other Javascript library, we
can write it as
(function ($) {
// plugin definition
// ...
})(jQuery);
Here is a simple debug plugin which alerts each of the series in the
plot. It has a single option that control whether it is enabled and
how much info to output:
(function ($) {
function init(plot) {
var debugLevel = 1;
function checkDebugEnabled(args) {
if (args.options.debug) {
debugLevel = args.options.debug;
plot.hooks.processDatapoints.push(alertSeries);
}
}
function alertSeries(args) {
var series = args.series;
var msg = "series " + series.label;
if (debugLevel > 1)
msg += " with " + series.data.length + " points";
alert(msg);
}
plot.hooks.processOptions.push(checkDebugEnabled);
}
var options = { debug: 0 };
$.plot.plugins.push({
init: init,
options: options,
name: "simpledebug",
version: "0.1"
});
})(jQuery);
We also define "name" and "version". It's not used by Flot, but might
be helpful for other plugins in resolving dependencies.
Put the above in a file named "jquery.flot.debug.js", include it in an
HTML page and then it can be used with:
$.plot($("#placeholder"), [...], { debug: 2 });
This simple plugin illustrates a couple of points:
- It uses the anonymous function trick to ensure no namespace pollution.
- It can be enabled/disabled through an option.
- Variables in the init function can be used to store plot-specific
state between the hooks.
Options guidelines
==================
Plugins should always support appropriate options to enable/disable
them because the plugin user may have several plots on the same page
where only one should use the plugin.
If the plugin needs series-specific options, you can put them in
"series" in the options object, e.g.
var options = {
series: {
downsample: {
algorithm: null,
maxpoints: 1000
}
}
}
Then they will be copied by Flot into each series, providing the
defaults in case the plugin user doesn't specify any. Again, in most
cases it's probably a good idea if the plugin is turned off rather
than on per default, just like most of the powerful features in Flot.
Think hard and long about naming the options. These names are going to
be public API, and code is going to depend on them if the plugin is
succesful.
......@@ -15,12 +15,26 @@
<ul>
<li><a href="basic.html">Basic example</a></li>
<li><a href="graph-types.html">Different graph types</a> and <a href="setting-options.html">setting various options</a></li>
<li><a href="turning-series.html">Turning series on/off</a> and <a href="thresholding.html">thresholding the data</a></li>
<li><a href="graph-types.html">Different graph types</a></li>
<li><a href="setting-options.html">Setting various options</a></li>
</ul>
<p>Being interactive:</p>
<ul>
<li><a href="turning-series.html">Turning series on/off</a></li>
<li><a href="selection.html">Selection support and zooming</a> and <a href="zooming.html">zooming with overview</a></li>
<li><a href="interacting.html">Interacting with the data points</a></li>
</ul>
<p>Some more esoteric features:</p>
<ul>
<li><a href="time.html">Plotting time series</a> and <a href="visitors.html">visitors per day with zooming and weekends</a></li>
<li><a href="dual-axis.html">Dual axis support</a></li>
<li><a href="interacting.html">Interacting with the data</a> and <a href="tracking.html">tracking curves with crosshair</a></li>
<li><a href="thresholding.html">Thresholding the data</a> (with plugin)</li>
<li><a href="stacking.html">Stacked charts</a> (with plugin)</li>
<li><a href="tracking.html">Tracking curves with crosshair</a></li>
</ul>
</body>
</html>
......@@ -33,9 +33,11 @@ $(function () {
}
var plot = $.plot($("#placeholder"),
[ { data: sin, label: "sin(x)"}, { data: cos, label: "cos(x)" } ],
{ lines: { show: true },
points: { show: true },
[ { data: sin, label: "sin(x)"}, { data: cos, label: "cos(x)" } ], {
series: {
lines: { show: true },
points: { show: true }
},
selection: { mode: "xy" },
grid: { hoverable: true, clickable: true },
yaxis: { min: -1.2, max: 1.2 }
......
......@@ -70,8 +70,10 @@ $(function () {
];
var options = {
series: {
lines: { show: true },
points: { show: true },
points: { show: true }
},
legend: { noColumns: 2 },
xaxis: { tickDecimals: 0 },
yaxis: { min: 0 },
......
......@@ -42,8 +42,10 @@ $(function () {
{ label: "cos(x)", data: d2},
{ label: "tan(x)", data: d3}
], {
series: {
lines: { show: true },
points: { show: true },
points: { show: true }
},
xaxis: {
ticks: [0, [Math.PI/2, "\u03c0/2"], [Math.PI, "\u03c0"], [Math.PI * 3/2, "3\u03c0/2"], [Math.PI * 2, "2\u03c0"]]
},
......
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Flot Examples</title>
<link href="layout.css" rel="stylesheet" type="text/css"></link>
<!--[if IE]><script language="javascript" type="text/javascript" src="../excanvas.min.js"></script><![endif]-->
<script language="javascript" type="text/javascript" src="../jquery.js"></script>
<script language="javascript" type="text/javascript" src="../jquery.flot.js"></script>
<script language="javascript" type="text/javascript" src="../jquery.flot.stack.js"></script>
</head>
<body>
<h1>Flot Examples</h1>
<div id="placeholder" style="width:600px;height:300px;"></div>
<p>With the stack plugin, you can have Flot stack the
series. This is useful if you wish to display both a total and the
constituents it is made of. The only requirement is that you provide
the input sorted on x.</p>
<p class="stackControls">
<input type="button" value="With stacking">
<input type="button" value="Without stacking">
</p>
<p class="graphControls">
<input type="button" value="Bars">
<input type="button" value="Lines">
<input type="button" value="Lines with steps">
</p>
<script id="source">
$(function () {
var d1 = [];
for (var i = 0; i <= 10; i += 1)
d1.push([i, parseInt(Math.random() * 30)]);
var d2 = [];
for (var i = 0; i <= 10; i += 1)
d2.push([i, parseInt(Math.random() * 30)]);
var d3 = [];
for (var i = 0; i <= 10; i += 1)
d3.push([i, parseInt(Math.random() * 30)]);
var stack = 0, bars = true, lines = false, steps = false;
function plotWithOptions() {
$.plot($("#placeholder"), [ d1, d2, d3 ], {
series: {
stack: stack,
lines: { show: lines, steps: steps },
bars: { show: bars, barWidth: 0.6 }
}
});
}
plotWithOptions();
$(".stackControls input").click(function (e) {
e.preventDefault();
stack = $(this).val() == "With stacking" ? true : null;
plotWithOptions();
});
$(".graphControls input").click(function (e) {
e.preventDefault();
bars = $(this).val().indexOf("Bars") != -1;
lines = $(this).val().indexOf("Lines") != -1;
steps = $(this).val().indexOf("steps") != -1;
plotWithOptions();
});
});
</script>
</body>
</html>
......@@ -7,15 +7,17 @@
<!--[if IE]><script language="javascript" type="text/javascript" src="../excanvas.min.js"></script><![endif]-->
<script language="javascript" type="text/javascript" src="../jquery.js"></script>
<script language="javascript" type="text/javascript" src="../jquery.flot.js"></script>
<script language="javascript" type="text/javascript" src="../jquery.flot.threshold.js"></script>
</head>
<body>
<h1>Flot Examples</h1>
<div id="placeholder" style="width:600px;height:300px;"></div>
<p>You can apply a specific color to the part of a data series
below a threshold. This is can be useful for highlighting negative
values, e.g. when displaying net results or what's in stock.</p>
<p>With the threshold plugin, you can apply a specific color to
the part of a data series below a threshold. This is can be useful
for highlighting negative values, e.g. when displaying net results
or what's in stock.</p>
<p class="controls">
<input type="button" value="Threshold at 5">
......@@ -29,7 +31,7 @@ $(function () {
for (var i = 0; i <= 60; i += 1)
d1.push([i, parseInt(Math.random() * 30 - 10)]);
function doPlot(t) {
function plotWithOptions(t) {
$.plot($("#placeholder"), [ {
data: d1,
color: "rgb(30, 180, 20)",
......@@ -38,12 +40,12 @@ $(function () {
} ]);
}
doPlot(0);
plotWithOptions(0);
$(".controls input").click(function (e) {
e.preventDefault();
var t = parseFloat($(this).val().replace('Threshold at ', ''));
doPlot(t);
plotWithOptions(t);
});
});
</script>
......
......@@ -34,7 +34,7 @@
to the timestamps or simply pretend that the data was produced
in UTC instead of your local time zone.</p>
<script id="source" language="javascript" type="text/javascript">
<script id="source">
$(function () {
var d = [[-373597200000, 315.71], [-370918800000, 317.45], [-368326800000, 317.50], [-363056400000, 315.86], [-360378000000, 314.93], [-357699600000, 313.19], [-352429200000, 313.34], [-349837200000, 314.67], [-347158800000, 315.58], [-344480400000, 316.47], [-342061200000, 316.65], [-339382800000, 317.71], [-336790800000, 318.29], [-334112400000, 318.16], [-331520400000, 316.55], [-328842000000, 314.80], [-326163600000, 313.84], [-323571600000, 313.34], [-320893200000, 314.81], [-318301200000, 315.59], [-315622800000, 316.43], [-312944400000, 316.97], [-310438800000, 317.58], [-307760400000, 319.03], [-305168400000, 320.03], [-302490000000, 319.59], [-299898000000, 318.18], [-297219600000, 315.91], [-294541200000, 314.16], [-291949200000, 313.83], [-289270800000, 315.00], [-286678800000, 316.19], [-284000400000, 316.89], [-281322000000, 317.70], [-278902800000, 318.54], [-276224400000, 319.48], [-273632400000, 320.58], [-270954000000, 319.78], [-268362000000, 318.58], [-265683600000, 316.79], [-263005200000, 314.99], [-260413200000, 315.31], [-257734800000, 316.10], [-255142800000, 317.01], [-252464400000, 317.94], [-249786000000, 318.56], [-247366800000, 319.69], [-244688400000, 320.58], [-242096400000, 321.01], [-239418000000, 320.61], [-236826000000, 319.61], [-234147600000, 317.40], [-231469200000, 316.26], [-228877200000, 315.42], [-226198800000, 316.69], [-223606800000, 317.69], [-220928400000, 318.74], [-218250000000, 319.08], [-215830800000, 319.86], [-213152400000, 321.39], [-210560400000, 322.24], [-207882000000, 321.47], [-205290000000, 319.74], [-202611600000, 317.77], [-199933200000, 316.21], [-197341200000, 315.99], [-194662800000, 317.07], [-192070800000, 318.36], [-189392400000, 319.57], [-178938000000, 322.23], [-176259600000, 321.89], [-173667600000, 320.44], [-170989200000, 318.70], [-168310800000, 316.70], [-165718800000, 316.87], [-163040400000, 317.68], [-160448400000, 318.71], [-157770000000, 319.44], [-155091600000, 320.44], [-152672400000, 320.89], [-149994000000, 322.13], [-147402000000, 322.16], [-144723600000, 321.87], [-142131600000, 321.21], [-139453200000, 318.87], [-136774800000, 317.81], [-134182800000, 317.30], [-131504400000, 318.87], [-128912400000, 319.42], [-126234000000, 320.62], [-123555600000, 321.59], [-121136400000, 322.39], [-118458000000, 323.70], [-115866000000, 324.07], [-113187600000, 323.75], [-110595600000, 322.40], [-107917200000, 320.37], [-105238800000, 318.64], [-102646800000, 318.10], [-99968400000, 319.79], [-97376400000, 321.03], [-94698000000, 322.33], [-92019600000, 322.50], [-89600400000, 323.04], [-86922000000, 324.42], [-84330000000, 325.00], [-81651600000, 324.09], [-79059600000, 322.55], [-76381200000, 320.92], [-73702800000, 319.26], [-71110800000, 319.39], [-68432400000, 320.72], [-65840400000, 321.96], [-63162000000, 322.57], [-60483600000, 323.15], [-57978000000, 323.89], [-55299600000, 325.02], [-52707600000, 325.57], [-50029200000, 325.36], [-47437200000, 324.14], [-44758800000, 322.11], [-42080400000, 320.33], [-39488400000, 320.25], [-36810000000, 321.32], [-34218000000, 322.90], [-31539600000, 324.00], [-28861200000, 324.42], [-26442000000, 325.64], [-23763600000, 326.66], [-21171600000, 327.38], [-18493200000, 326.70], [-15901200000, 325.89], [-13222800000, 323.67], [-10544400000, 322.38], [-7952400000, 321.78], [-5274000000, 322.85], [-2682000000, 324.12], [-3600000, 325.06], [2674800000, 325.98], [5094000000, 326.93], [7772400000, 328.13], [10364400000, 328.07], [13042800000, 327.66], [15634800000, 326.35], [18313200000, 324.69], [20991600000, 323.10], [23583600000, 323.07], [26262000000, 324.01], [28854000000, 325.13], [31532400000, 326.17], [34210800000, 326.68], [36630000000, 327.18], [39308400000, 327.78], [41900400000, 328.92], [44578800000, 328.57], [47170800000, 327.37], [49849200000, 325.43], [52527600000, 323.36], [55119600000, 323.56], [57798000000, 324.80], [60390000000, 326.01], [63068400000, 326.77], [65746800000, 327.63], [68252400000, 327.75], [70930800000, 329.72], [73522800000, 330.07], [76201200000, 329.09], [78793200000, 328.05], [81471600000, 326.32], [84150000000, 324.84], [86742000000, 325.20], [89420400000, 326.50], [92012400000, 327.55], [94690800000, 328.54], [97369200000, 329.56], [99788400000, 330.30], [102466800000, 331.50], [105058800000, 332.48], [107737200000, 332.07], [110329200000, 330.87], [113007600000, 329.31], [115686000000, 327.51], [118278000000, 327.18], [120956400000, 328.16], [123548400000, 328.64], [126226800000, 329.35], [128905200000, 330.71], [131324400000, 331.48], [134002800000, 332.65], [136594800000, 333.16], [139273200000, 332.06], [141865200000, 330.99], [144543600000, 329.17], [147222000000, 327.41], [149814000000, 327.20], [152492400000, 328.33], [155084400000, 329.50], [157762800000, 330.68], [160441200000, 331.41], [162860400000, 331.85], [165538800000, 333.29], [168130800000, 333.91], [170809200000, 333.40], [173401200000, 331.78], [176079600000, 329.88], [178758000000, 328.57], [181350000000, 328.46], [184028400000, 329.26], [189298800000, 331.71], [191977200000, 332.76], [194482800000, 333.48], [197161200000, 334.78], [199753200000, 334.78], [202431600000, 334.17], [205023600000, 332.78], [207702000000, 330.64], [210380400000, 328.95], [212972400000, 328.77], [215650800000, 330.23], [218242800000, 331.69], [220921200000, 332.70], [223599600000, 333.24], [226018800000, 334.96], [228697200000, 336.04], [231289200000, 336.82], [233967600000, 336.13], [236559600000, 334.73], [239238000000, 332.52], [241916400000, 331.19], [244508400000, 331.19], [247186800000, 332.35], [249778800000, 333.47], [252457200000, 335.11], [255135600000, 335.26], [257554800000, 336.60], [260233200000, 337.77], [262825200000, 338.00], [265503600000, 337.99], [268095600000, 336.48], [270774000000, 334.37], [273452400000, 332.27], [276044400000, 332.41], [278722800000, 333.76], [281314800000, 334.83], [283993200000, 336.21], [286671600000, 336.64], [289090800000, 338.12], [291769200000, 339.02], [294361200000, 339.02], [297039600000, 339.20], [299631600000, 337.58], [302310000000, 335.55], [304988400000, 333.89], [307580400000, 334.14], [310258800000, 335.26], [312850800000, 336.71], [315529200000, 337.81], [318207600000, 338.29], [320713200000, 340.04], [323391600000, 340.86], [325980000000, 341.47], [328658400000, 341.26], [331250400000, 339.29], [333928800000, 337.60], [336607200000, 336.12], [339202800000, 336.08], [341881200000, 337.22], [344473200000, 338.34], [347151600000, 339.36], [349830000000, 340.51], [352249200000, 341.57], [354924000000, 342.56], [357516000000, 343.01], [360194400000, 342.47], [362786400000, 340.71], [365464800000, 338.52], [368143200000, 336.96], [370738800000, 337.13], [373417200000, 338.58], [376009200000, 339.89], [378687600000, 340.93], [381366000000, 341.69], [383785200000, 342.69], [389052000000, 344.30], [391730400000, 343.43], [394322400000, 341.88], [397000800000, 339.89], [399679200000, 337.95], [402274800000, 338.10], [404953200000, 339.27], [407545200000, 340.67], [410223600000, 341.42], [412902000000, 342.68], [415321200000, 343.46], [417996000000, 345.10], [420588000000, 345.76], [423266400000, 345.36], [425858400000, 343.91], [428536800000, 342.05], [431215200000, 340.00], [433810800000, 340.12], [436489200000, 341.33], [439081200000, 342.94], [441759600000, 343.87], [444438000000, 344.60], [446943600000, 345.20], [452210400000, 347.36], [454888800000, 346.74], [457480800000, 345.41], [460159200000, 343.01], [462837600000, 341.23], [465433200000, 341.52], [468111600000, 342.86], [470703600000, 344.41], [473382000000, 345.09], [476060400000, 345.89], [478479600000, 347.49], [481154400000, 348.00], [483746400000, 348.75], [486424800000, 348.19], [489016800000, 346.54], [491695200000, 344.63], [494373600000, 343.03], [496969200000, 342.92], [499647600000, 344.24], [502239600000, 345.62], [504918000000, 346.43], [507596400000, 346.94], [510015600000, 347.88], [512690400000, 349.57], [515282400000, 350.35], [517960800000, 349.72], [520552800000, 347.78], [523231200000, 345.86], [525909600000, 344.84], [528505200000, 344.32], [531183600000, 345.67], [533775600000, 346.88], [536454000000, 348.19], [539132400000, 348.55], [541551600000, 349.52], [544226400000, 351.12], [546818400000, 351.84], [549496800000, 351.49], [552088800000, 349.82], [554767200000, 347.63], [557445600000, 346.38], [560041200000, 346.49], [562719600000, 347.75], [565311600000, 349.03], [567990000000, 350.20], [570668400000, 351.61], [573174000000, 352.22], [575848800000, 353.53], [578440800000, 354.14], [581119200000, 353.62], [583711200000, 352.53], [586389600000, 350.41], [589068000000, 348.84], [591663600000, 348.94], [594342000000, 350.04], [596934000000, 351.29], [599612400000, 352.72], [602290800000, 353.10], [604710000000, 353.65], [607384800000, 355.43], [609976800000, 355.70], [612655200000, 355.11], [615247200000, 353.79], [617925600000, 351.42], [620604000000, 349.81], [623199600000, 350.11], [625878000000, 351.26], [628470000000, 352.63], [631148400000, 353.64], [633826800000, 354.72], [636246000000, 355.49], [638920800000, 356.09], [641512800000, 357.08], [644191200000, 356.11], [646783200000, 354.70], [649461600000, 352.68], [652140000000, 351.05], [654735600000, 351.36], [657414000000, 352.81], [660006000000, 354.22], [662684400000, 354.85], [665362800000, 355.66], [667782000000, 357.04], [670456800000, 358.40], [673048800000, 359.00], [675727200000, 357.99], [678319200000, 356.00], [680997600000, 353.78], [683676000000, 352.20], [686271600000, 352.22], [688950000000, 353.70], [691542000000, 354.98], [694220400000, 356.09], [696898800000, 356.85], [699404400000, 357.73], [702079200000, 358.91], [704671200000, 359.45], [707349600000, 359.19], [709941600000, 356.72], [712620000000, 354.79], [715298400000, 352.79], [717894000000, 353.20], [720572400000, 354.15], [723164400000, 355.39], [725842800000, 356.77], [728521200000, 357.17], [730940400000, 358.26], [733615200000, 359.16], [736207200000, 360.07], [738885600000, 359.41], [741477600000, 357.44], [744156000000, 355.30], [746834400000, 353.87], [749430000000, 354.04], [752108400000, 355.27], [754700400000, 356.70], [757378800000, 358.00], [760057200000, 358.81], [762476400000, 359.68], [765151200000, 361.13], [767743200000, 361.48], [770421600000, 360.60], [773013600000, 359.20], [775692000000, 357.23], [778370400000, 355.42], [780966000000, 355.89], [783644400000, 357.41], [786236400000, 358.74], [788914800000, 359.73], [791593200000, 360.61], [794012400000, 361.58], [796687200000, 363.05], [799279200000, 363.62], [801957600000, 363.03], [804549600000, 361.55], [807228000000, 358.94], [809906400000, 357.93], [812502000000, 357.80], [815180400000, 359.22], [817772400000, 360.44], [820450800000, 361.83], [823129200000, 362.95], [825634800000, 363.91], [828309600000, 364.28], [830901600000, 364.94], [833580000000, 364.70], [836172000000, 363.31], [838850400000, 361.15], [841528800000, 359.40], [844120800000, 359.34], [846802800000, 360.62], [849394800000, 361.96], [852073200000, 362.81], [854751600000, 363.87], [857170800000, 364.25], [859845600000, 366.02], [862437600000, 366.46], [865116000000, 365.32], [867708000000, 364.07], [870386400000, 361.95], [873064800000, 360.06], [875656800000, 360.49], [878338800000, 362.19], [880930800000, 364.12], [883609200000, 364.99], [886287600000, 365.82], [888706800000, 366.95], [891381600000, 368.42], [893973600000, 369.33], [896652000000, 368.78], [899244000000, 367.59], [901922400000, 365.84], [904600800000, 363.83], [907192800000, 364.18], [909874800000, 365.34], [912466800000, 366.93], [915145200000, 367.94], [917823600000, 368.82], [920242800000, 369.46], [922917600000, 370.77], [925509600000, 370.66], [928188000000, 370.10], [930780000000, 369.08], [933458400000, 366.66], [936136800000, 364.60], [938728800000, 365.17], [941410800000, 366.51], [944002800000, 367.89], [946681200000, 369.04], [949359600000, 369.35], [951865200000, 370.38], [954540000000, 371.63], [957132000000, 371.32], [959810400000, 371.53], [962402400000, 369.75], [965080800000, 368.23], [967759200000, 366.87], [970351200000, 366.94], [973033200000, 368.27], [975625200000, 369.64], [978303600000, 370.46], [980982000000, 371.44], [983401200000, 372.37], [986076000000, 373.33], [988668000000, 373.77], [991346400000, 373.09], [993938400000, 371.51], [996616800000, 369.55], [999295200000, 368.12], [1001887200000, 368.38], [1004569200000, 369.66], [1007161200000, 371.11], [1009839600000, 372.36], [1012518000000, 373.09], [1014937200000, 373.81], [1017612000000, 374.93], [1020204000000, 375.58], [1022882400000, 375.44], [1025474400000, 373.86], [1028152800000, 371.77], [1030831200000, 370.73], [1033423200000, 370.50], [1036105200000, 372.18], [1038697200000, 373.70], [1041375600000, 374.92], [1044054000000, 375.62], [1046473200000, 376.51], [1049148000000, 377.75], [1051740000000, 378.54], [1054418400000, 378.20], [1057010400000, 376.68], [1059688800000, 374.43], [1062367200000, 373.11], [1064959200000, 373.10], [1067641200000, 374.77], [1070233200000, 375.97], [1072911600000, 377.03], [1075590000000, 377.87], [1078095600000, 378.88], [1080770400000, 380.42], [1083362400000, 380.62], [1086040800000, 379.70], [1088632800000, 377.43], [1091311200000, 376.32], [1093989600000, 374.19], [1096581600000, 374.47], [1099263600000, 376.15], [1101855600000, 377.51], [1104534000000, 378.43], [1107212400000, 379.70], [1109631600000, 380.92], [1112306400000, 382.18], [1114898400000, 382.45], [1117576800000, 382.14], [1120168800000, 380.60], [1122847200000, 378.64], [1125525600000, 376.73], [1128117600000, 376.84], [1130799600000, 378.29], [1133391600000, 380.06], [1136070000000, 381.40], [1138748400000, 382.20], [1141167600000, 382.66], [1143842400000, 384.69], [1146434400000, 384.94], [1149112800000, 384.01], [1151704800000, 382.14], [1154383200000, 380.31], [1157061600000, 378.81], [1159653600000, 379.03], [1162335600000, 380.17], [1164927600000, 381.85], [1167606000000, 382.94], [1170284400000, 383.86], [1172703600000, 384.49], [1175378400000, 386.37], [1177970400000, 386.54], [1180648800000, 385.98], [1183240800000, 384.36], [1185919200000, 381.85], [1188597600000, 380.74], [1191189600000, 381.15], [1193871600000, 382.38], [1196463600000, 383.94], [1199142000000, 385.44]];
......@@ -45,20 +45,24 @@ $(function () {
});
$("#nineties").click(function () {
$.plot($("#placeholder"), [d], { xaxis: {
$.plot($("#placeholder"), [d], {
xaxis: {
mode: "time",
min: (new Date("1990/01/01")).getTime(),
max: (new Date("2000/01/01")).getTime()
} });
}
});
});
$("#ninetynine").click(function () {
$.plot($("#placeholder"), [d], { xaxis: {
$.plot($("#placeholder"), [d], {
xaxis: {
mode: "time",
minTickSize: [1, "month"],
min: (new Date("1999/01/01")).getTime(),
max: (new Date("2000/01/01")).getTime()
} });
}
});
});
});
</script>
......
......@@ -18,7 +18,7 @@
<p>If you combine it with listening on hover events, you can use
it to track the intersection on the curves by interpolating
the data points.</p>
the data points (look at the legend).</p>
<p id="hoverdata"></p>
......@@ -31,8 +31,11 @@ $(function () {
}
var plot = $.plot($("#placeholder"),
[ { data: sin, label: "sin(x) = -0.00"}, { data: cos, label: "cos(x) = -0.00" } ],
{ lines: { show: true },
[ { data: sin, label: "sin(x) = -0.00"},
{ data: cos, label: "cos(x) = -0.00" } ], {
series: {
lines: { show: true }
},
crosshair: { mode: "x" },
grid: { hoverable: true, autoHighlight: false },
yaxis: { min: -1.2, max: 1.2 }
......
......@@ -18,7 +18,7 @@
<div id="overview" style="margin-left:50px;margin-top:20px;width:400px;height:50px"></div>
<script id="source" language="javascript" type="text/javascript">
<script id="source">
$(function () {
var d = [[1196463600000, 0], [1196550000000, 0], [1196636400000, 0], [1196722800000, 77], [1196809200000, 3636], [1196895600000, 3575], [1196982000000, 2736], [1197068400000, 1086], [1197154800000, 676], [1197241200000, 1205], [1197327600000, 906], [1197414000000, 710], [1197500400000, 639], [1197586800000, 540], [1197673200000, 435], [1197759600000, 301], [1197846000000, 575], [1197932400000, 481], [1198018800000, 591], [1198105200000, 608], [1198191600000, 459], [1198278000000, 234], [1198364400000, 1352], [1198450800000, 686], [1198537200000, 279], [1198623600000, 449], [1198710000000, 468], [1198796400000, 392], [1198882800000, 282], [1198969200000, 208], [1199055600000, 229], [1199142000000, 177], [1199228400000, 374], [1199314800000, 436], [1199401200000, 404], [1199487600000, 253], [1199574000000, 218], [1199660400000, 476], [1199746800000, 462], [1199833200000, 448], [1199919600000, 442], [1200006000000, 403], [1200092400000, 204], [1200178800000, 194], [1200265200000, 327], [1200351600000, 374], [1200438000000, 507], [1200524400000, 546], [1200610800000, 482], [1200697200000, 283], [1200783600000, 221], [1200870000000, 483], [1200956400000, 523], [1201042800000, 528], [1201129200000, 483], [1201215600000, 452], [1201302000000, 270], [1201388400000, 222], [1201474800000, 439], [1201561200000, 559], [1201647600000, 521], [1201734000000, 477], [1201820400000, 442], [1201906800000, 252], [1201993200000, 236], [1202079600000, 525], [1202166000000, 477], [1202252400000, 386], [1202338800000, 409], [1202425200000, 408], [1202511600000, 237], [1202598000000, 193], [1202684400000, 357], [1202770800000, 414], [1202857200000, 393], [1202943600000, 353], [1203030000000, 364], [1203116400000, 215], [1203202800000, 214], [1203289200000, 356], [1203375600000, 399], [1203462000000, 334], [1203548400000, 348], [1203634800000, 243], [1203721200000, 126], [1203807600000, 157], [1203894000000, 288]];
......@@ -39,7 +39,7 @@ $(function () {
d.setUTCHours(0);
var i = d.getTime();
do {
// when we don't set yaxis the rectangle automatically
// 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;
......@@ -57,8 +57,10 @@ $(function () {
var plot = $.plot($("#placeholder"), [d], options);
var overview = $.plot($("#overview"), [d], {
series: {
lines: { show: true, lineWidth: 1 },
shadowSize: 0,
shadowSize: 0
},
xaxis: { ticks: [], mode: "time" },
yaxis: { ticks: [], min: 0, autoscaleMargin: 0.1 },
selection: { mode: "x" }
......
......@@ -26,7 +26,7 @@
the small overview plot to the right has been connected to the large
plot. Try selecting a rectangle on either of them.</p>
<script id="source" language="javascript" type="text/javascript">
<script id="source">
$(function () {
// setup plot
function getData(x1, x2) {
......@@ -43,8 +43,10 @@ $(function () {
var options = {
legend: { show: false },
series: {
lines: { show: true },
points: { show: true },
points: { show: true }
},
yaxis: { ticks: 10 },
selection: { mode: "xy" }
};
......@@ -56,8 +58,10 @@ $(function () {
// setup overview
var overview = $.plot($("#overview"), startData, {
legend: { show: true, container: $("#overviewLegend") },
series: {
lines: { show: true, lineWidth: 1 },
shadowSize: 0,
shadowSize: 0
},
xaxis: { ticks: 4 },
yaxis: { ticks: 3, min: -2, max: 2 },
grid: { color: "#999" },
......
......@@ -5,11 +5,11 @@
*/
(function($) {
function Plot(target_, data_, options_) {
function Plot(target, data_, options_, plugins) {
// data is on the form:
// [ series1, series2 ... ]
// 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 = [],
options = {
......@@ -52,6 +52,7 @@
y2axis: {
autoscaleMargin: 0.02
},
series: {
points: {
show: false,
radius: 3,
......@@ -76,7 +77,8 @@
align: "left", // or "center"
horizontal: false // when horizontal, left is now top
},
threshold: null, // or { below: number, color: color spec}
shadowSize: 3
},
grid: {
color: "#545454", // primary color used for outline and labels
backgroundColor: null, // null for transparent, else color
......@@ -100,70 +102,71 @@
crosshair: {
mode: null, // one of null, "x", "y" or "xy",
color: "#aa0000"
},
shadowSize: 3
}
},
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,
hooks = {
processOptions: [],
processRawData: [],
processDatapoints: []
},
plot = this,
// dedicated to storing data for buggy standard compliance cases
workarounds = {};
this.setData = setData;
this.setupGrid = setupGrid;
this.draw = draw;
this.clearSelection = clearSelection;
this.setSelection = setSelection;
this.getCanvas = function() { return canvas; };
this.getPlotOffset = function() { return plotOffset; };
this.getData = function() { return series; };
this.getAxes = function() { return axes; };
this.setCrosshair = setCrosshair;
this.clearCrosshair = function () { setCrosshair(null); };
this.highlight = highlight;
this.unhighlight = unhighlight;
// public functions
plot.setData = setData;
plot.setupGrid = setupGrid;
plot.draw = draw;
plot.clearSelection = clearSelection;
plot.setSelection = setSelection;
plot.getCanvas = function() { return canvas; };
plot.getPlotOffset = function() { return plotOffset; };
plot.getData = function() { return series; };
plot.getAxes = function() { return axes; };
plot.getOptions = function() { return options; };
plot.setCrosshair = setCrosshair;
plot.clearCrosshair = function () { setCrosshair(null); };
plot.highlight = highlight;
plot.unhighlight = unhighlight;
// public attributes
plot.hooks = hooks;
// initialize
initPlugins(plot);
parseOptions(options_);
setData(data_);
constructCanvas();
setData(data_);
setupGrid();
draw();
bindEvents();
function setData(d) {
series = parseData(d);
fillInSeriesOptions();
processData();
function executeHooks(hook, args) {
args = [plot].concat(args);
for (var i = 0; i < hook.length; ++i)
hook[i].apply(this, args);
}
function parseData(d) {
var res = [];
for (var i = 0; i < d.length; ++i) {
var s;
if (d[i].data) {
s = {};
for (var v in d[i])
s[v] = d[i][v];
}
else {
s = { data: d[i] };
}
res.push(s);
function initPlugins() {
for (var i = 0; i < plugins.length; ++i) {
var p = plugins[i];
p.init(plot);
if (p.options)
$.extend(true, options, p.options);
}
return res;
}
function parseOptions(o) {
$.extend(true, options, o);
function parseOptions(opts) {
$.extend(true, options, opts);
if (options.grid.borderColor == null)
options.grid.borderColor = options.grid.color
// backwards compatibility, to be removed in future
......@@ -175,6 +178,43 @@
options.grid.markings = options.grid.coloredAreas;
if (options.grid.coloredAreasColor)
options.grid.markingsColor = options.grid.coloredAreasColor;
if (options.lines)
$.extend(true, options.series.lines, options.lines);
if (options.points)
$.extend(true, options.series.points, options.points);
if (options.bars)
$.extend(true, options.series.bars, options.bars);
if (options.shadowSize)
options.series.shadowSize = options.shadowSize;
executeHooks(hooks.processOptions, [options]);
}
function setData(d) {
series = parseData(d);
fillInSeriesOptions();
processData();
}
function parseData(d) {
var res = [];
for (var i = 0; i < d.length; ++i) {
var s = $.extend(true, {}, options.series);
if (d[i].data) {
s.data = d[i].data; // move the data instead of deep-copy
delete d[i].data;
$.extend(true, s, d[i]);
d[i].data = s.data;
}
else
s.data = d[i];
res.push(s);
}
return res;
}
function fillInSeriesOptions() {
......@@ -240,17 +280,11 @@
else if (typeof s.color == "number")
s.color = colors[s.color].toString();
// copy the rest
s.lines = $.extend(true, {}, options.lines, s.lines);
s.points = $.extend(true, {}, options.points, s.points);
s.bars = $.extend(true, {}, options.bars, s.bars);
// turn on lines automatically in case nothing is set
if (s.lines.show == null && !s.bars.show && !s.points.show)
s.lines.show = true;
if (s.shadowSize == null)
s.shadowSize = options.shadowSize;
// setup axes
if (!s.xaxis)
s.xaxis = axes.xaxis;
......@@ -266,17 +300,14 @@
s.yaxis = axes.yaxis;
else if (s.yaxis == 2)
s.yaxis = axes.y2axis;
if (!s.threshold)
s.threshold = options.threshold;
s.subseries = null;
}
}
function processData() {
var topSentry = Number.POSITIVE_INFINITY,
bottomSentry = Number.NEGATIVE_INFINITY,
axis, i, j, k, m, s;
i, j, k, m, length,
s, points, ps, x, y;
for (axis in axes) {
axes[axis].datamin = topSentry;
......@@ -286,182 +317,151 @@
axes[axis].used = false;
}
function updateAxis(axis, min, max) {
if (min < axis.datamin)
axis.datamin = min;
if (max > axis.datamax)
axis.datamax = max;
}
for (i = 0; i < series.length; ++i) {
s = series[i];
s.datapoints = { points: [], incr: 2 };
s.datapoints = { points: [] };
var data = s.data,
points = s.datapoints.points,
axisx = s.xaxis, axisy = s.yaxis,
xmin = topSentry, xmax = bottomSentry,
ymin = topSentry, ymax = bottomSentry,
x, y, p, incr, format = [];
executeHooks(hooks.processRawData, [ s, s.data, s.datapoints ]);
}
// first pass: clean and copy data
for (i = 0; i < series.length; ++i) {
s = series[i];
// determine the increment
if (s.datapoints.pointsize != null)
continue; // already filled in
var data = s.data, format = [], p;
// determine the point size
if (s.bars.show) {
s.datapoints.incr = 3;
s.datapoints.pointsize = 3;
format.push({ d: 0 });
}
else
s.datapoints.pointsize = 2;
/*
// examine data to find out how to copy
for (j = 0; j < data.length; ++j) {
}*/
ps = s.datapoints.pointsize;
points = s.datapoints.points;
axisx.used = axisy.used = true;
incr = s.datapoints.incr;
insertSteps = s.lines.show && s.lines.steps;
s.xaxis.used = s.yaxis.used = true;
for (j = k = 0; j < data.length; ++j, k += incr) {
for (j = k = 0; j < data.length; ++j, k += ps) {
p = data[j];
x = null;
y = null;
if (data[j] != null) {
if (p != null) {
x = p[0];
y = p[1];
}
// convert to number
if (x != null && !isNaN(x = +x)) {
if (x < xmin)
xmin = x;
if (x > xmax)
xmax = x
}
else
x = null;
y = x = null;
if (y != null && !isNaN(y = +y)) {
if (y < ymin)
ymin = y;
if (y > ymax)
ymax = y;
if (x != null) {
x = +x; // convert to number
if (isNaN(x))
x = null;
}
else
y = null;
if (x == null || y == null)
x = y = null; // make sure everything is cleared
for (m = 2; m < incr; ++m)
points[k + m] = p[m] == null ? format[m-2].d : p[m];
points[k + 1] = y;
points[k] = x;
if (y != null) {
y = +y; // convert to number
if (isNaN(y))
y = null;
}
if (s.bars.show) {
// make sure we got room for the bar on the dancing floor
var delta = s.bars.align == "left" ? 0 : -s.bars.barWidth/2;
if(s.bars.horizontal) {
ymin += delta;
ymax += delta + s.bars.barWidth;
}
else {
xmin += delta;
xmax += delta + s.bars.barWidth;
}
// check validity of point, making sure both are cleared
if (x == null && y != null) {
// extract min/max info before we whack
updateAxis(s.yaxis, y, y);
y = null;
}
axisx.datamin = Math.min(axisx.datamin, xmin);
axisx.datamax = Math.max(axisx.datamax, xmax);
axisy.datamin = Math.min(axisy.datamin, ymin);
axisy.datamax = Math.max(axisy.datamax, ymax);
if (y == null && x != null) {
updateAxis(s.xaxis, x, x);
x = null;
}
if (insertSteps && x != null && k > 0
&& points[k - ps] != null
&& points[k - ps] != x && points[k - ps + 1] != y) {
points[k + 1] = points[k - ps + 1];
points[k] = x;
// step charts
if (s.lines.show && s.lines.steps) {
p = [];
// copy, inserting extra points to make steps
for (j = k = 0; j < points.length; j += incr, k += incr) {
x = points[j];
y = points[j + 1];
if (j > 0
&& points[j - incr] != null
&& x != null
&& points[j - incr + 1] != y) {
p[k] = x;
p[k + 1] = points[j - incr + 1];
k += incr;
// copy the remainding from real point
for (m = 2; m < ps; ++m)
points[k + m] = p[m] == null ? format[m-2].d : p[m];
k += ps;
}
p[k] = x;
p[k + 1] = y;
for (m = 2; m < ps; ++m)
points[k + m] = p[m] == null ? format[m-2].d : p[m];
points[k] = x;
points[k + 1] = y;
}
s.datapoints.linespoints = p;
}
// possibly split data points because of threshold
if (s.threshold) {
var orig = $.extend({}, s), thresholded = $.extend({}, s);
orig.datapoints = { points: [], incr: incr };
thresholded.datapoints = { points: [], incr: incr };
thresholded.color = s.threshold.color;
var below = s.threshold.below,
origpoints = orig.datapoints.points,
threspoints = thresholded.datapoints.points;
for (i = 0; i < series.length; ++i) {
s = series[i];
// ordinary points
for (j = 0; j < points.length; j += incr) {
x = points[j];
y = points[j + 1];
executeHooks(hooks.processDatapoints, [ s, s.datapoints]);
}
if (y < below)
p = threspoints;
else
p = origpoints;
// second pass: find datamax/datamin for auto-scaling
for (i = 0; i < series.length; ++i) {
s = series[i];
points = s.datapoints.points,
ps = s.datapoints.pointsize;
p.push(x);
p.push(y);
var xmin = topSentry, ymin = topSentry,
xmax = bottomSentry, ymax = bottomSentry;
for (m = 2; m < incr; ++m)
p.push(points[j + m]);
}
for (j = 0; j < points.length; j += ps) {
x = points[j];
// possibly split lines
if (s.lines.show) {
var lp = s.datapoints.linespoints || points;
if (x == null)
continue;
origpoints = [];
threspoints = [];
p = origpoints;
if (x < xmin)
xmin = x;
if (x > xmax)
xmax = x;
for (j = 0; j < lp.length; j += incr) {
x = lp[j];
y = lp[j + 1];
y = points[j + 1];
var prevp = p;
if (y != null) {
if (y < below)
p = threspoints;
else
p = origpoints;
if (y < ymin)
ymin = y;
if (y > ymax)
ymax = y;
}
if (p != prevp && x != null && j > 0 && lp[j - incr] != null) {
// find intersection and add it to both
k = (x - lp[j - incr]) / (y - lp[j - incr + 1]) * (below - y) + x;
prevp.push(k);
prevp.push(below);
p.push(null); // start new segment
p.push(null);
p.push(k);
p.push(below);
if (s.bars.show) {
// make sure we got room for the bar on the dancing floor
var delta = s.bars.align == "left" ? 0 : -s.bars.barWidth/2;
if (s.bars.horizontal) {
ymin += delta;
ymax += delta + s.bars.barWidth;
}
p.push(x);
p.push(y);
else {
xmin += delta;
xmax += delta + s.bars.barWidth;
}
orig.datapoints.linespoints = origpoints
thresholded.datapoints.linespoints = threspoints;
}
s.subseries = [orig, thresholded];
}
updateAxis(s.xaxis, xmin, xmax);
updateAxis(s.yaxis, ymin, ymax);
}
}
......@@ -494,7 +494,9 @@
// overlay canvas for interactive features
overlay = $(makeCanvas(canvasWidth, canvasHeight)).css({ position: 'absolute', left: 0, top: 0 }).appendTo(target).get(0);
octx = overlay.getContext("2d");
}
function bindEvents() {
// we include the canvas in the event holder too, because IE 7
// sometimes has trouble with the stacking order
eventHolder = $([overlay, canvas]);
......@@ -967,14 +969,8 @@
function draw() {
drawGrid();
for (var i = 0; i < series.length; ++i) {
var s = series[i];
if (s.subseries)
for (var j = 0; j < s.subseries.length; ++j)
drawSeries(s.subseries[j]);
else
drawSeries(s);
}
for (var i = 0; i < series.length; ++i)
drawSeries(series[i]);
}
function extractRange(ranges, coord) {
......@@ -1187,13 +1183,13 @@
function drawSeriesLines(series) {
function plotLine(datapoints, xoffset, yoffset, axisx, axisy) {
var points = datapoints.linespoints || datapoints.points,
incr = datapoints.incr,
var points = datapoints.points,
ps = datapoints.pointsize,
prevx = null, prevy = null;
ctx.beginPath();
for (var i = incr; i < points.length; i += incr) {
var x1 = points[i - incr], y1 = points[i - incr + 1],
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)
......@@ -1267,13 +1263,13 @@
}
function plotLineArea(datapoints, axisx, axisy) {
var points = datapoints.linespoints || datapoints.points,
incr = datapoints.incr,
var points = datapoints.points,
ps = datapoints.pointsize,
bottom = Math.min(Math.max(0, axisy.min), axisy.max),
top, lastX = 0, areaOpen = false;
for (var i = incr; i < points.length; i += incr) {
var x1 = points[i - incr], y1 = points[i - incr + 1],
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 (areaOpen && x1 != null && x2 == null) {
......@@ -1434,9 +1430,9 @@
function drawSeriesPoints(series) {
function plotPoints(datapoints, radius, fillStyle, offset, circumference, axisx, axisy) {
var points = datapoints.points, incr = datapoints.incr;
var points = datapoints.points, ps = datapoints.pointsize;
for (var i = 0; i < points.length; i += incr) {
for (var i = 0; i < points.length; i += ps) {
var x = points[i], y = points[i + 1];
if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)
continue;
......@@ -1587,9 +1583,9 @@
function drawSeriesBars(series) {
function plotBars(datapoints, barLeft, barRight, offset, fillStyleCallback, axisx, axisy) {
var points = datapoints.points, incr = datapoints.incr;
var points = datapoints.points, ps = datapoints.pointsize;
for (var i = 0; i < points.length; i += incr) {
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);
......@@ -1706,7 +1702,7 @@
redrawTimeout = null,
hoverTimeout = null;
// Returns the data item the mouse is over, or null if none is found
// returns the data item the mouse is over, or null if none is found
function findNearbyItem(mouseX, mouseY, seriesFilter) {
var maxDistance = options.grid.mouseActiveRadius,
lowestDistance = maxDistance * maxDistance + 1,
......@@ -1720,14 +1716,14 @@
axisx = s.xaxis,
axisy = s.yaxis,
points = s.datapoints.points,
incr = s.datapoints.incr,
ps = s.datapoints.pointsize,
mx = axisx.c2p(mouseX), // precompute some stuff to make the loop faster
my = axisy.c2p(mouseY),
maxx = maxDistance / axisx.scale,
maxy = maxDistance / axisy.scale;
if (s.lines.show || s.points.show) {
for (j = 0; j < points.length; j += incr) {
for (j = 0; j < points.length; j += ps) {
var x = points[j], y = points[j + 1];
if (x == null)
continue;
......@@ -1745,7 +1741,7 @@
dist = dx * dx + dy * dy; // no idea in taking sqrt
if (dist < lowestDistance) {
lowestDistance = dist;
item = [i, j / incr];
item = [i, j / ps];
}
}
}
......@@ -1754,7 +1750,7 @@
var barLeft = s.bars.align == "left" ? 0 : -s.bars.barWidth/2,
barRight = barLeft + s.bars.barWidth;
for (j = 0; j < points.length; j += incr) {
for (j = 0; j < points.length; j += ps) {
var x = points[j], y = points[j + 1], b = points[j + 2];
if (x == null)
continue;
......@@ -1765,7 +1761,7 @@
my >= y + barLeft && my <= y + barRight) :
(mx >= x + barLeft && mx <= x + barRight &&
my >= Math.min(b, y) && my <= Math.max(b, y)))
item = [i, j / incr];
item = [i, j / ps];
}
}
}
......@@ -1774,10 +1770,10 @@
i = item[0];
j = item[1];
return { datapoint: series[i].data[j],
return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps),
dataIndex: j,
series: series[i],
seriesIndex: i }
seriesIndex: i };
}
return null;
......@@ -1989,6 +1985,11 @@
}
function unhighlight(s, point) {
if (s == null && point == null) {
highlights = [];
triggerRedrawOverlay();
}
if (typeof s == "number")
s = series[s];
......@@ -2203,7 +2204,7 @@
}
$.plot = function(target, data, options) {
var plot = new Plot(target, data, options);
var plot = new Plot($(target), data, options, $.plot.plugins);
/*var t0 = new Date();
var t1 = new Date();
var tstr = "time used (msecs): " + (t1.getTime() - t0.getTime())
......@@ -2214,6 +2215,8 @@
return plot;
};
$.plot.plugins = [];
// returns a string with the date d formatted according to fmt
$.plot.formatDate = function(d, fmt, monthNames) {
var leftPad = function(n) {
......
/*
Flot plugin for stacking data sets, i.e. putting them on top of each
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
specify the default stack, you can set
series: {
stack: null or true or key (number/string)
}
or specify it for a specific series
$.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. For bar charts, the second y value is
also adjusted.
*/
(function ($) {
var options = {
series: { stack: null } // or number/string
};
function init(plot) {
function findMatchingSeries(s, allseries) {
var res = null
for (var i = 0; i < allseries.length; ++i) {
if (s == allseries[i])
break;
if (allseries[i].stack == s.stack)
res = allseries[i];
}
return res;
}
function stackData(plot, s, datapoints) {
if (s.stack == null)
return;
var other = findMatchingSeries(s, plot.getData());
if (!other)
return;
var ps = datapoints.pointsize,
points = datapoints.points,
otherps = other.datapoints.pointsize,
otherpoints = other.datapoints.points,
newpoints = [],
px, py, intery, qx, qy,
withlines = s.lines.show, withbars = s.bars.show,
withsteps = withlines && s.lines.steps,
i = 0, j = 0, l;
while (true) {
if (i >= points.length)
break;
l = newpoints.length;
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 {
// cases where we actually got two points
px = points[i];
py = points[i + 1];
qx = otherpoints[j];
qy = otherpoints[j + 1];
if (px == qx) {
for (m = 0; m < ps; ++m)
newpoints.push(points[i + m]);
newpoints[l + 1] += 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 + qy)
for (m = 2; m < ps; ++m)
newpoints.push(points[i + m]);
}
j += otherps;
}
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 - ps] != null) {
intery = qy + (otherpoints[j - ps + 1] - qy) * (px - qx) / (otherpoints[j - ps] - qx);
newpoints[l + 1] += intery;
}
i += ps;
}
if (l != newpoints.length && withbars)
newpoints[l + 2] += qy;
}
// maintain the line steps invariant
if (withsteps && l != newpoints.length && l > 0
&& newpoints[l] != null
&& newpoints[l] != newpoints[l - ps]
&& newpoints[l + 1] != newpoints[l - ps + 1]) {
for (m = 0; m < ps; ++m)
newpoints[l + ps + m] = newpoints[l + m];
newpoints[l + 1] = newpoints[l - ps + 1];
}
}
datapoints.points = newpoints;
}
plot.hooks.processDatapoints.push(stackData);
}
$.plot.plugins.push({
init: init,
options: options,
name: 'stack',
version: '1.0'
});
})(jQuery);
/*
Flot plugin for thresholding data. Controlled through the option
"threshold" in either the global series options
series: {
threshold: {
below: number
color: colorspec
}
}
or in a specific series
$.plot($("#placeholder"), [{ data: [ ... ], threshold: { ... }}])
The data points below "below" are drawn with the specified color. This
makes it easy to mark points below 0, e.g. for budget data.
Internally, the plugin works by splitting the data into two series,
above and below the threshold. The extra series below the threshold
will have its label cleared and the special "originSeries" attribute
set to the original series. You may need to check for this in hover
events.
*/
(function ($) {
var options = {
series: { threshold: null } // or { below: number, color: color spec}
};
function init(plot) {
function thresholdData(plot, s, datapoints) {
if (!s.threshold)
return;
var ps = datapoints.pointsize, i, x, y, p, prevp,
thresholded = $.extend({}, s); // note: shallow copy
thresholded.datapoints = { points: [], pointsize: ps };
thresholded.label = null;
thresholded.color = s.threshold.color;
thresholded.threshold = null;
thresholded.originSeries = s;
thresholded.data = [];
var below = s.threshold.below,
origpoints = datapoints.points,
addCrossingPoints = s.lines.show;
threspoints = [];
newpoints = [];
for (i = 0; i < origpoints.length; i += ps) {
x = origpoints[i]
y = origpoints[i + 1];
prevp = p;
if (y < below)
p = threspoints;
else
p = newpoints;
if (addCrossingPoints && prevp != p && x != null
&& i > 0 && origpoints[i - ps] != null) {
var interx = (x - origpoints[i - ps]) / (y - origpoints[i - ps + 1]) * (below - y) + x;
prevp.push(interx);
prevp.push(below);
for (m = 2; m < ps; ++m)
prevp.push(origpoints[i + m]);
p.push(null); // start new segment
p.push(null);
for (m = 2; m < ps; ++m)
p.push(origpoints[i + m]);
p.push(interx);
p.push(below);
for (m = 2; m < ps; ++m)
p.push(origpoints[i + m]);
}
p.push(x);
p.push(y);
}
datapoints.points = newpoints;
thresholded.datapoints.points = threspoints;
if (thresholded.datapoints.points.length > 0)
plot.getData().push(thresholded);
// FIXME: there are probably some edge cases left in bars
}
plot.hooks.processDatapoints.push(thresholdData);
}
$.plot.plugins.push({
init: init,
options: options,
name: 'threshold',
version: '1.0'
});
})(jQuery);
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