├── .bowerrc
├── src
├── assets
│ └── inner-header-bg.jpg
├── css
│ ├── touch-menu-la.min.css
│ ├── touch-menu-la.css
│ └── sample.css
└── js
│ ├── touch-menu-la.min.js
│ ├── touch-menu-la.min.js.map
│ └── touch-menu-la.js
├── lib
└── hammerjs
│ ├── bower.json
│ ├── .bower.json
│ ├── LICENSE.md
│ ├── README.md
│ ├── CONTRIBUTING.md
│ ├── changelog.js
│ ├── CHANGELOG.md
│ ├── hammer.min.js
│ ├── hammer.min.map
│ └── hammer.js
├── bower.json
├── package.json
├── sample.html
└── README.md
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory":"lib"
3 | }
--------------------------------------------------------------------------------
/src/assets/inner-header-bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericktatsui/Touch-Menu-Like-Android/HEAD/src/assets/inner-header-bg.jpg
--------------------------------------------------------------------------------
/lib/hammerjs/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hammerjs",
3 | "main": "hammer.js",
4 | "ignore": [
5 | "tests",
6 | "src",
7 | ".bowerrc",
8 | ".gitignore",
9 | ".jscsrc",
10 | ".jshintrc",
11 | ".travis.yml",
12 | "component.json",
13 | "Gruntfile.coffee",
14 | "package.json"
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/src/css/touch-menu-la.min.css:
--------------------------------------------------------------------------------
1 | .tmla-mask,.touch-menu-la,.touch-menu-la .tmla-handle{height:100%;position:absolute;top:0}.touch-menu-la{width:280px;left:-280px;background-color:#FFF}.touch-menu-la.opened{transition:all .3s ease!important;-webkit-transition:all .3s ease!important;-moz-transition:all .3s ease!important}.touch-menu-la.closed{transition:all .3s ease!important;-webkit-transition:all .3s ease!important;-moz-transition:all .3s ease!important;transform:translate3d(0,0,0)!important;-webkit-transform:translate3d(0,0,0)!important;-moz-transform:translate3d(0,0,0)!important}.tmla-mask{width:100%;left:0;background-color:#000;opacity:0;z-index:-1}
--------------------------------------------------------------------------------
/lib/hammerjs/.bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hammerjs",
3 | "main": "hammer.js",
4 | "ignore": [
5 | "tests",
6 | "src",
7 | ".bowerrc",
8 | ".gitignore",
9 | ".jscsrc",
10 | ".jshintrc",
11 | ".travis.yml",
12 | "component.json",
13 | "Gruntfile.coffee",
14 | "package.json"
15 | ],
16 | "homepage": "https://github.com/hammerjs/hammer.js",
17 | "version": "2.0.6",
18 | "_release": "2.0.6",
19 | "_resolution": {
20 | "type": "version",
21 | "tag": "v2.0.6",
22 | "commit": "05f0872c6130daf537bad27a393310cd5833c9a1"
23 | },
24 | "_source": "git://github.com/hammerjs/hammer.js.git",
25 | "_target": "^2.0.6",
26 | "_originalSource": "hammer.js"
27 | }
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "touch-menu-la",
3 | "description": "A touch menu like Navigation Drawer of Android.",
4 | "main": "sample.hmtl",
5 | "authors": [
6 | "Erick Tatsui"
7 | ],
8 | "license": "MIT",
9 | "keywords": [
10 | "menu",
11 | "slide",
12 | "menu",
13 | "like",
14 | "android",
15 | "navigator",
16 | "drawer",
17 | "html5",
18 | "javascript",
19 | "plugin",
20 | "touch",
21 | "menu",
22 | "cordova",
23 | "ionic",
24 | "like android"
25 | ],
26 | "homepage": "https://github.com/ericktatsui/Touch-Menu-Like-Android",
27 | "moduleType": [],
28 | "ignore": [
29 | "**/.*",
30 | "node_modules",
31 | "bower_components",
32 | "test",
33 | "tests"
34 | ],
35 | "dependencies": {
36 | "hammerjs": "hammer.js#^2.0.6"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "touch-menu-la",
3 | "version": "0.5.0",
4 | "description": "A touch menu like Navigation Drawer of Android.",
5 | "main": "sample.hmtl",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/ericktatsui/Touch-Menu-Like-Android.git"
12 | },
13 | "keywords": [
14 | "menu",
15 | "slide",
16 | "menu",
17 | "like",
18 | "android",
19 | "navigator",
20 | "drawer",
21 | "html5",
22 | "javascript",
23 | "plugin",
24 | "touch",
25 | "menu",
26 | "cordova",
27 | "ionic",
28 | "like android"
29 | ],
30 | "author": "Erick Tatsui",
31 | "license": "MIT",
32 | "bugs": {
33 | "url": "https://github.com/ericktatsui/Touch-Menu-Like-Android/issues"
34 | },
35 | "homepage": "https://github.com/ericktatsui/Touch-Menu-Like-Android#readme"
36 | }
37 |
--------------------------------------------------------------------------------
/src/css/touch-menu-la.css:
--------------------------------------------------------------------------------
1 | .touch-menu-la {
2 | width: 280px;
3 | height: 100%;
4 | position: absolute;
5 | top: 0;
6 | left: -280px;
7 | background-color: #FFF;
8 | }
9 |
10 | .touch-menu-la .tmla-handle {
11 | position: absolute;
12 | /* right: -20px; */
13 | top: 0;
14 | /* width: 20px; */
15 | height: 100%;
16 | }
17 |
18 | .touch-menu-la.opened {
19 | transition: all 0.3s ease-out !important;
20 | -webkit-transition: all 0.3s ease-out !important;
21 | -moz-transition: all 0.3s ease-out !important;
22 | }
23 |
24 | .touch-menu-la.closed {
25 | transition: all 0.3s ease-out !important;
26 | -webkit-transition: all 0.3s ease-out !important;
27 | -moz-transition: all 0.3s ease-out !important;
28 | transform: none !important;
29 | -webkit-transform: none !important;
30 | -moz-transform: none !important;
31 | }
32 |
33 | .tmla-mask {
34 | width: 100%;
35 | height: 100%;
36 | position: absolute;
37 | top: 0;
38 | left: 0;
39 | background-color: #000;
40 | opacity: 0.0;
41 | z-index: -1;
42 | }
43 |
--------------------------------------------------------------------------------
/lib/hammerjs/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (C) 2011-2014 by Jorik Tangelder (Eight Media)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/lib/hammerjs/README.md:
--------------------------------------------------------------------------------
1 | # Hammer.js 2.0.6
2 |
3 | [](https://travis-ci.org/hammerjs/hammer.js)
4 |
5 | ## Support, Questions, and Collaboration
6 |
7 | [](https://hammerjs.herokuapp.com/)
8 |
9 | ## Documentation
10 |
11 | Visit [hammerjs.github.io](http://hammerjs.github.io) for detailed documentation.
12 |
13 | ```js
14 | // get a reference to an element
15 | var stage = document.getElementById('stage');
16 |
17 | // create a manager for that element
18 | var mc = new Hammer.Manager(stage);
19 |
20 | // create a recognizer
21 | var Rotate = new Hammer.Rotate();
22 |
23 | // add the recognizer
24 | mc.add(Rotate);
25 |
26 | // subscribe to events
27 | mc.on('rotate', function(e) {
28 | // do something cool
29 | var rotation = Math.round(e.rotation);
30 | stage.style.transform = 'rotate('+rotation+'deg)';
31 | });
32 | ```
33 |
34 |
35 | ## Contributing
36 |
37 | Read the [contributing guidelines](./CONTRIBUTING.md).
38 |
39 | For PRs.
40 |
41 | - Use [Angular Style commit messages](https://github.com/angular/angular.js/blob/v1.4.8/CONTRIBUTING.md#commit)
42 | - Rebase your PR branch when necessary
43 | - If you add a feature or fix a bug, please add or fix any necessary tests.
44 | - If a new feature, open a docs PR to go with.
45 |
46 | ## Building
47 |
48 | You can get the pre-build versions from the Hammer.js website, or do this by yourself running
49 | `npm install -g grunt-cli && npm install && grunt build`
50 |
--------------------------------------------------------------------------------
/lib/hammerjs/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Hammer.js
2 |
3 | Looking to contribute something to Hammer.js? **Here's how you can help.**
4 |
5 |
6 | ## Reporting issues
7 |
8 | We only accept issues that are bug reports or feature requests. Bugs must be
9 | isolated and reproducible problems that can be fixed within the Hammer.js.
10 | Please read the following guidelines before opening any issue.
11 |
12 | 1. [**Read the documentation**](https://hammerjs.github.io)
13 |
14 | 2. **Search for existing issues.** We get a lot of duplicate issues, and you'd
15 | help us out a lot by first checking if someone else has reported the same issue.
16 | Moreover, the issue may have already been resolved with a fix available. Also
17 | take a look if your problem is explained at the Wiki.
18 |
19 | 3. **Create an isolated and reproducible test case.** Be sure the problem exists
20 | in Hammer's code with a reduced test case that should be included in each bug
21 | report.
22 |
23 | 4. **Include a live example.** Make use of jsFiddle or jsBin to share your
24 | isolated test cases. Also, a screen capture would work, with tools like LICEcap.
25 |
26 | 5. **Share as much information as possible.** Include operating system and
27 | version, browser and version, version of Hammer.js, customized or vanilla build,
28 | etc. where appropriate. Also include steps to reproduce the bug.
29 |
30 | ## Pull requests
31 |
32 | 1. Changes must be done in `/src` files, never just the compiled files. Also, don't
33 | commit the compiled files.
34 |
35 | 2. Try not to pollute your pull request with unintended changes. Keep them simple
36 | and small
37 |
38 | 3. Try to share which browsers your code has been tested in before submitting a
39 | pull request
40 |
41 | 4. Write tests for your code, these can be found in `/tests`.
42 |
--------------------------------------------------------------------------------
/lib/hammerjs/changelog.js:
--------------------------------------------------------------------------------
1 | var changelog = require( "changelogplease" );
2 | var gittags = require( "git-tags" ).get( function( error, tags ) {
3 | if ( error ) {
4 | throw error
5 | }
6 | console.log( tags[ 1 ] + ".." + tags[ 0 ] );
7 | var exclude = [ "Merge", "Whitespace", "Fixup", "Cleanup", "Formatting", "Ignore" ];
8 | changelog( {
9 | ticketUrl: "https://github.com/hammerjs/hammer.js/issues/{id}",
10 | commitUrl: "https://github.com/hammerjs/hammerjs/commit/{id}",
11 | sort: false,
12 | repo: "./",
13 | committish: tags[ 1 ] + ".." + tags[ 0 ]
14 | }, function( error, log ) {
15 | if ( error ) {
16 | throw error;
17 | }
18 | log = parseLog( log );
19 | console.log( log );
20 | } );
21 | function parseLog( log ) {
22 | var lines = log.split( "\n" );
23 | var newLog = [];
24 | var log = [];
25 | var currentComponent;
26 |
27 |
28 | lines.shift();
29 | lines.forEach( function( line ) {
30 | var newLine = parseLine( line );
31 | if ( newLine ) {
32 | log.push( line );
33 | }
34 | } );
35 | var log = log.join( "\n" );
36 | return log.replace( /\*/g, "-" ).replace( /__TICKETREF__,/g, "" );
37 | }
38 | function parseLine( line ) {
39 | var parts = getParts( line );
40 |
41 | if ( exclude.indexOf( parts.component ) > -1 ) {
42 | return false;
43 | }
44 | return parts;
45 | }
46 | function getParts( line ) {
47 | var parts = line.split( ":" );
48 | var component = "";
49 | var message;
50 | var commits = line.match( /\{\{([A-Za-z0-9 ]){0,99}\}\}/ )
51 |
52 | if ( parts.length > 1 && parts[ 0 ].length <= 20 ) {
53 | component = parts[ 0 ];
54 | parts.shift();
55 | message = parts.join( ":" );
56 | } else {
57 | parts = line.split( " " );
58 | component = parts[ 1 ];
59 | parts.shift();
60 | message = parts.join( " " );
61 | }
62 |
63 | if ( component ) {
64 | component = component.replace( /\* |,/, "" );
65 | }
66 | return {
67 | component: component,
68 | message: message
69 | };
70 | }
71 | } );
72 |
--------------------------------------------------------------------------------
/src/css/sample.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | background: #ECEFF1;
4 | font-family: Roboto;
5 | }
6 |
7 | header {
8 | width: 100%;
9 | height: 56px;
10 | background: #3F51B5;
11 | box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
12 | }
13 | header i.fa, header i.material-icons {
14 | height: 56px;
15 | width: 56px;
16 | line-height: 56px;
17 | text-align: center;
18 | color: #FFF;
19 | float: left;
20 | cursor: pointer;
21 | }
22 |
23 | header span.title {
24 | height: 56px;
25 | color: #FFF;
26 | float: left;
27 | line-height: 56px;
28 | }
29 |
30 | i {
31 | font-size: 22px !important;
32 | }
33 |
34 | .touch-menu-la .inner-header {
35 | width: 100%;
36 | height: 100px;
37 | background-image: url(../../src/assets/inner-header-bg.jpg);
38 | font-size: 26px;
39 | font-weight: 300;
40 | text-align: center;
41 | color: #FFF;
42 | padding: 20px 0;
43 | box-sizing: border-box;
44 | }
45 |
46 | .touch-menu-la .inner-header span {
47 | display: block;
48 | font-size: 16px;
49 | }
50 |
51 | .menu-items {
52 | list-style: none;
53 | margin: 16px 0;
54 | padding: 0;
55 | }
56 |
57 | .menu-items li a {
58 | padding: 0 16px;
59 | height: 48px;
60 | line-height: 48px;
61 | display: inline-block;
62 | width: 100%;
63 | box-sizing: border-box;
64 | text-decoration: none;
65 | color: #000;
66 | }
67 |
68 | .menu-items li:hover a {
69 | background: #F3F3F3;
70 | cursor: pointer;
71 | }
72 |
73 | .menu-items li i {
74 | width: 24px;
75 | height: 24px;
76 | float: left;
77 | margin-top: 12px;
78 | margin-right: 32px;
79 | }
80 |
81 | .inner-footer {
82 | padding: 16px;
83 | font-size: 14px;
84 | }
85 |
86 | .center-icon {
87 | height: 56px;
88 | width: 100%;
89 | position: absolute;
90 | top: 50%;
91 | left: 0;
92 | z-index: 0;
93 | margin-top: -28px;
94 | color: #CCC;
95 | pointer-events: none;
96 | }
97 | .center-icon i {
98 | font-size: 56px !important;
99 | color: #CCC;
100 | margin-left: 10px;
101 | float: left;
102 | }
103 |
104 | .center-icon i.arrow{
105 | font-size: 20px;
106 | margin-top: 20px;
107 | margin-left: 20px;
108 | }
109 |
110 | .center-icon .text {
111 | height: 56px;
112 | line-height: 70px;
113 | margin-left: 20px;
114 | float: left;
115 | font-size: 25px;
116 | }
--------------------------------------------------------------------------------
/sample.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Sample - Touch Menu Like Android
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Touch Menu L.A.
17 |
18 |
19 |
20 |
keyboard_backspace
21 |
touch_app
22 |
Drag
23 |
24 |
25 |
49 |
50 |
51 |
52 |
61 |
62 |
--------------------------------------------------------------------------------
/src/js/touch-menu-la.min.js:
--------------------------------------------------------------------------------
1 | var TouchMenuLA=function(n){var t,s,e="",r,f,h,c,u=0,o=0,v=0,l=0,a=0,i=function(){t=this;s={width:280,zIndex:99999,disableSlide:!1,handleSize:20,disableMask:!1,maxMaskOpacity:.5};this.isVisible=!1;this.initialize()};return i.prototype.setDefaultsOptions=function(){for(var t in s)n[t]||(n[t]=s[t])},i.prototype.initElements=function(){n.target.style.zIndex=n.zIndex;n.target.style.width=n.width+"px";n.target.style.left=-n.width+"px";f=document.createElement("div");f.className="tmla-handle";f.style.width=n.handleSize+"px";f.style.right=-n.handleSize+"px";n.target.appendChild(f);n.disableMask||(r=document.createElement("div"),r.className="tmla-mask",document.body.appendChild(r),c=new Hammer(r,null))},i.prototype.touchStartMenu=function(){h.on("panstart panmove",function(n){u=o+n.deltaX;t.changeMenuPos();a=Math.abs(n.velocity)})},i.prototype.changeMenuPos=function(){u<=n.width&&(n.target.className=e+" tmla-menu",n.target.style.transform="translate3d("+u+"px, 0, 0)",n.target.style.WebkitTransform="translate3d("+u+"px, 0, 0)",n.target.style.MozTransform="translate3d("+u+"px, 0, 0)",n.disableMask||this.setMaskOpacity(u))},i.prototype.setMaskOpacity=function(t){var i=parseFloat(t/n.width*n.maxMaskOpacity);r.style.opacity=i;r.style.zIndex=i===0?-1:n.zIndex-1},i.prototype.touchEndMenu=function(){h.on("panend pancancel",function(n){o=n.deltaX;t.checkMenuState(n.deltaX)})},i.prototype.eventStartMask=function(){c.on("panstart panmove",function(i){i.center.x<=n.width&&t.isVisible&&(l++,l==1&&(v=i.deltaX),i.deltaX<0&&(u=i.deltaX-v+n.width,t.changeMenuPos(),a=Math.abs(i.velocity)))})},i.prototype.eventEndMask=function(){c.on("panend pancancel",function(n){t.checkMenuState(n.deltaX);l=0})},i.prototype.clickMaskClose=function(){r.addEventListener("click",function(){t.close()})},i.prototype.checkMenuState=function(n){a>=1?n>=0?t.open():t.close():u>=100?t.open():t.close()},i.prototype.open=function(){n.target.className=e+" tmla-menu opened";n.target.style.transform="translate3d("+n.width+"px, 0, 0)";n.target.style.WebkitTransform="translate3d("+n.width+"px, 0, 0)";n.target.style.MozTransform="translate3d("+n.width+"px, 0, 0)";o=n.width;this.isVisible=!0;t.showMask();t.invoke(n.onOpen)},i.prototype.close=function(){n.target.className=e+" tmla-menu closed";o=0;t.isVisible=!1;t.hideMask();t.invoke(n.onClose)},i.prototype.toggle=function(){t.isVisible?t.close():t.open()},i.prototype.showMask=function(){r.className="tmla-mask transition";r.style.opacity=n.maxMaskOpacity;r.style.zIndex=n.zIndex-1},i.prototype.hideMask=function(){r.className="tmla-mask transition";r.style.opacity=0;r.style.zIndex=-1},i.prototype.setMenuClassName=function(){e=n.target.className},i.prototype.invoke=function(n){n&&n.apply(t)},i.prototype.initialize=function(){n.target?(h=Hammer(n.target,null),t.setDefaultsOptions(),t.setMenuClassName(),t.initElements(),n.disableSlide||(t.touchStartMenu(),t.touchEndMenu(),t.eventStartMask(),t.eventEndMask()),n.disableMask||t.clickMaskClose()):console.error("TouchMenuLA: The option 'target' is required.")},new i};
2 | //# sourceMappingURL=touch-menu-la.min.js.map
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Touch Menu Like Android [DEPRECATED]
2 |
3 | **[Warning] This library is deprecated, it hasn't received any updates since 2017.**
4 |
5 | ----
6 |
7 | hammer.js to touch events.
8 |
9 | > A touch menu like Navigation Drawer of Android.
10 |
11 | Use with cordova project, ionic.
12 |
13 | # Demo
14 | [Click here to see a demo](http://ericktatsui.github.io/Touch-Menu-Like-Android/sample.html)
15 |
16 | ## Browser Support
17 |
18 | Browser | Support
19 | --- | ---
20 | I.E | 10+ ✔
21 | Chrome | 4+ ✔
22 | Firefox | 3.5+ ✔
23 | Opera | 23+ ✔
24 | Safari | 3.1+ ✔
25 | Android Browser | 2.3.4+ ✔
26 | iOS Safari | 5.1+ ✔
27 |
28 | ## Getting started
29 |
30 | Four ways:
31 |
32 | * [Download .zip](https://github.com/ericktatsui/Touch-Menu-Like-Android/zipball/master)
33 | * Clone the repo: `git@github.com:ericktatsui/Touch-Menu-Like-Android.git`
34 | * Install with [Bower](http://bower.io/) `bower install touch-menu-la`
35 | * Install with [npm](https://npmjs.com/) `npm install touch-menu-la`
36 |
37 | ## Usage
38 |
39 | Include plugin's css
40 | ```html
41 |
42 | ```
43 |
44 | Include a div with class 'touch-menu-la'
45 | ```html
46 |
47 | ```
48 |
49 | Include hammer.js and plugin's js
50 | ```html
51 |
52 |
53 | ```
54 |
55 | Call the plugin with required option 'target'
56 | ```javascript
57 | TouchMenuLA({
58 | target: document.getElementById('menu')
59 | });
60 | ```
61 |
62 | ## Options
63 |
64 | list of avaliable settings:
65 |
66 | Attribute | Type | Default | Description
67 | --- | --- | --- | ---
68 | `target` | *DOM Element* | | *Required* - The target element for the plugin to create the menu
69 | `width` | *Integer* | `280` | Width of menu
70 | `zIndex` | *Integer* | `99999` | Style z-index of menu and mask
71 | `disableSlide` | *Boolean* | `false` | Disable slide of menu
72 | `handleSize` | *Integer* | `20` | Size of handle to slide
73 | `disableMask` | *Boolean* | `false` | Disable the mask
74 | `maxMaskOpacity` | *Double* | `0.5` | Max opacity of mask (when menu is opened). Numbers between 0 and 1.
75 | `onOpen` | *Function* | | Function called when menu is opened. (the scope is this menu instance, try to use 'this')
76 | `onClose` | *Function* | | Function called when menu is closed. (the scope is this menu instance, try to use 'this')
77 |
78 | ## Property and Methods
79 |
80 | You can use this property and methods after calling the plugin.
81 |
82 | **Property**
83 |
84 | isVisible - Return a `Boolean` of state of menu
85 |
86 | **Methods**
87 |
88 | open(); - Open the menu
89 |
90 | close(); - close the menu
91 |
92 | toggle(); - open or close menu
93 |
94 |
95 | **See this example:**
96 |
97 | ```javascript
98 | var TouchMenu = TouchMenuLA({
99 | target: document.getElementById('menu'),
100 | onOpen: function(){
101 | console.log(this.isVisible);
102 | }
103 | });
104 |
105 | document.getElementByID('any-element').addEventListener('click', function(){
106 | TouchMenu.open();
107 | });
108 | ```
109 |
110 | ## License
111 | ```license
112 | The MIT License (MIT)
113 |
114 | Copyright (c) 2016 Erick R. Alves Aguiar
115 |
116 | Permission is hereby granted, free of charge, to any person obtaining a copy
117 | of this software and associated documentation files (the "Software"), to deal
118 | in the Software without restriction, including without limitation the rights
119 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
120 | copies of the Software, and to permit persons to whom the Software is
121 | furnished to do so, subject to the following conditions:
122 |
123 | The above copyright notice and this permission notice shall be included in all
124 | copies or substantial portions of the Software.
125 |
126 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
127 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
128 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
129 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
130 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
131 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
132 | SOFTWARE.
133 | ```
134 |
--------------------------------------------------------------------------------
/lib/hammerjs/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ### 2.0.6, 2015-12-23
4 | - 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))
5 | - 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))
6 | - 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))
7 | - Fixed moz-prefix, prefix should be Moz not moz. ([3ea47f3](https://github.com/hammerjs/hammer.js/commit/3ea47f3aebadc9d3bb6bf52bc8402cad135ef8a9))
8 | - Removed non-existant recognizer ([f1c2d3b](https://github.com/hammerjs/hammer.js/commit/f1c2d3bf05f530ae092ecfc2335fceeff0e9eec9))
9 | - Fixed config leaking between instances([189098f](https://github.com/hammerjs/hammer.js/commit/189098ff7736f6ed2fce9a3d3e1f5a3afee085ba))
10 | - Fixed gaps in gesture configs and update tests to match ([70c2902](https://github.com/hammerjs/hammer.js/commit/70c2902d773a750e92ce8c423f8a4165c07eab97))
11 | - Fixed Manager off method ([#768](https://github.com/hammerjs/hammer.js/issues/768), [da49a27](https://github.com/hammerjs/hammer.js/commit/da49a2730779ecc3b4dd147cc418a0df7c70fad9))
12 | - Added compatibility with requirejs optimizer namespaces ( [70075f2](https://github.com/hammerjs/hammer.js/commit/70075f2df1b855f7c6d8d3caac49b9276b88c8d6))
13 | - Made touchaction test zoomable ( [50264a7](https://github.com/hammerjs/hammer.js/commit/50264a70251ca88bbaf7b666401e527eee616de5))
14 | - Fixed preventing default when for `pan-x pan-y` case ( [95eaafa](https://github.com/hammerjs/hammer.js/commit/95eaafadad27bd1b25d20cf976811a451922f1c4))
15 | - Fixed incorrect touch action pan direction ( [a81da57](https://github.com/hammerjs/hammer.js/commit/a81da57a82ebf37e695e7c443e4e2715e7f32856))
16 | - Fixed combined pan-x pan-y to resolve to none ( [fdae07b](https://github.com/hammerjs/hammer.js/commit/fdae07bc2ba3c90aad28da6791b3d5df627bc612))
17 | - 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))
18 | - 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))
19 | - 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))
20 | - Fixed swipe recognizer to use overall gesture direction and velocity ( [963fe69](https://github.com/hammerjs/hammer.js/commit/963fe697515273fee508414bc29e2656465cea55))
21 | - Fixed getDirection returning reversed direction ( [e40dcde](https://github.com/hammerjs/hammer.js/commit/e40dcde43bdac7a74c8ce5c05a4f62121089cd91))
22 | - Fixed detection of tap when multi touch gestures are present ( [c46cbba](https://github.com/hammerjs/hammer.js/commit/c46cbba1c2cbbf874b59913416858d9dae297e64))
23 | - Fixed incorrect event order ([#824](https://github.com/hammerjs/hammer.js/issues/824), [92f2d76](https://github.com/hammerjs/hammer.js/commit/92f2d76188480d967e738a19cd508d0b94a31329))
24 | - Fixed leaking options between recognizer instances ([#813](https://github.com/hammerjs/hammer.js/issues/813), [af32c9b](https://github.com/hammerjs/hammer.js/commit/af32c9bace3f04bb34bee852ff56a33cc8fc27cd))
25 | - Fixed detection when element has no style attribute ( [5ca6d8c](https://github.com/hammerjs/hammer.js/commit/5ca6d8cbead02c71929a8073e95ddf98e11c0e06))
26 |
27 | ### 2.0.4, 2014-09-28
28 | - Fix IE pointer issue. [#665](https://github.com/hammerjs/hammer.js/pull/665)
29 | - Fix multi-touch at different elements. [#668](https://github.com/hammerjs/hammer.js/pull/668)
30 | - 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.
31 |
32 | ### 2.0.3, 2014-09-10
33 | - Manager.set improvements.
34 | - Fix requireFailure() call in Manager.options.recognizers.
35 | - Make DIRECTION_ALL for pan and swipe gestures less blocking.
36 | - Fix Swipe recognizer threshold option.
37 | - Expose the Input classes.
38 | - Added the option `inputClass` to set the used input handler.
39 |
40 | ### 2.0.2, 2014-07-26
41 | - Improved mouse and pointer-events input, now able to move outside the window.
42 | - Added the export name (`Hammer`) as an argument to the wrapper.
43 | - Add the option *experimental* `inputTarget` to change the element that receives the events.
44 | - Improved performance when only one touch being active.
45 | - Fixed the jumping deltaXY bug when going from single to multi-touch.
46 | - Improved velocity calculations.
47 |
48 | ### 2.0.1, 2014-07-15
49 | - Fix issue when no document.body is available
50 | - Added pressup event for the press recognizer
51 | - Removed alternative for Object.create
52 |
53 | ### 2.0.0, 2014-07-11
54 | - Full rewrite of the library.
55 |
--------------------------------------------------------------------------------
/src/js/touch-menu-la.min.js.map:
--------------------------------------------------------------------------------
1 | {
2 | "version":3,
3 | "file":"touch-menu-la.min.js",
4 | "lineCount":1,
5 | "mappings":"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",
6 | "sources":["touch-menu-la.js"],
7 | "names":["TouchMenuLA","options","self","defaults","menuClassName","mask","handle","menuHammer","maskHammer","newPos","currentPos","startPoint","countStart","velocity","isVisible","initialize","prototype","setDefaultsOptions","TouchMenuLA.prototype.setDefaultsOptions","key","initElements","TouchMenuLA.prototype.initElements","target","style","zIndex","width","left","document","createElement","className","handleSize","right","appendChild","disableMask","body","Hammer","touchStartMenu","TouchMenuLA.prototype.touchStartMenu","on","ev","deltaX","changeMenuPos","Math","abs","TouchMenuLA.prototype.changeMenuPos","transform","WebkitTransform","MozTransform","setMaskOpacity","TouchMenuLA.prototype.setMaskOpacity","newMenuPos","opacity","parseFloat","maxMaskOpacity","touchEndMenu","TouchMenuLA.prototype.touchEndMenu","checkMenuState","eventStartMask","TouchMenuLA.prototype.eventStartMask","center","x","eventEndMask","TouchMenuLA.prototype.eventEndMask","clickMaskClose","TouchMenuLA.prototype.clickMaskClose","addEventListener","close","TouchMenuLA.prototype.checkMenuState","open","TouchMenuLA.prototype.open","showMask","invoke","onOpen","TouchMenuLA.prototype.close","hideMask","onClose","toggle","TouchMenuLA.prototype.toggle","TouchMenuLA.prototype.showMask","TouchMenuLA.prototype.hideMask","setMenuClassName","TouchMenuLA.prototype.setMenuClassName","TouchMenuLA.prototype.invoke","fn","apply","TouchMenuLA.prototype.initialize","disableSlide","console","error"]
8 | }
9 |
--------------------------------------------------------------------------------
/src/js/touch-menu-la.js:
--------------------------------------------------------------------------------
1 | var TouchMenuLA = function (options) {
2 | var self,
3 | defaults,
4 | menuClassName = '',
5 | mask,
6 | handle,
7 | menuHammer,
8 | maskHammer,
9 | newPos = 0,
10 | currentPos = 0,
11 | startPoint = 0,
12 | countStart = 0,
13 | velocity = 0.0;
14 |
15 | var TouchMenuLA = function () {
16 | self = this;
17 |
18 | defaults = {
19 | width: 280,
20 | zIndex: 99999,
21 | disableSlide: false,
22 | handleSize: 20,
23 | disableMask: false,
24 | maxMaskOpacity: 0.5
25 | };
26 |
27 | this.isVisible = false;
28 |
29 | this.initialize();
30 | };
31 |
32 | TouchMenuLA.prototype.setDefaultsOptions = function () {
33 | for (var key in defaults) {
34 | if (!options[key]) {
35 | options[key] = defaults[key];
36 | }
37 | }
38 | };
39 |
40 | TouchMenuLA.prototype.initElements = function () {
41 | options.target.style.zIndex = options.zIndex;
42 | options.target.style.width = options.width + 'px';
43 | options.target.style.left = -options.width + 'px';
44 |
45 | handle = document.createElement('div');
46 | handle.className = "tmla-handle";
47 | handle.style.width = options.handleSize + 'px';
48 | handle.style.right = -options.handleSize + 'px';
49 |
50 | options.target.appendChild(handle);
51 |
52 | if (!options.disableMask) {
53 | mask = document.createElement('div');
54 | mask.className = 'tmla-mask';
55 | document.body.appendChild(mask);
56 |
57 | maskHammer = new Hammer(mask, null);
58 | }
59 | };
60 |
61 | TouchMenuLA.prototype.touchStartMenu = function () {
62 | menuHammer.on('panstart panmove', function (ev) {
63 | newPos = currentPos + ev.deltaX;
64 | self.changeMenuPos();
65 | velocity = Math.abs(ev.velocity);
66 | });
67 | };
68 |
69 | TouchMenuLA.prototype.animateToPosition = function (pos) {
70 | options.target.style.transform = 'translate3d(' + pos + 'px, 0, 0)';
71 | options.target.style.WebkitTransform = 'translate3d(' + pos + 'px, 0, 0)';
72 | options.target.style.MozTransform = 'translate3d(' + pos + 'px, 0, 0)';
73 |
74 | };
75 |
76 | TouchMenuLA.prototype.changeMenuPos = function () {
77 | if (newPos <= options.width) {
78 | options.target.className = menuClassName + ' tmla-menu';
79 | this.animateToPosition(newPos);
80 |
81 | if (!options.disableMask) {
82 | this.setMaskOpacity(newPos);
83 | }
84 | }
85 | };
86 |
87 | TouchMenuLA.prototype.setMaskOpacity = function (newMenuPos) {
88 | var opacity = parseFloat((newMenuPos / options.width) * options.maxMaskOpacity);
89 |
90 | mask.style.opacity = opacity;
91 |
92 | if (opacity === 0) {
93 | mask.style.zIndex = -1;
94 | } else {
95 | mask.style.zIndex = options.zIndex - 1;
96 | }
97 | };
98 |
99 | TouchMenuLA.prototype.touchEndMenu = function () {
100 | menuHammer.on('panend pancancel', function (ev) {
101 | currentPos = ev.deltaX;
102 | self.checkMenuState(ev.deltaX);
103 | });
104 | };
105 |
106 | TouchMenuLA.prototype.eventStartMask = function () {
107 | maskHammer.on('panstart panmove', function (ev) {
108 | if (ev.center.x <= options.width && self.isVisible) {
109 | countStart++;
110 |
111 | if (countStart == 1) {
112 | startPoint = ev.deltaX;
113 | }
114 |
115 | if (ev.deltaX < 0) {
116 | newPos = (ev.deltaX - startPoint) + options.width;
117 | self.changeMenuPos();
118 | velocity = Math.abs(ev.velocity);
119 | }
120 | }
121 | });
122 | };
123 |
124 | TouchMenuLA.prototype.eventEndMask = function () {
125 | maskHammer.on('panend pancancel', function (ev) {
126 | self.checkMenuState(ev.deltaX);
127 | countStart = 0;
128 | });
129 | };
130 |
131 | TouchMenuLA.prototype.clickMaskClose = function () {
132 | mask.addEventListener('click', function () {
133 | self.close();
134 | });
135 | };
136 |
137 | TouchMenuLA.prototype.checkMenuState = function (deltaX) {
138 | if (velocity >= 1.0) {
139 | if (deltaX >= 0) {
140 | self.open();
141 | } else {
142 | self.close();
143 | }
144 | } else {
145 | if (newPos >= 100) {
146 | self.open();
147 | } else {
148 | self.close();
149 | }
150 | }
151 | };
152 |
153 | TouchMenuLA.prototype.open = function () {
154 | options.target.className = menuClassName + " tmla-menu opened";
155 | this.animateToPosition(options.width);
156 |
157 | currentPos = options.width;
158 | this.isVisible = true;
159 |
160 | self.showMask();
161 | self.invoke(options.onOpen);
162 | };
163 |
164 | TouchMenuLA.prototype.close = function () {
165 | options.target.className = menuClassName + " tmla-menu closed";
166 | currentPos = 0;
167 | self.isVisible = false;
168 |
169 | self.hideMask();
170 | self.invoke(options.onClose);
171 | };
172 |
173 | TouchMenuLA.prototype.toggle = function () {
174 | if (self.isVisible) {
175 | self.close();
176 | } else {
177 | self.open();
178 | }
179 | };
180 |
181 | TouchMenuLA.prototype.showMask = function () {
182 | mask.className = "tmla-mask transition";
183 | mask.style.opacity = options.maxMaskOpacity;
184 | mask.style.zIndex = options.zIndex - 1;
185 | };
186 |
187 | TouchMenuLA.prototype.hideMask = function () {
188 | mask.className = "tmla-mask transition";
189 | mask.style.opacity = 0;
190 | mask.style.zIndex = -1;
191 | };
192 |
193 | TouchMenuLA.prototype.setMenuClassName = function () {
194 | menuClassName = options.target.className;
195 | };
196 |
197 | TouchMenuLA.prototype.invoke = function (fn) {
198 | if (fn) {
199 | fn.apply(self);
200 | }
201 | };
202 |
203 | TouchMenuLA.prototype.initialize = function () {
204 | if (options.target) {
205 | menuHammer = Hammer(options.target, null);
206 |
207 | self.setDefaultsOptions();
208 | self.setMenuClassName();
209 | self.initElements();
210 |
211 | if (!options.disableSlide) {
212 | self.touchStartMenu();
213 | self.touchEndMenu();
214 | self.eventStartMask();
215 | self.eventEndMask();
216 | }
217 |
218 | if (!options.disableMask) {
219 | self.clickMaskClose();
220 | }
221 | } else {
222 | console.error('TouchMenuLA: The option \'target\' is required.');
223 | }
224 | };
225 |
226 | return new TouchMenuLA();
227 | };
--------------------------------------------------------------------------------
/lib/hammerjs/hammer.min.js:
--------------------------------------------------------------------------------
1 | /*! Hammer.JS - v2.0.6 - 2015-12-23
2 | * http://hammerjs.github.io/
3 | *
4 | * Copyright (c) 2015 Jorik Tangelder;
5 | * Licensed under the license */
6 | !function(a,b,c,d){"use strict";function e(a,b,c){return setTimeout(j(a,c),b)}function f(a,b,c){return Array.isArray(a)?(g(a,c[b],c),!0):!1}function g(a,b,c){var e;if(a)if(a.forEach)a.forEach(b,c);else if(a.length!==d)for(e=0;e\s*\(/gm,"{anonymous}()@"):"Unknown Stack Trace",f=a.console&&(a.console.warn||a.console.log);return f&&f.call(a.console,e,d),b.apply(this,arguments)}}function i(a,b,c){var d,e=b.prototype;d=a.prototype=Object.create(e),d.constructor=a,d._super=e,c&&hb(d,c)}function j(a,b){return function(){return a.apply(b,arguments)}}function k(a,b){return typeof a==kb?a.apply(b?b[0]||d:d,b):a}function l(a,b){return a===d?b:a}function m(a,b,c){g(q(b),function(b){a.addEventListener(b,c,!1)})}function n(a,b,c){g(q(b),function(b){a.removeEventListener(b,c,!1)})}function o(a,b){for(;a;){if(a==b)return!0;a=a.parentNode}return!1}function p(a,b){return a.indexOf(b)>-1}function q(a){return a.trim().split(/\s+/g)}function r(a,b,c){if(a.indexOf&&!c)return a.indexOf(b);for(var d=0;dc[b]}):d.sort()),d}function u(a,b){for(var c,e,f=b[0].toUpperCase()+b.slice(1),g=0;g1&&!c.firstMultiple?c.firstMultiple=D(b):1===e&&(c.firstMultiple=!1);var f=c.firstInput,g=c.firstMultiple,h=g?g.center:f.center,i=b.center=E(d);b.timeStamp=nb(),b.deltaTime=b.timeStamp-f.timeStamp,b.angle=I(h,i),b.distance=H(h,i),B(c,b),b.offsetDirection=G(b.deltaX,b.deltaY);var j=F(b.deltaTime,b.deltaX,b.deltaY);b.overallVelocityX=j.x,b.overallVelocityY=j.y,b.overallVelocity=mb(j.x)>mb(j.y)?j.x:j.y,b.scale=g?K(g.pointers,d):1,b.rotation=g?J(g.pointers,d):0,b.maxPointers=c.prevInput?b.pointers.length>c.prevInput.maxPointers?b.pointers.length:c.prevInput.maxPointers:b.pointers.length,C(c,b);var k=a.element;o(b.srcEvent.target,k)&&(k=b.srcEvent.target),b.target=k}function B(a,b){var c=b.center,d=a.offsetDelta||{},e=a.prevDelta||{},f=a.prevInput||{};(b.eventType===Ab||f.eventType===Cb)&&(e=a.prevDelta={x:f.deltaX||0,y:f.deltaY||0},d=a.offsetDelta={x:c.x,y:c.y}),b.deltaX=e.x+(c.x-d.x),b.deltaY=e.y+(c.y-d.y)}function C(a,b){var c,e,f,g,h=a.lastInterval||b,i=b.timeStamp-h.timeStamp;if(b.eventType!=Db&&(i>zb||h.velocity===d)){var j=b.deltaX-h.deltaX,k=b.deltaY-h.deltaY,l=F(i,j,k);e=l.x,f=l.y,c=mb(l.x)>mb(l.y)?l.x:l.y,g=G(j,k),a.lastInterval=b}else c=h.velocity,e=h.velocityX,f=h.velocityY,g=h.direction;b.velocity=c,b.velocityX=e,b.velocityY=f,b.direction=g}function D(a){for(var b=[],c=0;ce;)c+=a[e].clientX,d+=a[e].clientY,e++;return{x:lb(c/b),y:lb(d/b)}}function F(a,b,c){return{x:b/a||0,y:c/a||0}}function G(a,b){return a===b?Eb:mb(a)>=mb(b)?0>a?Fb:Gb:0>b?Hb:Ib}function H(a,b,c){c||(c=Mb);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return Math.sqrt(d*d+e*e)}function I(a,b,c){c||(c=Mb);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return 180*Math.atan2(e,d)/Math.PI}function J(a,b){return I(b[1],b[0],Nb)+I(a[1],a[0],Nb)}function K(a,b){return H(b[0],b[1],Nb)/H(a[0],a[1],Nb)}function L(){this.evEl=Pb,this.evWin=Qb,this.allow=!0,this.pressed=!1,x.apply(this,arguments)}function M(){this.evEl=Tb,this.evWin=Ub,x.apply(this,arguments),this.store=this.manager.session.pointerEvents=[]}function N(){this.evTarget=Wb,this.evWin=Xb,this.started=!1,x.apply(this,arguments)}function O(a,b){var c=s(a.touches),d=s(a.changedTouches);return b&(Cb|Db)&&(c=t(c.concat(d),"identifier",!0)),[c,d]}function P(){this.evTarget=Zb,this.targetIds={},x.apply(this,arguments)}function Q(a,b){var c=s(a.touches),d=this.targetIds;if(b&(Ab|Bb)&&1===c.length)return d[c[0].identifier]=!0,[c,c];var e,f,g=s(a.changedTouches),h=[],i=this.target;if(f=c.filter(function(a){return o(a.target,i)}),b===Ab)for(e=0;eh&&(b.push(a),h=b.length-1):e&(Cb|Db)&&(c=!0),0>h||(b[h]=a,this.callback(this.manager,e,{pointers:b,changedPointers:[a],pointerType:f,srcEvent:a}),c&&b.splice(h,1))}});var Vb={touchstart:Ab,touchmove:Bb,touchend:Cb,touchcancel:Db},Wb="touchstart",Xb="touchstart touchmove touchend touchcancel";i(N,x,{handler:function(a){var b=Vb[a.type];if(b===Ab&&(this.started=!0),this.started){var c=O.call(this,a,b);b&(Cb|Db)&&c[0].length-c[1].length===0&&(this.started=!1),this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:vb,srcEvent:a})}}});var Yb={touchstart:Ab,touchmove:Bb,touchend:Cb,touchcancel:Db},Zb="touchstart touchmove touchend touchcancel";i(P,x,{handler:function(a){var b=Yb[a.type],c=Q.call(this,a,b);c&&this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:vb,srcEvent:a})}}),i(R,x,{handler:function(a,b,c){var d=c.pointerType==vb,e=c.pointerType==xb;if(d)this.mouse.allow=!1;else if(e&&!this.mouse.allow)return;b&(Cb|Db)&&(this.mouse.allow=!0),this.callback(a,b,c)},destroy:function(){this.touch.destroy(),this.mouse.destroy()}});var $b=u(jb.style,"touchAction"),_b=$b!==d,ac="compute",bc="auto",cc="manipulation",dc="none",ec="pan-x",fc="pan-y";S.prototype={set:function(a){a==ac&&(a=this.compute()),_b&&this.manager.element.style&&(this.manager.element.style[$b]=a),this.actions=a.toLowerCase().trim()},update:function(){this.set(this.manager.options.touchAction)},compute:function(){var a=[];return g(this.manager.recognizers,function(b){k(b.options.enable,[b])&&(a=a.concat(b.getTouchAction()))}),T(a.join(" "))},preventDefaults:function(a){if(!_b){var b=a.srcEvent,c=a.offsetDirection;if(this.manager.session.prevented)return void b.preventDefault();var d=this.actions,e=p(d,dc),f=p(d,fc),g=p(d,ec);if(e){var h=1===a.pointers.length,i=a.distance<2,j=a.deltaTime<250;if(h&&i&&j)return}if(!g||!f)return e||f&&c&Jb||g&&c&Kb?this.preventSrc(b):void 0}},preventSrc:function(a){this.manager.session.prevented=!0,a.preventDefault()}};var gc=1,hc=2,ic=4,jc=8,kc=jc,lc=16,mc=32;U.prototype={defaults:{},set:function(a){return hb(this.options,a),this.manager&&this.manager.touchAction.update(),this},recognizeWith:function(a){if(f(a,"recognizeWith",this))return this;var b=this.simultaneous;return a=X(a,this),b[a.id]||(b[a.id]=a,a.recognizeWith(this)),this},dropRecognizeWith:function(a){return f(a,"dropRecognizeWith",this)?this:(a=X(a,this),delete this.simultaneous[a.id],this)},requireFailure:function(a){if(f(a,"requireFailure",this))return this;var b=this.requireFail;return a=X(a,this),-1===r(b,a)&&(b.push(a),a.requireFailure(this)),this},dropRequireFailure:function(a){if(f(a,"dropRequireFailure",this))return this;a=X(a,this);var b=r(this.requireFail,a);return b>-1&&this.requireFail.splice(b,1),this},hasRequireFailures:function(){return this.requireFail.length>0},canRecognizeWith:function(a){return!!this.simultaneous[a.id]},emit:function(a){function b(b){c.manager.emit(b,a)}var c=this,d=this.state;jc>d&&b(c.options.event+V(d)),b(c.options.event),a.additionalEvent&&b(a.additionalEvent),d>=jc&&b(c.options.event+V(d))},tryEmit:function(a){return this.canEmit()?this.emit(a):void(this.state=mc)},canEmit:function(){for(var a=0;af?Fb:Gb,c=f!=this.pX,d=Math.abs(a.deltaX)):(e=0===g?Eb:0>g?Hb:Ib,c=g!=this.pY,d=Math.abs(a.deltaY))),a.direction=e,c&&d>b.threshold&&e&b.direction},attrTest:function(a){return Y.prototype.attrTest.call(this,a)&&(this.state&hc||!(this.state&hc)&&this.directionTest(a))},emit:function(a){this.pX=a.deltaX,this.pY=a.deltaY;var b=W(a.direction);b&&(a.additionalEvent=this.options.event+b),this._super.emit.call(this,a)}}),i($,Y,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[dc]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.scale-1)>this.options.threshold||this.state&hc)},emit:function(a){if(1!==a.scale){var b=a.scale<1?"in":"out";a.additionalEvent=this.options.event+b}this._super.emit.call(this,a)}}),i(_,U,{defaults:{event:"press",pointers:1,time:251,threshold:9},getTouchAction:function(){return[bc]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distanceb.time;if(this._input=a,!d||!c||a.eventType&(Cb|Db)&&!f)this.reset();else if(a.eventType&Ab)this.reset(),this._timer=e(function(){this.state=kc,this.tryEmit()},b.time,this);else if(a.eventType&Cb)return kc;return mc},reset:function(){clearTimeout(this._timer)},emit:function(a){this.state===kc&&(a&&a.eventType&Cb?this.manager.emit(this.options.event+"up",a):(this._input.timeStamp=nb(),this.manager.emit(this.options.event,this._input)))}}),i(ab,Y,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[dc]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.rotation)>this.options.threshold||this.state&hc)}}),i(bb,Y,{defaults:{event:"swipe",threshold:10,velocity:.3,direction:Jb|Kb,pointers:1},getTouchAction:function(){return Z.prototype.getTouchAction.call(this)},attrTest:function(a){var b,c=this.options.direction;return c&(Jb|Kb)?b=a.overallVelocity:c&Jb?b=a.overallVelocityX:c&Kb&&(b=a.overallVelocityY),this._super.attrTest.call(this,a)&&c&a.offsetDirection&&a.distance>this.options.threshold&&a.maxPointers==this.options.pointers&&mb(b)>this.options.velocity&&a.eventType&Cb},emit:function(a){var b=W(a.offsetDirection);b&&this.manager.emit(this.options.event+b,a),this.manager.emit(this.options.event,a)}}),i(cb,U,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:9,posThreshold:10},getTouchAction:function(){return[cc]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distance\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace';
88 |
89 | var log = window.console && (window.console.warn || window.console.log);
90 | if (log) {
91 | log.call(window.console, deprecationMessage, stack);
92 | }
93 | return method.apply(this, arguments);
94 | };
95 | }
96 |
97 | /**
98 | * extend object.
99 | * means that properties in dest will be overwritten by the ones in src.
100 | * @param {Object} target
101 | * @param {...Object} objects_to_assign
102 | * @returns {Object} target
103 | */
104 | var assign;
105 | if (typeof Object.assign !== 'function') {
106 | assign = function assign(target) {
107 | if (target === undefined || target === null) {
108 | throw new TypeError('Cannot convert undefined or null to object');
109 | }
110 |
111 | var output = Object(target);
112 | for (var index = 1; index < arguments.length; index++) {
113 | var source = arguments[index];
114 | if (source !== undefined && source !== null) {
115 | for (var nextKey in source) {
116 | if (source.hasOwnProperty(nextKey)) {
117 | output[nextKey] = source[nextKey];
118 | }
119 | }
120 | }
121 | }
122 | return output;
123 | };
124 | } else {
125 | assign = Object.assign;
126 | }
127 |
128 | /**
129 | * extend object.
130 | * means that properties in dest will be overwritten by the ones in src.
131 | * @param {Object} dest
132 | * @param {Object} src
133 | * @param {Boolean=false} [merge]
134 | * @returns {Object} dest
135 | */
136 | var extend = deprecate(function extend(dest, src, merge) {
137 | var keys = Object.keys(src);
138 | var i = 0;
139 | while (i < keys.length) {
140 | if (!merge || (merge && dest[keys[i]] === undefined)) {
141 | dest[keys[i]] = src[keys[i]];
142 | }
143 | i++;
144 | }
145 | return dest;
146 | }, 'extend', 'Use `assign`.');
147 |
148 | /**
149 | * merge the values from src in the dest.
150 | * means that properties that exist in dest will not be overwritten by src
151 | * @param {Object} dest
152 | * @param {Object} src
153 | * @returns {Object} dest
154 | */
155 | var merge = deprecate(function merge(dest, src) {
156 | return extend(dest, src, true);
157 | }, 'merge', 'Use `assign`.');
158 |
159 | /**
160 | * simple class inheritance
161 | * @param {Function} child
162 | * @param {Function} base
163 | * @param {Object} [properties]
164 | */
165 | function inherit(child, base, properties) {
166 | var baseP = base.prototype,
167 | childP;
168 |
169 | childP = child.prototype = Object.create(baseP);
170 | childP.constructor = child;
171 | childP._super = baseP;
172 |
173 | if (properties) {
174 | assign(childP, properties);
175 | }
176 | }
177 |
178 | /**
179 | * simple function bind
180 | * @param {Function} fn
181 | * @param {Object} context
182 | * @returns {Function}
183 | */
184 | function bindFn(fn, context) {
185 | return function boundFn() {
186 | return fn.apply(context, arguments);
187 | };
188 | }
189 |
190 | /**
191 | * let a boolean value also be a function that must return a boolean
192 | * this first item in args will be used as the context
193 | * @param {Boolean|Function} val
194 | * @param {Array} [args]
195 | * @returns {Boolean}
196 | */
197 | function boolOrFn(val, args) {
198 | if (typeof val == TYPE_FUNCTION) {
199 | return val.apply(args ? args[0] || undefined : undefined, args);
200 | }
201 | return val;
202 | }
203 |
204 | /**
205 | * use the val2 when val1 is undefined
206 | * @param {*} val1
207 | * @param {*} val2
208 | * @returns {*}
209 | */
210 | function ifUndefined(val1, val2) {
211 | return (val1 === undefined) ? val2 : val1;
212 | }
213 |
214 | /**
215 | * addEventListener with multiple events at once
216 | * @param {EventTarget} target
217 | * @param {String} types
218 | * @param {Function} handler
219 | */
220 | function addEventListeners(target, types, handler) {
221 | each(splitStr(types), function(type) {
222 | target.addEventListener(type, handler, false);
223 | });
224 | }
225 |
226 | /**
227 | * removeEventListener with multiple events at once
228 | * @param {EventTarget} target
229 | * @param {String} types
230 | * @param {Function} handler
231 | */
232 | function removeEventListeners(target, types, handler) {
233 | each(splitStr(types), function(type) {
234 | target.removeEventListener(type, handler, false);
235 | });
236 | }
237 |
238 | /**
239 | * find if a node is in the given parent
240 | * @method hasParent
241 | * @param {HTMLElement} node
242 | * @param {HTMLElement} parent
243 | * @return {Boolean} found
244 | */
245 | function hasParent(node, parent) {
246 | while (node) {
247 | if (node == parent) {
248 | return true;
249 | }
250 | node = node.parentNode;
251 | }
252 | return false;
253 | }
254 |
255 | /**
256 | * small indexOf wrapper
257 | * @param {String} str
258 | * @param {String} find
259 | * @returns {Boolean} found
260 | */
261 | function inStr(str, find) {
262 | return str.indexOf(find) > -1;
263 | }
264 |
265 | /**
266 | * split string on whitespace
267 | * @param {String} str
268 | * @returns {Array} words
269 | */
270 | function splitStr(str) {
271 | return str.trim().split(/\s+/g);
272 | }
273 |
274 | /**
275 | * find if a array contains the object using indexOf or a simple polyFill
276 | * @param {Array} src
277 | * @param {String} find
278 | * @param {String} [findByKey]
279 | * @return {Boolean|Number} false when not found, or the index
280 | */
281 | function inArray(src, find, findByKey) {
282 | if (src.indexOf && !findByKey) {
283 | return src.indexOf(find);
284 | } else {
285 | var i = 0;
286 | while (i < src.length) {
287 | if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) {
288 | return i;
289 | }
290 | i++;
291 | }
292 | return -1;
293 | }
294 | }
295 |
296 | /**
297 | * convert array-like objects to real arrays
298 | * @param {Object} obj
299 | * @returns {Array}
300 | */
301 | function toArray(obj) {
302 | return Array.prototype.slice.call(obj, 0);
303 | }
304 |
305 | /**
306 | * unique array with objects based on a key (like 'id') or just by the array's value
307 | * @param {Array} src [{id:1},{id:2},{id:1}]
308 | * @param {String} [key]
309 | * @param {Boolean} [sort=False]
310 | * @returns {Array} [{id:1},{id:2}]
311 | */
312 | function uniqueArray(src, key, sort) {
313 | var results = [];
314 | var values = [];
315 | var i = 0;
316 |
317 | while (i < src.length) {
318 | var val = key ? src[i][key] : src[i];
319 | if (inArray(values, val) < 0) {
320 | results.push(src[i]);
321 | }
322 | values[i] = val;
323 | i++;
324 | }
325 |
326 | if (sort) {
327 | if (!key) {
328 | results = results.sort();
329 | } else {
330 | results = results.sort(function sortUniqueArray(a, b) {
331 | return a[key] > b[key];
332 | });
333 | }
334 | }
335 |
336 | return results;
337 | }
338 |
339 | /**
340 | * get the prefixed property
341 | * @param {Object} obj
342 | * @param {String} property
343 | * @returns {String|Undefined} prefixed
344 | */
345 | function prefixed(obj, property) {
346 | var prefix, prop;
347 | var camelProp = property[0].toUpperCase() + property.slice(1);
348 |
349 | var i = 0;
350 | while (i < VENDOR_PREFIXES.length) {
351 | prefix = VENDOR_PREFIXES[i];
352 | prop = (prefix) ? prefix + camelProp : property;
353 |
354 | if (prop in obj) {
355 | return prop;
356 | }
357 | i++;
358 | }
359 | return undefined;
360 | }
361 |
362 | /**
363 | * get a unique id
364 | * @returns {number} uniqueId
365 | */
366 | var _uniqueId = 1;
367 | function uniqueId() {
368 | return _uniqueId++;
369 | }
370 |
371 | /**
372 | * get the window object of an element
373 | * @param {HTMLElement} element
374 | * @returns {DocumentView|Window}
375 | */
376 | function getWindowForElement(element) {
377 | var doc = element.ownerDocument || element;
378 | return (doc.defaultView || doc.parentWindow || window);
379 | }
380 |
381 | var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
382 |
383 | var SUPPORT_TOUCH = ('ontouchstart' in window);
384 | var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;
385 | var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
386 |
387 | var INPUT_TYPE_TOUCH = 'touch';
388 | var INPUT_TYPE_PEN = 'pen';
389 | var INPUT_TYPE_MOUSE = 'mouse';
390 | var INPUT_TYPE_KINECT = 'kinect';
391 |
392 | var COMPUTE_INTERVAL = 25;
393 |
394 | var INPUT_START = 1;
395 | var INPUT_MOVE = 2;
396 | var INPUT_END = 4;
397 | var INPUT_CANCEL = 8;
398 |
399 | var DIRECTION_NONE = 1;
400 | var DIRECTION_LEFT = 2;
401 | var DIRECTION_RIGHT = 4;
402 | var DIRECTION_UP = 8;
403 | var DIRECTION_DOWN = 16;
404 |
405 | var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
406 | var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
407 | var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
408 |
409 | var PROPS_XY = ['x', 'y'];
410 | var PROPS_CLIENT_XY = ['clientX', 'clientY'];
411 |
412 | /**
413 | * create new input type manager
414 | * @param {Manager} manager
415 | * @param {Function} callback
416 | * @returns {Input}
417 | * @constructor
418 | */
419 | function Input(manager, callback) {
420 | var self = this;
421 | this.manager = manager;
422 | this.callback = callback;
423 | this.element = manager.element;
424 | this.target = manager.options.inputTarget;
425 |
426 | // smaller wrapper around the handler, for the scope and the enabled state of the manager,
427 | // so when disabled the input events are completely bypassed.
428 | this.domHandler = function(ev) {
429 | if (boolOrFn(manager.options.enable, [manager])) {
430 | self.handler(ev);
431 | }
432 | };
433 |
434 | this.init();
435 |
436 | }
437 |
438 | Input.prototype = {
439 | /**
440 | * should handle the inputEvent data and trigger the callback
441 | * @virtual
442 | */
443 | handler: function() { },
444 |
445 | /**
446 | * bind the events
447 | */
448 | init: function() {
449 | this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
450 | this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
451 | this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
452 | },
453 |
454 | /**
455 | * unbind the events
456 | */
457 | destroy: function() {
458 | this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
459 | this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
460 | this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
461 | }
462 | };
463 |
464 | /**
465 | * create new input type manager
466 | * called by the Manager constructor
467 | * @param {Hammer} manager
468 | * @returns {Input}
469 | */
470 | function createInputInstance(manager) {
471 | var Type;
472 | var inputClass = manager.options.inputClass;
473 |
474 | if (inputClass) {
475 | Type = inputClass;
476 | } else if (SUPPORT_POINTER_EVENTS) {
477 | Type = PointerEventInput;
478 | } else if (SUPPORT_ONLY_TOUCH) {
479 | Type = TouchInput;
480 | } else if (!SUPPORT_TOUCH) {
481 | Type = MouseInput;
482 | } else {
483 | Type = TouchMouseInput;
484 | }
485 | return new (Type)(manager, inputHandler);
486 | }
487 |
488 | /**
489 | * handle input events
490 | * @param {Manager} manager
491 | * @param {String} eventType
492 | * @param {Object} input
493 | */
494 | function inputHandler(manager, eventType, input) {
495 | var pointersLen = input.pointers.length;
496 | var changedPointersLen = input.changedPointers.length;
497 | var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));
498 | var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));
499 |
500 | input.isFirst = !!isFirst;
501 | input.isFinal = !!isFinal;
502 |
503 | if (isFirst) {
504 | manager.session = {};
505 | }
506 |
507 | // source event is the normalized value of the domEvents
508 | // like 'touchstart, mouseup, pointerdown'
509 | input.eventType = eventType;
510 |
511 | // compute scale, rotation etc
512 | computeInputData(manager, input);
513 |
514 | // emit secret event
515 | manager.emit('hammer.input', input);
516 |
517 | manager.recognize(input);
518 | manager.session.prevInput = input;
519 | }
520 |
521 | /**
522 | * extend the data with some usable properties like scale, rotate, velocity etc
523 | * @param {Object} manager
524 | * @param {Object} input
525 | */
526 | function computeInputData(manager, input) {
527 | var session = manager.session;
528 | var pointers = input.pointers;
529 | var pointersLength = pointers.length;
530 |
531 | // store the first input to calculate the distance and direction
532 | if (!session.firstInput) {
533 | session.firstInput = simpleCloneInputData(input);
534 | }
535 |
536 | // to compute scale and rotation we need to store the multiple touches
537 | if (pointersLength > 1 && !session.firstMultiple) {
538 | session.firstMultiple = simpleCloneInputData(input);
539 | } else if (pointersLength === 1) {
540 | session.firstMultiple = false;
541 | }
542 |
543 | var firstInput = session.firstInput;
544 | var firstMultiple = session.firstMultiple;
545 | var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
546 |
547 | var center = input.center = getCenter(pointers);
548 | input.timeStamp = now();
549 | input.deltaTime = input.timeStamp - firstInput.timeStamp;
550 |
551 | input.angle = getAngle(offsetCenter, center);
552 | input.distance = getDistance(offsetCenter, center);
553 |
554 | computeDeltaXY(session, input);
555 | input.offsetDirection = getDirection(input.deltaX, input.deltaY);
556 |
557 | var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY);
558 | input.overallVelocityX = overallVelocity.x;
559 | input.overallVelocityY = overallVelocity.y;
560 | input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y;
561 |
562 | input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
563 | input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
564 |
565 | input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length >
566 | session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers);
567 |
568 | computeIntervalInputData(session, input);
569 |
570 | // find the correct target
571 | var target = manager.element;
572 | if (hasParent(input.srcEvent.target, target)) {
573 | target = input.srcEvent.target;
574 | }
575 | input.target = target;
576 | }
577 |
578 | function computeDeltaXY(session, input) {
579 | var center = input.center;
580 | var offset = session.offsetDelta || {};
581 | var prevDelta = session.prevDelta || {};
582 | var prevInput = session.prevInput || {};
583 |
584 | if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
585 | prevDelta = session.prevDelta = {
586 | x: prevInput.deltaX || 0,
587 | y: prevInput.deltaY || 0
588 | };
589 |
590 | offset = session.offsetDelta = {
591 | x: center.x,
592 | y: center.y
593 | };
594 | }
595 |
596 | input.deltaX = prevDelta.x + (center.x - offset.x);
597 | input.deltaY = prevDelta.y + (center.y - offset.y);
598 | }
599 |
600 | /**
601 | * velocity is calculated every x ms
602 | * @param {Object} session
603 | * @param {Object} input
604 | */
605 | function computeIntervalInputData(session, input) {
606 | var last = session.lastInterval || input,
607 | deltaTime = input.timeStamp - last.timeStamp,
608 | velocity, velocityX, velocityY, direction;
609 |
610 | if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {
611 | var deltaX = input.deltaX - last.deltaX;
612 | var deltaY = input.deltaY - last.deltaY;
613 |
614 | var v = getVelocity(deltaTime, deltaX, deltaY);
615 | velocityX = v.x;
616 | velocityY = v.y;
617 | velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;
618 | direction = getDirection(deltaX, deltaY);
619 |
620 | session.lastInterval = input;
621 | } else {
622 | // use latest velocity info if it doesn't overtake a minimum period
623 | velocity = last.velocity;
624 | velocityX = last.velocityX;
625 | velocityY = last.velocityY;
626 | direction = last.direction;
627 | }
628 |
629 | input.velocity = velocity;
630 | input.velocityX = velocityX;
631 | input.velocityY = velocityY;
632 | input.direction = direction;
633 | }
634 |
635 | /**
636 | * create a simple clone from the input used for storage of firstInput and firstMultiple
637 | * @param {Object} input
638 | * @returns {Object} clonedInputData
639 | */
640 | function simpleCloneInputData(input) {
641 | // make a simple copy of the pointers because we will get a reference if we don't
642 | // we only need clientXY for the calculations
643 | var pointers = [];
644 | var i = 0;
645 | while (i < input.pointers.length) {
646 | pointers[i] = {
647 | clientX: round(input.pointers[i].clientX),
648 | clientY: round(input.pointers[i].clientY)
649 | };
650 | i++;
651 | }
652 |
653 | return {
654 | timeStamp: now(),
655 | pointers: pointers,
656 | center: getCenter(pointers),
657 | deltaX: input.deltaX,
658 | deltaY: input.deltaY
659 | };
660 | }
661 |
662 | /**
663 | * get the center of all the pointers
664 | * @param {Array} pointers
665 | * @return {Object} center contains `x` and `y` properties
666 | */
667 | function getCenter(pointers) {
668 | var pointersLength = pointers.length;
669 |
670 | // no need to loop when only one touch
671 | if (pointersLength === 1) {
672 | return {
673 | x: round(pointers[0].clientX),
674 | y: round(pointers[0].clientY)
675 | };
676 | }
677 |
678 | var x = 0, y = 0, i = 0;
679 | while (i < pointersLength) {
680 | x += pointers[i].clientX;
681 | y += pointers[i].clientY;
682 | i++;
683 | }
684 |
685 | return {
686 | x: round(x / pointersLength),
687 | y: round(y / pointersLength)
688 | };
689 | }
690 |
691 | /**
692 | * calculate the velocity between two points. unit is in px per ms.
693 | * @param {Number} deltaTime
694 | * @param {Number} x
695 | * @param {Number} y
696 | * @return {Object} velocity `x` and `y`
697 | */
698 | function getVelocity(deltaTime, x, y) {
699 | return {
700 | x: x / deltaTime || 0,
701 | y: y / deltaTime || 0
702 | };
703 | }
704 |
705 | /**
706 | * get the direction between two points
707 | * @param {Number} x
708 | * @param {Number} y
709 | * @return {Number} direction
710 | */
711 | function getDirection(x, y) {
712 | if (x === y) {
713 | return DIRECTION_NONE;
714 | }
715 |
716 | if (abs(x) >= abs(y)) {
717 | return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
718 | }
719 | return y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
720 | }
721 |
722 | /**
723 | * calculate the absolute distance between two points
724 | * @param {Object} p1 {x, y}
725 | * @param {Object} p2 {x, y}
726 | * @param {Array} [props] containing x and y keys
727 | * @return {Number} distance
728 | */
729 | function getDistance(p1, p2, props) {
730 | if (!props) {
731 | props = PROPS_XY;
732 | }
733 | var x = p2[props[0]] - p1[props[0]],
734 | y = p2[props[1]] - p1[props[1]];
735 |
736 | return Math.sqrt((x * x) + (y * y));
737 | }
738 |
739 | /**
740 | * calculate the angle between two coordinates
741 | * @param {Object} p1
742 | * @param {Object} p2
743 | * @param {Array} [props] containing x and y keys
744 | * @return {Number} angle
745 | */
746 | function getAngle(p1, p2, props) {
747 | if (!props) {
748 | props = PROPS_XY;
749 | }
750 | var x = p2[props[0]] - p1[props[0]],
751 | y = p2[props[1]] - p1[props[1]];
752 | return Math.atan2(y, x) * 180 / Math.PI;
753 | }
754 |
755 | /**
756 | * calculate the rotation degrees between two pointersets
757 | * @param {Array} start array of pointers
758 | * @param {Array} end array of pointers
759 | * @return {Number} rotation
760 | */
761 | function getRotation(start, end) {
762 | return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY);
763 | }
764 |
765 | /**
766 | * calculate the scale factor between two pointersets
767 | * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
768 | * @param {Array} start array of pointers
769 | * @param {Array} end array of pointers
770 | * @return {Number} scale
771 | */
772 | function getScale(start, end) {
773 | return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
774 | }
775 |
776 | var MOUSE_INPUT_MAP = {
777 | mousedown: INPUT_START,
778 | mousemove: INPUT_MOVE,
779 | mouseup: INPUT_END
780 | };
781 |
782 | var MOUSE_ELEMENT_EVENTS = 'mousedown';
783 | var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';
784 |
785 | /**
786 | * Mouse events input
787 | * @constructor
788 | * @extends Input
789 | */
790 | function MouseInput() {
791 | this.evEl = MOUSE_ELEMENT_EVENTS;
792 | this.evWin = MOUSE_WINDOW_EVENTS;
793 |
794 | this.allow = true; // used by Input.TouchMouse to disable mouse events
795 | this.pressed = false; // mousedown state
796 |
797 | Input.apply(this, arguments);
798 | }
799 |
800 | inherit(MouseInput, Input, {
801 | /**
802 | * handle mouse events
803 | * @param {Object} ev
804 | */
805 | handler: function MEhandler(ev) {
806 | var eventType = MOUSE_INPUT_MAP[ev.type];
807 |
808 | // on start we want to have the left mouse button down
809 | if (eventType & INPUT_START && ev.button === 0) {
810 | this.pressed = true;
811 | }
812 |
813 | if (eventType & INPUT_MOVE && ev.which !== 1) {
814 | eventType = INPUT_END;
815 | }
816 |
817 | // mouse must be down, and mouse events are allowed (see the TouchMouse input)
818 | if (!this.pressed || !this.allow) {
819 | return;
820 | }
821 |
822 | if (eventType & INPUT_END) {
823 | this.pressed = false;
824 | }
825 |
826 | this.callback(this.manager, eventType, {
827 | pointers: [ev],
828 | changedPointers: [ev],
829 | pointerType: INPUT_TYPE_MOUSE,
830 | srcEvent: ev
831 | });
832 | }
833 | });
834 |
835 | var POINTER_INPUT_MAP = {
836 | pointerdown: INPUT_START,
837 | pointermove: INPUT_MOVE,
838 | pointerup: INPUT_END,
839 | pointercancel: INPUT_CANCEL,
840 | pointerout: INPUT_CANCEL
841 | };
842 |
843 | // in IE10 the pointer types is defined as an enum
844 | var IE10_POINTER_TYPE_ENUM = {
845 | 2: INPUT_TYPE_TOUCH,
846 | 3: INPUT_TYPE_PEN,
847 | 4: INPUT_TYPE_MOUSE,
848 | 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816
849 | };
850 |
851 | var POINTER_ELEMENT_EVENTS = 'pointerdown';
852 | var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';
853 |
854 | // IE10 has prefixed support, and case-sensitive
855 | if (window.MSPointerEvent && !window.PointerEvent) {
856 | POINTER_ELEMENT_EVENTS = 'MSPointerDown';
857 | POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
858 | }
859 |
860 | /**
861 | * Pointer events input
862 | * @constructor
863 | * @extends Input
864 | */
865 | function PointerEventInput() {
866 | this.evEl = POINTER_ELEMENT_EVENTS;
867 | this.evWin = POINTER_WINDOW_EVENTS;
868 |
869 | Input.apply(this, arguments);
870 |
871 | this.store = (this.manager.session.pointerEvents = []);
872 | }
873 |
874 | inherit(PointerEventInput, Input, {
875 | /**
876 | * handle mouse events
877 | * @param {Object} ev
878 | */
879 | handler: function PEhandler(ev) {
880 | var store = this.store;
881 | var removePointer = false;
882 |
883 | var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
884 | var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
885 | var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
886 |
887 | var isTouch = (pointerType == INPUT_TYPE_TOUCH);
888 |
889 | // get index of the event in the store
890 | var storeIndex = inArray(store, ev.pointerId, 'pointerId');
891 |
892 | // start and mouse must be down
893 | if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
894 | if (storeIndex < 0) {
895 | store.push(ev);
896 | storeIndex = store.length - 1;
897 | }
898 | } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
899 | removePointer = true;
900 | }
901 |
902 | // it not found, so the pointer hasn't been down (so it's probably a hover)
903 | if (storeIndex < 0) {
904 | return;
905 | }
906 |
907 | // update the event in the store
908 | store[storeIndex] = ev;
909 |
910 | this.callback(this.manager, eventType, {
911 | pointers: store,
912 | changedPointers: [ev],
913 | pointerType: pointerType,
914 | srcEvent: ev
915 | });
916 |
917 | if (removePointer) {
918 | // remove from the store
919 | store.splice(storeIndex, 1);
920 | }
921 | }
922 | });
923 |
924 | var SINGLE_TOUCH_INPUT_MAP = {
925 | touchstart: INPUT_START,
926 | touchmove: INPUT_MOVE,
927 | touchend: INPUT_END,
928 | touchcancel: INPUT_CANCEL
929 | };
930 |
931 | var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
932 | var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';
933 |
934 | /**
935 | * Touch events input
936 | * @constructor
937 | * @extends Input
938 | */
939 | function SingleTouchInput() {
940 | this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;
941 | this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;
942 | this.started = false;
943 |
944 | Input.apply(this, arguments);
945 | }
946 |
947 | inherit(SingleTouchInput, Input, {
948 | handler: function TEhandler(ev) {
949 | var type = SINGLE_TOUCH_INPUT_MAP[ev.type];
950 |
951 | // should we handle the touch events?
952 | if (type === INPUT_START) {
953 | this.started = true;
954 | }
955 |
956 | if (!this.started) {
957 | return;
958 | }
959 |
960 | var touches = normalizeSingleTouches.call(this, ev, type);
961 |
962 | // when done, reset the started state
963 | if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {
964 | this.started = false;
965 | }
966 |
967 | this.callback(this.manager, type, {
968 | pointers: touches[0],
969 | changedPointers: touches[1],
970 | pointerType: INPUT_TYPE_TOUCH,
971 | srcEvent: ev
972 | });
973 | }
974 | });
975 |
976 | /**
977 | * @this {TouchInput}
978 | * @param {Object} ev
979 | * @param {Number} type flag
980 | * @returns {undefined|Array} [all, changed]
981 | */
982 | function normalizeSingleTouches(ev, type) {
983 | var all = toArray(ev.touches);
984 | var changed = toArray(ev.changedTouches);
985 |
986 | if (type & (INPUT_END | INPUT_CANCEL)) {
987 | all = uniqueArray(all.concat(changed), 'identifier', true);
988 | }
989 |
990 | return [all, changed];
991 | }
992 |
993 | var TOUCH_INPUT_MAP = {
994 | touchstart: INPUT_START,
995 | touchmove: INPUT_MOVE,
996 | touchend: INPUT_END,
997 | touchcancel: INPUT_CANCEL
998 | };
999 |
1000 | var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';
1001 |
1002 | /**
1003 | * Multi-user touch events input
1004 | * @constructor
1005 | * @extends Input
1006 | */
1007 | function TouchInput() {
1008 | this.evTarget = TOUCH_TARGET_EVENTS;
1009 | this.targetIds = {};
1010 |
1011 | Input.apply(this, arguments);
1012 | }
1013 |
1014 | inherit(TouchInput, Input, {
1015 | handler: function MTEhandler(ev) {
1016 | var type = TOUCH_INPUT_MAP[ev.type];
1017 | var touches = getTouches.call(this, ev, type);
1018 | if (!touches) {
1019 | return;
1020 | }
1021 |
1022 | this.callback(this.manager, type, {
1023 | pointers: touches[0],
1024 | changedPointers: touches[1],
1025 | pointerType: INPUT_TYPE_TOUCH,
1026 | srcEvent: ev
1027 | });
1028 | }
1029 | });
1030 |
1031 | /**
1032 | * @this {TouchInput}
1033 | * @param {Object} ev
1034 | * @param {Number} type flag
1035 | * @returns {undefined|Array} [all, changed]
1036 | */
1037 | function getTouches(ev, type) {
1038 | var allTouches = toArray(ev.touches);
1039 | var targetIds = this.targetIds;
1040 |
1041 | // when there is only one touch, the process can be simplified
1042 | if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
1043 | targetIds[allTouches[0].identifier] = true;
1044 | return [allTouches, allTouches];
1045 | }
1046 |
1047 | var i,
1048 | targetTouches,
1049 | changedTouches = toArray(ev.changedTouches),
1050 | changedTargetTouches = [],
1051 | target = this.target;
1052 |
1053 | // get target touches from touches
1054 | targetTouches = allTouches.filter(function(touch) {
1055 | return hasParent(touch.target, target);
1056 | });
1057 |
1058 | // collect touches
1059 | if (type === INPUT_START) {
1060 | i = 0;
1061 | while (i < targetTouches.length) {
1062 | targetIds[targetTouches[i].identifier] = true;
1063 | i++;
1064 | }
1065 | }
1066 |
1067 | // filter changed touches to only contain touches that exist in the collected target ids
1068 | i = 0;
1069 | while (i < changedTouches.length) {
1070 | if (targetIds[changedTouches[i].identifier]) {
1071 | changedTargetTouches.push(changedTouches[i]);
1072 | }
1073 |
1074 | // cleanup removed touches
1075 | if (type & (INPUT_END | INPUT_CANCEL)) {
1076 | delete targetIds[changedTouches[i].identifier];
1077 | }
1078 | i++;
1079 | }
1080 |
1081 | if (!changedTargetTouches.length) {
1082 | return;
1083 | }
1084 |
1085 | return [
1086 | // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
1087 | uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true),
1088 | changedTargetTouches
1089 | ];
1090 | }
1091 |
1092 | /**
1093 | * Combined touch and mouse input
1094 | *
1095 | * Touch has a higher priority then mouse, and while touching no mouse events are allowed.
1096 | * This because touch devices also emit mouse events while doing a touch.
1097 | *
1098 | * @constructor
1099 | * @extends Input
1100 | */
1101 | function TouchMouseInput() {
1102 | Input.apply(this, arguments);
1103 |
1104 | var handler = bindFn(this.handler, this);
1105 | this.touch = new TouchInput(this.manager, handler);
1106 | this.mouse = new MouseInput(this.manager, handler);
1107 | }
1108 |
1109 | inherit(TouchMouseInput, Input, {
1110 | /**
1111 | * handle mouse and touch events
1112 | * @param {Hammer} manager
1113 | * @param {String} inputEvent
1114 | * @param {Object} inputData
1115 | */
1116 | handler: function TMEhandler(manager, inputEvent, inputData) {
1117 | var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH),
1118 | isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE);
1119 |
1120 | // when we're in a touch event, so block all upcoming mouse events
1121 | // most mobile browser also emit mouseevents, right after touchstart
1122 | if (isTouch) {
1123 | this.mouse.allow = false;
1124 | } else if (isMouse && !this.mouse.allow) {
1125 | return;
1126 | }
1127 |
1128 | // reset the allowMouse when we're done
1129 | if (inputEvent & (INPUT_END | INPUT_CANCEL)) {
1130 | this.mouse.allow = true;
1131 | }
1132 |
1133 | this.callback(manager, inputEvent, inputData);
1134 | },
1135 |
1136 | /**
1137 | * remove the event listeners
1138 | */
1139 | destroy: function destroy() {
1140 | this.touch.destroy();
1141 | this.mouse.destroy();
1142 | }
1143 | });
1144 |
1145 | var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
1146 | var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;
1147 |
1148 | // magical touchAction value
1149 | var TOUCH_ACTION_COMPUTE = 'compute';
1150 | var TOUCH_ACTION_AUTO = 'auto';
1151 | var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented
1152 | var TOUCH_ACTION_NONE = 'none';
1153 | var TOUCH_ACTION_PAN_X = 'pan-x';
1154 | var TOUCH_ACTION_PAN_Y = 'pan-y';
1155 |
1156 | /**
1157 | * Touch Action
1158 | * sets the touchAction property or uses the js alternative
1159 | * @param {Manager} manager
1160 | * @param {String} value
1161 | * @constructor
1162 | */
1163 | function TouchAction(manager, value) {
1164 | this.manager = manager;
1165 | this.set(value);
1166 | }
1167 |
1168 | TouchAction.prototype = {
1169 | /**
1170 | * set the touchAction value on the element or enable the polyfill
1171 | * @param {String} value
1172 | */
1173 | set: function(value) {
1174 | // find out the touch-action by the event handlers
1175 | if (value == TOUCH_ACTION_COMPUTE) {
1176 | value = this.compute();
1177 | }
1178 |
1179 | if (NATIVE_TOUCH_ACTION && this.manager.element.style) {
1180 | this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
1181 | }
1182 | this.actions = value.toLowerCase().trim();
1183 | },
1184 |
1185 | /**
1186 | * just re-set the touchAction value
1187 | */
1188 | update: function() {
1189 | this.set(this.manager.options.touchAction);
1190 | },
1191 |
1192 | /**
1193 | * compute the value for the touchAction property based on the recognizer's settings
1194 | * @returns {String} value
1195 | */
1196 | compute: function() {
1197 | var actions = [];
1198 | each(this.manager.recognizers, function(recognizer) {
1199 | if (boolOrFn(recognizer.options.enable, [recognizer])) {
1200 | actions = actions.concat(recognizer.getTouchAction());
1201 | }
1202 | });
1203 | return cleanTouchActions(actions.join(' '));
1204 | },
1205 |
1206 | /**
1207 | * this method is called on each input cycle and provides the preventing of the browser behavior
1208 | * @param {Object} input
1209 | */
1210 | preventDefaults: function(input) {
1211 | // not needed with native support for the touchAction property
1212 | if (NATIVE_TOUCH_ACTION) {
1213 | return;
1214 | }
1215 |
1216 | var srcEvent = input.srcEvent;
1217 | var direction = input.offsetDirection;
1218 |
1219 | // if the touch action did prevented once this session
1220 | if (this.manager.session.prevented) {
1221 | srcEvent.preventDefault();
1222 | return;
1223 | }
1224 |
1225 | var actions = this.actions;
1226 | var hasNone = inStr(actions, TOUCH_ACTION_NONE);
1227 | var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
1228 | var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
1229 |
1230 | if (hasNone) {
1231 | //do not prevent defaults if this is a tap gesture
1232 |
1233 | var isTapPointer = input.pointers.length === 1;
1234 | var isTapMovement = input.distance < 2;
1235 | var isTapTouchTime = input.deltaTime < 250;
1236 |
1237 | if (isTapPointer && isTapMovement && isTapTouchTime) {
1238 | return;
1239 | }
1240 | }
1241 |
1242 | if (hasPanX && hasPanY) {
1243 | // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent
1244 | return;
1245 | }
1246 |
1247 | if (hasNone ||
1248 | (hasPanY && direction & DIRECTION_HORIZONTAL) ||
1249 | (hasPanX && direction & DIRECTION_VERTICAL)) {
1250 | return this.preventSrc(srcEvent);
1251 | }
1252 | },
1253 |
1254 | /**
1255 | * call preventDefault to prevent the browser's default behavior (scrolling in most cases)
1256 | * @param {Object} srcEvent
1257 | */
1258 | preventSrc: function(srcEvent) {
1259 | this.manager.session.prevented = true;
1260 | srcEvent.preventDefault();
1261 | }
1262 | };
1263 |
1264 | /**
1265 | * when the touchActions are collected they are not a valid value, so we need to clean things up. *
1266 | * @param {String} actions
1267 | * @returns {*}
1268 | */
1269 | function cleanTouchActions(actions) {
1270 | // none
1271 | if (inStr(actions, TOUCH_ACTION_NONE)) {
1272 | return TOUCH_ACTION_NONE;
1273 | }
1274 |
1275 | var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
1276 | var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
1277 |
1278 | // if both pan-x and pan-y are set (different recognizers
1279 | // for different directions, e.g. horizontal pan but vertical swipe?)
1280 | // we need none (as otherwise with pan-x pan-y combined none of these
1281 | // recognizers will work, since the browser would handle all panning
1282 | if (hasPanX && hasPanY) {
1283 | return TOUCH_ACTION_NONE;
1284 | }
1285 |
1286 | // pan-x OR pan-y
1287 | if (hasPanX || hasPanY) {
1288 | return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
1289 | }
1290 |
1291 | // manipulation
1292 | if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
1293 | return TOUCH_ACTION_MANIPULATION;
1294 | }
1295 |
1296 | return TOUCH_ACTION_AUTO;
1297 | }
1298 |
1299 | /**
1300 | * Recognizer flow explained; *
1301 | * All recognizers have the initial state of POSSIBLE when a input session starts.
1302 | * The definition of a input session is from the first input until the last input, with all it's movement in it. *
1303 | * Example session for mouse-input: mousedown -> mousemove -> mouseup
1304 | *
1305 | * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
1306 | * which determines with state it should be.
1307 | *
1308 | * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
1309 | * POSSIBLE to give it another change on the next cycle.
1310 | *
1311 | * Possible
1312 | * |
1313 | * +-----+---------------+
1314 | * | |
1315 | * +-----+-----+ |
1316 | * | | |
1317 | * Failed Cancelled |
1318 | * +-------+------+
1319 | * | |
1320 | * Recognized Began
1321 | * |
1322 | * Changed
1323 | * |
1324 | * Ended/Recognized
1325 | */
1326 | var STATE_POSSIBLE = 1;
1327 | var STATE_BEGAN = 2;
1328 | var STATE_CHANGED = 4;
1329 | var STATE_ENDED = 8;
1330 | var STATE_RECOGNIZED = STATE_ENDED;
1331 | var STATE_CANCELLED = 16;
1332 | var STATE_FAILED = 32;
1333 |
1334 | /**
1335 | * Recognizer
1336 | * Every recognizer needs to extend from this class.
1337 | * @constructor
1338 | * @param {Object} options
1339 | */
1340 | function Recognizer(options) {
1341 | this.options = assign({}, this.defaults, options || {});
1342 |
1343 | this.id = uniqueId();
1344 |
1345 | this.manager = null;
1346 |
1347 | // default is enable true
1348 | this.options.enable = ifUndefined(this.options.enable, true);
1349 |
1350 | this.state = STATE_POSSIBLE;
1351 |
1352 | this.simultaneous = {};
1353 | this.requireFail = [];
1354 | }
1355 |
1356 | Recognizer.prototype = {
1357 | /**
1358 | * @virtual
1359 | * @type {Object}
1360 | */
1361 | defaults: {},
1362 |
1363 | /**
1364 | * set options
1365 | * @param {Object} options
1366 | * @return {Recognizer}
1367 | */
1368 | set: function(options) {
1369 | assign(this.options, options);
1370 |
1371 | // also update the touchAction, in case something changed about the directions/enabled state
1372 | this.manager && this.manager.touchAction.update();
1373 | return this;
1374 | },
1375 |
1376 | /**
1377 | * recognize simultaneous with an other recognizer.
1378 | * @param {Recognizer} otherRecognizer
1379 | * @returns {Recognizer} this
1380 | */
1381 | recognizeWith: function(otherRecognizer) {
1382 | if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
1383 | return this;
1384 | }
1385 |
1386 | var simultaneous = this.simultaneous;
1387 | otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
1388 | if (!simultaneous[otherRecognizer.id]) {
1389 | simultaneous[otherRecognizer.id] = otherRecognizer;
1390 | otherRecognizer.recognizeWith(this);
1391 | }
1392 | return this;
1393 | },
1394 |
1395 | /**
1396 | * drop the simultaneous link. it doesnt remove the link on the other recognizer.
1397 | * @param {Recognizer} otherRecognizer
1398 | * @returns {Recognizer} this
1399 | */
1400 | dropRecognizeWith: function(otherRecognizer) {
1401 | if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
1402 | return this;
1403 | }
1404 |
1405 | otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
1406 | delete this.simultaneous[otherRecognizer.id];
1407 | return this;
1408 | },
1409 |
1410 | /**
1411 | * recognizer can only run when an other is failing
1412 | * @param {Recognizer} otherRecognizer
1413 | * @returns {Recognizer} this
1414 | */
1415 | requireFailure: function(otherRecognizer) {
1416 | if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
1417 | return this;
1418 | }
1419 |
1420 | var requireFail = this.requireFail;
1421 | otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
1422 | if (inArray(requireFail, otherRecognizer) === -1) {
1423 | requireFail.push(otherRecognizer);
1424 | otherRecognizer.requireFailure(this);
1425 | }
1426 | return this;
1427 | },
1428 |
1429 | /**
1430 | * drop the requireFailure link. it does not remove the link on the other recognizer.
1431 | * @param {Recognizer} otherRecognizer
1432 | * @returns {Recognizer} this
1433 | */
1434 | dropRequireFailure: function(otherRecognizer) {
1435 | if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
1436 | return this;
1437 | }
1438 |
1439 | otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
1440 | var index = inArray(this.requireFail, otherRecognizer);
1441 | if (index > -1) {
1442 | this.requireFail.splice(index, 1);
1443 | }
1444 | return this;
1445 | },
1446 |
1447 | /**
1448 | * has require failures boolean
1449 | * @returns {boolean}
1450 | */
1451 | hasRequireFailures: function() {
1452 | return this.requireFail.length > 0;
1453 | },
1454 |
1455 | /**
1456 | * if the recognizer can recognize simultaneous with an other recognizer
1457 | * @param {Recognizer} otherRecognizer
1458 | * @returns {Boolean}
1459 | */
1460 | canRecognizeWith: function(otherRecognizer) {
1461 | return !!this.simultaneous[otherRecognizer.id];
1462 | },
1463 |
1464 | /**
1465 | * You should use `tryEmit` instead of `emit` directly to check
1466 | * that all the needed recognizers has failed before emitting.
1467 | * @param {Object} input
1468 | */
1469 | emit: function(input) {
1470 | var self = this;
1471 | var state = this.state;
1472 |
1473 | function emit(event) {
1474 | self.manager.emit(event, input);
1475 | }
1476 |
1477 | // 'panstart' and 'panmove'
1478 | if (state < STATE_ENDED) {
1479 | emit(self.options.event + stateStr(state));
1480 | }
1481 |
1482 | emit(self.options.event); // simple 'eventName' events
1483 |
1484 | if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...)
1485 | emit(input.additionalEvent);
1486 | }
1487 |
1488 | // panend and pancancel
1489 | if (state >= STATE_ENDED) {
1490 | emit(self.options.event + stateStr(state));
1491 | }
1492 | },
1493 |
1494 | /**
1495 | * Check that all the require failure recognizers has failed,
1496 | * if true, it emits a gesture event,
1497 | * otherwise, setup the state to FAILED.
1498 | * @param {Object} input
1499 | */
1500 | tryEmit: function(input) {
1501 | if (this.canEmit()) {
1502 | return this.emit(input);
1503 | }
1504 | // it's failing anyway
1505 | this.state = STATE_FAILED;
1506 | },
1507 |
1508 | /**
1509 | * can we emit?
1510 | * @returns {boolean}
1511 | */
1512 | canEmit: function() {
1513 | var i = 0;
1514 | while (i < this.requireFail.length) {
1515 | if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
1516 | return false;
1517 | }
1518 | i++;
1519 | }
1520 | return true;
1521 | },
1522 |
1523 | /**
1524 | * update the recognizer
1525 | * @param {Object} inputData
1526 | */
1527 | recognize: function(inputData) {
1528 | // make a new copy of the inputData
1529 | // so we can change the inputData without messing up the other recognizers
1530 | var inputDataClone = assign({}, inputData);
1531 |
1532 | // is is enabled and allow recognizing?
1533 | if (!boolOrFn(this.options.enable, [this, inputDataClone])) {
1534 | this.reset();
1535 | this.state = STATE_FAILED;
1536 | return;
1537 | }
1538 |
1539 | // reset when we've reached the end
1540 | if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
1541 | this.state = STATE_POSSIBLE;
1542 | }
1543 |
1544 | this.state = this.process(inputDataClone);
1545 |
1546 | // the recognizer has recognized a gesture
1547 | // so trigger an event
1548 | if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
1549 | this.tryEmit(inputDataClone);
1550 | }
1551 | },
1552 |
1553 | /**
1554 | * return the state of the recognizer
1555 | * the actual recognizing happens in this method
1556 | * @virtual
1557 | * @param {Object} inputData
1558 | * @returns {Const} STATE
1559 | */
1560 | process: function(inputData) { }, // jshint ignore:line
1561 |
1562 | /**
1563 | * return the preferred touch-action
1564 | * @virtual
1565 | * @returns {Array}
1566 | */
1567 | getTouchAction: function() { },
1568 |
1569 | /**
1570 | * called when the gesture isn't allowed to recognize
1571 | * like when another is being recognized or it is disabled
1572 | * @virtual
1573 | */
1574 | reset: function() { }
1575 | };
1576 |
1577 | /**
1578 | * get a usable string, used as event postfix
1579 | * @param {Const} state
1580 | * @returns {String} state
1581 | */
1582 | function stateStr(state) {
1583 | if (state & STATE_CANCELLED) {
1584 | return 'cancel';
1585 | } else if (state & STATE_ENDED) {
1586 | return 'end';
1587 | } else if (state & STATE_CHANGED) {
1588 | return 'move';
1589 | } else if (state & STATE_BEGAN) {
1590 | return 'start';
1591 | }
1592 | return '';
1593 | }
1594 |
1595 | /**
1596 | * direction cons to string
1597 | * @param {Const} direction
1598 | * @returns {String}
1599 | */
1600 | function directionStr(direction) {
1601 | if (direction == DIRECTION_DOWN) {
1602 | return 'down';
1603 | } else if (direction == DIRECTION_UP) {
1604 | return 'up';
1605 | } else if (direction == DIRECTION_LEFT) {
1606 | return 'left';
1607 | } else if (direction == DIRECTION_RIGHT) {
1608 | return 'right';
1609 | }
1610 | return '';
1611 | }
1612 |
1613 | /**
1614 | * get a recognizer by name if it is bound to a manager
1615 | * @param {Recognizer|String} otherRecognizer
1616 | * @param {Recognizer} recognizer
1617 | * @returns {Recognizer}
1618 | */
1619 | function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
1620 | var manager = recognizer.manager;
1621 | if (manager) {
1622 | return manager.get(otherRecognizer);
1623 | }
1624 | return otherRecognizer;
1625 | }
1626 |
1627 | /**
1628 | * This recognizer is just used as a base for the simple attribute recognizers.
1629 | * @constructor
1630 | * @extends Recognizer
1631 | */
1632 | function AttrRecognizer() {
1633 | Recognizer.apply(this, arguments);
1634 | }
1635 |
1636 | inherit(AttrRecognizer, Recognizer, {
1637 | /**
1638 | * @namespace
1639 | * @memberof AttrRecognizer
1640 | */
1641 | defaults: {
1642 | /**
1643 | * @type {Number}
1644 | * @default 1
1645 | */
1646 | pointers: 1
1647 | },
1648 |
1649 | /**
1650 | * Used to check if it the recognizer receives valid input, like input.distance > 10.
1651 | * @memberof AttrRecognizer
1652 | * @param {Object} input
1653 | * @returns {Boolean} recognized
1654 | */
1655 | attrTest: function(input) {
1656 | var optionPointers = this.options.pointers;
1657 | return optionPointers === 0 || input.pointers.length === optionPointers;
1658 | },
1659 |
1660 | /**
1661 | * Process the input and return the state for the recognizer
1662 | * @memberof AttrRecognizer
1663 | * @param {Object} input
1664 | * @returns {*} State
1665 | */
1666 | process: function(input) {
1667 | var state = this.state;
1668 | var eventType = input.eventType;
1669 |
1670 | var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
1671 | var isValid = this.attrTest(input);
1672 |
1673 | // on cancel input and we've recognized before, return STATE_CANCELLED
1674 | if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
1675 | return state | STATE_CANCELLED;
1676 | } else if (isRecognized || isValid) {
1677 | if (eventType & INPUT_END) {
1678 | return state | STATE_ENDED;
1679 | } else if (!(state & STATE_BEGAN)) {
1680 | return STATE_BEGAN;
1681 | }
1682 | return state | STATE_CHANGED;
1683 | }
1684 | return STATE_FAILED;
1685 | }
1686 | });
1687 |
1688 | /**
1689 | * Pan
1690 | * Recognized when the pointer is down and moved in the allowed direction.
1691 | * @constructor
1692 | * @extends AttrRecognizer
1693 | */
1694 | function PanRecognizer() {
1695 | AttrRecognizer.apply(this, arguments);
1696 |
1697 | this.pX = null;
1698 | this.pY = null;
1699 | }
1700 |
1701 | inherit(PanRecognizer, AttrRecognizer, {
1702 | /**
1703 | * @namespace
1704 | * @memberof PanRecognizer
1705 | */
1706 | defaults: {
1707 | event: 'pan',
1708 | threshold: 10,
1709 | pointers: 1,
1710 | direction: DIRECTION_ALL
1711 | },
1712 |
1713 | getTouchAction: function() {
1714 | var direction = this.options.direction;
1715 | var actions = [];
1716 | if (direction & DIRECTION_HORIZONTAL) {
1717 | actions.push(TOUCH_ACTION_PAN_Y);
1718 | }
1719 | if (direction & DIRECTION_VERTICAL) {
1720 | actions.push(TOUCH_ACTION_PAN_X);
1721 | }
1722 | return actions;
1723 | },
1724 |
1725 | directionTest: function(input) {
1726 | var options = this.options;
1727 | var hasMoved = true;
1728 | var distance = input.distance;
1729 | var direction = input.direction;
1730 | var x = input.deltaX;
1731 | var y = input.deltaY;
1732 |
1733 | // lock to axis?
1734 | if (!(direction & options.direction)) {
1735 | if (options.direction & DIRECTION_HORIZONTAL) {
1736 | direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;
1737 | hasMoved = x != this.pX;
1738 | distance = Math.abs(input.deltaX);
1739 | } else {
1740 | direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN;
1741 | hasMoved = y != this.pY;
1742 | distance = Math.abs(input.deltaY);
1743 | }
1744 | }
1745 | input.direction = direction;
1746 | return hasMoved && distance > options.threshold && direction & options.direction;
1747 | },
1748 |
1749 | attrTest: function(input) {
1750 | return AttrRecognizer.prototype.attrTest.call(this, input) &&
1751 | (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input)));
1752 | },
1753 |
1754 | emit: function(input) {
1755 |
1756 | this.pX = input.deltaX;
1757 | this.pY = input.deltaY;
1758 |
1759 | var direction = directionStr(input.direction);
1760 |
1761 | if (direction) {
1762 | input.additionalEvent = this.options.event + direction;
1763 | }
1764 | this._super.emit.call(this, input);
1765 | }
1766 | });
1767 |
1768 | /**
1769 | * Pinch
1770 | * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
1771 | * @constructor
1772 | * @extends AttrRecognizer
1773 | */
1774 | function PinchRecognizer() {
1775 | AttrRecognizer.apply(this, arguments);
1776 | }
1777 |
1778 | inherit(PinchRecognizer, AttrRecognizer, {
1779 | /**
1780 | * @namespace
1781 | * @memberof PinchRecognizer
1782 | */
1783 | defaults: {
1784 | event: 'pinch',
1785 | threshold: 0,
1786 | pointers: 2
1787 | },
1788 |
1789 | getTouchAction: function() {
1790 | return [TOUCH_ACTION_NONE];
1791 | },
1792 |
1793 | attrTest: function(input) {
1794 | return this._super.attrTest.call(this, input) &&
1795 | (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
1796 | },
1797 |
1798 | emit: function(input) {
1799 | if (input.scale !== 1) {
1800 | var inOut = input.scale < 1 ? 'in' : 'out';
1801 | input.additionalEvent = this.options.event + inOut;
1802 | }
1803 | this._super.emit.call(this, input);
1804 | }
1805 | });
1806 |
1807 | /**
1808 | * Press
1809 | * Recognized when the pointer is down for x ms without any movement.
1810 | * @constructor
1811 | * @extends Recognizer
1812 | */
1813 | function PressRecognizer() {
1814 | Recognizer.apply(this, arguments);
1815 |
1816 | this._timer = null;
1817 | this._input = null;
1818 | }
1819 |
1820 | inherit(PressRecognizer, Recognizer, {
1821 | /**
1822 | * @namespace
1823 | * @memberof PressRecognizer
1824 | */
1825 | defaults: {
1826 | event: 'press',
1827 | pointers: 1,
1828 | time: 251, // minimal time of the pointer to be pressed
1829 | threshold: 9 // a minimal movement is ok, but keep it low
1830 | },
1831 |
1832 | getTouchAction: function() {
1833 | return [TOUCH_ACTION_AUTO];
1834 | },
1835 |
1836 | process: function(input) {
1837 | var options = this.options;
1838 | var validPointers = input.pointers.length === options.pointers;
1839 | var validMovement = input.distance < options.threshold;
1840 | var validTime = input.deltaTime > options.time;
1841 |
1842 | this._input = input;
1843 |
1844 | // we only allow little movement
1845 | // and we've reached an end event, so a tap is possible
1846 | if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) {
1847 | this.reset();
1848 | } else if (input.eventType & INPUT_START) {
1849 | this.reset();
1850 | this._timer = setTimeoutContext(function() {
1851 | this.state = STATE_RECOGNIZED;
1852 | this.tryEmit();
1853 | }, options.time, this);
1854 | } else if (input.eventType & INPUT_END) {
1855 | return STATE_RECOGNIZED;
1856 | }
1857 | return STATE_FAILED;
1858 | },
1859 |
1860 | reset: function() {
1861 | clearTimeout(this._timer);
1862 | },
1863 |
1864 | emit: function(input) {
1865 | if (this.state !== STATE_RECOGNIZED) {
1866 | return;
1867 | }
1868 |
1869 | if (input && (input.eventType & INPUT_END)) {
1870 | this.manager.emit(this.options.event + 'up', input);
1871 | } else {
1872 | this._input.timeStamp = now();
1873 | this.manager.emit(this.options.event, this._input);
1874 | }
1875 | }
1876 | });
1877 |
1878 | /**
1879 | * Rotate
1880 | * Recognized when two or more pointer are moving in a circular motion.
1881 | * @constructor
1882 | * @extends AttrRecognizer
1883 | */
1884 | function RotateRecognizer() {
1885 | AttrRecognizer.apply(this, arguments);
1886 | }
1887 |
1888 | inherit(RotateRecognizer, AttrRecognizer, {
1889 | /**
1890 | * @namespace
1891 | * @memberof RotateRecognizer
1892 | */
1893 | defaults: {
1894 | event: 'rotate',
1895 | threshold: 0,
1896 | pointers: 2
1897 | },
1898 |
1899 | getTouchAction: function() {
1900 | return [TOUCH_ACTION_NONE];
1901 | },
1902 |
1903 | attrTest: function(input) {
1904 | return this._super.attrTest.call(this, input) &&
1905 | (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
1906 | }
1907 | });
1908 |
1909 | /**
1910 | * Swipe
1911 | * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
1912 | * @constructor
1913 | * @extends AttrRecognizer
1914 | */
1915 | function SwipeRecognizer() {
1916 | AttrRecognizer.apply(this, arguments);
1917 | }
1918 |
1919 | inherit(SwipeRecognizer, AttrRecognizer, {
1920 | /**
1921 | * @namespace
1922 | * @memberof SwipeRecognizer
1923 | */
1924 | defaults: {
1925 | event: 'swipe',
1926 | threshold: 10,
1927 | velocity: 0.3,
1928 | direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
1929 | pointers: 1
1930 | },
1931 |
1932 | getTouchAction: function() {
1933 | return PanRecognizer.prototype.getTouchAction.call(this);
1934 | },
1935 |
1936 | attrTest: function(input) {
1937 | var direction = this.options.direction;
1938 | var velocity;
1939 |
1940 | if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
1941 | velocity = input.overallVelocity;
1942 | } else if (direction & DIRECTION_HORIZONTAL) {
1943 | velocity = input.overallVelocityX;
1944 | } else if (direction & DIRECTION_VERTICAL) {
1945 | velocity = input.overallVelocityY;
1946 | }
1947 |
1948 | return this._super.attrTest.call(this, input) &&
1949 | direction & input.offsetDirection &&
1950 | input.distance > this.options.threshold &&
1951 | input.maxPointers == this.options.pointers &&
1952 | abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
1953 | },
1954 |
1955 | emit: function(input) {
1956 | var direction = directionStr(input.offsetDirection);
1957 | if (direction) {
1958 | this.manager.emit(this.options.event + direction, input);
1959 | }
1960 |
1961 | this.manager.emit(this.options.event, input);
1962 | }
1963 | });
1964 |
1965 | /**
1966 | * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
1967 | * between the given interval and position. The delay option can be used to recognize multi-taps without firing
1968 | * a single tap.
1969 | *
1970 | * The eventData from the emitted event contains the property `tapCount`, which contains the amount of
1971 | * multi-taps being recognized.
1972 | * @constructor
1973 | * @extends Recognizer
1974 | */
1975 | function TapRecognizer() {
1976 | Recognizer.apply(this, arguments);
1977 |
1978 | // previous time and center,
1979 | // used for tap counting
1980 | this.pTime = false;
1981 | this.pCenter = false;
1982 |
1983 | this._timer = null;
1984 | this._input = null;
1985 | this.count = 0;
1986 | }
1987 |
1988 | inherit(TapRecognizer, Recognizer, {
1989 | /**
1990 | * @namespace
1991 | * @memberof PinchRecognizer
1992 | */
1993 | defaults: {
1994 | event: 'tap',
1995 | pointers: 1,
1996 | taps: 1,
1997 | interval: 300, // max time between the multi-tap taps
1998 | time: 250, // max time of the pointer to be down (like finger on the screen)
1999 | threshold: 9, // a minimal movement is ok, but keep it low
2000 | posThreshold: 10 // a multi-tap can be a bit off the initial position
2001 | },
2002 |
2003 | getTouchAction: function() {
2004 | return [TOUCH_ACTION_MANIPULATION];
2005 | },
2006 |
2007 | process: function(input) {
2008 | var options = this.options;
2009 |
2010 | var validPointers = input.pointers.length === options.pointers;
2011 | var validMovement = input.distance < options.threshold;
2012 | var validTouchTime = input.deltaTime < options.time;
2013 |
2014 | this.reset();
2015 |
2016 | if ((input.eventType & INPUT_START) && (this.count === 0)) {
2017 | return this.failTimeout();
2018 | }
2019 |
2020 | // we only allow little movement
2021 | // and we've reached an end event, so a tap is possible
2022 | if (validMovement && validTouchTime && validPointers) {
2023 | if (input.eventType != INPUT_END) {
2024 | return this.failTimeout();
2025 | }
2026 |
2027 | var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true;
2028 | var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;
2029 |
2030 | this.pTime = input.timeStamp;
2031 | this.pCenter = input.center;
2032 |
2033 | if (!validMultiTap || !validInterval) {
2034 | this.count = 1;
2035 | } else {
2036 | this.count += 1;
2037 | }
2038 |
2039 | this._input = input;
2040 |
2041 | // if tap count matches we have recognized it,
2042 | // else it has began recognizing...
2043 | var tapCount = this.count % options.taps;
2044 | if (tapCount === 0) {
2045 | // no failing requirements, immediately trigger the tap event
2046 | // or wait as long as the multitap interval to trigger
2047 | if (!this.hasRequireFailures()) {
2048 | return STATE_RECOGNIZED;
2049 | } else {
2050 | this._timer = setTimeoutContext(function() {
2051 | this.state = STATE_RECOGNIZED;
2052 | this.tryEmit();
2053 | }, options.interval, this);
2054 | return STATE_BEGAN;
2055 | }
2056 | }
2057 | }
2058 | return STATE_FAILED;
2059 | },
2060 |
2061 | failTimeout: function() {
2062 | this._timer = setTimeoutContext(function() {
2063 | this.state = STATE_FAILED;
2064 | }, this.options.interval, this);
2065 | return STATE_FAILED;
2066 | },
2067 |
2068 | reset: function() {
2069 | clearTimeout(this._timer);
2070 | },
2071 |
2072 | emit: function() {
2073 | if (this.state == STATE_RECOGNIZED) {
2074 | this._input.tapCount = this.count;
2075 | this.manager.emit(this.options.event, this._input);
2076 | }
2077 | }
2078 | });
2079 |
2080 | /**
2081 | * Simple way to create a manager with a default set of recognizers.
2082 | * @param {HTMLElement} element
2083 | * @param {Object} [options]
2084 | * @constructor
2085 | */
2086 | function Hammer(element, options) {
2087 | options = options || {};
2088 | options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
2089 | return new Manager(element, options);
2090 | }
2091 |
2092 | /**
2093 | * @const {string}
2094 | */
2095 | Hammer.VERSION = '2.0.6';
2096 |
2097 | /**
2098 | * default settings
2099 | * @namespace
2100 | */
2101 | Hammer.defaults = {
2102 | /**
2103 | * set if DOM events are being triggered.
2104 | * But this is slower and unused by simple implementations, so disabled by default.
2105 | * @type {Boolean}
2106 | * @default false
2107 | */
2108 | domEvents: false,
2109 |
2110 | /**
2111 | * The value for the touchAction property/fallback.
2112 | * When set to `compute` it will magically set the correct value based on the added recognizers.
2113 | * @type {String}
2114 | * @default compute
2115 | */
2116 | touchAction: TOUCH_ACTION_COMPUTE,
2117 |
2118 | /**
2119 | * @type {Boolean}
2120 | * @default true
2121 | */
2122 | enable: true,
2123 |
2124 | /**
2125 | * EXPERIMENTAL FEATURE -- can be removed/changed
2126 | * Change the parent input target element.
2127 | * If Null, then it is being set the to main element.
2128 | * @type {Null|EventTarget}
2129 | * @default null
2130 | */
2131 | inputTarget: null,
2132 |
2133 | /**
2134 | * force an input class
2135 | * @type {Null|Function}
2136 | * @default null
2137 | */
2138 | inputClass: null,
2139 |
2140 | /**
2141 | * Default recognizer setup when calling `Hammer()`
2142 | * When creating a new Manager these will be skipped.
2143 | * @type {Array}
2144 | */
2145 | preset: [
2146 | // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
2147 | [RotateRecognizer, {enable: false}],
2148 | [PinchRecognizer, {enable: false}, ['rotate']],
2149 | [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}],
2150 | [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']],
2151 | [TapRecognizer],
2152 | [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']],
2153 | [PressRecognizer]
2154 | ],
2155 |
2156 | /**
2157 | * Some CSS properties can be used to improve the working of Hammer.
2158 | * Add them to this method and they will be set when creating a new Manager.
2159 | * @namespace
2160 | */
2161 | cssProps: {
2162 | /**
2163 | * Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
2164 | * @type {String}
2165 | * @default 'none'
2166 | */
2167 | userSelect: 'none',
2168 |
2169 | /**
2170 | * Disable the Windows Phone grippers when pressing an element.
2171 | * @type {String}
2172 | * @default 'none'
2173 | */
2174 | touchSelect: 'none',
2175 |
2176 | /**
2177 | * Disables the default callout shown when you touch and hold a touch target.
2178 | * On iOS, when you touch and hold a touch target such as a link, Safari displays
2179 | * a callout containing information about the link. This property allows you to disable that callout.
2180 | * @type {String}
2181 | * @default 'none'
2182 | */
2183 | touchCallout: 'none',
2184 |
2185 | /**
2186 | * Specifies whether zooming is enabled. Used by IE10>
2187 | * @type {String}
2188 | * @default 'none'
2189 | */
2190 | contentZooming: 'none',
2191 |
2192 | /**
2193 | * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
2194 | * @type {String}
2195 | * @default 'none'
2196 | */
2197 | userDrag: 'none',
2198 |
2199 | /**
2200 | * Overrides the highlight color shown when the user taps a link or a JavaScript
2201 | * clickable element in iOS. This property obeys the alpha value, if specified.
2202 | * @type {String}
2203 | * @default 'rgba(0,0,0,0)'
2204 | */
2205 | tapHighlightColor: 'rgba(0,0,0,0)'
2206 | }
2207 | };
2208 |
2209 | var STOP = 1;
2210 | var FORCED_STOP = 2;
2211 |
2212 | /**
2213 | * Manager
2214 | * @param {HTMLElement} element
2215 | * @param {Object} [options]
2216 | * @constructor
2217 | */
2218 | function Manager(element, options) {
2219 | this.options = assign({}, Hammer.defaults, options || {});
2220 |
2221 | this.options.inputTarget = this.options.inputTarget || element;
2222 |
2223 | this.handlers = {};
2224 | this.session = {};
2225 | this.recognizers = [];
2226 |
2227 | this.element = element;
2228 | this.input = createInputInstance(this);
2229 | this.touchAction = new TouchAction(this, this.options.touchAction);
2230 |
2231 | toggleCssProps(this, true);
2232 |
2233 | each(this.options.recognizers, function(item) {
2234 | var recognizer = this.add(new (item[0])(item[1]));
2235 | item[2] && recognizer.recognizeWith(item[2]);
2236 | item[3] && recognizer.requireFailure(item[3]);
2237 | }, this);
2238 | }
2239 |
2240 | Manager.prototype = {
2241 | /**
2242 | * set options
2243 | * @param {Object} options
2244 | * @returns {Manager}
2245 | */
2246 | set: function(options) {
2247 | assign(this.options, options);
2248 |
2249 | // Options that need a little more setup
2250 | if (options.touchAction) {
2251 | this.touchAction.update();
2252 | }
2253 | if (options.inputTarget) {
2254 | // Clean up existing event listeners and reinitialize
2255 | this.input.destroy();
2256 | this.input.target = options.inputTarget;
2257 | this.input.init();
2258 | }
2259 | return this;
2260 | },
2261 |
2262 | /**
2263 | * stop recognizing for this session.
2264 | * This session will be discarded, when a new [input]start event is fired.
2265 | * When forced, the recognizer cycle is stopped immediately.
2266 | * @param {Boolean} [force]
2267 | */
2268 | stop: function(force) {
2269 | this.session.stopped = force ? FORCED_STOP : STOP;
2270 | },
2271 |
2272 | /**
2273 | * run the recognizers!
2274 | * called by the inputHandler function on every movement of the pointers (touches)
2275 | * it walks through all the recognizers and tries to detect the gesture that is being made
2276 | * @param {Object} inputData
2277 | */
2278 | recognize: function(inputData) {
2279 | var session = this.session;
2280 | if (session.stopped) {
2281 | return;
2282 | }
2283 |
2284 | // run the touch-action polyfill
2285 | this.touchAction.preventDefaults(inputData);
2286 |
2287 | var recognizer;
2288 | var recognizers = this.recognizers;
2289 |
2290 | // this holds the recognizer that is being recognized.
2291 | // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
2292 | // if no recognizer is detecting a thing, it is set to `null`
2293 | var curRecognizer = session.curRecognizer;
2294 |
2295 | // reset when the last recognizer is recognized
2296 | // or when we're in a new session
2297 | if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) {
2298 | curRecognizer = session.curRecognizer = null;
2299 | }
2300 |
2301 | var i = 0;
2302 | while (i < recognizers.length) {
2303 | recognizer = recognizers[i];
2304 |
2305 | // find out if we are allowed try to recognize the input for this one.
2306 | // 1. allow if the session is NOT forced stopped (see the .stop() method)
2307 | // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
2308 | // that is being recognized.
2309 | // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
2310 | // this can be setup with the `recognizeWith()` method on the recognizer.
2311 | if (session.stopped !== FORCED_STOP && ( // 1
2312 | !curRecognizer || recognizer == curRecognizer || // 2
2313 | recognizer.canRecognizeWith(curRecognizer))) { // 3
2314 | recognizer.recognize(inputData);
2315 | } else {
2316 | recognizer.reset();
2317 | }
2318 |
2319 | // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
2320 | // current active recognizer. but only if we don't already have an active recognizer
2321 | if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
2322 | curRecognizer = session.curRecognizer = recognizer;
2323 | }
2324 | i++;
2325 | }
2326 | },
2327 |
2328 | /**
2329 | * get a recognizer by its event name.
2330 | * @param {Recognizer|String} recognizer
2331 | * @returns {Recognizer|Null}
2332 | */
2333 | get: function(recognizer) {
2334 | if (recognizer instanceof Recognizer) {
2335 | return recognizer;
2336 | }
2337 |
2338 | var recognizers = this.recognizers;
2339 | for (var i = 0; i < recognizers.length; i++) {
2340 | if (recognizers[i].options.event == recognizer) {
2341 | return recognizers[i];
2342 | }
2343 | }
2344 | return null;
2345 | },
2346 |
2347 | /**
2348 | * add a recognizer to the manager
2349 | * existing recognizers with the same event name will be removed
2350 | * @param {Recognizer} recognizer
2351 | * @returns {Recognizer|Manager}
2352 | */
2353 | add: function(recognizer) {
2354 | if (invokeArrayArg(recognizer, 'add', this)) {
2355 | return this;
2356 | }
2357 |
2358 | // remove existing
2359 | var existing = this.get(recognizer.options.event);
2360 | if (existing) {
2361 | this.remove(existing);
2362 | }
2363 |
2364 | this.recognizers.push(recognizer);
2365 | recognizer.manager = this;
2366 |
2367 | this.touchAction.update();
2368 | return recognizer;
2369 | },
2370 |
2371 | /**
2372 | * remove a recognizer by name or instance
2373 | * @param {Recognizer|String} recognizer
2374 | * @returns {Manager}
2375 | */
2376 | remove: function(recognizer) {
2377 | if (invokeArrayArg(recognizer, 'remove', this)) {
2378 | return this;
2379 | }
2380 |
2381 | recognizer = this.get(recognizer);
2382 |
2383 | // let's make sure this recognizer exists
2384 | if (recognizer) {
2385 | var recognizers = this.recognizers;
2386 | var index = inArray(recognizers, recognizer);
2387 |
2388 | if (index !== -1) {
2389 | recognizers.splice(index, 1);
2390 | this.touchAction.update();
2391 | }
2392 | }
2393 |
2394 | return this;
2395 | },
2396 |
2397 | /**
2398 | * bind event
2399 | * @param {String} events
2400 | * @param {Function} handler
2401 | * @returns {EventEmitter} this
2402 | */
2403 | on: function(events, handler) {
2404 | var handlers = this.handlers;
2405 | each(splitStr(events), function(event) {
2406 | handlers[event] = handlers[event] || [];
2407 | handlers[event].push(handler);
2408 | });
2409 | return this;
2410 | },
2411 |
2412 | /**
2413 | * unbind event, leave emit blank to remove all handlers
2414 | * @param {String} events
2415 | * @param {Function} [handler]
2416 | * @returns {EventEmitter} this
2417 | */
2418 | off: function(events, handler) {
2419 | var handlers = this.handlers;
2420 | each(splitStr(events), function(event) {
2421 | if (!handler) {
2422 | delete handlers[event];
2423 | } else {
2424 | handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1);
2425 | }
2426 | });
2427 | return this;
2428 | },
2429 |
2430 | /**
2431 | * emit event to the listeners
2432 | * @param {String} event
2433 | * @param {Object} data
2434 | */
2435 | emit: function(event, data) {
2436 | // we also want to trigger dom events
2437 | if (this.options.domEvents) {
2438 | triggerDomEvent(event, data);
2439 | }
2440 |
2441 | // no handlers, so skip it all
2442 | var handlers = this.handlers[event] && this.handlers[event].slice();
2443 | if (!handlers || !handlers.length) {
2444 | return;
2445 | }
2446 |
2447 | data.type = event;
2448 | data.preventDefault = function() {
2449 | data.srcEvent.preventDefault();
2450 | };
2451 |
2452 | var i = 0;
2453 | while (i < handlers.length) {
2454 | handlers[i](data);
2455 | i++;
2456 | }
2457 | },
2458 |
2459 | /**
2460 | * destroy the manager and unbinds all events
2461 | * it doesn't unbind dom events, that is the user own responsibility
2462 | */
2463 | destroy: function() {
2464 | this.element && toggleCssProps(this, false);
2465 |
2466 | this.handlers = {};
2467 | this.session = {};
2468 | this.input.destroy();
2469 | this.element = null;
2470 | }
2471 | };
2472 |
2473 | /**
2474 | * add/remove the css properties as defined in manager.options.cssProps
2475 | * @param {Manager} manager
2476 | * @param {Boolean} add
2477 | */
2478 | function toggleCssProps(manager, add) {
2479 | var element = manager.element;
2480 | if (!element.style) {
2481 | return;
2482 | }
2483 | each(manager.options.cssProps, function(value, name) {
2484 | element.style[prefixed(element.style, name)] = add ? value : '';
2485 | });
2486 | }
2487 |
2488 | /**
2489 | * trigger dom event
2490 | * @param {String} event
2491 | * @param {Object} data
2492 | */
2493 | function triggerDomEvent(event, data) {
2494 | var gestureEvent = document.createEvent('Event');
2495 | gestureEvent.initEvent(event, true, true);
2496 | gestureEvent.gesture = data;
2497 | data.target.dispatchEvent(gestureEvent);
2498 | }
2499 |
2500 | assign(Hammer, {
2501 | INPUT_START: INPUT_START,
2502 | INPUT_MOVE: INPUT_MOVE,
2503 | INPUT_END: INPUT_END,
2504 | INPUT_CANCEL: INPUT_CANCEL,
2505 |
2506 | STATE_POSSIBLE: STATE_POSSIBLE,
2507 | STATE_BEGAN: STATE_BEGAN,
2508 | STATE_CHANGED: STATE_CHANGED,
2509 | STATE_ENDED: STATE_ENDED,
2510 | STATE_RECOGNIZED: STATE_RECOGNIZED,
2511 | STATE_CANCELLED: STATE_CANCELLED,
2512 | STATE_FAILED: STATE_FAILED,
2513 |
2514 | DIRECTION_NONE: DIRECTION_NONE,
2515 | DIRECTION_LEFT: DIRECTION_LEFT,
2516 | DIRECTION_RIGHT: DIRECTION_RIGHT,
2517 | DIRECTION_UP: DIRECTION_UP,
2518 | DIRECTION_DOWN: DIRECTION_DOWN,
2519 | DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
2520 | DIRECTION_VERTICAL: DIRECTION_VERTICAL,
2521 | DIRECTION_ALL: DIRECTION_ALL,
2522 |
2523 | Manager: Manager,
2524 | Input: Input,
2525 | TouchAction: TouchAction,
2526 |
2527 | TouchInput: TouchInput,
2528 | MouseInput: MouseInput,
2529 | PointerEventInput: PointerEventInput,
2530 | TouchMouseInput: TouchMouseInput,
2531 | SingleTouchInput: SingleTouchInput,
2532 |
2533 | Recognizer: Recognizer,
2534 | AttrRecognizer: AttrRecognizer,
2535 | Tap: TapRecognizer,
2536 | Pan: PanRecognizer,
2537 | Swipe: SwipeRecognizer,
2538 | Pinch: PinchRecognizer,
2539 | Rotate: RotateRecognizer,
2540 | Press: PressRecognizer,
2541 |
2542 | on: addEventListeners,
2543 | off: removeEventListeners,
2544 | each: each,
2545 | merge: merge,
2546 | extend: extend,
2547 | assign: assign,
2548 | inherit: inherit,
2549 | bindFn: bindFn,
2550 | prefixed: prefixed
2551 | });
2552 |
2553 | // this prevents errors when Hammer is loaded in the presence of an AMD
2554 | // style loader but by script tag, not by the loader.
2555 | var freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line
2556 | freeGlobal.Hammer = Hammer;
2557 |
2558 | if (typeof define === 'function' && define.amd) {
2559 | define(function() {
2560 | return Hammer;
2561 | });
2562 | } else if (typeof module != 'undefined' && module.exports) {
2563 | module.exports = Hammer;
2564 | } else {
2565 | window[exportName] = Hammer;
2566 | }
2567 |
2568 | })(window, document, 'Hammer');
2569 |
--------------------------------------------------------------------------------