13551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)/* Flot plugin for adding the ability to pan and zoom the plot.
23551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
33551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)Copyright (c) 2007-2013 IOLA and Ole Laursen.
43551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)Licensed under the MIT license.
53551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
63551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)The default behaviour is double click and scrollwheel up/down to zoom in, drag
73551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)to pan. The plugin defines plot.zoom({ center }), plot.zoomOut() and
83551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)plot.pan( offset ) so you easily can add custom controls. It also fires
93551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)"plotpan" and "plotzoom" events, useful for synchronizing plots.
103551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
113551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)The plugin supports these options:
123551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
133551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)	zoom: {
143551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)		interactive: false
153551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)		trigger: "dblclick" // or "click" for single click
163551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)		amount: 1.5         // 2 = 200% (zoom in), 0.5 = 50% (zoom out)
173551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)	}
183551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
193551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)	pan: {
203551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)		interactive: false
213551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)		cursor: "move"      // CSS mouse cursor value used when dragging, e.g. "pointer"
223551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)		frameRate: 20
233551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)	}
243551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
253551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)	xaxis, yaxis, x2axis, y2axis: {
263551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)		zoomRange: null  // or [ number, number ] (min range, max range) or false
273551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)		panRange: null   // or [ number, number ] (min, max) or false
283551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)	}
293551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
303551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)"interactive" enables the built-in drag/click behaviour. If you enable
313551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)interactive for pan, then you'll have a basic plot that supports moving
323551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)around; the same for zoom.
333551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
343551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)"amount" specifies the default amount to zoom in (so 1.5 = 150%) relative to
353551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)the current viewport.
363551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
373551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)"cursor" is a standard CSS mouse cursor string used for visual feedback to the
383551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)user when dragging.
393551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
403551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)"frameRate" specifies the maximum number of times per second the plot will
413551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)update itself while the user is panning around on it (set to null to disable
423551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)intermediate pans, the plot will then not update until the mouse button is
433551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)released).
443551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
453551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)"zoomRange" is the interval in which zooming can happen, e.g. with zoomRange:
463551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)[1, 100] the zoom will never scale the axis so that the difference between min
473551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)and max is smaller than 1 or larger than 100. You can set either end to null
483551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)to ignore, e.g. [1, null]. If you set zoomRange to false, zooming on that axis
493551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)will be disabled.
503551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
513551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)"panRange" confines the panning to stay within a range, e.g. with panRange:
523551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)[-10, 20] panning stops at -10 in one end and at 20 in the other. Either can
533551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)be null, e.g. [-10, null]. If you set panRange to false, panning on that axis
543551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)will be disabled.
553551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
563551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)Example API usage:
573551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
583551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)	plot = $.plot(...);
593551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
603551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)	// zoom default amount in on the pixel ( 10, 20 )
613551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)	plot.zoom({ center: { left: 10, top: 20 } });
623551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
633551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)	// zoom out again
643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)	plot.zoomOut({ center: { left: 10, top: 20 } });
653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
663551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)	// zoom 200% in on the pixel (10, 20)
673551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)	plot.zoom({ amount: 2, center: { left: 10, top: 20 } });
683551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
693551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)	// pan 100 pixels to the left and 20 down
703551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)	plot.pan({ left: -100, top: 20 })
713551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
723551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)Here, "center" specifies where the center of the zooming should happen. Note
733551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)that this is defined in pixel space, not the space of the data points (you can
743551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)use the p2c helpers on the axes in Flot to help you convert between these).
753551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
763551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)"amount" is the amount to zoom the viewport relative to the current range, so
773551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)1 is 100% (i.e. no change), 1.5 is 150% (zoom in), 0.7 is 70% (zoom out). You
783551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)can set the default in the options.
793551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
803551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)*/// First two dependencies, jquery.event.drag.js and
813551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)// jquery.mousewheel.js, we put them inline here to save people the
823551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)// effort of downloading them.
833551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)/*
843551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)jquery.event.drag.js ~ v1.5 ~ Copyright (c) 2008, Three Dub Media (http://threedubmedia.com)
853551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt
863551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)*/(function(e){function t(i){var l,h=this,p=i.data||{};if(p.elem)h=i.dragTarget=p.elem,i.dragProxy=a.proxy||h,i.cursorOffsetX=p.pageX-p.left,i.cursorOffsetY=p.pageY-p.top,i.offsetX=i.pageX-i.cursorOffsetX,i.offsetY=i.pageY-i.cursorOffsetY;else if(a.dragging||p.which>0&&i.which!=p.which||e(i.target).is(p.not))return;switch(i.type){case"mousedown":return e.extend(p,e(h).offset(),{elem:h,target:i.target,pageX:i.pageX,pageY:i.pageY}),o.add(document,"mousemove mouseup",t,p),s(h,!1),a.dragging=null,!1;case!a.dragging&&"mousemove":if(r(i.pageX-p.pageX)+r(i.pageY-p.pageY)<p.distance)break;i.target=p.target,l=n(i,"dragstart",h),l!==!1&&(a.dragging=h,a.proxy=i.dragProxy=e(l||h)[0]);case"mousemove":if(a.dragging){if(l=n(i,"drag",h),u.drop&&(u.drop.allowed=l!==!1,u.drop.handler(i)),l!==!1)break;i.type="mouseup"};case"mouseup":o.remove(document,"mousemove mouseup",t),a.dragging&&(u.drop&&u.drop.handler(i),n(i,"dragend",h)),s(h,!0),a.dragging=a.proxy=p.elem=!1}return!0}function n(t,n,r){t.type=n;var i=e.event.dispatch.call(r,t);return i===!1?!1:i||t.result}function r(e){return Math.pow(e,2)}function i(){return a.dragging===!1}function s(e,t){e&&(e.unselectable=t?"off":"on",e.onselectstart=function(){return t},e.style&&(e.style.MozUserSelect=t?"":"none"))}e.fn.drag=function(e,t,n){return t&&this.bind("dragstart",e),n&&this.bind("dragend",n),e?this.bind("drag",t?t:e):this.trigger("drag")};var o=e.event,u=o.special,a=u.drag={not:":input",distance:0,which:1,dragging:!1,setup:function(n){n=e.extend({distance:a.distance,which:a.which,not:a.not},n||{}),n.distance=r(n.distance),o.add(this,"mousedown",t,n),this.attachEvent&&this.attachEvent("ondragstart",i)},teardown:function(){o.remove(this,"mousedown",t),this===a.dragging&&(a.dragging=a.proxy=!1),s(this,!0),this.detachEvent&&this.detachEvent("ondragstart",i)}};u.dragstart=u.dragend={setup:function(){},teardown:function(){}}})(jQuery),function(e){function t(t){var n=t||window.event,r=[].slice.call(arguments,1),i=0,s=0,o=0,t=e.event.fix(n);return t.type="mousewheel",n.wheelDelta&&(i=n.wheelDelta/120),n.detail&&(i=-n.detail/3),o=i,void 0!==n.axis&&n.axis===n.HORIZONTAL_AXIS&&(o=0,s=-1*i),void 0!==n.wheelDeltaY&&(o=n.wheelDeltaY/120),void 0!==n.wheelDeltaX&&(s=-1*n.wheelDeltaX/120),r.unshift(t,i,s,o),(e.event.dispatch||e.event.handle).apply(this,r)}var n=["DOMMouseScroll","mousewheel"];if(e.event.fixHooks)for(var r=n.length;r;)e.event.fixHooks[n[--r]]=e.event.mouseHooks;e.event.special.mousewheel={setup:function(){if(this.addEventListener)for(var e=n.length;e;)this.addEventListener(n[--e],t,!1);else this.onmousewheel=t},teardown:function(){if(this.removeEventListener)for(var e=n.length;e;)this.removeEventListener(n[--e],t,!1);else this.onmousewheel=null}},e.fn.extend({mousewheel:function(e){return e?this.bind("mousewheel",e):this.trigger("mousewheel")},unmousewheel:function(e){return this.unbind("mousewheel",e)}})}(jQuery),function(e){function n(t){function n(e,n){var r=t.offset();r.left=e.pageX-r.left,r.top=e.pageY-r.top,n?t.zoomOut({center:r}):t.zoom({center:r})}function r(e,t){return e.preventDefault(),n(e,t<0),!1}function a(e){if(e.which!=1)return!1;var n=t.getPlaceholder().css("cursor");n&&(i=n),t.getPlaceholder().css("cursor",t.getOptions().pan.cursor),s=e.pageX,o=e.pageY}function f(e){var n=t.getOptions().pan.frameRate;if(u||!n)return;u=setTimeout(function(){t.pan({left:s-e.pageX,top:o-e.pageY}),s=e.pageX,o=e.pageY,u=null},1/n*1e3)}function l(e){u&&(clearTimeout(u),u=null),t.getPlaceholder().css("cursor",i),t.pan({left:s-e.pageX,top:o-e.pageY})}function c(e,t){var i=e.getOptions();i.zoom.interactive&&(t[i.zoom.trigger](n),t.mousewheel(r)),i.pan.interactive&&(t.bind("dragstart",{distance:10},a),t.bind("drag",f),t.bind("dragend",l))}function h(e,t){t.unbind(e.getOptions().zoom.trigger,n),t.unbind("mousewheel",r),t.unbind("dragstart",a),t.unbind("drag",f),t.unbind("dragend",l),u&&clearTimeout(u)}var i="default",s=0,o=0,u=null;t.zoomOut=function(e){e||(e={}),e.amount||(e.amount=t.getOptions().zoom.amount),e.amount=1/e.amount,t.zoom(e)},t.zoom=function(n){n||(n={});var r=n.center,i=n.amount||t.getOptions().zoom.amount,s=t.width(),o=t.height();r||(r={left:s/2,top:o/2});var u=r.left/s,a=r.top/o,f={x:{min:r.left-u*s/i,max:r.left+(1-u)*s/i},y:{min:r.top-a*o/i,max:r.top+(1-a)*o/i}};e.each(t.getAxes(),function(e,t){var n=t.options,r=f[t.direction].min,i=f[t.direction].max,s=n.zoomRange,o=n.panRange;if(s===!1)return;r=t.c2p(r),i=t.c2p(i);if(r>i){var u=r;r=i,i=u}o&&(o[0]!=null&&r<o[0]&&(r=o[0]),o[1]!=null&&i>o[1]&&(i=o[1]));var a=i-r;if(s&&(s[0]!=null&&a<s[0]||s[1]!=null&&a>s[1]))return;n.min=r,n.max=i}),t.setupGrid(),t.draw(),n.preventEvent||t.getPlaceholder().trigger("plotzoom",[t,n])},t.pan=function(n){var r={x:+n.left,y:+n.top};isNaN(r.x)&&(r.x=0),isNaN(r.y)&&(r.y=0),e.each(t.getAxes(),function(e,t){var n=t.options,i,s,o=r[t.direction];i=t.c2p(t.p2c(t.min)+o),s=t.c2p(t.p2c(t.max)+o);var u=n.panRange;if(u===!1)return;u&&(u[0]!=null&&u[0]>i&&(o=u[0]-i,i+=o,s+=o),u[1]!=null&&u[1]<s&&(o=u[1]-s,i+=o,s+=o)),n.min=i,n.max=s}),t.setupGrid(),t.draw(),n.preventEvent||t.getPlaceholder().trigger("plotpan",[t,n])},t.hooks.bindEvents.push(c),t.hooks.shutdown.push(h)}var t={xaxis:{zoomRange:null,panRange:null},zoom:{interactive:!1,trigger:"dblclick",amount:1.5},pan:{interactive:!1,cursor:"move",frameRate:20}};e.plot.plugins.push({init:n,options:t,name:"navigate",version:"1.3"})}(jQuery);