1 /* Flot plugin for adding the ability to pan and zoom the plot.
3 Copyright (c) 2007-2013 IOLA and Ole Laursen.
4 Licensed under the MIT license.
6 The default behaviour is double click and scrollwheel up/down to zoom in, drag
7 to pan. The plugin defines plot.zoom({ center }), plot.zoomOut() and
8 plot.pan( offset ) so you easily can add custom controls. It also fires
9 "plotpan" and "plotzoom" events, useful for synchronizing plots.
11 The plugin supports these options:
15 trigger: "dblclick" // or "click" for single click
16 amount: 1.5 // 2 = 200% (zoom in), 0.5 = 50% (zoom out)
21 cursor: "move" // CSS mouse cursor value used when dragging, e.g. "pointer"
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
30 "interactive" enables the built-in drag/click behaviour. If you enable
31 interactive for pan, then you'll have a basic plot that supports moving
32 around; the same for zoom.
34 "amount" specifies the default amount to zoom in (so 1.5 = 150%) relative to
37 "cursor" is a standard CSS mouse cursor string used for visual feedback to the
40 "frameRate" specifies the maximum number of times per second the plot will
41 update itself while the user is panning around on it (set to null to disable
42 intermediate pans, the plot will then not update until the mouse button is
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
47 and max is smaller than 1 or larger than 100. You can set either end to null
48 to ignore, e.g. [1, null]. If you set zoomRange to false, zooming on that axis
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
53 be null, e.g. [-10, null]. If you set panRange to false, panning on that axis
60 // zoom default amount in on the pixel ( 10, 20 )
61 plot.zoom({ center: { left: 10, top: 20 } });
64 plot.zoomOut({ center: { left: 10, top: 20 } });
66 // zoom 200% in on the pixel (10, 20)
67 plot.zoom({ amount: 2, center: { left: 10, top: 20 } });
69 // pan 100 pixels to the left and 20 down
70 plot.pan({ left: -100, top: 20 })
72 Here, "center" specifies where the center of the zooming should happen. Note
73 that this is defined in pixel space, not the space of the data points (you can
74 use the p2c helpers on the axes in Flot to help you convert between these).
76 "amount" is the amount to zoom the viewport relative to the current range, so
77 1 is 100% (i.e. no change), 1.5 is 150% (zoom in), 0.7 is 70% (zoom out). You
78 can set the default in the options.
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.
87 jquery.event.drag.js ~ v1.5 ~ Copyright (c) 2008, Three Dub Media (http://threedubmedia.com)
88 Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt
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);
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
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);
111 zoomRange: null, // or [number, number] (min range, max range)
112 panRange: null // or [number, number] (min, max)
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)
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;
132 plot.zoomOut({ center: c });
134 plot.zoom({ center: c });
137 function onMouseWheel(e, delta) {
139 onZoomClick(e, delta < 0);
143 var prevCursor = 'default', prevPageX = 0, prevPageY = 0,
146 function onDragStart(e) {
147 if (e.which != 1) // only accept left-click
149 var c = plot.getPlaceholder().css('cursor');
152 plot.getPlaceholder().css('cursor', plot.getOptions().pan.cursor);
158 var frameRate = plot.getOptions().pan.frameRate;
159 if (panTimeout || !frameRate)
162 panTimeout = setTimeout(function () {
163 plot.pan({ left: prevPageX - e.pageX,
164 top: prevPageY - e.pageY });
169 }, 1 / frameRate * 1000);
172 function onDragEnd(e) {
174 clearTimeout(panTimeout);
178 plot.getPlaceholder().css('cursor', prevCursor);
179 plot.pan({ left: prevPageX - e.pageX,
180 top: prevPageY - e.pageY });
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);
190 if (o.pan.interactive) {
191 eventHolder.bind("dragstart", { distance: 10 }, onDragStart);
192 eventHolder.bind("drag", onDrag);
193 eventHolder.bind("dragend", onDragEnd);
197 plot.zoomOut = function (args) {
202 args.amount = plot.getOptions().zoom.amount;
204 args.amount = 1 / args.amount;
208 plot.zoom = function (args) {
213 amount = args.amount || plot.getOptions().zoom.amount,
214 w = plot.width(), h = plot.height();
217 c = { left: w / 2, top: h / 2 };
223 min: c.left - xf * w / amount,
224 max: c.left + (1 - xf) * w / amount
227 min: c.top - yf * h / amount,
228 max: c.top + (1 - yf) * h / amount
232 $.each(plot.getAxes(), function(_, axis) {
233 var opts = axis.options,
234 min = minmax[axis.direction].min,
235 max = minmax[axis.direction].max,
239 if (zr === false) // no zooming on this axis
245 // make sure min < max
251 //Check that we are in panRange
253 if (pr[0] != null && min < pr[0]) {
256 if (pr[1] != null && max > pr[1]) {
261 var range = max - min;
263 ((zr[0] != null && range < zr[0]) ||
264 (zr[1] != null && range > zr[1])))
274 if (!args.preventEvent)
275 plot.getPlaceholder().trigger("plotzoom", [ plot, args ]);
278 plot.pan = function (args) {
289 $.each(plot.getAxes(), function (_, axis) {
290 var opts = axis.options,
291 min, max, d = delta[axis.direction];
293 min = axis.c2p(axis.p2c(axis.min) + d),
294 max = axis.c2p(axis.p2c(axis.max) + d);
296 var pr = opts.panRange;
297 if (pr === false) // no panning on this axis
301 // check whether we hit the wall
302 if (pr[0] != null && pr[0] > min) {
308 if (pr[1] != null && pr[1] < max) {
322 if (!args.preventEvent)
323 plot.getPlaceholder().trigger("plotpan", [ plot, args ]);
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);
333 clearTimeout(panTimeout);
336 plot.hooks.bindEvents.push(bindEvents);
337 plot.hooks.shutdown.push(shutdown);
340 $.plot.plugins.push({