├── .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 | [![Build Status](https://travis-ci.org/hammerjs/hammer.js.svg)](https://travis-ci.org/hammerjs/hammer.js) 4 | 5 | ## Support, Questions, and Collaboration 6 | 7 | [![Slack Status](https://hammerjs.herokuapp.com/badge.svg)](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 | menu 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 | --------------------------------------------------------------------------------