OLD | NEW |
(Empty) | |
| 1 /* Flot plugin for adding the ability to pan and zoom the plot. |
| 2 |
| 3 Copyright (c) 2007-2014 IOLA and Ole Laursen. |
| 4 Licensed under the MIT license. |
| 5 |
| 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. |
| 10 |
| 11 The 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 |
| 31 interactive for pan, then you'll have a basic plot that supports moving |
| 32 around; the same for zoom. |
| 33 |
| 34 "amount" specifies the default amount to zoom in (so 1.5 = 150%) relative to |
| 35 the current viewport. |
| 36 |
| 37 "cursor" is a standard CSS mouse cursor string used for visual feedback to the |
| 38 user when dragging. |
| 39 |
| 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 |
| 43 released). |
| 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 |
| 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 |
| 49 will 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 |
| 53 be null, e.g. [-10, null]. If you set panRange to false, panning on that axis |
| 54 will be disabled. |
| 55 |
| 56 Example 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 |
| 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). |
| 75 |
| 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. |
| 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 /* |
| 87 jquery.event.drag.js ~ v1.5 ~ Copyright (c) 2008, Three Dub Media (http://threed
ubmedia.com) |
| 88 Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-L
ICENSE.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.pag
eY-l.top,h.offsetX=h.pageX-h.cursorOffsetX,h.offsetY=h.pageY-h.cursorOffsetY;els
e if(d.dragging||l.which>0&&h.which!=l.which||a(h.target).is(l.not))return;switc
h(h.type){case"mousedown":return a.extend(l,a(j).offset(),{elem:j,target:h.targe
t,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.drag
ging=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.d
rop&&c.drop.handler(h),f(h,"dragend",j)),i(j,!0),d.dragging=d.proxy=l.elem=!1}re
turn!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.draggin
g===!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("dr
ag",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.distan
ce,which:d.which,not:d.not},c||{}),c.distance=g(c.distance),b.add(this,"mousedow
n",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.han
dle).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.m
ousewheel={setup:function(){if(this.addEventListener)for(var a=c.length;a;)this.
addEventListener(c[--a],e,!1);else this.onmousewheel=e},teardown:function(){if(t
his.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?t
his.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){re
turn 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 = 20
0% (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); |
OLD | NEW |