Showing preview only (286K chars total). Download the full file or copy to clipboard to get everything.
Repository: hammerjs/hammer.js
Branch: master
Commit: ff687ea0daa3
Files: 107
Total size: 262.0 KB
Directory structure:
gitextract_3wvcy3sg/
├── .bowerrc
├── .editorconfig
├── .gitignore
├── .jscsrc
├── .jshintrc
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── banner.ejs
├── bower.json
├── changelog.js
├── component.json
├── hammer.js
├── package.json
├── rollup.config.js
├── src/
│ ├── .babelrc
│ ├── hammer.js
│ ├── input/
│ │ ├── mouse.js
│ │ ├── pointerevent.js
│ │ ├── singletouch.js
│ │ ├── touch.js
│ │ └── touchmouse.js
│ ├── inputjs/
│ │ ├── compute-delta-xy.js
│ │ ├── compute-input-data.js
│ │ ├── compute-interval-input-data.js
│ │ ├── create-input-instance.js
│ │ ├── get-angle.js
│ │ ├── get-center.js
│ │ ├── get-direction.js
│ │ ├── get-distance.js
│ │ ├── get-rotation.js
│ │ ├── get-scale.js
│ │ ├── get-velocity.js
│ │ ├── input-constructor.js
│ │ ├── input-consts.js
│ │ ├── input-handler.js
│ │ └── simple-clone-input-data.js
│ ├── main.js
│ ├── manager.js
│ ├── recognizerjs/
│ │ ├── direction-str.js
│ │ ├── get-recognizer-by-name-if-manager.js
│ │ ├── recognizer-constructor.js
│ │ ├── recognizer-consts.js
│ │ └── state-str.js
│ ├── recognizers/
│ │ ├── attribute.js
│ │ ├── pan.js
│ │ ├── pinch.js
│ │ ├── press.js
│ │ ├── rotate.js
│ │ ├── swipe.js
│ │ └── tap.js
│ ├── touchactionjs/
│ │ ├── clean-touch-actions.js
│ │ ├── get-touchaction-props.js
│ │ ├── touchaction-Consts.js
│ │ └── touchaction-constructor.js
│ └── utils/
│ ├── add-event-listeners.js
│ ├── assign.js
│ ├── bind-fn.js
│ ├── bool-or-fn.js
│ ├── deprecate.js
│ ├── each.js
│ ├── extend.js
│ ├── get-window-for-element.js
│ ├── has-parent.js
│ ├── if-undefined.js
│ ├── in-array.js
│ ├── in-str.js
│ ├── inherit.js
│ ├── invoke-array-arg.js
│ ├── merge.js
│ ├── prefixed.js
│ ├── remove-event-listeners.js
│ ├── set-timeout-context.js
│ ├── split-str.js
│ ├── to-array.js
│ ├── unique-array.js
│ ├── unique-id.js
│ └── utils-consts.js
├── string-replace.js
└── tests/
├── manual/
│ ├── assets/
│ │ └── style.css
│ ├── compute_touch_action.html
│ ├── input.html
│ ├── log.html
│ ├── multiple.html
│ ├── nested.html
│ ├── simulator-googlemaps.html
│ ├── simulator.html
│ ├── touchaction.html
│ └── visual.html
└── unit/
├── assets/
│ └── utils.js
├── gestures/
│ ├── test_pan.js
│ ├── test_pinch.js
│ └── test_swipe.js
├── index.html
├── test_enable.js
├── test_events.js
├── test_gestures.js
├── test_hammer.js
├── test_jquery_plugin.js
├── test_multiple_taps.js
├── test_nested_gesture_recognizers.js
├── test_propagation_bubble.js
├── test_require_failure.js
├── test_simultaneous_recognition.js
└── test_utils.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .bowerrc
================================================
{
"json": "bower.json"
}
================================================
FILE: .editorconfig
================================================
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.js]
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = false
================================================
FILE: .gitignore
================================================
# ide
.idea
.iml
# node
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
tests/build.js
npm-debug.log
node_modules
================================================
FILE: .jscsrc
================================================
{
"preset": "ember-suave",
"excludeFiles": [
"tests/**/assets",
"node_modules/**"
],
"disallowConstOutsideModuleScope": false,
"requireArrowFunctions": true,
"disallowEmptyBlocks": false
}
================================================
FILE: .jshintrc
================================================
{
"maxerr": 100,
"freeze": true,
"node": false,
"indent": 2,
"predef": [
"document",
"window"
],
"browser": true,
"boss": true,
"curly": true,
"debug": false,
"devel": true,
"eqeqeq": true,
"expr": true,
"validthis": true,
"evil": true,
"forin": false,
"immed": false,
"laxbreak": false,
"newcap": true,
"noarg": true,
"node": true,
"noempty": false,
"nonew": false,
"nomen": false,
"onevar": false,
"plusplus": false,
"regexp": false,
"undef": true,
"sub": true,
"strict": false,
"white": false,
"eqnull": true,
"esversion": 6,
"unused": true,
"-W116": true,
"-W080": true,
"-W038": true,
"proto": true
}
================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
- "6.3.0"
sudo: false
script:
- npm run test
================================================
FILE: CHANGELOG.md
================================================
# Changelog
### 2.0.8, 2016-04-22
##### Manager
- Added check to ensure that the required parameters are present ([#908](https://github.com/hammerjs/hammer.js/issues/908), [085d3a8](https://github.com/hammer.js/hammerjs/commit/085d3a87eab8674c45e9d3a14c4ca44ad7b97e26))
- Fixed restoration of Hammer.defaults.cssProps on destory ([#904theregttr5ki](https://github.com/hammerjs/hammer.js/issues/904), [7d0e60f](https://github.com/hammer.js/hammerjs/commit/7d0e60f6743517db3c05a38e966fb9fb5052fa03))
##### Input
- Fixed de-duping of mouse events on mouse touch combo devices ([#917](https://github.com/hammer.js/hammer.js/issues/917), [#863](https://github.com/hammerjs/hammer.js/issues/863), [bfeb89a](https://github.com/hammerjs/hammerjs/commit/bfeb89a77f778c527f771150d1e9687bd318ce8d))
##### Touch-action
- Added support map for specific values of touch-action ([#952](https://github.com/hammer.js/hammer.js/issues/952), [fbe9fd7](https://github.com/hammerjs/hammer.js/commit/fbe9fd775fe8cb3d43faa9428bfa56b61b16edc7))
### 2.0.6, 2015-12-23
- Add Assign method and deprecate merge and extend ([#895](https://github.com/hammerjs/hammer.js/pull/895)[fc01eae](https://github.com/hammerjs/hammer.js/commit/fc01eaea678acc430c664eb374555fbe3d403bdd))
- Expose Hammer on window or self if either is defined to avoid issues when AMD is present but not used. ( [356f795](https://github.com/hammerjs/hammer.js/commit/356f7955b01f3679c29d6c45931679256b45036e))
- Add support for PointerEvent instead of MSPointerEvent if supported. ([#754](https://github.com/hammerjs/hammer.js/issues/754), [439c7a6](https://github.com/hammerjs/hammer.js/commit/439c7a6c46978ab387b4b8289399e904d1c49535))
- Fixed moz-prefix, prefix should be Moz not moz. ([3ea47f3](https://github.com/hammerjs/hammer.js/commit/3ea47f3aebadc9d3bb6bf52bc8402cad135ef8a9))
- Removed non-existant recognizer ([f1c2d3b](https://github.com/hammerjs/hammer.js/commit/f1c2d3bf05f530ae092ecfc2335fceeff0e9eec9))
- Fixed config leaking between instances([189098f](https://github.com/hammerjs/hammer.js/commit/189098ff7736f6ed2fce9a3d3e1f5a3afee085ba))
- Fixed gaps in gesture configs and update tests to match ([70c2902](https://github.com/hammerjs/hammer.js/commit/70c2902d773a750e92ce8c423f8a4165c07eab97))
- Fixed Manager off method ([#768](https://github.com/hammerjs/hammer.js/issues/768), [da49a27](https://github.com/hammerjs/hammer.js/commit/da49a2730779ecc3b4dd147cc418a0df7c70fad9))
- Added compatibility with requirejs optimizer namespaces ( [70075f2](https://github.com/hammerjs/hammer.js/commit/70075f2df1b855f7c6d8d3caac49b9276b88c8d6))
- Made touchaction test zoomable ( [50264a7](https://github.com/hammerjs/hammer.js/commit/50264a70251ca88bbaf7b666401e527eee616de5))
- Fixed preventing default when for `pan-x pan-y` case ( [95eaafa](https://github.com/hammerjs/hammer.js/commit/95eaafadad27bd1b25d20cf976811a451922f1c4))
- Fixed incorrect touch action pan direction ( [a81da57](https://github.com/hammerjs/hammer.js/commit/a81da57a82ebf37e695e7c443e4e2715e7f32856))
- Fixed combined pan-x pan-y to resolve to none ( [fdae07b](https://github.com/hammerjs/hammer.js/commit/fdae07bc2ba3c90aad28da6791b3d5df627bc612))
- Fixed inverted touch-action for pan recognizer ([#728](https://github.com/hammerjs/hammer.js/issues/728), [605bd3b](https://github.com/hammerjs/hammer.js/commit/605bd3beca780be91dd43f9da8b809d155a43d1a))
- Fixed dependency on non standard touch list ordering ([#610](https://github.com/hammerjs/hammer.js/issues/610), [#791](https://github.com/hammerjs/hammer.js/issues/791), [287720a](https://github.com/hammerjs/hammer.js/commit/287720a6e5067e7f28be8b8b3b266d22905361c4))
- Fixed swipe to not trigger after multitouch gesture ([#640](https://github.com/hammerjs/hammer.js/issues/640), [711d8a1](https://github.com/hammerjs/hammer.js/commit/711d8a1df1aa5057ecb536454a36257e3c0d6d91))
- Fixed swipe recognizer to use overall gesture direction and velocity ( [963fe69](https://github.com/hammerjs/hammer.js/commit/963fe697515273fee508414bc29e2656465cea55))
- Fixed getDirection returning reversed direction ( [e40dcde](https://github.com/hammerjs/hammer.js/commit/e40dcde43bdac7a74c8ce5c05a4f62121089cd91))
- Fixed detection of tap when multi touch gestures are present ( [c46cbba](https://github.com/hammerjs/hammer.js/commit/c46cbba1c2cbbf874b59913416858d9dae297e64))
- Fixed incorrect event order ([#824](https://github.com/hammerjs/hammer.js/issues/824), [92f2d76](https://github.com/hammerjs/hammer.js/commit/92f2d76188480d967e738a19cd508d0b94a31329))
- Fixed leaking options between recognizer instances ([#813](https://github.com/hammerjs/hammer.js/issues/813), [af32c9b](https://github.com/hammerjs/hammer.js/commit/af32c9bace3f04bb34bee852ff56a33cc8fc27cd))
- Fixed detection when element has no style attribute ( [5ca6d8c](https://github.com/hammerjs/hammer.js/commit/5ca6d8cbead02c71929a8073e95ddf98e11c0e06))
### 2.0.4, 2014-09-28
- Fix IE pointer issue. [#665](https://github.com/hammerjs/hammer.js/pull/665)
- Fix multi-touch at different elements. [#668](https://github.com/hammerjs/hammer.js/pull/668)
- Added experimental [single-user Touch input handler](src/input/singletouch.js). This to improve performance/ux when only a single user has to be supported. Plans are to release 2.1 with this as default, and a settings to enable the multi-user handler.
### 2.0.3, 2014-09-10
- Manager.set improvements.
- Fix requireFailure() call in Manager.options.recognizers.
- Make DIRECTION_ALL for pan and swipe gestures less blocking.
- Fix Swipe recognizer threshold option.
- Expose the Input classes.
- Added the option `inputClass` to set the used input handler.
### 2.0.2, 2014-07-26
- Improved mouse and pointer-events input, now able to move outside the window.
- Added the export name (`Hammer`) as an argument to the wrapper.
- Add the option *experimental* `inputTarget` to change the element that receives the events.
- Improved performance when only one touch being active.
- Fixed the jumping deltaXY bug when going from single to multi-touch.
- Improved velocity calculations.
### 2.0.1, 2014-07-15
- Fix issue when no document.body is available
- Added pressup event for the press recognizer
- Removed alternative for Object.create
### 2.0.0, 2014-07-11
- Full rewrite of the library.
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Hammer.js
Looking to contribute something to Hammer.js? **Here's how you can help.**
## Reporting issues
We only accept issues that are bug reports or feature requests. Bugs must be
isolated and reproducible problems that can be fixed within the Hammer.js.
Please read the following guidelines before opening any issue.
1. [**Read the documentation**](https://hammerjs.github.io)
2. **Search for existing issues.** We get a lot of duplicate issues, and you'd
help us out a lot by first checking if someone else has reported the same issue.
Moreover, the issue may have already been resolved with a fix available. Also
take a look if your problem is explained at the Wiki.
3. **Create an isolated and reproducible test case.** Be sure the problem exists
in Hammer's code with a reduced test case that should be included in each bug
report.
4. **Include a live example.** Make use of jsFiddle or jsBin to share your
isolated test cases. Also, a screen capture would work, with tools like LICEcap.
5. **Share as much information as possible.** Include operating system and
version, browser and version, version of Hammer.js, customized or vanilla build,
etc. where appropriate. Also include steps to reproduce the bug.
## Pull requests
1. Changes must be done in `/src` files, never just the compiled files. Also, don't
commit the compiled files.
2. Try not to pollute your pull request with unintended changes. Keep them simple
and small
3. Try to share which browsers your code has been tested in before submitting a
pull request
4. Write tests for your code, these can be found in `/tests`.
================================================
FILE: LICENSE.md
================================================
The MIT License (MIT)
Copyright (C) 2011-2017 by Jorik Tangelder (Eight Media)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: README.md
================================================
# [hammer.js][hammerjs-url] [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![Build Status][travis-image]][travis-url]
> A JavaScript library for detecting touch gestures.
## Installation
### NPM
```sh
npm install --save hammerjs
```
**or**
### Yarn
```sh
yarn add hammerjs
```
**or**
### CDN
[https://cdnjs.com/libraries/hammer.js/](https://cdnjs.com/libraries/hammer.js/)
## Usage
hammer.js has a quick start option for gestures it already recognizes.
```js
// Get a reference to an element.
var square = document.querySelector('.square');
// Create an instance of Hammer with the reference.
var hammer = new Hammer(square);
// Subscribe to a quick start event: press, tap, or doubletap.
// For a full list of quick start events, read the documentation.
hammer.on('press', function(e) {
e.target.classList.toggle('expand');
console.log("You're pressing me!");
console.log(e);
});
```
If you want to recognize your own gestures, such as `tripletap`, then you'll have to use these steps:
```js
// Get a reference to an element.
var square = document.querySelector('.square');
// Create a manager to manage the element.
var manager = new Hammer.Manager(square);
// Create a recognizer.
var TripleTap = new Hammer.Tap({
event: 'tripletap',
taps: 3
});
// Add the recognizer to the manager.
manager.add(TripleTap);
// Subscribe to the event.
manager.on('tripletap', function(e) {
e.target.classList.toggle('expand');
console.log("You're triple tapping me!");
console.log(e);
});
```
## Examples
- [tap][tap]
- [double tap][double-tap]
- [press][press]
- [swipe][swipe]
## Documentation
For further information regarding hammer.js, please read our [documentation][hammerjs-url].
## Contributions [![Github Issues][issues-image]][issues-url] [![Github PRs][pulls-image]][pulls-url] [![Slack][slack-image]][slack-url]
Feel encouraged to report issues or submit pull requests. When you're ready to do either, read our [contribution guidelines][contribution-guidelines]. If you're looking for another form of contribution, we love help answering questions on our [slack channel][slack-url].
## License
[MIT][license]
[hammerjs-url]: http://hammerjs.github.io
[npm-image]: https://img.shields.io/npm/v/hammerjs.svg
[npm-url]: https://npmjs.org/package/hammerjs
[travis-image]: https://img.shields.io/travis/stream-utils/raw-body/master.svg
[travis-url]: https://travis-ci.org/hammerjs/hammer.js
[downloads-image]: https://img.shields.io/npm/dm/hammerjs.svg
[downloads-url]: https://npmjs.org/package/hammerjs
<!-- Examples -->
[tap]: https://codepen.io/choskim/pen/WZggmg
[double-tap]: https://codepen.io/choskim/pen/vezzwZ
[press]: https://codepen.io/choskim/pen/RLYebL
[pan]: ''
[swipe]: https://codepen.io/choskim/pen/rGZqxa
[pinch]: ''
[rotate]: ''
<!-- Contributions -->
[issues-image]: https://img.shields.io/github/issues/hammerjs/hammer.js.svg
[issues-url]: https://github.com/hammerjs/hammer.js/issues
[pulls-image]: https://img.shields.io/github/issues-pr/hammerjs/hammer.js.svg
[pulls-url]: https://github.com/hammerjs/hammer.js/pulls
[slack-image]: https://hammerjs.herokuapp.com/badge.svg
[slack-url]: https://hammerjs.herokuapp.com/
[contribution-guidelines]: ./CONTRIBUTING.md
[license]: ./LICENSE.md
================================================
FILE: banner.ejs
================================================
/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - <%= [ date.getFullYear(), ('0' + (date.getMonth() + 1)).slice(-2), ('0' + date.getDate()).slice(-2)].join('-') %>
* <%= pkg.homepage %>
*
* Copyright (c) <%= %> <%= pkg.author.name %>;
* Licensed under the <%= pkg.license %> license */
================================================
FILE: bower.json
================================================
{
"name": "hammerjs",
"license": "MIT",
"main": "hammer.js",
"ignore": [
"tests",
"src",
".bowerrc",
".gitignore",
".jscsrc",
".jshintrc",
".travis.yml",
"component.json",
"package.json"
]
}
================================================
FILE: changelog.js
================================================
var changelog = require( "changelogplease" );
var gittags = require( "git-tags" ).get( function( error, tags ) {
if ( error ) {
throw error
}
console.log( tags[ 2 ] + ".." + tags[ 0 ] );
var exclude = [ "Merge", "Whitespace", "Fixup", "Cleanup", "Formatting", "Ignore" ];
changelog( {
ticketUrl: "https://github.com/hammerjs/hammer.js/issues/{id}",
commitUrl: "https://github.com/hammerjs/hammerjs/commit/{id}",
sort: false,
repo: "./",
committish: tags[ 2 ] + ".." + tags[ 0 ]
}, function( error, log ) {
if ( error ) {
throw error;
}
log = parseLog( log );
console.log( log );
} );
function parseLog( log ) {
var lines = log.split( "\n" );
var newLog = [];
var log = [];
var currentComponent;
lines.shift();
lines.forEach( function( line ) {
var newLine = parseLine( line );
if ( newLine ) {
log.push( line );
}
} );
var log = log.join( "\n" );
return log.replace( /\*/g, "-" ).replace( /__TICKETREF__,/g, "" );
}
function parseLine( line ) {
var parts = getParts( line );
if ( exclude.indexOf( parts.component ) > -1 ) {
return false;
}
return parts;
}
function getParts( line ) {
var parts = line.split( ":" );
var component = "";
var message;
var commits = line.match( /\{\{([A-Za-z0-9 ]){0,99}\}\}/ )
if ( parts.length > 1 && parts[ 0 ].length <= 20 ) {
component = parts[ 0 ];
parts.shift();
message = parts.join( ":" );
} else {
parts = line.split( " " );
component = parts[ 1 ];
parts.shift();
message = parts.join( " " );
}
if ( component ) {
component = component.replace( /\* |,/, "" );
}
return {
component: component,
message: message
};
}
} );
================================================
FILE: component.json
================================================
{
"name": "hammerjs",
"version": "2.0.6",
"main": "hammer.js",
"scripts": [
"hammer.js"
]
}
================================================
FILE: hammer.js
================================================
/*! Hammer.JS - v2.0.8 - 2016-09-30
* http://hammerjs.github.io/
*
* Copyright (c) Jorik Tangelder;
* Licensed under the MIT license */
(function(window, document, exportName, undefined) {
'use strict';
/**
* @private
* use the val2 when val1 is undefined
* @param {*} val1
* @param {*} val2
* @returns {*}
*/
function ifUndefined(val1, val2) {
return val1 === undefined ? val2 : val1;
}
var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];
var TEST_ELEMENT = document.createElement('div');
var TYPE_FUNCTION = 'function';
var round = Math.round;
var abs = Math.abs;
var now = Date.now;
/**
* @private
* get the prefixed property
* @param {Object} obj
* @param {String} property
* @returns {String|Undefined} prefixed
*/
function prefixed(obj, property) {
var prefix = void 0;
var prop = void 0;
var camelProp = property[0].toUpperCase() + property.slice(1);
var i = 0;
while (i < VENDOR_PREFIXES.length) {
prefix = VENDOR_PREFIXES[i];
prop = prefix ? prefix + camelProp : property;
if (prop in obj) {
return prop;
}
i++;
}
return undefined;
}
function getTouchActionProps() {
if (!NATIVE_TOUCH_ACTION) {
return false;
}
var touchMap = {};
var cssSupports = window.CSS && window.CSS.supports;
['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function (val) {
// If css.supports is not supported but there is native touch-action assume it supports
// all values. This is the case for IE 10 and 11.
return touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true;
});
return touchMap;
}
var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;
// magical touchAction value
var TOUCH_ACTION_COMPUTE = 'compute';
var TOUCH_ACTION_AUTO = 'auto';
var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented
var TOUCH_ACTION_NONE = 'none';
var TOUCH_ACTION_PAN_X = 'pan-x';
var TOUCH_ACTION_PAN_Y = 'pan-y';
var TOUCH_ACTION_MAP = getTouchActionProps();
var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
var SUPPORT_TOUCH = 'ontouchstart' in window;
var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;
var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
var INPUT_TYPE_TOUCH = 'touch';
var INPUT_TYPE_PEN = 'pen';
var INPUT_TYPE_MOUSE = 'mouse';
var INPUT_TYPE_KINECT = 'kinect';
var COMPUTE_INTERVAL = 25;
var INPUT_START = 1;
var INPUT_MOVE = 2;
var INPUT_END = 4;
var INPUT_CANCEL = 8;
var DIRECTION_NONE = 1;
var DIRECTION_LEFT = 2;
var DIRECTION_RIGHT = 4;
var DIRECTION_UP = 8;
var DIRECTION_DOWN = 16;
var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
var PROPS_XY = ['x', 'y'];
var PROPS_CLIENT_XY = ['clientX', 'clientY'];
var STATE_POSSIBLE = 1;
var STATE_BEGAN = 2;
var STATE_CHANGED = 4;
var STATE_ENDED = 8;
var STATE_RECOGNIZED = STATE_ENDED;
var STATE_CANCELLED = 16;
var STATE_FAILED = 32;
/**
* @private
* extend object.
* means that properties in dest will be overwritten by the ones in src.
* @param {Object} target
* @param {...Object} objects_to_assign
* @returns {Object} target
*/
var assign = void 0;
if (typeof Object.assign !== 'function') {
assign = function assign(target) {
if (target === undefined || target === null) {
throw new TypeError('Cannot convert undefined or null to object');
}
var output = Object(target);
for (var index = 1; index < arguments.length; index++) {
var source = arguments[index];
if (source !== undefined && source !== null) {
for (var nextKey in source) {
if (source.hasOwnProperty(nextKey)) {
output[nextKey] = source[nextKey];
}
}
}
}
return output;
};
} else {
assign = Object.assign;
}
var assign$1 = assign;
/**
* @private
* get a unique id
* @returns {number} uniqueId
*/
var _uniqueId = 1;
function uniqueId() {
return _uniqueId++;
}
/**
* @private
* walk objects and arrays
* @param {Object} obj
* @param {Function} iterator
* @param {Object} context
*/
function each(obj, iterator, context) {
var i = void 0;
if (!obj) {
return;
}
if (obj.forEach) {
obj.forEach(iterator, context);
} else if (obj.length !== undefined) {
i = 0;
while (i < obj.length) {
iterator.call(context, obj[i], i, obj);
i++;
}
} else {
for (i in obj) {
obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
}
}
}
/**
* @private
* if the argument is an array, we want to execute the fn on each entry
* if it aint an array we don't want to do a thing.
* this is used by all the methods that accept a single and array argument.
* @param {*|Array} arg
* @param {String} fn
* @param {Object} [context]
* @returns {Boolean}
*/
function invokeArrayArg(arg, fn, context) {
if (Array.isArray(arg)) {
each(arg, context[fn], context);
return true;
}
return false;
}
/**
* @private
* find if a array contains the object using indexOf or a simple polyFill
* @param {Array} src
* @param {String} find
* @param {String} [findByKey]
* @return {number} -1 when not found, or the index
*/
function inArray(src, find, findByKey) {
if (src.indexOf && !findByKey) {
return src.indexOf(find);
} else {
var i = 0;
while (i < src.length) {
if (findByKey && src[i][findByKey] == find || !findByKey && src[i] === find) {
// do not use === here, test fails
return i;
}
i++;
}
return -1;
}
}
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
var asyncGenerator = function () {
function AwaitValue(value) {
this.value = value;
}
function AsyncGenerator(gen) {
var front, back;
function send(key, arg) {
return new Promise(function (resolve, reject) {
var request = {
key: key,
arg: arg,
resolve: resolve,
reject: reject,
next: null
};
if (back) {
back = back.next = request;
} else {
front = back = request;
resume(key, arg);
}
});
}
function resume(key, arg) {
try {
var result = gen[key](arg);
var value = result.value;
if (value instanceof AwaitValue) {
Promise.resolve(value.value).then(function (arg) {
resume("next", arg);
}, function (arg) {
resume("throw", arg);
});
} else {
settle(result.done ? "return" : "normal", result.value);
}
} catch (err) {
settle("throw", err);
}
}
function settle(type, value) {
switch (type) {
case "return":
front.resolve({
value: value,
done: true
});
break;
case "throw":
front.reject(value);
break;
default:
front.resolve({
value: value,
done: false
});
break;
}
front = front.next;
if (front) {
resume(front.key, front.arg);
} else {
back = null;
}
}
this._invoke = send;
if (typeof gen.return !== "function") {
this.return = undefined;
}
}
if (typeof Symbol === "function" && Symbol.asyncIterator) {
AsyncGenerator.prototype[Symbol.asyncIterator] = function () {
return this;
};
}
AsyncGenerator.prototype.next = function (arg) {
return this._invoke("next", arg);
};
AsyncGenerator.prototype.throw = function (arg) {
return this._invoke("throw", arg);
};
AsyncGenerator.prototype.return = function (arg) {
return this._invoke("return", arg);
};
return {
wrap: function (fn) {
return function () {
return new AsyncGenerator(fn.apply(this, arguments));
};
},
await: function (value) {
return new AwaitValue(value);
}
};
}();
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
var get = function get(object, property, receiver) {
if (object === null) object = Function.prototype;
var desc = Object.getOwnPropertyDescriptor(object, property);
if (desc === undefined) {
var parent = Object.getPrototypeOf(object);
if (parent === null) {
return undefined;
} else {
return get(parent, property, receiver);
}
} else if ("value" in desc) {
return desc.value;
} else {
var getter = desc.get;
if (getter === undefined) {
return undefined;
}
return getter.call(receiver);
}
};
var inherits = function (subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
};
var possibleConstructorReturn = function (self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
};
var slicedToArray = function () {
function sliceIterator(arr, i) {
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"]) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
return function (arr, i) {
if (Array.isArray(arr)) {
return arr;
} else if (Symbol.iterator in Object(arr)) {
return sliceIterator(arr, i);
} else {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
}
};
}();
/**
* @private
* let a boolean value also be a function that must return a boolean
* this first item in args will be used as the context
* @param {Boolean|Function} val
* @param {Array} [args]
* @returns {Boolean}
*/
function boolOrFn(val, args) {
if ((typeof val === 'undefined' ? 'undefined' : _typeof(val)) === TYPE_FUNCTION) {
return val.apply(args ? args[0] || undefined : undefined, args);
}
return val;
}
/**
* @private
* get a recognizer by name if it is bound to a manager
* @param {Recognizer|String} otherRecognizer
* @param {Recognizer} recognizer
* @returns {Recognizer}
*/
function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
var manager = recognizer.manager;
if (manager) {
return manager.get(otherRecognizer);
}
return otherRecognizer;
}
/**
* @private
* get a usable string, used as event postfix
* @param {constant} state
* @returns {String} state
*/
function stateStr(state) {
if (state & STATE_CANCELLED) {
return 'cancel';
} else if (state & STATE_ENDED) {
return 'end';
} else if (state & STATE_CHANGED) {
return 'move';
} else if (state & STATE_BEGAN) {
return 'start';
}
return '';
}
/**
* @private
* Recognizer flow explained; *
* All recognizers have the initial state of POSSIBLE when a input session starts.
* The definition of a input session is from the first input until the last input, with all it's movement in it. *
* Example session for mouse-input: mousedown -> mousemove -> mouseup
*
* On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
* which determines with state it should be.
*
* If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
* POSSIBLE to give it another change on the next cycle.
*
* Possible
* |
* +-----+---------------+
* | |
* +-----+-----+ |
* | | |
* Failed Cancelled |
* +-------+------+
* | |
* Recognized Began
* |
* Changed
* |
* Ended/Recognized
*/
/**
* @private
* Recognizer
* Every recognizer needs to extend from this class.
* @constructor
* @param {Object} options
*/
var Recognizer = function () {
function Recognizer(options) {
classCallCheck(this, Recognizer);
this.options = assign$1({}, this.defaults, options || {});
this.id = uniqueId();
this.manager = null;
// default is enable true
this.options.enable = ifUndefined(this.options.enable, true);
this.state = STATE_POSSIBLE;
this.simultaneous = {};
this.requireFail = [];
}
/**
* @private
* set options
* @param {Object} options
* @return {Recognizer}
*/
createClass(Recognizer, [{
key: 'set',
value: function set(options) {
assign$1(this.options, options);
// also update the touchAction, in case something changed about the directions/enabled state
this.manager && this.manager.touchAction.update();
return this;
}
/**
* @private
* recognize simultaneous with an other recognizer.
* @param {Recognizer} otherRecognizer
* @returns {Recognizer} this
*/
}, {
key: 'recognizeWith',
value: function recognizeWith(otherRecognizer) {
if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
return this;
}
var simultaneous = this.simultaneous;
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
if (!simultaneous[otherRecognizer.id]) {
simultaneous[otherRecognizer.id] = otherRecognizer;
otherRecognizer.recognizeWith(this);
}
return this;
}
/**
* @private
* drop the simultaneous link. it doesnt remove the link on the other recognizer.
* @param {Recognizer} otherRecognizer
* @returns {Recognizer} this
*/
}, {
key: 'dropRecognizeWith',
value: function dropRecognizeWith(otherRecognizer) {
if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
return this;
}
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
delete this.simultaneous[otherRecognizer.id];
return this;
}
/**
* @private
* recognizer can only run when an other is failing
* @param {Recognizer} otherRecognizer
* @returns {Recognizer} this
*/
}, {
key: 'requireFailure',
value: function requireFailure(otherRecognizer) {
if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
return this;
}
var requireFail = this.requireFail;
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
if (inArray(requireFail, otherRecognizer) === -1) {
requireFail.push(otherRecognizer);
otherRecognizer.requireFailure(this);
}
return this;
}
/**
* @private
* drop the requireFailure link. it does not remove the link on the other recognizer.
* @param {Recognizer} otherRecognizer
* @returns {Recognizer} this
*/
}, {
key: 'dropRequireFailure',
value: function dropRequireFailure(otherRecognizer) {
if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
return this;
}
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
var index = inArray(this.requireFail, otherRecognizer);
if (index > -1) {
this.requireFail.splice(index, 1);
}
return this;
}
/**
* @private
* has require failures boolean
* @returns {boolean}
*/
}, {
key: 'hasRequireFailures',
value: function hasRequireFailures() {
return this.requireFail.length > 0;
}
/**
* @private
* if the recognizer can recognize simultaneous with an other recognizer
* @param {Recognizer} otherRecognizer
* @returns {Boolean}
*/
}, {
key: 'canRecognizeWith',
value: function canRecognizeWith(otherRecognizer) {
return !!this.simultaneous[otherRecognizer.id];
}
/**
* @private
* You should use `tryEmit` instead of `emit` directly to check
* that all the needed recognizers has failed before emitting.
* @param {Object} input
*/
}, {
key: 'emit',
value: function emit(input) {
var self = this;
var state = this.state;
function emit(event) {
self.manager.emit(event, input);
}
// 'panstart' and 'panmove'
if (state < STATE_ENDED) {
emit(self.options.event + stateStr(state));
}
emit(self.options.event); // simple 'eventName' events
if (input.additionalEvent) {
// additional event(panleft, panright, pinchin, pinchout...)
emit(input.additionalEvent);
}
// panend and pancancel
if (state >= STATE_ENDED) {
emit(self.options.event + stateStr(state));
}
}
/**
* @private
* Check that all the require failure recognizers has failed,
* if true, it emits a gesture event,
* otherwise, setup the state to FAILED.
* @param {Object} input
*/
}, {
key: 'tryEmit',
value: function tryEmit(input) {
if (this.canEmit()) {
return this.emit(input);
}
// it's failing anyway
this.state = STATE_FAILED;
}
/**
* @private
* can we emit?
* @returns {boolean}
*/
}, {
key: 'canEmit',
value: function canEmit() {
var i = 0;
while (i < this.requireFail.length) {
if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
return false;
}
i++;
}
return true;
}
/**
* @private
* update the recognizer
* @param {Object} inputData
*/
}, {
key: 'recognize',
value: function recognize(inputData) {
// make a new copy of the inputData
// so we can change the inputData without messing up the other recognizers
var inputDataClone = assign$1({}, inputData);
// is is enabled and allow recognizing?
if (!boolOrFn(this.options.enable, [this, inputDataClone])) {
this.reset();
this.state = STATE_FAILED;
return;
}
// reset when we've reached the end
if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
this.state = STATE_POSSIBLE;
}
this.state = this.process(inputDataClone);
// the recognizer has recognized a gesture
// so trigger an event
if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
this.tryEmit(inputDataClone);
}
}
/**
* @private
* return the state of the recognizer
* the actual recognizing happens in this method
* @virtual
* @param {Object} inputData
* @returns {constant} STATE
*/
/* jshint ignore:start */
}, {
key: 'process',
value: function process(inputData) {}
/* jshint ignore:end */
/**
* @private
* return the preferred touch-action
* @virtual
* @returns {Array}
*/
}, {
key: 'getTouchAction',
value: function getTouchAction() {}
/**
* @private
* called when the gesture isn't allowed to recognize
* like when another is being recognized or it is disabled
* @virtual
*/
}, {
key: 'reset',
value: function reset() {}
}]);
return Recognizer;
}();
Recognizer.prototype.defaults = {};
/**
* @private
* This recognizer is just used as a base for the simple attribute recognizers.
* @constructor
* @extends Recognizer
*/
var AttrRecognizer = function (_Recognizer) {
inherits(AttrRecognizer, _Recognizer);
function AttrRecognizer() {
classCallCheck(this, AttrRecognizer);
return possibleConstructorReturn(this, (AttrRecognizer.__proto__ || Object.getPrototypeOf(AttrRecognizer)).apply(this, arguments));
}
/**
* @private
* Used to check if it the recognizer receives valid input, like input.distance > 10.
* @memberof AttrRecognizer
* @param {Object} input
* @returns {Boolean} recognized
*/
createClass(AttrRecognizer, [{
key: 'attrTest',
value: function attrTest(input) {
var optionPointers = this.options.pointers;
return optionPointers === 0 || input.pointers.length === optionPointers;
}
/**
* @private
* Process the input and return the state for the recognizer
* @memberof AttrRecognizer
* @param {Object} input
* @returns {*} State
*/
}, {
key: 'process',
value: function process(input) {
var state = this.state;
var eventType = input.eventType;
var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
var isValid = this.attrTest(input);
// on cancel input and we've recognized before, return STATE_CANCELLED
if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
return state | STATE_CANCELLED;
} else if (isRecognized || isValid) {
if (eventType & INPUT_END) {
return state | STATE_ENDED;
} else if (!(state & STATE_BEGAN)) {
return STATE_BEGAN;
}
return state | STATE_CHANGED;
}
return STATE_FAILED;
}
}]);
return AttrRecognizer;
}(Recognizer);
AttrRecognizer.prototype.defaults = {
/**
* @private
* @type {Number}
* @default 1
*/
pointers: 1
};
/**
* @private
* Rotate
* Recognized when two or more pointer are moving in a circular motion.
* @constructor
* @extends AttrRecognizer
*/
var RotateRecognizer = function (_AttrRecognizer) {
inherits(RotateRecognizer, _AttrRecognizer);
function RotateRecognizer() {
classCallCheck(this, RotateRecognizer);
return possibleConstructorReturn(this, (RotateRecognizer.__proto__ || Object.getPrototypeOf(RotateRecognizer)).apply(this, arguments));
}
createClass(RotateRecognizer, [{
key: 'getTouchAction',
value: function getTouchAction() {
return [TOUCH_ACTION_NONE];
}
}, {
key: 'attrTest',
value: function attrTest(input) {
return get(RotateRecognizer.prototype.__proto__ || Object.getPrototypeOf(RotateRecognizer.prototype), 'attrTest', this).call(this, input) && (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
}
}]);
return RotateRecognizer;
}(AttrRecognizer);
RotateRecognizer.prototype.defaults = {
event: 'rotate',
threshold: 0,
pointers: 2
};
/**
* @private
* Pinch
* Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
* @constructor
* @extends AttrRecognizer
*/
var PinchRecognizer = function (_AttrRecognizer) {
inherits(PinchRecognizer, _AttrRecognizer);
function PinchRecognizer() {
classCallCheck(this, PinchRecognizer);
return possibleConstructorReturn(this, (PinchRecognizer.__proto__ || Object.getPrototypeOf(PinchRecognizer)).apply(this, arguments));
}
createClass(PinchRecognizer, [{
key: 'getTouchAction',
value: function getTouchAction() {
return [TOUCH_ACTION_NONE];
}
}, {
key: 'attrTest',
value: function attrTest(input) {
return get(PinchRecognizer.prototype.__proto__ || Object.getPrototypeOf(PinchRecognizer.prototype), 'attrTest', this).call(this, input) && (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
}
}, {
key: 'emit',
value: function emit(input) {
if (input.scale !== 1) {
var inOut = input.scale < 1 ? 'in' : 'out';
input.additionalEvent = this.options.event + inOut;
}
get(PinchRecognizer.prototype.__proto__ || Object.getPrototypeOf(PinchRecognizer.prototype), 'emit', this).call(this, input);
}
}]);
return PinchRecognizer;
}(AttrRecognizer);
PinchRecognizer.prototype.defaults = {
event: 'pinch',
threshold: 0,
pointers: 2
};
/**
* @private
* direction cons to string
* @param {constant} direction
* @returns {String}
*/
function directionStr(direction) {
if (direction === DIRECTION_DOWN) {
return 'down';
} else if (direction === DIRECTION_UP) {
return 'up';
} else if (direction === DIRECTION_LEFT) {
return 'left';
} else if (direction === DIRECTION_RIGHT) {
return 'right';
}
return '';
}
/**
* @private
* Pan
* Recognized when the pointer is down and moved in the allowed direction.
* @constructor
* @extends AttrRecognizer
*/
var PanRecognizer = function (_AttrRecognizer) {
inherits(PanRecognizer, _AttrRecognizer);
function PanRecognizer() {
classCallCheck(this, PanRecognizer);
var _this = possibleConstructorReturn(this, (PanRecognizer.__proto__ || Object.getPrototypeOf(PanRecognizer)).apply(this, arguments));
_this.pX = null;
_this.pY = null;
return _this;
}
createClass(PanRecognizer, [{
key: 'getTouchAction',
value: function getTouchAction() {
var direction = this.options.direction;
var actions = [];
if (direction & DIRECTION_HORIZONTAL) {
actions.push(TOUCH_ACTION_PAN_Y);
}
if (direction & DIRECTION_VERTICAL) {
actions.push(TOUCH_ACTION_PAN_X);
}
return actions;
}
}, {
key: 'directionTest',
value: function directionTest(input) {
var options = this.options;
var hasMoved = true;
var distance = input.distance;
var direction = input.direction;
var x = input.deltaX;
var y = input.deltaY;
// lock to axis?
if (!(direction & options.direction)) {
if (options.direction & DIRECTION_HORIZONTAL) {
direction = x === 0 ? DIRECTION_NONE : x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
hasMoved = x !== this.pX;
distance = Math.abs(input.deltaX);
} else {
direction = y === 0 ? DIRECTION_NONE : y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
hasMoved = y !== this.pY;
distance = Math.abs(input.deltaY);
}
}
input.direction = direction;
return hasMoved && distance > options.threshold && direction & options.direction;
}
}, {
key: 'attrTest',
value: function attrTest(input) {
return AttrRecognizer.prototype.attrTest.call(this, input) && ( // replace with a super call
this.state & STATE_BEGAN || !(this.state & STATE_BEGAN) && this.directionTest(input));
}
}, {
key: 'emit',
value: function emit(input) {
this.pX = input.deltaX;
this.pY = input.deltaY;
var direction = directionStr(input.direction);
if (direction) {
input.additionalEvent = this.options.event + direction;
}
get(PanRecognizer.prototype.__proto__ || Object.getPrototypeOf(PanRecognizer.prototype), 'emit', this).call(this, input);
}
}]);
return PanRecognizer;
}(AttrRecognizer);
PanRecognizer.prototype.defaults = {
event: 'pan',
threshold: 10,
pointers: 1,
direction: DIRECTION_ALL
};
/**
* @private
* Swipe
* Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
* @constructor
* @extends AttrRecognizer
*/
var SwipeRecognizer = function (_AttrRecognizer) {
inherits(SwipeRecognizer, _AttrRecognizer);
function SwipeRecognizer() {
classCallCheck(this, SwipeRecognizer);
return possibleConstructorReturn(this, (SwipeRecognizer.__proto__ || Object.getPrototypeOf(SwipeRecognizer)).apply(this, arguments));
}
createClass(SwipeRecognizer, [{
key: 'getTouchAction',
value: function getTouchAction() {
return PanRecognizer.prototype.getTouchAction.call(this);
}
}, {
key: 'attrTest',
value: function attrTest(input) {
var direction = this.options.direction;
var velocity = void 0;
if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
velocity = input.overallVelocity;
} else if (direction & DIRECTION_HORIZONTAL) {
velocity = input.overallVelocityX;
} else if (direction & DIRECTION_VERTICAL) {
velocity = input.overallVelocityY;
}
return get(SwipeRecognizer.prototype.__proto__ || Object.getPrototypeOf(SwipeRecognizer.prototype), 'attrTest', this).call(this, input) && direction & input.offsetDirection && input.distance > this.options.threshold && input.maxPointers === this.options.pointers && abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
}
}, {
key: 'emit',
value: function emit(input) {
var direction = directionStr(input.offsetDirection);
if (direction) {
this.manager.emit(this.options.event + direction, input);
}
this.manager.emit(this.options.event, input);
}
}]);
return SwipeRecognizer;
}(AttrRecognizer);
SwipeRecognizer.prototype.defaults = {
event: 'swipe',
threshold: 10,
velocity: 0.3,
direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
pointers: 1
};
/**
* @private
* simple function bind
* @param {Function} fn
* @param {Object} context
* @returns {Function}
*/
function bindFn(fn, context) {
return function boundFn() {
return fn.apply(context, arguments);
};
}
/**
* @private
* set a timeout with a given scope
* @param {Function} fn
* @param {Number} timeout
* @param {Object} context
* @returns {number}
*/
function setTimeoutContext(fn, timeout, context) {
return setTimeout(bindFn(fn, context), timeout);
}
/**
* @private
* calculate the absolute distance between two points
* @param {Object} p1 {x, y}
* @param {Object} p2 {x, y}
* @param {Array} [props] containing x and y keys
* @return {Number} distance
*/
function getDistance(p1, p2, props) {
if (!props) {
props = PROPS_XY;
}
var x = p2[props[0]] - p1[props[0]];
var y = p2[props[1]] - p1[props[1]];
return Math.sqrt(x * x + y * y);
}
/**
* @private
* A tap is recognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
* between the given interval and position. The delay option can be used to recognize multi-taps without firing
* a single tap.
*
* The eventData from the emitted event contains the property `tapCount`, which contains the amount of
* multi-taps being recognized.
* @constructor
* @extends Recognizer
*/
var TapRecognizer = function (_Recognizer) {
inherits(TapRecognizer, _Recognizer);
function TapRecognizer() {
classCallCheck(this, TapRecognizer);
// previous time and center,
// used for tap counting
var _this = possibleConstructorReturn(this, (TapRecognizer.__proto__ || Object.getPrototypeOf(TapRecognizer)).apply(this, arguments));
_this.pTime = false;
_this.pCenter = false;
_this._timer = null;
_this._input = null;
_this.count = 0;
return _this;
}
createClass(TapRecognizer, [{
key: 'getTouchAction',
value: function getTouchAction() {
return [TOUCH_ACTION_MANIPULATION];
}
}, {
key: 'process',
value: function process(input) {
var _this2 = this;
var options = this.options;
var validPointers = input.pointers.length === options.pointers;
var validMovement = input.distance < options.threshold;
var validTouchTime = input.deltaTime < options.time;
this.reset();
if (input.eventType & INPUT_START && this.count === 0) {
return this.failTimeout();
}
// we only allow little movement
// and we've reached an end event, so a tap is possible
if (validMovement && validTouchTime && validPointers) {
if (input.eventType !== INPUT_END) {
return this.failTimeout();
}
var validInterval = this.pTime ? input.timeStamp - this.pTime < options.interval : true;
var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;
this.pTime = input.timeStamp;
this.pCenter = input.center;
if (!validMultiTap || !validInterval) {
this.count = 1;
} else {
this.count += 1;
}
this._input = input;
// if tap count matches we have recognized it,
// else it has began recognizing...
var tapCount = this.count % options.taps;
if (tapCount === 0) {
// no failing requirements, immediately trigger the tap event
// or wait as long as the multitap interval to trigger
if (!this.hasRequireFailures()) {
return STATE_RECOGNIZED;
} else {
this._timer = setTimeoutContext(function () {
_this2.state = STATE_RECOGNIZED;
_this2.tryEmit();
}, options.interval, this);
return STATE_BEGAN;
}
}
}
return STATE_FAILED;
}
}, {
key: 'failTimeout',
value: function failTimeout() {
var _this3 = this;
this._timer = setTimeoutContext(function () {
_this3.state = STATE_FAILED;
}, this.options.interval, this);
return STATE_FAILED;
}
}, {
key: 'reset',
value: function reset() {
clearTimeout(this._timer);
}
}, {
key: 'emit',
value: function emit() {
if (this.state === STATE_RECOGNIZED) {
this._input.tapCount = this.count;
this.manager.emit(this.options.event, this._input);
}
}
}]);
return TapRecognizer;
}(Recognizer);
TapRecognizer.prototype.defaults = {
event: 'tap',
pointers: 1,
taps: 1,
interval: 300, // max time between the multi-tap taps
time: 250, // max time of the pointer to be down (like finger on the screen)
threshold: 9, // a minimal movement is ok, but keep it low
posThreshold: 10 // a multi-tap can be a bit off the initial position
};
/**
* @private
* Press
* Recognized when the pointer is down for x ms without any movement.
* @constructor
* @extends Recognizer
*/
var PressRecognizer = function (_Recognizer) {
inherits(PressRecognizer, _Recognizer);
function PressRecognizer() {
classCallCheck(this, PressRecognizer);
var _this = possibleConstructorReturn(this, (PressRecognizer.__proto__ || Object.getPrototypeOf(PressRecognizer)).apply(this, arguments));
_this._timer = null;
_this._input = null;
return _this;
}
createClass(PressRecognizer, [{
key: 'getTouchAction',
value: function getTouchAction() {
return [TOUCH_ACTION_AUTO];
}
}, {
key: 'process',
value: function process(input) {
var _this2 = this;
var options = this.options;
var validPointers = input.pointers.length === options.pointers;
var validMovement = input.distance < options.threshold;
var validTime = input.deltaTime > options.time;
this._input = input;
// we only allow little movement
// and we've reached an end event, so a tap is possible
if (!validMovement || !validPointers || input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime) {
this.reset();
} else if (input.eventType & INPUT_START) {
this.reset();
this._timer = setTimeoutContext(function () {
_this2.state = STATE_RECOGNIZED;
_this2.tryEmit();
}, options.time, this);
} else if (input.eventType & INPUT_END) {
return STATE_RECOGNIZED;
}
return STATE_FAILED;
}
}, {
key: 'reset',
value: function reset() {
clearTimeout(this._timer);
}
}, {
key: 'emit',
value: function emit(input) {
if (this.state !== STATE_RECOGNIZED) {
return;
}
if (input && input.eventType & INPUT_END) {
this.manager.emit(this.options.event + 'up', input);
} else {
this._input.timeStamp = now();
this.manager.emit(this.options.event, this._input);
}
}
}]);
return PressRecognizer;
}(Recognizer);
PressRecognizer.prototype.defaults = {
event: 'press',
pointers: 1,
time: 251, // minimal time of the pointer to be pressed
threshold: 9 // a minimal movement is ok, but keep it low
};
/**
* @private
* small indexOf wrapper
* @param {String} str
* @param {String} find
* @returns {Boolean} found
*/
function inStr(str, find) {
return str.indexOf(find) > -1;
}
/**
* @private
* when the touchActions are collected they are not a valid value, so we need to clean things up. *
* @param {String} actions
* @returns {*}
*/
function cleanTouchActions(actions) {
// none
if (inStr(actions, TOUCH_ACTION_NONE)) {
return TOUCH_ACTION_NONE;
}
var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
// if both pan-x and pan-y are set (different recognizers
// for different directions, e.g. horizontal pan but vertical swipe?)
// we need none (as otherwise with pan-x pan-y combined none of these
// recognizers will work, since the browser would handle all panning
if (hasPanX && hasPanY) {
return TOUCH_ACTION_NONE;
}
// pan-x OR pan-y
if (hasPanX || hasPanY) {
return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
}
// manipulation
if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
return TOUCH_ACTION_MANIPULATION;
}
return TOUCH_ACTION_AUTO;
}
/**
* @private
* Touch Action
* sets the touchAction property or uses the js alternative
* @param {Manager} manager
* @param {String} value
* @constructor
*/
var TouchAction = function () {
function TouchAction(manager, value) {
classCallCheck(this, TouchAction);
this.manager = manager;
this.set(value);
}
/**
* @private
* set the touchAction value on the element or enable the polyfill
* @param {String} value
*/
createClass(TouchAction, [{
key: 'set',
value: function set(value) {
// find out the touch-action by the event handlers
if (value === TOUCH_ACTION_COMPUTE) {
value = this.compute();
}
if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) {
this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
}
this.actions = value.toLowerCase().trim();
}
/**
* @private
* just re-set the touchAction value
*/
}, {
key: 'update',
value: function update() {
this.set(this.manager.options.touchAction);
}
/**
* @private
* compute the value for the touchAction property based on the recognizer's settings
* @returns {String} value
*/
}, {
key: 'compute',
value: function compute() {
var actions = [];
each(this.manager.recognizers, function (recognizer) {
if (boolOrFn(recognizer.options.enable, [recognizer])) {
actions = actions.concat(recognizer.getTouchAction());
}
});
return cleanTouchActions(actions.join(' '));
}
/**
* @private
* this method is called on each input cycle and provides the preventing of the browser behavior
* @param {Object} input
*/
}, {
key: 'preventDefaults',
value: function preventDefaults(input) {
var srcEvent = input.srcEvent;
var direction = input.offsetDirection;
// if the touch action did prevented once this session
if (this.manager.session.prevented) {
srcEvent.preventDefault();
return;
}
var actions = this.actions;
var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE];
var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y];
var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X];
if (hasNone) {
// do not prevent defaults if this is a tap gesture
var isTapPointer = input.pointers.length === 1;
var isTapMovement = input.distance < 2;
var isTapTouchTime = input.deltaTime < 250;
if (isTapPointer && isTapMovement && isTapTouchTime) {
return;
}
}
if (hasPanX && hasPanY) {
// `pan-x pan-y` means browser handles all scrolling/panning, do not prevent
return;
}
if (hasNone || hasPanY && direction & DIRECTION_HORIZONTAL || hasPanX && direction & DIRECTION_VERTICAL) {
return this.preventSrc(srcEvent);
}
}
/**
* @private
* call preventDefault to prevent the browser's default behavior (scrolling in most cases)
* @param {Object} srcEvent
*/
}, {
key: 'preventSrc',
value: function preventSrc(srcEvent) {
this.manager.session.prevented = true;
srcEvent.preventDefault();
}
}]);
return TouchAction;
}();
/**
* @private
* find if a node is in the given parent
* @method hasParent
* @param {HTMLElement} node
* @param {HTMLElement} parent
* @return {Boolean} found
*/
function hasParent(node, parent) {
while (node) {
if (node === parent) {
return true;
}
node = node.parentNode;
}
return false;
}
/**
* @private
* get the center of all the pointers
* @param {Array} pointers
* @return {Object} center contains `x` and `y` properties
*/
function getCenter(pointers) {
var pointersLength = pointers.length;
// no need to loop when only one touch
if (pointersLength === 1) {
return {
x: round(pointers[0].clientX),
y: round(pointers[0].clientY)
};
}
var x = 0;
var y = 0;
var i = 0;
while (i < pointersLength) {
x += pointers[i].clientX;
y += pointers[i].clientY;
i++;
}
return {
x: round(x / pointersLength),
y: round(y / pointersLength)
};
}
/**
* @private
* create a simple clone from the input used for storage of firstInput and firstMultiple
* @param {Object} input
* @returns {Object} clonedInputData
*/
function simpleCloneInputData(input) {
// make a simple copy of the pointers because we will get a reference if we don't
// we only need clientXY for the calculations
var pointers = [];
var i = 0;
while (i < input.pointers.length) {
pointers[i] = {
clientX: round(input.pointers[i].clientX),
clientY: round(input.pointers[i].clientY)
};
i++;
}
return {
timeStamp: now(),
pointers: pointers,
center: getCenter(pointers),
deltaX: input.deltaX,
deltaY: input.deltaY
};
}
/**
* @private
* calculate the angle between two coordinates
* @param {Object} p1
* @param {Object} p2
* @param {Array} [props] containing x and y keys
* @return {Number} angle
*/
function getAngle(p1, p2, props) {
if (!props) {
props = PROPS_XY;
}
var x = p2[props[0]] - p1[props[0]];
var y = p2[props[1]] - p1[props[1]];
return Math.atan2(y, x) * 180 / Math.PI;
}
/**
* @private
* get the direction between two points
* @param {Number} x
* @param {Number} y
* @return {Number} direction
*/
function getDirection(x, y) {
if (x === y) {
return DIRECTION_NONE;
}
if (abs(x) >= abs(y)) {
return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
}
return y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
}
function computeDeltaXY(session, input) {
var center = input.center;
// let { offsetDelta:offset = {}, prevDelta = {}, prevInput = {} } = session;
// jscs throwing error on defalut destructured values and without defaults tests fail
var offset = session.offsetDelta || {};
var prevDelta = session.prevDelta || {};
var prevInput = session.prevInput || {};
if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
prevDelta = session.prevDelta = {
x: prevInput.deltaX || 0,
y: prevInput.deltaY || 0
};
offset = session.offsetDelta = {
x: center.x,
y: center.y
};
}
input.deltaX = prevDelta.x + (center.x - offset.x);
input.deltaY = prevDelta.y + (center.y - offset.y);
}
/**
* @private
* calculate the velocity between two points. unit is in px per ms.
* @param {Number} deltaTime
* @param {Number} x
* @param {Number} y
* @return {Object} velocity `x` and `y`
*/
function getVelocity(deltaTime, x, y) {
return {
x: x / deltaTime || 0,
y: y / deltaTime || 0
};
}
/**
* @private
* calculate the scale factor between two pointersets
* no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
* @param {Array} start array of pointers
* @param {Array} end array of pointers
* @return {Number} scale
*/
function getScale(start, end) {
return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
}
/**
* @private
* calculate the rotation degrees between two pointersets
* @param {Array} start array of pointers
* @param {Array} end array of pointers
* @return {Number} rotation
*/
function getRotation(start, end) {
return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY);
}
/**
* @private
* velocity is calculated every x ms
* @param {Object} session
* @param {Object} input
*/
function computeIntervalInputData(session, input) {
var last = session.lastInterval || input;
var deltaTime = input.timeStamp - last.timeStamp;
var velocity = void 0;
var velocityX = void 0;
var velocityY = void 0;
var direction = void 0;
if (input.eventType !== INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {
var deltaX = input.deltaX - last.deltaX;
var deltaY = input.deltaY - last.deltaY;
var v = getVelocity(deltaTime, deltaX, deltaY);
velocityX = v.x;
velocityY = v.y;
velocity = abs(v.x) > abs(v.y) ? v.x : v.y;
direction = getDirection(deltaX, deltaY);
session.lastInterval = input;
} else {
// use latest velocity info if it doesn't overtake a minimum period
velocity = last.velocity;
velocityX = last.velocityX;
velocityY = last.velocityY;
direction = last.direction;
}
input.velocity = velocity;
input.velocityX = velocityX;
input.velocityY = velocityY;
input.direction = direction;
}
/**
* @private
* extend the data with some usable properties like scale, rotate, velocity etc
* @param {Object} manager
* @param {Object} input
*/
function computeInputData(manager, input) {
var session = manager.session;
var pointers = input.pointers;
var pointersLength = pointers.length;
// store the first input to calculate the distance and direction
if (!session.firstInput) {
session.firstInput = simpleCloneInputData(input);
}
// to compute scale and rotation we need to store the multiple touches
if (pointersLength > 1 && !session.firstMultiple) {
session.firstMultiple = simpleCloneInputData(input);
} else if (pointersLength === 1) {
session.firstMultiple = false;
}
var firstInput = session.firstInput;
var firstMultiple = session.firstMultiple;
var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
var center = input.center = getCenter(pointers);
input.timeStamp = now();
input.deltaTime = input.timeStamp - firstInput.timeStamp;
input.angle = getAngle(offsetCenter, center);
input.distance = getDistance(offsetCenter, center);
computeDeltaXY(session, input);
input.offsetDirection = getDirection(input.deltaX, input.deltaY);
var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY);
input.overallVelocityX = overallVelocity.x;
input.overallVelocityY = overallVelocity.y;
input.overallVelocity = abs(overallVelocity.x) > abs(overallVelocity.y) ? overallVelocity.x : overallVelocity.y;
input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
input.maxPointers = !session.prevInput ? input.pointers.length : input.pointers.length > session.prevInput.maxPointers ? input.pointers.length : session.prevInput.maxPointers;
computeIntervalInputData(session, input);
// find the correct target
var target = manager.element;
if (hasParent(input.srcEvent.target, target)) {
target = input.srcEvent.target;
}
input.target = target;
}
/**
* @private
* handle input events
* @param {Manager} manager
* @param {String} eventType
* @param {Object} input
*/
function inputHandler(manager, eventType, input) {
var pointersLen = input.pointers.length;
var changedPointersLen = input.changedPointers.length;
var isFirst = eventType & INPUT_START && pointersLen - changedPointersLen === 0;
var isFinal = eventType & (INPUT_END | INPUT_CANCEL) && pointersLen - changedPointersLen === 0;
input.isFirst = !!isFirst;
input.isFinal = !!isFinal;
if (isFirst) {
manager.session = {};
}
// source event is the normalized value of the domEvents
// like 'touchstart, mouseup, pointerdown'
input.eventType = eventType;
// compute scale, rotation etc
computeInputData(manager, input);
// emit secret event
manager.emit('hammer.input', input);
manager.recognize(input);
manager.session.prevInput = input;
}
/**
* @private
* split string on whitespace
* @param {String} str
* @returns {Array} words
*/
function splitStr(str) {
return str.trim().split(/\s+/g);
}
/**
* @private
* addEventListener with multiple events at once
* @param {EventTarget} target
* @param {String} types
* @param {Function} handler
*/
function addEventListeners(target, types, handler) {
each(splitStr(types), function (type) {
target.addEventListener(type, handler, false);
});
}
/**
* @private
* removeEventListener with multiple events at once
* @param {EventTarget} target
* @param {String} types
* @param {Function} handler
*/
function removeEventListeners(target, types, handler) {
each(splitStr(types), function (type) {
target.removeEventListener(type, handler, false);
});
}
/**
* @private
* get the window object of an element
* @param {HTMLElement} element
* @returns {DocumentView|Window}
*/
function getWindowForElement(element) {
var doc = element.ownerDocument || element;
return doc.defaultView || doc.parentWindow || window;
}
/**
* @private
* create new input type manager
* @param {Manager} manager
* @param {Function} callback
* @returns {Input}
* @constructor
*/
var Input = function () {
function Input(manager, callback) {
classCallCheck(this, Input);
var self = this;
this.manager = manager;
this.callback = callback;
this.element = manager.element;
this.target = manager.options.inputTarget;
// smaller wrapper around the handler, for the scope and the enabled state of the manager,
// so when disabled the input events are completely bypassed.
this.domHandler = function (ev) {
if (boolOrFn(manager.options.enable, [manager])) {
self.handler(ev);
}
};
this.init();
}
/**
* @private
* should handle the inputEvent data and trigger the callback
* @virtual
*/
createClass(Input, [{
key: 'handler',
value: function handler() {}
/**
* @private
* bind the events
*/
}, {
key: 'init',
value: function init() {
this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
}
/**
* @private
* unbind the events
*/
}, {
key: 'destroy',
value: function destroy() {
this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
}
}]);
return Input;
}();
var POINTER_INPUT_MAP = {
pointerdown: INPUT_START,
pointermove: INPUT_MOVE,
pointerup: INPUT_END,
pointercancel: INPUT_CANCEL,
pointerout: INPUT_CANCEL
};
// in IE10 the pointer types is defined as an enum
var IE10_POINTER_TYPE_ENUM = {
2: INPUT_TYPE_TOUCH,
3: INPUT_TYPE_PEN,
4: INPUT_TYPE_MOUSE,
5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816
};
var POINTER_ELEMENT_EVENTS = 'pointerdown';
var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';
// IE10 has prefixed support, and case-sensitive
if (window.MSPointerEvent && !window.PointerEvent) {
POINTER_ELEMENT_EVENTS = 'MSPointerDown';
POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
}
/**
* @private
* Pointer events input
* @constructor
* @extends Input
*/
var PointerEventInput = function (_Input) {
inherits(PointerEventInput, _Input);
function PointerEventInput() {
classCallCheck(this, PointerEventInput);
var _this = possibleConstructorReturn(this, (PointerEventInput.__proto__ || Object.getPrototypeOf(PointerEventInput)).apply(this, arguments));
_this.evEl = POINTER_ELEMENT_EVENTS;
_this.evWin = POINTER_WINDOW_EVENTS;
_this.store = _this.manager.session.pointerEvents = [];
return _this;
}
/**
* @private
* handle mouse events
* @param {Object} ev
*/
createClass(PointerEventInput, [{
key: 'handler',
value: function handler(ev) {
var store = this.store;
var removePointer = false;
var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
var isTouch = pointerType === INPUT_TYPE_TOUCH;
// get index of the event in the store
var storeIndex = inArray(store, ev.pointerId, 'pointerId');
// start and mouse must be down
if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
if (storeIndex < 0) {
store.push(ev);
storeIndex = store.length - 1;
}
} else if (eventType & (INPUT_END | INPUT_CANCEL)) {
removePointer = true;
}
// it not found, so the pointer hasn't been down (so it's probably a hover)
if (storeIndex < 0) {
return;
}
// update the event in the store
store[storeIndex] = ev;
this.callback(this.manager, eventType, {
pointers: store,
changedPointers: [ev],
pointerType: pointerType,
srcEvent: ev
});
if (removePointer) {
// remove from the store
store.splice(storeIndex, 1);
}
}
}]);
return PointerEventInput;
}(Input);
/**
* @private
* convert array-like objects to real arrays
* @param {Object} obj
* @returns {Array}
*/
function toArray$1(obj) {
return Array.prototype.slice.call(obj, 0);
}
/**
* @private
* unique array with objects based on a key (like 'id') or just by the array's value
* @param {Array} src [{id:1},{id:2},{id:1}]
* @param {String} [key]
* @param {Boolean} [sort=False]
* @returns {Array} [{id:1},{id:2}]
*/
function uniqueArray(src, key, sort) {
var results = [];
var values = [];
var i = 0;
while (i < src.length) {
var val = key ? src[i][key] : src[i];
if (inArray(values, val) < 0) {
results.push(src[i]);
}
values[i] = val;
i++;
}
if (sort) {
if (!key) {
results = results.sort();
} else {
results = results.sort(function (a, b) {
return a[key] > b[key];
});
}
}
return results;
}
var TOUCH_INPUT_MAP = {
touchstart: INPUT_START,
touchmove: INPUT_MOVE,
touchend: INPUT_END,
touchcancel: INPUT_CANCEL
};
var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';
/**
* @private
* Multi-user touch events input
* @constructor
* @extends Input
*/
var TouchInput = function (_Input) {
inherits(TouchInput, _Input);
function TouchInput() {
classCallCheck(this, TouchInput);
TouchInput.prototype.evTarget = TOUCH_TARGET_EVENTS;
TouchInput.prototype.targetIds = {};
var _this = possibleConstructorReturn(this, (TouchInput.__proto__ || Object.getPrototypeOf(TouchInput)).apply(this, arguments));
_this.evTarget = TOUCH_TARGET_EVENTS;
_this.targetIds = {};
return _this;
}
createClass(TouchInput, [{
key: 'handler',
value: function handler(ev) {
var type = TOUCH_INPUT_MAP[ev.type];
var touches = getTouches.call(this, ev, type);
if (!touches) {
return;
}
this.callback(this.manager, type, {
pointers: touches[0],
changedPointers: touches[1],
pointerType: INPUT_TYPE_TOUCH,
srcEvent: ev
});
}
}]);
return TouchInput;
}(Input);
function getTouches(ev, type) {
var allTouches = toArray$1(ev.touches);
var targetIds = this.targetIds;
// when there is only one touch, the process can be simplified
if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
targetIds[allTouches[0].identifier] = true;
return [allTouches, allTouches];
}
var i = void 0;
var targetTouches = void 0;
var changedTouches = toArray$1(ev.changedTouches);
var changedTargetTouches = [];
var target = this.target;
// get target touches from touches
targetTouches = allTouches.filter(function (touch) {
return hasParent(touch.target, target);
});
// collect touches
if (type === INPUT_START) {
i = 0;
while (i < targetTouches.length) {
targetIds[targetTouches[i].identifier] = true;
i++;
}
}
// filter changed touches to only contain touches that exist in the collected target ids
i = 0;
while (i < changedTouches.length) {
if (targetIds[changedTouches[i].identifier]) {
changedTargetTouches.push(changedTouches[i]);
}
// cleanup removed touches
if (type & (INPUT_END | INPUT_CANCEL)) {
delete targetIds[changedTouches[i].identifier];
}
i++;
}
if (!changedTargetTouches.length) {
return;
}
return [
// merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), changedTargetTouches];
}
var MOUSE_INPUT_MAP = {
mousedown: INPUT_START,
mousemove: INPUT_MOVE,
mouseup: INPUT_END
};
var MOUSE_ELEMENT_EVENTS = 'mousedown';
var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';
/**
* @private
* Mouse events input
* @constructor
* @extends Input
*/
var MouseInput = function (_Input) {
inherits(MouseInput, _Input);
function MouseInput() {
classCallCheck(this, MouseInput);
var _this = possibleConstructorReturn(this, (MouseInput.__proto__ || Object.getPrototypeOf(MouseInput)).apply(this, arguments));
_this.evEl = MOUSE_ELEMENT_EVENTS;
_this.evWin = MOUSE_WINDOW_EVENTS;
_this.pressed = false; // mousedown state
return _this;
}
/**
* @private
* handle mouse events
* @param {Object} ev
*/
createClass(MouseInput, [{
key: 'handler',
value: function handler(ev) {
var eventType = MOUSE_INPUT_MAP[ev.type];
// on start we want to have the left mouse button down
if (eventType & INPUT_START && ev.button === 0) {
this.pressed = true;
}
if (eventType & INPUT_MOVE && ev.which !== 1) {
eventType = INPUT_END;
}
// mouse must be down
if (!this.pressed) {
return;
}
if (eventType & INPUT_END) {
this.pressed = false;
}
this.callback(this.manager, eventType, {
pointers: [ev],
changedPointers: [ev],
pointerType: INPUT_TYPE_MOUSE,
srcEvent: ev
});
}
}]);
return MouseInput;
}(Input);
/**
* @private
* Combined touch and mouse input
*
* Touch has a higher priority then mouse, and while touching no mouse events are allowed.
* This because touch devices also emit mouse events while doing a touch.
*
* @constructor
* @extends Input
*/
var DEDUP_TIMEOUT = 2500;
var DEDUP_DISTANCE = 25;
var TouchMouseInput = function (_Input) {
inherits(TouchMouseInput, _Input);
function TouchMouseInput() {
classCallCheck(this, TouchMouseInput);
var _this = possibleConstructorReturn(this, (TouchMouseInput.__proto__ || Object.getPrototypeOf(TouchMouseInput)).apply(this, arguments));
var handler = bindFn(_this.handler, _this);
_this.touch = new TouchInput(_this.manager, handler);
_this.mouse = new MouseInput(_this.manager, handler);
_this.primaryTouch = null;
_this.lastTouches = [];
return _this;
}
/**
* @private
* handle mouse and touch events
* @param {Hammer} manager
* @param {String} inputEvent
* @param {Object} inputData
*/
createClass(TouchMouseInput, [{
key: 'handler',
value: function handler(manager, inputEvent, inputData) {
var isTouch = inputData.pointerType === INPUT_TYPE_TOUCH;
var isMouse = inputData.pointerType === INPUT_TYPE_MOUSE;
if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) {
return;
}
// when we're in a touch event, record touches to de-dupe synthetic mouse event
if (isTouch) {
recordTouches.call(this, inputEvent, inputData);
} else if (isMouse && isSyntheticEvent.call(this, inputData)) {
return;
}
this.callback(manager, inputEvent, inputData);
}
/**
* @private
* remove the event listeners
*/
}, {
key: 'destroy',
value: function destroy() {
this.touch.destroy();
this.mouse.destroy();
}
}]);
return TouchMouseInput;
}(Input);
function recordTouches(eventType, eventData) {
if (eventType & INPUT_START) {
this.primaryTouch = eventData.changedPointers[0].identifier;
setLastTouch.call(this, eventData);
} else if (eventType & (INPUT_END | INPUT_CANCEL)) {
setLastTouch.call(this, eventData);
}
}
function setLastTouch(eventData) {
var _this2 = this;
var _eventData$changedPoi = slicedToArray(eventData.changedPointers, 1);
var touch = _eventData$changedPoi[0];
if (touch.identifier === this.primaryTouch) {
(function () {
var lastTouch = { x: touch.clientX, y: touch.clientY };
_this2.lastTouches.push(lastTouch);
var lts = _this2.lastTouches;
var removeLastTouch = function removeLastTouch() {
var i = lts.indexOf(lastTouch);
if (i > -1) {
lts.splice(i, 1);
}
};
setTimeout(removeLastTouch, DEDUP_TIMEOUT);
})();
}
}
function isSyntheticEvent(eventData) {
var x = eventData.srcEvent.clientX;
var y = eventData.srcEvent.clientY;
for (var i = 0; i < this.lastTouches.length; i++) {
var t = this.lastTouches[i];
var dx = Math.abs(x - t.x);
var dy = Math.abs(y - t.y);
if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) {
return true;
}
}
return false;
}
/**
* @private
* create new input type manager
* called by the Manager constructor
* @param {Hammer} manager
* @returns {Input}
*/
function createInputInstance(manager) {
var Type = void 0;
// let inputClass = manager.options.inputClass;
var inputClass = manager.options.inputClass;
if (inputClass) {
Type = inputClass;
} else if (SUPPORT_POINTER_EVENTS) {
Type = PointerEventInput;
} else if (SUPPORT_ONLY_TOUCH) {
Type = TouchInput;
} else if (!SUPPORT_TOUCH) {
Type = MouseInput;
} else {
Type = TouchMouseInput;
}
return new Type(manager, inputHandler);
}
var STOP = 1;
var FORCED_STOP = 2;
/**
* @private
* Manager
* @param {HTMLElement} element
* @param {Object} [options]
* @constructor
*/
var Manager = function () {
function Manager(element, options) {
var _this = this;
classCallCheck(this, Manager);
this.options = assign$1({}, Hammer.defaults, options || {});
this.options.inputTarget = this.options.inputTarget || element;
this.handlers = {};
this.session = {};
this.recognizers = [];
this.oldCssProps = {};
this.element = element;
this.input = createInputInstance(this);
this.touchAction = new TouchAction(this, this.options.touchAction);
toggleCssProps(this, true);
each(this.options.recognizers, function (item) {
var recognizer = _this.add(new item[0](item[1]));
item[2] && recognizer.recognizeWith(item[2]);
item[3] && recognizer.requireFailure(item[3]);
}, this);
}
/**
* @private
* set options
* @param {Object} options
* @returns {Manager}
*/
createClass(Manager, [{
key: 'set',
value: function set(options) {
assign$1(this.options, options);
// Options that need a little more setup
if (options.touchAction) {
this.touchAction.update();
}
if (options.inputTarget) {
// Clean up existing event listeners and reinitialize
this.input.destroy();
this.input.target = options.inputTarget;
this.input.init();
}
return this;
}
/**
* @private
* stop recognizing for this session.
* This session will be discarded, when a new [input]start event is fired.
* When forced, the recognizer cycle is stopped immediately.
* @param {Boolean} [force]
*/
}, {
key: 'stop',
value: function stop(force) {
this.session.stopped = force ? FORCED_STOP : STOP;
}
/**
* @private
* run the recognizers!
* called by the inputHandler function on every movement of the pointers (touches)
* it walks through all the recognizers and tries to detect the gesture that is being made
* @param {Object} inputData
*/
}, {
key: 'recognize',
value: function recognize(inputData) {
var session = this.session;
if (session.stopped) {
return;
}
// run the touch-action polyfill
this.touchAction.preventDefaults(inputData);
var recognizer = void 0;
var recognizers = this.recognizers;
// this holds the recognizer that is being recognized.
// so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
// if no recognizer is detecting a thing, it is set to `null`
var curRecognizer = session.curRecognizer;
// reset when the last recognizer is recognized
// or when we're in a new session
if (!curRecognizer || curRecognizer && curRecognizer.state & STATE_RECOGNIZED) {
curRecognizer = session.curRecognizer = null;
}
var i = 0;
while (i < recognizers.length) {
recognizer = recognizers[i];
// find out if we are allowed try to recognize the input for this one.
// 1. allow if the session is NOT forced stopped (see the .stop() method)
// 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
// that is being recognized.
// 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
// this can be setup with the `recognizeWith()` method on the recognizer.
if (session.stopped !== FORCED_STOP && ( // 1
!curRecognizer || recognizer === curRecognizer || // 2
recognizer.canRecognizeWith(curRecognizer))) {
// 3
recognizer.recognize(inputData);
} else {
recognizer.reset();
}
// if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
// current active recognizer. but only if we don't already have an active recognizer
if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
curRecognizer = session.curRecognizer = recognizer;
}
i++;
}
}
/**
* @private
* get a recognizer by its event name.
* @param {Recognizer|String} recognizer
* @returns {Recognizer|Null}
*/
}, {
key: 'get',
value: function get(recognizer) {
if (recognizer instanceof Recognizer) {
return recognizer;
}
var recognizers = this.recognizers;
for (var i = 0; i < recognizers.length; i++) {
if (recognizers[i].options.event === recognizer) {
return recognizers[i];
}
}
return null;
}
/**
* @private add a recognizer to the manager
* existing recognizers with the same event name will be removed
* @param {Recognizer} recognizer
* @returns {Recognizer|Manager}
*/
}, {
key: 'add',
value: function add(recognizer) {
if (invokeArrayArg(recognizer, 'add', this)) {
return this;
}
// remove existing
var existing = this.get(recognizer.options.event);
if (existing) {
this.remove(existing);
}
this.recognizers.push(recognizer);
recognizer.manager = this;
this.touchAction.update();
return recognizer;
}
/**
* @private
* remove a recognizer by name or instance
* @param {Recognizer|String} recognizer
* @returns {Manager}
*/
}, {
key: 'remove',
value: function remove(recognizer) {
if (invokeArrayArg(recognizer, 'remove', this)) {
return this;
}
recognizer = this.get(recognizer);
// let's make sure this recognizer exists
if (recognizer) {
var recognizers = this.recognizers;
var index = inArray(recognizers, recognizer);
if (index !== -1) {
recognizers.splice(index, 1);
this.touchAction.update();
}
}
return this;
}
/**
* @private
* bind event
* @param {String} events
* @param {Function} handler
* @returns {EventEmitter} this
*/
}, {
key: 'on',
value: function on(events, handler) {
if (events === undefined) {
return;
}
if (handler === undefined) {
return;
}
var handlers = this.handlers;
each(splitStr(events), function (event) {
handlers[event] = handlers[event] || [];
handlers[event].push(handler);
});
return this;
}
/**
* @private unbind event, leave emit blank to remove all handlers
* @param {String} events
* @param {Function} [handler]
* @returns {EventEmitter} this
*/
}, {
key: 'off',
value: function off(events, handler) {
if (events === undefined) {
return;
}
var handlers = this.handlers;
each(splitStr(events), function (event) {
if (!handler) {
delete handlers[event];
} else {
handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1);
}
});
return this;
}
/**
* @private emit event to the listeners
* @param {String} event
* @param {Object} data
*/
}, {
key: 'emit',
value: function emit(event, data) {
// we also want to trigger dom events
if (this.options.domEvents) {
triggerDomEvent(event, data);
}
// no handlers, so skip it all
var handlers = this.handlers[event] && this.handlers[event].slice();
if (!handlers || !handlers.length) {
return;
}
data.type = event;
data.preventDefault = function () {
data.srcEvent.preventDefault();
};
var i = 0;
while (i < handlers.length) {
handlers[i](data);
i++;
}
}
/**
* @private
* destroy the manager and unbinds all events
* it doesn't unbind dom events, that is the user own responsibility
*/
}, {
key: 'destroy',
value: function destroy() {
this.element && toggleCssProps(this, false);
this.handlers = {};
this.session = {};
this.input.destroy();
this.element = null;
}
}]);
return Manager;
}();
function toggleCssProps(manager, add) {
var element = manager.element;
if (!element.style) {
return;
}
var prop = void 0;
each(manager.options.cssProps, function (value, name) {
prop = prefixed(element.style, name);
if (add) {
manager.oldCssProps[prop] = element.style[prop];
element.style[prop] = value;
} else {
element.style[prop] = manager.oldCssProps[prop] || '';
}
});
if (!add) {
manager.oldCssProps = {};
}
}
/**
* @private
* trigger dom event
* @param {String} event
* @param {Object} data
*/
function triggerDomEvent(event, data) {
var gestureEvent = document.createEvent('Event');
gestureEvent.initEvent(event, true, true);
gestureEvent.gesture = data;
data.target.dispatchEvent(gestureEvent);
}
/**
* @private
* Simple way to create a manager with a default set of recognizers.
* @param {HTMLElement} element
* @param {Object} [options]
* @constructor
*/
var Hammer = function Hammer(element, options) {
classCallCheck(this, Hammer);
options = options || {};
options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
return new Manager(element, options);
};
Hammer.VERSION = '2.0.8';
/**
* @private
* default settings
* @namespace
*/
Hammer.defaults = {
/**
* @private
* set if DOM events are being triggered.
* But this is slower and unused by simple implementations, so disabled by default.
* @type {Boolean}
* @default false
*/
domEvents: false,
/**
* @private
* The value for the touchAction property/fallback.
* When set to `compute` it will magically set the correct value based on the added recognizers.
* @type {String}
* @default compute
*/
touchAction: TOUCH_ACTION_COMPUTE,
/**
* @private
* @type {Boolean}
* @default true
*/
enable: true,
/**
* @private
* EXPERIMENTAL FEATURE -- can be removed/changed
* Change the parent input target element.
* If Null, then it is being set the to main element.
* @type {Null|EventTarget}
* @default null
*/
inputTarget: null,
/**
* @private
* force an input class
* @type {Null|Function}
* @default null
*/
inputClass: null,
/**
* @private
* Default recognizer setup when calling `Hammer()`
* When creating a new Manager these will be skipped.
* @type {Array}
*/
preset: [
// RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
[RotateRecognizer, { enable: false }], [PinchRecognizer, { enable: false }, ['rotate']], [SwipeRecognizer, { direction: DIRECTION_HORIZONTAL }], [PanRecognizer, { direction: DIRECTION_HORIZONTAL }, ['swipe']], [TapRecognizer], [TapRecognizer, { event: 'doubletap', taps: 2 }, ['tap']], [PressRecognizer]],
/**
* @private
* Some CSS properties can be used to improve the working of Hammer.
* Add them to this method and they will be set when creating a new Manager.
* @namespace
*/
cssProps: {
/**
* @private
* Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
* @type {String}
* @default 'none'
*/
userSelect: 'none',
/**
* @private
* Disable the Windows Phone grippers when pressing an element.
* @type {String}
* @default 'none'
*/
touchSelect: 'none',
/**
* @private
* Disables the default callout shown when you touch and hold a touch target.
* On iOS, when you touch and hold a touch target such as a link, Safari displays
* a callout containing information about the link. This property allows you to disable that callout.
* @type {String}
* @default 'none'
*/
touchCallout: 'none',
/**
* @private
* Specifies whether zooming is enabled. Used by IE10>
* @type {String}
* @default 'none'
*/
contentZooming: 'none',
/**
* @private
* Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
* @type {String}
* @default 'none'
*/
userDrag: 'none',
/**
* @private
* Overrides the highlight color shown when the user taps a link or a JavaScript
* clickable element in iOS. This property obeys the alpha value, if specified.
* @type {String}
* @default 'rgba(0,0,0,0)'
*/
tapHighlightColor: 'rgba(0,0,0,0)'
}
};
var SINGLE_TOUCH_INPUT_MAP = {
touchstart: INPUT_START,
touchmove: INPUT_MOVE,
touchend: INPUT_END,
touchcancel: INPUT_CANCEL
};
var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';
/**
* @private
* Touch events input
* @constructor
* @extends Input
*/
var SingleTouchInput = function (_Input) {
inherits(SingleTouchInput, _Input);
function SingleTouchInput() {
classCallCheck(this, SingleTouchInput);
var _this = possibleConstructorReturn(this, (SingleTouchInput.__proto__ || Object.getPrototypeOf(SingleTouchInput)).apply(this, arguments));
_this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;
_this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;
_this.started = false;
Input.apply(_this, arguments);
return _this;
}
createClass(SingleTouchInput, [{
key: 'handler',
value: function handler(ev) {
var type = SINGLE_TOUCH_INPUT_MAP[ev.type];
// should we handle the touch events?
if (type === INPUT_START) {
this.started = true;
}
if (!this.started) {
return;
}
var touches = normalizeSingleTouches.call(this, ev, type);
// when done, reset the started state
if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {
this.started = false;
}
this.callback(this.manager, type, {
pointers: touches[0],
changedPointers: touches[1],
pointerType: INPUT_TYPE_TOUCH,
srcEvent: ev
});
}
}]);
return SingleTouchInput;
}(Input);
function normalizeSingleTouches(ev, type) {
var all = toArray$1(ev.touches);
var changed = toArray$1(ev.changedTouches);
if (type & (INPUT_END | INPUT_CANCEL)) {
all = uniqueArray(all.concat(changed), 'identifier', true);
}
return [all, changed];
}
/**
* @private
* wrap a method with a deprecation warning and stack trace
* @param {Function} method
* @param {String} name
* @param {String} message
* @returns {Function} A new function wrapping the supplied method.
*/
function deprecate(method, name, message) {
var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n';
return function () {
var e = new Error('get-stack-trace');
var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '').replace(/^\s+at\s+/gm, '').replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace';
var log = window.console && (window.console.warn || window.console.log);
if (log) {
log.call(window.console, deprecationMessage, stack);
}
return method.apply(this, arguments);
};
}
/**
* @private
* extend object.
* means that properties in dest will be overwritten by the ones in src.
* @param {Object} dest
* @param {Object} src
* @param {Boolean} [merge=false]
* @returns {Object} dest
*/
var extend = deprecate(function (dest, src, merge) {
var keys = Object.keys(src);
var i = 0;
while (i < keys.length) {
if (!merge || merge && dest[keys[i]] === undefined) {
dest[keys[i]] = src[keys[i]];
}
i++;
}
return dest;
}, 'extend', 'Use `assign`.');
/**
* @private
* merge the values from src in the dest.
* means that properties that exist in dest will not be overwritten by src
* @param {Object} dest
* @param {Object} src
* @returns {Object} dest
*/
var merge = deprecate(function (dest, src) {
return extend(dest, src, true);
}, 'merge', 'Use `assign`.');
/**
* @private
* simple class inheritance
* @param {Function} child
* @param {Function} base
* @param {Object} [properties]
*/
function inherit(child, base, properties) {
var baseP = base.prototype;
var childP = void 0;
childP = child.prototype = Object.create(baseP);
childP.constructor = child;
childP._super = baseP;
if (properties) {
assign$1(childP, properties);
}
}
// this prevents errors when Hammer is loaded in the presence of an AMD
// style loader but by script tag, not by the loader.
assign$1(Hammer, {
INPUT_START: INPUT_START,
INPUT_MOVE: INPUT_MOVE,
INPUT_END: INPUT_END,
INPUT_CANCEL: INPUT_CANCEL,
STATE_POSSIBLE: STATE_POSSIBLE,
STATE_BEGAN: STATE_BEGAN,
STATE_CHANGED: STATE_CHANGED,
STATE_ENDED: STATE_ENDED,
STATE_RECOGNIZED: STATE_RECOGNIZED,
STATE_CANCELLED: STATE_CANCELLED,
STATE_FAILED: STATE_FAILED,
DIRECTION_NONE: DIRECTION_NONE,
DIRECTION_LEFT: DIRECTION_LEFT,
DIRECTION_RIGHT: DIRECTION_RIGHT,
DIRECTION_UP: DIRECTION_UP,
DIRECTION_DOWN: DIRECTION_DOWN,
DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
DIRECTION_VERTICAL: DIRECTION_VERTICAL,
DIRECTION_ALL: DIRECTION_ALL,
Manager: Manager,
Input: Input,
TouchAction: TouchAction,
TouchInput: TouchInput,
MouseInput: MouseInput,
PointerEventInput: PointerEventInput,
TouchMouseInput: TouchMouseInput,
SingleTouchInput: SingleTouchInput,
Recognizer: Recognizer,
AttrRecognizer: AttrRecognizer,
Tap: TapRecognizer,
Pan: PanRecognizer,
Swipe: SwipeRecognizer,
Pinch: PinchRecognizer,
Rotate: RotateRecognizer,
Press: PressRecognizer,
on: addEventListeners,
off: removeEventListeners,
each: each,
merge: merge,
extend: extend,
assign: assign$1,
inherit: inherit,
bindFn: bindFn,
prefixed: prefixed,
toArray: toArray$1,
inArray: inArray,
uniqueArray: uniqueArray,
splitStr: splitStr,
boolOrFn: boolOrFn,
hasParent: hasParent,
addEventListeners: addEventListeners,
removeEventListeners: removeEventListeners
});
/* jshint ignore:start */
if (typeof define === 'function' && define.amd) {
define(function () {
return Hammer;
});
} else if (typeof module !== 'undefined' && module.exports) {
module.exports = Hammer;
} else {
window[exportName] = Hammer;
}
/* jshint ignore:end */
})(window, document, 'Hammer');
================================================
FILE: package.json
================================================
{
"name": "hammerjs",
"title": "Hammer.JS",
"description": "A javascript library for multi-touch gestures",
"version": "2.0.8",
"homepage": "http://hammerjs.github.io/",
"license": "MIT",
"keywords": [
"touch",
"gestures"
],
"author": {
"name": "Jorik Tangelder",
"email": "j.tangelder@gmail.com"
},
"contributors": [
{
"name": "Alexander Schmitz",
"email": "arschmitz@gmail.com"
},
{
"name": "Chris Thoburn",
"email": ""
}
],
"repository": {
"type": "git",
"url": "git://github.com/hammerjs/hammer.js.git"
},
"bugs": {
"url": "https://github.com/hammerjs/hammer.js/issues"
},
"dependencies": {},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-plugin-external-helpers": "^6.22.0",
"babel-preset-env": "^1.7.0",
"bannerize": "^1.0.2",
"blanket": "^1.2.3",
"changelogplease": "^1.2.0",
"ember-suave": "^4.0.0",
"git-tags": "^0.2.4",
"hammer-simulator": "git://github.com/hammerjs/simulator#master",
"jquery": "^3.1.0",
"jquery-hammerjs": "2.0.x",
"jscs": "^3.0.7",
"jshint": "^2.9.2",
"jshint-stylish": "^2.2.0",
"lodash-compat": "^3.10.2",
"node-qunit-phantomjs": "^1.4.0",
"qunitjs": "^2.0.0",
"rollup": "^0.59.4",
"rollup-plugin-babel": "^3.0.4",
"run-when-changed": "^2.1.0",
"serve": "^1.4.0",
"uglify-js": "^3.4.0"
},
"main": "hammer.js",
"engines": {
"node": ">=0.8.0"
},
"scripts": {
"bannerize": "bannerize hammer.js hammer.min.js",
"connect": "serve -p 8000 ./",
"rollup": "rollup -c",
"uglify": "uglifyjs hammer.js --source-map \"filename=hammer.min.js.map\" -o hammer.min.js --timings",
"string-replace": "node string-replace.js",
"jshint": "jshint src/**/*.js tests/unit/*.js tests/unit/gestures/*.js --config .jshintrc --verbose --reporter=node_modules/jshint-stylish",
"jscs": " jscs src/**/*.js tests/unit/*.js tests/unit/gestures/*.js --config=./.jscsrc",
"qunit": "phantomjs node_modules/qunit-phantomjs-runner/runner-list.js tests/unit/index.html 15",
"watch": "run-when-changed --watch 'src/**/*.js' --watch 'tests/unit/*.js' --watch 'tests/unit/gestures/*.js' --exec 'npm run watch-tasks'",
"watch-tasks": "npm run rollup && npm run string-replace && npm run uglify && npm run jshint && npm run jscs",
"build": "npm run rollup && npm run string-replace && npm run uglify && npm run bannerize",
"test": "npm run build && npm run jshint && npm run jscs && npm run qunit"
}
}
================================================
FILE: rollup.config.js
================================================
import babel from 'rollup-plugin-babel';
export default {
entry: 'src/main.js',
format: 'es',
plugins: [ babel({ exclude: 'node_modules/**' }) ],
dest: 'hammer.js',
intro: " (function(window, document, exportName, undefined) { \n'use strict';",
outro: "})(window, document, 'Hammer');"
};
================================================
FILE: src/.babelrc
================================================
{
"presets": [
[
"env",
{
"modules": false
}
]
],
"plugins": [
"external-helpers"
]
}
================================================
FILE: src/hammer.js
================================================
import ifUndefined from './utils/if-undefined';
import { TOUCH_ACTION_COMPUTE } from './touchactionjs/touchaction-Consts';
import { DIRECTION_HORIZONTAL } from './inputjs/input-consts';
import RotateRecognizer from './recognizers/rotate';
import PinchRecognizer from './recognizers/pinch';
import SwipeRecognizer from './recognizers/swipe';
import PanRecognizer from './recognizers/pan';
import TapRecognizer from './recognizers/tap';
import PressRecognizer from './recognizers/press';
import Manager from './manager';
/**
* @private
* Simple way to create a manager with a default set of recognizers.
* @param {HTMLElement} element
* @param {Object} [options]
* @constructor
*/
export default class Hammer {
constructor(element, options) {
options = options || {};
options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
return new Manager(element, options);
}
}
/**
* @private
* @const {string}
*/
Hammer.VERSION = '{{PKG_VERSION}}';
/**
* @private
* default settings
* @namespace
*/
Hammer.defaults = {
/**
* @private
* set if DOM events are being triggered.
* But this is slower and unused by simple implementations, so disabled by default.
* @type {Boolean}
* @default false
*/
domEvents: false,
/**
* @private
* The value for the touchAction property/fallback.
* When set to `compute` it will magically set the correct value based on the added recognizers.
* @type {String}
* @default compute
*/
touchAction: TOUCH_ACTION_COMPUTE,
/**
* @private
* @type {Boolean}
* @default true
*/
enable: true,
/**
* @private
* EXPERIMENTAL FEATURE -- can be removed/changed
* Change the parent input target element.
* If Null, then it is being set the to main element.
* @type {Null|EventTarget}
* @default null
*/
inputTarget: null,
/**
* @private
* force an input class
* @type {Null|Function}
* @default null
*/
inputClass: null,
/**
* @private
* Default recognizer setup when calling `Hammer()`
* When creating a new Manager these will be skipped.
* @type {Array}
*/
preset: [
// RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
[RotateRecognizer, { enable: false }],
[PinchRecognizer, { enable: false }, ['rotate']],
[SwipeRecognizer, { direction: DIRECTION_HORIZONTAL }],
[PanRecognizer, { direction: DIRECTION_HORIZONTAL }, ['swipe']],
[TapRecognizer],
[TapRecognizer, { event: 'doubletap', taps: 2 }, ['tap']],
[PressRecognizer]
],
/**
* @private
* Some CSS properties can be used to improve the working of Hammer.
* Add them to this method and they will be set when creating a new Manager.
* @namespace
*/
cssProps: {
/**
* @private
* Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
* @type {String}
* @default 'none'
*/
userSelect: 'none',
/**
* @private
* Disable the Windows Phone grippers when pressing an element.
* @type {String}
* @default 'none'
*/
touchSelect: 'none',
/**
* @private
* Disables the default callout shown when you touch and hold a touch target.
* On iOS, when you touch and hold a touch target such as a link, Safari displays
* a callout containing information about the link. This property allows you to disable that callout.
* @type {String}
* @default 'none'
*/
touchCallout: 'none',
/**
* @private
* Specifies whether zooming is enabled. Used by IE10>
* @type {String}
* @default 'none'
*/
contentZooming: 'none',
/**
* @private
* Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
* @type {String}
* @default 'none'
*/
userDrag: 'none',
/**
* @private
* Overrides the highlight color shown when the user taps a link or a JavaScript
* clickable element in iOS. This property obeys the alpha value, if specified.
* @type {String}
* @default 'rgba(0,0,0,0)'
*/
tapHighlightColor: 'rgba(0,0,0,0)'
}
};
================================================
FILE: src/input/mouse.js
================================================
import {
INPUT_START,
INPUT_MOVE,
INPUT_END,
INPUT_TYPE_MOUSE
} from '../inputjs/input-consts';
import Input from '../inputjs/input-constructor';
const MOUSE_INPUT_MAP = {
mousedown: INPUT_START,
mousemove: INPUT_MOVE,
mouseup: INPUT_END
};
const MOUSE_ELEMENT_EVENTS = 'mousedown';
const MOUSE_WINDOW_EVENTS = 'mousemove mouseup';
/**
* @private
* Mouse events input
* @constructor
* @extends Input
*/
export default class MouseInput extends Input {
constructor() {
super(...arguments);
this.evEl = MOUSE_ELEMENT_EVENTS;
this.evWin = MOUSE_WINDOW_EVENTS;
this.pressed = false; // mousedown state
}
/**
* @private
* handle mouse events
* @param {Object} ev
*/
handler(ev) {
let eventType = MOUSE_INPUT_MAP[ev.type];
// on start we want to have the left mouse button down
if (eventType & INPUT_START && ev.button === 0) {
this.pressed = true;
}
if (eventType & INPUT_MOVE && ev.which !== 1) {
eventType = INPUT_END;
}
// mouse must be down
if (!this.pressed) {
return;
}
if (eventType & INPUT_END) {
this.pressed = false;
}
this.callback(this.manager, eventType, {
pointers: [ev],
changedPointers: [ev],
pointerType: INPUT_TYPE_MOUSE,
srcEvent: ev
});
}
}
================================================
FILE: src/input/pointerevent.js
================================================
import {
INPUT_START,
INPUT_END,
INPUT_CANCEL,
INPUT_MOVE,
INPUT_TYPE_TOUCH,
INPUT_TYPE_MOUSE,
INPUT_TYPE_PEN,
INPUT_TYPE_KINECT
} from '../inputjs/input-consts';
import Input from '../inputjs/input-constructor';
import inArray from '../utils/in-array';
const POINTER_INPUT_MAP = {
pointerdown: INPUT_START,
pointermove: INPUT_MOVE,
pointerup: INPUT_END,
pointercancel: INPUT_CANCEL,
pointerout: INPUT_CANCEL
};
// in IE10 the pointer types is defined as an enum
const IE10_POINTER_TYPE_ENUM = {
2: INPUT_TYPE_TOUCH,
3: INPUT_TYPE_PEN,
4: INPUT_TYPE_MOUSE,
5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816
};
let POINTER_ELEMENT_EVENTS = 'pointerdown';
let POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';
// IE10 has prefixed support, and case-sensitive
if (window.MSPointerEvent && !window.PointerEvent) {
POINTER_ELEMENT_EVENTS = 'MSPointerDown';
POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
}
/**
* @private
* Pointer events input
* @constructor
* @extends Input
*/
export default class PointerEventInput extends Input {
constructor() {
super(...arguments);
this.evEl = POINTER_ELEMENT_EVENTS;
this.evWin = POINTER_WINDOW_EVENTS;
this.store = (this.manager.session.pointerEvents = []);
}
/**
* @private
* handle mouse events
* @param {Object} ev
*/
handler(ev) {
let { store } = this;
let removePointer = false;
let eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
let eventType = POINTER_INPUT_MAP[eventTypeNormalized];
let pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
let isTouch = (pointerType === INPUT_TYPE_TOUCH);
// get index of the event in the store
let storeIndex = inArray(store, ev.pointerId, 'pointerId');
// start and mouse must be down
if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
if (storeIndex < 0) {
store.push(ev);
storeIndex = store.length - 1;
}
} else if (eventType & (INPUT_END | INPUT_CANCEL)) {
removePointer = true;
}
// it not found, so the pointer hasn't been down (so it's probably a hover)
if (storeIndex < 0) {
return;
}
// update the event in the store
store[storeIndex] = ev;
this.callback(this.manager, eventType, {
pointers: store,
changedPointers: [ev],
pointerType,
srcEvent: ev
});
if (removePointer) {
// remove from the store
store.splice(storeIndex, 1);
}
}
}
================================================
FILE: src/input/singletouch.js
================================================
import {
INPUT_START,
INPUT_MOVE,
INPUT_END,
INPUT_CANCEL,
INPUT_TYPE_TOUCH
} from '../inputjs/input-consts';
import Input from '../inputjs/input-constructor';
import toArray from '../utils/to-array';
import uniqueArray from '../utils/unique-array';
const SINGLE_TOUCH_INPUT_MAP = {
touchstart: INPUT_START,
touchmove: INPUT_MOVE,
touchend: INPUT_END,
touchcancel: INPUT_CANCEL
};
const SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
const SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';
/**
* @private
* Touch events input
* @constructor
* @extends Input
*/
export default class SingleTouchInput extends Input {
constructor() {
super(...arguments);
this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;
this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;
this.started = false;
Input.apply(this, arguments);
}
handler(ev) {
let type = SINGLE_TOUCH_INPUT_MAP[ev.type];
// should we handle the touch events?
if (type === INPUT_START) {
this.started = true;
}
if (!this.started) {
return;
}
let touches = normalizeSingleTouches.call(this, ev, type);
// when done, reset the started state
if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {
this.started = false;
}
this.callback(this.manager, type, {
pointers: touches[0],
changedPointers: touches[1],
pointerType: INPUT_TYPE_TOUCH,
srcEvent: ev
});
}
}
/**
* @private
* @this {TouchInput}
* @param {Object} ev
* @param {Number} type flag
* @returns {undefined|Array} [all, changed]
*/
function normalizeSingleTouches(ev, type) {
let all = toArray(ev.touches);
let changed = toArray(ev.changedTouches);
if (type & (INPUT_END | INPUT_CANCEL)) {
all = uniqueArray(all.concat(changed), 'identifier', true);
}
return [all, changed];
}
================================================
FILE: src/input/touch.js
================================================
import {
INPUT_START,
INPUT_MOVE,
INPUT_END,
INPUT_CANCEL,
INPUT_TYPE_TOUCH
} from '../inputjs/input-consts';
import Input from '../inputjs/input-constructor';
import toArray from '../utils/to-array';
import hasParent from '../utils/has-parent';
import uniqueArray from '../utils/unique-array';
const TOUCH_INPUT_MAP = {
touchstart: INPUT_START,
touchmove: INPUT_MOVE,
touchend: INPUT_END,
touchcancel: INPUT_CANCEL
};
const TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';
/**
* @private
* Multi-user touch events input
* @constructor
* @extends Input
*/
export default class TouchInput extends Input {
constructor() {
TouchInput.prototype.evTarget = TOUCH_TARGET_EVENTS;
TouchInput.prototype.targetIds = {};
super(...arguments);
this.evTarget = TOUCH_TARGET_EVENTS;
this.targetIds = {};
}
handler(ev) {
let type = TOUCH_INPUT_MAP[ev.type];
let touches = getTouches.call(this, ev, type);
if (!touches) {
return;
}
this.callback(this.manager, type, {
pointers: touches[0],
changedPointers: touches[1],
pointerType: INPUT_TYPE_TOUCH,
srcEvent: ev
});
}
}
/**
* @private
* @this {TouchInput}
* @param {Object} ev
* @param {Number} type flag
* @returns {undefined|Array} [all, changed]
*/
function getTouches(ev, type) {
let allTouches = toArray(ev.touches);
let { targetIds } = this;
// when there is only one touch, the process can be simplified
if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
targetIds[allTouches[0].identifier] = true;
return [allTouches, allTouches];
}
let i;
let targetTouches;
let changedTouches = toArray(ev.changedTouches);
let changedTargetTouches = [];
let { target } = this;
// get target touches from touches
targetTouches = allTouches.filter((touch) => {
return hasParent(touch.target, target);
});
// collect touches
if (type === INPUT_START) {
i = 0;
while (i < targetTouches.length) {
targetIds[targetTouches[i].identifier] = true;
i++;
}
}
// filter changed touches to only contain touches that exist in the collected target ids
i = 0;
while (i < changedTouches.length) {
if (targetIds[changedTouches[i].identifier]) {
changedTargetTouches.push(changedTouches[i]);
}
// cleanup removed touches
if (type & (INPUT_END | INPUT_CANCEL)) {
delete targetIds[changedTouches[i].identifier];
}
i++;
}
if (!changedTargetTouches.length) {
return;
}
return [
// merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true),
changedTargetTouches
];
}
================================================
FILE: src/input/touchmouse.js
================================================
import Input from '../inputjs/input-constructor';
import bindFn from '../utils/bind-fn';
import TouchInput from './touch';
import MouseInput from './mouse';
import {
INPUT_START,
INPUT_END,
INPUT_CANCEL,
INPUT_TYPE_TOUCH,
INPUT_TYPE_MOUSE
} from '../inputjs/input-consts';
/**
* @private
* Combined touch and mouse input
*
* Touch has a higher priority then mouse, and while touching no mouse events are allowed.
* This because touch devices also emit mouse events while doing a touch.
*
* @constructor
* @extends Input
*/
const DEDUP_TIMEOUT = 2500;
const DEDUP_DISTANCE = 25;
export default class TouchMouseInput extends Input {
constructor() {
super(...arguments);
let handler = bindFn(this.handler, this);
this.touch = new TouchInput(this.manager, handler);
this.mouse = new MouseInput(this.manager, handler);
this.primaryTouch = null;
this.lastTouches = [];
}
/**
* @private
* handle mouse and touch events
* @param {Hammer} manager
* @param {String} inputEvent
* @param {Object} inputData
*/
handler(manager, inputEvent, inputData) {
let isTouch = (inputData.pointerType === INPUT_TYPE_TOUCH);
let isMouse = (inputData.pointerType === INPUT_TYPE_MOUSE);
if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) {
return;
}
// when we're in a touch event, record touches to de-dupe synthetic mouse event
if (isTouch) {
recordTouches.call(this, inputEvent, inputData);
} else if (isMouse && isSyntheticEvent.call(this, inputData)) {
return;
}
this.callback(manager, inputEvent, inputData);
}
/**
* @private
* remove the event listeners
*/
destroy() {
this.touch.destroy();
this.mouse.destroy();
}
}
function recordTouches(eventType, eventData) {
if (eventType & INPUT_START) {
this.primaryTouch = eventData.changedPointers[0].identifier;
setLastTouch.call(this, eventData);
} else if (eventType & (INPUT_END | INPUT_CANCEL)) {
setLastTouch.call(this, eventData);
}
}
function setLastTouch(eventData) {
let { changedPointers:[touch] } = eventData;
if (touch.identifier === this.primaryTouch) {
let lastTouch = { x: touch.clientX, y: touch.clientY };
this.lastTouches.push(lastTouch);
let lts = this.lastTouches;
let removeLastTouch = function() {
let i = lts.indexOf(lastTouch);
if (i > -1) {
lts.splice(i, 1);
}
};
setTimeout(removeLastTouch, DEDUP_TIMEOUT);
}
}
function isSyntheticEvent(eventData) {
let x = eventData.srcEvent.clientX;
let y = eventData.srcEvent.clientY;
for (let i = 0; i < this.lastTouches.length; i++) {
let t = this.lastTouches[i];
let dx = Math.abs(x - t.x);
let dy = Math.abs(y - t.y);
if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) {
return true;
}
}
return false;
}
================================================
FILE: src/inputjs/compute-delta-xy.js
================================================
import { INPUT_START, INPUT_END } from './input-consts';
export default function computeDeltaXY(session, input) {
let { center } = input;
// let { offsetDelta:offset = {}, prevDelta = {}, prevInput = {} } = session;
// jscs throwing error on defalut destructured values and without defaults tests fail
let offset = session.offsetDelta || {};
let prevDelta = session.prevDelta || {};
let prevInput = session.prevInput || {};
if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
prevDelta = session.prevDelta = {
x: prevInput.deltaX || 0,
y: prevInput.deltaY || 0
};
offset = session.offsetDelta = {
x: center.x,
y: center.y
};
}
input.deltaX = prevDelta.x + (center.x - offset.x);
input.deltaY = prevDelta.y + (center.y - offset.y);
}
================================================
FILE: src/inputjs/compute-input-data.js
================================================
import { now } from '../utils/utils-consts';
import { abs } from '../utils/utils-consts';
import hasParent from '../utils/has-parent';
import simpleCloneInputData from './simple-clone-input-data';
import getCenter from './get-center';
import getDistance from './get-distance';
import getAngle from './get-angle';
import getDirection from './get-direction';
import computeDeltaXY from './compute-delta-xy';
import getVelocity from './get-velocity';
import getScale from './get-scale';
import getRotation from './get-rotation';
import computeIntervalInputData from './compute-interval-input-data';
/**
* @private
* extend the data with some usable properties like scale, rotate, velocity etc
* @param {Object} manager
* @param {Object} input
*/
export default function computeInputData(manager, input) {
let { session } = manager;
let { pointers } = input;
let { length:pointersLength } = pointers;
// store the first input to calculate the distance and direction
if (!session.firstInput) {
session.firstInput = simpleCloneInputData(input);
}
// to compute scale and rotation we need to store the multiple touches
if (pointersLength > 1 && !session.firstMultiple) {
session.firstMultiple = simpleCloneInputData(input);
} else if (pointersLength === 1) {
session.firstMultiple = false;
}
let { firstInput, firstMultiple } = session;
let offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
let center = input.center = getCenter(pointers);
input.timeStamp = now();
input.deltaTime = input.timeStamp - firstInput.timeStamp;
input.angle = getAngle(offsetCenter, center);
input.distance = getDistance(offsetCenter, center);
computeDeltaXY(session, input);
input.offsetDirection = getDirection(input.deltaX, input.deltaY);
let overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY);
input.overallVelocityX = overallVelocity.x;
input.overallVelocityY = overallVelocity.y;
input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y;
input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length >
session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers);
computeIntervalInputData(session, input);
// find the correct target
let target = manager.element;
if (hasParent(input.srcEvent.target, target)) {
target = input.srcEvent.target;
}
input.target = target;
}
================================================
FILE: src/inputjs/compute-interval-input-data.js
================================================
import { INPUT_CANCEL,COMPUTE_INTERVAL } from './input-consts';
import { abs } from '../utils/utils-consts';
import getVelocity from './get-velocity';
import getDirection from './get-direction';
/**
* @private
* velocity is calculated every x ms
* @param {Object} session
* @param {Object} input
*/
export default function computeIntervalInputData(session, input) {
let last = session.lastInterval || input;
let deltaTime = input.timeStamp - last.timeStamp;
let velocity;
let velocityX;
let velocityY;
let direction;
if (input.eventType !== INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {
let deltaX = input.deltaX - last.deltaX;
let deltaY = input.deltaY - last.deltaY;
let v = getVelocity(deltaTime, deltaX, deltaY);
velocityX = v.x;
velocityY = v.y;
velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;
direction = getDirection(deltaX, deltaY);
session.lastInterval = input;
} else {
// use latest velocity info if it doesn't overtake a minimum period
velocity = last.velocity;
velocityX = last.velocityX;
velocityY = last.velocityY;
direction = last.direction;
}
input.velocity = velocity;
input.velocityX = velocityX;
input.velocityY = velocityY;
input.direction = direction;
}
================================================
FILE: src/inputjs/create-input-instance.js
================================================
import { SUPPORT_POINTER_EVENTS,SUPPORT_ONLY_TOUCH,SUPPORT_TOUCH } from './input-consts';
import inputHandler from './input-handler';
import PointerEventInput from '../input/pointerevent';
import TouchInput from '../input/touch';
import MouseInput from '../input/mouse';
import TouchMouseInput from '../input/touchmouse';
/**
* @private
* create new input type manager
* called by the Manager constructor
* @param {Hammer} manager
* @returns {Input}
*/
export default function createInputInstance(manager) {
let Type;
// let inputClass = manager.options.inputClass;
let { options:{ inputClass } } = manager;
if (inputClass) {
Type = inputClass;
} else if (SUPPORT_POINTER_EVENTS) {
Type = PointerEventInput;
} else if (SUPPORT_ONLY_TOUCH) {
Type = TouchInput;
} else if (!SUPPORT_TOUCH) {
Type = MouseInput;
} else {
Type = TouchMouseInput;
}
return new (Type)(manager, inputHandler);
}
================================================
FILE: src/inputjs/get-angle.js
================================================
import { PROPS_XY } from './input-consts';
/**
* @private
* calculate the angle between two coordinates
* @param {Object} p1
* @param {Object} p2
* @param {Array} [props] containing x and y keys
* @return {Number} angle
*/
export default function getAngle(p1, p2, props) {
if (!props) {
props = PROPS_XY;
}
let x = p2[props[0]] - p1[props[0]];
let y = p2[props[1]] - p1[props[1]];
return Math.atan2(y, x) * 180 / Math.PI;
}
================================================
FILE: src/inputjs/get-center.js
================================================
import { round } from '../utils/utils-consts';
/**
* @private
* get the center of all the pointers
* @param {Array} pointers
* @return {Object} center contains `x` and `y` properties
*/
export default function getCenter(pointers) {
let pointersLength = pointers.length;
// no need to loop when only one touch
if (pointersLength === 1) {
return {
x: round(pointers[0].clientX),
y: round(pointers[0].clientY)
};
}
let x = 0;
let y = 0;
let i = 0;
while (i < pointersLength) {
x += pointers[i].clientX;
y += pointers[i].clientY;
i++;
}
return {
x: round(x / pointersLength),
y: round(y / pointersLength)
};
}
================================================
FILE: src/inputjs/get-direction.js
================================================
import { abs } from '../utils/utils-consts';
import { DIRECTION_NONE,DIRECTION_LEFT,DIRECTION_RIGHT,DIRECTION_UP,DIRECTION_DOWN } from './input-consts';
/**
* @private
* get the direction between two points
* @param {Number} x
* @param {Number} y
* @return {Number} direction
*/
export default function getDirection(x, y) {
if (x === y) {
return DIRECTION_NONE;
}
if (abs(x) >= abs(y)) {
return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
}
return y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
}
================================================
FILE: src/inputjs/get-distance.js
================================================
import { PROPS_XY } from './input-consts';
/**
* @private
* calculate the absolute distance between two points
* @param {Object} p1 {x, y}
* @param {Object} p2 {x, y}
* @param {Array} [props] containing x and y keys
* @return {Number} distance
*/
export default function getDistance(p1, p2, props) {
if (!props) {
props = PROPS_XY;
}
let x = p2[props[0]] - p1[props[0]];
let y = p2[props[1]] - p1[props[1]];
return Math.sqrt((x * x) + (y * y));
}
================================================
FILE: src/inputjs/get-rotation.js
================================================
import getAngle from './get-angle';
import { PROPS_CLIENT_XY } from './input-consts';
/**
* @private
* calculate the rotation degrees between two pointersets
* @param {Array} start array of pointers
* @param {Array} end array of pointers
* @return {Number} rotation
*/
export default function getRotation(start, end) {
return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY);
}
================================================
FILE: src/inputjs/get-scale.js
================================================
import { PROPS_CLIENT_XY } from './input-consts';
import getDistance from './get-distance';
/**
* @private
* calculate the scale factor between two pointersets
* no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
* @param {Array} start array of pointers
* @param {Array} end array of pointers
* @return {Number} scale
*/
export default function getScale(start, end) {
return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
}
================================================
FILE: src/inputjs/get-velocity.js
================================================
/**
* @private
* calculate the velocity between two points. unit is in px per ms.
* @param {Number} deltaTime
* @param {Number} x
* @param {Number} y
* @return {Object} velocity `x` and `y`
*/
export default function getVelocity(deltaTime, x, y) {
return {
x: x / deltaTime || 0,
y: y / deltaTime || 0
};
}
================================================
FILE: src/inputjs/input-constructor.js
================================================
import boolOrFn from '../utils/bool-or-fn';
import addEventListeners from '../utils/add-event-listeners';
import removeEventListeners from '../utils/remove-event-listeners';
import getWindowForElement from '../utils/get-window-for-element';
/**
* @private
* create new input type manager
* @param {Manager} manager
* @param {Function} callback
* @returns {Input}
* @constructor
*/
export default class Input {
constructor(manager, callback) {
let self = this;
this.manager = manager;
this.callback = callback;
this.element = manager.element;
this.target = manager.options.inputTarget;
// smaller wrapper around the handler, for the scope and the enabled state of the manager,
// so when disabled the input events are completely bypassed.
this.domHandler = function(ev) {
if (boolOrFn(manager.options.enable, [manager])) {
self.handler(ev);
}
};
this.init();
}
/**
* @private
* should handle the inputEvent data and trigger the callback
* @virtual
*/
handler() { }
/**
* @private
* bind the events
*/
init() {
this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
}
/**
* @private
* unbind the events
*/
destroy() {
this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
}
}
================================================
FILE: src/inputjs/input-consts.js
================================================
import prefixed from '../utils/prefixed';
const MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
const SUPPORT_TOUCH = ('ontouchstart' in window);
const SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;
const SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
const INPUT_TYPE_TOUCH = 'touch';
const INPUT_TYPE_PEN = 'pen';
const INPUT_TYPE_MOUSE = 'mouse';
const INPUT_TYPE_KINECT = 'kinect';
const COMPUTE_INTERVAL = 25;
const INPUT_START = 1;
const INPUT_MOVE = 2;
const INPUT_END = 4;
const INPUT_CANCEL = 8;
const DIRECTION_NONE = 1;
const DIRECTION_LEFT = 2;
const DIRECTION_RIGHT = 4;
const DIRECTION_UP = 8;
const DIRECTION_DOWN = 16;
const DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
const DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
const DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
const PROPS_XY = ['x', 'y'];
const PROPS_CLIENT_XY = ['clientX', 'clientY'];
export {
MOBILE_REGEX,
SUPPORT_ONLY_TOUCH,
SUPPORT_POINTER_EVENTS,
SUPPORT_TOUCH,
INPUT_TYPE_KINECT,
INPUT_TYPE_MOUSE,
INPUT_TYPE_PEN,
INPUT_TYPE_TOUCH,
COMPUTE_INTERVAL,
INPUT_START,
INPUT_MOVE,
INPUT_END,
INPUT_CANCEL,
DIRECTION_NONE,
DIRECTION_LEFT,
DIRECTION_RIGHT,
DIRECTION_UP,
DIRECTION_DOWN,
DIRECTION_HORIZONTAL,
DIRECTION_VERTICAL,
DIRECTION_ALL,
PROPS_XY,
PROPS_CLIENT_XY
};
================================================
FILE: src/inputjs/input-handler.js
================================================
import { INPUT_START,INPUT_END,INPUT_CANCEL } from './input-consts';
import computeInputData from './compute-input-data';
/**
* @private
* handle input events
* @param {Manager} manager
* @param {String} eventType
* @param {Object} input
*/
export default function inputHandler(manager, eventType, input) {
let pointersLen = input.pointers.length;
let changedPointersLen = input.changedPointers.length;
let isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));
let isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));
input.isFirst = !!isFirst;
input.isFinal = !!isFinal;
if (isFirst) {
manager.session = {};
}
// source event is the normalized value of the domEvents
// like 'touchstart, mouseup, pointerdown'
input.eventType = eventType;
// compute scale, rotation etc
computeInputData(manager, input);
// emit secret event
manager.emit('hammer.input', input);
manager.recognize(input);
manager.session.prevInput = input;
}
================================================
FILE: src/inputjs/simple-clone-input-data.js
================================================
import { now,round } from '../utils/utils-consts';
import getCenter from './get-center';
/**
* @private
* create a simple clone from the input used for storage of firstInput and firstMultiple
* @param {Object} input
* @returns {Object} clonedInputData
*/
export default function simpleCloneInputData(input) {
// make a simple copy of the pointers because we will get a reference if we don't
// we only need clientXY for the calculations
let pointers = [];
let i = 0;
while (i < input.pointers.length) {
pointers[i] = {
clientX: round(input.pointers[i].clientX),
clientY: round(input.pointers[i].clientY)
};
i++;
}
return {
timeStamp: now(),
pointers,
center: getCenter(pointers),
deltaX: input.deltaX,
deltaY: input.deltaY
};
}
================================================
FILE: src/main.js
================================================
import Hammer from './hammer';
import assign from './utils/assign';
import {
INPUT_START,
INPUT_MOVE,
INPUT_END,
INPUT_CANCEL
} from './inputjs/input-consts';
import {
STATE_POSSIBLE,
STATE_BEGAN,
STATE_CHANGED,
STATE_ENDED,
STATE_RECOGNIZED,
STATE_CANCELLED,
STATE_FAILED
} from './recognizerjs/recognizer-consts';
import {
DIRECTION_NONE,
DIRECTION_LEFT,
DIRECTION_RIGHT,
DIRECTION_UP,
DIRECTION_DOWN,
DIRECTION_HORIZONTAL,
DIRECTION_VERTICAL,
DIRECTION_ALL
} from './inputjs/input-consts';
import Manager from './manager';
import Input from './inputjs/input-constructor';
import TouchAction from './touchactionjs/touchaction-constructor';
import TouchInput from './input/touch';
import MouseInput from './input/mouse';
import PointerEventInput from './input/pointerevent';
import SingleTouchInput from './input/singletouch';
import TouchMouseInput from './input/touchmouse';
import Recognizer from './recognizerjs/recognizer-constructor';
import AttrRecognizer from './recognizers/attribute';
import TapRecognizer from './recognizers/tap';
import PanRecognizer from './recognizers/pan';
import SwipeRecognizer from './recognizers/swipe';
import PinchRecognizer from './recognizers/pinch';
import RotateRecognizer from './recognizers/rotate';
import PressRecognizer from './recognizers/press';
import addEventListeners from './utils/add-event-listeners';
import removeEventListeners from './utils/remove-event-listeners';
import each from './utils/each';
import merge from './utils/merge';
import extend from './utils/extend';
import inherit from './utils/inherit';
import bindFn from './utils/bind-fn';
import prefixed from './utils/prefixed';
import toArray from'./utils/to-array';
import uniqueArray from'./utils/unique-array';
import splitStr from'./utils/split-str';
import inArray from'./utils/in-array';
import boolOrFn from'./utils/bool-or-fn';
import hasParent from'./utils/has-parent';
// this prevents errors when Hammer is loaded in the presence of an AMD
// style loader but by script tag, not by the loader.
assign(Hammer, {
INPUT_START,
INPUT_MOVE,
INPUT_END,
INPUT_CANCEL,
STATE_POSSIBLE,
STATE_BEGAN,
STATE_CHANGED,
STATE_ENDED,
STATE_RECOGNIZED,
STATE_CANCELLED,
STATE_FAILED,
DIRECTION_NONE,
DIRECTION_LEFT,
DIRECTION_RIGHT,
DIRECTION_UP,
DIRECTION_DOWN,
DIRECTION_HORIZONTAL,
DIRECTION_VERTICAL,
DIRECTION_ALL,
Manager,
Input,
TouchAction,
TouchInput,
MouseInput,
PointerEventInput,
TouchMouseInput,
SingleTouchInput,
Recognizer,
AttrRecognizer,
Tap: TapRecognizer,
Pan: PanRecognizer,
Swipe: SwipeRecognizer,
Pinch: PinchRecognizer,
Rotate: RotateRecognizer,
Press: PressRecognizer,
on: addEventListeners,
off: removeEventListeners,
each,
merge,
extend,
assign,
inherit,
bindFn,
prefixed,
toArray,
inArray,
uniqueArray,
splitStr,
boolOrFn,
hasParent,
addEventListeners,
removeEventListeners
});
let freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line
freeGlobal.Hammer = Hammer;
/* jshint ignore:start */
if (typeof define === 'function' && define.amd) {
define(() => {
return Hammer;
});
} else if (typeof module !== 'undefined' && module.exports) {
module.exports = Hammer;
} else {
window[exportName] = Hammer;
}
/* jshint ignore:end */
================================================
FILE: src/manager.js
================================================
import assign from './utils/assign';
import Hammer from './hammer';
import TouchAction from './touchactionjs/touchaction-constructor';
import createInputInstance from './inputjs/create-input-instance';
import each from './utils/each';
import inArray from './utils/in-array';
import invokeArrayArg from './utils/invoke-array-arg';
import splitStr from './utils/split-str';
import prefixed from './utils/prefixed';
import Recognizer from './recognizerjs/recognizer-constructor';
import {
STATE_BEGAN,
STATE_ENDED,
STATE_CHANGED,
STATE_RECOGNIZED
} from './recognizerjs/recognizer-consts';
const STOP = 1;
const FORCED_STOP = 2;
/**
* @private
* Manager
* @param {HTMLElement} element
* @param {Object} [options]
* @constructor
*/
export default class Manager {
constructor(element, options) {
this.options = assign({}, Hammer.defaults, options || {});
this.options.inputTarget = this.options.inputTarget || element;
this.handlers = {};
this.session = {};
this.recognizers = [];
this.oldCssProps = {};
this.element = element;
this.input = createInputInstance(this);
this.touchAction = new TouchAction(this, this.options.touchAction);
toggleCssProps(this, true);
each(this.options.recognizers, (item) => {
let recognizer = this.add(new (item[0])(item[1]));
item[2] && recognizer.recognizeWith(item[2]);
item[3] && recognizer.requireFailure(item[3]);
}, this);
}
/**
* @private
* set options
* @param {Object} options
* @returns {Manager}
*/
set(options) {
assign(this.options, options);
// Options that need a little more setup
if (options.touchAction) {
this.touchAction.update();
}
if (options.inputTarget) {
// Clean up existing event listeners and reinitialize
this.input.destroy();
this.input.target = options.inputTarget;
this.input.init();
}
return this;
}
/**
* @private
* stop recognizing for this session.
* This session will be discarded, when a new [input]start event is fired.
* When forced, the recognizer cycle is stopped immediately.
* @param {Boolean} [force]
*/
stop(force) {
this.session.stopped = force ? FORCED_STOP : STOP;
}
/**
* @private
* run the recognizers!
* called by the inputHandler function on every movement of the pointers (touches)
* it walks through all the recognizers and tries to detect the gesture that is being made
* @param {Object} inputData
*/
recognize(inputData) {
let { session } = this;
if (session.stopped) {
return;
}
// run the touch-action polyfill
this.touchAction.preventDefaults(inputData);
let recognizer;
let { recognizers } = this;
// this holds the recognizer that is being recognized.
// so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
// if no recognizer is detecting a thing, it is set to `null`
let { curRecognizer } = session;
// reset when the last recognizer is recognized
// or when we're in a new session
if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) {
curRecognizer = session.curRecognizer = null;
}
let i = 0;
while (i < recognizers.length) {
recognizer = recognizers[i];
// find out if we are allowed try to recognize the input for this one.
// 1. allow if the session is NOT forced stopped (see the .stop() method)
// 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
// that is being recognized.
// 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
// this can be setup with the `recognizeWith()` method on the recognizer.
if (session.stopped !== FORCED_STOP && (// 1
!curRecognizer || recognizer === curRecognizer || // 2
recognizer.canRecognizeWith(curRecognizer))) { // 3
recognizer.recognize(inputData);
} else {
recognizer.reset();
}
// if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
// current active recognizer. but only if we don't already have an active recognizer
if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
curRecognizer = session.curRecognizer = recognizer;
}
i++;
}
}
/**
* @private
* get a recognizer by its event name.
* @param {Recognizer|String} recognizer
* @returns {Recognizer|Null}
*/
get(recognizer) {
if (recognizer instanceof Recognizer) {
return recognizer;
}
let { recognizers } = this;
for (let i = 0; i < recognizers.length; i++) {
if (recognizers[i].options.event === recognizer) {
return recognizers[i];
}
}
return null;
}
/**
* @private add a recognizer to the manager
* existing recognizers with the same event name will be removed
* @param {Recognizer} recognizer
* @returns {Recognizer|Manager}
*/
add(recognizer) {
if (invokeArrayArg(recognizer, 'add', this)) {
return this;
}
// remove existing
let existing = this.get(recognizer.options.event);
if (existing) {
this.remove(existing);
}
this.recognizers.push(recognizer);
recognizer.manager = this;
this.touchAction.update();
return recognizer;
}
/**
* @private
* remove a recognizer by name or instance
* @param {Recognizer|String} recognizer
* @returns {Manager}
*/
remove(recognizer) {
if (invokeArrayArg(recognizer, 'remove', this)) {
return this;
}
recognizer = this.get(recognizer);
// let's make sure this recognizer exists
if (recognizer) {
let { recognizers } = this;
let index = inArray(recognizers, recognizer);
if (index !== -1) {
recognizers.splice(index, 1);
this.touchAction.update();
}
}
return this;
}
/**
* @private
* bind event
* @param {String} events
* @param {Function} handler
* @returns {EventEmitter} this
*/
on(events, handler) {
if (events === undefined) {
return;
}
if (handler === undefined) {
return;
}
let { handlers } = this;
each(splitStr(events), (event) => {
handlers[event] = handlers[event] || [];
handlers[event].push(handler);
});
return this;
}
/**
* @private unbind event, leave emit blank to remove all handlers
* @param {String} events
* @param {Function} [handler]
* @returns {EventEmitter} this
*/
off(events, handler) {
if (events === undefined) {
return;
}
let { handlers } = this;
each(splitStr(events), (event) => {
if (!handler) {
delete handlers[event];
} else {
handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1);
}
});
return this;
}
/**
* @private emit event to the listeners
* @param {String} event
* @param {Object} data
*/
emit(event, data) {
// we also want to trigger dom events
if (this.options.domEvents) {
triggerDomEvent(event, data);
}
// no handlers, so skip it all
let handlers = this.handlers[event] && this.handlers[event].slice();
if (!handlers || !handlers.length) {
return;
}
data.type = event;
data.preventDefault = function() {
data.srcEvent.preventDefault();
};
let i = 0;
while (i < handlers.length) {
handlers[i](data);
i++;
}
}
/**
* @private
* destroy the manager and unbinds all events
* it doesn't unbind dom events, that is the user own responsibility
*/
destroy() {
this.element && toggleCssProps(this, false);
this.handlers = {};
this.session = {};
this.input.destroy();
this.element = null;
}
}
/**
* @private
* add/remove the css properties as defined in manager.options.cssProps
* @param {Manager} manager
* @param {Boolean} add
*/
function toggleCssProps(manager, add) {
let { element } = manager;
if (!element.style) {
return;
}
let prop;
each(manager.options.cssProps, (value, name) => {
prop = prefixed(element.style, name);
if (add) {
manager.oldCssProps[prop] = element.style[prop];
element.style[prop] = value;
} else {
element.style[prop] = manager.oldCssProps[prop] || '';
}
});
if (!add) {
manager.oldCssProps = {};
}
}
/**
* @private
* trigger dom event
* @param {String} event
* @param {Object} data
*/
function triggerDomEvent(event, data) {
let gestureEvent = document.createEvent('Event');
gestureEvent.initEvent(event, true, true);
gestureEvent.gesture = data;
data.target.dispatchEvent(gestureEvent);
}
================================================
FILE: src/recognizerjs/direction-str.js
================================================
import {
DIRECTION_LEFT,
DIRECTION_RIGHT,
DIRECTION_UP,
DIRECTION_DOWN
} from '../inputjs/input-consts';
/**
* @private
* direction cons to string
* @param {constant} direction
* @returns {String}
*/
export default function directionStr(direction) {
if (direction === DIRECTION_DOWN) {
return 'down';
} else if (direction === DIRECTION_UP) {
return 'up';
} else if (direction === DIRECTION_LEFT) {
return 'left';
} else if (direction === DIRECTION_RIGHT) {
return 'right';
}
return '';
}
================================================
FILE: src/recognizerjs/get-recognizer-by-name-if-manager.js
================================================
/**
* @private
* get a recognizer by name if it is bound to a manager
* @param {Recognizer|String} otherRecognizer
* @param {Recognizer} recognizer
* @returns {Recognizer}
*/
export default function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
let { manager } = recognizer;
if (manager) {
return manager.get(otherRecognizer);
}
return otherRecognizer;
}
================================================
FILE: src/recognizerjs/recognizer-constructor.js
================================================
import {
STATE_POSSIBLE,
STATE_ENDED,
STATE_FAILED,
STATE_RECOGNIZED,
STATE_CANCELLED,
STATE_BEGAN,
STATE_CHANGED
} from './recognizer-consts';
import assign from '../utils/assign';
import uniqueId from '../utils/unique-id';
import ifUndefined from '../utils/if-undefined';
import invokeArrayArg from '../utils/invoke-array-arg';
import inArray from '../utils/in-array';
import boolOrFn from '../utils/bool-or-fn';
import getRecognizerByNameIfManager from './get-recognizer-by-name-if-manager';
import stateStr from './state-str';
/**
* @private
* Recognizer flow explained; *
* All recognizers have the initial state of POSSIBLE when a input session starts.
* The definition of a input session is from the first input until the last input, with all it's movement in it. *
* Example session for mouse-input: mousedown -> mousemove -> mouseup
*
* On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
* which determines with state it should be.
*
* If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
* POSSIBLE to give it another change on the next cycle.
*
* Possible
* |
* +-----+---------------+
* | |
* +-----+-----+ |
* | | |
* Failed Cancelled |
* +-------+------+
* | |
* Recognized Began
* |
* Changed
* |
* Ended/Recognized
*/
/**
* @private
* Recognizer
* Every recognizer needs to extend from this class.
* @constructor
* @param {Object} options
*/
export default class Recognizer {
constructor(options) {
this.options = assign({}, this.defaults, options || {});
this.id = uniqueId();
this.manager = null;
// default is enable true
this.options.enable = ifUndefined(this.options.enable, true);
this.state = STATE_POSSIBLE;
this.simultaneous = {};
this.requireFail = [];
}
/**
* @private
* set options
* @param {Object} options
* @return {Recognizer}
*/
set(options) {
assign(this.options, options);
// also update the touchAction, in case something changed about the directions/enabled state
this.manager && this.manager.touchAction.update();
return this;
}
/**
* @private
* recognize simultaneous with an other recognizer.
* @param {Recognizer} otherRecognizer
* @returns {Recognizer} this
*/
recognizeWith(otherRecognizer) {
if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
return this;
}
let { simultaneous } = this;
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
if (!simultaneous[otherRecognizer.id]) {
simultaneous[otherRecognizer.id] = otherRecognizer;
otherRecognizer.recognizeWith(this);
}
return this;
}
/**
* @private
* drop the simultaneous link. it doesnt remove the link on the other recognizer.
* @param {Recognizer} otherRecognizer
* @returns {Recognizer} this
*/
dropRecognizeWith(otherRecognizer) {
if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
return this;
}
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
delete this.simultaneous[otherRecognizer.id];
return this;
}
/**
* @private
* recognizer can only run when an other is failing
* @param {Recognizer} otherRecognizer
* @returns {Recognizer} this
*/
requireFailure(otherRecognizer) {
if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
return this;
}
let { requireFail } = this;
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
if (inArray(requireFail, otherRecognizer) === -1) {
requireFail.push(otherRecognizer);
otherRecognizer.requireFailure(this);
}
return this;
}
/**
* @private
* drop the requireFailure link. it does not remove the link on the other recognizer.
* @param {Recognizer} otherRecognizer
* @returns {Recognizer} this
*/
dropRequireFailure(otherRecognizer) {
if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
return this;
}
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
let index = inArray(this.requireFail, otherRecognizer);
if (index > -1) {
this.requireFail.splice(index, 1);
}
return this;
}
/**
* @private
* has require failures boolean
* @returns {boolean}
*/
hasRequireFailures() {
return this.requireFail.length > 0;
}
/**
* @private
* if the recognizer can recognize simultaneous with an other recognizer
* @param {Recognizer} otherRecognizer
* @returns {Boolean}
*/
canRecognizeWith(otherRecognizer) {
return !!this.simultaneous[otherRecognizer.id];
}
/**
* @private
* You should use `tryEmit` instead of `emit` directly to check
* that all the needed recognizers has failed before emitting.
* @param {Object} input
*/
emit(input) {
let self = this;
let { state } = this;
function emit(event) {
self.manager.emit(event, input);
}
// 'panstart' and 'panmove'
if (state < STATE_ENDED) {
emit(self.options.event + stateStr(state));
}
emit(self.options.event); // simple 'eventName' events
if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...)
emit(input.additionalEvent);
}
// panend and pancancel
if (state >= STATE_ENDED) {
emit(self.options.event + stateStr(state));
}
}
/**
* @private
* Check that all the require failure recognizers has failed,
* if true, it emits a gesture event,
* otherwise, setup the state to FAILED.
* @param {Object} input
*/
tryEmit(input) {
if (this.canEmit()) {
return this.emit(input);
}
// it's failing anyway
this.state = STATE_FAILED;
}
/**
* @private
* can we emit?
* @returns {boolean}
*/
canEmit() {
let i = 0;
while (i < this.requireFail.length) {
if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
return false;
}
i++;
}
return true;
}
/**
* @private
* update the recognizer
* @param {Object} inputData
*/
recognize(inputData) {
// make a new copy of the inputData
// so we can change the inputData without messing up the other recognizers
let inputDataClone = assign({}, inputData);
// is is enabled and allow recognizing?
if (!boolOrFn(this.options.enable, [this, inputDataClone])) {
this.reset();
this.state = STATE_FAILED;
return;
}
// reset when we've reached the end
if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
this.state = STATE_POSSIBLE;
}
this.state = this.process(inputDataClone);
// the recognizer has recognized a gesture
// so trigger an event
if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
this.tryEmit(inputDataClone);
}
}
/**
* @private
* return the state of the recognizer
* the actual recognizing happens in this method
* @virtual
* @param {Object} inputData
* @returns {constant} STATE
*/
/* jshint ignore:start */
process(inputData) { }
/* jshint ignore:end */
/**
* @private
* return the preferred touch-action
* @virtual
* @returns {Array}
*/
getTouchAction() { }
/**
* @private
* called when the gesture isn't allowed to recognize
* like when another is being recognized or it is disabled
* @virtual
*/
reset() { }
}
Recognizer.prototype.defaults = {};
================================================
FILE: src/recognizerjs/recognizer-consts.js
================================================
const STATE_POSSIBLE = 1;
const STATE_BEGAN = 2;
const STATE_CHANGED = 4;
const STATE_ENDED = 8;
const STATE_RECOGNIZED = STATE_ENDED;
const STATE_CANCELLED = 16;
const STATE_FAILED = 32;
export {
STATE_POSSIBLE,
STATE_BEGAN,
STATE_CHANGED,
STATE_ENDED,
STATE_RECOGNIZED,
STATE_CANCELLED,
STATE_FAILED
};
================================================
FILE: src/recognizerjs/state-str.js
================================================
import {
STATE_CANCELLED,
STATE_ENDED,
STATE_CHANGED,
STATE_BEGAN
} from './recognizer-consts';
/**
* @private
* get a usable string, used as event postfix
* @param {constant} state
* @returns {String} state
*/
export default function stateStr(state) {
if (state & STATE_CANCELLED) {
return 'cancel';
} else if (state & STATE_ENDED) {
return 'end';
} else if (state & STATE_CHANGED) {
return 'move';
} else if (state & STATE_BEGAN) {
return 'start';
}
return '';
}
================================================
FILE: src/recognizers/attribute.js
================================================
import Recognizer from '../recognizerjs/recognizer-constructor';
import {
STATE_BEGAN,
STATE_CHANGED,
STATE_CANCELLED,
STATE_ENDED,
STATE_FAILED
} from '../recognizerjs/recognizer-consts';
import {
INPUT_CANCEL,
INPUT_END
} from '../inputjs/input-consts';
/**
* @private
* This recognizer is just used as a base for the simple attribute recognizers.
* @constructor
* @extends Recognizer
*/
export default class AttrRecognizer extends Recognizer {
constructor() {
super(...arguments);
}
/**
* @private
* Used to check if it the recognizer receives valid input, like input.distance > 10.
* @memberof AttrRecognizer
* @param {Object} input
* @returns {Boolean} recognized
*/
attrTest(input) {
let optionPointers = this.options.pointers;
return optionPointers === 0 || input.pointers.length === optionPointers;
}
/**
* @private
* Process the input and return the state for the recognizer
* @memberof AttrRecognizer
* @param {Object} input
* @returns {*} State
*/
process(input) {
let { state } = this;
let { eventType } = input;
let isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
let isValid = this.attrTest(input);
// on cancel input and we've recognized before, return STATE_CANCELLED
if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
return state | STATE_CANCELLED;
} else if (isRecognized || isValid) {
if (eventType & INPUT_END) {
return state | STATE_ENDED;
} else if (!(state & STATE_BEGAN)) {
return STATE_BEGAN;
}
return state | STATE_CHANGED;
}
return STATE_FAILED;
}
}
AttrRecognizer.prototype.defaults = {
/**
* @private
* @type {Number}
* @default 1
*/
pointers: 1
};
================================================
FILE: src/recognizers/pan.js
================================================
import AttrRecognizer from './attribute';
import {
DIRECTION_ALL,
DIRECTION_HORIZONTAL,
DIRECTION_VERTICAL,
DIRECTION_NONE,
DIRECTION_UP,
DIRECTION_DOWN,
DIRECTION_LEFT,
DIRECTION_RIGHT
} from '../inputjs/input-consts';
import { STATE_BEGAN } from '../recognizerjs/recognizer-consts';
import { TOUCH_ACTION_PAN_X,TOUCH_ACTION_PAN_Y } from '../touchactionjs/touchaction-Consts';
import directionStr from '../recognizerjs/direction-str';
/**
* @private
* Pan
* Recognized when the pointer is down and moved in the allowed direction.
* @constructor
* @extends AttrRecognizer
*/
export default class PanRecognizer extends AttrRecognizer {
constructor() {
super(...arguments);
this.pX = null;
this.pY = null;
}
getTouchAction() {
let { options:{ direction } } = this;
let actions = [];
if (direction & DIRECTION_HORIZONTAL) {
actions.push(TOUCH_ACTION_PAN_Y);
}
if (direction & DIRECTION_VERTICAL) {
actions.push(TOUCH_ACTION_PAN_X);
}
return actions;
}
directionTest(input) {
let { options } = this;
let hasMoved = true;
let { distance } = input;
let { direction } = input;
let x = input.deltaX;
let y = input.deltaY;
// lock to axis?
if (!(direction & options.direction)) {
if (options.direction & DIRECTION_HORIZONTAL) {
direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;
hasMoved = x !== this.pX;
distance = Math.abs(input.deltaX);
} else {
direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN;
hasMoved = y !== this.pY;
distance = Math.abs(input.deltaY);
}
}
input.direction = direction;
return hasMoved && distance > options.threshold && direction & options.direction;
}
attrTest(input) {
return AttrRecognizer.prototype.attrTest.call(this, input) && // replace with a super call
(this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input)));
}
emit(input) {
this.pX = input.deltaX;
this.pY = input.deltaY;
let direction = directionStr(input.direction);
if (direction) {
input.additionalEvent = this.options.event + direction;
}
super.emit(input);
}
}
PanRecognizer.prototype.defaults = {
event: 'pan',
threshold: 10,
pointers: 1,
direction: DIRECTION_ALL
};
================================================
FILE: src/recognizers/pinch.js
================================================
import AttrRecognizer from './attribute';
import { TOUCH_ACTION_NONE } from '../touchactionjs/touchaction-Consts';
import { STATE_BEGAN } from '../recognizerjs/recognizer-consts';
/**
* @private
* Pinch
* Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
* @constructor
* @extends AttrRecognizer
*/
export default class PinchRecognizer extends AttrRecognizer {
constructor() {
super(...arguments);
}
getTouchAction() {
return [TOUCH_ACTION_NONE];
}
attrTest(input) {
return super.attrTest(input) &&
(Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
}
emit(input) {
if (input.scale !== 1) {
let inOut = input.scale < 1 ? 'in' : 'out';
input.additionalEvent = this.options.event + inOut;
}
super.emit(input);
}
}
PinchRecognizer.prototype.defaults = {
event: 'pinch',
threshold: 0,
pointers: 2
};
================================================
FILE: src/recognizers/press.js
================================================
import Recognizer from '../recognizerjs/recognizer-constructor';
import {
STATE_RECOGNIZED,
STATE_FAILED
} from '../recognizerjs/recognizer-consts';
import { now } from '../utils/utils-consts';
import setTimeoutContext from '../utils/set-timeout-context';
import { TOUCH_ACTION_AUTO } from '../touchactionjs/touchaction-Consts';
import {
INPUT_START,
INPUT_END,
INPUT_CANCEL
} from '../inputjs/input-consts';
/**
* @private
* Press
* Recognized when the pointer is down for x ms without any movement.
* @constructor
* @extends Recognizer
*/
export default class PressRecognizer extends Recognizer {
constructor() {
super(...arguments);
this._timer = null;
this._input = null;
}
getTouchAction() {
return [TOUCH_ACTION_AUTO];
}
process(input) {
let { options } = this;
let validPointers = input.pointers.length === options.pointers;
let validMovement = input.distance < options.threshold;
let validTime = input.deltaTime > options.time;
this._input = input;
// we only allow little movement
// and we've reached an end event, so a tap is possible
if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) {
this.reset();
} else if (input.eventType & INPUT_START) {
this.reset();
this._timer = setTimeoutContext(() => {
this.state = STATE_RECOGNIZED;
this.tryEmit();
}, options.time, this);
} else if (input.eventType & INPUT_END) {
return STATE_RECOGNIZED;
}
return STATE_FAILED;
}
reset() {
clearTimeout(this._timer);
}
emit(input) {
if (this.state !== STATE_RECOGNIZED) {
return;
}
if (input && (input.eventType & INPUT_END)) {
this.manager.emit(`${this.options.event}up`, input);
} else {
this._input.timeStamp = now();
this.manager.emit(this.options.event, this._input);
}
}
}
PressRecognizer.prototype.defaults = {
event: 'press',
pointers: 1,
time: 251, // minimal time of the pointer to be pressed
threshold: 9 // a minimal movement is ok, but keep it low
};
================================================
FILE: src/recognizers/rotate.js
================================================
import AttrRecognizer from './attribute';
import { TOUCH_ACTION_NONE } from '../touchactionjs/touchaction-Consts';
import { STATE_BEGAN } from '../recognizerjs/recognizer-consts';
/**
* @private
* Rotate
* Recognized when two or more pointer are moving in a circular motion.
* @constructor
* @extends AttrRecognizer
*/
export default class RotateRecognizer extends AttrRecognizer {
constructor() {
super(...arguments);
}
getTouchAction() {
return [TOUCH_ACTION_NONE];
}
attrTest(input) {
return super.attrTest(input) &&
(Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
}
}
RotateRecognizer.prototype.defaults = {
event: 'rotate',
threshold: 0,
pointers: 2
};
================================================
FILE: src/recognizers/swipe.js
================================================
import AttrRecognizer from '../recognizers/attribute';
import { abs } from '../utils/utils-consts';
import { DIRECTION_HORIZONTAL,DIRECTION_VERTICAL } from '../inputjs/input-consts';
import PanRecognizer from './pan';
import { INPUT_END } from '../inputjs/input-consts';
import directionStr from '../recognizerjs/direction-str';
/**
* @private
* Swipe
* Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
* @constructor
* @extends AttrRecognizer
*/
export default class SwipeRecognizer extends AttrRecognizer {
constructor() {
super(...arguments);
}
getTouchAction() {
return PanRecognizer.prototype.getTouchAction.call(this);
}
attrTest(input) {
let { direction } = this.options;
let velocity;
if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
velocity = input.overallVelocity;
} else if (direction & DIRECTION_HORIZONTAL) {
velocity = input.overallVelocityX;
} else if (direction & DIRECTION_VERTICAL) {
velocity = input.overallVelocityY;
}
return super.attrTest(input) &&
direction & input.offsetDirection &&
input.distance > this.options.threshold &&
input.maxPointers === this.options.pointers &&
abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
}
emit(input) {
let direction = directionStr(input.offsetDirection);
if (direction) {
this.manager.emit(this.options.event + direction, input);
}
this.manager.emit(this.options.event, input);
}
}
SwipeRecognizer.prototype.defaults = {
event: 'swipe',
threshold: 10,
velocity: 0.3,
direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
pointers: 1
};
================================================
FILE: src/recognizers/tap.js
================================================
import setTimeoutContext from '../utils/set-timeout-context';
import Recognizer from '../recognizerjs/recognizer-constructor';
import { TOUCH_ACTION_MANIPULATION } from '../touchactionjs/touchaction-Consts';
import {INPUT_START,INPUT_END } from '../inputjs/input-consts';
import {
STATE_RECOGNIZED,
STATE_BEGAN,
STATE_FAILED
} from '../recognizerjs/recognizer-consts';
import getDistance from '../inputjs/get-distance';
/**
* @private
* A tap is recognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
* between the given interval and position. The delay option can be used to recognize multi-taps without firing
* a single tap.
*
* The eventData from the emitted event contains the property `tapCount`, which contains the amount of
* multi-taps being recognized.
* @constructor
* @extends Recognizer
*/
export default class TapRecognizer extends Recognizer {
constructor() {
super(...arguments);
// previous time and center,
// used for tap counting
this.pTime = false;
this.pCenter = false;
this._timer = null;
this._input = null;
this.count = 0;
}
getTouchAction() {
return [TOUCH_ACTION_MANIPULATION];
}
process(input) {
let { options } = this;
let validPointers = input.pointers.length === options.pointers;
let validMovement = input.distance < options.threshold;
let validTouchTime = input.deltaTime < options.time;
this.reset();
if ((input.eventType & INPUT_START) && (this.count === 0)) {
return this.failTimeout();
}
// we only allow little movement
// and we've reached an end event, so a tap is possible
if (validMovement && validTouchTime && validPointers) {
if (input.eventType !== INPUT_END) {
return this.failTimeout();
}
let validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true;
let validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;
this.pTime = input.timeStamp;
this.pCenter = input.center;
if (!validMultiTap || !validInterval) {
this.count = 1;
} else {
this.count += 1;
}
this._input = input;
// if tap count matches we have recognized it,
// else it has began recognizing...
let tapCount = this.count % options.taps;
if (tapCount === 0) {
// no failing requirements, immediately trigger the tap event
// or wait as long as the multitap interval to trigger
if (!this.hasRequireFailures()) {
return STATE_RECOGNIZED;
} else {
this._timer = setTimeoutContext(() => {
this.state = STATE_RECOGNIZED;
this.tryEmit();
}, options.interval, this);
return STATE_BEGAN;
}
}
}
return STATE_FAILED;
}
failTimeout() {
this._timer = setTimeoutContext(() => {
this.state = STATE_FAILED;
}, this.options.interval, this);
return STATE_FAILED;
}
reset() {
clearTimeout(this._timer);
}
emit() {
if (this.state === STATE_RECOGNIZED) {
this._input.tapCount = this.count;
this.manager.emit(this.options.event, this._input);
}
}
}
TapRecognizer.prototype.defaults = {
event: 'tap',
pointers: 1,
taps: 1,
interval: 300, // max time between the multi-tap taps
time: 250, // max time of the pointer to be down (like finger on the screen)
threshold: 9, // a minimal movement is ok, but keep it low
posThreshold: 10 // a multi-tap can be a bit off the initial position
};
================================================
FILE: src/touchactionjs/clean-touch-actions.js
================================================
import inStr from '../utils/in-str';
import {
TOUCH_ACTION_NONE,
TOUCH_ACTION_PAN_X,
TOUCH_ACTION_PAN_Y,
TOUCH_ACTION_MANIPULATION,
TOUCH_ACTION_AUTO
} from './touchaction-Consts';
/**
* @private
* when the touchActions are collected they are not a valid value, so we need to clean things up. *
* @param {String} actions
* @returns {*}
*/
export default function cleanTouchActions(actions) {
// none
if (inStr(actions, TOUCH_ACTION_NONE)) {
return TOUCH_ACTION_NONE;
}
let hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
let hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
// if both pan-x and pan-y are set (different recognizers
// for different directions, e.g. horizontal pan but vertical swipe?)
// we need none (as otherwise with pan-x pan-y combined none of these
// recognizers will work, since the browser would handle all panning
if (hasPanX && hasPanY) {
return TOUCH_ACTION_NONE;
}
// pan-x OR pan-y
if (hasPanX || hasPanY) {
return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
}
// manipulation
if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
return TOUCH_ACTION_MANIPULATION;
}
return TOUCH_ACTION_AUTO;
}
================================================
FILE: src/touchactionjs/get-touchaction-props.js
================================================
import { NATIVE_TOUCH_ACTION } from './touchaction-Consts';
export default function getTouchActionProps() {
if (!NATIVE_TOUCH_ACTION) {
return false;
}
let touchMap = {};
let cssSupports = window.CSS && window.CSS.supports;
['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach((val) => {
// If css.supports is not supported but there is native touch-action assume it supports
// all values. This is the case for IE 10 and 11.
return touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true;
});
return touchMap;
}
================================================
FILE: src/touchactionjs/touchaction-Consts.js
================================================
import prefixed from '../utils/prefixed';
import { TEST_ELEMENT } from '../utils/utils-consts';
import getTouchActionProps from './get-touchaction-props';
const PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
const NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;
// magical touchAction value
const TOUCH_ACTION_COMPUTE = 'compute';
const TOUCH_ACTION_AUTO = 'auto';
const TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented
const TOUCH_ACTION_NONE = 'none';
const TOUCH_ACTION_PAN_X = 'pan-x';
const TOUCH_ACTION_PAN_Y = 'pan-y';
const TOUCH_ACTION_MAP = getTouchActionProps();
export {
PREFIXED_TOUCH_ACTION,
NATIVE_TOUCH_ACTION,
TOUCH_ACTION_AUTO,
TOUCH_ACTION_COMPUTE,
TOUCH_ACTION_MANIPULATION,
TOUCH_ACTION_NONE,
TOUCH_ACTION_PAN_X,
TOUCH_ACTION_PAN_Y,
TOUCH_ACTION_MAP
};
================================================
FILE: src/touchactionjs/touchaction-constructor.js
================================================
import {
TOUCH_ACTION_COMPUTE,
TOUCH_ACTION_MAP,
NATIVE_TOUCH_ACTION,
PREFIXED_TOUCH_ACTION,
TOUCH_ACTION_NONE,
TOUCH_ACTION_PAN_X,
TOUCH_ACTION_PAN_Y
} from './touchaction-Consts';
import {
DIRECTION_VERTICAL,
DIRECTION_HORIZONTAL
} from '../inputjs/input-consts';
import each from '../utils/each';
import boolOrFn from '../utils/bool-or-fn';
import inStr from '../utils/in-str';
import cleanTouchActions from './clean-touch-actions';
/**
* @private
* Touch Action
* sets the touchAction property or uses the js alternative
* @param {Manager} manager
* @param {String} value
* @constructor
*/
export default class TouchAction {
constructor(manager, value) {
this.manager = manager;
this.set(value);
}
/**
* @private
* set the touchAction value on the element or enable the polyfill
* @param {String} value
*/
set(value) {
// find out the touch-action by the event handlers
if (value === TOUCH_ACTION_COMPUTE) {
value = this.compute();
}
if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) {
this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
}
this.actions = value.toLowerCase().trim();
}
/**
* @private
* just re-set the touchAction value
*/
update() {
this.set(this.manager.options.touchAction);
}
/**
* @private
* compute the value for the touchAction property based on the recognizer's settings
* @returns {String} value
*/
compute() {
let actions = [];
each(this.manager.recognizers, (recognizer) => {
if (boolOrFn(recognizer.options.enable, [recognizer])) {
actions = actions.concat(recognizer.getTouchAction());
}
});
return cleanTouchActions(actions.join(' '));
}
/**
* @private
* this method is called on each input cycle and provides the preventing of the browser behavior
* @param {Object} input
*/
preventDefaults(input) {
let { srcEvent } = input;
let direction = input.offsetDirection;
// if the touch action did prevented once this session
if (this.manager.session.prevented) {
srcEvent.preventDefault();
return;
}
let { actions } = this;
let hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE];
let hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y];
let hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X];
if (hasNone) {
// do not prevent defaults if this is a tap gesture
let isTapPointer = input.pointers.length === 1;
let isTapMovement = input.distance < 2;
let isTapTouchTime = input.deltaTime < 250;
if (isTapPointer && isTapMovement && isTapTouchTime) {
return;
}
}
if (hasPanX && hasPanY) {
// `pan-x pan-y` means browser handles all scrolling/panning, do not prevent
return;
}
if (hasNone ||
(hasPanY && direction & DIRECTION_HORIZONTAL) ||
(hasPanX && direction & DIRECTION_VERTICAL)) {
return this.preventSrc(srcEvent);
}
}
/**
* @private
* call preventDefault to prevent the browser's default behavior (scrolling in most cases)
* @param {Object} srcEvent
*/
preventSrc(srcEvent) {
this.manager.session.prevented = true;
srcEvent.preventDefault();
}
}
================================================
FILE: src/utils/add-event-listeners.js
================================================
import each from './each';
import splitStr from './split-str';
/**
* @private
* addEventListener with multiple events at once
* @param {EventTarget} target
* @param {String} types
* @param {Function} handler
*/
export default function addEventListeners(target, types, handler) {
each(splitStr(types), (type) => {
target.addEventListener(type, handler, false);
});
}
================================================
FILE: src/utils/assign.js
================================================
/**
* @private
* extend object.
* means that properties in dest will be overwritten by the ones in src.
* @param {Object} target
* @param {...Object} objects_to_assign
* @returns {Object} target
*/
let assign;
if (typeof Object.assign !== 'function') {
assign = function assign(target) {
if (target === undefined || target === null) {
throw new TypeError('Cannot convert undefined or null to object');
}
let output = Object(target);
for (let index = 1; index < arguments.length; index++) {
const source = arguments[index];
if (source !== undefined && source !== null) {
for (const nextKey in source) {
if (source.hasOwnProperty(nextKey)) {
output[nextKey] = source[nextKey];
}
}
}
}
return output;
};
} else {
assign = Object.assign;
}
export default assign;
================================================
FILE: src/utils/bind-fn.js
================================================
/**
* @private
* simple function bind
* @param {Function} fn
* @param {Object} context
* @returns {Function}
*/
export default function bindFn(fn, context) {
return function boundFn() {
return fn.apply(context, arguments);
};
}
================================================
FILE: src/utils/bool-or-fn.js
================================================
import { TYPE_FUNCTION } from './utils-consts';
/**
* @private
* let a boolean value also be a function that must return a boolean
* this first item in args will be used as the context
* @param {Boolean|Function} val
* @param {Array} [args]
* @returns {Boolean}
*/
export default function boolOrFn(val, args) {
if (typeof val === TYPE_FUNCTION) {
return val.apply(args ? args[0] || undefined : undefined, args);
}
return val;
}
================================================
FILE: src/utils/deprecate.js
================================================
/**
* @private
* wrap a method with a deprecation warning and stack trace
* @param {Function} method
* @param {String} name
* @param {String} message
* @returns {Function} A new function wrapping the supplied method.
*/
export default function deprecate(method, name, message) {
let deprecationMessage = `DEPRECATED METHOD: ${name}\n${message} AT \n`;
return function() {
let e = new Error('get-stack-trace');
let stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '')
.replace(/^\s+at\s+/gm, '')
.replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace';
let log = window.console && (window.console.warn || window.console.log);
if (log) {
log.call(window.console, deprecationMessage, stack);
}
return method.apply(this, arguments);
};
}
================================================
FILE: src/utils/each.js
================================================
/**
* @private
* walk objects and arrays
* @param {Object} obj
* @param {Function} iterator
* @param {Object} context
*/
export default function each(obj, iterator, context) {
let i;
if (!obj) {
return;
}
if (obj.forEach) {
obj.forEach(iterator, context);
} else if (obj.length !== undefined) {
i = 0;
while (i < obj.length) {
iterator.call(context, obj[i], i, obj);
i++;
}
} else {
for (i in obj) {
obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
}
}
}
================================================
FILE: src/utils/extend.js
================================================
import deprecate from './deprecate';
/**
* @private
* extend object.
* means that properties in dest will be overwritten by the ones in src.
* @param {Object} dest
* @param {Object} src
* @param {Boolean} [merge=false]
* @returns {Object} dest
*/
const extend = deprecate((dest, src, merge) => {
let keys = Object.keys(src);
let i = 0;
while (i < keys.length) {
if (!merge || (merge && dest[keys[i]] === undefined)) {
dest[keys[i]] = src[keys[i]];
}
i++;
}
return dest;
}, 'extend', 'Use `assign`.');
export default extend;
================================================
FILE: src/utils/get-window-for-element.js
================================================
/**
* @private
* get the window object of an element
* @param {HTMLElement} element
* @returns {DocumentView|Window}
*/
export default function getWindowForElement(element) {
let doc = element.ownerDocument || element;
return (doc.defaultView || doc.parentWindow || window);
}
================================================
FILE: src/utils/has-parent.js
================================================
/**
* @private
* find if a node is in the given parent
* @method hasParent
* @param {HTMLElement} node
* @param {HTMLElement} parent
* @return {Boolean} found
*/
export default function hasParent(node, parent) {
while (node) {
if (node === parent) {
return true;
}
node = node.parentNode;
}
return false;
}
================================================
FILE: src/utils/if-undefined.js
================================================
/**
* @private
* use the val2 when val1 is undefined
* @param {*} val1
* @param {*} val2
* @returns {*}
*/
export default function ifUndefined(val1, val2) {
return (val1 === undefined) ? val2 : val1;
}
================================================
FILE: src/utils/in-array.js
================================================
/**
* @private
* find if a array contains the object using indexOf or a simple polyFill
* @param {Array} src
* @param {String} find
* @param {String} [findByKey]
* @return {Boolean|Number} false when not found, or the index
*/
export default function inArray(src, find, findByKey) {
if (src.indexOf && !findByKey) {
return src.indexOf(find);
} else {
let i = 0;
while (i < src.length) {
if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) {// do not use === here, test fails
return i;
}
i++;
}
return -1;
}
}
================================================
FILE: src/utils/in-str.js
================================================
/**
* @private
* small indexOf wrapper
* @param {String} str
* @param {String} find
* @returns {Boolean} found
*/
export default function inStr(str, find) {
return str.indexOf(find) > -1;
}
================================================
FILE: src/utils/inherit.js
================================================
import assign from './assign';
/**
* @private
* simple class inheritance
* @param {Function} child
* @param {Function} base
* @param {Object} [properties]
*/
export default function inherit(child, base, properties) {
let baseP = base.prototype;
let childP;
childP = child.prototype = Object.create(baseP);
childP.constructor = child;
childP._super = baseP;
if (properties) {
assign(childP, properties);
}
}
================================================
FILE: src/utils/invoke-array-arg.js
================================================
import each from './each';
/**
* @private
* if the argument is an array, we want to execute the fn on each entry
* if it aint an array we don't want to do a thing.
* this is used by all the methods that accept a single and array argument.
* @param {*|Array} arg
* @param {String} fn
* @param {Object} [context]
* @returns {Boolean}
*/
export default function invokeArrayArg(arg, fn, context) {
if (Array.isArray(arg)) {
each(arg, context[fn], context);
return true;
}
return false;
}
================================================
FILE: src/utils/merge.js
================================================
import deprecate from './deprecate';
import extend from './extend';
/**
* @private
* merge the values from src in the dest.
* means that properties that exist in dest will not be overwritten by src
* @param {Object} dest
* @param {Object} src
* @returns {Object} dest
*/
const merge = deprecate((dest, src) => {
return extend(dest, src, true);
}, 'merge', 'Use `assign`.');
export default merge;
================================================
FILE: src/utils/prefixed.js
================================================
import { VENDOR_PREFIXES } from './utils-consts';
/**
* @private
* get the prefixed property
* @param {Object} obj
* @param {String} property
* @returns {String|Undefined} prefixed
*/
export default function prefixed(obj, property) {
let prefix;
let prop;
let camelProp = property[0].toUpperCase() + property.slice(1);
let i = 0;
while (i < VENDOR_PREFIXES.length) {
prefix = VENDOR_PREFIXES[i];
prop = (prefix) ? prefix + camelProp : property;
if (prop in obj) {
return prop;
}
i++;
}
return undefined;
}
================================================
FILE: src/utils/remove-event-listeners.js
================================================
import each from './each';
import splitStr from './split-str';
/**
* @private
* removeEventListener with multiple events at once
* @param {EventTarget} target
* @param {String} types
* @param {Function} handler
*/
export default function removeEventListeners(target, types, handler) {
each(splitStr(types), (type) => {
target.removeEventListener(type, handler, false);
});
}
================================================
FILE: src/utils/set-timeout-context.js
================================================
import bindFn from './bind-fn';
/**
* @private
* set a timeout with a given scope
* @param {Function} fn
* @param {Number} timeout
* @param {Object} context
* @returns {number}
*/
export default function setTimeoutContext(fn, timeout, context) {
return setTimeout(bindFn(fn, context), timeout);
}
================================================
FILE: src/utils/split-str.js
================================================
/**
* @private
* split string on whitespace
* @param {String} str
* @returns {Array} words
*/
export default function splitStr(str) {
return str.trim().split(/\s+/g);
}
================================================
FILE: src/utils/to-array.js
================================================
/**
* @private
* convert array-like objects to real arrays
* @param {Object} obj
* @returns {Array}
*/
export default function toArray(obj) {
return Array.prototype.slice.call(obj, 0);
}
================================================
FILE: src/utils/unique-array.js
================================================
import inArray from './in-array';
/**
* @private
* unique array with objects based on a key (like 'id') or just by the array's value
* @param {Array} src [{id:1},{id:2},{id:1}]
* @param {String} [key]
* @param {Boolean} [sort=False]
* @returns {Array} [{id:1},{id:2}]
*/
export default function uniqueArray(src, key, sort) {
let results = [];
let values = [];
let i = 0;
while (i < src.length) {
let val = key ? src[i][key] : src[i];
if (inArray(values, val) < 0) {
results.push(src[i]);
}
values[i] = val;
i++;
}
if (sort) {
if (!key) {
results = results.sort();
} else {
results = results.sort((a, b) => {
return a[key] > b[key];
});
}
}
return results;
}
================================================
FILE: src/utils/unique-id.js
================================================
/**
* @private
* get a unique id
* @returns {number} uniqueId
*/
let _uniqueId = 1;
export default function uniqueId() {
return _uniqueId++;
}
================================================
FILE: src/utils/utils-consts.js
================================================
const VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];
const TEST_ELEMENT = document.createElement('div');
const TYPE_FUNCTION = 'function';
const { round, abs } = Math;
const { now } = Date;
export {
VENDOR_PREFIXES,
TEST_ELEMENT,
TYPE_FUNCTION,
round,
abs,
now
};
================================================
FILE: string-replace.js
================================================
const fs = require('fs');
const pkg = require('./package.json');
let hammer = fs.readFileSync('hammer.js', 'utf8');
hammer = hammer.replace(/{{PKG_VERSION}}/g, pkg.version);
fs.writeFile('hammer.js', hammer, 'utf8', (err) => {
if (err) {
throw err;
}
console.log('String Replaced!');
});
================================================
FILE: tests/manual/assets/style.css
================================================
@import url(http://fonts.googleapis.com/css?family=Open+Sans);
*, *:after, *:before {
box-sizing: border-box;
-moz-box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
height: 100%;
min-height: 100%;
background: #eee;
font: 13px/1.5em 'Open Sans', Helvetica, Arial, sans-serif;
}
a {
color: #4986e7;
}
.bg1, .green { background: #42d692; }
.bg2, .blue { background: #4986e7; }
.bg3, .red { background: #d06b64; }
.bg4, .purple { background: #cd74e6; }
.bg5, .azure { background: #9fe1e7; }
body {
margin: 20px;
}
pre {
background: #fff;
padding: 20px;
margin-bottom: 20px;
}
.container {
max-width: 900px;
margin: 0 auto;
}
.clear { clear: both; }
================================================
FILE: tests/manual/compute_touch_action.html
================================================
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, user-scalable=no">
<title></title>
</head>
<body>
Open the inspector and play a bit with the touchAction property.
<script src="../../hammer.min.js"></script>
<script>
var mc = new Hammer(document.body);
mc.add(new Hammer.Swipe({ direction: Hammer.DIRECTION_HORIZONTAL }));
mc.add(new Hammer.Pan({ direction: Hammer.DIRECTION_HORIZONTAL }));
console.log(document.body.style.touchAction)
</script>
</body>
</html>
================================================
FILE: tests/manual/input.html
================================================
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<meta name="msapplication-tap-highlight" content="no"/>
<link rel="stylesheet" href="assets/style.css">
<title>Hammer.js</title>
</head>
<body>
<div class="container">
<div id="hit" class="bg1" style="padding: 30px; height: 200px;">
</div>
<pre id="debug" style="overflow:hidden; background: #eee; padding: 15px;"></pre>
<pre id="log" style="overflow:hidden;"></pre>
</div>
<script src="../../hammer.js"></script>
<script>
Object.prototype.toDirString = function() {
var output = [];
for(var key in this) {
if(this.hasOwnProperty(key)) {
var value = this[key];
if(Array.isArray(value)) {
value = "Array("+ value.length +"):"+ value;
} else if(value instanceof HTMLElement) {
value = value +" ("+ value.outerHTML.substring(0, 50) +"...)";
}
output.push(key +": "+ value);
}
}
return output.join("\n")
};
var el = document.querySelector("#hit");
var log = document.querySelector("#log");
var debug = document.querySelector("#debug");
var mc = new Hammer(el);
mc.get('pinch').set({ enable: true });
mc.on("hammer.input", function(ev) {
debug.innerHTML = [ev.srcEvent.type, ev.pointers.length, ev.isFinal, ev.deltaX, ev.deltaY].join("<br>");
});
</script>
</body>
</html>
================================================
FILE: tests/manual/log.html
================================================
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="assets/style.css">
<title>Hammer.js</title>
</head>
<body>
<div class="container">
<div id="hit" class="bg1" style="padding: 30px;">
<span id="target" class="bg5" style="display: block; height: 100px;"></span>
</div>
<pre id="debug" style="overflow:hidden; background: #eee; padding: 15px;"></pre>
<pre id="log" style="overflow:hidden;"></pre>
</div>
<script src="../../hammer.min.js"></script>
<script>
Object.prototype.toDirString = function() {
var output = [];
for(var key in this) {
if(this.hasOwnProperty(key)) {
var value = this[key];
if(Array.isArray(value)) {
value = "Array("+ value.length +"):"+ value;
} else if(value instanceof HTMLElement) {
value = value +" ("+ value.outerHTML.substring(0, 50) +"...)";
}
output.push(key +": "+ value);
}
}
return output.join("\n")
};
var el = document.querySelector("#hit");
var log = document.querySelector("#log");
var debug = document.querySelector("#debug");
var mc = new Hammer(el);
mc.get('pinch').set({ enable: true });
mc.get('rotate').set({ enable: true });
mc.on("swipe pan panstart panmove panend pancancel multipan press pressup pinch rotate tap doubletap",
logGesture
gitextract_3wvcy3sg/
├── .bowerrc
├── .editorconfig
├── .gitignore
├── .jscsrc
├── .jshintrc
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── banner.ejs
├── bower.json
├── changelog.js
├── component.json
├── hammer.js
├── package.json
├── rollup.config.js
├── src/
│ ├── .babelrc
│ ├── hammer.js
│ ├── input/
│ │ ├── mouse.js
│ │ ├── pointerevent.js
│ │ ├── singletouch.js
│ │ ├── touch.js
│ │ └── touchmouse.js
│ ├── inputjs/
│ │ ├── compute-delta-xy.js
│ │ ├── compute-input-data.js
│ │ ├── compute-interval-input-data.js
│ │ ├── create-input-instance.js
│ │ ├── get-angle.js
│ │ ├── get-center.js
│ │ ├── get-direction.js
│ │ ├── get-distance.js
│ │ ├── get-rotation.js
│ │ ├── get-scale.js
│ │ ├── get-velocity.js
│ │ ├── input-constructor.js
│ │ ├── input-consts.js
│ │ ├── input-handler.js
│ │ └── simple-clone-input-data.js
│ ├── main.js
│ ├── manager.js
│ ├── recognizerjs/
│ │ ├── direction-str.js
│ │ ├── get-recognizer-by-name-if-manager.js
│ │ ├── recognizer-constructor.js
│ │ ├── recognizer-consts.js
│ │ └── state-str.js
│ ├── recognizers/
│ │ ├── attribute.js
│ │ ├── pan.js
│ │ ├── pinch.js
│ │ ├── press.js
│ │ ├── rotate.js
│ │ ├── swipe.js
│ │ └── tap.js
│ ├── touchactionjs/
│ │ ├── clean-touch-actions.js
│ │ ├── get-touchaction-props.js
│ │ ├── touchaction-Consts.js
│ │ └── touchaction-constructor.js
│ └── utils/
│ ├── add-event-listeners.js
│ ├── assign.js
│ ├── bind-fn.js
│ ├── bool-or-fn.js
│ ├── deprecate.js
│ ├── each.js
│ ├── extend.js
│ ├── get-window-for-element.js
│ ├── has-parent.js
│ ├── if-undefined.js
│ ├── in-array.js
│ ├── in-str.js
│ ├── inherit.js
│ ├── invoke-array-arg.js
│ ├── merge.js
│ ├── prefixed.js
│ ├── remove-event-listeners.js
│ ├── set-timeout-context.js
│ ├── split-str.js
│ ├── to-array.js
│ ├── unique-array.js
│ ├── unique-id.js
│ └── utils-consts.js
├── string-replace.js
└── tests/
├── manual/
│ ├── assets/
│ │ └── style.css
│ ├── compute_touch_action.html
│ ├── input.html
│ ├── log.html
│ ├── multiple.html
│ ├── nested.html
│ ├── simulator-googlemaps.html
│ ├── simulator.html
│ ├── touchaction.html
│ └── visual.html
└── unit/
├── assets/
│ └── utils.js
├── gestures/
│ ├── test_pan.js
│ ├── test_pinch.js
│ └── test_swipe.js
├── index.html
├── test_enable.js
├── test_events.js
├── test_gestures.js
├── test_hammer.js
├── test_jquery_plugin.js
├── test_multiple_taps.js
├── test_nested_gesture_recognizers.js
├── test_propagation_bubble.js
├── test_require_failure.js
├── test_simultaneous_recognition.js
└── test_utils.js
SYMBOL INDEX (269 symbols across 62 files)
FILE: changelog.js
function parseLog (line 21) | function parseLog( log ) {
function parseLine (line 38) | function parseLine( line ) {
function getParts (line 46) | function getParts( line ) {
FILE: hammer.js
function ifUndefined (line 15) | function ifUndefined(val1, val2) {
function prefixed (line 35) | function prefixed(obj, property) {
function getTouchActionProps (line 53) | function getTouchActionProps() {
function uniqueId (line 159) | function uniqueId() {
function each (line 170) | function each(obj, iterator, context) {
function invokeArrayArg (line 202) | function invokeArrayArg(arg, fn, context) {
function inArray (line 218) | function inArray(src, find, findByKey) {
function AwaitValue (line 241) | function AwaitValue(value) {
function AsyncGenerator (line 245) | function AsyncGenerator(gen) {
function defineProperties (line 360) | function defineProperties(target, props) {
function sliceIterator (line 427) | function sliceIterator(arr, i) {
function boolOrFn (line 472) | function boolOrFn(val, args) {
function getRecognizerByNameIfManager (line 486) | function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
function stateStr (line 501) | function stateStr(state) {
function Recognizer (line 552) | function Recognizer(options) {
function emit (line 715) | function emit(event) {
function AttrRecognizer (line 861) | function AttrRecognizer() {
function RotateRecognizer (line 937) | function RotateRecognizer() {
function PinchRecognizer (line 973) | function PinchRecognizer() {
function directionStr (line 1013) | function directionStr(direction) {
function PanRecognizer (line 1037) | function PanRecognizer() {
function SwipeRecognizer (line 1130) | function SwipeRecognizer() {
function bindFn (line 1186) | function bindFn(fn, context) {
function setTimeoutContext (line 1200) | function setTimeoutContext(fn, timeout, context) {
function getDistance (line 1212) | function getDistance(p1, p2, props) {
function TapRecognizer (line 1237) | function TapRecognizer() {
function PressRecognizer (line 1364) | function PressRecognizer() {
function inStr (line 1444) | function inStr(str, find) {
function cleanTouchActions (line 1454) | function cleanTouchActions(actions) {
function TouchAction (line 1494) | function TouchAction(manager, value) {
function hasParent (line 1621) | function hasParent(node, parent) {
function getCenter (line 1637) | function getCenter(pointers) {
function simpleCloneInputData (line 1669) | function simpleCloneInputData(input) {
function getAngle (line 1699) | function getAngle(p1, p2, props) {
function getDirection (line 1715) | function getDirection(x, y) {
function computeDeltaXY (line 1726) | function computeDeltaXY(session, input) {
function getVelocity (line 1759) | function getVelocity(deltaTime, x, y) {
function getScale (line 1774) | function getScale(start, end) {
function getRotation (line 1785) | function getRotation(start, end) {
function computeIntervalInputData (line 1795) | function computeIntervalInputData(session, input) {
function computeInputData (line 1834) | function computeInputData(manager, input) {
function inputHandler (line 1894) | function inputHandler(manager, eventType, input) {
function splitStr (line 1928) | function splitStr(str) {
function addEventListeners (line 1939) | function addEventListeners(target, types, handler) {
function removeEventListeners (line 1952) | function removeEventListeners(target, types, handler) {
function getWindowForElement (line 1964) | function getWindowForElement(element) {
function Input (line 1979) | function Input(manager, callback) {
function PointerEventInput (line 2073) | function PointerEventInput() {
function toArray$1 (line 2148) | function toArray$1(obj) {
function uniqueArray (line 2160) | function uniqueArray(src, key, sort) {
function TouchInput (line 2206) | function TouchInput() {
function getTouches (line 2239) | function getTouches(ev, type) {
function MouseInput (line 2313) | function MouseInput() {
function TouchMouseInput (line 2383) | function TouchMouseInput() {
function recordTouches (line 2441) | function recordTouches(eventType, eventData) {
function setLastTouch (line 2450) | function setLastTouch(eventData) {
function isSyntheticEvent (line 2473) | function isSyntheticEvent(eventData) {
function createInputInstance (line 2494) | function createInputInstance(manager) {
function Manager (line 2525) | function Manager(element, options) {
function toggleCssProps (line 2843) | function toggleCssProps(manager, add) {
function triggerDomEvent (line 2870) | function triggerDomEvent(event, data) {
function SingleTouchInput (line 3034) | function SingleTouchInput() {
function normalizeSingleTouches (line 3079) | function normalizeSingleTouches(ev, type) {
function deprecate (line 3098) | function deprecate(method, name, message) {
function inherit (line 3152) | function inherit(child, base, properties) {
FILE: src/hammer.js
class Hammer (line 19) | class Hammer {
method constructor (line 20) | constructor(element, options) {
FILE: src/input/mouse.js
constant MOUSE_INPUT_MAP (line 9) | const MOUSE_INPUT_MAP = {
constant MOUSE_ELEMENT_EVENTS (line 15) | const MOUSE_ELEMENT_EVENTS = 'mousedown';
constant MOUSE_WINDOW_EVENTS (line 16) | const MOUSE_WINDOW_EVENTS = 'mousemove mouseup';
class MouseInput (line 24) | class MouseInput extends Input {
method constructor (line 25) | constructor() {
method handler (line 39) | handler(ev) {
FILE: src/input/pointerevent.js
constant POINTER_INPUT_MAP (line 14) | const POINTER_INPUT_MAP = {
constant IE10_POINTER_TYPE_ENUM (line 23) | const IE10_POINTER_TYPE_ENUM = {
constant POINTER_ELEMENT_EVENTS (line 30) | let POINTER_ELEMENT_EVENTS = 'pointerdown';
constant POINTER_WINDOW_EVENTS (line 31) | let POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';
class PointerEventInput (line 45) | class PointerEventInput extends Input {
method constructor (line 46) | constructor() {
method handler (line 59) | handler(ev) {
FILE: src/input/singletouch.js
constant SINGLE_TOUCH_INPUT_MAP (line 12) | const SINGLE_TOUCH_INPUT_MAP = {
constant SINGLE_TOUCH_TARGET_EVENTS (line 19) | const SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
constant SINGLE_TOUCH_WINDOW_EVENTS (line 20) | const SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchc...
class SingleTouchInput (line 28) | class SingleTouchInput extends Input {
method constructor (line 29) | constructor() {
method handler (line 38) | handler(ev) {
function normalizeSingleTouches (line 73) | function normalizeSingleTouches(ev, type) {
FILE: src/input/touch.js
constant TOUCH_INPUT_MAP (line 13) | const TOUCH_INPUT_MAP = {
constant TOUCH_TARGET_EVENTS (line 20) | const TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';
class TouchInput (line 28) | class TouchInput extends Input {
method constructor (line 30) | constructor() {
method handler (line 39) | handler(ev) {
function getTouches (line 62) | function getTouches(ev, type) {
FILE: src/input/touchmouse.js
constant DEDUP_TIMEOUT (line 24) | const DEDUP_TIMEOUT = 2500;
constant DEDUP_DISTANCE (line 25) | const DEDUP_DISTANCE = 25;
class TouchMouseInput (line 27) | class TouchMouseInput extends Input {
method constructor (line 28) | constructor() {
method handler (line 46) | handler(manager, inputEvent, inputData) {
method destroy (line 68) | destroy() {
function recordTouches (line 74) | function recordTouches(eventType, eventData) {
function setLastTouch (line 83) | function setLastTouch(eventData) {
function isSyntheticEvent (line 99) | function isSyntheticEvent(eventData) {
FILE: src/inputjs/compute-delta-xy.js
function computeDeltaXY (line 3) | function computeDeltaXY(session, input) {
FILE: src/inputjs/compute-input-data.js
function computeInputData (line 21) | function computeInputData(manager, input) {
FILE: src/inputjs/compute-interval-input-data.js
function computeIntervalInputData (line 12) | function computeIntervalInputData(session, input) {
FILE: src/inputjs/create-input-instance.js
function createInputInstance (line 15) | function createInputInstance(manager) {
FILE: src/inputjs/get-angle.js
function getAngle (line 11) | function getAngle(p1, p2, props) {
FILE: src/inputjs/get-center.js
function getCenter (line 9) | function getCenter(pointers) {
FILE: src/inputjs/get-direction.js
function getDirection (line 11) | function getDirection(x, y) {
FILE: src/inputjs/get-distance.js
function getDistance (line 11) | function getDistance(p1, p2, props) {
FILE: src/inputjs/get-rotation.js
function getRotation (line 11) | function getRotation(start, end) {
FILE: src/inputjs/get-scale.js
function getScale (line 11) | function getScale(start, end) {
FILE: src/inputjs/get-velocity.js
function getVelocity (line 9) | function getVelocity(deltaTime, x, y) {
FILE: src/inputjs/input-constructor.js
class Input (line 14) | class Input {
method constructor (line 15) | constructor(manager, callback) {
method handler (line 38) | handler() { }
method init (line 44) | init() {
method destroy (line 54) | destroy() {
FILE: src/inputjs/input-consts.js
constant MOBILE_REGEX (line 3) | const MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
constant SUPPORT_TOUCH (line 5) | const SUPPORT_TOUCH = ('ontouchstart' in window);
constant SUPPORT_POINTER_EVENTS (line 6) | const SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== unde...
constant SUPPORT_ONLY_TOUCH (line 7) | const SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator....
constant INPUT_TYPE_TOUCH (line 9) | const INPUT_TYPE_TOUCH = 'touch';
constant INPUT_TYPE_PEN (line 10) | const INPUT_TYPE_PEN = 'pen';
constant INPUT_TYPE_MOUSE (line 11) | const INPUT_TYPE_MOUSE = 'mouse';
constant INPUT_TYPE_KINECT (line 12) | const INPUT_TYPE_KINECT = 'kinect';
constant COMPUTE_INTERVAL (line 14) | const COMPUTE_INTERVAL = 25;
constant INPUT_START (line 16) | const INPUT_START = 1;
constant INPUT_MOVE (line 17) | const INPUT_MOVE = 2;
constant INPUT_END (line 18) | const INPUT_END = 4;
constant INPUT_CANCEL (line 19) | const INPUT_CANCEL = 8;
constant DIRECTION_NONE (line 21) | const DIRECTION_NONE = 1;
constant DIRECTION_LEFT (line 22) | const DIRECTION_LEFT = 2;
constant DIRECTION_RIGHT (line 23) | const DIRECTION_RIGHT = 4;
constant DIRECTION_UP (line 24) | const DIRECTION_UP = 8;
constant DIRECTION_DOWN (line 25) | const DIRECTION_DOWN = 16;
constant DIRECTION_HORIZONTAL (line 27) | const DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
constant DIRECTION_VERTICAL (line 28) | const DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
constant DIRECTION_ALL (line 29) | const DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
constant PROPS_XY (line 31) | const PROPS_XY = ['x', 'y'];
constant PROPS_CLIENT_XY (line 32) | const PROPS_CLIENT_XY = ['clientX', 'clientY'];
FILE: src/inputjs/input-handler.js
function inputHandler (line 11) | function inputHandler(manager, eventType, input) {
FILE: src/inputjs/simple-clone-input-data.js
function simpleCloneInputData (line 10) | function simpleCloneInputData(input) {
FILE: src/manager.js
constant STOP (line 18) | const STOP = 1;
constant FORCED_STOP (line 19) | const FORCED_STOP = 2;
class Manager (line 28) | class Manager {
method constructor (line 29) | constructor(element, options) {
method set (line 58) | set(options) {
method stop (line 81) | stop(force) {
method recognize (line 92) | recognize(inputData) {
method get (line 148) | get(recognizer) {
method add (line 168) | add(recognizer) {
method remove (line 192) | remove(recognizer) {
method on (line 220) | on(events, handler) {
method off (line 242) | off(events, handler) {
method emit (line 263) | emit(event, data) {
method destroy (line 292) | destroy() {
function toggleCssProps (line 308) | function toggleCssProps(manager, add) {
function triggerDomEvent (line 334) | function triggerDomEvent(event, data) {
FILE: src/recognizerjs/direction-str.js
function directionStr (line 14) | function directionStr(direction) {
FILE: src/recognizerjs/get-recognizer-by-name-if-manager.js
function getRecognizerByNameIfManager (line 8) | function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
FILE: src/recognizerjs/recognizer-constructor.js
class Recognizer (line 55) | class Recognizer {
method constructor (line 56) | constructor(options) {
method set (line 77) | set(options) {
method recognizeWith (line 91) | recognizeWith(otherRecognizer) {
method dropRecognizeWith (line 111) | dropRecognizeWith(otherRecognizer) {
method requireFailure (line 127) | requireFailure(otherRecognizer) {
method dropRequireFailure (line 147) | dropRequireFailure(otherRecognizer) {
method hasRequireFailures (line 165) | hasRequireFailures() {
method canRecognizeWith (line 175) | canRecognizeWith(otherRecognizer) {
method emit (line 185) | emit(input) {
method tryEmit (line 217) | tryEmit(input) {
method canEmit (line 230) | canEmit() {
method recognize (line 246) | recognize(inputData) {
method process (line 282) | process(inputData) { }
method getTouchAction (line 291) | getTouchAction() { }
method reset (line 299) | reset() { }
FILE: src/recognizerjs/recognizer-consts.js
constant STATE_POSSIBLE (line 1) | const STATE_POSSIBLE = 1;
constant STATE_BEGAN (line 2) | const STATE_BEGAN = 2;
constant STATE_CHANGED (line 3) | const STATE_CHANGED = 4;
constant STATE_ENDED (line 4) | const STATE_ENDED = 8;
constant STATE_RECOGNIZED (line 5) | const STATE_RECOGNIZED = STATE_ENDED;
constant STATE_CANCELLED (line 6) | const STATE_CANCELLED = 16;
constant STATE_FAILED (line 7) | const STATE_FAILED = 32;
FILE: src/recognizerjs/state-str.js
function stateStr (line 14) | function stateStr(state) {
FILE: src/recognizers/attribute.js
class AttrRecognizer (line 20) | class AttrRecognizer extends Recognizer {
method constructor (line 21) | constructor() {
method attrTest (line 32) | attrTest(input) {
method process (line 44) | process(input) {
FILE: src/recognizers/pan.js
class PanRecognizer (line 23) | class PanRecognizer extends AttrRecognizer {
method constructor (line 24) | constructor() {
method getTouchAction (line 30) | getTouchAction() {
method directionTest (line 42) | directionTest(input) {
method attrTest (line 66) | attrTest(input) {
method emit (line 71) | emit(input) {
FILE: src/recognizers/pinch.js
class PinchRecognizer (line 12) | class PinchRecognizer extends AttrRecognizer {
method constructor (line 13) | constructor() {
method getTouchAction (line 17) | getTouchAction() {
method attrTest (line 21) | attrTest(input) {
method emit (line 26) | emit(input) {
FILE: src/recognizers/press.js
class PressRecognizer (line 22) | class PressRecognizer extends Recognizer {
method constructor (line 23) | constructor() {
method getTouchAction (line 29) | getTouchAction() {
method process (line 33) | process(input) {
method reset (line 57) | reset() {
method emit (line 61) | emit(input) {
FILE: src/recognizers/rotate.js
class RotateRecognizer (line 12) | class RotateRecognizer extends AttrRecognizer {
method constructor (line 13) | constructor() {
method getTouchAction (line 17) | getTouchAction() {
method attrTest (line 21) | attrTest(input) {
FILE: src/recognizers/swipe.js
class SwipeRecognizer (line 15) | class SwipeRecognizer extends AttrRecognizer {
method constructor (line 16) | constructor() {
method getTouchAction (line 20) | getTouchAction() {
method attrTest (line 24) | attrTest(input) {
method emit (line 43) | emit(input) {
FILE: src/recognizers/tap.js
class TapRecognizer (line 23) | class TapRecognizer extends Recognizer {
method constructor (line 24) | constructor() {
method getTouchAction (line 37) | getTouchAction() {
method process (line 41) | process(input) {
method failTimeout (line 95) | failTimeout() {
method reset (line 102) | reset() {
method emit (line 106) | emit() {
FILE: src/touchactionjs/clean-touch-actions.js
function cleanTouchActions (line 16) | function cleanTouchActions(actions) {
FILE: src/touchactionjs/get-touchaction-props.js
function getTouchActionProps (line 3) | function getTouchActionProps() {
FILE: src/touchactionjs/touchaction-Consts.js
constant PREFIXED_TOUCH_ACTION (line 5) | const PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
constant NATIVE_TOUCH_ACTION (line 6) | const NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;
constant TOUCH_ACTION_COMPUTE (line 9) | const TOUCH_ACTION_COMPUTE = 'compute';
constant TOUCH_ACTION_AUTO (line 10) | const TOUCH_ACTION_AUTO = 'auto';
constant TOUCH_ACTION_MANIPULATION (line 11) | const TOUCH_ACTION_MANIPULATION = 'manipulation';
constant TOUCH_ACTION_NONE (line 12) | const TOUCH_ACTION_NONE = 'none';
constant TOUCH_ACTION_PAN_X (line 13) | const TOUCH_ACTION_PAN_X = 'pan-x';
constant TOUCH_ACTION_PAN_Y (line 14) | const TOUCH_ACTION_PAN_Y = 'pan-y';
constant TOUCH_ACTION_MAP (line 15) | const TOUCH_ACTION_MAP = getTouchActionProps();
FILE: src/touchactionjs/touchaction-constructor.js
class TouchAction (line 27) | class TouchAction {
method constructor (line 28) | constructor(manager, value) {
method set (line 38) | set(value) {
method update (line 54) | update() {
method compute (line 63) | compute() {
method preventDefaults (line 78) | preventDefaults(input) {
method preventSrc (line 121) | preventSrc(srcEvent) {
FILE: src/utils/add-event-listeners.js
function addEventListeners (line 10) | function addEventListeners(target, types, handler) {
FILE: src/utils/bind-fn.js
function bindFn (line 8) | function bindFn(fn, context) {
FILE: src/utils/bool-or-fn.js
function boolOrFn (line 10) | function boolOrFn(val, args) {
FILE: src/utils/deprecate.js
function deprecate (line 9) | function deprecate(method, name, message) {
FILE: src/utils/each.js
function each (line 8) | function each(obj, iterator, context) {
FILE: src/utils/get-window-for-element.js
function getWindowForElement (line 7) | function getWindowForElement(element) {
FILE: src/utils/has-parent.js
function hasParent (line 9) | function hasParent(node, parent) {
FILE: src/utils/if-undefined.js
function ifUndefined (line 8) | function ifUndefined(val1, val2) {
FILE: src/utils/in-array.js
function inArray (line 9) | function inArray(src, find, findByKey) {
FILE: src/utils/in-str.js
function inStr (line 8) | function inStr(str, find) {
FILE: src/utils/inherit.js
function inherit (line 9) | function inherit(child, base, properties) {
FILE: src/utils/invoke-array-arg.js
function invokeArrayArg (line 12) | function invokeArrayArg(arg, fn, context) {
FILE: src/utils/prefixed.js
function prefixed (line 9) | function prefixed(obj, property) {
FILE: src/utils/remove-event-listeners.js
function removeEventListeners (line 10) | function removeEventListeners(target, types, handler) {
FILE: src/utils/set-timeout-context.js
function setTimeoutContext (line 11) | function setTimeoutContext(fn, timeout, context) {
FILE: src/utils/split-str.js
function splitStr (line 8) | function splitStr(str) {
FILE: src/utils/to-array.js
function toArray (line 7) | function toArray(obj) {
FILE: src/utils/unique-array.js
function uniqueArray (line 11) | function uniqueArray(src, key, sort) {
FILE: src/utils/unique-id.js
function uniqueId (line 7) | function uniqueId() {
FILE: src/utils/utils-consts.js
constant VENDOR_PREFIXES (line 1) | const VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];
constant TEST_ELEMENT (line 2) | const TEST_ELEMENT = document.createElement('div');
constant TYPE_FUNCTION (line 4) | const TYPE_FUNCTION = 'function';
FILE: tests/unit/test_events.js
function event3Handler (line 20) | function event3Handler() {
FILE: tests/unit/test_utils.js
function Base (line 27) | function Base() {
function Child (line 31) | function Child() {
function handleEvent (line 154) | function handleEvent() {
Condensed preview — 107 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (285K chars).
[
{
"path": ".bowerrc",
"chars": 26,
"preview": "{\n \"json\": \"bower.json\"\n}"
},
{
"path": ".editorconfig",
"chars": 367,
"preview": "# EditorConfig helps developers define and maintain consistent\n# coding styles between different editors and IDEs\n# edit"
},
{
"path": ".gitignore",
"chars": 137,
"preview": "# ide\n.idea\n.iml\n\n# node\nlib-cov\n*.seed\n*.log\n*.csv\n*.dat\n*.out\n*.pid\n*.gz\n\npids\nlogs\nresults\ntests/build.js\n\nnpm-debug."
},
{
"path": ".jscsrc",
"chars": 209,
"preview": "{\n \"preset\": \"ember-suave\",\n \"excludeFiles\": [\n \"tests/**/assets\",\n \"node_modules/**\"\n ],\n \"disallowConstOutsi"
},
{
"path": ".jshintrc",
"chars": 690,
"preview": "{\n \"maxerr\": 100,\n \"freeze\": true,\n \"node\": false,\n \"indent\": 2,\n \"predef\": [\n \"document\",\n \"window\"\n ],\n \""
},
{
"path": ".travis.yml",
"chars": 78,
"preview": "language: node_js\nnode_js:\n - \"6.3.0\"\n\nsudo: false\n\nscript:\n - npm run test\n"
},
{
"path": "CHANGELOG.md",
"chars": 6304,
"preview": "# Changelog\n\n### 2.0.8, 2016-04-22\n\n##### Manager\n- Added check to ensure that the required parameters are present ([#90"
},
{
"path": "CONTRIBUTING.md",
"chars": 1624,
"preview": "# Contributing to Hammer.js\n\nLooking to contribute something to Hammer.js? **Here's how you can help.**\n\n\n## Reporting i"
},
{
"path": "LICENSE.md",
"chars": 1104,
"preview": "The MIT License (MIT)\n\nCopyright (C) 2011-2017 by Jorik Tangelder (Eight Media)\n\nPermission is hereby granted, free of c"
},
{
"path": "README.md",
"chars": 3305,
"preview": "# [hammer.js][hammerjs-url] [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] ["
},
{
"path": "banner.ejs",
"chars": 298,
"preview": "/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - <%= [ date.getFullYear(), ('0' + (date.getMonth() + 1)).slice(-"
},
{
"path": "bower.json",
"chars": 283,
"preview": "{\n \"name\": \"hammerjs\",\n \"license\": \"MIT\",\n \"main\": \"hammer.js\",\n \"ignore\": [\n \"tests\",\n \"src\","
},
{
"path": "changelog.js",
"chars": 1696,
"preview": "var changelog = require( \"changelogplease\" );\nvar gittags = require( \"git-tags\" ).get( function( error, tags ) {\n\tif ( e"
},
{
"path": "component.json",
"chars": 118,
"preview": "{\n \"name\": \"hammerjs\",\n \"version\": \"2.0.6\",\n \"main\": \"hammer.js\",\n \"scripts\": [\n \"hammer.js\"\n ]\n}\n"
},
{
"path": "hammer.js",
"chars": 83585,
"preview": "/*! Hammer.JS - v2.0.8 - 2016-09-30\n * http://hammerjs.github.io/\n *\n * Copyright (c) Jorik Tangelder;\n * Licensed unde"
},
{
"path": "package.json",
"chars": 2554,
"preview": "{\n \"name\": \"hammerjs\",\n \"title\": \"Hammer.JS\",\n \"description\": \"A javascript library for multi-touch gestures\",\n \"ver"
},
{
"path": "rollup.config.js",
"chars": 302,
"preview": "import babel from 'rollup-plugin-babel';\n\nexport default {\n entry: 'src/main.js',\n format: 'es',\n plugins: [ babel({ "
},
{
"path": "src/.babelrc",
"chars": 131,
"preview": "{\n \"presets\": [\n [\n \"env\",\n {\n \"modules\": false\n }\n ]\n ],\n \"plugins\": [\n \"external-hel"
},
{
"path": "src/hammer.js",
"chars": 4221,
"preview": "import ifUndefined from './utils/if-undefined';\nimport { TOUCH_ACTION_COMPUTE } from './touchactionjs/touchaction-Consts"
},
{
"path": "src/input/mouse.js",
"chars": 1331,
"preview": "import {\n INPUT_START,\n INPUT_MOVE,\n INPUT_END,\n INPUT_TYPE_MOUSE\n} from '../inputjs/input-consts';\nimport I"
},
{
"path": "src/input/pointerevent.js",
"chars": 2610,
"preview": "import {\n INPUT_START,\n INPUT_END,\n INPUT_CANCEL,\n INPUT_MOVE,\n INPUT_TYPE_TOUCH,\n INPUT_TYPE_MOUSE,\n "
},
{
"path": "src/input/singletouch.js",
"chars": 1898,
"preview": "import {\n INPUT_START,\n INPUT_MOVE,\n INPUT_END,\n INPUT_CANCEL,\n INPUT_TYPE_TOUCH\n} from '../inputjs/input"
},
{
"path": "src/input/touch.js",
"chars": 2805,
"preview": "import {\n INPUT_START,\n INPUT_MOVE,\n INPUT_END,\n INPUT_CANCEL,\n INPUT_TYPE_TOUCH\n} from '../inputjs/input"
},
{
"path": "src/input/touchmouse.js",
"chars": 2923,
"preview": "import Input from '../inputjs/input-constructor';\nimport bindFn from '../utils/bind-fn';\nimport TouchInput from './tou"
},
{
"path": "src/inputjs/compute-delta-xy.js",
"chars": 821,
"preview": "import { INPUT_START, INPUT_END } from './input-consts';\n\nexport default function computeDeltaXY(session, input) {\n let"
},
{
"path": "src/inputjs/compute-input-data.js",
"chars": 2664,
"preview": "import { now } from '../utils/utils-consts';\nimport { abs } from '../utils/utils-consts';\nimport hasParent from '../util"
},
{
"path": "src/inputjs/compute-interval-input-data.js",
"chars": 1297,
"preview": "import { INPUT_CANCEL,COMPUTE_INTERVAL } from './input-consts';\nimport { abs } from '../utils/utils-consts';\nimport getV"
},
{
"path": "src/inputjs/create-input-instance.js",
"chars": 934,
"preview": "import { SUPPORT_POINTER_EVENTS,SUPPORT_ONLY_TOUCH,SUPPORT_TOUCH } from './input-consts';\nimport inputHandler from './in"
},
{
"path": "src/inputjs/get-angle.js",
"chars": 446,
"preview": "import { PROPS_XY } from './input-consts';\n\n/**\n * @private\n * calculate the angle between two coordinates\n * @param {Ob"
},
{
"path": "src/inputjs/get-center.js",
"chars": 677,
"preview": "import { round } from '../utils/utils-consts';\n\n/**\n * @private\n * get the center of all the pointers\n * @param {Array} "
},
{
"path": "src/inputjs/get-direction.js",
"chars": 513,
"preview": "import { abs } from '../utils/utils-consts';\nimport { DIRECTION_NONE,DIRECTION_LEFT,DIRECTION_RIGHT,DIRECTION_UP,DIRECTI"
},
{
"path": "src/inputjs/get-distance.js",
"chars": 470,
"preview": "import { PROPS_XY } from './input-consts';\n\n/**\n * @private\n * calculate the absolute distance between two points\n * @pa"
},
{
"path": "src/inputjs/get-rotation.js",
"chars": 428,
"preview": "import getAngle from './get-angle';\nimport { PROPS_CLIENT_XY } from './input-consts';\n\n/**\n * @private\n * calculate the "
},
{
"path": "src/inputjs/get-scale.js",
"chars": 517,
"preview": "import { PROPS_CLIENT_XY } from './input-consts';\nimport getDistance from './get-distance';\n/**\n * @private\n * calculate"
},
{
"path": "src/inputjs/get-velocity.js",
"chars": 326,
"preview": "/**\n * @private\n * calculate the velocity between two points. unit is in px per ms.\n * @param {Number} deltaTime\n * @par"
},
{
"path": "src/inputjs/input-constructor.js",
"chars": 1725,
"preview": "import boolOrFn from '../utils/bool-or-fn';\nimport addEventListeners from '../utils/add-event-listeners';\nimport removeE"
},
{
"path": "src/inputjs/input-consts.js",
"chars": 1450,
"preview": "import prefixed from '../utils/prefixed';\n\nconst MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;\n\nconst SUPPORT_"
},
{
"path": "src/inputjs/input-handler.js",
"chars": 1047,
"preview": "import { INPUT_START,INPUT_END,INPUT_CANCEL } from './input-consts';\nimport computeInputData from './compute-input-data'"
},
{
"path": "src/inputjs/simple-clone-input-data.js",
"chars": 795,
"preview": "import { now,round } from '../utils/utils-consts';\nimport getCenter from './get-center';\n\n/**\n * @private\n * create a si"
},
{
"path": "src/main.js",
"chars": 3439,
"preview": "import Hammer from './hammer';\nimport assign from './utils/assign';\nimport {\n INPUT_START,\n INPUT_MOVE,\n INPUT_"
},
{
"path": "src/manager.js",
"chars": 8874,
"preview": "import assign from './utils/assign';\nimport Hammer from './hammer';\nimport TouchAction from './touchactionjs/touchacti"
},
{
"path": "src/recognizerjs/direction-str.js",
"chars": 538,
"preview": "import {\n DIRECTION_LEFT,\n DIRECTION_RIGHT,\n DIRECTION_UP,\n DIRECTION_DOWN\n} from '../inputjs/input-consts';"
},
{
"path": "src/recognizerjs/get-recognizer-by-name-if-manager.js",
"chars": 387,
"preview": "/**\n * @private\n * get a recognizer by name if it is bound to a manager\n * @param {Recognizer|String} otherRecognizer\n *"
},
{
"path": "src/recognizerjs/recognizer-constructor.js",
"chars": 7984,
"preview": "import {\n STATE_POSSIBLE,\n STATE_ENDED,\n STATE_FAILED,\n STATE_RECOGNIZED,\n STATE_CANCELLED,\n STATE_BEG"
},
{
"path": "src/recognizerjs/recognizer-consts.js",
"chars": 334,
"preview": "const STATE_POSSIBLE = 1;\nconst STATE_BEGAN = 2;\nconst STATE_CHANGED = 4;\nconst STATE_ENDED = 8;\nconst STATE_RECOGNIZED "
},
{
"path": "src/recognizerjs/state-str.js",
"chars": 515,
"preview": "import {\n STATE_CANCELLED,\n STATE_ENDED,\n STATE_CHANGED,\n STATE_BEGAN\n} from './recognizer-consts';\n\n/**\n * "
},
{
"path": "src/recognizers/attribute.js",
"chars": 1797,
"preview": "import Recognizer from '../recognizerjs/recognizer-constructor';\nimport {\n STATE_BEGAN,\n STATE_CHANGED,\n STATE_"
},
{
"path": "src/recognizers/pan.js",
"chars": 2430,
"preview": "import AttrRecognizer from './attribute';\nimport {\n DIRECTION_ALL,\n DIRECTION_HORIZONTAL,\n DIRECTION_VERTICAL,"
},
{
"path": "src/recognizers/pinch.js",
"chars": 955,
"preview": "import AttrRecognizer from './attribute';\nimport { TOUCH_ACTION_NONE } from '../touchactionjs/touchaction-Consts';\nimpor"
},
{
"path": "src/recognizers/press.js",
"chars": 2130,
"preview": "import Recognizer from '../recognizerjs/recognizer-constructor';\nimport {\n STATE_RECOGNIZED,\n STATE_FAILED\n} from "
},
{
"path": "src/recognizers/rotate.js",
"chars": 739,
"preview": "import AttrRecognizer from './attribute';\nimport { TOUCH_ACTION_NONE } from '../touchactionjs/touchaction-Consts';\nimpor"
},
{
"path": "src/recognizers/swipe.js",
"chars": 1734,
"preview": "import AttrRecognizer from '../recognizers/attribute';\nimport { abs } from '../utils/utils-consts';\nimport { DIRECTION_H"
},
{
"path": "src/recognizers/tap.js",
"chars": 3615,
"preview": "import setTimeoutContext from '../utils/set-timeout-context';\nimport Recognizer from '../recognizerjs/recognizer-constru"
},
{
"path": "src/touchactionjs/clean-touch-actions.js",
"chars": 1206,
"preview": "import inStr from '../utils/in-str';\nimport {\n TOUCH_ACTION_NONE,\n TOUCH_ACTION_PAN_X,\n TOUCH_ACTION_PAN_Y,\n "
},
{
"path": "src/touchactionjs/get-touchaction-props.js",
"chars": 588,
"preview": "import { NATIVE_TOUCH_ACTION } from './touchaction-Consts';\n\nexport default function getTouchActionProps() {\n if (!NATI"
},
{
"path": "src/touchactionjs/touchaction-Consts.js",
"chars": 860,
"preview": "import prefixed from '../utils/prefixed';\nimport { TEST_ELEMENT } from '../utils/utils-consts';\nimport getTouchActionPro"
},
{
"path": "src/touchactionjs/touchaction-constructor.js",
"chars": 3409,
"preview": "import {\n TOUCH_ACTION_COMPUTE,\n TOUCH_ACTION_MAP,\n NATIVE_TOUCH_ACTION,\n PREFIXED_TOUCH_ACTION,\n TOUCH_A"
},
{
"path": "src/utils/add-event-listeners.js",
"chars": 380,
"preview": "import each from './each';\nimport splitStr from './split-str';\n/**\n * @private\n * addEventListener with multiple events "
},
{
"path": "src/utils/assign.js",
"chars": 870,
"preview": "/**\n * @private\n * extend object.\n * means that properties in dest will be overwritten by the ones in src.\n * @param {Ob"
},
{
"path": "src/utils/bind-fn.js",
"chars": 242,
"preview": "/**\n * @private\n * simple function bind\n * @param {Function} fn\n * @param {Object} context\n * @returns {Function}\n */\nex"
},
{
"path": "src/utils/bool-or-fn.js",
"chars": 445,
"preview": "import { TYPE_FUNCTION } from './utils-consts';\n/**\n * @private\n * let a boolean value also be a function that must retu"
},
{
"path": "src/utils/deprecate.js",
"chars": 828,
"preview": "/**\n * @private\n * wrap a method with a deprecation warning and stack trace\n * @param {Function} method\n * @param {Strin"
},
{
"path": "src/utils/each.js",
"chars": 539,
"preview": "/**\n * @private\n * walk objects and arrays\n * @param {Object} obj\n * @param {Function} iterator\n * @param {Object} conte"
},
{
"path": "src/utils/extend.js",
"chars": 561,
"preview": "import deprecate from './deprecate';\n/**\n * @private\n * extend object.\n * means that properties in dest will be overwrit"
},
{
"path": "src/utils/get-window-for-element.js",
"chars": 286,
"preview": "/**\n * @private\n * get the window object of an element\n * @param {HTMLElement} element\n * @returns {DocumentView|Window}"
},
{
"path": "src/utils/has-parent.js",
"chars": 338,
"preview": "/**\n * @private\n * find if a node is in the given parent\n * @method hasParent\n * @param {HTMLElement} node\n * @param {HT"
},
{
"path": "src/utils/if-undefined.js",
"chars": 210,
"preview": "/**\n * @private\n * use the val2 when val1 is undefined\n * @param {*} val1\n * @param {*} val2\n * @returns {*}\n */\nexport "
},
{
"path": "src/utils/in-array.js",
"chars": 597,
"preview": "/**\n * @private\n * find if a array contains the object using indexOf or a simple polyFill\n * @param {Array} src\n * @para"
},
{
"path": "src/utils/in-str.js",
"chars": 198,
"preview": "/**\n * @private\n * small indexOf wrapper\n * @param {String} str\n * @param {String} find\n * @returns {Boolean} found\n */\n"
},
{
"path": "src/utils/inherit.js",
"chars": 433,
"preview": "import assign from './assign';\n/**\n * @private\n * simple class inheritance\n * @param {Function} child\n * @param {Functio"
},
{
"path": "src/utils/invoke-array-arg.js",
"chars": 507,
"preview": "import each from './each';\n/**\n * @private\n * if the argument is an array, we want to execute the fn on each entry\n * if"
},
{
"path": "src/utils/merge.js",
"chars": 406,
"preview": "import deprecate from './deprecate';\nimport extend from './extend';\n/**\n * @private\n * merge the values from src in the "
},
{
"path": "src/utils/prefixed.js",
"chars": 554,
"preview": "import { VENDOR_PREFIXES } from './utils-consts';\n/**\n * @private\n * get the prefixed property\n * @param {Object} obj\n *"
},
{
"path": "src/utils/remove-event-listeners.js",
"chars": 389,
"preview": "import each from './each';\nimport splitStr from './split-str';\n/**\n * @private\n * removeEventListener with multiple even"
},
{
"path": "src/utils/set-timeout-context.js",
"chars": 307,
"preview": "import bindFn from './bind-fn';\n\n/**\n * @private\n * set a timeout with a given scope\n * @param {Function} fn\n * @param {"
},
{
"path": "src/utils/split-str.js",
"chars": 177,
"preview": "/**\n * @private\n * split string on whitespace\n * @param {String} str\n * @returns {Array} words\n */\n\nexport default funct"
},
{
"path": "src/utils/to-array.js",
"chars": 194,
"preview": "/**\n * @private\n * convert array-like objects to real arrays\n * @param {Object} obj\n * @returns {Array}\n */\nexport defau"
},
{
"path": "src/utils/unique-array.js",
"chars": 749,
"preview": "import inArray from './in-array';\n\n/**\n * @private\n * unique array with objects based on a key (like 'id') or just by th"
},
{
"path": "src/utils/unique-id.js",
"chars": 149,
"preview": "/**\n * @private\n * get a unique id\n * @returns {number} uniqueId\n */\nlet _uniqueId = 1;\nexport default function uniqueId"
},
{
"path": "src/utils/utils-consts.js",
"chars": 302,
"preview": "const VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];\nconst TEST_ELEMENT = document.createElement('div');\n\ncon"
},
{
"path": "string-replace.js",
"chars": 300,
"preview": "const fs = require('fs');\nconst pkg = require('./package.json');\n\nlet hammer = fs.readFileSync('hammer.js', 'utf8');\nham"
},
{
"path": "tests/manual/assets/style.css",
"chars": 725,
"preview": "@import url(http://fonts.googleapis.com/css?family=Open+Sans);\n\n*, *:after, *:before {\n box-sizing: border-box;\n -"
},
{
"path": "tests/manual/compute_touch_action.html",
"chars": 551,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <meta name=\"viewport\" content=\"width=device-width, user-scalable=no\">\n <title></tit"
},
{
"path": "tests/manual/input.html",
"chars": 1716,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, user-sca"
},
{
"path": "tests/manual/log.html",
"chars": 1972,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, user-sca"
},
{
"path": "tests/manual/multiple.html",
"chars": 2267,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, user-sca"
},
{
"path": "tests/manual/nested.html",
"chars": 8141,
"preview": "<!DOCTYPE html>\n<html>\n<head lang=\"en\">\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width"
},
{
"path": "tests/manual/simulator-googlemaps.html",
"chars": 2994,
"preview": "<!DOCTYPE html>\n<html>\n<head lang=\"en\">\n <meta name=\"viewport\" content=\"width=device-width, user-scalable=no, initial"
},
{
"path": "tests/manual/simulator.html",
"chars": 3371,
"preview": "<!DOCTYPE html>\n<html>\n<head lang=\"en\">\n <meta name=\"viewport\" content=\"width=device-width, user-scalable=no, initial"
},
{
"path": "tests/manual/touchaction.html",
"chars": 3031,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <meta name=\"viewport\" content=\"width=device-width\">\n <link rel=\"stylesheet\" href=\"a"
},
{
"path": "tests/manual/visual.html",
"chars": 5374,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <meta name=\"viewport\" content=\"width=device-width, user-scalable=no, initial-scale=1, "
},
{
"path": "tests/unit/assets/utils.js",
"chars": 1360,
"preview": "var utils = {\n /**\n * trigger simple dom event\n * @param obj\n * @param name\n */\n triggerDomEvent: "
},
{
"path": "tests/unit/gestures/test_pan.js",
"chars": 1823,
"preview": "// jscs:disable requireArrowFunctions,disallowVar,requireEnhancedObjectLiterals\n/* globals QUnit,Hammer,utils,Simulator "
},
{
"path": "tests/unit/gestures/test_pinch.js",
"chars": 1255,
"preview": "// jscs:disable requireArrowFunctions,disallowVar,requireEnhancedObjectLiterals\n/* globals QUnit,Hammer,Simulator */\n\nva"
},
{
"path": "tests/unit/gestures/test_swipe.js",
"chars": 730,
"preview": "// jscs:disable requireArrowFunctions,disallowVar,requireEnhancedObjectLiterals\n/* globals QUnit,Hammer,utils,Simulator "
},
{
"path": "tests/unit/index.html",
"chars": 1508,
"preview": "<!DOCTYPE html>\n<html>\n<head lang=\"en\">\n <meta charset=\"UTF-8\">\n <title>Tests</title>\n <link rel=\"stylesheet\" h"
},
{
"path": "tests/unit/test_enable.js",
"chars": 4910,
"preview": "// jscs:disable requireArrowFunctions,disallowVar,requireEnhancedObjectLiterals\n/* globals QUnit,Hammer,utils */\n/* jshi"
},
{
"path": "tests/unit/test_events.js",
"chars": 1913,
"preview": "// jscs:disable requireArrowFunctions,disallowVar,requireEnhancedObjectLiterals\n/* globals QUnit,Hammer,utils,el */\n\nQUn"
},
{
"path": "tests/unit/test_gestures.js",
"chars": 6492,
"preview": "// jscs:disable requireArrowFunctions,disallowVar,requireEnhancedObjectLiterals\n/* globals QUnit,Hammer,utils,Simulator "
},
{
"path": "tests/unit/test_hammer.js",
"chars": 6652,
"preview": "// jscs:disable requireArrowFunctions,disallowVar,requireEnhancedObjectLiterals,requireTemplateStringsForConcatenation,r"
},
{
"path": "tests/unit/test_jquery_plugin.js",
"chars": 1869,
"preview": "// jscs:disable requireArrowFunctions,disallowVar,requireEnhancedObjectLiterals\n/* globals QUnit,Hammer,utils,Simulator,"
},
{
"path": "tests/unit/test_multiple_taps.js",
"chars": 3047,
"preview": "// jscs:disable requireArrowFunctions,disallowVar,requireEnhancedObjectLiterals\n/* globals QUnit,Hammer,utils*/\n\nvar el;"
},
{
"path": "tests/unit/test_nested_gesture_recognizers.js",
"chars": 4559,
"preview": "// jscs:disable requireArrowFunctions,disallowVar,requireEnhancedObjectLiterals\n/* globals QUnit,Hammer,utils*/\n/*jshint"
},
{
"path": "tests/unit/test_propagation_bubble.js",
"chars": 1730,
"preview": "// jscs:disable requireArrowFunctions,disallowVar,requireEnhancedObjectLiterals\n/* globals QUnit,Hammer,utils*/\n/*jshint"
},
{
"path": "tests/unit/test_require_failure.js",
"chars": 3441,
"preview": "// jscs:disable requireArrowFunctions,disallowVar,requireEnhancedObjectLiterals\n/* globals QUnit,Hammer,utils,Simulator "
},
{
"path": "tests/unit/test_simultaneous_recognition.js",
"chars": 7592,
"preview": "// jscs:disable requireArrowFunctions,disallowVar,requireEnhancedObjectLiterals\n/* globals QUnit,Hammer,utils*/\n\nvar el;"
},
{
"path": "tests/unit/test_utils.js",
"chars": 5071,
"preview": "// jscs:disable requireArrowFunctions,disallowVar,requireEnhancedObjectLiterals\n/* globals QUnit,Hammer,utils,_*/\n\nQUnit"
}
]
About this extraction
This page contains the full source code of the hammerjs/hammer.js GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 107 files (262.0 KB), approximately 70.3k tokens, and a symbol index with 269 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.