Launch Week Day 5: Introducing Reachability for PHP.Learn More
Socket
Book a DemoSign in
Socket

react-fastclick

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-fastclick - npm Package Compare versions

Comparing version
2.1.2
to
3.0.0
+1
.nvmrc
6.9.4

Sorry, the diff of this file is not supported yet

machine:
node:
version: 6.9.4
general:
branches:
ignore:
- gh-pages
'use strict';
(function () {
var getReactFCInitializer = function (React) {
return function initializeReactFastclick () {
var originalCreateElement = React.createElement;
// Moved if Math.abs(downX - upX) > MOVE_THRESHOLD;
var MOVE_THRESHOLD = 8;
var TOUCH_DELAY = 1000;
var touchKeysToStore = [
'clientX',
'clientY',
'pageX',
'pageY',
'screenX',
'screenY',
'radiusX',
'radiusY'
];
var touchEvents = {
downPos: {},
lastPos: {}
};
var isDisabled = function (element) {
if (!element) {
return false;
}
var disabled = element.getAttribute('disabled');
return disabled !== false && disabled !== null;
};
var focus = function (event, target) {
var myTarget = target || event.currentTarget;
if (!myTarget || isDisabled(myTarget)) {
return;
}
myTarget.focus();
};
var handleType = {
input: function (event) {
focus(event);
event.stopPropagation();
},
textarea: function (event) {
focus(event);
event.stopPropagation();
},
select: function (event) {
focus(event);
event.stopPropagation();
},
label: function (event) {
var input;
var forTarget = event.currentTarget.getAttribute('for');
if (forTarget) {
input = document.getElementById(forTarget);
} else {
input = event.currentTarget.querySelectorAll('input, textarea, select')[0];
}
if (input) {
focus(event, input);
}
}
};
var fakeClickEvent = function (event) {
if (typeof event.persist === 'function') {
event.persist();
}
event.fastclick = true;
event.type = 'click';
event.button = 0;
};
var copyTouchKeys = function (touch, target) {
if (typeof target.persist === 'function') {
target.persist();
}
if (touch) {
for (var i = 0; i < touchKeysToStore.length; i += 1) {
var key = touchKeysToStore[i];
target[key] = touch[key];
}
}
};
var noTouchHappened = function () {
return !touchEvents.touched && (
!touchEvents.lastTouchDate || new Date().getTime() > touchEvents.lastTouchDate + TOUCH_DELAY
);
};
var invalidateIfMoreThanOneTouch = function (event) {
touchEvents.invalid = event.touches && event.touches.length > 1 || touchEvents.invalid;
};
var onMouseEvent = function (callback, event) {
// Prevent any mouse events if we touched recently
if (typeof callback === 'function' && noTouchHappened()) {
callback(event);
}
if (event.type === 'click') {
touchEvents.invalid = false;
touchEvents.touched = false;
touchEvents.moved = false;
}
};
var onTouchStart = function (callback, event) {
touchEvents.invalid = false;
touchEvents.moved = false;
touchEvents.touched = true;
touchEvents.lastTouchDate = new Date().getTime();
copyTouchKeys(event.touches[0], touchEvents.downPos);
copyTouchKeys(event.touches[0], touchEvents.lastPos);
invalidateIfMoreThanOneTouch(event);
if (typeof callback === 'function') {
callback(event);
}
};
var onTouchMove = function (callback, event) {
touchEvents.touched = true;
touchEvents.lastTouchDate = new Date().getTime();
copyTouchKeys(event.touches[0], touchEvents.lastPos);
invalidateIfMoreThanOneTouch(event);
if (Math.abs(touchEvents.downPos.clientX - touchEvents.lastPos.clientX) > MOVE_THRESHOLD ||
Math.abs(touchEvents.downPos.clientY - touchEvents.lastPos.clientY) > MOVE_THRESHOLD) {
touchEvents.moved = true;
}
if (typeof callback === 'function') {
callback(event);
}
};
var onTouchEnd = function (callback, onClick, type, event) {
touchEvents.touched = true;
touchEvents.lastTouchDate = new Date().getTime();
invalidateIfMoreThanOneTouch(event);
if (typeof callback === 'function') {
callback(event);
}
if (!touchEvents.invalid && !touchEvents.moved) {
var box = event.currentTarget.getBoundingClientRect();
if (touchEvents.lastPos.clientX - (touchEvents.lastPos.radiusX || 0) <= box.right &&
touchEvents.lastPos.clientX + (touchEvents.lastPos.radiusX || 0) >= box.left &&
touchEvents.lastPos.clientY - (touchEvents.lastPos.radiusY || 0) <= box.bottom &&
touchEvents.lastPos.clientY + (touchEvents.lastPos.radiusY || 0) >= box.top) {
if (!isDisabled(event.currentTarget)) {
if (typeof onClick === 'function') {
copyTouchKeys(touchEvents.lastPos, event);
fakeClickEvent(event);
onClick(event);
}
if (!event.defaultPrevented && handleType[type]) {
handleType[type](event);
}
}
}
}
};
var propsWithFastclickEvents = function (type, props) {
var newProps = {};
// Loop over props
for (var key in props) {
// Copy props to newProps
newProps[key] = props[key];
}
// Apply our wrapped mouse and touch handlers
newProps.onClick = onMouseEvent.bind(null, props.onClick);
newProps.onMouseDown = onMouseEvent.bind(null, props.onMouseDown);
newProps.onMouseMove = onMouseEvent.bind(null, props.onMouseMove);
newProps.onMouseUp = onMouseEvent.bind(null, props.onMouseUp);
newProps.onTouchStart = onTouchStart.bind(null, props.onTouchStart);
newProps.onTouchMove = onTouchMove.bind(null, props.onTouchMove);
newProps.onTouchEnd = onTouchEnd.bind(null, props.onTouchEnd, props.onClick, type);
if (typeof Object.freeze === 'function') {
Object.freeze(newProps);
}
return newProps;
};
React.createElement = function () {
// Convert arguments to array
var args = Array.prototype.slice.call(arguments);
var type = args[0];
var props = args[1];
// Check if basic element & has onClick prop
if (type && typeof type === 'string' && (
(props && typeof props.onClick === 'function') || handleType[type]
)) {
// Add our own events to props
args[1] = propsWithFastclickEvents(type, props || {});
}
// Apply args to original createElement function
return originalCreateElement.apply(null, args);
};
if (typeof React.DOM === 'object') {
for (var key in React.DOM) {
React.DOM[key] = React.createElement.bind(null, key);
}
}
};
};
/* istanbul ignore next */
// Export for commonjs / browserify
if (typeof exports === 'object' && typeof module !== 'undefined') {
var React = require('react');
module.exports = getReactFCInitializer(React);
// Export for amd / require
} else if (typeof define === 'function' && define.amd) { // eslint-disable-line no-undef
define(['react'], function (ReactAMD) { // eslint-disable-line no-undef
return getReactFCInitializer(ReactAMD);
});
// Export globally
} else {
var root;
if (typeof window !== 'undefined') {
root = window;
} else if (typeof global !== 'undefined') {
root = global;
} else if (typeof self !== 'undefined') {
root = self;
} else {
root = this;
}
root.Reorder = getReactFCInitializer(root.React);
}
})();
'use strict';
var expect = require('chai').expect;
var sinon = require('sinon');
var spy = sinon.spy;
var stub = sinon.stub;
var TestUtils = require('react-addons-test-utils');
var renderIntoApp = require('./helpers/render-into-app');
describe('react-fastclick', function () {
var originalCreateElement, fastclickCreateElement;
function handlerKeyToSimulatedEventKey (key) {
var simulatedEventKey = key.replace(/^on/, '');
return simulatedEventKey.charAt(0).toLowerCase() + simulatedEventKey.substring(1);
}
function getBoundingClientRect () {
return {
top: 25,
left: 25,
right: 75,
bottom: 75,
width: 50,
height: 50
};
}
var touches = [
{
clientX: 50,
clientY: 50
}
];
var specialTypes = [
'input',
'textarea',
'select',
'label'
];
var additionalProps = {
onClick: function () {},
onMouseDown: function () {},
onMouseMove: function () {},
onMouseUp: function () {},
onTouchStart: function () {},
onTouchMove: function () {},
onTouchEnd: function () {}
};
beforeEach(function () {
// Clear module cache
delete require.cache[require.resolve('react')];
delete require.cache[require.resolve('../src/index')];
});
it('should redefine React.createElement', function () {
originalCreateElement = require('react').createElement;
var theSameCreateElement = require('react').createElement;
expect(originalCreateElement).to.equal(theSameCreateElement);
require('../src/index')();
fastclickCreateElement = require('react').createElement;
expect(originalCreateElement).not.to.equal(fastclickCreateElement);
});
describe('createElement', function () {
it('should create a regular React element', function () {
var element = fastclickCreateElement('div');
expect(element).to.exist;
expect(element.ref).to.be.null;
expect(element.key).to.be.null;
expect(element.type).to.equal('div');
expect(element.props).to.eql({});
});
it('should add events if it is a special element', function () {
var element;
for (var i = 0; i < specialTypes.length; i += 1) {
element = fastclickCreateElement(specialTypes[i]);
for (var key in additionalProps) {
expect(typeof element.props[key]).to.equal('function');
}
}
});
it('should add events if it has an onClick handler', function () {
var element = fastclickCreateElement('div', {onClick: function () {}});
for (var key in additionalProps) {
expect(typeof element.props[key]).to.equal('function');
}
});
});
describe('mouse events', function () {
it('should trigger standard mouse event handlers', function () {
var props = {
onMouseDown: spy(),
onMouseMove: spy(),
onMouseUp: spy(),
onClick: spy()
};
var node = renderIntoApp(fastclickCreateElement('div', props));
for (var key in props) {
var mouseEvent = handlerKeyToSimulatedEventKey(key);
TestUtils.Simulate[mouseEvent](node);
expect(props[key]).to.have.been.calledOnce;
}
});
});
describe('touch events', function () {
it('should trigger standard touch event handlers', function () {
var props = {
onClick: function () {},
onTouchStart: spy(),
onTouchMove: spy(),
onTouchEnd: spy()
};
var node = renderIntoApp(fastclickCreateElement('div', props));
for (var key in props) {
if (key !== 'onClick') {
var touchEvent = handlerKeyToSimulatedEventKey(key);
TestUtils.Simulate[touchEvent](node, {touches: [{}]});
expect(props[key]).to.have.been.calledOnce;
}
}
});
it('should trigger the click handler when a fastclick happens', function () {
var props = {
onClick: spy()
};
var node = renderIntoApp(fastclickCreateElement('div', props));
var getBoundingClientRectStub = stub(node, 'getBoundingClientRect', getBoundingClientRect);
TestUtils.Simulate.touchStart(
node,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchEnd(
node,
{
type: 'touchend',
touches: null
}
);
expect(props.onClick).to.have.been.calledOnce;
TestUtils.Simulate.click(
node,
{
type: 'click'
}
);
expect(props.onClick).to.have.been.calledOnce;
getBoundingClientRectStub.restore();
});
it('should not trigger the click handler if multiple touches', function () {
var props = {
onClick: spy()
};
var node = renderIntoApp(fastclickCreateElement('div', props));
var getBoundingClientRectStub = stub(node, 'getBoundingClientRect', getBoundingClientRect);
TestUtils.Simulate.touchStart(
node,
{
type: 'touchstart',
touches: [touches[0], touches[0]]
}
);
TestUtils.Simulate.touchEnd(
node,
{
type: 'touchend',
touches: null
}
);
expect(props.onClick).not.to.have.been.called;
TestUtils.Simulate.click(
node,
{
type: 'click'
}
);
expect(props.onClick).not.to.have.been.called;
getBoundingClientRectStub.restore();
});
it('should not trigger the click handler if touch moves', function () {
var props = {
onClick: spy()
};
var node = renderIntoApp(fastclickCreateElement('div', props));
var getBoundingClientRectStub = stub(node, 'getBoundingClientRect', getBoundingClientRect);
TestUtils.Simulate.touchStart(
node,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchMove(
node,
{
type: 'touchmove',
touches: [
{
clientX: 60,
clientY: 50
}
]
}
);
TestUtils.Simulate.touchEnd(
node,
{
type: 'touchend',
touches: null
}
);
expect(props.onClick).not.to.have.been.called;
TestUtils.Simulate.click(
node,
{
type: 'click'
}
);
expect(props.onClick).not.to.have.been.called;
getBoundingClientRectStub.restore();
});
it('should not trigger the click handler if touch is outside of the element', function () {
var props = {
onClick: spy()
};
var node = renderIntoApp(fastclickCreateElement('div', props));
var getBoundingClientRectStub = stub(node, 'getBoundingClientRect', getBoundingClientRect);
TestUtils.Simulate.touchStart(
node,
{
type: 'touchstart',
touches: [
{
clientX: 80,
clientY: 80
}
]
}
);
TestUtils.Simulate.touchEnd(
node,
{
type: 'touchend',
touches: null
}
);
expect(props.onClick).not.to.have.been.called;
TestUtils.Simulate.click(
node,
{
type: 'click'
}
);
expect(props.onClick).not.to.have.been.called;
getBoundingClientRectStub.restore();
});
});
describe('special elements', function () {
it('should focus inputs, selects, and textareas when a fastclick is triggered', function () {
var node, getBoundingClientRectStub, focusSpy;
for (var i = 0; i < specialTypes.length; i += 1) {
var type = specialTypes[i];
if (type !== 'label') {
node = renderIntoApp(fastclickCreateElement(type));
getBoundingClientRectStub = stub(node, 'getBoundingClientRect', getBoundingClientRect);
focusSpy = spy(node, 'focus');
TestUtils.Simulate.touchStart(
node,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchEnd(
node,
{
type: 'touchend',
touches: null
}
);
expect(focusSpy).to.have.been.calledOnce;
TestUtils.Simulate.click(
node,
{
type: 'click'
}
);
expect(focusSpy).to.have.been.calledOnce;
getBoundingClientRectStub.restore();
focusSpy.restore();
}
}
});
it('should not focus inputs, selects, and textareas if they are disabled', function () {
var node, getBoundingClientRectStub, focusSpy;
for (var i = 0; i < specialTypes.length; i += 1) {
var type = specialTypes[i];
if (type !== 'label') {
node = renderIntoApp(fastclickCreateElement(type, {disabled: true}));
getBoundingClientRectStub = stub(node, 'getBoundingClientRect', getBoundingClientRect);
focusSpy = spy(node, 'focus');
TestUtils.Simulate.touchStart(
node,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchEnd(
node,
{
type: 'touchend',
touches: null
}
);
expect(focusSpy).not.to.have.been.called;
TestUtils.Simulate.click(
node,
{
type: 'click'
}
);
expect(focusSpy).not.to.have.been.called;
getBoundingClientRectStub.restore();
focusSpy.restore();
}
}
});
it('should focus an input inside a label', function () {
var node = renderIntoApp(
fastclickCreateElement('label', null, fastclickCreateElement('input'))
);
var label = node;
var input = label.getElementsByTagName('input')[0];
var getBoundingClientRectStub = stub(label, 'getBoundingClientRect', getBoundingClientRect);
var focusSpy = spy(input, 'focus');
TestUtils.Simulate.touchStart(
label,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchEnd(
label,
{
type: 'touchend',
touches: null
}
);
expect(focusSpy).to.have.been.calledOnce;
TestUtils.Simulate.click(
label,
{
type: 'click'
}
);
expect(focusSpy).to.have.been.calledOnce;
getBoundingClientRectStub.restore();
focusSpy.restore();
});
it('should not focus a disabled input inside a label', function () {
var node = renderIntoApp(
fastclickCreateElement('label', null, fastclickCreateElement('input', {disabled: true}))
);
var label = node;
var input = label.getElementsByTagName('input')[0];
var getBoundingClientRectStub = stub(label, 'getBoundingClientRect', getBoundingClientRect);
var focusSpy = spy(input, 'focus');
TestUtils.Simulate.touchStart(
label,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchEnd(
label,
{
type: 'touchend',
touches: null
}
);
expect(focusSpy).not.to.have.been.calledOnce;
TestUtils.Simulate.click(
label,
{
type: 'click'
}
);
expect(focusSpy).not.to.have.been.calledOnce;
getBoundingClientRectStub.restore();
focusSpy.restore();
});
it('should focus an input for a label', function () {
var node = renderIntoApp(
fastclickCreateElement(
'div',
null,
fastclickCreateElement('label', {htmlFor: 'my-input'}),
fastclickCreateElement('input', {id: 'my-input'})
)
);
var label = node.getElementsByTagName('label')[0];
var input = node.getElementsByTagName('input')[0];
var getBoundingClientRectStub = stub(label, 'getBoundingClientRect', getBoundingClientRect);
var focusSpy = spy(input, 'focus');
TestUtils.Simulate.touchStart(
label,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchEnd(
label,
{
type: 'touchend',
touches: null
}
);
expect(focusSpy).to.have.been.calledOnce;
TestUtils.Simulate.click(
label,
{
type: 'click'
}
);
expect(focusSpy).to.have.been.calledOnce;
getBoundingClientRectStub.restore();
focusSpy.restore();
});
it('should not focus a disabled input for a label', function () {
var node = renderIntoApp(
fastclickCreateElement(
'div',
null,
fastclickCreateElement('label', {htmlFor: 'my-input'}),
fastclickCreateElement('input', {id: 'my-input', disabled: true})
)
);
var label = node.getElementsByTagName('label')[0];
var input = node.getElementsByTagName('input')[0];
var getBoundingClientRectStub = stub(label, 'getBoundingClientRect', getBoundingClientRect);
var focusSpy = spy(input, 'focus');
TestUtils.Simulate.touchStart(
label,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchEnd(
label,
{
type: 'touchend',
touches: null
}
);
expect(focusSpy).not.to.have.been.calledOnce;
TestUtils.Simulate.click(
label,
{
type: 'click'
}
);
expect(focusSpy).not.to.have.been.calledOnce;
getBoundingClientRectStub.restore();
focusSpy.restore();
});
it('should gracefully handle no input inside a label', function () {
var node = renderIntoApp(fastclickCreateElement('label'));
var getBoundingClientRectStub = stub(node, 'getBoundingClientRect', getBoundingClientRect);
TestUtils.Simulate.touchStart(
node,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchEnd(
node,
{
type: 'touchend',
touches: null
}
);
TestUtils.Simulate.click(
node,
{
type: 'click'
}
);
getBoundingClientRectStub.restore();
});
it('should gracefully handle no input for a label', function () {
var node = renderIntoApp(fastclickCreateElement('label', {htmlFor: 'my-input'}));
var getBoundingClientRectStub = stub(node, 'getBoundingClientRect', getBoundingClientRect);
TestUtils.Simulate.touchStart(
node,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchEnd(
node,
{
type: 'touchend',
touches: null
}
);
TestUtils.Simulate.click(
node,
{
type: 'click'
}
);
getBoundingClientRectStub.restore();
});
});
describe('events', function () {
it('should persist events if they are to be mutated', function () {
var props = {
onClick: spy()
};
var node = renderIntoApp(fastclickCreateElement('div', props));
var getBoundingClientRectStub = stub(node, 'getBoundingClientRect', getBoundingClientRect);
var persistSpy = spy();
TestUtils.Simulate.touchStart(
node,
{
type: 'touchstart',
touches: touches,
persist: persistSpy
}
);
expect(persistSpy).not.to.have.been.called;
TestUtils.Simulate.touchEnd(
node,
{
type: 'touchend',
touches: null,
persist: persistSpy
}
);
// Properties copied onto event from stored positions, and fake click event properties
expect(persistSpy).to.have.been.calledTwice;
persistSpy.reset();
TestUtils.Simulate.click(
node,
{
type: 'click',
persist: persistSpy
}
);
expect(persistSpy).not.to.have.been.called;
expect(props.onClick).to.have.been.calledOnce;
getBoundingClientRectStub.restore();
});
});
});
+16
-10
{
"name": "react-fastclick",
"version": "2.1.2",
"version": "3.0.0",
"description": "Fast Touch Events for React",
"main": "lib/index.js",
"main": "src/index.js",
"scripts": {
"mocha": "istanbul cover node_modules/mocha/bin/_mocha -- --require tests/helpers/test-setup.js --bail --recursive tests",
"eslint": "eslint -c node_modules/eslintrc/.eslintrc-es5 lib tests/helpers && eslint -c node_modules/eslintrc/.eslintrc-es5-mocha tests",
"test": "npm run eslint && npm run mocha"
"mocha": "nyc mocha --bail --recursive 'tests/**/*.test.js'",
"lint-tests": "eslint -c node_modules/eslintrc/.eslintrc-es5-mocha tests/",
"lint-src": "eslint -c node_modules/eslintrc/.eslintrc-es5 src/",
"test": "npm run lint-src && npm run lint-tests && npm run mocha"
},

@@ -31,16 +32,21 @@ "repository": {

"homepage": "https://github.com/JakeSidSmith/react-fastclick",
"dependencies": {
"react": ">=0.12.0"
"peerDependencies": {
"react": "*"
},
"dependencies": {},
"devDependencies": {
"chai": "=3.5.0",
"eslintrc": "git+https://github.com/JakeSidSmith/eslintrc.git#v0.0.1",
"istanbul": "=1.0.0-alpha.2",
"jsdom": "=8.4.1",
"mocha": "=2.4.5",
"react-addons-test-utils": ">=0.12.0",
"react-dom": ">=0.12.0",
"nyc": "=10.1.2",
"react": "=15.4.2",
"react-addons-test-utils": "=15.4.2",
"react-dom": "=15.4.2",
"sinon": "=1.17.3",
"sinon-chai": "=2.8.0"
},
"engines": {
"node": "6.9.4"
}
}

@@ -1,4 +0,2 @@

# React Fastclick
[![Build Status](https://travis-ci.org/JakeSidSmith/react-fastclick.svg?branch=master)](https://travis-ci.org/JakeSidSmith/react-fastclick)
# React Fastclick [![CircleCI](https://circleci.com/gh/JakeSidSmith/react-fastclick.svg?style=svg)](https://circleci.com/gh/JakeSidSmith/react-fastclick)
**Instantly make your desktop / hybrid apps more responsive on touch devices.**

@@ -18,3 +16,3 @@

Include `react-fastclick` in your main javascript file before any of your components are created, and you're done.
Initialize `react-fastclick` in your main javascript file before any of your components are created, and you're done.

@@ -26,3 +24,4 @@ Now any calls to onClick or elements with special functionality, such as inputs, will have fast touch events added automatically - no need to write any additional listeners.

```javascript
import 'react-fastclick';
import initReactFastclick from 'react-fastclick';
initReactFastclick();
```

@@ -33,3 +32,4 @@

```javascript
require('react-fastclick');
var initReactFastclick = require('react-fastclick');
initReactFastclick();
```

@@ -72,4 +72,2 @@

React Fastclick 2.1.2 has been tested with React 15, and should support older versions listed below.
React Fastclick version 2.x.x has been tested with React 0.12, 0.13 and 0.14, and should work with even older versions.
React Fastclick 3.x.x has been tested with React 15, but should support older versions also.
verbose: false
instrumentation:
root: 'lib'
extensions:
- .js
default-excludes: true
excludes: []
include-all-sources: true
language: node_js
node_js:
- '5.9.0'
install:
- npm install
script:
- npm test
'use strict';
(function () {
var React = require('react');
var originalCreateElement = React.createElement;
// Moved if Math.abs(downX - upX) > MOVE_THRESHOLD;
var MOVE_THRESHOLD = 8;
var TOUCH_DELAY = 1000;
var touchKeysToStore = [
'clientX',
'clientY',
'pageX',
'pageY',
'screenX',
'screenY',
'radiusX',
'radiusY'
];
var touchEvents = {
downPos: {},
lastPos: {}
};
var isDisabled = function (element) {
if (!element) {
return false;
}
var disabled = element.getAttribute('disabled');
return disabled !== false && disabled !== null;
};
var focus = function (event, target) {
var myTarget = target || event.currentTarget;
if (!myTarget || isDisabled(myTarget)) {
return;
}
myTarget.focus();
};
var handleType = {
input: function (event) {
focus(event);
event.stopPropagation();
},
textarea: function (event) {
focus(event);
event.stopPropagation();
},
select: function (event) {
focus(event);
event.stopPropagation();
},
label: function (event) {
var input;
var forTarget = event.currentTarget.getAttribute('for');
if (forTarget) {
input = document.getElementById(forTarget);
} else {
input = event.currentTarget.querySelectorAll('input, textarea, select')[0];
}
if (input) {
focus(event, input);
}
}
};
var fakeClickEvent = function (event) {
if (typeof event.persist === 'function') {
event.persist();
}
event.fastclick = true;
event.type = 'click';
event.button = 0;
};
var copyTouchKeys = function (touch, target) {
if (typeof target.persist === 'function') {
target.persist();
}
if (touch) {
for (var i = 0; i < touchKeysToStore.length; i += 1) {
var key = touchKeysToStore[i];
target[key] = touch[key];
}
}
};
var noTouchHappened = function () {
return !touchEvents.touched && (
!touchEvents.lastTouchDate || new Date().getTime() > touchEvents.lastTouchDate + TOUCH_DELAY
);
};
var invalidateIfMoreThanOneTouch = function (event) {
touchEvents.invalid = event.touches && event.touches.length > 1 || touchEvents.invalid;
};
var onMouseEvent = function (callback, event) {
// Prevent any mouse events if we touched recently
if (typeof callback === 'function' && noTouchHappened()) {
callback(event);
}
if (event.type === 'click') {
touchEvents.invalid = false;
touchEvents.touched = false;
touchEvents.moved = false;
}
};
var onTouchStart = function (callback, event) {
touchEvents.invalid = false;
touchEvents.moved = false;
touchEvents.touched = true;
touchEvents.lastTouchDate = new Date().getTime();
copyTouchKeys(event.touches[0], touchEvents.downPos);
copyTouchKeys(event.touches[0], touchEvents.lastPos);
invalidateIfMoreThanOneTouch(event);
if (typeof callback === 'function') {
callback(event);
}
};
var onTouchMove = function (callback, event) {
touchEvents.touched = true;
touchEvents.lastTouchDate = new Date().getTime();
copyTouchKeys(event.touches[0], touchEvents.lastPos);
invalidateIfMoreThanOneTouch(event);
if (Math.abs(touchEvents.downPos.clientX - touchEvents.lastPos.clientX) > MOVE_THRESHOLD ||
Math.abs(touchEvents.downPos.clientY - touchEvents.lastPos.clientY) > MOVE_THRESHOLD) {
touchEvents.moved = true;
}
if (typeof callback === 'function') {
callback(event);
}
};
var onTouchEnd = function (callback, onClick, type, event) {
touchEvents.touched = true;
touchEvents.lastTouchDate = new Date().getTime();
invalidateIfMoreThanOneTouch(event);
if (typeof callback === 'function') {
callback(event);
}
if (!touchEvents.invalid && !touchEvents.moved) {
var box = event.currentTarget.getBoundingClientRect();
if (touchEvents.lastPos.clientX - (touchEvents.lastPos.radiusX || 0) <= box.right &&
touchEvents.lastPos.clientX + (touchEvents.lastPos.radiusX || 0) >= box.left &&
touchEvents.lastPos.clientY - (touchEvents.lastPos.radiusY || 0) <= box.bottom &&
touchEvents.lastPos.clientY + (touchEvents.lastPos.radiusY || 0) >= box.top) {
if (!isDisabled(event.currentTarget)) {
if (typeof onClick === 'function') {
copyTouchKeys(touchEvents.lastPos, event);
fakeClickEvent(event);
onClick(event);
}
if (!event.defaultPrevented && handleType[type]) {
handleType[type](event);
}
}
}
}
};
var propsWithFastclickEvents = function (type, props) {
var newProps = {};
// Loop over props
for (var key in props) {
// Copy props to newProps
newProps[key] = props[key];
}
// Apply our wrapped mouse and touch handlers
newProps.onClick = onMouseEvent.bind(null, props.onClick);
newProps.onMouseDown = onMouseEvent.bind(null, props.onMouseDown);
newProps.onMouseMove = onMouseEvent.bind(null, props.onMouseMove);
newProps.onMouseUp = onMouseEvent.bind(null, props.onMouseUp);
newProps.onTouchStart = onTouchStart.bind(null, props.onTouchStart);
newProps.onTouchMove = onTouchMove.bind(null, props.onTouchMove);
newProps.onTouchEnd = onTouchEnd.bind(null, props.onTouchEnd, props.onClick, type);
if (typeof Object.freeze === 'function') {
Object.freeze(newProps);
}
return newProps;
};
React.createElement = function () {
// Convert arguments to array
var args = Array.prototype.slice.call(arguments);
var type = args[0];
var props = args[1];
// Check if basic element & has onClick prop
if (type && typeof type === 'string' && (
(props && typeof props.onClick === 'function') || handleType[type]
)) {
// Add our own events to props
args[1] = propsWithFastclickEvents(type, props || {});
}
// Apply args to original createElement function
return originalCreateElement.apply(null, args);
};
if (typeof React.DOM === 'object') {
for (var key in React.DOM) {
React.DOM[key] = React.createElement.bind(null, key);
}
}
})();
'use strict';
var expect = require('chai').expect;
var sinon = require('sinon');
var spy = sinon.spy;
var stub = sinon.stub;
var TestUtils = require('react-addons-test-utils');
var renderIntoApp = require('./helpers/render-into-app');
describe('react-fastclick', function () {
var originalCreateElement, fastclickCreateElement;
function handlerKeyToSimulatedEventKey (key) {
var simulatedEventKey = key.replace(/^on/, '');
return simulatedEventKey.charAt(0).toLowerCase() + simulatedEventKey.substring(1);
}
function getBoundingClientRect () {
return {
top: 25,
left: 25,
right: 75,
bottom: 75,
width: 50,
height: 50
};
}
var touches = [
{
clientX: 50,
clientY: 50
}
];
var specialTypes = [
'input',
'textarea',
'select',
'label'
];
var additionalProps = {
onClick: function () {},
onMouseDown: function () {},
onMouseMove: function () {},
onMouseUp: function () {},
onTouchStart: function () {},
onTouchMove: function () {},
onTouchEnd: function () {}
};
beforeEach(function () {
// Clear module cache
delete require.cache[require.resolve('react')];
delete require.cache[require.resolve('../lib/index')];
});
it('should redefine React.createElement', function () {
originalCreateElement = require('react').createElement;
var theSameCreateElement = require('react').createElement;
expect(originalCreateElement).to.equal(theSameCreateElement);
require('../lib/index');
fastclickCreateElement = require('react').createElement;
expect(originalCreateElement).not.to.equal(fastclickCreateElement);
});
describe('createElement', function () {
it('should create a regular React element', function () {
var element = fastclickCreateElement('div');
expect(element).to.exist;
expect(element.ref).to.be.null;
expect(element.key).to.be.null;
expect(element.type).to.equal('div');
expect(element.props).to.eql({});
});
it('should add events if it is a special element', function () {
var element;
for (var i = 0; i < specialTypes.length; i += 1) {
element = fastclickCreateElement(specialTypes[i]);
for (var key in additionalProps) {
expect(typeof element.props[key]).to.equal('function');
}
}
});
it('should add events if it has an onClick handler', function () {
var element = fastclickCreateElement('div', {onClick: function () {}});
for (var key in additionalProps) {
expect(typeof element.props[key]).to.equal('function');
}
});
});
describe('mouse events', function () {
it('should trigger standard mouse event handlers', function () {
var props = {
onMouseDown: spy(),
onMouseMove: spy(),
onMouseUp: spy(),
onClick: spy()
};
var node = renderIntoApp(fastclickCreateElement('div', props));
for (var key in props) {
var mouseEvent = handlerKeyToSimulatedEventKey(key);
TestUtils.Simulate[mouseEvent](node);
expect(props[key]).to.have.been.calledOnce;
}
});
});
describe('touch events', function () {
it('should trigger standard touch event handlers', function () {
var props = {
onClick: function () {},
onTouchStart: spy(),
onTouchMove: spy(),
onTouchEnd: spy()
};
var node = renderIntoApp(fastclickCreateElement('div', props));
for (var key in props) {
if (key !== 'onClick') {
var touchEvent = handlerKeyToSimulatedEventKey(key);
TestUtils.Simulate[touchEvent](node, {touches: [{}]});
expect(props[key]).to.have.been.calledOnce;
}
}
});
it('should trigger the click handler when a fastclick happens', function () {
var props = {
onClick: spy()
};
var node = renderIntoApp(fastclickCreateElement('div', props));
var getBoundingClientRectStub = stub(node, 'getBoundingClientRect', getBoundingClientRect);
TestUtils.Simulate.touchStart(
node,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchEnd(
node,
{
type: 'touchend',
touches: null
}
);
expect(props.onClick).to.have.been.calledOnce;
TestUtils.Simulate.click(
node,
{
type: 'click'
}
);
expect(props.onClick).to.have.been.calledOnce;
getBoundingClientRectStub.restore();
});
it('should not trigger the click handler if multiple touches', function () {
var props = {
onClick: spy()
};
var node = renderIntoApp(fastclickCreateElement('div', props));
var getBoundingClientRectStub = stub(node, 'getBoundingClientRect', getBoundingClientRect);
TestUtils.Simulate.touchStart(
node,
{
type: 'touchstart',
touches: [touches[0], touches[0]]
}
);
TestUtils.Simulate.touchEnd(
node,
{
type: 'touchend',
touches: null
}
);
expect(props.onClick).not.to.have.been.called;
TestUtils.Simulate.click(
node,
{
type: 'click'
}
);
expect(props.onClick).not.to.have.been.called;
getBoundingClientRectStub.restore();
});
it('should not trigger the click handler if touch moves', function () {
var props = {
onClick: spy()
};
var node = renderIntoApp(fastclickCreateElement('div', props));
var getBoundingClientRectStub = stub(node, 'getBoundingClientRect', getBoundingClientRect);
TestUtils.Simulate.touchStart(
node,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchMove(
node,
{
type: 'touchmove',
touches: [
{
clientX: 60,
clientY: 50
}
]
}
);
TestUtils.Simulate.touchEnd(
node,
{
type: 'touchend',
touches: null
}
);
expect(props.onClick).not.to.have.been.called;
TestUtils.Simulate.click(
node,
{
type: 'click'
}
);
expect(props.onClick).not.to.have.been.called;
getBoundingClientRectStub.restore();
});
it('should not trigger the click handler if touch is outside of the element', function () {
var props = {
onClick: spy()
};
var node = renderIntoApp(fastclickCreateElement('div', props));
var getBoundingClientRectStub = stub(node, 'getBoundingClientRect', getBoundingClientRect);
TestUtils.Simulate.touchStart(
node,
{
type: 'touchstart',
touches: [
{
clientX: 80,
clientY: 80
}
]
}
);
TestUtils.Simulate.touchEnd(
node,
{
type: 'touchend',
touches: null
}
);
expect(props.onClick).not.to.have.been.called;
TestUtils.Simulate.click(
node,
{
type: 'click'
}
);
expect(props.onClick).not.to.have.been.called;
getBoundingClientRectStub.restore();
});
});
describe('special elements', function () {
it('should focus inputs, selects, and textareas when a fastclick is triggered', function () {
var node, getBoundingClientRectStub, focusSpy;
for (var i = 0; i < specialTypes.length; i += 1) {
var type = specialTypes[i];
if (type !== 'label') {
node = renderIntoApp(fastclickCreateElement(type));
getBoundingClientRectStub = stub(node, 'getBoundingClientRect', getBoundingClientRect);
focusSpy = spy(node, 'focus');
TestUtils.Simulate.touchStart(
node,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchEnd(
node,
{
type: 'touchend',
touches: null
}
);
expect(focusSpy).to.have.been.calledOnce;
TestUtils.Simulate.click(
node,
{
type: 'click'
}
);
expect(focusSpy).to.have.been.calledOnce;
getBoundingClientRectStub.restore();
focusSpy.restore();
}
}
});
it('should not focus inputs, selects, and textareas if they are disabled', function () {
var node, getBoundingClientRectStub, focusSpy;
for (var i = 0; i < specialTypes.length; i += 1) {
var type = specialTypes[i];
if (type !== 'label') {
node = renderIntoApp(fastclickCreateElement(type, {disabled: true}));
getBoundingClientRectStub = stub(node, 'getBoundingClientRect', getBoundingClientRect);
focusSpy = spy(node, 'focus');
TestUtils.Simulate.touchStart(
node,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchEnd(
node,
{
type: 'touchend',
touches: null
}
);
expect(focusSpy).not.to.have.been.called;
TestUtils.Simulate.click(
node,
{
type: 'click'
}
);
expect(focusSpy).not.to.have.been.called;
getBoundingClientRectStub.restore();
focusSpy.restore();
}
}
});
it('should focus an input inside a label', function () {
var node = renderIntoApp(
fastclickCreateElement('label', null, fastclickCreateElement('input'))
);
var label = node;
var input = label.getElementsByTagName('input')[0];
var getBoundingClientRectStub = stub(label, 'getBoundingClientRect', getBoundingClientRect);
var focusSpy = spy(input, 'focus');
TestUtils.Simulate.touchStart(
label,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchEnd(
label,
{
type: 'touchend',
touches: null
}
);
expect(focusSpy).to.have.been.calledOnce;
TestUtils.Simulate.click(
label,
{
type: 'click'
}
);
expect(focusSpy).to.have.been.calledOnce;
getBoundingClientRectStub.restore();
focusSpy.restore();
});
it('should not focus a disabled input inside a label', function () {
var node = renderIntoApp(
fastclickCreateElement('label', null, fastclickCreateElement('input', {disabled: true}))
);
var label = node;
var input = label.getElementsByTagName('input')[0];
var getBoundingClientRectStub = stub(label, 'getBoundingClientRect', getBoundingClientRect);
var focusSpy = spy(input, 'focus');
TestUtils.Simulate.touchStart(
label,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchEnd(
label,
{
type: 'touchend',
touches: null
}
);
expect(focusSpy).not.to.have.been.calledOnce;
TestUtils.Simulate.click(
label,
{
type: 'click'
}
);
expect(focusSpy).not.to.have.been.calledOnce;
getBoundingClientRectStub.restore();
focusSpy.restore();
});
it('should focus an input for a label', function () {
var node = renderIntoApp(
fastclickCreateElement(
'div',
null,
fastclickCreateElement('label', {htmlFor: 'my-input'}),
fastclickCreateElement('input', {id: 'my-input'})
)
);
var label = node.getElementsByTagName('label')[0];
var input = node.getElementsByTagName('input')[0];
var getBoundingClientRectStub = stub(label, 'getBoundingClientRect', getBoundingClientRect);
var focusSpy = spy(input, 'focus');
TestUtils.Simulate.touchStart(
label,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchEnd(
label,
{
type: 'touchend',
touches: null
}
);
expect(focusSpy).to.have.been.calledOnce;
TestUtils.Simulate.click(
label,
{
type: 'click'
}
);
expect(focusSpy).to.have.been.calledOnce;
getBoundingClientRectStub.restore();
focusSpy.restore();
});
it('should not focus a disabled input for a label', function () {
var node = renderIntoApp(
fastclickCreateElement(
'div',
null,
fastclickCreateElement('label', {htmlFor: 'my-input'}),
fastclickCreateElement('input', {id: 'my-input', disabled: true})
)
);
var label = node.getElementsByTagName('label')[0];
var input = node.getElementsByTagName('input')[0];
var getBoundingClientRectStub = stub(label, 'getBoundingClientRect', getBoundingClientRect);
var focusSpy = spy(input, 'focus');
TestUtils.Simulate.touchStart(
label,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchEnd(
label,
{
type: 'touchend',
touches: null
}
);
expect(focusSpy).not.to.have.been.calledOnce;
TestUtils.Simulate.click(
label,
{
type: 'click'
}
);
expect(focusSpy).not.to.have.been.calledOnce;
getBoundingClientRectStub.restore();
focusSpy.restore();
});
it('should gracefully handle no input inside a label', function () {
var node = renderIntoApp(fastclickCreateElement('label'));
var getBoundingClientRectStub = stub(node, 'getBoundingClientRect', getBoundingClientRect);
TestUtils.Simulate.touchStart(
node,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchEnd(
node,
{
type: 'touchend',
touches: null
}
);
TestUtils.Simulate.click(
node,
{
type: 'click'
}
);
getBoundingClientRectStub.restore();
});
it('should gracefully handle no input for a label', function () {
var node = renderIntoApp(fastclickCreateElement('label', {htmlFor: 'my-input'}));
var getBoundingClientRectStub = stub(node, 'getBoundingClientRect', getBoundingClientRect);
TestUtils.Simulate.touchStart(
node,
{
type: 'touchstart',
touches: touches
}
);
TestUtils.Simulate.touchEnd(
node,
{
type: 'touchend',
touches: null
}
);
TestUtils.Simulate.click(
node,
{
type: 'click'
}
);
getBoundingClientRectStub.restore();
});
});
describe('events', function () {
it('should persist events if they are to be mutated', function () {
var props = {
onClick: spy()
};
var node = renderIntoApp(fastclickCreateElement('div', props));
var getBoundingClientRectStub = stub(node, 'getBoundingClientRect', getBoundingClientRect);
var persistSpy = spy();
TestUtils.Simulate.touchStart(
node,
{
type: 'touchstart',
touches: touches,
persist: persistSpy
}
);
expect(persistSpy).not.to.have.been.called;
TestUtils.Simulate.touchEnd(
node,
{
type: 'touchend',
touches: null,
persist: persistSpy
}
);
// Properties copied onto event from stored positions, and fake click event properties
expect(persistSpy).to.have.been.calledTwice;
persistSpy.reset();
TestUtils.Simulate.click(
node,
{
type: 'click',
persist: persistSpy
}
);
expect(persistSpy).not.to.have.been.called;
expect(props.onClick).to.have.been.calledOnce;
getBoundingClientRectStub.restore();
});
});
});

Sorry, the diff of this file is not supported yet