src/event.js
/**
* @module hammer
*/
/**
* @class Event
* @static
*/
var Event = Hammer.event = {
/**
* when touch events have been fired, this is true
* this is used to stop mouse events
* @property prevent_mouseevents
* @private
* @type {Boolean}
*/
prevent_mouseevents: false,
/**
* if EVENT_START has been fired
* @property started
* @private
* @type {Boolean}
*/
started: false,
/**
* when the mouse is hold down, this is true
* @property should_detect
* @private
* @type {Boolean}
*/
should_detect: false,
/**
* simple event binder with a hook and support for multiple types
* @method on
* @param {HTMLElement} element
* @param {String} type
* @param {Function} handler
* @param {Function} [hook]
* @param {Object} hook.type
*/
on: function on(element, type, handler, hook) {
var types = type.split(' ');
Utils.each(types, function(type){
Utils.on(element, type, handler);
hook && hook(type);
});
},
/**
* simple event unbinder with a hook and support for multiple types
* @method off
* @param {HTMLElement} element
* @param {String} type
* @param {Function} handler
* @param {Function} [hook]
* @param {Object} hook.type
*/
off: function off(element, type, handler, hook) {
var types = type.split(' ');
Utils.each(types, function(type){
Utils.off(element, type, handler);
hook && hook(type);
});
},
/**
* the core touch event handler.
* this finds out if we should to detect gestures
* @method onTouch
* @param {HTMLElement} element
* @param {String} eventType matches `EVENT_START|MOVE|END`
* @param {Function} handler
* @return onTouchHandler {Function} the core event handler
*/
onTouch: function onTouch(element, eventType, handler) {
var self = this;
var onTouchHandler = function onTouchHandler(ev) {
var src_type = ev.type.toLowerCase()
, has_pointerevents = Hammer.HAS_POINTEREVENTS
, trigger_type
, is_mouse = Utils.inStr(src_type, 'mouse');
// if we are in a mouseevent, but there has been a touchevent triggered in this session
// we want to do nothing. simply break out of the event.
if(is_mouse && self.prevent_mouseevents) {
return;
}
// mousebutton must be down
else if(is_mouse && eventType == EVENT_START) {
self.prevent_mouseevents = false;
self.should_detect = true;
}
// just a valid start event, but no mouse
else if(eventType == EVENT_START && !is_mouse) {
self.prevent_mouseevents = true;
self.should_detect = true;
}
// update the pointer event before entering the detection
if(has_pointerevents && eventType != EVENT_END) {
PointerEvent.updatePointer(eventType, ev);
}
// we are in a touch/down state, so allowed detection of gestures
if(self.should_detect) {
trigger_type = self.doDetect.call(self, ev, eventType, element, handler);
}
// ...and we are done with the detection
// so reset everything to start each detection totally fresh
if(trigger_type == EVENT_END) {
self.prevent_mouseevents = false;
self.should_detect = false;
PointerEvent.reset();
}
// update the pointerevent object after the detection
else if(has_pointerevents && eventType == EVENT_END) {
PointerEvent.updatePointer(eventType, ev);
}
};
this.on(element, EVENT_TYPES[eventType], onTouchHandler);
return onTouchHandler;
},
/**
* the core detection method
* this finds out what hammer-touch-events to trigger
* @method doDetect
* @param {Object} ev
* @param {String} eventType matches `EVENT_START|MOVE|END`
* @param {HTMLElement} element
* @param {Function} handler
* @return {String} triggerType matches `EVENT_START|MOVE|END`
*/
doDetect: function doDetect(ev, eventType, element, handler) {
var touchList = this.getTouchList(ev, eventType);
var touchList_length = touchList.length;
var trigger_type = eventType;
var trigger_change = touchList.trigger; // used by fakeMultitouch plugin
var change_length = touchList_length;
// at each touchstart-like event we want also want to trigger a TOUCH event...
if(eventType == EVENT_START) {
trigger_change = EVENT_TOUCH;
}
// ...the same for a touchend-like event
else if(eventType == EVENT_END) {
trigger_change = EVENT_RELEASE;
// keep track of how many touches have been removed
change_length = touchList.length - ((ev.changedTouches) ? ev.changedTouches.length : 1);
}
// after there are still touches on the screen,
// we just want to trigger a MOVE event. so change the START or END to a MOVE
// but only after detection has been started, the first time we actualy want a START
if(change_length > 0 && this.started) {
trigger_type = EVENT_MOVE;
}
// detection has been started, we keep track of this, see above
this.started = true;
// generate some event data, some basic information
var ev_data = this.collectEventData(element, trigger_type, touchList, ev);
// trigger the trigger_type event before the change (TOUCH, RELEASE) events
// but the END event should be at last
if(eventType != EVENT_END) {
handler.call(Detection, ev_data);
}
// trigger a change (TOUCH, RELEASE) event, this means the length of the touches changed
if(trigger_change) {
ev_data.changedLength = change_length;
ev_data.eventType = trigger_change;
handler.call(Detection, ev_data);
ev_data.eventType = trigger_type;
delete ev_data.changedLength;
}
// trigger the END event
if(trigger_type == EVENT_END) {
handler.call(Detection, ev_data);
// ...and we are done with the detection
// so reset everything to start each detection totally fresh
this.started = false;
}
return trigger_type;
},
/**
* we have different events for each device/browser
* determine what we need and set them in the EVENT_TYPES constant
* the `onTouch` method is bind to these properties.
* @method determineEventTypes
* @return {Object} events
*/
determineEventTypes: function determineEventTypes() {
var types;
if(Hammer.HAS_POINTEREVENTS) {
// prefixed or full support?
if(window.PointerEvent) {
types = [
'pointerdown',
'pointermove',
'pointerup pointercancel'
];
}
// only IE has prefixed
else {
types = [
'MSPointerDown',
'MSPointerMove',
'MSPointerUp MSPointerCancel'
];
}
}
else {
types = [
'touchstart mousedown',
'touchmove mousemove',
'touchend touchcancel mouseup'];
}
EVENT_TYPES[EVENT_START] = types[0];
EVENT_TYPES[EVENT_MOVE] = types[1];
EVENT_TYPES[EVENT_END] = types[2];
return EVENT_TYPES;
},
/**
* create touchlist depending on the event
* @method getTouchList
* @param {Object} ev
* @param {String} eventType
* @return {Array} touches
*/
getTouchList: function getTouchList(ev, eventType) {
// get the fake pointerEvent touchlist
if(Hammer.HAS_POINTEREVENTS) {
return PointerEvent.getTouchList();
}
// get the touchlist
if(ev.touches) {
if(eventType == EVENT_MOVE) {
return ev.touches;
}
var identifiers = [];
var concat_touches = [].concat(Utils.toArray(ev.touches), Utils.toArray(ev.changedTouches));
var touchlist = [];
Utils.each(concat_touches, function(touch) {
if(Utils.inArray(identifiers, touch.identifier) === false) {
touchlist.push(touch);
}
identifiers.push(touch.identifier);
});
return touchlist;
}
// make fake touchlist from mouse position
ev.identifier = 1;
return [ev];
},
/**
* collect basic event data
* @method collectEventData
* @param {HTMLElement} element
* @param {String} eventType matches `EVENT_START|MOVE|END`
* @param {Array} touches
* @param {Object} ev
* @return {Object} ev
*/
collectEventData: function collectEventData(element, eventType, touches, ev) {
// find out pointerType
var pointerType = POINTER_TOUCH;
if(Utils.inStr(ev.type, 'mouse') || PointerEvent.matchType(POINTER_MOUSE, ev)) {
pointerType = POINTER_MOUSE;
}
return {
center : Utils.getCenter(touches),
timeStamp : Date.now(),
target : ev.target,
touches : touches,
eventType : eventType,
pointerType: pointerType,
srcEvent : ev,
/**
* prevent the browser default actions
* mostly used to disable scrolling of the browser
*/
preventDefault: function() {
var srcEvent = this.srcEvent;
srcEvent.preventManipulation && srcEvent.preventManipulation();
srcEvent.preventDefault && srcEvent.preventDefault();
},
/**
* stop bubbling the event up to its parents
*/
stopPropagation: function() {
this.srcEvent.stopPropagation();
},
/**
* immediately stop gesture detection
* might be useful after a swipe was detected
* @return {*}
*/
stopDetect: function() {
return Detection.stopDetect();
}
};
}
};