[SPIP][PLUGINS] v3.0-->v3.2
[lhc/web/www.git] / www / plugins-dist / statistiques / javascript / jquery.flot.selection.js
1 /* Flot plugin for selecting regions of a plot.
2
3 Copyright (c) 2007-2014 IOLA and Ole Laursen.
4 Licensed under the MIT license.
5
6 The plugin supports these options:
7
8 selection: {
9 mode: null or "x" or "y" or "xy",
10 color: color,
11 shape: "round" or "miter" or "bevel",
12 minSize: number of pixels
13 }
14
15 Selection support is enabled by setting the mode to one of "x", "y" or "xy".
16 In "x" mode, the user will only be able to specify the x range, similarly for
17 "y" mode. For "xy", the selection becomes a rectangle where both ranges can be
18 specified. "color" is color of the selection (if you need to change the color
19 later on, you can get to it with plot.getOptions().selection.color). "shape"
20 is the shape of the corners of the selection.
21
22 "minSize" is the minimum size a selection can be in pixels. This value can
23 be customized to determine the smallest size a selection can be and still
24 have the selection rectangle be displayed. When customizing this value, the
25 fact that it refers to pixels, not axis units must be taken into account.
26 Thus, for example, if there is a bar graph in time mode with BarWidth set to 1
27 minute, setting "minSize" to 1 will not make the minimum selection size 1
28 minute, but rather 1 pixel. Note also that setting "minSize" to 0 will prevent
29 "plotunselected" events from being fired when the user clicks the mouse without
30 dragging.
31
32 When selection support is enabled, a "plotselected" event will be emitted on
33 the DOM element you passed into the plot function. The event handler gets a
34 parameter with the ranges selected on the axes, like this:
35
36 placeholder.on( "plotselected", function( event, ranges ) {
37 alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to)
38 // similar for yaxis - with multiple axes, the extra ones are in
39 // x2axis, x3axis, ...
40 });
41
42 The "plotselected" event is only fired when the user has finished making the
43 selection. A "plotselecting" event is fired during the process with the same
44 parameters as the "plotselected" event, in case you want to know what's
45 happening while it's happening,
46
47 A "plotunselected" event with no arguments is emitted when the user clicks the
48 mouse to remove the selection. As stated above, setting "minSize" to 0 will
49 destroy this behavior.
50
51 The plugin allso adds the following methods to the plot object:
52
53 - setSelection( ranges, preventEvent )
54
55 Set the selection rectangle. The passed in ranges is on the same form as
56 returned in the "plotselected" event. If the selection mode is "x", you
57 should put in either an xaxis range, if the mode is "y" you need to put in
58 an yaxis range and both xaxis and yaxis if the selection mode is "xy", like
59 this:
60
61 setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } });
62
63 setSelection will trigger the "plotselected" event when called. If you don't
64 want that to happen, e.g. if you're inside a "plotselected" handler, pass
65 true as the second parameter. If you are using multiple axes, you can
66 specify the ranges on any of those, e.g. as x2axis/x3axis/... instead of
67 xaxis, the plugin picks the first one it sees.
68
69 - clearSelection( preventEvent )
70
71 Clear the selection rectangle. Pass in true to avoid getting a
72 "plotunselected" event.
73
74 - getSelection()
75
76 Returns the current selection in the same format as the "plotselected"
77 event. If there's currently no selection, the function returns null.
78
79 */
80
81 (function ($) {
82 function init(plot) {
83 var selection = {
84 first: { x: -1, y: -1}, second: { x: -1, y: -1},
85 show: false,
86 active: false
87 };
88
89 // FIXME: The drag handling implemented here should be
90 // abstracted out, there's some similar code from a library in
91 // the navigation plugin, this should be massaged a bit to fit
92 // the Flot cases here better and reused. Doing this would
93 // make this plugin much slimmer.
94 var savedhandlers = {};
95
96 var mouseUpHandler = null;
97
98 function onMouseMove(e) {
99 if (selection.active) {
100 updateSelection(e);
101
102 plot.getPlaceholder().trigger("plotselecting", [ getSelection() ]);
103 }
104 }
105
106 function onMouseDown(e) {
107 if (e.which != 1) // only accept left-click
108 return;
109
110 // cancel out any text selections
111 document.body.focus();
112
113 // prevent text selection and drag in old-school browsers
114 if (document.onselectstart !== undefined && savedhandlers.onselectstart == null) {
115 savedhandlers.onselectstart = document.onselectstart;
116 document.onselectstart = function () { return false; };
117 }
118 if (document.ondrag !== undefined && savedhandlers.ondrag == null) {
119 savedhandlers.ondrag = document.ondrag;
120 document.ondrag = function () { return false; };
121 }
122
123 setSelectionPos(selection.first, e);
124
125 selection.active = true;
126
127 // this is a bit silly, but we have to use a closure to be
128 // able to whack the same handler again
129 mouseUpHandler = function (e) { onMouseUp(e); };
130
131 $(document).one("mouseup", mouseUpHandler);
132 }
133
134 function onMouseUp(e) {
135 mouseUpHandler = null;
136
137 // revert drag stuff for old-school browsers
138 if (document.onselectstart !== undefined)
139 document.onselectstart = savedhandlers.onselectstart;
140 if (document.ondrag !== undefined)
141 document.ondrag = savedhandlers.ondrag;
142
143 // no more dragging
144 selection.active = false;
145 updateSelection(e);
146
147 if (selectionIsSane())
148 triggerSelectedEvent();
149 else {
150 // this counts as a clear
151 plot.getPlaceholder().trigger("plotunselected", [ ]);
152 plot.getPlaceholder().trigger("plotselecting", [ null ]);
153 }
154
155 return false;
156 }
157
158 function getSelection() {
159 if (!selectionIsSane())
160 return null;
161
162 if (!selection.show) return null;
163
164 var r = {}, c1 = selection.first, c2 = selection.second;
165 $.each(plot.getAxes(), function (name, axis) {
166 if (axis.used) {
167 var p1 = axis.c2p(c1[axis.direction]), p2 = axis.c2p(c2[axis.direction]);
168 r[name] = { from: Math.min(p1, p2), to: Math.max(p1, p2) };
169 }
170 });
171 return r;
172 }
173
174 function triggerSelectedEvent() {
175 var r = getSelection();
176
177 plot.getPlaceholder().trigger("plotselected", [ r ]);
178
179 // backwards-compat stuff, to be removed in future
180 if (r.xaxis && r.yaxis)
181 plot.getPlaceholder().trigger("selected", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]);
182 }
183
184 function clamp(min, value, max) {
185 return value < min ? min: (value > max ? max: value);
186 }
187
188 function setSelectionPos(pos, e) {
189 var o = plot.getOptions();
190 var offset = plot.getPlaceholder().offset();
191 var plotOffset = plot.getPlotOffset();
192 pos.x = clamp(0, e.pageX - offset.left - plotOffset.left, plot.width());
193 pos.y = clamp(0, e.pageY - offset.top - plotOffset.top, plot.height());
194
195 if (o.selection.mode == "y")
196 pos.x = pos == selection.first ? 0 : plot.width();
197
198 if (o.selection.mode == "x")
199 pos.y = pos == selection.first ? 0 : plot.height();
200 }
201
202 function updateSelection(pos) {
203 if (pos.pageX == null)
204 return;
205
206 setSelectionPos(selection.second, pos);
207 if (selectionIsSane()) {
208 selection.show = true;
209 plot.triggerRedrawOverlay();
210 }
211 else
212 clearSelection(true);
213 }
214
215 function clearSelection(preventEvent) {
216 if (selection.show) {
217 selection.show = false;
218 plot.triggerRedrawOverlay();
219 if (!preventEvent)
220 plot.getPlaceholder().trigger("plotunselected", [ ]);
221 }
222 }
223
224 // function taken from markings support in Flot
225 function extractRange(ranges, coord) {
226 var axis, from, to, key, axes = plot.getAxes();
227
228 for (var k in axes) {
229 axis = axes[k];
230 if (axis.direction == coord) {
231 key = coord + axis.n + "axis";
232 if (!ranges[key] && axis.n == 1)
233 key = coord + "axis"; // support x1axis as xaxis
234 if (ranges[key]) {
235 from = ranges[key].from;
236 to = ranges[key].to;
237 break;
238 }
239 }
240 }
241
242 // backwards-compat stuff - to be removed in future
243 if (!ranges[key]) {
244 axis = coord == "x" ? plot.getXAxes()[0] : plot.getYAxes()[0];
245 from = ranges[coord + "1"];
246 to = ranges[coord + "2"];
247 }
248
249 // auto-reverse as an added bonus
250 if (from != null && to != null && from > to) {
251 var tmp = from;
252 from = to;
253 to = tmp;
254 }
255
256 return { from: from, to: to, axis: axis };
257 }
258
259 function setSelection(ranges, preventEvent) {
260 var axis, range, o = plot.getOptions();
261
262 if (o.selection.mode == "y") {
263 selection.first.x = 0;
264 selection.second.x = plot.width();
265 }
266 else {
267 range = extractRange(ranges, "x");
268
269 selection.first.x = range.axis.p2c(range.from);
270 selection.second.x = range.axis.p2c(range.to);
271 }
272
273 if (o.selection.mode == "x") {
274 selection.first.y = 0;
275 selection.second.y = plot.height();
276 }
277 else {
278 range = extractRange(ranges, "y");
279
280 selection.first.y = range.axis.p2c(range.from);
281 selection.second.y = range.axis.p2c(range.to);
282 }
283
284 selection.show = true;
285 plot.triggerRedrawOverlay();
286 if (!preventEvent && selectionIsSane())
287 triggerSelectedEvent();
288 }
289
290 function selectionIsSane() {
291 var minSize = plot.getOptions().selection.minSize;
292 return Math.abs(selection.second.x - selection.first.x) >= minSize &&
293 Math.abs(selection.second.y - selection.first.y) >= minSize;
294 }
295
296 plot.clearSelection = clearSelection;
297 plot.setSelection = setSelection;
298 plot.getSelection = getSelection;
299
300 plot.hooks.bindEvents.push(function(plot, eventHolder) {
301 var o = plot.getOptions();
302 if (o.selection.mode != null) {
303 eventHolder.mousemove(onMouseMove);
304 eventHolder.mousedown(onMouseDown);
305 }
306 });
307
308
309 plot.hooks.drawOverlay.push(function (plot, ctx) {
310 // draw selection
311 if (selection.show && selectionIsSane()) {
312 var plotOffset = plot.getPlotOffset();
313 var o = plot.getOptions();
314
315 ctx.save();
316 ctx.translate(plotOffset.left, plotOffset.top);
317
318 var c = $.color.parse(o.selection.color);
319
320 ctx.strokeStyle = c.scale('a', 0.8).toString();
321 ctx.lineWidth = 1;
322 ctx.lineJoin = o.selection.shape;
323 ctx.fillStyle = c.scale('a', 0.4).toString();
324
325 var x = Math.min(selection.first.x, selection.second.x) + 0.5,
326 y = Math.min(selection.first.y, selection.second.y) + 0.5,
327 w = Math.abs(selection.second.x - selection.first.x) - 1,
328 h = Math.abs(selection.second.y - selection.first.y) - 1;
329
330 ctx.fillRect(x, y, w, h);
331 ctx.strokeRect(x, y, w, h);
332
333 ctx.restore();
334 }
335 });
336
337 plot.hooks.shutdown.push(function (plot, eventHolder) {
338 eventHolder.off("mousemove", onMouseMove);
339 eventHolder.off("mousedown", onMouseDown);
340
341 if (mouseUpHandler)
342 $(document).off("mouseup", mouseUpHandler);
343 });
344
345 }
346
347 $.plot.plugins.push({
348 init: init,
349 options: {
350 selection: {
351 mode: null, // one of null, "x", "y" or "xy"
352 color: "#e8cfac",
353 shape: "round", // one of "round", "miter", or "bevel"
354 minSize: 5 // minimum number of pixels
355 }
356 },
357 name: 'selection',
358 version: '1.1'
359 });
360 })(jQuery);