Mercurial > hg > LGMap
comparison geotemco/lib/flot/jquery.flot.selection.js @ 0:57bde4830927
first commit
author | Zoe Hong <zhong@mpiwg-berlin.mpg.de> |
---|---|
date | Tue, 24 Mar 2015 11:37:17 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:57bde4830927 |
---|---|
1 /* Flot plugin for selecting regions of a plot. | |
2 | |
3 Copyright (c) 2007-2013 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.bind( "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.unbind("mousemove", onMouseMove); | |
339 eventHolder.unbind("mousedown", onMouseDown); | |
340 | |
341 if (mouseUpHandler) | |
342 $(document).unbind("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); |