src/pyams_skin/resources/js/ext/flot/jquery.flot.navigate.js
changeset 566 a1707c607eec
parent 565 318533413200
child 567 bca1726b1d85
equal deleted inserted replaced
565:318533413200 566:a1707c607eec
     1 /* Flot plugin for adding the ability to pan and zoom the plot.
       
     2 
       
     3 Copyright (c) 2007-2014 IOLA and Ole Laursen.
       
     4 Copyright (c) 2016 Ciprian Ceteras.
       
     5 Copyright (c) 2017 Raluca Portase.
       
     6 Licensed under the MIT license.
       
     7 
       
     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
       
    17 to pan. The plugin defines plot.zoom({ center }), plot.zoomOut() and
       
    18 plot.pan( offset ) so you easily can add custom controls. It also fires
       
    19 "plotpan" and "plotzoom" events, useful for synchronizing plots.
       
    20 
       
    21 The plugin supports these options:
       
    22 ```js
       
    23     zoom: {
       
    24         interactive: false,
       
    25         active: false,
       
    26         amount: 1.5         // 2 = 200% (zoom in), 0.5 = 50% (zoom out)
       
    27     }
       
    28 
       
    29     pan: {
       
    30         interactive: false,
       
    31         active: false,
       
    32         cursor: "move",     // CSS mouse cursor value used when dragging, e.g. "pointer"
       
    33         frameRate: 60,
       
    34         mode: "smart"       // enable smart pan mode
       
    35     }
       
    36 
       
    37     xaxis: {
       
    38         axisZoom: true, //zoom axis when mouse over it is allowed
       
    39         plotZoom: true, //zoom axis is allowed for plot zoom
       
    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
       
    52 interactive for pan, then you'll have a basic plot that supports moving
       
    53 around; the same for zoom.
       
    54 
       
    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
       
    60 the current viewport.
       
    61 
       
    62 **cursor** is a standard CSS mouse cursor string used for visual feedback to the
       
    63 user when dragging.
       
    64 
       
    65 **frameRate** specifies the maximum number of times per second the plot will
       
    66 update itself while the user is panning around on it (set to null to disable
       
    67 intermediate pans, the plot will then not update until the mouse button is
       
    68 released).
       
    69 
       
    70 **mode** a string specifies the pan mode for mouse interaction. Accepted values:
       
    71 'manual': no pan hint or direction snapping;
       
    72 'smart': The graph shows pan hint bar and the pan movement will snap
       
    73 to one direction when the drag direction is close to it;
       
    74 'smartLock'. The graph shows pan hint bar and the pan movement will always
       
    75 snap to a direction that the drag diorection started with.
       
    76 
       
    77 Example API usage:
       
    78 ```js
       
    79     plot = $.plot(...);
       
    80 
       
    81     // zoom default amount in on the pixel ( 10, 20 )
       
    82     plot.zoom({ center: { left: 10, top: 20 } });
       
    83 
       
    84     // zoom out again
       
    85     plot.zoomOut({ center: { left: 10, top: 20 } });
       
    86 
       
    87     // zoom 200% in on the pixel (10, 20)
       
    88     plot.zoom({ amount: 2, center: { left: 10, top: 20 } });
       
    89 
       
    90     // pan 100 pixels to the left (changing x-range in a positive way) and 20 down
       
    91     plot.pan({ left: -100, top: 20 })
       
    92 ```
       
    93 
       
    94 Here, "center" specifies where the center of the zooming should happen. Note
       
    95 that this is defined in pixel space, not the space of the data points (you can
       
    96 use the p2c helpers on the axes in Flot to help you convert between these).
       
    97 
       
    98 **amount** is the amount to zoom the viewport relative to the current range, so
       
    99 1 is 100% (i.e. no change), 1.5 is 150% (zoom in), 0.7 is 70% (zoom out). You
       
   100 can set the default in the options.
       
   101 */
       
   102 
       
   103 /* eslint-enable */
       
   104 (function($) {
       
   105     'use strict';
       
   106 
       
   107     var options = {
       
   108         zoom: {
       
   109             interactive: false,
       
   110             active: false,
       
   111             amount: 1.5 // how much to zoom relative to current position, 2 = 200% (zoom in), 0.5 = 50% (zoom out)
       
   112         },
       
   113         pan: {
       
   114             interactive: false,
       
   115             active: false,
       
   116             cursor: "move",
       
   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
       
   134         }
       
   135     };
       
   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 
       
   142     function init(plot) {
       
   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 
       
   156             var c = plot.offset();
       
   157             c.left = page.X - c.left;
       
   158             c.top = page.Y - c.top;
       
   159 
       
   160             var ec = plot.getPlaceholder().offset();
       
   161             ec.left = page.X - ec.left;
       
   162             ec.top = page.Y - ec.top;
       
   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;
       
   197 
       
   198         function onMouseWheel(e, delta) {
       
   199             var maxAbsoluteDeltaOnMac = 1,
       
   200                 isMacScroll = Math.abs(e.originalEvent.deltaY) <= maxAbsoluteDeltaOnMac,
       
   201                 defaultNonMacScrollAmount = null,
       
   202                 macMagicRatio = 50,
       
   203                 amount = isMacScroll ? 1 + Math.abs(e.originalEvent.deltaY) / macMagicRatio : defaultNonMacScrollAmount;
       
   204 
       
   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         }
       
   246 
       
   247         function onDragStart(e) {
       
   248             if (!canDrag || !isLeftMouseButtonPressed(e)) {
       
   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 
       
   271             var c = plot.getPlaceholder().css('cursor');
       
   272             if (c) {
       
   273                 prevCursor = c;
       
   274             }
       
   275 
       
   276             plot.getPlaceholder().css('cursor', plot.getOptions().pan.cursor);
       
   277 
       
   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             }
       
   284         }
       
   285         
       
   286         function onDrag(e) {
       
   287             if (!isPanAction) { 
       
   288                 return; 
       
   289             }
       
   290 
       
   291             var page = browser.getPageXY(e);
       
   292             var frameRate = plot.getOptions().pan.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                 }
       
   309                 return;
       
   310             }
       
   311 
       
   312             if (panTimeout || !frameRate) return;
       
   313 
       
   314             panTimeout = setTimeout(function() {
       
   315                 if (useSmartPan) {
       
   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 
       
   330                 panTimeout = null;
       
   331             }, 1 / frameRate * 1000);
       
   332         }
       
   333 
       
   334         function onDragEnd(e) {
       
   335             if (!isPanAction) { 
       
   336                 return; 
       
   337             }
       
   338 
       
   339             if (panTimeout) {
       
   340                 clearTimeout(panTimeout);
       
   341                 panTimeout = null;
       
   342             }
       
   343 
       
   344             isPanAction = false;
       
   345             var page = browser.getPageXY(e);
       
   346 
       
   347             plot.getPlaceholder().css('cursor', prevCursor);
       
   348 
       
   349             if (useSmartPan) {
       
   350                 plot.smartPan({
       
   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 
       
   406         function bindEvents(plot, eventHolder) {
       
   407             var o = plot.getOptions();
       
   408             if (o.zoom.interactive) {
       
   409                 eventHolder.mousewheel(onMouseWheel);
       
   410             }
       
   411 
       
   412             if (o.pan.interactive) {
       
   413                 plot.addEventHandler("dragstart", onDragStart, eventHolder, 0);
       
   414                 plot.addEventHandler("drag", onDrag, eventHolder, 0);
       
   415                 plot.addEventHandler("dragend", onDragEnd, eventHolder, 0);
       
   416                 eventHolder.bind("mousedown", onMouseDown);
       
   417                 eventHolder.bind("mouseup", onMouseUp);
       
   418             }
       
   419 
       
   420             eventHolder.dblclick(onDblClick);
       
   421             eventHolder.click(onClick);
       
   422         }
       
   423 
       
   424         plot.zoomOut = function(args) {
       
   425             if (!args) {
       
   426                 args = {};
       
   427             }
       
   428 
       
   429             if (!args.amount) {
       
   430                 args.amount = plot.getOptions().zoom.amount;
       
   431             }
       
   432 
       
   433             args.amount = 1 / args.amount;
       
   434             plot.zoom(args);
       
   435         };
       
   436 
       
   437         plot.zoom = function(args) {
       
   438             if (!args) {
       
   439                 args = {};
       
   440             }
       
   441 
       
   442             var c = args.center,
       
   443                 amount = args.amount || plot.getOptions().zoom.amount,
       
   444                 w = plot.width(),
       
   445                 h = plot.height(),
       
   446                 axes = args.axes || plot.getAxes();
       
   447 
       
   448             if (!c) {
       
   449                 c = {
       
   450                     left: w / 2,
       
   451                     top: h / 2
       
   452                 };
       
   453             }
       
   454 
       
   455             var xf = c.left / w,
       
   456                 yf = c.top / h,
       
   457                 minmax = {
       
   458                     x: {
       
   459                         min: c.left - xf * w / amount,
       
   460                         max: c.left + (1 - xf) * w / amount
       
   461                     },
       
   462                     y: {
       
   463                         min: c.top - yf * h / amount,
       
   464                         max: c.top + (1 - yf) * h / amount
       
   465                     }
       
   466                 };
       
   467 
       
   468             for (var key in axes) {
       
   469                 if (!axes.hasOwnProperty(key)) {
       
   470                     continue;
       
   471                 }
       
   472 
       
   473                 var axis = axes[key],
       
   474                     opts = axis.options,
       
   475                     min = minmax[axis.direction].min,
       
   476                     max = minmax[axis.direction].max,
       
   477                     navigationOffset = axis.options.offset;
       
   478 
       
   479                 //skip axis without axisZoom when zooming only on certain axis or axis without plotZoom for zoom on entire plot
       
   480                 if ((!opts.axisZoom && args.axes) || (!args.axes && !opts.plotZoom)) {
       
   481                     continue;
       
   482                 }
       
   483 
       
   484                 min = $.plot.saturated.saturate(axis.c2p(min));
       
   485                 max = $.plot.saturated.saturate(axis.c2p(max));
       
   486                 if (min > max) {
       
   487                     // make sure min < max
       
   488                     var tmp = min;
       
   489                     min = max;
       
   490                     max = tmp;
       
   491                 }
       
   492 
       
   493                 var offsetBelow = $.plot.saturated.saturate(navigationOffset.below - (axis.min - min));
       
   494                 var offsetAbove = $.plot.saturated.saturate(navigationOffset.above - (axis.max - max));
       
   495                 opts.offset = { below: offsetBelow, above: offsetAbove };
       
   496             };
       
   497 
       
   498             plot.setupGrid(true);
       
   499             plot.draw();
       
   500 
       
   501             if (!args.preventEvent) {
       
   502                 plot.getPlaceholder().trigger("plotzoom", [plot, args]);
       
   503             }
       
   504         };
       
   505 
       
   506         plot.pan = function(args) {
       
   507             var delta = {
       
   508                 x: +args.left,
       
   509                 y: +args.top
       
   510             };
       
   511 
       
   512             if (isNaN(delta.x)) delta.x = 0;
       
   513             if (isNaN(delta.y)) delta.y = 0;
       
   514 
       
   515             $.each(args.axes || plot.getAxes(), function(_, axis) {
       
   516                 var opts = axis.options,
       
   517                     d = delta[axis.direction];
       
   518 
       
   519                 //skip axis without axisPan when panning only on certain axis or axis without plotPan for pan the entire plot
       
   520                 if ((!opts.axisPan && args.axes) || (!opts.plotPan && !args.axes)) {
       
   521                     return;
       
   522                 }
       
   523 
       
   524                 if (d !== 0) {
       
   525                     var navigationOffsetBelow = saturated.saturate(axis.c2p(axis.p2c(axis.min) + d) - axis.c2p(axis.p2c(axis.min))),
       
   526                         navigationOffsetAbove = saturated.saturate(axis.c2p(axis.p2c(axis.max) + d) - axis.c2p(axis.p2c(axis.max)));
       
   527 
       
   528                     if (!isFinite(navigationOffsetBelow)) {
       
   529                         navigationOffsetBelow = 0;
       
   530                     }
       
   531 
       
   532                     if (!isFinite(navigationOffsetAbove)) {
       
   533                         navigationOffsetAbove = 0;
       
   534                     }
       
   535 
       
   536                     opts.offset = {
       
   537                         below: saturated.saturate(navigationOffsetBelow + (opts.offset.below || 0)),
       
   538                         above: saturated.saturate(navigationOffsetAbove + (opts.offset.above || 0))
       
   539                     };
       
   540                 }
       
   541             });
       
   542 
       
   543             plot.setupGrid(true);
       
   544             plot.draw();
       
   545             if (!args.preventEvent) {
       
   546                 plot.getPlaceholder().trigger("plotpan", [plot, args]);
       
   547             }
       
   548         };
       
   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 
       
   711         function shutdown(plot, eventHolder) {
       
   712             eventHolder.unbind("mousewheel", onMouseWheel);
       
   713             eventHolder.unbind("mousedown", onMouseDown);
       
   714             eventHolder.unbind("mouseup", onMouseUp);
       
   715             eventHolder.unbind("dragstart", onDragStart);
       
   716             eventHolder.unbind("drag", onDrag);
       
   717             eventHolder.unbind("dragend", onDragEnd);
       
   718             eventHolder.unbind("dblclick", onDblClick);
       
   719             eventHolder.unbind("click", onClick);
       
   720 
       
   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);
       
   788         plot.hooks.bindEvents.push(bindEvents);
       
   789         plot.hooks.shutdown.push(shutdown);
       
   790     }
       
   791 
       
   792     $.plot.plugins.push({
       
   793         init: init,
       
   794         options: options,
       
   795         name: 'navigate',
       
   796         version: '1.3'
       
   797     });
       
   798 })(jQuery);