AdminLTE/plugins/flot/plugins/jquery.flot.touch.js

321 lines
11 KiB
JavaScript

/* global jQuery */
(function($) {
'use strict';
var options = {
propagateSupportedGesture: false
};
function init(plot) {
plot.hooks.processOptions.push(initTouchNavigation);
}
function initTouchNavigation(plot, options) {
var gestureState = {
twoTouches: false,
currentTapStart: { x: 0, y: 0 },
currentTapEnd: { x: 0, y: 0 },
prevTap: { x: 0, y: 0 },
currentTap: { x: 0, y: 0 },
interceptedLongTap: false,
isUnsupportedGesture: false,
prevTapTime: null,
tapStartTime: null,
longTapTriggerId: null
},
maxDistanceBetweenTaps = 20,
maxIntervalBetweenTaps = 500,
maxLongTapDistance = 20,
minLongTapDuration = 1500,
pressedTapDuration = 125,
mainEventHolder;
function interpretGestures(e) {
var o = plot.getOptions();
if (!o.pan.active && !o.zoom.active) {
return;
}
updateOnMultipleTouches(e);
mainEventHolder.dispatchEvent(new CustomEvent('touchevent', { detail: e }));
if (isPinchEvent(e)) {
executeAction(e, 'pinch');
} else {
executeAction(e, 'pan');
if (!wasPinchEvent(e)) {
if (isDoubleTap(e)) {
executeAction(e, 'doubleTap');
}
executeAction(e, 'tap');
executeAction(e, 'longTap');
}
}
}
function executeAction(e, gesture) {
switch (gesture) {
case 'pan':
pan[e.type](e);
break;
case 'pinch':
pinch[e.type](e);
break;
case 'doubleTap':
doubleTap.onDoubleTap(e);
break;
case 'longTap':
longTap[e.type](e);
break;
case 'tap':
tap[e.type](e);
break;
}
}
function bindEvents(plot, eventHolder) {
mainEventHolder = eventHolder[0];
eventHolder[0].addEventListener('touchstart', interpretGestures, false);
eventHolder[0].addEventListener('touchmove', interpretGestures, false);
eventHolder[0].addEventListener('touchend', interpretGestures, false);
}
function shutdown(plot, eventHolder) {
eventHolder[0].removeEventListener('touchstart', interpretGestures);
eventHolder[0].removeEventListener('touchmove', interpretGestures);
eventHolder[0].removeEventListener('touchend', interpretGestures);
if (gestureState.longTapTriggerId) {
clearTimeout(gestureState.longTapTriggerId);
gestureState.longTapTriggerId = null;
}
}
var pan = {
touchstart: function(e) {
updatePrevForDoubleTap();
updateCurrentForDoubleTap(e);
updateStateForLongTapStart(e);
mainEventHolder.dispatchEvent(new CustomEvent('panstart', { detail: e }));
},
touchmove: function(e) {
preventEventBehaviors(e);
updateCurrentForDoubleTap(e);
updateStateForLongTapEnd(e);
if (!gestureState.isUnsupportedGesture) {
mainEventHolder.dispatchEvent(new CustomEvent('pandrag', { detail: e }));
}
},
touchend: function(e) {
preventEventBehaviors(e);
if (wasPinchEvent(e)) {
mainEventHolder.dispatchEvent(new CustomEvent('pinchend', { detail: e }));
mainEventHolder.dispatchEvent(new CustomEvent('panstart', { detail: e }));
} else if (noTouchActive(e)) {
mainEventHolder.dispatchEvent(new CustomEvent('panend', { detail: e }));
}
}
};
var pinch = {
touchstart: function(e) {
mainEventHolder.dispatchEvent(new CustomEvent('pinchstart', { detail: e }));
},
touchmove: function(e) {
preventEventBehaviors(e);
gestureState.twoTouches = isPinchEvent(e);
if (!gestureState.isUnsupportedGesture) {
mainEventHolder.dispatchEvent(new CustomEvent('pinchdrag', { detail: e }));
}
},
touchend: function(e) {
preventEventBehaviors(e);
}
};
var doubleTap = {
onDoubleTap: function(e) {
preventEventBehaviors(e);
mainEventHolder.dispatchEvent(new CustomEvent('doubletap', { detail: e }));
}
};
var longTap = {
touchstart: function(e) {
longTap.waitForLongTap(e);
},
touchmove: function(e) {
},
touchend: function(e) {
if (gestureState.longTapTriggerId) {
clearTimeout(gestureState.longTapTriggerId);
gestureState.longTapTriggerId = null;
}
},
isLongTap: function(e) {
var currentTime = new Date().getTime(),
tapDuration = currentTime - gestureState.tapStartTime;
if (tapDuration >= minLongTapDuration && !gestureState.interceptedLongTap) {
if (distance(gestureState.currentTapStart.x, gestureState.currentTapStart.y, gestureState.currentTapEnd.x, gestureState.currentTapEnd.y) < maxLongTapDistance) {
gestureState.interceptedLongTap = true;
return true;
}
}
return false;
},
waitForLongTap: function(e) {
var longTapTrigger = function() {
if (longTap.isLongTap(e)) {
mainEventHolder.dispatchEvent(new CustomEvent('longtap', { detail: e }));
}
gestureState.longTapTriggerId = null;
};
if (!gestureState.longTapTriggerId) {
gestureState.longTapTriggerId = setTimeout(longTapTrigger, minLongTapDuration);
}
}
};
var tap = {
touchstart: function(e) {
gestureState.tapStartTime = new Date().getTime();
},
touchmove: function(e) {
},
touchend: function(e) {
if (tap.isTap(e)) {
mainEventHolder.dispatchEvent(new CustomEvent('tap', { detail: e }));
preventEventBehaviors(e);
}
},
isTap: function(e) {
var currentTime = new Date().getTime(),
tapDuration = currentTime - gestureState.tapStartTime;
if (tapDuration <= pressedTapDuration) {
if (distance(gestureState.currentTapStart.x, gestureState.currentTapStart.y, gestureState.currentTapEnd.x, gestureState.currentTapEnd.y) < maxLongTapDistance) {
return true;
}
}
return false;
}
};
if (options.pan.enableTouch === true || options.zoom.enableTouch) {
plot.hooks.bindEvents.push(bindEvents);
plot.hooks.shutdown.push(shutdown);
};
function updatePrevForDoubleTap() {
gestureState.prevTap = {
x: gestureState.currentTap.x,
y: gestureState.currentTap.y
};
};
function updateCurrentForDoubleTap(e) {
gestureState.currentTap = {
x: e.touches[0].pageX,
y: e.touches[0].pageY
};
}
function updateStateForLongTapStart(e) {
gestureState.tapStartTime = new Date().getTime();
gestureState.interceptedLongTap = false;
gestureState.currentTapStart = {
x: e.touches[0].pageX,
y: e.touches[0].pageY
};
gestureState.currentTapEnd = {
x: e.touches[0].pageX,
y: e.touches[0].pageY
};
};
function updateStateForLongTapEnd(e) {
gestureState.currentTapEnd = {
x: e.touches[0].pageX,
y: e.touches[0].pageY
};
};
function isDoubleTap(e) {
var currentTime = new Date().getTime(),
intervalBetweenTaps = currentTime - gestureState.prevTapTime;
if (intervalBetweenTaps >= 0 && intervalBetweenTaps < maxIntervalBetweenTaps) {
if (distance(gestureState.prevTap.x, gestureState.prevTap.y, gestureState.currentTap.x, gestureState.currentTap.y) < maxDistanceBetweenTaps) {
e.firstTouch = gestureState.prevTap;
e.secondTouch = gestureState.currentTap;
return true;
}
}
gestureState.prevTapTime = currentTime;
return false;
}
function preventEventBehaviors(e) {
if (!gestureState.isUnsupportedGesture) {
e.preventDefault();
if (!plot.getOptions().propagateSupportedGesture) {
e.stopPropagation();
}
}
}
function distance(x1, y1, x2, y2) {
return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
function noTouchActive(e) {
return (e.touches && e.touches.length === 0);
}
function wasPinchEvent(e) {
return (gestureState.twoTouches && e.touches.length === 1);
}
function updateOnMultipleTouches(e) {
if (e.touches.length >= 3) {
gestureState.isUnsupportedGesture = true;
} else {
gestureState.isUnsupportedGesture = false;
}
}
function isPinchEvent(e) {
if (e.touches && e.touches.length >= 2) {
if (e.touches[0].target === plot.getEventHolder() &&
e.touches[1].target === plot.getEventHolder()) {
return true;
}
}
return false;
}
}
$.plot.plugins.push({
init: init,
options: options,
name: 'navigateTouch',
version: '0.3'
});
})(jQuery);