import type from './type';
import objects from './object';
/**
* Various event mappings
*
* @module Utils
* @class Utils.Events
* @static
*/
/**
* Creates device-specific eventtypes
*
* @property eventType
* @type {object}
* @for Utils.Events
*
* @example
* import {eventType} from './utils/events';
*
* eventType.DOWN; // -> mousedown/touchstart/pointerdown
* eventType.UP; // -> mouseup/touchend/pointerup
* eventType.MOVE; // -> mousemove/touchmove/pointermove
* eventType.CLICK; // -> click
* eventType.FOCUSIN; // -> focus
* eventType.FOCUSOUT; // -> blur
* eventType.OVER; // -> mouseover/mouseover/pointerover
* eventType.OUT; // -> mouseout/mouseout/pointerout
* eventType.SUBMIT; // -> submit
* eventType.RESET; // -> reset
* eventType.CHANGE; // -> change
* eventType.RESIZE; // -> resize/orientationchange/orientationchange
* eventType.TRANSIITON; // -> transitionend with vendor event names
*/
export const eventType = (() => {
var mapping = {
// initial mapping for mouse-devices
DOWN: 'mousedown',
MOVE: 'mousemove',
UP: 'mouseup',
CLICK: 'click',
FOCUSIN: 'focus',
FOCUSOUT: 'blur',
OVER: 'mouseover',
OUT: 'mouseout',
ENTER: 'mouseenter',
LEAVE: 'mouseleave',
SUBMIT: 'submit',
RESET: 'reset',
CHANGE: 'change',
RESIZE: 'resize',
SCROLL: 'scroll',
INPUT: 'keyup',
TRANSITION: 'transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd'
},
pointers = [
'-pointer-down',
'-pointer-up',
'-pointer-move',
'-pointer-over',
'-pointer-out',
'-pointer-enter',
'-pointer-leave'
],
format;
// modifies the given string, prepends it with b-arg
format = function (a, b, toUpper) {
return b + a.replace(/(-[a-z])/g, function (match) {
return toUpper ?
match.charAt(1).toUpperCase() + match.slice(2) :
match.slice(1);
});
}
if (navigator.pointerEnabled) {
// pointer-enabled device
return objects.extend(mapping, {
DOWN: format(pointers[0], ''),
MOVE: format(pointers[2], ''),
UP: format(pointers[1], ''),
OVER: format(pointers[3], ''),
OUT: format(pointers[4], ''),
ENTER: format(pointers[5], ''),
LEAVE: format(pointers[6], ''),
RESIZE: 'orientationchange resize'
});
}
if (navigator.msPointerEnabled) {
// ms-pointer-enabled device
return objects.extend(mapping, {
DOWN: format(pointers[0], 'MS', true),
MOVE: format(pointers[2], 'MS', true),
UP: format(pointers[1], 'MS', true),
OVER: format(pointers[3], 'MS', true),
OUT: format(pointers[4], 'MS', true),
ENTER: 'mouseenter',
LEAVE: 'mouseleave',
RESIZE: 'orientationchange resize'
});
}
if (document.ontouchstart !== undefined) {
// touch-enabled device
return objects.extend(mapping, {
DOWN: 'touchstart',
MOVE: 'touchmove',
UP: 'touchend',
RESIZE: 'orientationchange resize'
});
}
// if not returned until now, assume mouse-device
return mapping;
})();
// Helpers
const Swipe = {
current: {
startTime: null, // timestamp when swiping has started
endTime: null, // timestamp when swiping has stopped
duration: null, // absolute duration of the swipe
startPointX: null, // x-coordinate where swipe has started
endPointX: null, // x-coordinate where swipe has ended
startPointY: null, // y-coordinate where swipe has started
endPointY: null, // y-coordinate where swipe has ended
distanceX: 0, // absolute value of distance on x-axis in px
distanceY: 0, // absolute value of distance on y-axis in px
directionX: null, // -1 => left, 1 => right, 0 => not
directionY: null // 1 => down, -1 => up, 0 => not
},
trackStart: function (obj) {
Swipe.current.startTime = +new Date();
Swipe.current.startPointX = obj.pageX;
Swipe.current.startPointY = obj.pageY;
},
trackMove: function (obj) {
var cur = Swipe.current;
cur.directionX = Swipe.verticalDir(obj.pageX);
cur.directionY = Swipe.horizontalDir(obj.pageY);
cur.endTime = +new Date();
cur.duration = cur.endTime - cur.startTime;
cur.endPointX = obj.pageX;
cur.endPointY = obj.pageY;
cur.distanceX = Math.abs(cur.endPointX - cur.startPointX);
cur.distanceY = Math.abs(cur.endPointY - cur.startPointY);
return objects.inherit(obj, cur);
},
trackEnd: function (obj) {
var cur = Swipe.current;
cur.endTime = +new Date();
cur.duration = cur.endTime - cur.startTime;
cur.endPointX = obj.pageX;
cur.endPointY = obj.pageY;
cur.distanceX = Math.abs(cur.endPointX - cur.startPointX);
cur.distanceY = Math.abs(cur.endPointY - cur.startPointY);
// reset the current swipe values back to initial state
Swipe.current = {
startTime: null,
endTime: null,
duration: null,
startPointX: null,
endPointX: null,
startPointY: null,
endPointY: null,
distanceX: 0,
distanceY: 0,
directionX: null,
directionY: null
};
return objects.inherit(obj, cur);
},
unify: function (obj) {
// takes the eventobject (generic) and unifies it beween
// different implementations (mouse/touch/pointer events)
var oEvent = obj.originalEvent,
event = objects.extend({}, obj);
if (!type.isNil(oEvent.touches)) {
// touch driven device
if (!type.isNil(oEvent.changedTouches) && oEvent.changedTouches.length > 0) {
// iOS driven, where changedTouches have highest precedence
oEvent = oEvent.changedTouches[0];
} else if (!type.isNil(oEvent.targetTouches) && oEvent.targetTouches.length > 0) {
// iOS driven, targetTouches have second highest precedence
oEvent = oEvent.targetTouches[0];
} else {
// android device or target/changedTouches is empty
oEvent = oEvent.touches[0];
}
}
// pointer-devices and mouse-devices do not have the nonsense of different
// types of touches and we can safely use the oEvent data
event.pageX = oEvent.pageX;
event.pageY = oEvent.pageY;
event.preventDefault = obj.preventDefault.bind(obj);
event.stopPropagation = obj.stopPropagation.bind(obj);
event.stopImmediatePropagation = obj.stopImmediatePropagation.bind(obj);
return event;
},
verticalDir: function (xPos) {
var startPoint = Swipe.current.startPointX;
return xPos < startPoint ? 'left' :
xPos > startPoint ? 'right' :
'unmoved';
},
horizontalDir: function (yPos) {
var startPoint = Swipe.current.startPointY;
return yPos < startPoint ? 'up' :
yPos > startPoint ? 'down' :
'unmoved';
},
setTouchAction: function (nodeRef) {
// adds the css "touch-action" property to ms-pointer using
// devices since they otherwise show strange behaviour and
// touch-action events are only supported by IE 10+
//
nodeRef.css({
WebkitTouchAction: 'pan-y',
MozTouchAction: 'pan-y',
msTouchAction: 'pan-y',
touchAction: 'pan-y'
});
}
};
// API
export const onSwipe = (nodeRef, func) => {
var $node = $(nodeRef);
// add a listener to start listening on mousedown/touchstart/pointerdown
$node.on(eventType.DOWN, function (event) {
Swipe.trackStart(Swipe.unify(event));
});
// add a listener which updates on swipe movements for mousemove/touchmove/pointermove
$node.on(eventType.MOVE, function (event) {
if (Swipe.current.startTime !== null) {
func.call(this, Swipe.trackMove(Swipe.unify(event)));
}
});
// register a listener which fires the given function with a unifyd event object
// as argument on the node for mouseup/touchend/pointerup events
$node.on(eventType.UP, function (event) {
if (Swipe.current.startTime !== null) {
func.call(this, Swipe.trackEnd(Swipe.unify(event)));
}
});
Swipe.setTouchAction($node);
// return a "release" function which removes the swipe behaviour again
return function () {
$node.off(eventType.DOWN);
$node.off(eventType.MOVE);
$node.off(eventType.UP);
}
}
export const isMultiTouch = (() => {
var _isMultiTouch = false;
if (navigator.pointerEnabled || navigator.msPointerEnabled) {
_isMultiTouch = (navigator.maxTouchPoints || navigator.msMaxTouchPoints) > 1;
} else if (document.ontouchstart) {
_isMultiTouch = true;
}
return () => _isMultiTouch;
})();
export default {
eventType, onSwipe, isMultiTouch
}