src/pyams_skin/resources/js/ext/flot/jquery.flot.touchNavigate.js
changeset 557 bca7a7e058a3
equal deleted inserted replaced
-1:000000000000 557:bca7a7e058a3
       
     1 /* global jQuery */
       
     2 
       
     3 (function($) {
       
     4     'use strict';
       
     5 
       
     6     var options = {
       
     7         zoom: {
       
     8             enableTouch: false
       
     9         },
       
    10         pan: {
       
    11             enableTouch: false,
       
    12             touchMode: 'manual'
       
    13         },
       
    14         recenter: {
       
    15             enableTouch: true
       
    16         }
       
    17     };
       
    18 
       
    19     var ZOOM_DISTANCE_MARGIN = $.plot.uiConstants.ZOOM_DISTANCE_MARGIN;
       
    20 
       
    21     function init(plot) {
       
    22         plot.hooks.processOptions.push(initTouchNavigation);
       
    23     }
       
    24 
       
    25     function initTouchNavigation(plot, options) {
       
    26         var gestureState = {
       
    27                 zoomEnable: false,
       
    28                 prevDistance: null,
       
    29                 prevTapTime: 0,
       
    30                 prevPanPosition: { x: 0, y: 0 },
       
    31                 prevTapPosition: { x: 0, y: 0 }
       
    32             },
       
    33             navigationState = {
       
    34                 prevTouchedAxis: 'none',
       
    35                 currentTouchedAxis: 'none',
       
    36                 touchedAxis: null,
       
    37                 navigationConstraint: 'unconstrained',
       
    38                 initialState: null,
       
    39             },
       
    40             useManualPan = options.pan.interactive && options.pan.touchMode === 'manual',
       
    41             smartPanLock = options.pan.touchMode === 'smartLock',
       
    42             useSmartPan = options.pan.interactive && (smartPanLock || options.pan.touchMode === 'smart'),
       
    43             pan, pinch, doubleTap;
       
    44 
       
    45         function bindEvents(plot, eventHolder) {
       
    46             var o = plot.getOptions();
       
    47 
       
    48             if (o.zoom.interactive && o.zoom.enableTouch) {
       
    49                 eventHolder[0].addEventListener('pinchstart', pinch.start, false);
       
    50                 eventHolder[0].addEventListener('pinchdrag', pinch.drag, false);
       
    51                 eventHolder[0].addEventListener('pinchend', pinch.end, false);
       
    52             }
       
    53 
       
    54             if (o.pan.interactive && o.pan.enableTouch) {
       
    55                 eventHolder[0].addEventListener('panstart', pan.start, false);
       
    56                 eventHolder[0].addEventListener('pandrag', pan.drag, false);
       
    57                 eventHolder[0].addEventListener('panend', pan.end, false);
       
    58             }
       
    59 
       
    60             if ((o.recenter.interactive && o.recenter.enableTouch)) {
       
    61                 eventHolder[0].addEventListener('doubletap', doubleTap.recenterPlot, false);
       
    62             }
       
    63         }
       
    64 
       
    65         function shutdown(plot, eventHolder) {
       
    66             eventHolder[0].removeEventListener('panstart', pan.start);
       
    67             eventHolder[0].removeEventListener('pandrag', pan.drag);
       
    68             eventHolder[0].removeEventListener('panend', pan.end);
       
    69             eventHolder[0].removeEventListener('pinchstart', pinch.start);
       
    70             eventHolder[0].removeEventListener('pinchdrag', pinch.drag);
       
    71             eventHolder[0].removeEventListener('pinchend', pinch.end);
       
    72             eventHolder[0].removeEventListener('doubletap', doubleTap.recenterPlot);
       
    73         }
       
    74 
       
    75         pan = {
       
    76             start: function(e) {
       
    77                 presetNavigationState(e, 'pan', gestureState);
       
    78                 updateData(e, 'pan', gestureState, navigationState);
       
    79 
       
    80                 if (useSmartPan) {
       
    81                     var point = getPoint(e, 'pan');
       
    82                     navigationState.initialState = plot.navigationState(point.x, point.y);
       
    83                 }
       
    84             },
       
    85 
       
    86             drag: function(e) {
       
    87                 presetNavigationState(e, 'pan', gestureState);
       
    88 
       
    89                 if (useSmartPan) {
       
    90                     var point = getPoint(e, 'pan');
       
    91                     plot.smartPan({
       
    92                         x: navigationState.initialState.startPageX - point.x,
       
    93                         y: navigationState.initialState.startPageY - point.y
       
    94                     }, navigationState.initialState, navigationState.touchedAxis, false, smartPanLock);
       
    95                 } else if (useManualPan) {
       
    96                     plot.pan({
       
    97                         left: -delta(e, 'pan', gestureState).x,
       
    98                         top: -delta(e, 'pan', gestureState).y,
       
    99                         axes: navigationState.touchedAxis
       
   100                     });
       
   101                     updatePrevPanPosition(e, 'pan', gestureState, navigationState);
       
   102                 }
       
   103             },
       
   104 
       
   105             end: function(e) {
       
   106                 presetNavigationState(e, 'pan', gestureState);
       
   107 
       
   108                 if (useSmartPan) {
       
   109                     plot.smartPan.end();
       
   110                 }
       
   111 
       
   112                 if (wasPinchEvent(e, gestureState)) {
       
   113                     updateprevPanPosition(e, 'pan', gestureState, navigationState);
       
   114                 }
       
   115             }
       
   116         };
       
   117 
       
   118         var pinchDragTimeout;
       
   119         pinch = {
       
   120             start: function(e) {
       
   121                 if (pinchDragTimeout) {
       
   122                     clearTimeout(pinchDragTimeout);
       
   123                     pinchDragTimeout = null;
       
   124                 }
       
   125                 presetNavigationState(e, 'pinch', gestureState);
       
   126                 setPrevDistance(e, gestureState);
       
   127                 updateData(e, 'pinch', gestureState, navigationState);
       
   128             },
       
   129 
       
   130             drag: function(e) {
       
   131                 if (pinchDragTimeout) {
       
   132                     return;
       
   133                 }
       
   134                 pinchDragTimeout = setTimeout(function() {
       
   135                     presetNavigationState(e, 'pinch', gestureState);
       
   136                     plot.pan({
       
   137                         left: -delta(e, 'pinch', gestureState).x,
       
   138                         top: -delta(e, 'pinch', gestureState).y,
       
   139                         axes: navigationState.touchedAxis
       
   140                     });
       
   141                     updatePrevPanPosition(e, 'pinch', gestureState, navigationState);
       
   142 
       
   143                     var dist = pinchDistance(e);
       
   144 
       
   145                     if (gestureState.zoomEnable || Math.abs(dist - gestureState.prevDistance) > ZOOM_DISTANCE_MARGIN) {
       
   146                         zoomPlot(plot, e, gestureState, navigationState);
       
   147 
       
   148                         //activate zoom mode
       
   149                         gestureState.zoomEnable = true;
       
   150                     }
       
   151                     pinchDragTimeout = null;
       
   152                 }, 1000 / 60);
       
   153             },
       
   154 
       
   155             end: function(e) {
       
   156                 if (pinchDragTimeout) {
       
   157                     clearTimeout(pinchDragTimeout);
       
   158                     pinchDragTimeout = null;
       
   159                 }
       
   160                 presetNavigationState(e, 'pinch', gestureState);
       
   161                 gestureState.prevDistance = null;
       
   162             }
       
   163         };
       
   164 
       
   165         doubleTap = {
       
   166             recenterPlot: function(e) {
       
   167                 if (e && e.detail && e.detail.type === 'touchstart') {
       
   168                     // only do not recenter for touch start;
       
   169                     recenterPlotOnDoubleTap(plot, e, gestureState, navigationState);
       
   170                 }
       
   171             }
       
   172         };
       
   173 
       
   174         if (options.pan.enableTouch === true || options.zoom.enableTouch === true) {
       
   175             plot.hooks.bindEvents.push(bindEvents);
       
   176             plot.hooks.shutdown.push(shutdown);
       
   177         }
       
   178 
       
   179         function presetNavigationState(e, gesture, gestureState) {
       
   180             navigationState.touchedAxis = getAxis(plot, e, gesture, navigationState);
       
   181             if (noAxisTouched(navigationState)) {
       
   182                 navigationState.navigationConstraint = 'unconstrained';
       
   183             } else {
       
   184                 navigationState.navigationConstraint = 'axisConstrained';
       
   185             }
       
   186         }
       
   187     }
       
   188 
       
   189     $.plot.plugins.push({
       
   190         init: init,
       
   191         options: options,
       
   192         name: 'navigateTouch',
       
   193         version: '0.3'
       
   194     });
       
   195 
       
   196     function recenterPlotOnDoubleTap(plot, e, gestureState, navigationState) {
       
   197         checkAxesForDoubleTap(plot, e, navigationState);
       
   198         if ((navigationState.currentTouchedAxis === 'x' && navigationState.prevTouchedAxis === 'x') ||
       
   199             (navigationState.currentTouchedAxis === 'y' && navigationState.prevTouchedAxis === 'y') ||
       
   200             (navigationState.currentTouchedAxis === 'none' && navigationState.prevTouchedAxis === 'none')) {
       
   201             var event;
       
   202 
       
   203             plot.recenter({ axes: navigationState.touchedAxis });
       
   204 
       
   205             if (navigationState.touchedAxis) {
       
   206                 event = new $.Event('re-center', { detail: { axisTouched: navigationState.touchedAxis } });
       
   207             } else {
       
   208                 event = new $.Event('re-center', { detail: e });
       
   209             }
       
   210             plot.getPlaceholder().trigger(event);
       
   211         }
       
   212     }
       
   213 
       
   214     function checkAxesForDoubleTap(plot, e, navigationState) {
       
   215         var axis = plot.getTouchedAxis(e.detail.firstTouch.x, e.detail.firstTouch.y);
       
   216         if (axis[0] !== undefined) {
       
   217             navigationState.prevTouchedAxis = axis[0].direction;
       
   218         }
       
   219 
       
   220         axis = plot.getTouchedAxis(e.detail.secondTouch.x, e.detail.secondTouch.y);
       
   221         if (axis[0] !== undefined) {
       
   222             navigationState.touchedAxis = axis;
       
   223             navigationState.currentTouchedAxis = axis[0].direction;
       
   224         }
       
   225 
       
   226         if (noAxisTouched(navigationState)) {
       
   227             navigationState.touchedAxis = null;
       
   228             navigationState.prevTouchedAxis = 'none';
       
   229             navigationState.currentTouchedAxis = 'none';
       
   230         }
       
   231     }
       
   232 
       
   233     function zoomPlot(plot, e, gestureState, navigationState) {
       
   234         var offset = plot.offset(),
       
   235             center = {
       
   236                 left: 0,
       
   237                 top: 0
       
   238             },
       
   239             zoomAmount = pinchDistance(e) / gestureState.prevDistance,
       
   240             dist = pinchDistance(e);
       
   241 
       
   242         center.left = getPoint(e, 'pinch').x - offset.left;
       
   243         center.top = getPoint(e, 'pinch').y - offset.top;
       
   244 
       
   245         // send the computed touched axis to the zoom function so that it only zooms on that one
       
   246         plot.zoom({
       
   247             center: center,
       
   248             amount: zoomAmount,
       
   249             axes: navigationState.touchedAxis
       
   250         });
       
   251         gestureState.prevDistance = dist;
       
   252     }
       
   253 
       
   254     function wasPinchEvent(e, gestureState) {
       
   255         return (gestureState.zoomEnable && e.detail.touches.length === 1);
       
   256     }
       
   257 
       
   258     function getAxis(plot, e, gesture, navigationState) {
       
   259         if (e.type === 'pinchstart') {
       
   260             var axisTouch1 = plot.getTouchedAxis(e.detail.touches[0].pageX, e.detail.touches[0].pageY);
       
   261             var axisTouch2 = plot.getTouchedAxis(e.detail.touches[1].pageX, e.detail.touches[1].pageY);
       
   262 
       
   263             if (axisTouch1.length === axisTouch2.length && axisTouch1.toString() === axisTouch2.toString()) {
       
   264                 return axisTouch1;
       
   265             }
       
   266         } else if (e.type === 'panstart') {
       
   267             return plot.getTouchedAxis(e.detail.touches[0].pageX, e.detail.touches[0].pageY);
       
   268         } else if (e.type === 'pinchend') {
       
   269             //update axis since instead on pinch, a pan event is made
       
   270             return plot.getTouchedAxis(e.detail.touches[0].pageX, e.detail.touches[0].pageY);
       
   271         } else {
       
   272             return navigationState.touchedAxis;
       
   273         }
       
   274     }
       
   275 
       
   276     function noAxisTouched(navigationState) {
       
   277         return (!navigationState.touchedAxis || navigationState.touchedAxis.length === 0);
       
   278     }
       
   279 
       
   280     function setPrevDistance(e, gestureState) {
       
   281         gestureState.prevDistance = pinchDistance(e);
       
   282     }
       
   283 
       
   284     function updateData(e, gesture, gestureState, navigationState) {
       
   285         var axisDir,
       
   286             point = getPoint(e, gesture);
       
   287 
       
   288         switch (navigationState.navigationConstraint) {
       
   289             case 'unconstrained':
       
   290                 navigationState.touchedAxis = null;
       
   291                 gestureState.prevTapPosition = {
       
   292                     x: gestureState.prevPanPosition.x,
       
   293                     y: gestureState.prevPanPosition.y
       
   294                 };
       
   295                 gestureState.prevPanPosition = {
       
   296                     x: point.x,
       
   297                     y: point.y
       
   298                 };
       
   299                 break;
       
   300             case 'axisConstrained':
       
   301                 axisDir = navigationState.touchedAxis[0].direction;
       
   302                 navigationState.currentTouchedAxis = axisDir;
       
   303                 gestureState.prevTapPosition[axisDir] = gestureState.prevPanPosition[axisDir];
       
   304                 gestureState.prevPanPosition[axisDir] = point[axisDir];
       
   305                 break;
       
   306             default:
       
   307                 break;
       
   308         }
       
   309     }
       
   310 
       
   311     function distance(x1, y1, x2, y2) {
       
   312         return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
       
   313     }
       
   314 
       
   315     function pinchDistance(e) {
       
   316         var t1 = e.detail.touches[0],
       
   317             t2 = e.detail.touches[1];
       
   318         return distance(t1.pageX, t1.pageY, t2.pageX, t2.pageY);
       
   319     }
       
   320 
       
   321     function updatePrevPanPosition(e, gesture, gestureState, navigationState) {
       
   322         var point = getPoint(e, gesture);
       
   323 
       
   324         switch (navigationState.navigationConstraint) {
       
   325             case 'unconstrained':
       
   326                 gestureState.prevPanPosition.x = point.x;
       
   327                 gestureState.prevPanPosition.y = point.y;
       
   328                 break;
       
   329             case 'axisConstrained':
       
   330                 gestureState.prevPanPosition[navigationState.currentTouchedAxis] =
       
   331                 point[navigationState.currentTouchedAxis];
       
   332                 break;
       
   333             default:
       
   334                 break;
       
   335         }
       
   336     }
       
   337 
       
   338     function delta(e, gesture, gestureState) {
       
   339         var point = getPoint(e, gesture);
       
   340 
       
   341         return {
       
   342             x: point.x - gestureState.prevPanPosition.x,
       
   343             y: point.y - gestureState.prevPanPosition.y
       
   344         }
       
   345     }
       
   346 
       
   347     function getPoint(e, gesture) {
       
   348         if (gesture === 'pinch') {
       
   349             return {
       
   350                 x: (e.detail.touches[0].pageX + e.detail.touches[1].pageX) / 2,
       
   351                 y: (e.detail.touches[0].pageY + e.detail.touches[1].pageY) / 2
       
   352             }
       
   353         } else {
       
   354             return {
       
   355                 x: e.detail.touches[0].pageX,
       
   356                 y: e.detail.touches[0].pageY
       
   357             }
       
   358         }
       
   359     }
       
   360 })(jQuery);