/** * Copyright 2013-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * * */ 'use strict'; var EventPluginUtils = require('./EventPluginUtils'); var EventPropagators = require('./EventPropagators'); var SyntheticUIEvent = require('./SyntheticUIEvent'); var TouchEventUtils = require('fbjs/lib/TouchEventUtils'); var ViewportMetrics = require('./ViewportMetrics'); var isStartish = EventPluginUtils.isStartish; var isEndish = EventPluginUtils.isEndish; /** * We are extending the Flow 'Touch' declaration to enable using bracket * notation to access properties. * Without this adjustment Flow throws * "Indexable signature not found in Touch". * See https://github.com/facebook/flow/issues/1323 */ /** * Number of pixels that are tolerated in between a `touchStart` and `touchEnd` * in order to still be considered a 'tap' event. */ var tapMoveThreshold = 10; var startCoords = { x: 0, y: 0 }; var Axis = { x: { page: 'pageX', client: 'clientX', envScroll: 'currentPageScrollLeft' }, y: { page: 'pageY', client: 'clientY', envScroll: 'currentPageScrollTop' } }; function getAxisCoordOfEvent(axis, nativeEvent) { var singleTouch = TouchEventUtils.extractSingleTouch(nativeEvent); if (singleTouch) { return singleTouch[axis.page]; } return axis.page in nativeEvent ? nativeEvent[axis.page] : nativeEvent[axis.client] + ViewportMetrics[axis.envScroll]; } function getDistance(coords, nativeEvent) { var pageX = getAxisCoordOfEvent(Axis.x, nativeEvent); var pageY = getAxisCoordOfEvent(Axis.y, nativeEvent); return Math.pow(Math.pow(pageX - coords.x, 2) + Math.pow(pageY - coords.y, 2), 0.5); } var touchEvents = ['topTouchStart', 'topTouchCancel', 'topTouchEnd', 'topTouchMove']; var dependencies = ['topMouseDown', 'topMouseMove', 'topMouseUp'].concat(touchEvents); var eventTypes = { touchTap: { phasedRegistrationNames: { bubbled: 'onTouchTap', captured: 'onTouchTapCapture' }, dependencies: dependencies } }; var usedTouch = false; var usedTouchTime = 0; var TOUCH_DELAY = 1000; var TapEventPlugin = { tapMoveThreshold: tapMoveThreshold, eventTypes: eventTypes, extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) { if (!isStartish(topLevelType) && !isEndish(topLevelType)) { return null; } // on ios, there is a delay after touch event and synthetic // mouse events, so that user can perform double tap // solution: ignore mouse events following touchevent within small timeframe if (touchEvents.indexOf(topLevelType) !== -1) { usedTouch = true; usedTouchTime = Date.now(); } else { if (usedTouch && Date.now() - usedTouchTime < TOUCH_DELAY) { return null; } } var event = null; var distance = getDistance(startCoords, nativeEvent); if (isEndish(topLevelType) && distance < tapMoveThreshold) { event = SyntheticUIEvent.getPooled(eventTypes.touchTap, targetInst, nativeEvent, nativeEventTarget); } if (isStartish(topLevelType)) { startCoords.x = getAxisCoordOfEvent(Axis.x, nativeEvent); startCoords.y = getAxisCoordOfEvent(Axis.y, nativeEvent); } else if (isEndish(topLevelType)) { startCoords.x = 0; startCoords.y = 0; } EventPropagators.accumulateTwoPhaseDispatches(event); return event; } }; module.exports = TapEventPlugin;