• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/* Flot plugin for adding the ability to pan and zoom the plot.
2
3Copyright (c) 2007-2014 IOLA and Ole Laursen.
4Licensed under the MIT license.
5
6The default behaviour is double click and scrollwheel up/down to zoom in, drag
7to pan. The plugin defines plot.zoom({ center }), plot.zoomOut() and
8plot.pan( offset ) so you easily can add custom controls. It also fires
9"plotpan" and "plotzoom" events, useful for synchronizing plots.
10
11The plugin supports these options:
12
13	zoom: {
14		interactive: false
15		trigger: "dblclick" // or "click" for single click
16		amount: 1.5         // 2 = 200% (zoom in), 0.5 = 50% (zoom out)
17	}
18
19	pan: {
20		interactive: false
21		cursor: "move"      // CSS mouse cursor value used when dragging, e.g. "pointer"
22		frameRate: 20
23	}
24
25	xaxis, yaxis, x2axis, y2axis: {
26		zoomRange: null  // or [ number, number ] (min range, max range) or false
27		panRange: null   // or [ number, number ] (min, max) or false
28	}
29
30"interactive" enables the built-in drag/click behaviour. If you enable
31interactive for pan, then you'll have a basic plot that supports moving
32around; the same for zoom.
33
34"amount" specifies the default amount to zoom in (so 1.5 = 150%) relative to
35the current viewport.
36
37"cursor" is a standard CSS mouse cursor string used for visual feedback to the
38user when dragging.
39
40"frameRate" specifies the maximum number of times per second the plot will
41update itself while the user is panning around on it (set to null to disable
42intermediate pans, the plot will then not update until the mouse button is
43released).
44
45"zoomRange" is the interval in which zooming can happen, e.g. with zoomRange:
46[1, 100] the zoom will never scale the axis so that the difference between min
47and max is smaller than 1 or larger than 100. You can set either end to null
48to ignore, e.g. [1, null]. If you set zoomRange to false, zooming on that axis
49will be disabled.
50
51"panRange" confines the panning to stay within a range, e.g. with panRange:
52[-10, 20] panning stops at -10 in one end and at 20 in the other. Either can
53be null, e.g. [-10, null]. If you set panRange to false, panning on that axis
54will be disabled.
55
56Example API usage:
57
58	plot = $.plot(...);
59
60	// zoom default amount in on the pixel ( 10, 20 )
61	plot.zoom({ center: { left: 10, top: 20 } });
62
63	// zoom out again
64	plot.zoomOut({ center: { left: 10, top: 20 } });
65
66	// zoom 200% in on the pixel (10, 20)
67	plot.zoom({ amount: 2, center: { left: 10, top: 20 } });
68
69	// pan 100 pixels to the left and 20 down
70	plot.pan({ left: -100, top: 20 })
71
72Here, "center" specifies where the center of the zooming should happen. Note
73that this is defined in pixel space, not the space of the data points (you can
74use the p2c helpers on the axes in Flot to help you convert between these).
75
76"amount" is the amount to zoom the viewport relative to the current range, so
771 is 100% (i.e. no change), 1.5 is 150% (zoom in), 0.7 is 70% (zoom out). You
78can set the default in the options.
79
80*/
81
82// First two dependencies, jquery.event.drag.js and
83// jquery.mousewheel.js, we put them inline here to save people the
84// effort of downloading them.
85
86/*
87jquery.event.drag.js ~ v1.5 ~ Copyright (c) 2008, Three Dub Media (http://threedubmedia.com)
88Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt
89*/
90(function(a){function e(h){var k,j=this,l=h.data||{};if(l.elem)j=h.dragTarget=l.elem,h.dragProxy=d.proxy||j,h.cursorOffsetX=l.pageX-l.left,h.cursorOffsetY=l.pageY-l.top,h.offsetX=h.pageX-h.cursorOffsetX,h.offsetY=h.pageY-h.cursorOffsetY;else if(d.dragging||l.which>0&&h.which!=l.which||a(h.target).is(l.not))return;switch(h.type){case"mousedown":return a.extend(l,a(j).offset(),{elem:j,target:h.target,pageX:h.pageX,pageY:h.pageY}),b.add(document,"mousemove mouseup",e,l),i(j,!1),d.dragging=null,!1;case!d.dragging&&"mousemove":if(g(h.pageX-l.pageX)+g(h.pageY-l.pageY)<l.distance)break;h.target=l.target,k=f(h,"dragstart",j),k!==!1&&(d.dragging=j,d.proxy=h.dragProxy=a(k||j)[0]);case"mousemove":if(d.dragging){if(k=f(h,"drag",j),c.drop&&(c.drop.allowed=k!==!1,c.drop.handler(h)),k!==!1)break;h.type="mouseup"}case"mouseup":b.remove(document,"mousemove mouseup",e),d.dragging&&(c.drop&&c.drop.handler(h),f(h,"dragend",j)),i(j,!0),d.dragging=d.proxy=l.elem=!1}return!0}function f(b,c,d){b.type=c;var e=a.event.dispatch.call(d,b);return e===!1?!1:e||b.result}function g(a){return Math.pow(a,2)}function h(){return d.dragging===!1}function i(a,b){a&&(a.unselectable=b?"off":"on",a.onselectstart=function(){return b},a.style&&(a.style.MozUserSelect=b?"":"none"))}a.fn.drag=function(a,b,c){return b&&this.bind("dragstart",a),c&&this.bind("dragend",c),a?this.bind("drag",b?b:a):this.trigger("drag")};var b=a.event,c=b.special,d=c.drag={not:":input",distance:0,which:1,dragging:!1,setup:function(c){c=a.extend({distance:d.distance,which:d.which,not:d.not},c||{}),c.distance=g(c.distance),b.add(this,"mousedown",e,c),this.attachEvent&&this.attachEvent("ondragstart",h)},teardown:function(){b.remove(this,"mousedown",e),this===d.dragging&&(d.dragging=d.proxy=!1),i(this,!0),this.detachEvent&&this.detachEvent("ondragstart",h)}};c.dragstart=c.dragend={setup:function(){},teardown:function(){}}})(jQuery);
91
92/* jquery.mousewheel.min.js
93 * Copyright (c) 2011 Brandon Aaron (http://brandonaaron.net)
94 * Licensed under the MIT License (LICENSE.txt).
95 * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
96 * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
97 * Thanks to: Seamus Leahy for adding deltaX and deltaY
98 *
99 * Version: 3.0.6
100 *
101 * Requires: 1.2.2+
102 */
103(function(d){function e(a){var b=a||window.event,c=[].slice.call(arguments,1),f=0,e=0,g=0,a=d.event.fix(b);a.type="mousewheel";b.wheelDelta&&(f=b.wheelDelta/120);b.detail&&(f=-b.detail/3);g=f;void 0!==b.axis&&b.axis===b.HORIZONTAL_AXIS&&(g=0,e=-1*f);void 0!==b.wheelDeltaY&&(g=b.wheelDeltaY/120);void 0!==b.wheelDeltaX&&(e=-1*b.wheelDeltaX/120);c.unshift(a,f,e,g);return(d.event.dispatch||d.event.handle).apply(this,c)}var c=["DOMMouseScroll","mousewheel"];if(d.event.fixHooks)for(var h=c.length;h;)d.event.fixHooks[c[--h]]=d.event.mouseHooks;d.event.special.mousewheel={setup:function(){if(this.addEventListener)for(var a=c.length;a;)this.addEventListener(c[--a],e,!1);else this.onmousewheel=e},teardown:function(){if(this.removeEventListener)for(var a=c.length;a;)this.removeEventListener(c[--a],e,!1);else this.onmousewheel=null}};d.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})})(jQuery);
104
105
106
107
108(function ($) {
109    var options = {
110        xaxis: {
111            zoomRange: null, // or [number, number] (min range, max range)
112            panRange: null // or [number, number] (min, max)
113        },
114        zoom: {
115            interactive: false,
116            trigger: "dblclick", // or "click" for single click
117            amount: 1.5 // how much to zoom relative to current position, 2 = 200% (zoom in), 0.5 = 50% (zoom out)
118        },
119        pan: {
120            interactive: false,
121            cursor: "move",
122            frameRate: 20
123        }
124    };
125
126    function init(plot) {
127        function onZoomClick(e, zoomOut) {
128            var c = plot.offset();
129            c.left = e.pageX - c.left;
130            c.top = e.pageY - c.top;
131            if (zoomOut)
132                plot.zoomOut({ center: c });
133            else
134                plot.zoom({ center: c });
135        }
136
137        function onMouseWheel(e, delta) {
138            e.preventDefault();
139            onZoomClick(e, delta < 0);
140            return false;
141        }
142
143        var prevCursor = 'default', prevPageX = 0, prevPageY = 0,
144            panTimeout = null;
145
146        function onDragStart(e) {
147            if (e.which != 1)  // only accept left-click
148                return false;
149            var c = plot.getPlaceholder().css('cursor');
150            if (c)
151                prevCursor = c;
152            plot.getPlaceholder().css('cursor', plot.getOptions().pan.cursor);
153            prevPageX = e.pageX;
154            prevPageY = e.pageY;
155        }
156
157        function onDrag(e) {
158            var frameRate = plot.getOptions().pan.frameRate;
159            if (panTimeout || !frameRate)
160                return;
161
162            panTimeout = setTimeout(function () {
163                plot.pan({ left: prevPageX - e.pageX,
164                           top: prevPageY - e.pageY });
165                prevPageX = e.pageX;
166                prevPageY = e.pageY;
167
168                panTimeout = null;
169            }, 1 / frameRate * 1000);
170        }
171
172        function onDragEnd(e) {
173            if (panTimeout) {
174                clearTimeout(panTimeout);
175                panTimeout = null;
176            }
177
178            plot.getPlaceholder().css('cursor', prevCursor);
179            plot.pan({ left: prevPageX - e.pageX,
180                       top: prevPageY - e.pageY });
181        }
182
183        function bindEvents(plot, eventHolder) {
184            var o = plot.getOptions();
185            if (o.zoom.interactive) {
186                eventHolder[o.zoom.trigger](onZoomClick);
187                eventHolder.mousewheel(onMouseWheel);
188            }
189
190            if (o.pan.interactive) {
191                eventHolder.bind("dragstart", { distance: 10 }, onDragStart);
192                eventHolder.bind("drag", onDrag);
193                eventHolder.bind("dragend", onDragEnd);
194            }
195        }
196
197        plot.zoomOut = function (args) {
198            if (!args)
199                args = {};
200
201            if (!args.amount)
202                args.amount = plot.getOptions().zoom.amount;
203
204            args.amount = 1 / args.amount;
205            plot.zoom(args);
206        };
207
208        plot.zoom = function (args) {
209            if (!args)
210                args = {};
211
212            var c = args.center,
213                amount = args.amount || plot.getOptions().zoom.amount,
214                w = plot.width(), h = plot.height();
215
216            if (!c)
217                c = { left: w / 2, top: h / 2 };
218
219            var xf = c.left / w,
220                yf = c.top / h,
221                minmax = {
222                    x: {
223                        min: c.left - xf * w / amount,
224                        max: c.left + (1 - xf) * w / amount
225                    },
226                    y: {
227                        min: c.top - yf * h / amount,
228                        max: c.top + (1 - yf) * h / amount
229                    }
230                };
231
232            $.each(plot.getAxes(), function(_, axis) {
233                var opts = axis.options,
234                    min = minmax[axis.direction].min,
235                    max = minmax[axis.direction].max,
236                    zr = opts.zoomRange,
237                    pr = opts.panRange;
238
239                if (zr === false) // no zooming on this axis
240                    return;
241
242                min = axis.c2p(min);
243                max = axis.c2p(max);
244                if (min > max) {
245                    // make sure min < max
246                    var tmp = min;
247                    min = max;
248                    max = tmp;
249                }
250
251                //Check that we are in panRange
252                if (pr) {
253                    if (pr[0] != null && min < pr[0]) {
254                        min = pr[0];
255                    }
256                    if (pr[1] != null && max > pr[1]) {
257                        max = pr[1];
258                    }
259                }
260
261                var range = max - min;
262                if (zr &&
263                    ((zr[0] != null && range < zr[0] && amount >1) ||
264                     (zr[1] != null && range > zr[1] && amount <1)))
265                    return;
266
267                opts.min = min;
268                opts.max = max;
269            });
270
271            plot.setupGrid();
272            plot.draw();
273
274            if (!args.preventEvent)
275                plot.getPlaceholder().trigger("plotzoom", [ plot, args ]);
276        };
277
278        plot.pan = function (args) {
279            var delta = {
280                x: +args.left,
281                y: +args.top
282            };
283
284            if (isNaN(delta.x))
285                delta.x = 0;
286            if (isNaN(delta.y))
287                delta.y = 0;
288
289            $.each(plot.getAxes(), function (_, axis) {
290                var opts = axis.options,
291                    min, max, d = delta[axis.direction];
292
293                min = axis.c2p(axis.p2c(axis.min) + d),
294                max = axis.c2p(axis.p2c(axis.max) + d);
295
296                var pr = opts.panRange;
297                if (pr === false) // no panning on this axis
298                    return;
299
300                if (pr) {
301                    // check whether we hit the wall
302                    if (pr[0] != null && pr[0] > min) {
303                        d = pr[0] - min;
304                        min += d;
305                        max += d;
306                    }
307
308                    if (pr[1] != null && pr[1] < max) {
309                        d = pr[1] - max;
310                        min += d;
311                        max += d;
312                    }
313                }
314
315                opts.min = min;
316                opts.max = max;
317            });
318
319            plot.setupGrid();
320            plot.draw();
321
322            if (!args.preventEvent)
323                plot.getPlaceholder().trigger("plotpan", [ plot, args ]);
324        };
325
326        function shutdown(plot, eventHolder) {
327            eventHolder.unbind(plot.getOptions().zoom.trigger, onZoomClick);
328            eventHolder.unbind("mousewheel", onMouseWheel);
329            eventHolder.unbind("dragstart", onDragStart);
330            eventHolder.unbind("drag", onDrag);
331            eventHolder.unbind("dragend", onDragEnd);
332            if (panTimeout)
333                clearTimeout(panTimeout);
334        }
335
336        plot.hooks.bindEvents.push(bindEvents);
337        plot.hooks.shutdown.push(shutdown);
338    }
339
340    $.plot.plugins.push({
341        init: init,
342        options: options,
343        name: 'navigate',
344        version: '1.3'
345    });
346})(jQuery);
347