UNPKG

hammerjs

Version:

A javascript library for multi-touch gestures

339 lines (293 loc) 9.53 kB
/** * @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(); } }; } };