Commit bad53abc authored by olau@iola.dk's avatar olau@iola.dk

Revamped internals to add support for extra axes, not just dual (sponsored by Flight Data Services)


git-svn-id: https://flot.googlecode.com/svn/trunk@251 1e0a6537-2640-0410-bfb7-f154510ff394
parent 6f969865
......@@ -15,8 +15,8 @@ you apply to the div, e.g. background images have been reported to be a
problem on IE 7.
The format of the data is documented below, as is the available
options. The "plot" object returned has some methods you can call.
These are documented separately below.
options. The plot object returned from the call has some methods you
can call. These are documented separately below.
Note that in general Flot gives no guarantees if you change any of the
objects you pass in to the plot function or get out of it since
......@@ -65,8 +65,8 @@ The format of a single series object is as follows:
lines: specific lines options
bars: specific bars options
points: specific points options
xaxis: 1 or 2
yaxis: 1 or 2
xaxis: number
yaxis: number
clickable: boolean
hoverable: boolean
shadowSize: number
......@@ -93,10 +93,9 @@ The latter is mostly useful if you let the user add and remove series,
in which case you can hard-code the color index to prevent the colors
from jumping around between the series.
The "xaxis" and "yaxis" options specify which axis to use, specify 2
to get the secondary axis (x axis at top or y axis to the right).
E.g., you can use this to make a dual axis plot by specifying
{ yaxis: 2 } for one data series.
The "xaxis" and "yaxis" options specify which axis to use. The axes
are numbered from 1 (default), so { yaxis: 2} means that the series
should be plotted against the second y axis.
"clickable" and "hoverable" can be set to false to disable
interactivity for specific series if interactivity is turned on in
......@@ -172,15 +171,13 @@ ignored. Note that Flot will overwrite the contents of the container.
Customizing the axes
====================
xaxis, yaxis, x2axis, y2axis: {
xaxis, yaxis: {
position: "bottom" or "top" or "left" or "right"
mode: null or "time"
min: null or number
max: null or number
autoscaleMargin: null or number
labelWidth: null or number
labelHeight: null or number
transform: null or fn: number -> number
inverseTransform: null or fn: number -> number
......@@ -189,11 +186,22 @@ Customizing the axes
minTickSize: number or array
tickFormatter: (fn: number, object -> string) or string
tickDecimals: null or number
labelWidth: null or number
labelHeight: null or number
tickLength: null or number
}
All axes have the same kind of options. The "mode" option
determines how the data is interpreted, the default of null means as
decimal numbers. Use "time" for time series data, see the next section.
All axes have the same kind of options. The following describes how to
configure one axis, see below for what to do if you've got more than
one x axis or y axis.
The "position" option specifies where the axis is placed, bottom or
top for x axes, left or right for y axes. The "mode" option determines
how the data is interpreted, the default of null means as decimal
numbers. Use "time" for time series data, see the time series data
section.
The options "min"/"max" are the precise minimum/maximum value on the
scale. If you don't specify either of them, a value will automatically
......@@ -207,10 +215,6 @@ specified, the plot will furthermore extend the axis end-point to the
nearest whole tick. The default value is "null" for the x axis and
0.02 for the y axis which seems appropriate for most cases.
"labelWidth" and "labelHeight" specifies a fixed size of the tick
labels in pixels. They're useful in case you need to align several
plots.
"transform" and "inverseTransform" are callbacks you can put in to
change the way the data is drawn. You can design a function to
compress or expand certain parts of the axis non-linearly, e.g.
......@@ -225,7 +229,7 @@ into a natural logarithm axis with the following code:
}
Note that for finding extrema, Flot assumes that the transform
function does not reorder values (monotonicity is assumed).
function does not reorder values (it should be monotone).
The inverseTransform is simply the inverse of the transform function
(so v == inverseTransform(transform(v)) for all relevant v). It is
......@@ -282,11 +286,10 @@ axis for trigonometric functions:
return res;
}
You can control how the ticks look like with "tickDecimals", the
number of decimals to display (default is auto-detected).
Alternatively, for ultimate control over how ticks look like you can
Alternatively, for ultimate control over how ticks are formatted you can
provide a function to "tickFormatter". The function is passed two
parameters, the tick value and an "axis" object with information, and
should return a string. The default formatter looks like this:
......@@ -310,6 +313,49 @@ an example of a custom formatter:
return val.toFixed(axis.tickDecimals) + " B";
}
"labelWidth" and "labelHeight" specifies a fixed size of the tick
labels in pixels. They're useful in case you need to align several
plots.
"tickLength" is the length of the tick lines in pixels. By default, the
innermost axes will have ticks that extend all across the plot, while
any extra axes use small ticks. A value of null means use the default,
while a number means small ticks of that length - set it to 0 to hide
the lines completely.
Multiple axes
=============
If you need more than one x axis or y axis, you need to specify for
each data series which axis they are to use, as described under the
format of the data series, e.g. { data: [...], yaxis: 2 } specifies
that a series should be plotted against the second y axis.
To actually configure that axis, you can't use the xaxis/yaxis options
directly - instead there are two arrays in the options:
xaxes: []
yaxes: []
Here's an example of configuring a single x axis and two y axes (we
can leave options of the first y axis empty as the defaults are fine):
{
xaxes: [ { position: "top" } ],
yaxes: [ { }, { position: "right", min: 20 } ]
}
The arrays get their default values from the xaxis/yaxis settings, so
say you want to have all y axes start at zero, you can simply specify
yaxis: { min: 0 } instead of adding a min parameter to all the axes.
Generally, the various interfaces in Flot dealing with data points
either accept an xaxis/yaxis parameter to specify which axis number to
use (starting from 1), or lets you specify the coordinate directly as
x2/x3/... or x2axis/x3axis/... instead of "x" or "xaxis".
Time series data
================
......@@ -547,6 +593,7 @@ Customizing the grid
backgroundColor: color/gradient or null
tickColor: color
labelMargin: number
axisMargin: number
markings: array of markings or (fn: axes -> array of markings)
borderWidth: number
borderColor: color or null
......@@ -566,19 +613,21 @@ You can turn off the whole grid including tick labels by setting
"show" to false. "aboveData" determines whether the grid is drawn
above the data or below (below is default).
"tickColor" is the color of the ticks and "labelMargin" is the spacing
between tick labels and the grid. Note that you can style the tick
labels with CSS, e.g. to change the color. They have class "tickLabel".
"borderWidth" is the width of the border around the plot. Set it to 0
to disable the border. You can also set "borderColor" if you want the
border to have a different color than the grid lines.
"tickColor" is the color of the ticks, "labelMargin" is the space in
pixels between tick labels and axis line, and "axisMargin" is the
space in pixels between axes when there are two next to each other.
Note that you can style the tick labels with CSS, e.g. to change the
color. They have class "tickLabel". "borderWidth" is the width of the
border around the plot. Set it to 0 to disable the border. You can
also set "borderColor" if you want the border to have a different
color than the grid lines.
"markings" is used to draw simple lines and rectangular areas in the
background of the plot. You can either specify an array of ranges on
the form { xaxis: { from, to }, yaxis: { from, to } } (secondary axis
coordinates with x2axis/y2axis) or with a function that returns such
an array given the axes for the plot in an object as the first
parameter.
the form { xaxis: { from, to }, yaxis: { from, to } } (with multiple
axes, you can specify coordinates for other axes instead, e.g. as
x2axis/x3axis/...) or with a function that returns such an array given
the axes for the plot in an object as the first parameter.
You can set the color of markings by specifying "color" in the ranges
object. Here's an example array:
......@@ -597,7 +646,7 @@ A line is drawn if from and to are the same, e.g.
would draw a line parallel to the x axis at y = 1. You can control the
line width with "lineWidth" in the range object.
An example function might look like this:
An example function that makes vertical stripes might look like this:
markings: function (axes) {
var markings = [];
......@@ -626,7 +675,7 @@ You can use "plotclick" and "plothover" events like this:
$("#placeholder").bind("plotclick", function (event, pos, item) {
alert("You clicked at " + pos.x + ", " + pos.y);
// secondary axis coordinates if present are in pos.x2, pos.y2,
// axis coordinates for other axes, if present, are in pos.x2, pos.x3, ...
// if you need global screen coordinates, they are pos.pageX, pos.pageY
if (item) {
......@@ -780,10 +829,10 @@ can call:
- pointOffset({ x: xpos, y: ypos })
Returns the calculated offset of the data point at (x, y) in data
space within the placeholder div. If you are working with dual axes, you
space within the placeholder div. If you are working with multiple axes, you
can specify the x and y axis references, e.g.
o = pointOffset({ x: xpos, y: ypos, xaxis: 2, yaxis: 2 })
o = pointOffset({ x: xpos, y: ypos, xaxis: 2, yaxis: 3 })
// o.left and o.top now contains the offset within the div
......@@ -811,14 +860,21 @@ Flot to keep track of its state, so be careful.
- getAxes()
Gets an object with the axes settings as { xaxis, yaxis, x2axis,
y2axis }.
Gets an object with the axes. The axes are returned as the
attributes of the object, so for instance getAxes().xaxis is the
x axis.
Various things are stuffed inside an axis object, e.g. you could
use getAxes().xaxis.ticks to find out what the ticks are for the
xaxis. Two other useful attributes are p2c and c2p, functions for
transforming from data point space to the canvas plot space and
back. Both returns values that are offset with the plot offset.
Check the Flot source code for the complete set of attributes (or
output an axis with console.log() and inspect it).
With multiple axes, the extra axes are returned as x2axis, x3axis,
etc., e.g. getAxes().y2axis is the second y axis. You can check
y2axis.used to see whether the axis is actually in use or not.
- getPlaceholder()
......
Flot x.x
--------
API changes:
Multiple axes support. Code using dual axes should be changed from
using x2axis/y2axis in the options to using an array. For instance,
{
xaxis: { ... }, x2axis: { ... },
yaxis: { ... }, y2axis: { ... }
}
becomes
{
xaxes: [ { ... }, { ... } ],
yaxes: [ { ... }, { ... } ]
}
Note that if you're just using one axis, continue to use the
xaxis/yaxis directly (it now sets the default settings for the
arrays). Plugins touching the axes must be ported to take the extra
axes into account, a couple of helper functions have been added for
that purpose, check the source.
Changes:
- Support for specifying a bottom for each point for line charts when
......@@ -15,9 +39,11 @@ Changes:
- Stacking plugin can stack horizontal bar charts.
- Navigate plugin now redraws the plot while panning instead of only
after the fact (can be disabled by setting the pan.frameRate option
to null). Issue 235.
to null), raised by lastthemy (issue 235).
- Date formatter now accepts %0m and %0d to get a zero-padded month or
day (issue raised by Maximillian Dornseif).
- Revamped internals to support an unlimited number of axes, not just
dual (sponsored by Flight Data Services, www.flightdataservices.com).
- New hooks: drawSeries
Bug fixes:
......
......@@ -33,7 +33,7 @@
<ul>
<li><a href="time.html">Plotting time series</a> and <a href="visitors.html">visitors per day with zooming and weekends</a> (with selection plugin)</li>
<li><a href="dual-axis.html">Dual axis support</a></li>
<li><a href="multiple-axes.html">Multiple axes</a></li>
<li><a href="thresholding.html">Thresholding the data</a> (with threshold plugin)</li>
<li><a href="stacking.html">Stacked charts</a> (with stacking plugin)</li>
<li><a href="percentiles.html">Using filled areas to plot percentiles</a> (with fillbetween plugin)</li>
......
......@@ -13,26 +13,42 @@
<div id="placeholder" style="width:600px;height:300px;"></div>
<p>Dual axis support showing the raw oil price in US $/barrel of
crude oil (left axis) vs. the exchange rate from US $ to € (right
axis).</p>
<p>Multiple axis support showing the raw oil price in US $/barrel of
crude oil vs. the exchange rate from US $ to €.</p>
<p>As illustrated, you can put in secondary y and x axes if you
need to. For each data series, simply specify the axis number.</p>
<p>As illustrated, you can put in multiple axes if you
need to. For each data series, simply specify the axis number.
In the options, you can then configure where you want the extra
axes to appear.</p>
<p>Position axis <button>left</button> or <button>right</button>.</p>
<script id="source" language="javascript" type="text/javascript">
$(function () {
var oilprices = [[1167692400000,61.05], [1167778800000,58.32], [1167865200000,57.35], [1167951600000,56.31], [1168210800000,55.55], [1168297200000,55.64], [1168383600000,54.02], [1168470000000,51.88], [1168556400000,52.99], [1168815600000,52.99], [1168902000000,51.21], [1168988400000,52.24], [1169074800000,50.48], [1169161200000,51.99], [1169420400000,51.13], [1169506800000,55.04], [1169593200000,55.37], [1169679600000,54.23], [1169766000000,55.42], [1170025200000,54.01], [1170111600000,56.97], [1170198000000,58.14], [1170284400000,58.14], [1170370800000,59.02], [1170630000000,58.74], [1170716400000,58.88], [1170802800000,57.71], [1170889200000,59.71], [1170975600000,59.89], [1171234800000,57.81], [1171321200000,59.06], [1171407600000,58.00], [1171494000000,57.99], [1171580400000,59.39], [1171839600000,59.39], [1171926000000,58.07], [1172012400000,60.07], [1172098800000,61.14], [1172444400000,61.39], [1172530800000,61.46], [1172617200000,61.79], [1172703600000,62.00], [1172790000000,60.07], [1173135600000,60.69], [1173222000000,61.82], [1173308400000,60.05], [1173654000000,58.91], [1173740400000,57.93], [1173826800000,58.16], [1173913200000,57.55], [1173999600000,57.11], [1174258800000,56.59], [1174345200000,59.61], [1174518000000,61.69], [1174604400000,62.28], [1174860000000,62.91], [1174946400000,62.93], [1175032800000,64.03], [1175119200000,66.03], [1175205600000,65.87], [1175464800000,64.64], [1175637600000,64.38], [1175724000000,64.28], [1175810400000,64.28], [1176069600000,61.51], [1176156000000,61.89], [1176242400000,62.01], [1176328800000,63.85], [1176415200000,63.63], [1176674400000,63.61], [1176760800000,63.10], [1176847200000,63.13], [1176933600000,61.83], [1177020000000,63.38], [1177279200000,64.58], [1177452000000,65.84], [1177538400000,65.06], [1177624800000,66.46], [1177884000000,64.40], [1178056800000,63.68], [1178143200000,63.19], [1178229600000,61.93], [1178488800000,61.47], [1178575200000,61.55], [1178748000000,61.81], [1178834400000,62.37], [1179093600000,62.46], [1179180000000,63.17], [1179266400000,62.55], [1179352800000,64.94], [1179698400000,66.27], [1179784800000,65.50], [1179871200000,65.77], [1179957600000,64.18], [1180044000000,65.20], [1180389600000,63.15], [1180476000000,63.49], [1180562400000,65.08], [1180908000000,66.30], [1180994400000,65.96], [1181167200000,66.93], [1181253600000,65.98], [1181599200000,65.35], [1181685600000,66.26], [1181858400000,68.00], [1182117600000,69.09], [1182204000000,69.10], [1182290400000,68.19], [1182376800000,68.19], [1182463200000,69.14], [1182722400000,68.19], [1182808800000,67.77], [1182895200000,68.97], [1182981600000,69.57], [1183068000000,70.68], [1183327200000,71.09], [1183413600000,70.92], [1183586400000,71.81], [1183672800000,72.81], [1183932000000,72.19], [1184018400000,72.56], [1184191200000,72.50], [1184277600000,74.15], [1184623200000,75.05], [1184796000000,75.92], [1184882400000,75.57], [1185141600000,74.89], [1185228000000,73.56], [1185314400000,75.57], [1185400800000,74.95], [1185487200000,76.83], [1185832800000,78.21], [1185919200000,76.53], [1186005600000,76.86], [1186092000000,76.00], [1186437600000,71.59], [1186696800000,71.47], [1186956000000,71.62], [1187042400000,71.00], [1187301600000,71.98], [1187560800000,71.12], [1187647200000,69.47], [1187733600000,69.26], [1187820000000,69.83], [1187906400000,71.09], [1188165600000,71.73], [1188338400000,73.36], [1188511200000,74.04], [1188856800000,76.30], [1189116000000,77.49], [1189461600000,78.23], [1189548000000,79.91], [1189634400000,80.09], [1189720800000,79.10], [1189980000000,80.57], [1190066400000,81.93], [1190239200000,83.32], [1190325600000,81.62], [1190584800000,80.95], [1190671200000,79.53], [1190757600000,80.30], [1190844000000,82.88], [1190930400000,81.66], [1191189600000,80.24], [1191276000000,80.05], [1191362400000,79.94], [1191448800000,81.44], [1191535200000,81.22], [1191794400000,79.02], [1191880800000,80.26], [1191967200000,80.30], [1192053600000,83.08], [1192140000000,83.69], [1192399200000,86.13], [1192485600000,87.61], [1192572000000,87.40], [1192658400000,89.47], [1192744800000,88.60], [1193004000000,87.56], [1193090400000,87.56], [1193176800000,87.10], [1193263200000,91.86], [1193612400000,93.53], [1193698800000,94.53], [1193871600000,95.93], [1194217200000,93.98], [1194303600000,96.37], [1194476400000,95.46], [1194562800000,96.32], [1195081200000,93.43], [1195167600000,95.10], [1195426800000,94.64], [1195513200000,95.10], [1196031600000,97.70], [1196118000000,94.42], [1196204400000,90.62], [1196290800000,91.01], [1196377200000,88.71], [1196636400000,88.32], [1196809200000,90.23], [1196982000000,88.28], [1197241200000,87.86], [1197327600000,90.02], [1197414000000,92.25], [1197586800000,90.63], [1197846000000,90.63], [1197932400000,90.49], [1198018800000,91.24], [1198105200000,91.06], [1198191600000,90.49], [1198710000000,96.62], [1198796400000,96.00], [1199142000000,99.62], [1199314800000,99.18], [1199401200000,95.09], [1199660400000,96.33], [1199833200000,95.67], [1200351600000,91.90], [1200438000000,90.84], [1200524400000,90.13], [1200610800000,90.57], [1200956400000,89.21], [1201042800000,86.99], [1201129200000,89.85], [1201474800000,90.99], [1201561200000,91.64], [1201647600000,92.33], [1201734000000,91.75], [1202079600000,90.02], [1202166000000,88.41], [1202252400000,87.14], [1202338800000,88.11], [1202425200000,91.77], [1202770800000,92.78], [1202857200000,93.27], [1202943600000,95.46], [1203030000000,95.46], [1203289200000,101.74], [1203462000000,98.81], [1203894000000,100.88], [1204066800000,99.64], [1204153200000,102.59], [1204239600000,101.84], [1204498800000,99.52], [1204585200000,99.52], [1204671600000,104.52], [1204758000000,105.47], [1204844400000,105.15], [1205103600000,108.75], [1205276400000,109.92], [1205362800000,110.33], [1205449200000,110.21], [1205708400000,105.68], [1205967600000,101.84], [1206313200000,100.86], [1206399600000,101.22], [1206486000000,105.90], [1206572400000,107.58], [1206658800000,105.62], [1206914400000,101.58], [1207000800000,100.98], [1207173600000,103.83], [1207260000000,106.23], [1207605600000,108.50], [1207778400000,110.11], [1207864800000,110.14], [1208210400000,113.79], [1208296800000,114.93], [1208383200000,114.86], [1208728800000,117.48], [1208815200000,118.30], [1208988000000,116.06], [1209074400000,118.52], [1209333600000,118.75], [1209420000000,113.46], [1209592800000,112.52], [1210024800000,121.84], [1210111200000,123.53], [1210197600000,123.69], [1210543200000,124.23], [1210629600000,125.80], [1210716000000,126.29], [1211148000000,127.05], [1211320800000,129.07], [1211493600000,132.19], [1211839200000,128.85], [1212357600000,127.76], [1212703200000,138.54], [1212962400000,136.80], [1213135200000,136.38], [1213308000000,134.86], [1213653600000,134.01], [1213740000000,136.68], [1213912800000,135.65], [1214172000000,134.62], [1214258400000,134.62], [1214344800000,134.62], [1214431200000,139.64], [1214517600000,140.21], [1214776800000,140.00], [1214863200000,140.97], [1214949600000,143.57], [1215036000000,145.29], [1215381600000,141.37], [1215468000000,136.04], [1215727200000,146.40], [1215986400000,145.18], [1216072800000,138.74], [1216159200000,134.60], [1216245600000,129.29], [1216332000000,130.65], [1216677600000,127.95], [1216850400000,127.95], [1217282400000,122.19], [1217455200000,124.08], [1217541600000,125.10], [1217800800000,121.41], [1217887200000,119.17], [1217973600000,118.58], [1218060000000,120.02], [1218405600000,114.45], [1218492000000,113.01], [1218578400000,116.00], [1218751200000,113.77], [1219010400000,112.87], [1219096800000,114.53], [1219269600000,114.98], [1219356000000,114.98], [1219701600000,116.27], [1219788000000,118.15], [1219874400000,115.59], [1219960800000,115.46], [1220306400000,109.71], [1220392800000,109.35], [1220565600000,106.23], [1220824800000,106.34]];
var exchangerates = [[1167606000000,0.7580], [1167692400000,0.7580], [1167778800000,0.75470], [1167865200000,0.75490], [1167951600000,0.76130], [1168038000000,0.76550], [1168124400000,0.76930], [1168210800000,0.76940], [1168297200000,0.76880], [1168383600000,0.76780], [1168470000000,0.77080], [1168556400000,0.77270], [1168642800000,0.77490], [1168729200000,0.77410], [1168815600000,0.77410], [1168902000000,0.77320], [1168988400000,0.77270], [1169074800000,0.77370], [1169161200000,0.77240], [1169247600000,0.77120], [1169334000000,0.7720], [1169420400000,0.77210], [1169506800000,0.77170], [1169593200000,0.77040], [1169679600000,0.7690], [1169766000000,0.77110], [1169852400000,0.7740], [1169938800000,0.77450], [1170025200000,0.77450], [1170111600000,0.7740], [1170198000000,0.77160], [1170284400000,0.77130], [1170370800000,0.76780], [1170457200000,0.76880], [1170543600000,0.77180], [1170630000000,0.77180], [1170716400000,0.77280], [1170802800000,0.77290], [1170889200000,0.76980], [1170975600000,0.76850], [1171062000000,0.76810], [1171148400000,0.7690], [1171234800000,0.7690], [1171321200000,0.76980], [1171407600000,0.76990], [1171494000000,0.76510], [1171580400000,0.76130], [1171666800000,0.76160], [1171753200000,0.76140], [1171839600000,0.76140], [1171926000000,0.76070], [1172012400000,0.76020], [1172098800000,0.76110], [1172185200000,0.76220], [1172271600000,0.76150], [1172358000000,0.75980], [1172444400000,0.75980], [1172530800000,0.75920], [1172617200000,0.75730], [1172703600000,0.75660], [1172790000000,0.75670], [1172876400000,0.75910], [1172962800000,0.75820], [1173049200000,0.75850], [1173135600000,0.76130], [1173222000000,0.76310], [1173308400000,0.76150], [1173394800000,0.760], [1173481200000,0.76130], [1173567600000,0.76270], [1173654000000,0.76270], [1173740400000,0.76080], [1173826800000,0.75830], [1173913200000,0.75750], [1173999600000,0.75620], [1174086000000,0.7520], [1174172400000,0.75120], [1174258800000,0.75120], [1174345200000,0.75170], [1174431600000,0.7520], [1174518000000,0.75110], [1174604400000,0.7480], [1174690800000,0.75090], [1174777200000,0.75310], [1174860000000,0.75310], [1174946400000,0.75270], [1175032800000,0.74980], [1175119200000,0.74930], [1175205600000,0.75040], [1175292000000,0.750], [1175378400000,0.74910], [1175464800000,0.74910], [1175551200000,0.74850], [1175637600000,0.74840], [1175724000000,0.74920], [1175810400000,0.74710], [1175896800000,0.74590], [1175983200000,0.74770], [1176069600000,0.74770], [1176156000000,0.74830], [1176242400000,0.74580], [1176328800000,0.74480], [1176415200000,0.7430], [1176501600000,0.73990], [1176588000000,0.73950], [1176674400000,0.73950], [1176760800000,0.73780], [1176847200000,0.73820], [1176933600000,0.73620], [1177020000000,0.73550], [1177106400000,0.73480], [1177192800000,0.73610], [1177279200000,0.73610], [1177365600000,0.73650], [1177452000000,0.73620], [1177538400000,0.73310], [1177624800000,0.73390], [1177711200000,0.73440], [1177797600000,0.73270], [1177884000000,0.73270], [1177970400000,0.73360], [1178056800000,0.73330], [1178143200000,0.73590], [1178229600000,0.73590], [1178316000000,0.73720], [1178402400000,0.7360], [1178488800000,0.7360], [1178575200000,0.7350], [1178661600000,0.73650], [1178748000000,0.73840], [1178834400000,0.73950], [1178920800000,0.74130], [1179007200000,0.73970], [1179093600000,0.73960], [1179180000000,0.73850], [1179266400000,0.73780], [1179352800000,0.73660], [1179439200000,0.740], [1179525600000,0.74110], [1179612000000,0.74060], [1179698400000,0.74050], [1179784800000,0.74140], [1179871200000,0.74310], [1179957600000,0.74310], [1180044000000,0.74380], [1180130400000,0.74430], [1180216800000,0.74430], [1180303200000,0.74430], [1180389600000,0.74340], [1180476000000,0.74290], [1180562400000,0.74420], [1180648800000,0.7440], [1180735200000,0.74390], [1180821600000,0.74370], [1180908000000,0.74370], [1180994400000,0.74290], [1181080800000,0.74030], [1181167200000,0.73990], [1181253600000,0.74180], [1181340000000,0.74680], [1181426400000,0.7480], [1181512800000,0.7480], [1181599200000,0.7490], [1181685600000,0.74940], [1181772000000,0.75220], [1181858400000,0.75150], [1181944800000,0.75020], [1182031200000,0.74720], [1182117600000,0.74720], [1182204000000,0.74620], [1182290400000,0.74550], [1182376800000,0.74490], [1182463200000,0.74670], [1182549600000,0.74580], [1182636000000,0.74270], [1182722400000,0.74270], [1182808800000,0.7430], [1182895200000,0.74290], [1182981600000,0.7440], [1183068000000,0.7430], [1183154400000,0.74220], [1183240800000,0.73880], [1183327200000,0.73880], [1183413600000,0.73690], [1183500000000,0.73450], [1183586400000,0.73450], [1183672800000,0.73450], [1183759200000,0.73520], [1183845600000,0.73410], [1183932000000,0.73410], [1184018400000,0.7340], [1184104800000,0.73240], [1184191200000,0.72720], [1184277600000,0.72640], [1184364000000,0.72550], [1184450400000,0.72580], [1184536800000,0.72580], [1184623200000,0.72560], [1184709600000,0.72570], [1184796000000,0.72470], [1184882400000,0.72430], [1184968800000,0.72440], [1185055200000,0.72350], [1185141600000,0.72350], [1185228000000,0.72350], [1185314400000,0.72350], [1185400800000,0.72620], [1185487200000,0.72880], [1185573600000,0.73010], [1185660000000,0.73370], [1185746400000,0.73370], [1185832800000,0.73240], [1185919200000,0.72970], [1186005600000,0.73170], [1186092000000,0.73150], [1186178400000,0.72880], [1186264800000,0.72630], [1186351200000,0.72630], [1186437600000,0.72420], [1186524000000,0.72530], [1186610400000,0.72640], [1186696800000,0.7270], [1186783200000,0.73120], [1186869600000,0.73050], [1186956000000,0.73050], [1187042400000,0.73180], [1187128800000,0.73580], [1187215200000,0.74090], [1187301600000,0.74540], [1187388000000,0.74370], [1187474400000,0.74240], [1187560800000,0.74240], [1187647200000,0.74150], [1187733600000,0.74190], [1187820000000,0.74140], [1187906400000,0.73770], [1187992800000,0.73550], [1188079200000,0.73150], [1188165600000,0.73150], [1188252000000,0.7320], [1188338400000,0.73320], [1188424800000,0.73460], [1188511200000,0.73280], [1188597600000,0.73230], [1188684000000,0.7340], [1188770400000,0.7340], [1188856800000,0.73360], [1188943200000,0.73510], [1189029600000,0.73460], [1189116000000,0.73210], [1189202400000,0.72940], [1189288800000,0.72660], [1189375200000,0.72660], [1189461600000,0.72540], [1189548000000,0.72420], [1189634400000,0.72130], [1189720800000,0.71970], [1189807200000,0.72090], [1189893600000,0.7210], [1189980000000,0.7210], [1190066400000,0.7210], [1190152800000,0.72090], [1190239200000,0.71590], [1190325600000,0.71330], [1190412000000,0.71050], [1190498400000,0.70990], [1190584800000,0.70990], [1190671200000,0.70930], [1190757600000,0.70930], [1190844000000,0.70760], [1190930400000,0.7070], [1191016800000,0.70490], [1191103200000,0.70120], [1191189600000,0.70110], [1191276000000,0.70190], [1191362400000,0.70460], [1191448800000,0.70630], [1191535200000,0.70890], [1191621600000,0.70770], [1191708000000,0.70770], [1191794400000,0.70770], [1191880800000,0.70910], [1191967200000,0.71180], [1192053600000,0.70790], [1192140000000,0.70530], [1192226400000,0.7050], [1192312800000,0.70550], [1192399200000,0.70550], [1192485600000,0.70450], [1192572000000,0.70510], [1192658400000,0.70510], [1192744800000,0.70170], [1192831200000,0.70], [1192917600000,0.69950], [1193004000000,0.69940], [1193090400000,0.70140], [1193176800000,0.70360], [1193263200000,0.70210], [1193349600000,0.70020], [1193436000000,0.69670], [1193522400000,0.6950], [1193612400000,0.6950], [1193698800000,0.69390], [1193785200000,0.6940], [1193871600000,0.69220], [1193958000000,0.69190], [1194044400000,0.69140], [1194130800000,0.68940], [1194217200000,0.68910], [1194303600000,0.69040], [1194390000000,0.6890], [1194476400000,0.68340], [1194562800000,0.68230], [1194649200000,0.68070], [1194735600000,0.68150], [1194822000000,0.68150], [1194908400000,0.68470], [1194994800000,0.68590], [1195081200000,0.68220], [1195167600000,0.68270], [1195254000000,0.68370], [1195340400000,0.68230], [1195426800000,0.68220], [1195513200000,0.68220], [1195599600000,0.67920], [1195686000000,0.67460], [1195772400000,0.67350], [1195858800000,0.67310], [1195945200000,0.67420], [1196031600000,0.67440], [1196118000000,0.67390], [1196204400000,0.67310], [1196290800000,0.67610], [1196377200000,0.67610], [1196463600000,0.67850], [1196550000000,0.68180], [1196636400000,0.68360], [1196722800000,0.68230], [1196809200000,0.68050], [1196895600000,0.67930], [1196982000000,0.68490], [1197068400000,0.68330], [1197154800000,0.68250], [1197241200000,0.68250], [1197327600000,0.68160], [1197414000000,0.67990], [1197500400000,0.68130], [1197586800000,0.68090], [1197673200000,0.68680], [1197759600000,0.69330], [1197846000000,0.69330], [1197932400000,0.69450], [1198018800000,0.69440], [1198105200000,0.69460], [1198191600000,0.69640], [1198278000000,0.69650], [1198364400000,0.69560], [1198450800000,0.69560], [1198537200000,0.6950], [1198623600000,0.69480], [1198710000000,0.69280], [1198796400000,0.68870], [1198882800000,0.68240], [1198969200000,0.67940], [1199055600000,0.67940], [1199142000000,0.68030], [1199228400000,0.68550], [1199314800000,0.68240], [1199401200000,0.67910], [1199487600000,0.67830], [1199574000000,0.67850], [1199660400000,0.67850], [1199746800000,0.67970], [1199833200000,0.680], [1199919600000,0.68030], [1200006000000,0.68050], [1200092400000,0.6760], [1200178800000,0.6770], [1200265200000,0.6770], [1200351600000,0.67360], [1200438000000,0.67260], [1200524400000,0.67640], [1200610800000,0.68210], [1200697200000,0.68310], [1200783600000,0.68420], [1200870000000,0.68420], [1200956400000,0.68870], [1201042800000,0.69030], [1201129200000,0.68480], [1201215600000,0.68240], [1201302000000,0.67880], [1201388400000,0.68140], [1201474800000,0.68140], [1201561200000,0.67970], [1201647600000,0.67690], [1201734000000,0.67650], [1201820400000,0.67330], [1201906800000,0.67290], [1201993200000,0.67580], [1202079600000,0.67580], [1202166000000,0.6750], [1202252400000,0.6780], [1202338800000,0.68330], [1202425200000,0.68560], [1202511600000,0.69030], [1202598000000,0.68960], [1202684400000,0.68960], [1202770800000,0.68820], [1202857200000,0.68790], [1202943600000,0.68620], [1203030000000,0.68520], [1203116400000,0.68230], [1203202800000,0.68130], [1203289200000,0.68130], [1203375600000,0.68220], [1203462000000,0.68020], [1203548400000,0.68020], [1203634800000,0.67840], [1203721200000,0.67480], [1203807600000,0.67470], [1203894000000,0.67470], [1203980400000,0.67480], [1204066800000,0.67330], [1204153200000,0.6650], [1204239600000,0.66110], [1204326000000,0.65830], [1204412400000,0.6590], [1204498800000,0.6590], [1204585200000,0.65810], [1204671600000,0.65780], [1204758000000,0.65740], [1204844400000,0.65320], [1204930800000,0.65020], [1205017200000,0.65140], [1205103600000,0.65140], [1205190000000,0.65070], [1205276400000,0.6510], [1205362800000,0.64890], [1205449200000,0.64240], [1205535600000,0.64060], [1205622000000,0.63820], [1205708400000,0.63820], [1205794800000,0.63410], [1205881200000,0.63440], [1205967600000,0.63780], [1206054000000,0.64390], [1206140400000,0.64780], [1206226800000,0.64810], [1206313200000,0.64810], [1206399600000,0.64940], [1206486000000,0.64380], [1206572400000,0.63770], [1206658800000,0.63290], [1206745200000,0.63360], [1206831600000,0.63330], [1206914400000,0.63330], [1207000800000,0.6330], [1207087200000,0.63710], [1207173600000,0.64030], [1207260000000,0.63960], [1207346400000,0.63640], [1207432800000,0.63560], [1207519200000,0.63560], [1207605600000,0.63680], [1207692000000,0.63570], [1207778400000,0.63540], [1207864800000,0.6320], [1207951200000,0.63320], [1208037600000,0.63280], [1208124000000,0.63310], [1208210400000,0.63420], [1208296800000,0.63210], [1208383200000,0.63020], [1208469600000,0.62780], [1208556000000,0.63080], [1208642400000,0.63240], [1208728800000,0.63240], [1208815200000,0.63070], [1208901600000,0.62770], [1208988000000,0.62690], [1209074400000,0.63350], [1209160800000,0.63920], [1209247200000,0.640], [1209333600000,0.64010], [1209420000000,0.63960], [1209506400000,0.64070], [1209592800000,0.64230], [1209679200000,0.64290], [1209765600000,0.64720], [1209852000000,0.64850], [1209938400000,0.64860], [1210024800000,0.64670], [1210111200000,0.64440], [1210197600000,0.64670], [1210284000000,0.65090], [1210370400000,0.64780], [1210456800000,0.64610], [1210543200000,0.64610], [1210629600000,0.64680], [1210716000000,0.64490], [1210802400000,0.6470], [1210888800000,0.64610], [1210975200000,0.64520], [1211061600000,0.64220], [1211148000000,0.64220], [1211234400000,0.64250], [1211320800000,0.64140], [1211407200000,0.63660], [1211493600000,0.63460], [1211580000000,0.6350], [1211666400000,0.63460], [1211752800000,0.63460], [1211839200000,0.63430], [1211925600000,0.63460], [1212012000000,0.63790], [1212098400000,0.64160], [1212184800000,0.64420], [1212271200000,0.64310], [1212357600000,0.64310], [1212444000000,0.64350], [1212530400000,0.6440], [1212616800000,0.64730], [1212703200000,0.64690], [1212789600000,0.63860], [1212876000000,0.63560], [1212962400000,0.6340], [1213048800000,0.63460], [1213135200000,0.6430], [1213221600000,0.64520], [1213308000000,0.64670], [1213394400000,0.65060], [1213480800000,0.65040], [1213567200000,0.65030], [1213653600000,0.64810], [1213740000000,0.64510], [1213826400000,0.6450], [1213912800000,0.64410], [1213999200000,0.64140], [1214085600000,0.64090], [1214172000000,0.64090], [1214258400000,0.64280], [1214344800000,0.64310], [1214431200000,0.64180], [1214517600000,0.63710], [1214604000000,0.63490], [1214690400000,0.63330], [1214776800000,0.63340], [1214863200000,0.63380], [1214949600000,0.63420], [1215036000000,0.6320], [1215122400000,0.63180], [1215208800000,0.6370], [1215295200000,0.63680], [1215381600000,0.63680], [1215468000000,0.63830], [1215554400000,0.63710], [1215640800000,0.63710], [1215727200000,0.63550], [1215813600000,0.6320], [1215900000000,0.62770], [1215986400000,0.62760], [1216072800000,0.62910], [1216159200000,0.62740], [1216245600000,0.62930], [1216332000000,0.63110], [1216418400000,0.6310], [1216504800000,0.63120], [1216591200000,0.63120], [1216677600000,0.63040], [1216764000000,0.62940], [1216850400000,0.63480], [1216936800000,0.63780], [1217023200000,0.63680], [1217109600000,0.63680], [1217196000000,0.63680], [1217282400000,0.6360], [1217368800000,0.6370], [1217455200000,0.64180], [1217541600000,0.64110], [1217628000000,0.64350], [1217714400000,0.64270], [1217800800000,0.64270], [1217887200000,0.64190], [1217973600000,0.64460], [1218060000000,0.64680], [1218146400000,0.64870], [1218232800000,0.65940], [1218319200000,0.66660], [1218405600000,0.66660], [1218492000000,0.66780], [1218578400000,0.67120], [1218664800000,0.67050], [1218751200000,0.67180], [1218837600000,0.67840], [1218924000000,0.68110], [1219010400000,0.68110], [1219096800000,0.67940], [1219183200000,0.68040], [1219269600000,0.67810], [1219356000000,0.67560], [1219442400000,0.67350], [1219528800000,0.67630], [1219615200000,0.67620], [1219701600000,0.67770], [1219788000000,0.68150], [1219874400000,0.68020], [1219960800000,0.6780], [1220047200000,0.67960], [1220133600000,0.68170], [1220220000000,0.68170], [1220306400000,0.68320], [1220392800000,0.68770], [1220479200000,0.69120], [1220565600000,0.69140], [1220652000000,0.70090], [1220738400000,0.70120], [1220824800000,0.7010], [1220911200000,0.70050]];
function euroFormatter(v, axis) {
return v.toFixed(axis.tickDecimals) +"€";
}
function doPlot(position) {
$.plot($("#placeholder"),
[ { data: oilprices, label: "Oil price ($)" },
{ data: exchangerates, label: "USD/EUR exchange rate", yaxis: 2 }],
{
xaxis: { mode: 'time' },
yaxis: { min: 0 },
y2axis: { tickFormatter: function (v, axis) { return v.toFixed(axis.tickDecimals) +"€" }},
legend: { position: 'sw' } });
xaxes: [ { mode: 'time' } ],
yaxes: [ { min: 0 },
{ position: position, tickFormatter: euroFormatter } ],
legend: { position: 'sw' }
});
}
doPlot("right");
$("button").click(function () {
doPlot($(this).text());
});
});
</script>
</body>
......
/*
Flot plugin for showing a crosshair, thin lines, when the mouse hovers
Flot plugin for showing crosshairs, thin lines, when the mouse hovers
over the plot.
crosshair: {
......@@ -19,10 +19,11 @@ The plugin also adds four public methods:
- setCrosshair(pos)
Set the position of the crosshair. Note that this is cleared if
the user moves the mouse. "pos" should be on the form { x: xpos,
y: ypos } (or x2 and y2 if you're using the secondary axes), which
is coincidentally the same format as what you get from a "plothover"
event. If "pos" is null, the crosshair is cleared.
the user moves the mouse. "pos" is in coordinates of the plot and
should be on the form { x: xpos, y: ypos } (you can use x2/x3/...
if you're using multiple axes), which is coincidentally the same
format as what you get from a "plothover" event. If "pos" is null,
the crosshair is cleared.
- clearCrosshair()
......@@ -69,10 +70,9 @@ The plugin also adds four public methods:
if (!pos)
crosshair.x = -1;
else {
var axes = plot.getAxes();
crosshair.x = Math.max(0, Math.min(pos.x != null ? axes.xaxis.p2c(pos.x) : axes.x2axis.p2c(pos.x2), plot.width()));
crosshair.y = Math.max(0, Math.min(pos.y != null ? axes.yaxis.p2c(pos.y) : axes.y2axis.p2c(pos.y2), plot.height()));
var o = plot.p2c(pos);
crosshair.x = Math.max(0, Math.min(o.left, plot.width()));
crosshair.y = Math.max(0, Math.min(o.top, plot.height()));
}
plot.triggerRedrawOverlay();
......
......@@ -51,6 +51,7 @@
backgroundOpacity: 0.85 // set to 0 to avoid background
},
xaxis: {
position: "bottom", // or "top"
mode: null, // null or "time"
transform: null, // null or f: number -> number to transform axis
inverseTransform: null, // if transform is set, this should be the inverse function
......@@ -59,6 +60,7 @@
autoscaleMargin: null, // margin in % to add if auto-setting min/max
ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks
tickFormatter: null, // fn: number -> string
tickLength: null, // size in pixels of ticks, or "full" for whole line
labelWidth: null, // size of tick labels in pixels
labelHeight: null,
......@@ -71,14 +73,11 @@
twelveHourClock: false // 12 or 24 time in time mode
},
yaxis: {
autoscaleMargin: 0.02
},
x2axis: {
autoscaleMargin: null
},
y2axis: {
autoscaleMargin: 0.02
autoscaleMargin: 0.02,
position: "left" // or "right"
},
xaxes: [],
yaxes: [],
series: {
points: {
show: false,
......@@ -113,6 +112,7 @@
backgroundColor: null, // null for transparent, else color
tickColor: "rgba(0,0,0,0.15)", // color used for the ticks
labelMargin: 5, // in pixels
axisMargin: 8, // in pixels
borderWidth: 2, // in pixels
borderColor: null, // set if different from the grid color
markings: null, // array of ranges or fn: axes -> array of ranges
......@@ -130,8 +130,9 @@
overlay = null, // canvas for interactive stuff on top of plot
eventHolder = null, // jQuery object that events should be bound to
ctx = null, octx = null,
axes = { xaxis: { n: 1 }, yaxis: { n: 1 },
x2axis: { n: 2 }, y2axis: { n: 2 } },
/*axes = { xaxis: { n: 1 }, yaxis: { n: 1 },
x2axis: { n: 2 }, y2axis: { n: 2 } },*/
xaxes = [], yaxes = [],
plotOffset = { left: 0, right: 0, top: 0, bottom: 0},
canvasWidth = 0, canvasHeight = 0,
plotWidth = 0, plotHeight = 0,
......@@ -161,17 +162,43 @@
o.top += plotOffset.top;
return o;
};
plot.getData = function() { return series; };
plot.getAxes = function() { return axes; };
plot.getOptions = function() { return options; };
plot.getData = function () { return series; };
plot.getAxis = function (dir, number) {
var a = (dir == x ? xaxes : yaxes)[number - 1];
if (a && !a.used)
a = null;
return a;
};
plot.getAxes = function () {
var res = {}, i;
for (i = 0; i < xaxes.length; ++i)
res["x" + (i ? (i + 1) : "") + "axis"] = xaxes[i] || {};
for (i = 0; i < yaxes.length; ++i)
res["y" + (i ? (i + 1) : "") + "axis"] = yaxes[i] || {};
// backwards compatibility - to be removed
if (!res.x2axis)
res.x2axis = { n: 2 };
if (!res.y2axis)
res.y2axis = { n: 2 };
return res;
};
plot.getXAxes = function () { return xaxes; };
plot.getYAxes = function () { return yaxes; };
plot.getUsedAxes = getUsedAxes; // return flat array with x and y axes that are in use
plot.c2p = canvasToAxisCoords;
plot.p2c = axisToCanvasCoords;
plot.getOptions = function () { return options; };
plot.highlight = highlight;
plot.unhighlight = unhighlight;
plot.triggerRedrawOverlay = triggerRedrawOverlay;
plot.pointOffset = function(point) {
return { left: parseInt(axisSpecToRealAxis(point, "xaxis").p2c(+point.x) + plotOffset.left),
top: parseInt(axisSpecToRealAxis(point, "yaxis").p2c(+point.y) + plotOffset.top) };
return {
left: parseInt(xaxes[axisNumber(point, "x") - 1].p2c(+point.x) + plotOffset.left),
top: parseInt(yaxes[axisNumber(point, "y") - 1].p2c(+point.y) + plotOffset.top)
};
};
// public attributes
plot.hooks = hooks;
......@@ -202,14 +229,33 @@
}
function parseOptions(opts) {
var i;
$.extend(true, options, opts);
if (options.grid.borderColor == null)
options.grid.borderColor = options.grid.color;
// fill in defaults in axes, copy at least always the
// first as the rest of the code assumes it'll be there
for (i = 0; i < Math.max(1, options.xaxes.length); ++i)
options.xaxes[i] = $.extend(true, {}, options.xaxis, options.xaxes[i]);
for (i = 0; i < Math.max(1, options.yaxes.length); ++i)
options.yaxes[i] = $.extend(true, {}, options.yaxis, options.yaxes[i]);
// backwards compatibility, to be removed in future
if (options.xaxis.noTicks && options.xaxis.ticks == null)
options.xaxis.ticks = options.xaxis.noTicks;
if (options.yaxis.noTicks && options.yaxis.ticks == null)
options.yaxis.ticks = options.yaxis.noTicks;
if (options.x2axis) {
options.y2axis.position = "top";
options.xaxes[1] = options.x2axis;
}
if (options.y2axis) {
if (options.y2axis.autoscaleMargin === undefined)
options.y2axis.autoscaleMargin = 0.02;
options.y2axis.position = "right";
options.yaxes[1] = options.y2axis;
}
if (options.grid.coloredAreas)
options.grid.markings = options.grid.coloredAreas;
if (options.grid.coloredAreasColor)
......@@ -223,6 +269,11 @@
if (options.shadowSize)
options.series.shadowSize = options.shadowSize;
for (i = 0; i < options.xaxes.length; ++i)
getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i];
for (i = 0; i < options.yaxes.length; ++i)
getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i];
// add hooks from options
for (var n in hooks)
if (options.hooks[n] && options.hooks[n].length)
......@@ -258,15 +309,97 @@
return res;
}
function axisSpecToRealAxis(obj, attr) {
var a = obj[attr];
function axisNumber(obj, coord) {
var a = obj[coord + "axis"];
if (typeof a == "object") // if we got a real axis, extract number
a = a.n;
if (typeof a == "number" && a != 1)
return axes[attr.charAt(0) + a + attr.slice(1)];
if (typeof a != "number")
a = 1; // default to first axis
return a;
}
function canvasToAxisCoords(pos) {
// return an object with x/y corresponding to all used axes
var res = {}, i, axis;
for (i = 0; i < xaxes.length; ++i) {
axis = xaxes[i];
if (axis && axis.used)
res["x" + axis.n] = axis.c2p(pos.left);
}
for (i = 0; i < yaxes.length; ++i) {
axis = yaxes[i];
if (axis && axis.used)
res["y" + axis.n] = axis.c2p(pos.top);
}
if (res.x1 !== undefined)
res.x = res.x1;
if (res.y1 !== undefined)
res.y = res.y1;
return res;
}
function axisToCanvasCoords(pos) {
// get canvas coords from the first pair of x/y found in pos
var res = {}, i, axis, key;
for (i = 0; i < xaxes.length; ++i) {
axis = xaxes[i];
if (axis && axis.used) {
key = "x" + axis.n;
if (pos[key] == null && axis.n == 1)
key = "x";
if (pos[key]) {
res.left = axis.p2c(pos[key]);
break;
}
}
}
for (i = 0; i < yaxes.length; ++i) {
axis = yaxes[i];
if (axis && axis.used) {
key = "y" + axis.n;
if (pos[key] == null && axis.n == 1)
key = "y";
if (pos[key]) {
res.top = axis.p2c(pos[key]);
break;
}
}
}
return res;
}
function getUsedAxes() {
var res = [], i, axis;
for (i = 0; i < xaxes.length; ++i) {
axis = xaxes[i];
if (axis && axis.used)
res.push(axis);
}
for (i = 0; i < yaxes.length; ++i) {
axis = yaxes[i];
if (axis && axis.used)
res.push(axis);
}
return res;
}
function getOrCreateAxis(axes, number) {
if (!axes[number - 1])
axes[number - 1] = {
n: number, // save the number for future reference
direction: axes == xaxes ? "x" : "y",
options: $.extend(true, {}, axes == xaxes ? options.xaxis : options.yaxis)
};
// default to first axis
return axes[attr];
return axes[number - 1];
}
function fillInSeriesOptions() {
......@@ -344,8 +477,8 @@
}
// setup axes
s.xaxis = axisSpecToRealAxis(s, "xaxis");
s.yaxis = axisSpecToRealAxis(s, "yaxis");
s.xaxis = getOrCreateAxis(xaxes, axisNumber(s, "x"));
s.yaxis = getOrCreateAxis(yaxes, axisNumber(s, "y"));
}
}
......@@ -355,10 +488,13 @@
i, j, k, m, length,
s, points, ps, x, y, axis, val, f, p;
for (axis in axes) {
axes[axis].datamin = topSentry;
axes[axis].datamax = bottomSentry;
axes[axis].used = false;
function initAxis(axis, number) {
if (!axis)
return;
axis.datamin = topSentry;
axis.datamax = bottomSentry;
axis.used = false;
}
function updateAxis(axis, min, max) {
......@@ -368,6 +504,11 @@
axis.datamax = max;
}
for (i = 0; i < xaxes.length; ++i)
initAxis(xaxes[i]);
for (i = 0; i < yaxes.length; ++i)
initAxis(yaxes[i]);
for (i = 0; i < series.length; ++i) {
s = series[i];
s.datapoints = { points: [] };
......@@ -533,12 +674,12 @@
updateAxis(s.yaxis, ymin, ymax);
}
for (axis in axes) {
if (axes[axis].datamin == topSentry)
axes[axis].datamin = null;
if (axes[axis].datamax == bottomSentry)
axes[axis].datamax = null;
}
$.each(getUsedAxes(), function (i, axis) {
if (axis.datamin == topSentry)
axis.datamin = null;
if (axis.datamax == bottomSentry)
axis.datamax = null;
});
}
function constructCanvas() {
......@@ -588,15 +729,16 @@
executeHooks(hooks.bindEvents, [eventHolder]);
}
function setupGrid() {
function setTransformationHelpers(axis, o) {
function setTransformationHelpers(axis) {
// set helper functions on the axis, assumes plot area
// has been computed already
function identity(x) { return x; }
var s, m, t = o.transform || identity,
it = o.inverseTransform;
var s, m, t = axis.options.transform || identity,
it = axis.options.inverseTransform;
// add transformation helpers
if (axis == axes.xaxis || axis == axes.x2axis) {
if (axis.direction == "x") {
// precompute how much the axis is scaling a point
// in canvas space
s = axis.scale = plotWidth / (t(axis.max) - t(axis.min));
......@@ -628,143 +770,242 @@
}
}
function measureLabels(axis, axisOptions) {
var i, labels = [], l;
function measureTickLabels(axis) {
if (!axis)
return;
var opts = axis.options, i, ticks = axis.ticks || [], labels = [],
l, w = opts.labelWidth, h = opts.labelHeight, dummyDiv;
axis.labelWidth = axisOptions.labelWidth;
axis.labelHeight = axisOptions.labelHeight;
function makeDummyDiv(labels, width) {
return $('<div style="position:absolute;top:-10000px;' + width + 'font-size:smaller">' +
'<div class="' + axis.direction + 'Axis ' + axis.direction + axis.n + 'Axis">'
+ labels.join("") + '</div></div>')
.appendTo(placeholder);
}
if (axis == axes.xaxis || axis == axes.x2axis) {
// to avoid measuring the widths of the labels, we
if (axis.direction == "x") {
// to avoid measuring the widths of the labels (it's slow), we
// construct fixed-size boxes and put the labels inside
// them, we don't need the exact figures and the
// fixed-size box content is easy to center
if (axis.labelWidth == null)
axis.labelWidth = canvasWidth / (axis.ticks.length > 0 ? axis.ticks.length : 1);
if (w == null)
w = canvasWidth / (ticks.length > 0 ? ticks.length : 1);
// measure x label heights
if (axis.labelHeight == null) {
if (h == null) {
labels = [];
for (i = 0; i < axis.ticks.length; ++i) {
l = axis.ticks[i].label;
for (i = 0; i < ticks.length; ++i) {
l = ticks[i].label;
if (l)
labels.push('<div class="tickLabel" style="float:left;width:' + axis.labelWidth + 'px">' + l + '</div>');
labels.push('<div class="tickLabel" style="float:left;width:' + w + 'px">' + l + '</div>');
}
if (labels.length > 0) {
var dummyDiv = $('<div style="position:absolute;top:-10000px;width:10000px;font-size:smaller">'
+ labels.join("") + '<div style="clear:left"></div></div>').appendTo(placeholder);
axis.labelHeight = dummyDiv.height();
// stick them all in the same div and measure
// collective height
labels.push('<div style="clear:left"></div>');
dummyDiv = makeDummyDiv(labels, "width:10000px;");
h = dummyDiv.height();
dummyDiv.remove();
}
}
}
else if (axis.labelWidth == null || axis.labelHeight == null) {
else if (w == null || h == null) {
// calculate y label dimensions
for (i = 0; i < axis.ticks.length; ++i) {
l = axis.ticks[i].label;
for (i = 0; i < ticks.length; ++i) {
l = ticks[i].label;
if (l)
labels.push('<div class="tickLabel">' + l + '</div>');
}
if (labels.length > 0) {
var dummyDiv = $('<div style="position:absolute;top:-10000px;font-size:smaller">'
+ labels.join("") + '</div>').appendTo(placeholder);
if (axis.labelWidth == null)
axis.labelWidth = dummyDiv.width();
if (axis.labelHeight == null)
axis.labelHeight = dummyDiv.find("div").height();
dummyDiv.remove();
dummyDiv = makeDummyDiv(labels, "");
if (w == null)
w = dummyDiv.children().width();
if (h == null)
h = dummyDiv.find("div.tickLabel").height();
}
}
if (axis.labelWidth == null)
axis.labelWidth = 0;
if (axis.labelHeight == null)
axis.labelHeight = 0;
if (w == null)
w = 0;
if (h == null)
h = 0;
axis.labelWidth = w;
axis.labelHeight = h;
}
function setGridSpacing() {
// get the most space needed around the grid for things
// that may stick out
var maxOutset = options.grid.borderWidth;
for (var i = 0; i < series.length; ++i)
maxOutset = Math.max(maxOutset, 2 * (series[i].points.radius + series[i].points.lineWidth/2));
function computeAxisBox(axis) {
if (!axis || (!axis.used && !(axis.labelWidth || axis.labelHeight)))
return;
plotOffset.left = plotOffset.right = plotOffset.top = plotOffset.bottom = maxOutset;
// find the bounding box of the axis by looking at label
// widths/heights and ticks, make room by diminishing the
// plotOffset
var lw = axis.labelWidth,
lh = axis.labelHeight,
pos = axis.options.position,
tickLength = axis.options.tickLength,
axismargin = options.grid.axisMargin,
padding = options.grid.labelMargin,
all = axis.direction == "x" ? xaxes : yaxes,
index;
// determine axis margin
var samePosition = $.grep(all, function (a) {
return a && a.options.position == pos && (a.labelHeight || a.labelWidth);
});
if ($.inArray(axis, samePosition) == samePosition.length - 1)
axismargin = 0; // outermost
var margin = options.grid.labelMargin + options.grid.borderWidth;
// determine tick length - if we're innermost, we can use "full"
if (tickLength == null)
tickLength = "full";
if (axes.xaxis.labelHeight > 0)
plotOffset.bottom = Math.max(maxOutset, axes.xaxis.labelHeight + margin);
if (axes.yaxis.labelWidth > 0)
plotOffset.left = Math.max(maxOutset, axes.yaxis.labelWidth + margin);
if (axes.x2axis.labelHeight > 0)
plotOffset.top = Math.max(maxOutset, axes.x2axis.labelHeight + margin);
if (axes.y2axis.labelWidth > 0)
plotOffset.right = Math.max(maxOutset, axes.y2axis.labelWidth + margin);
var sameDirection = $.grep(all, function (a) {
return a && (a.labelHeight || a.labelWidth);
});
plotWidth = canvasWidth - plotOffset.left - plotOffset.right;
plotHeight = canvasHeight - plotOffset.bottom - plotOffset.top;
if ($.inArray(axis, sameDirection) != 0 && tickLength == "full")
tickLength = 5;
if (!isNaN(+tickLength))
padding += +tickLength;
// compute box
if (axis.direction == "x") {
lh += padding;
if (pos == "bottom") {
plotOffset.bottom += lh + axismargin;
axis.box = { top: canvasHeight - plotOffset.bottom, height: lh };
}
else {
axis.box = { top: plotOffset.top + axismargin, height: lh };
plotOffset.top += lh + axismargin;
}
}
else {
lw += padding;
var axis;
for (axis in axes)
setRange(axes[axis], options[axis]);
if (pos == "left") {
axis.box = { left: plotOffset.left + axismargin, width: lw };
plotOffset.left += lw + axismargin;
}
else {
plotOffset.right += lw + axismargin;
axis.box = { left: canvasWidth - plotOffset.right, width: lw };
}
}
if (options.grid.show) {
for (axis in axes) {
prepareTickGeneration(axes[axis], options[axis]);
setTicks(axes[axis], options[axis]);
measureLabels(axes[axis], options[axis]);
// save for future reference
axis.position = pos;
axis.tickLength = tickLength;
axis.box.padding = padding;
axis.innermost = $.inArray(axis, samePosition) == 0;
}
setGridSpacing();
function fixupAxisBox(axis) {
// set remaining bounding box coordinates
if (axis.direction == "x") {
axis.box.left = plotOffset.left;
axis.box.width = plotWidth;
}
else {
axis.box.top = plotOffset.top;
axis.box.height = plotHeight;
}
}
function setupGrid() {
var axes = getUsedAxes(), j, k;
// compute axis intervals
for (k = 0; k < axes.length; ++k)
setRange(axes[k]);
plotOffset.left = plotOffset.right = plotOffset.top = plotOffset.bottom = 0;
plotWidth = canvasWidth;
plotHeight = canvasHeight;
if (options.grid.show) {
// make the ticks
for (k = 0; k < axes.length; ++k) {
setupTickGeneration(axes[k]);
setTicks(axes[k]);
}
// find labelWidth/Height, do this on all, not just
// used as we might need to reserve space for unused
// to if their labelWidth/Height is set
for (j = 0; j < xaxes.length; ++j)
measureTickLabels(xaxes[j]);
for (j = 0; j < yaxes.length; ++j)
measureTickLabels(yaxes[j]);
// compute the axis boxes, start from the outside (reverse order)
for (j = xaxes.length - 1; j >= 0; --j)
computeAxisBox(xaxes[j]);
for (j = yaxes.length - 1; j >= 0; --j)
computeAxisBox(yaxes[j]);
// make sure we've got enough space for things that
// might stick out
var maxOutset = options.grid.borderWidth;
for (var i = 0; i < series.length; ++i)
maxOutset = Math.max(maxOutset, 2 * (series[i].points.radius + series[i].points.lineWidth/2));
for (var a in plotOffset)
plotOffset[a] = Math.max(maxOutset, plotOffset[a]);
}
for (axis in axes)
setTransformationHelpers(axes[axis], options[axis]);
plotWidth = canvasWidth - plotOffset.left - plotOffset.right;
plotHeight = canvasHeight - plotOffset.bottom - plotOffset.top;
// now we got the proper plotWidth/Height, we can compute the scaling
for (k = 0; k < axes.length; ++k)
setTransformationHelpers(axes[k]);
if (options.grid.show) {
for (k = 0; k < axes.length; ++k)
fixupAxisBox(axes[k]);
if (options.grid.show)
insertLabels();
insertAxisLabels();
}
insertLegend();
}
function setRange(axis, axisOptions) {
var min = +(axisOptions.min != null ? axisOptions.min : axis.datamin),
max = +(axisOptions.max != null ? axisOptions.max : axis.datamax),
function setRange(axis) {
var opts = axis.options,
min = +(opts.min != null ? opts.min : axis.datamin),
max = +(opts.max != null ? opts.max : axis.datamax),
delta = max - min;
if (delta == 0.0) {
// degenerate case
var widen = max == 0 ? 1 : 0.01;
if (axisOptions.min == null)
if (opts.min == null)
min -= widen;
// alway widen max if we couldn't widen min to ensure we
// don't fall into min == max which doesn't work
if (axisOptions.max == null || axisOptions.min != null)
if (opts.max == null || opts.min != null)
max += widen;
}
else {
// consider autoscaling
var margin = axisOptions.autoscaleMargin;
var margin = opts.autoscaleMargin;
if (margin != null) {
if (axisOptions.min == null) {
if (opts.min == null) {
min -= delta * margin;
// make sure we don't go below zero if all values
// are positive
if (min < 0 && axis.datamin != null && axis.datamin >= 0)
min = 0;
}
if (axisOptions.max == null) {
if (opts.max == null) {
max += delta * margin;
if (max > 0 && axis.datamax != null && axis.datamax <= 0)
max = 0;
......@@ -775,12 +1016,14 @@
axis.max = max;
}
function prepareTickGeneration(axis, axisOptions) {
function setupTickGeneration(axis) {
var opts = axis.options;
// estimate number of ticks
var noTicks;
if (typeof axisOptions.ticks == "number" && axisOptions.ticks > 0)
noTicks = axisOptions.ticks;
else if (axis == axes.xaxis || axis == axes.x2axis)
if (typeof opts.ticks == "number" && opts.ticks > 0)
noTicks = opts.ticks;
else if (axis.direction == "x")
// heuristic based on the model a*sqrt(x) fitted to
// some reasonable data points
noTicks = 0.3 * Math.sqrt(canvasWidth);
......@@ -790,7 +1033,7 @@
var delta = (axis.max - axis.min) / noTicks,
size, generator, unit, formatter, i, magn, norm;
if (axisOptions.mode == "time") {
if (opts.mode == "time") {
// pretty handling of time
// map of app. size of time units in milliseconds
......@@ -820,11 +1063,11 @@
];
var minSize = 0;
if (axisOptions.minTickSize != null) {
if (typeof axisOptions.tickSize == "number")
minSize = axisOptions.tickSize;
if (opts.minTickSize != null) {
if (typeof opts.tickSize == "number")
minSize = opts.tickSize;
else
minSize = axisOptions.minTickSize[0] * timeUnitSize[axisOptions.minTickSize[1]];
minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]];
}
for (var i = 0; i < spec.length - 1; ++i)
......@@ -851,9 +1094,9 @@
size *= magn;
}
if (axisOptions.tickSize) {
size = axisOptions.tickSize[0];
unit = axisOptions.tickSize[1];
if (opts.tickSize) {
size = opts.tickSize[0];
unit = opts.tickSize[1];
}
generator = function(axis) {
......@@ -923,12 +1166,12 @@
var d = new Date(v);
// first check global format
if (axisOptions.timeformat != null)
return $.plot.formatDate(d, axisOptions.timeformat, axisOptions.monthNames);
if (opts.timeformat != null)
return $.plot.formatDate(d, opts.timeformat, opts.monthNames);
var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
var span = axis.max - axis.min;
var suffix = (axisOptions.twelveHourClock) ? " %p" : "";
var suffix = (opts.twelveHourClock) ? " %p" : "";
if (t < timeUnitSize.minute)
fmt = "%h:%M:%S" + suffix;
......@@ -949,12 +1192,12 @@
else
fmt = "%y";
return $.plot.formatDate(d, fmt, axisOptions.monthNames);
return $.plot.formatDate(d, fmt, opts.monthNames);
};
}
else {
// pretty rounding of base-10 numbers
var maxDec = axisOptions.tickDecimals;
var maxDec = opts.tickDecimals;
var dec = -Math.floor(Math.log(delta) / Math.LN10);
if (maxDec != null && dec > maxDec)
dec = maxDec;
......@@ -979,11 +1222,11 @@
size *= magn;
if (axisOptions.minTickSize != null && size < axisOptions.minTickSize)
size = axisOptions.minTickSize;
if (opts.minTickSize != null && size < opts.minTickSize)
size = opts.minTickSize;
if (axisOptions.tickSize != null)
size = axisOptions.tickSize;
if (opts.tickSize != null)
size = opts.tickSize;
axis.tickDecimals = Math.max(0, (maxDec != null) ? maxDec : dec);
......@@ -1009,26 +1252,25 @@
axis.tickSize = unit ? [size, unit] : size;
axis.tickGenerator = generator;
if ($.isFunction(axisOptions.tickFormatter))
axis.tickFormatter = function (v, axis) { return "" + axisOptions.tickFormatter(v, axis); };
if ($.isFunction(opts.tickFormatter))
axis.tickFormatter = function (v, axis) { return "" + opts.tickFormatter(v, axis); };
else
axis.tickFormatter = formatter;
}
function setTicks(axis, axisOptions) {
axis.ticks = [];
function setTicks(axis) {
var opts = axis.options;
if (!axis.used)
return;
axis.ticks = [];
if (axisOptions.ticks == null)
if (opts.ticks == null)
axis.ticks = axis.tickGenerator(axis);
else if (typeof axisOptions.ticks == "number") {
if (axisOptions.ticks > 0)
else if (typeof opts.ticks == "number") {
if (opts.ticks > 0)
axis.ticks = axis.tickGenerator(axis);
}
else if (axisOptions.ticks) {
var ticks = axisOptions.ticks;
else if (opts.ticks) {
var ticks = opts.ticks;
if ($.isFunction(ticks))
// generate the ticks
......@@ -1052,11 +1294,11 @@
}
}
if (axisOptions.autoscaleMargin != null && axis.ticks.length > 0) {
if (opts.autoscaleMargin != null && axis.ticks.length > 0) {
// snap to ticks
if (axisOptions.min == null)
if (opts.min == null)
axis.min = Math.min(axis.min, axis.ticks[0].v);
if (axisOptions.max == null && axis.ticks.length > 1)
if (opts.max == null && axis.ticks.length > 1)
axis.max = Math.max(axis.max, axis.ticks[axis.ticks.length - 1].v);
}
}
......@@ -1081,30 +1323,36 @@
}
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;
var axis, from, to, axes, key;
axes = getUsedAxes();
for (i = 0; i < axes.length; ++i) {
axis = axes[i];
if (axis.direction == coord) {
key = coord + axis.n + "axis";
if (!ranges[key] && axis.n == 1)
key = coord + "axis"; // support x1axis as xaxis
if (ranges[key]) {
from = ranges[key].from;
to = ranges[key].to;
break;
}
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];
if (!ranges[key]) {
axis = coord == "x" ? xaxes[0] : yaxes[0];
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 };
if (from != null && to != null && from > to) {
var tmp = from;
from = to;
to = tmp;
}
return { from: from, to: to, axis: axis };
}
......@@ -1124,9 +1372,17 @@
// draw markings
var markings = options.grid.markings;
if (markings) {
if ($.isFunction(markings))
// xmin etc. are backwards-compatible, to be removed in future
markings = markings({ xmin: axes.xaxis.min, xmax: axes.xaxis.max, ymin: axes.yaxis.min, ymax: axes.yaxis.max, xaxis: axes.xaxis, yaxis: axes.yaxis, x2axis: axes.x2axis, y2axis: axes.y2axis });
if ($.isFunction(markings)) {
var axes = plot.getAxes();
// xmin etc. is backwards compatibility, to be
// removed in the future
axes.xmin = axes.xaxis.min;
axes.xmax = axes.xaxis.max;
axes.ymin = axes.yaxis.min;
axes.ymax = axes.yaxis.max;
markings = markings(axes);
}
for (i = 0; i < markings.length; ++i) {
var m = markings[i],
......@@ -1167,8 +1423,6 @@
ctx.beginPath();
ctx.strokeStyle = m.color || options.grid.markingsColor;
ctx.lineWidth = m.lineWidth || options.grid.markingsLineWidth;
//ctx.moveTo(Math.floor(xrange.from), yrange.from);
//ctx.lineTo(Math.floor(xrange.to), yrange.to);
ctx.moveTo(xrange.from, yrange.from);
ctx.lineTo(xrange.to, yrange.to);
ctx.stroke();
......@@ -1183,59 +1437,94 @@
}
}
// draw the inner grid
ctx.lineWidth = 1;
// draw the ticks
ctx.strokeStyle = options.grid.tickColor;
ctx.beginPath();
var v, axis = axes.xaxis,
bw = options.grid.borderWidth;
for (i = 0; i < axis.ticks.length; ++i) {
v = axis.ticks[i].v;
if (v < axis.min || v > axis.max ||
(bw > 0 && (v == axis.min || v == axis.max)))
continue; // skip those lying on the axes if we got a border
var axes = getUsedAxes(), bw = options.grid.borderWidth;
for (var j = 0; j < axes.length; ++j) {
var axis = axes[j], box = axis.box,
t = axis.tickLength, x, y, xoff, yoff;
ctx.moveTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, 0);
ctx.lineTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, plotHeight);
// find the edges
if (axis.direction == "x") {
x = 0;
if (axis.position == "bottom")
y = box.top - plotOffset.top;
else
y = box.top - plotOffset.top + box.height;
}
else {
y = 0;
if (axis.position == "left")
x = box.left - plotOffset.left + box.width;
else
x = box.left - plotOffset.left;
}
axis = axes.yaxis;
for (i = 0; i < axis.ticks.length; ++i) {
v = axis.ticks[i].v;
if (v < axis.min || v > axis.max ||
(bw > 0 && (v == axis.min || v == axis.max)))
continue;
ctx.lineWidth = 1;
// draw tick bar
if (!axis.innermost) {
ctx.beginPath();
xoff = yoff = 0;
if (axis.direction == "x")
xoff = plotWidth;
else
yoff = plotHeight;
ctx.moveTo(0, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
ctx.lineTo(plotWidth, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
if (ctx.lineWidth == 1) {
x = Math.floor(x) + 0.5;
y = Math.floor(y) + 0.5;
}
ctx.moveTo(x, y);
ctx.lineTo(x + xoff, y + yoff);
ctx.stroke();
}
axis = axes.x2axis;
// draw ticks
ctx.beginPath();
for (i = 0; i < axis.ticks.length; ++i) {
v = axis.ticks[i].v;
if (v < axis.min || v > axis.max ||
(bw > 0 && (v == axis.min || v == axis.max)))
var v = axis.ticks[i].v;
xoff = yoff = 0;
if (v < axis.min || v > axis.max
// skip those lying on the axes if we got a border
|| (t == "full" && bw > 0
&& (v == axis.min || v == axis.max)))
continue;
ctx.moveTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, -5);
ctx.lineTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, 5);
if (axis.direction == "x") {
x = axis.p2c(v);
yoff = t == "full" ? -plotHeight : t;
if (axis.position == "top")
yoff = -yoff;
}
else {
y = axis.p2c(v);
xoff = t == "full" ? -plotWidth : t;
if (axis.position == "left")
xoff = -xoff;
}
axis = axes.y2axis;
for (i = 0; i < axis.ticks.length; ++i) {
v = axis.ticks[i].v;
if (v < axis.min || v > axis.max ||
(bw > 0 && (v == axis.min || v == axis.max)))
continue;
if (ctx.lineWidth == 1) {
x = Math.floor(x) + 0.5;
y = Math.floor(y) + 0.5;
}
ctx.moveTo(plotWidth-5, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
ctx.lineTo(plotWidth+5, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
ctx.moveTo(x, y);
ctx.lineTo(x + xoff, y + yoff);
}
ctx.stroke();
}
if (options.grid.borderWidth) {
// draw border
if (bw) {
ctx.lineWidth = bw;
ctx.strokeStyle = options.grid.borderColor;
ctx.strokeRect(-bw/2, -bw/2, plotWidth + bw, plotHeight + bw);
......@@ -1244,38 +1533,53 @@
ctx.restore();
}
function insertLabels() {
function insertAxisLabels() {
placeholder.find(".tickLabels").remove();
var html = ['<div class="tickLabels" style="font-size:smaller;color:' + options.grid.color + '">'];
function addLabels(axis, labelGenerator) {
var axes = getUsedAxes();
for (var j = 0; j < axes.length; ++j) {
var axis = axes[j], box = axis.box;
//debug: html.push('<div style="position:absolute;opacity:0.10;background-color:red;left:' + box.left + 'px;top:' + box.top + 'px;width:' + box.width + 'px;height:' + box.height + 'px"></div>')
html.push('<div class="' + axis.direction + 'Axis ' + axis.direction + axis.n + 'Axis">');
for (var i = 0; i < axis.ticks.length; ++i) {
var tick = axis.ticks[i];
if (!tick.label || tick.v < axis.min || tick.v > axis.max)
continue;
html.push(labelGenerator(tick, axis));
}
}
var margin = options.grid.labelMargin + options.grid.borderWidth;
addLabels(axes.xaxis, function (tick, axis) {
return '<div style="position:absolute;top:' + (plotOffset.top + plotHeight + margin) + 'px;left:' + Math.round(plotOffset.left + axis.p2c(tick.v) - axis.labelWidth/2) + 'px;width:' + axis.labelWidth + 'px;text-align:center" class="tickLabel">' + tick.label + "</div>";
});
var pos = {}, align;
if (axis.direction == "x") {
align = "center";
pos.left = Math.round(plotOffset.left + axis.p2c(tick.v) - axis.labelWidth/2);
if (axis.position == "bottom")
pos.top = box.top + box.padding;
else
pos.bottom = canvasHeight - (box.top + box.height - box.padding);
}
else {
pos.top = Math.round(plotOffset.top + axis.p2c(tick.v) - axis.labelHeight/2);
if (axis.position == "left") {
pos.right = canvasWidth - (box.left + box.width - box.padding)
align = "right";
}
else {
pos.left = box.left + box.padding;
align = "left";
}
}
addLabels(axes.yaxis, function (tick, axis) {
return '<div style="position:absolute;top:' + Math.round(plotOffset.top + axis.p2c(tick.v) - axis.labelHeight/2) + 'px;right:' + (plotOffset.right + plotWidth + margin) + 'px;width:' + axis.labelWidth + 'px;text-align:right" class="tickLabel">' + tick.label + "</div>";
});
pos.width = axis.labelWidth;
addLabels(axes.x2axis, function (tick, axis) {
return '<div style="position:absolute;bottom:' + (plotOffset.bottom + plotHeight + margin) + 'px;left:' + Math.round(plotOffset.left + axis.p2c(tick.v) - axis.labelWidth/2) + 'px;width:' + axis.labelWidth + 'px;text-align:center" class="tickLabel">' + tick.label + "</div>";
});
var style = ["position:absolute", "text-align:" + align ];
for (var a in pos)
style.push(a + ":" + pos[a] + "px")
addLabels(axes.y2axis, function (tick, axis) {
return '<div style="position:absolute;top:' + Math.round(plotOffset.top + axis.p2c(tick.v) - axis.labelHeight/2) + 'px;left:' + (plotOffset.left + plotWidth + margin) +'px;width:' + axis.labelWidth + 'px;text-align:left" class="tickLabel">' + tick.label + "</div>";
});
html.push('<div style="' + style.join(';') + '">' + tick.label + '</div>');
}
html.push('</div>');
}
html.push('</div>');
......@@ -1912,18 +2216,12 @@
// so we share their code)
function triggerClickHoverEvent(eventname, event, seriesFilter) {
var offset = eventHolder.offset(),
pos = { pageX: event.pageX, pageY: event.pageY },
canvasX = event.pageX - offset.left - plotOffset.left,
canvasY = event.pageY - offset.top - plotOffset.top;
canvasY = event.pageY - offset.top - plotOffset.top,
pos = canvasToAxisCoords({ left: canvasX, top: canvasY });
if (axes.xaxis.used)
pos.x = axes.xaxis.c2p(canvasX);
if (axes.yaxis.used)
pos.y = axes.yaxis.c2p(canvasY);
if (axes.x2axis.used)
pos.x2 = axes.x2axis.c2p(canvasX);
if (axes.y2axis.used)
pos.y2 = axes.y2axis.c2p(canvasY);
pos.pageX = event.pageX;
pos.pageY = event.pageY;
var item = findNearbyItem(canvasX, canvasY, seriesFilter);
......@@ -2078,14 +2376,9 @@
}
$.plot = function(placeholder, data, options) {
//var t0 = new Date();
var plot = new Plot($(placeholder), data, options, $.plot.plugins);
/*var t0 = new Date();
var t1 = new Date();
var tstr = "time used (msecs): " + (t1.getTime() - t0.getTime())
if (window.console)
console.log(tstr);
else
alert(tstr);*/
//(window.console ? console.log : alert)("time used (msecs): " + ((new Date()).getTime() - t0.getTime()));
return plot;
};
......
......@@ -64,7 +64,7 @@ Example API usage:
Here, "center" specifies where the center of the zooming should
happen. Note that this is defined in pixel space, not the space of the
data points (you can use the c2p helpers on the axes in Flot to help
data points (you can use the p2c helpers on the axes in Flot to help
you convert between these).
"amount" is the amount to zoom the viewport relative to the current
......@@ -192,51 +192,49 @@ Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-L
if (!args)
args = {};
var axes = plot.getAxes(),
options = plot.getOptions(),
c = args.center,
amount = args.amount ? args.amount : options.zoom.amount,
var c = args.center,
amount = args.amount || plot.getOptions().zoom.amount,
w = plot.width(), h = plot.height();
if (!c)
c = { left: w / 2, top: h / 2 };
var xf = c.left / w,
x1 = c.left - xf * w / amount,
x2 = c.left + (1 - xf) * w / amount,
yf = c.top / h,
y1 = c.top - yf * h / amount,
y2 = c.top + (1 - yf) * h / amount;
minmax = {
x: {
min: c.left - xf * w / amount,
max: c.left + (1 - xf) * w / amount
},
y: {
min: c.top - yf * h / amount,
max: c.top + (1 - yf) * h / amount
}
};
function scaleAxis(min, max, name) {
var axis = axes[name],
axisOptions = options[name];
if (!axis.used)
return;
$.each(plot.getUsedAxes(), function(i, axis) {
var opts = axis.options,
min = minmax[axis.direction].min,
max = minmax[axis.direction].max
min = axis.c2p(min);
max = axis.c2p(max);
if (max < min) { // make sure min < max
var tmp = min
if (min > max) {
// make sure min < max
var tmp = min;
min = max;
max = tmp;
}
var range = max - min, zr = axisOptions.zoomRange;
var range = max - min, zr = opts.zoomRange;
if (zr &&
((zr[0] != null && range < zr[0]) ||
(zr[1] != null && range > zr[1])))
return;
axisOptions.min = min;
axisOptions.max = max;
}
scaleAxis(x1, x2, 'xaxis');
scaleAxis(x1, x2, 'x2axis');
scaleAxis(y1, y2, 'yaxis');
scaleAxis(y1, y2, 'y2axis');
opts.min = min;
opts.max = max;
});
plot.setupGrid();
plot.draw();
......@@ -246,49 +244,42 @@ Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-L
}
plot.pan = function (args) {
var l = +args.left, t = +args.top,
axes = plot.getAxes(), options = plot.getOptions();
if (isNaN(l))
l = 0;
if (isNaN(t))
t = 0;
var delta = {
x: +args.left,
y: +args.top
};
function panAxis(delta, name) {
var axis = axes[name],
axisOptions = options[name],
min, max;
if (isNaN(delta.x))
delta.x = 0;
if (isNaN(delta.y))
delta.y = 0;
if (!axis.used)
return;
$.each(plot.getUsedAxes(), function (i, axis) {
var opts = axis.options,
min, max, d = delta[axis.direction];
min = axis.c2p(axis.p2c(axis.min) + delta),
max = axis.c2p(axis.p2c(axis.max) + delta);
min = axis.c2p(axis.p2c(axis.min) + d),
max = axis.c2p(axis.p2c(axis.max) + d);
var pr = axisOptions.panRange;
var pr = opts.panRange;
if (pr) {
// check whether we hit the wall
if (pr[0] != null && pr[0] > min) {
delta = pr[0] - min;
min += delta;
max += delta;
d = pr[0] - min;
min += d;
max += d;
}
if (pr[1] != null && pr[1] < max) {
delta = pr[1] - max;
min += delta;
max += delta;
d = pr[1] - max;
min += d;
max += d;
}
}
axisOptions.min = min;
axisOptions.max = max;
}
panAxis(l, 'xaxis');
panAxis(l, 'x2axis');
panAxis(t, 'yaxis');
panAxis(t, 'y2axis');
opts.min = min;
opts.max = max;
});
plot.setupGrid();
plot.draw();
......
......@@ -8,20 +8,20 @@ The plugin defines the following options:
color: color
}
You enable selection support by setting the mode to one of "x", "y" or
Selection support is enabled by setting the mode to one of "x", "y" or
"xy". In "x" mode, the user will only be able to specify the x range,
similarly for "y" mode. For "xy", the selection becomes a rectangle
where both ranges can be specified. "color" is color of the selection.
When selection support is enabled, a "plotselected" event will be emitted
on the DOM element you passed into the plot function. The event
handler gets one extra parameter with the ranges selected on the axes,
When selection support is enabled, a "plotselected" event will be
emitted on the DOM element you passed into the plot function. The
event handler gets a parameter with the ranges selected on the axes,
like this:
placeholder.bind("plotselected", function(event, ranges) {
alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to)
// similar for yaxis, secondary axes are in x2axis
// and y2axis if present
// similar for yaxis - with multiple axes, the extra ones are in
// x2axis, x3axis, ...
});
The "plotselected" event is only fired when the user has finished
......@@ -37,17 +37,19 @@ The plugin allso adds the following methods to the plot object:
- setSelection(ranges, preventEvent)
Set the selection rectangle. The passed in ranges is on the same
form as returned in the "plotselected" event. If the selection
mode is "x", you should put in either an xaxis (or x2axis) object,
if the mode is "y" you need to put in an yaxis (or y2axis) object
and both xaxis/x2axis and yaxis/y2axis if the selection mode is
"xy", like this:
form as returned in the "plotselected" event. If the selection mode
is "x", you should put in either an xaxis range, if the mode is "y"
you need to put in an yaxis range and both xaxis and yaxis if the
selection mode is "xy", like this:
setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } });
setSelection will trigger the "plotselected" event when called. If
you don't want that to happen, e.g. if you're inside a
"plotselected" handler, pass true as the second parameter.
"plotselected" handler, pass true as the second parameter. If you
are using multiple axes, you can specify the ranges on any of those,
e.g. as x2axis/x3axis/... instead of xaxis, the plugin picks the
first one it sees.
- clearSelection(preventEvent)
......@@ -135,21 +137,13 @@ The plugin allso adds the following methods to the plot object:
if (!selectionIsSane())
return null;
var x1 = Math.min(selection.first.x, selection.second.x),
x2 = Math.max(selection.first.x, selection.second.x),
y1 = Math.max(selection.first.y, selection.second.y),
y2 = Math.min(selection.first.y, selection.second.y);
var r = {};
var axes = plot.getAxes();
if (axes.xaxis.used)
r.xaxis = { from: axes.xaxis.c2p(x1), to: axes.xaxis.c2p(x2) };
if (axes.x2axis.used)
r.x2axis = { from: axes.x2axis.c2p(x1), to: axes.x2axis.c2p(x2) };
if (axes.yaxis.used)
r.yaxis = { from: axes.yaxis.c2p(y1), to: axes.yaxis.c2p(y2) };
if (axes.y2axis.used)
r.y2axis = { from: axes.y2axis.c2p(y1), to: axes.y2axis.c2p(y2) };
var r = {}, c1 = selection.first, c2 = selection.second;
$.each(plot.getAxes(), function (name, axis) {
if (axis.used) {
var p1 = axis.c2p(c1[axis.direction]), p2 = axis.c2p(c2[axis.direction]);
r[name] = { from: Math.min(p1, p2), to: Math.max(p1, p2) };
}
});
return r;
}
......@@ -159,13 +153,12 @@ The plugin allso adds the following methods to the plot object:
plot.getPlaceholder().trigger("plotselected", [ r ]);
// backwards-compat stuff, to be removed in future
var axes = plot.getAxes();
if (axes.xaxis.used && axes.yaxis.used)
if (r.xaxis && r.yaxis)
plot.getPlaceholder().trigger("selected", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]);
}
function clamp(min, value, max) {
return value < min? min: (value > max? max: value);
return value < min ? min: (value > max ? max: value);
}
function setSelectionPos(pos, e) {
......@@ -176,10 +169,10 @@ The plugin allso adds the following methods to the plot object:
pos.y = clamp(0, e.pageY - offset.top - plotOffset.top, plot.height());
if (o.selection.mode == "y")
pos.x = pos == selection.first? 0: plot.width();
pos.x = pos == selection.first ? 0 : plot.width();
if (o.selection.mode == "x")
pos.y = pos == selection.first? 0: plot.height();
pos.y = pos == selection.first ? 0 : plot.height();
}
function updateSelection(pos) {
......@@ -204,19 +197,55 @@ The plugin allso adds the following methods to the plot object:
}
}
// taken from markings support
function extractRange(ranges, coord) {
var axis, from, to, axes, key;
axes = plot.getUsedAxes();
for (i = 0; i < axes.length; ++i) {
axis = axes[i];
if (axis.direction == coord) {
key = coord + axis.n + "axis";
if (!ranges[key] && axis.n == 1)
key = coord + "axis"; // support x1axis as xaxis
if (ranges[key]) {
from = ranges[key].from;
to = ranges[key].to;
break;
}
}
}
// backwards-compat stuff - to be removed in future
if (!ranges[key]) {
axis = coord == "x" ? plot.getXAxes()[0] : plot.getYAxes()[0];
from = ranges[coord + "1"];
to = ranges[coord + "2"];
}
// auto-reverse as an added bonus
if (from != null && to != null && from > to) {
var tmp = from;
from = to;
to = tmp;
}
return { from: from, to: to, axis: axis };
}
function setSelection(ranges, preventEvent) {
var axis, range, axes = plot.getAxes();
var o = plot.getOptions();
var axis, range, o = plot.getOptions();
if (o.selection.mode == "y") {
selection.first.x = 0;
selection.second.x = plot.width();
}
else {
axis = ranges["xaxis"]? axes["xaxis"]: (ranges["x2axis"]? axes["x2axis"]: axes["xaxis"]);
range = ranges["xaxis"] || ranges["x2axis"] || { from:ranges["x1"], to:ranges["x2"] }
selection.first.x = axis.p2c(Math.min(range.from, range.to));
selection.second.x = axis.p2c(Math.max(range.from, range.to));
range = extractRange(ranges, "x");
selection.first.x = range.axis.p2c(range.from);
selection.second.x = range.axis.p2c(range.to);
}
if (o.selection.mode == "x") {
......@@ -224,10 +253,10 @@ The plugin allso adds the following methods to the plot object:
selection.second.y = plot.height();
}
else {
axis = ranges["yaxis"]? axes["yaxis"]: (ranges["y2axis"]? axes["y2axis"]: axes["yaxis"]);
range = ranges["yaxis"] || ranges["y2axis"] || { from:ranges["y1"], to:ranges["y2"] }
selection.first.y = axis.p2c(Math.min(range.from, range.to));
selection.second.y = axis.p2c(Math.max(range.from, range.to));
range = extractRange(ranges, "y");
selection.first.y = range.axis.p2c(range.from);
selection.second.y = range.axis.p2c(range.to);
}
selection.show = true;
......
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