src/pyams_skin/resources/js/ext/flot/jquery.flot.navigate.js
changeset 546 9f02c09d2393
parent 98 89a28618a327
equal deleted inserted replaced
545:f3af77e371a1 546:9f02c09d2393
     1 /* Flot plugin for adding the ability to pan and zoom the plot.
     1 /* Flot plugin for adding the ability to pan and zoom the plot.
     2 
     2 
     3 Copyright (c) 2007-2014 IOLA and Ole Laursen.
     3 Copyright (c) 2007-2014 IOLA and Ole Laursen.
       
     4 Copyright (c) 2016 Ciprian Ceteras.
       
     5 Copyright (c) 2017 Raluca Portase.
     4 Licensed under the MIT license.
     6 Licensed under the MIT license.
     5 
     7 
     6 The default behaviour is double click and scrollwheel up/down to zoom in, drag
     8 */
       
     9 
       
    10 /**
       
    11 ## jquery.flot.navigate.js
       
    12 
       
    13 This flot plugin is used for adding the ability to pan and zoom the plot.
       
    14 A higher level overview is available at [interactions](interactions.md) documentation.
       
    15 
       
    16 The default behaviour is scrollwheel up/down to zoom in, drag
     7 to pan. The plugin defines plot.zoom({ center }), plot.zoomOut() and
    17 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
    18 plot.pan( offset ) so you easily can add custom controls. It also fires
     9 "plotpan" and "plotzoom" events, useful for synchronizing plots.
    19 "plotpan" and "plotzoom" events, useful for synchronizing plots.
    10 
    20 
    11 The plugin supports these options:
    21 The plugin supports these options:
    12 
    22 ```js
    13 	zoom: {
    23     zoom: {
    14 		interactive: false
    24         interactive: false,
    15 		trigger: "dblclick" // or "click" for single click
    25         active: false,
    16 		amount: 1.5         // 2 = 200% (zoom in), 0.5 = 50% (zoom out)
    26         amount: 1.5         // 2 = 200% (zoom in), 0.5 = 50% (zoom out)
    17 	}
    27     }
    18 
    28 
    19 	pan: {
    29     pan: {
    20 		interactive: false
    30         interactive: false,
    21 		cursor: "move"      // CSS mouse cursor value used when dragging, e.g. "pointer"
    31         active: false,
    22 		frameRate: 20
    32         cursor: "move",     // CSS mouse cursor value used when dragging, e.g. "pointer"
    23 	}
    33         frameRate: 60,
    24 
    34         mode: "smart"       // enable smart pan mode
    25 	xaxis, yaxis, x2axis, y2axis: {
    35     }
    26 		zoomRange: null  // or [ number, number ] (min range, max range) or false
    36 
    27 		panRange: null   // or [ number, number ] (min, max) or false
    37     xaxis: {
    28 	}
    38         axisZoom: true, //zoom axis when mouse over it is allowed
    29 
    39         plotZoom: true, //zoom axis is allowed for plot zoom
    30 "interactive" enables the built-in drag/click behaviour. If you enable
    40         axisPan: true, //pan axis when mouse over it is allowed
       
    41         plotPan: true //pan axis is allowed for plot pan
       
    42     }
       
    43 
       
    44     yaxis: {
       
    45         axisZoom: true, //zoom axis when mouse over it is allowed
       
    46         plotZoom: true, //zoom axis is allowed for plot zoom
       
    47         axisPan: true, //pan axis when mouse over it is allowed
       
    48         plotPan: true //pan axis is allowed for plot pan
       
    49     }
       
    50 ```
       
    51 **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
    52 interactive for pan, then you'll have a basic plot that supports moving
    32 around; the same for zoom.
    53 around; the same for zoom.
    33 
    54 
    34 "amount" specifies the default amount to zoom in (so 1.5 = 150%) relative to
    55 **active** is true after a touch tap on plot. This enables plot navigation.
       
    56 Once activated, zoom and pan cannot be deactivated. When the plot becomes active,
       
    57 "plotactivated" event is triggered.
       
    58 
       
    59 **amount** specifies the default amount to zoom in (so 1.5 = 150%) relative to
    35 the current viewport.
    60 the current viewport.
    36 
    61 
    37 "cursor" is a standard CSS mouse cursor string used for visual feedback to the
    62 **cursor** is a standard CSS mouse cursor string used for visual feedback to the
    38 user when dragging.
    63 user when dragging.
    39 
    64 
    40 "frameRate" specifies the maximum number of times per second the plot will
    65 **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
    66 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
    67 intermediate pans, the plot will then not update until the mouse button is
    43 released).
    68 released).
    44 
    69 
    45 "zoomRange" is the interval in which zooming can happen, e.g. with zoomRange:
    70 **mode** a string specifies the pan mode for mouse interaction. Accepted values:
    46 [1, 100] the zoom will never scale the axis so that the difference between min
    71 'manual': no pan hint or direction snapping;
    47 and max is smaller than 1 or larger than 100. You can set either end to null
    72 'smart': The graph shows pan hint bar and the pan movement will snap
    48 to ignore, e.g. [1, null]. If you set zoomRange to false, zooming on that axis
    73 to one direction when the drag direction is close to it;
    49 will be disabled.
    74 'smartLock'. The graph shows pan hint bar and the pan movement will always
    50 
    75 snap to a direction that the drag diorection started with.
    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 
    76 
    56 Example API usage:
    77 Example API usage:
    57 
    78 ```js
    58 	plot = $.plot(...);
    79     plot = $.plot(...);
    59 
    80 
    60 	// zoom default amount in on the pixel ( 10, 20 )
    81     // zoom default amount in on the pixel ( 10, 20 )
    61 	plot.zoom({ center: { left: 10, top: 20 } });
    82     plot.zoom({ center: { left: 10, top: 20 } });
    62 
    83 
    63 	// zoom out again
    84     // zoom out again
    64 	plot.zoomOut({ center: { left: 10, top: 20 } });
    85     plot.zoomOut({ center: { left: 10, top: 20 } });
    65 
    86 
    66 	// zoom 200% in on the pixel (10, 20)
    87     // zoom 200% in on the pixel (10, 20)
    67 	plot.zoom({ amount: 2, center: { left: 10, top: 20 } });
    88     plot.zoom({ amount: 2, center: { left: 10, top: 20 } });
    68 
    89 
    69 	// pan 100 pixels to the left and 20 down
    90     // pan 100 pixels to the left (changing x-range in a positive way) and 20 down
    70 	plot.pan({ left: -100, top: 20 })
    91     plot.pan({ left: -100, top: 20 })
       
    92 ```
    71 
    93 
    72 Here, "center" specifies where the center of the zooming should happen. Note
    94 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
    95 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).
    96 use the p2c helpers on the axes in Flot to help you convert between these).
    75 
    97 
    76 "amount" is the amount to zoom the viewport relative to the current range, so
    98 **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
    99 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.
   100 can set the default in the options.
    79 
       
    80 */
   101 */
    81 
   102 
    82 // First two dependencies, jquery.event.drag.js and
   103 /* eslint-enable */
    83 // jquery.mousewheel.js, we put them inline here to save people the
   104 (function($) {
    84 // effort of downloading them.
   105     'use strict';
    85 
   106 
    86 /*
       
    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
       
    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 = {
   107     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: {
   108         zoom: {
   115             interactive: false,
   109             interactive: false,
   116             trigger: "dblclick", // or "click" for single click
   110             active: false,
   117             amount: 1.5 // how much to zoom relative to current position, 2 = 200% (zoom in), 0.5 = 50% (zoom out)
   111             amount: 1.5 // how much to zoom relative to current position, 2 = 200% (zoom in), 0.5 = 50% (zoom out)
   118         },
   112         },
   119         pan: {
   113         pan: {
   120             interactive: false,
   114             interactive: false,
       
   115             active: false,
   121             cursor: "move",
   116             cursor: "move",
   122             frameRate: 20
   117             frameRate: 60,
       
   118             mode: 'smart'
       
   119         },
       
   120         recenter: {
       
   121             interactive: true
       
   122         },
       
   123         xaxis: {
       
   124             axisZoom: true, //zoom axis when mouse over it is allowed
       
   125             plotZoom: true, //zoom axis is allowed for plot zoom
       
   126             axisPan: true, //pan axis when mouse over it is allowed
       
   127             plotPan: true //pan axis is allowed for plot pan
       
   128         },
       
   129         yaxis: {
       
   130             axisZoom: true,
       
   131             plotZoom: true,
       
   132             axisPan: true,
       
   133             plotPan: true
   123         }
   134         }
   124     };
   135     };
   125 
   136 
       
   137     var saturated = $.plot.saturated;
       
   138     var browser = $.plot.browser;
       
   139     var SNAPPING_CONSTANT = $.plot.uiConstants.SNAPPING_CONSTANT;
       
   140     var PANHINT_LENGTH_CONSTANT = $.plot.uiConstants.PANHINT_LENGTH_CONSTANT;
       
   141 
   126     function init(plot) {
   142     function init(plot) {
   127         function onZoomClick(e, zoomOut) {
   143         plot.hooks.processOptions.push(initNevigation);
       
   144     }
       
   145 
       
   146     function initNevigation(plot, options) {
       
   147         var panAxes = null;
       
   148         var canDrag = false;
       
   149         var useManualPan = options.pan.mode === 'manual',
       
   150             smartPanLock = options.pan.mode === 'smartLock',
       
   151             useSmartPan = smartPanLock || options.pan.mode === 'smart';
       
   152 
       
   153         function onZoomClick(e, zoomOut, amount) {
       
   154             var page = browser.getPageXY(e);
       
   155 
   128             var c = plot.offset();
   156             var c = plot.offset();
   129             c.left = e.pageX - c.left;
   157             c.left = page.X - c.left;
   130             c.top = e.pageY - c.top;
   158             c.top = page.Y - c.top;
   131             if (zoomOut)
   159 
   132                 plot.zoomOut({ center: c });
   160             var ec = plot.getPlaceholder().offset();
   133             else
   161             ec.left = page.X - ec.left;
   134                 plot.zoom({ center: c });
   162             ec.top = page.Y - ec.top;
   135         }
   163 
       
   164             var axes = plot.getXAxes().concat(plot.getYAxes()).filter(function (axis) {
       
   165                 var box = axis.box;
       
   166                 if (box !== undefined) {
       
   167                     return (ec.left > box.left) && (ec.left < box.left + box.width) &&
       
   168                         (ec.top > box.top) && (ec.top < box.top + box.height);
       
   169                 }
       
   170             });
       
   171 
       
   172             if (axes.length === 0) {
       
   173                 axes = undefined;
       
   174             }
       
   175 
       
   176             if (zoomOut) {
       
   177                 plot.zoomOut({
       
   178                     center: c,
       
   179                     axes: axes,
       
   180                     amount: amount
       
   181                 });
       
   182             } else {
       
   183                 plot.zoom({
       
   184                     center: c,
       
   185                     axes: axes,
       
   186                     amount: amount
       
   187                 });
       
   188             }
       
   189         }
       
   190 
       
   191         var prevCursor = 'default',
       
   192             panHint = null,
       
   193             panTimeout = null,
       
   194             plotState,
       
   195             prevDragPosition = { x: 0, y: 0 },
       
   196             isPanAction = false;
   136 
   197 
   137         function onMouseWheel(e, delta) {
   198         function onMouseWheel(e, delta) {
   138             e.preventDefault();
   199             var maxAbsoluteDeltaOnMac = 1,
   139             onZoomClick(e, delta < 0);
   200                 isMacScroll = Math.abs(e.originalEvent.deltaY) <= maxAbsoluteDeltaOnMac,
   140             return false;
   201                 defaultNonMacScrollAmount = null,
   141         }
   202                 macMagicRatio = 50,
   142         
   203                 amount = isMacScroll ? 1 + Math.abs(e.originalEvent.deltaY) / macMagicRatio : defaultNonMacScrollAmount;
   143         var prevCursor = 'default', prevPageX = 0, prevPageY = 0,
   204 
   144             panTimeout = null;
   205             if (isPanAction) {
       
   206                 onDragEnd(e);
       
   207             }
       
   208 
       
   209             if (plot.getOptions().zoom.active) {
       
   210                 e.preventDefault();
       
   211                 onZoomClick(e, delta < 0, amount);
       
   212                 return false;
       
   213             }
       
   214         }
       
   215 
       
   216         plot.navigationState = function(startPageX, startPageY) {
       
   217             var axes = this.getAxes();
       
   218             var result = {};
       
   219             Object.keys(axes).forEach(function(axisName) {
       
   220                 var axis = axes[axisName];
       
   221                 result[axisName] = {
       
   222                     navigationOffset: { below: axis.options.offset.below || 0,
       
   223                         above: axis.options.offset.above || 0},
       
   224                     axisMin: axis.min,
       
   225                     axisMax: axis.max,
       
   226                     diagMode: false
       
   227                 }
       
   228             });
       
   229 
       
   230             result.startPageX = startPageX || 0;
       
   231             result.startPageY = startPageY || 0;
       
   232             return result;
       
   233         }
       
   234 
       
   235         function onMouseDown(e) {
       
   236             canDrag = true;
       
   237         }
       
   238 
       
   239         function onMouseUp(e) {
       
   240             canDrag = false;
       
   241         }
       
   242 
       
   243         function isLeftMouseButtonPressed(e) {
       
   244             return e.button === 0;
       
   245         }
   145 
   246 
   146         function onDragStart(e) {
   247         function onDragStart(e) {
   147             if (e.which != 1)  // only accept left-click
   248             if (!canDrag || !isLeftMouseButtonPressed(e)) {
   148                 return false;
   249                 return false;
       
   250             }
       
   251 
       
   252             isPanAction = true;
       
   253             var page = browser.getPageXY(e);
       
   254 
       
   255             var ec = plot.getPlaceholder().offset();
       
   256             ec.left = page.X - ec.left;
       
   257             ec.top = page.Y - ec.top;
       
   258 
       
   259             panAxes = plot.getXAxes().concat(plot.getYAxes()).filter(function (axis) {
       
   260                 var box = axis.box;
       
   261                 if (box !== undefined) {
       
   262                     return (ec.left > box.left) && (ec.left < box.left + box.width) &&
       
   263                         (ec.top > box.top) && (ec.top < box.top + box.height);
       
   264                 }
       
   265             });
       
   266 
       
   267             if (panAxes.length === 0) {
       
   268                 panAxes = undefined;
       
   269             }
       
   270 
   149             var c = plot.getPlaceholder().css('cursor');
   271             var c = plot.getPlaceholder().css('cursor');
   150             if (c)
   272             if (c) {
   151                 prevCursor = c;
   273                 prevCursor = c;
       
   274             }
       
   275 
   152             plot.getPlaceholder().css('cursor', plot.getOptions().pan.cursor);
   276             plot.getPlaceholder().css('cursor', plot.getOptions().pan.cursor);
   153             prevPageX = e.pageX;
   277 
   154             prevPageY = e.pageY;
   278             if (useSmartPan) {
       
   279                 plotState = plot.navigationState(page.X, page.Y);
       
   280             } else if (useManualPan) {
       
   281                 prevDragPosition.x = page.X;
       
   282                 prevDragPosition.y = page.Y;
       
   283             }
   155         }
   284         }
   156         
   285         
   157         function onDrag(e) {
   286         function onDrag(e) {
       
   287             if (!isPanAction) { 
       
   288                 return; 
       
   289             }
       
   290 
       
   291             var page = browser.getPageXY(e);
   158             var frameRate = plot.getOptions().pan.frameRate;
   292             var frameRate = plot.getOptions().pan.frameRate;
   159             if (panTimeout || !frameRate)
   293 
       
   294             if (frameRate === -1) {
       
   295                 if (useSmartPan) {
       
   296                     plot.smartPan({
       
   297                         x: plotState.startPageX - page.X,
       
   298                         y: plotState.startPageY - page.Y
       
   299                     }, plotState, panAxes, false, smartPanLock);
       
   300                 } else if (useManualPan) {
       
   301                     plot.pan({
       
   302                         left: prevDragPosition.x - page.X,
       
   303                         top: prevDragPosition.y - page.Y,
       
   304                         axes: panAxes
       
   305                     });
       
   306                     prevDragPosition.x = page.X;
       
   307                     prevDragPosition.y = page.Y;
       
   308                 }
   160                 return;
   309                 return;
   161 
   310             }
   162             panTimeout = setTimeout(function () {
   311 
   163                 plot.pan({ left: prevPageX - e.pageX,
   312             if (panTimeout || !frameRate) return;
   164                            top: prevPageY - e.pageY });
   313 
   165                 prevPageX = e.pageX;
   314             panTimeout = setTimeout(function() {
   166                 prevPageY = e.pageY;
   315                 if (useSmartPan) {
   167                                                     
   316                     plot.smartPan({
       
   317                         x: plotState.startPageX - page.X,
       
   318                         y: plotState.startPageY - page.Y
       
   319                     }, plotState, panAxes, false, smartPanLock);
       
   320                 } else if (useManualPan) {
       
   321                     plot.pan({
       
   322                         left: prevDragPosition.x - page.X,
       
   323                         top: prevDragPosition.y - page.Y,
       
   324                         axes: panAxes
       
   325                     });
       
   326                     prevDragPosition.x = page.X;
       
   327                     prevDragPosition.y = page.Y;
       
   328                 }
       
   329 
   168                 panTimeout = null;
   330                 panTimeout = null;
   169             }, 1 / frameRate * 1000);
   331             }, 1 / frameRate * 1000);
   170         }
   332         }
   171 
   333 
   172         function onDragEnd(e) {
   334         function onDragEnd(e) {
       
   335             if (!isPanAction) { 
       
   336                 return; 
       
   337             }
       
   338 
   173             if (panTimeout) {
   339             if (panTimeout) {
   174                 clearTimeout(panTimeout);
   340                 clearTimeout(panTimeout);
   175                 panTimeout = null;
   341                 panTimeout = null;
   176             }
   342             }
   177                     
   343 
       
   344             isPanAction = false;
       
   345             var page = browser.getPageXY(e);
       
   346 
   178             plot.getPlaceholder().css('cursor', prevCursor);
   347             plot.getPlaceholder().css('cursor', prevCursor);
   179             plot.pan({ left: prevPageX - e.pageX,
   348 
   180                        top: prevPageY - e.pageY });
   349             if (useSmartPan) {
   181         }
   350                 plot.smartPan({
   182         
   351                     x: plotState.startPageX - page.X,
       
   352                     y: plotState.startPageY - page.Y
       
   353                 }, plotState, panAxes, false, smartPanLock);
       
   354                 plot.smartPan.end();
       
   355             } else if (useManualPan) {
       
   356                 plot.pan({
       
   357                     left: prevDragPosition.x - page.X,
       
   358                     top: prevDragPosition.y - page.Y,
       
   359                     axes: panAxes
       
   360                 });
       
   361                 prevDragPosition.x = 0;
       
   362                 prevDragPosition.y = 0;
       
   363             }
       
   364         }
       
   365 
       
   366         function onDblClick(e) {
       
   367             plot.activate();
       
   368             var o = plot.getOptions()
       
   369 
       
   370             if (!o.recenter.interactive) { return; }
       
   371 
       
   372             var axes = plot.getTouchedAxis(e.clientX, e.clientY),
       
   373                 event;
       
   374 
       
   375             plot.recenter({ axes: axes[0] ? axes : null });
       
   376 
       
   377             if (axes[0]) {
       
   378                 event = new $.Event('re-center', { detail: {
       
   379                     axisTouched: axes[0]
       
   380                 }});
       
   381             } else {
       
   382                 event = new $.Event('re-center', { detail: e });
       
   383             }
       
   384             plot.getPlaceholder().trigger(event);
       
   385         }
       
   386 
       
   387         function onClick(e) {
       
   388             plot.activate();
       
   389 
       
   390             if (isPanAction) {
       
   391                 onDragEnd(e);
       
   392             }
       
   393 
       
   394             return false;
       
   395         }
       
   396 
       
   397         plot.activate = function() {
       
   398             var o = plot.getOptions();
       
   399             if (!o.pan.active || !o.zoom.active) {
       
   400                 o.pan.active = true;
       
   401                 o.zoom.active = true;
       
   402                 plot.getPlaceholder().trigger("plotactivated", [plot]);
       
   403             }
       
   404         }
       
   405 
   183         function bindEvents(plot, eventHolder) {
   406         function bindEvents(plot, eventHolder) {
   184             var o = plot.getOptions();
   407             var o = plot.getOptions();
   185             if (o.zoom.interactive) {
   408             if (o.zoom.interactive) {
   186                 eventHolder[o.zoom.trigger](onZoomClick);
       
   187                 eventHolder.mousewheel(onMouseWheel);
   409                 eventHolder.mousewheel(onMouseWheel);
   188             }
   410             }
   189 
   411 
   190             if (o.pan.interactive) {
   412             if (o.pan.interactive) {
   191                 eventHolder.bind("dragstart", { distance: 10 }, onDragStart);
   413                 plot.addEventHandler("dragstart", onDragStart, eventHolder, 0);
   192                 eventHolder.bind("drag", onDrag);
   414                 plot.addEventHandler("drag", onDrag, eventHolder, 0);
   193                 eventHolder.bind("dragend", onDragEnd);
   415                 plot.addEventHandler("dragend", onDragEnd, eventHolder, 0);
   194             }
   416                 eventHolder.bind("mousedown", onMouseDown);
   195         }
   417                 eventHolder.bind("mouseup", onMouseUp);
   196 
   418             }
   197         plot.zoomOut = function (args) {
   419 
   198             if (!args)
   420             eventHolder.dblclick(onDblClick);
       
   421             eventHolder.click(onClick);
       
   422         }
       
   423 
       
   424         plot.zoomOut = function(args) {
       
   425             if (!args) {
   199                 args = {};
   426                 args = {};
   200             
   427             }
   201             if (!args.amount)
   428 
       
   429             if (!args.amount) {
   202                 args.amount = plot.getOptions().zoom.amount;
   430                 args.amount = plot.getOptions().zoom.amount;
       
   431             }
   203 
   432 
   204             args.amount = 1 / args.amount;
   433             args.amount = 1 / args.amount;
   205             plot.zoom(args);
   434             plot.zoom(args);
   206         };
   435         };
   207         
   436 
   208         plot.zoom = function (args) {
   437         plot.zoom = function(args) {
   209             if (!args)
   438             if (!args) {
   210                 args = {};
   439                 args = {};
   211             
   440             }
       
   441 
   212             var c = args.center,
   442             var c = args.center,
   213                 amount = args.amount || plot.getOptions().zoom.amount,
   443                 amount = args.amount || plot.getOptions().zoom.amount,
   214                 w = plot.width(), h = plot.height();
   444                 w = plot.width(),
   215 
   445                 h = plot.height(),
   216             if (!c)
   446                 axes = args.axes || plot.getAxes();
   217                 c = { left: w / 2, top: h / 2 };
   447 
   218                 
   448             if (!c) {
       
   449                 c = {
       
   450                     left: w / 2,
       
   451                     top: h / 2
       
   452                 };
       
   453             }
       
   454 
   219             var xf = c.left / w,
   455             var xf = c.left / w,
   220                 yf = c.top / h,
   456                 yf = c.top / h,
   221                 minmax = {
   457                 minmax = {
   222                     x: {
   458                     x: {
   223                         min: c.left - xf * w / amount,
   459                         min: c.left - xf * w / amount,
   227                         min: c.top - yf * h / amount,
   463                         min: c.top - yf * h / amount,
   228                         max: c.top + (1 - yf) * h / amount
   464                         max: c.top + (1 - yf) * h / amount
   229                     }
   465                     }
   230                 };
   466                 };
   231 
   467 
   232             $.each(plot.getAxes(), function(_, axis) {
   468             for (var key in axes) {
   233                 var opts = axis.options,
   469                 if (!axes.hasOwnProperty(key)) {
       
   470                     continue;
       
   471                 }
       
   472 
       
   473                 var axis = axes[key],
       
   474                     opts = axis.options,
   234                     min = minmax[axis.direction].min,
   475                     min = minmax[axis.direction].min,
   235                     max = minmax[axis.direction].max,
   476                     max = minmax[axis.direction].max,
   236                     zr = opts.zoomRange,
   477                     navigationOffset = axis.options.offset;
   237                     pr = opts.panRange;
   478 
   238 
   479                 //skip axis without axisZoom when zooming only on certain axis or axis without plotZoom for zoom on entire plot
   239                 if (zr === false) // no zooming on this axis
   480                 if ((!opts.axisZoom && args.axes) || (!args.axes && !opts.plotZoom)) {
   240                     return;
   481                     continue;
   241                     
   482                 }
   242                 min = axis.c2p(min);
   483 
   243                 max = axis.c2p(max);
   484                 min = $.plot.saturated.saturate(axis.c2p(min));
       
   485                 max = $.plot.saturated.saturate(axis.c2p(max));
   244                 if (min > max) {
   486                 if (min > max) {
   245                     // make sure min < max
   487                     // make sure min < max
   246                     var tmp = min;
   488                     var tmp = min;
   247                     min = max;
   489                     min = max;
   248                     max = tmp;
   490                     max = tmp;
   249                 }
   491                 }
   250 
   492 
   251                 //Check that we are in panRange
   493                 var offsetBelow = $.plot.saturated.saturate(navigationOffset.below - (axis.min - min));
   252                 if (pr) {
   494                 var offsetAbove = $.plot.saturated.saturate(navigationOffset.above - (axis.max - max));
   253                     if (pr[0] != null && min < pr[0]) {
   495                 opts.offset = { below: offsetBelow, above: offsetAbove };
   254                         min = pr[0];
   496             };
   255                     }
   497 
   256                     if (pr[1] != null && max > pr[1]) {
   498             plot.setupGrid(true);
   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();
   499             plot.draw();
   273             
   500 
   274             if (!args.preventEvent)
   501             if (!args.preventEvent) {
   275                 plot.getPlaceholder().trigger("plotzoom", [ plot, args ]);
   502                 plot.getPlaceholder().trigger("plotzoom", [plot, args]);
       
   503             }
   276         };
   504         };
   277 
   505 
   278         plot.pan = function (args) {
   506         plot.pan = function(args) {
   279             var delta = {
   507             var delta = {
   280                 x: +args.left,
   508                 x: +args.left,
   281                 y: +args.top
   509                 y: +args.top
   282             };
   510             };
   283 
   511 
   284             if (isNaN(delta.x))
   512             if (isNaN(delta.x)) delta.x = 0;
   285                 delta.x = 0;
   513             if (isNaN(delta.y)) delta.y = 0;
   286             if (isNaN(delta.y))
   514 
   287                 delta.y = 0;
   515             $.each(args.axes || plot.getAxes(), function(_, axis) {
   288 
       
   289             $.each(plot.getAxes(), function (_, axis) {
       
   290                 var opts = axis.options,
   516                 var opts = axis.options,
   291                     min, max, d = delta[axis.direction];
   517                     d = delta[axis.direction];
   292 
   518 
   293                 min = axis.c2p(axis.p2c(axis.min) + d),
   519                 //skip axis without axisPan when panning only on certain axis or axis without plotPan for pan the entire plot
   294                 max = axis.c2p(axis.p2c(axis.max) + d);
   520                 if ((!opts.axisPan && args.axes) || (!opts.plotPan && !args.axes)) {
   295 
       
   296                 var pr = opts.panRange;
       
   297                 if (pr === false) // no panning on this axis
       
   298                     return;
   521                     return;
   299                 
   522                 }
   300                 if (pr) {
   523 
   301                     // check whether we hit the wall
   524                 if (d !== 0) {
   302                     if (pr[0] != null && pr[0] > min) {
   525                     var navigationOffsetBelow = saturated.saturate(axis.c2p(axis.p2c(axis.min) + d) - axis.c2p(axis.p2c(axis.min))),
   303                         d = pr[0] - min;
   526                         navigationOffsetAbove = saturated.saturate(axis.c2p(axis.p2c(axis.max) + d) - axis.c2p(axis.p2c(axis.max)));
   304                         min += d;
   527 
   305                         max += d;
   528                     if (!isFinite(navigationOffsetBelow)) {
       
   529                         navigationOffsetBelow = 0;
   306                     }
   530                     }
   307                     
   531 
   308                     if (pr[1] != null && pr[1] < max) {
   532                     if (!isFinite(navigationOffsetAbove)) {
   309                         d = pr[1] - max;
   533                         navigationOffsetAbove = 0;
   310                         min += d;
       
   311                         max += d;
       
   312                     }
   534                     }
   313                 }
   535 
   314                 
   536                     opts.offset = {
   315                 opts.min = min;
   537                         below: saturated.saturate(navigationOffsetBelow + (opts.offset.below || 0)),
   316                 opts.max = max;
   538                         above: saturated.saturate(navigationOffsetAbove + (opts.offset.above || 0))
       
   539                     };
       
   540                 }
   317             });
   541             });
   318             
   542 
   319             plot.setupGrid();
   543             plot.setupGrid(true);
   320             plot.draw();
   544             plot.draw();
   321             
   545             if (!args.preventEvent) {
   322             if (!args.preventEvent)
   546                 plot.getPlaceholder().trigger("plotpan", [plot, args]);
   323                 plot.getPlaceholder().trigger("plotpan", [ plot, args ]);
   547             }
   324         };
   548         };
   325 
   549 
       
   550         plot.recenter = function(args) {
       
   551             $.each(args.axes || plot.getAxes(), function(_, axis) {
       
   552                 if (args.axes) {
       
   553                     if (this.direction === 'x') {
       
   554                         axis.options.offset = { below: 0 };
       
   555                     } else if (this.direction === 'y') {
       
   556                         axis.options.offset = { above: 0 };
       
   557                     }
       
   558                 } else {
       
   559                     axis.options.offset = { below: 0, above: 0 };
       
   560                 }
       
   561             });
       
   562             plot.setupGrid(true);
       
   563             plot.draw();
       
   564         };
       
   565 
       
   566         var shouldSnap = function(delta) {
       
   567             return (Math.abs(delta.y) < SNAPPING_CONSTANT && Math.abs(delta.x) >= SNAPPING_CONSTANT) ||
       
   568                 (Math.abs(delta.x) < SNAPPING_CONSTANT && Math.abs(delta.y) >= SNAPPING_CONSTANT);
       
   569         }
       
   570 
       
   571         // adjust delta so the pan action is constrained on the vertical or horizontal direction
       
   572         // it the movements in the other direction are small
       
   573         var adjustDeltaToSnap = function(delta) {
       
   574             if (Math.abs(delta.x) < SNAPPING_CONSTANT && Math.abs(delta.y) >= SNAPPING_CONSTANT) {
       
   575                 return {x: 0, y: delta.y};
       
   576             }
       
   577 
       
   578             if (Math.abs(delta.y) < SNAPPING_CONSTANT && Math.abs(delta.x) >= SNAPPING_CONSTANT) {
       
   579                 return {x: delta.x, y: 0};
       
   580             }
       
   581 
       
   582             return delta;
       
   583         }
       
   584 
       
   585         var lockedDirection = null;
       
   586         var lockDeltaDirection = function(delta) {
       
   587             if (!lockedDirection && Math.max(Math.abs(delta.x), Math.abs(delta.y)) >= SNAPPING_CONSTANT) {
       
   588                 lockedDirection = Math.abs(delta.x) < Math.abs(delta.y) ? 'y' : 'x';
       
   589             }
       
   590 
       
   591             switch (lockedDirection) {
       
   592                 case 'x':
       
   593                     return { x: delta.x, y: 0 };
       
   594                 case 'y':
       
   595                     return { x: 0, y: delta.y };
       
   596                 default:
       
   597                     return { x: 0, y: 0 };
       
   598             }
       
   599         }
       
   600 
       
   601         var isDiagonalMode = function(delta) {
       
   602             if (Math.abs(delta.x) > 0 && Math.abs(delta.y) > 0) {
       
   603                 return true;
       
   604             }
       
   605             return false;
       
   606         }
       
   607 
       
   608         var restoreAxisOffset = function(axes, initialState, delta) {
       
   609             var axis;
       
   610             Object.keys(axes).forEach(function(axisName) {
       
   611                 axis = axes[axisName];
       
   612                 if (delta[axis.direction] === 0) {
       
   613                     axis.options.offset.below = initialState[axisName].navigationOffset.below;
       
   614                     axis.options.offset.above = initialState[axisName].navigationOffset.above;
       
   615                 }
       
   616             });
       
   617         }
       
   618 
       
   619         var prevDelta = { x: 0, y: 0 };
       
   620         plot.smartPan = function(delta, initialState, panAxes, preventEvent, smartLock) {
       
   621             var snap = smartLock ? true : shouldSnap(delta),
       
   622                 axes = plot.getAxes(),
       
   623                 opts;
       
   624             delta = smartLock ? lockDeltaDirection(delta) : adjustDeltaToSnap(delta);
       
   625 
       
   626             if (isDiagonalMode(delta)) {
       
   627                 initialState.diagMode = true;
       
   628             }
       
   629 
       
   630             if (snap && initialState.diagMode === true) {
       
   631                 initialState.diagMode = false;
       
   632                 restoreAxisOffset(axes, initialState, delta);
       
   633             }
       
   634 
       
   635             if (snap) {
       
   636                 panHint = {
       
   637                     start: {
       
   638                         x: initialState.startPageX - plot.offset().left + plot.getPlotOffset().left,
       
   639                         y: initialState.startPageY - plot.offset().top + plot.getPlotOffset().top
       
   640                     },
       
   641                     end: {
       
   642                         x: initialState.startPageX - delta.x - plot.offset().left + plot.getPlotOffset().left,
       
   643                         y: initialState.startPageY - delta.y - plot.offset().top + plot.getPlotOffset().top
       
   644                     }
       
   645                 }
       
   646             } else {
       
   647                 panHint = {
       
   648                     start: {
       
   649                         x: initialState.startPageX - plot.offset().left + plot.getPlotOffset().left,
       
   650                         y: initialState.startPageY - plot.offset().top + plot.getPlotOffset().top
       
   651                     },
       
   652                     end: false
       
   653                 }
       
   654             }
       
   655 
       
   656             if (isNaN(delta.x)) delta.x = 0;
       
   657             if (isNaN(delta.y)) delta.y = 0;
       
   658 
       
   659             if (panAxes) {
       
   660                 axes = panAxes;
       
   661             }
       
   662 
       
   663             var axis, axisMin, axisMax, p, d;
       
   664             Object.keys(axes).forEach(function(axisName) {
       
   665                 axis = axes[axisName];
       
   666                 axisMin = axis.min;
       
   667                 axisMax = axis.max;
       
   668                 opts = axis.options;
       
   669 
       
   670                 d = delta[axis.direction];
       
   671                 p = prevDelta[axis.direction];
       
   672 
       
   673                 //skip axis without axisPan when panning only on certain axis or axis without plotPan for pan the entire plot
       
   674                 if ((!opts.axisPan && panAxes) || (!panAxes && !opts.plotPan)) {
       
   675                     return;
       
   676                 }
       
   677 
       
   678                 if (d !== 0) {
       
   679                     var navigationOffsetBelow = saturated.saturate(axis.c2p(axis.p2c(axisMin) - (p - d)) - axis.c2p(axis.p2c(axisMin))),
       
   680                         navigationOffsetAbove = saturated.saturate(axis.c2p(axis.p2c(axisMax) - (p - d)) - axis.c2p(axis.p2c(axisMax)));
       
   681 
       
   682                     if (!isFinite(navigationOffsetBelow)) {
       
   683                         navigationOffsetBelow = 0;
       
   684                     }
       
   685 
       
   686                     if (!isFinite(navigationOffsetAbove)) {
       
   687                         navigationOffsetAbove = 0;
       
   688                     }
       
   689 
       
   690                     axis.options.offset.below = saturated.saturate(navigationOffsetBelow + (axis.options.offset.below || 0));
       
   691                     axis.options.offset.above = saturated.saturate(navigationOffsetAbove + (axis.options.offset.above || 0));
       
   692                 }
       
   693             });
       
   694 
       
   695             prevDelta = delta;
       
   696             plot.setupGrid(true);
       
   697             plot.draw();
       
   698 
       
   699             if (!preventEvent) {
       
   700                 plot.getPlaceholder().trigger("plotpan", [plot, delta, panAxes, initialState]);
       
   701             }
       
   702         };
       
   703 
       
   704         plot.smartPan.end = function() {
       
   705             panHint = null;
       
   706             lockedDirection = null;
       
   707             prevDelta = { x: 0, y: 0 };
       
   708             plot.triggerRedrawOverlay();
       
   709         }
       
   710 
   326         function shutdown(plot, eventHolder) {
   711         function shutdown(plot, eventHolder) {
   327             eventHolder.unbind(plot.getOptions().zoom.trigger, onZoomClick);
       
   328             eventHolder.unbind("mousewheel", onMouseWheel);
   712             eventHolder.unbind("mousewheel", onMouseWheel);
       
   713             eventHolder.unbind("mousedown", onMouseDown);
       
   714             eventHolder.unbind("mouseup", onMouseUp);
   329             eventHolder.unbind("dragstart", onDragStart);
   715             eventHolder.unbind("dragstart", onDragStart);
   330             eventHolder.unbind("drag", onDrag);
   716             eventHolder.unbind("drag", onDrag);
   331             eventHolder.unbind("dragend", onDragEnd);
   717             eventHolder.unbind("dragend", onDragEnd);
   332             if (panTimeout)
   718             eventHolder.unbind("dblclick", onDblClick);
   333                 clearTimeout(panTimeout);
   719             eventHolder.unbind("click", onClick);
   334         }
   720 
   335         
   721             if (panTimeout) clearTimeout(panTimeout);
       
   722         }
       
   723 
       
   724         function drawOverlay(plot, ctx) {
       
   725             if (panHint) {
       
   726                 ctx.strokeStyle = 'rgba(96, 160, 208, 0.7)';
       
   727                 ctx.lineWidth = 2;
       
   728                 ctx.lineJoin = "round";
       
   729                 var startx = Math.round(panHint.start.x),
       
   730                     starty = Math.round(panHint.start.y),
       
   731                     endx, endy;
       
   732 
       
   733                 if (panAxes) {
       
   734                     if (panAxes[0].direction === 'x') {
       
   735                         endy = Math.round(panHint.start.y);
       
   736                         endx = Math.round(panHint.end.x);
       
   737                     } else if (panAxes[0].direction === 'y') {
       
   738                         endx = Math.round(panHint.start.x);
       
   739                         endy = Math.round(panHint.end.y);
       
   740                     }
       
   741                 } else {
       
   742                     endx = Math.round(panHint.end.x);
       
   743                     endy = Math.round(panHint.end.y);
       
   744                 }
       
   745 
       
   746                 ctx.beginPath();
       
   747 
       
   748                 if (panHint.end === false) {
       
   749                     ctx.moveTo(startx, starty - PANHINT_LENGTH_CONSTANT);
       
   750                     ctx.lineTo(startx, starty + PANHINT_LENGTH_CONSTANT);
       
   751 
       
   752                     ctx.moveTo(startx + PANHINT_LENGTH_CONSTANT, starty);
       
   753                     ctx.lineTo(startx - PANHINT_LENGTH_CONSTANT, starty);
       
   754                 } else {
       
   755                     var dirX = starty === endy;
       
   756 
       
   757                     ctx.moveTo(startx - (dirX ? 0 : PANHINT_LENGTH_CONSTANT), starty - (dirX ? PANHINT_LENGTH_CONSTANT : 0));
       
   758                     ctx.lineTo(startx + (dirX ? 0 : PANHINT_LENGTH_CONSTANT), starty + (dirX ? PANHINT_LENGTH_CONSTANT : 0));
       
   759 
       
   760                     ctx.moveTo(startx, starty);
       
   761                     ctx.lineTo(endx, endy);
       
   762 
       
   763                     ctx.moveTo(endx - (dirX ? 0 : PANHINT_LENGTH_CONSTANT), endy - (dirX ? PANHINT_LENGTH_CONSTANT : 0));
       
   764                     ctx.lineTo(endx + (dirX ? 0 : PANHINT_LENGTH_CONSTANT), endy + (dirX ? PANHINT_LENGTH_CONSTANT : 0));
       
   765                 }
       
   766 
       
   767                 ctx.stroke();
       
   768             }
       
   769         }
       
   770 
       
   771         plot.getTouchedAxis = function(touchPointX, touchPointY) {
       
   772             var ec = plot.getPlaceholder().offset();
       
   773             ec.left = touchPointX - ec.left;
       
   774             ec.top = touchPointY - ec.top;
       
   775 
       
   776             var axis = plot.getXAxes().concat(plot.getYAxes()).filter(function (axis) {
       
   777                 var box = axis.box;
       
   778                 if (box !== undefined) {
       
   779                     return (ec.left > box.left) && (ec.left < box.left + box.width) &&
       
   780                             (ec.top > box.top) && (ec.top < box.top + box.height);
       
   781                 }
       
   782             });
       
   783 
       
   784             return axis;
       
   785         }
       
   786 
       
   787         plot.hooks.drawOverlay.push(drawOverlay);
   336         plot.hooks.bindEvents.push(bindEvents);
   788         plot.hooks.bindEvents.push(bindEvents);
   337         plot.hooks.shutdown.push(shutdown);
   789         plot.hooks.shutdown.push(shutdown);
   338     }
   790     }
   339     
   791 
   340     $.plot.plugins.push({
   792     $.plot.plugins.push({
   341         init: init,
   793         init: init,
   342         options: options,
   794         options: options,
   343         name: 'navigate',
   795         name: 'navigate',
   344         version: '1.3'
   796         version: '1.3'