Update data/params after Bugzilla 4.2.11 upgrade
[WebKit-https.git] / Websites / perf.webkit.org / public / js / jquery.flot.selection.js
1 /* Flot plugin for selecting regions of a plot.
2
3 Copyright (c) 2007-2012 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 }
12
13 Selection support is enabled by setting the mode to one of "x", "y" or "xy".
14 In "x" mode, the user will only be able to specify the x range, similarly for
15 "y" mode. For "xy", the selection becomes a rectangle where both ranges can be
16 specified. "color" is color of the selection (if you need to change the color
17 later on, you can get to it with plot.getOptions().selection.color).
18
19 When selection support is enabled, a "plotselected" event will be emitted on
20 the DOM element you passed into the plot function. The event handler gets a
21 parameter with the ranges selected on the axes, 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 making the
30 selection. A "plotselecting" event is fired during the process with the same
31 parameters as the "plotselected" event, in case you want to know what's
32 happening while it's happening,
33
34 A "plotunselected" event with no arguments is emitted when the user clicks the
35 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 form as
42   returned in the "plotselected" event. If the selection mode is "x", you
43   should put in either an xaxis range, if the mode is "y" you need to put in
44   an yaxis range and both xaxis and yaxis if the selection mode is "xy", like
45   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 you don't
50   want that to happen, e.g. if you're inside a "plotselected" handler, pass
51   true as the second parameter. If you are using multiple axes, you can
52   specify the ranges on any of those, e.g. as x2axis/x3axis/... instead of
53   xaxis, the plugin picks the first one it sees.
54
55 - clearSelection( preventEvent )
56
57   Clear the selection rectangle. Pass in true to avoid getting a
58   "plotunselected" event.
59
60 - getSelection()
61
62   Returns the current selection in the same format as the "plotselected"
63   event. If there's currently no selection, the function returns null.
64
65 */
66
67 (function ($) {
68     function init(plot) {
69         var selection = {
70                 first: { x: -1, y: -1}, second: { x: -1, y: -1},
71                 show: false,
72                 active: false
73             };
74
75         // FIXME: The drag handling implemented here should be
76         // abstracted out, there's some similar code from a library in
77         // the navigation plugin, this should be massaged a bit to fit
78         // the Flot cases here better and reused. Doing this would
79         // make this plugin much slimmer.
80         var savedhandlers = {};
81
82         var mouseUpHandler = null;
83         
84         function onMouseMove(e) {
85             if (selection.active) {
86                 updateSelection(e);
87                 
88                 plot.getPlaceholder().trigger("plotselecting", [ getSelection() ]);
89             }
90         }
91
92         function onMouseDown(e) {
93             if (e.which != 1)  // only accept left-click
94                 return;
95             
96             // cancel out any text selections
97             document.body.focus();
98
99             // prevent text selection and drag in old-school browsers
100             if (document.onselectstart !== undefined && savedhandlers.onselectstart == null) {
101                 savedhandlers.onselectstart = document.onselectstart;
102                 document.onselectstart = function () { return false; };
103             }
104             if (document.ondrag !== undefined && savedhandlers.ondrag == null) {
105                 savedhandlers.ondrag = document.ondrag;
106                 document.ondrag = function () { return false; };
107             }
108
109             setSelectionPos(selection.first, e);
110
111             selection.active = true;
112
113             // this is a bit silly, but we have to use a closure to be
114             // able to whack the same handler again
115             mouseUpHandler = function (e) { onMouseUp(e); };
116             
117             $(document).one("mouseup", mouseUpHandler);
118         }
119
120         function onMouseUp(e) {
121             mouseUpHandler = null;
122             
123             // revert drag stuff for old-school browsers
124             if (document.onselectstart !== undefined)
125                 document.onselectstart = savedhandlers.onselectstart;
126             if (document.ondrag !== undefined)
127                 document.ondrag = savedhandlers.ondrag;
128
129             // no more dragging
130             selection.active = false;
131             updateSelection(e);
132
133             if (selectionIsSane())
134                 triggerSelectedEvent();
135             else {
136                 // this counts as a clear
137                 plot.getPlaceholder().trigger("plotunselected", [ ]);
138                 plot.getPlaceholder().trigger("plotselecting", [ null ]);
139             }
140
141             return false;
142         }
143
144         function getSelection() {
145             if (!selectionIsSane())
146                 return null;
147             
148             if (!selection.show) 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) + 0.5,
312                     y = Math.min(selection.first.y, selection.second.y) + 0.5,
313                     w = Math.abs(selection.second.x - selection.first.x) - 1,
314                     h = Math.abs(selection.second.y - selection.first.y) - 1;
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);