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