├── .bowerrc
├── .gitignore
├── .jshintrc
├── Gruntfile.js
├── LICENSE.md
├── README.md
├── bower.json
├── demo
├── .bowerrc
├── .editorconfig
├── .gitattributes
├── .gitignore
├── .jshintrc
├── .travis.yml
├── app
│ ├── .buildignore
│ ├── .htaccess
│ ├── favicon.ico
│ ├── index.html
│ ├── robots.txt
│ ├── scripts
│ │ ├── app.js
│ │ └── controllers
│ │ │ └── main.js
│ ├── styles
│ │ ├── bootstrap.css
│ │ └── main.css
│ └── views
│ │ └── main.html
├── bower.json
├── gulpfile.js
├── karma-e2e.conf.js
├── karma.conf.js
├── package.json
└── test
│ ├── .jshintrc
│ ├── runner.html
│ └── spec
│ └── controllers
│ └── main.js
├── dist
├── LICENSE.md
├── README.md
├── bower.json
├── gestures.js
└── gestures.min.js
├── gulpfile.js
├── karma.conf.js
├── package.json
├── src
└── gestures.js
└── test
└── gestures.Spec.js
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "components"
3 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .project
2 | node_modules
3 | components
4 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "bitwise": true,
6 | "camelcase": true,
7 | "curly": true,
8 | "eqeqeq": true,
9 | "immed": true,
10 | "indent": 2,
11 | "latedef": true,
12 | "newcap": true,
13 | "noarg": true,
14 | "quotmark": "single",
15 | "regexp": true,
16 | "undef": true,
17 | "unused": true,
18 | "strict": true,
19 | "trailing": true,
20 | "smarttabs": true,
21 | "globals": {
22 | "angular": false,
23 | "Hammer": false,
24 | // testing
25 | "it": false,
26 | "beforeEach": false,
27 | "describe": false,
28 | "expect": false,
29 | "inject": false
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(grunt) {
4 | // load all grunt tasks
5 | require('load-grunt-tasks')(grunt);
6 | require('time-grunt')(grunt);
7 |
8 | grunt.initConfig({
9 | concat : {
10 | dist : {
11 | files : {
12 | /*'components/hammerjs/hammer.js', */
13 | 'dist/gestures.js' : [ 'src/gestures.js' ]
14 | }
15 | }
16 | },
17 | watch: {
18 | js: {
19 | files: ['src/{,*/}*.js'],
20 | tasks: ['build']
21 | }
22 | },
23 | clean : {
24 | dist : {
25 | files : [ {
26 | dot : true,
27 | src : [ 'dist/**' ]
28 | } ]
29 | },
30 | server : '.tmp'
31 | },
32 | uglify: {
33 | dist: {
34 | files: { 'dist/gestures.min.js': [ 'dist/gestures.js' ] }
35 | }
36 | },
37 | jshint : {
38 | options : {
39 | jshintrc : '.jshintrc'
40 | },
41 | all : [ 'Gruntfile.js', 'src/*.js' ]
42 | },
43 | copy: {
44 | main: {
45 | files: [
46 | {
47 | expand: true,
48 | src: ['./*.md'],
49 | dest: 'dist/',
50 | filter: 'isFile'
51 | } // copy *.md
52 | ]
53 | }
54 | }
55 | });
56 |
57 | grunt.registerTask('build', [
58 | 'clean:dist',
59 | // 'jshint',
60 | 'concat',
61 | 'uglify',
62 | 'copy',
63 | 'bowerdist'
64 | ]);
65 |
66 | grunt.registerTask('default', [ 'build' ]);
67 | grunt.registerTask('watchme', [ 'watch' ]);
68 |
69 | grunt.registerTask('bowerdist', function () {
70 | var bower = require('./bower.json');
71 | var fs = require('fs');
72 |
73 | bower.main = bower.main.map(function (main) {
74 | return main.replace('dist/', '');
75 | });
76 |
77 | fs.writeFileSync('./dist/bower.json', JSON.stringify(bower, null, 2));
78 | });
79 | };
80 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | ## The MIT License
2 |
3 | Copyright (c) 2012-2013 Patrick Bartsch
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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # angular-gestures
2 |
3 | AngularJS directive that adds support for multi touch gestures to your app, based on hammer.js.
4 |
5 | ## Usage
6 |
7 | * Include `gestures.js` or `gestures.min.js` into your page
8 | * Declare `'angular-gestures'` as a dependency for your angular app: `angular.module('myApp', ['angular-gestures']);`
9 | * Config the **recognizers** before you use the directive like this: `hammerDefaultOptsProvider.set({recognizers: [[Hammer.Tap, {time: 250}]] });`
10 | * Use attributes on containers the same way you use `ng-click`: e.g. `hm-tap`
11 | ```HTML
12 |
13 | ```
14 | * You can use angular interpolations like this : `hm-swipe="remove_something({{ id }})"`
15 | * You can also use Hammer.js options by e.g. `hm-tap-opts="{hold: false}"`
16 |
17 | Note that [hammer.js](http://hammerjs.github.io/) is an additional requirement and is not included in `angular-gestures`.
18 |
19 | ### Event data
20 |
21 | Pass the `$event` object in the usual way e.g. `hm-drag="myDrag($event)"` then access its internals like so:
22 | ```JS
23 | $scope.myDrag = function(event) {
24 | console.log(event.gesture);
25 | }
26 | ```
27 | Refer to the [Hammer.js docs](https://github.com/EightMedia/hammer.js/wiki/Getting-Started) for more details on the properties of `event`.
28 |
29 | ## Supported events
30 |
31 |
32 | * hmDoubleTap : 'doubletap',
33 | * hmDragstart : 'dragstart',
34 | * hmDrag : 'drag',
35 | * hmDragUp : 'dragup',
36 | * hmDragDown : 'dragdown',
37 | * hmDragLeft : 'dragleft',
38 | * hmDragRight : 'dragright',
39 | * hmDragend : 'dragend',
40 | * hmHold : 'hold',
41 | * hmPinch : 'pinch',
42 | * hmPinchIn : 'pinchin',
43 | * hmPinchOut : 'pinchout',
44 | * hmRelease : 'release',
45 | * hmRotate : 'rotate',
46 | * hmSwipe : 'swipe',
47 | * hmSwipeUp : 'swipeup',
48 | * hmSwipeDown : 'swipedown',
49 | * hmSwipeLeft : 'swipeleft',
50 | * hmSwipeRight : 'swiperight',
51 | * hmTap : 'tap',
52 | * hmTouch : 'touch',
53 | * hmTransformstart : 'transformstart',
54 | * hmTransform : 'transform',
55 | * hmTransformend : 'transformend'
56 |
57 |
58 | All [Hammerjs events](https://github.com/EightMedia/hammer.js/wiki/Getting-Started) are supported. The corresponding Angularjs attribute has `hm-` prepended to the name. So for example, the 'doubletap' event becomes `hm-double-tap` etc.
59 |
60 | *Attention* : *end and *start events are NOT CamelCased because of issues caused by $animate interference.
61 |
62 | ## Default options
63 | To set recognizer default options you can use `hammerDefaultOptsProvider`. Access it like in the demo:
64 |
65 | ```
66 | angular.module('angularGesturesDemoApp', ['angular-gestures', 'ngRoute'])
67 | .config(function ($routeProvider, hammerDefaultOptsProvider) {
68 | $routeProvider
69 | .when('/', {
70 | templateUrl: 'views/main.html',
71 | controller: 'MainCtrl'
72 | })
73 | .otherwise({
74 | redirectTo: '/'
75 | });
76 | hammerDefaultOptsProvider.set({
77 | recognizers: [[Hammer.Tap, {time: 250}]]
78 | });
79 | });
80 | ```
81 |
82 | ## Bower
83 | If you want to use angular-momentum-scroll with bower, add the following dependency to your component.json
84 |
85 | `"angular-gestures": "latest"`
86 |
87 | ## Require.js/AMD/Node.js
88 | angular-gestures has support for Require.js/AMD/Node.js. When using AMD modules, make sure that you define
89 | hammer.js using `Hammer`, same goes for `node.js`. If you are not using Require.js/AMD/Node.js, angular-gestures
90 | will fall back to using the global `Hammer`/`angular` objects.
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-gestures",
3 | "description": "AngularJS directive that adds support for multi touch gestures to your app. Based on hammer.js.",
4 | "version": "0.3.3",
5 | "main": [
6 | "dist/gestures.js"
7 | ],
8 | "homepage": "http://github.com/wzr1337/angular-gestures",
9 | "repository": {
10 | "type": "git",
11 | "url": "git://github.com/wzr1337/bower-angular-gestures"
12 | },
13 | "author": "wzr1337",
14 | "license": "MIT",
15 | "readmeFilename": "README.md",
16 | "dependencies": {
17 | "angular": ">=1.2.0 <=2.0.0",
18 | "hammerjs": "~2.0.0"
19 | },
20 | "devDependencies": {
21 | "angular-mocks": ">=1.2.0 <=2.0.0"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/demo/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "app/bower_components"
3 | }
4 |
--------------------------------------------------------------------------------
/demo/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 |
10 | # Change these settings to your own preference
11 | indent_style = space
12 | indent_size = 2
13 |
14 | # We recommend you to keep these unchanged
15 | end_of_line = lf
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
--------------------------------------------------------------------------------
/demo/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/demo/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .tmp
4 | .sass-cache
5 | app/bower_components
6 |
--------------------------------------------------------------------------------
/demo/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "bitwise": true,
6 | "camelcase": true,
7 | "curly": true,
8 | "eqeqeq": true,
9 | "immed": true,
10 | "indent": 2,
11 | "latedef": true,
12 | "newcap": true,
13 | "noarg": true,
14 | "quotmark": "single",
15 | "regexp": true,
16 | "undef": true,
17 | "unused": true,
18 | "strict": true,
19 | "trailing": true,
20 | "smarttabs": true,
21 | "globals": {
22 | "angular": false,
23 | "Hammer" : false
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/demo/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.8'
4 | - '0.10'
5 | before_script:
6 | - 'npm install -g bower grunt-cli'
7 | - 'bower install'
8 |
--------------------------------------------------------------------------------
/demo/app/.buildignore:
--------------------------------------------------------------------------------
1 | *.coffee
--------------------------------------------------------------------------------
/demo/app/.htaccess:
--------------------------------------------------------------------------------
1 | # Apache Configuration File
2 |
3 | # (!) Using `.htaccess` files slows down Apache, therefore, if you have access
4 | # to the main server config file (usually called `httpd.conf`), you should add
5 | # this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html.
6 |
7 | # ##############################################################################
8 | # # CROSS-ORIGIN RESOURCE SHARING (CORS) #
9 | # ##############################################################################
10 |
11 | # ------------------------------------------------------------------------------
12 | # | Cross-domain AJAX requests |
13 | # ------------------------------------------------------------------------------
14 |
15 | # Enable cross-origin AJAX requests.
16 | # http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity
17 | # http://enable-cors.org/
18 |
19 | #
20 | # Header set Access-Control-Allow-Origin "*"
21 | #
22 |
23 | # ------------------------------------------------------------------------------
24 | # | CORS-enabled images |
25 | # ------------------------------------------------------------------------------
26 |
27 | # Send the CORS header for images when browsers request it.
28 | # https://developer.mozilla.org/en/CORS_Enabled_Image
29 | # http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html
30 | # http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/
31 |
32 |
33 |
34 |
35 | SetEnvIf Origin ":" IS_CORS
36 | Header set Access-Control-Allow-Origin "*" env=IS_CORS
37 |
38 |
39 |
40 |
41 | # ------------------------------------------------------------------------------
42 | # | Web fonts access |
43 | # ------------------------------------------------------------------------------
44 |
45 | # Allow access from all domains for web fonts
46 |
47 |
48 |
49 | Header set Access-Control-Allow-Origin "*"
50 |
51 |
52 |
53 |
54 | # ##############################################################################
55 | # # ERRORS #
56 | # ##############################################################################
57 |
58 | # ------------------------------------------------------------------------------
59 | # | 404 error prevention for non-existing redirected folders |
60 | # ------------------------------------------------------------------------------
61 |
62 | # Prevent Apache from returning a 404 error for a rewrite if a directory
63 | # with the same name does not exist.
64 | # http://httpd.apache.org/docs/current/content-negotiation.html#multiviews
65 | # http://www.webmasterworld.com/apache/3808792.htm
66 |
67 | Options -MultiViews
68 |
69 | # ------------------------------------------------------------------------------
70 | # | Custom error messages / pages |
71 | # ------------------------------------------------------------------------------
72 |
73 | # You can customize what Apache returns to the client in case of an error (see
74 | # http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.:
75 |
76 | ErrorDocument 404 /404.html
77 |
78 |
79 | # ##############################################################################
80 | # # INTERNET EXPLORER #
81 | # ##############################################################################
82 |
83 | # ------------------------------------------------------------------------------
84 | # | Better website experience |
85 | # ------------------------------------------------------------------------------
86 |
87 | # Force IE to render pages in the highest available mode in the various
88 | # cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf.
89 |
90 |
91 | Header set X-UA-Compatible "IE=edge"
92 | # `mod_headers` can't match based on the content-type, however, we only
93 | # want to send this header for HTML pages and not for the other resources
94 |
95 | Header unset X-UA-Compatible
96 |
97 |
98 |
99 | # ------------------------------------------------------------------------------
100 | # | Cookie setting from iframes |
101 | # ------------------------------------------------------------------------------
102 |
103 | # Allow cookies to be set from iframes in IE.
104 |
105 | #
106 | # Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\""
107 | #
108 |
109 | # ------------------------------------------------------------------------------
110 | # | Screen flicker |
111 | # ------------------------------------------------------------------------------
112 |
113 | # Stop screen flicker in IE on CSS rollovers (this only works in
114 | # combination with the `ExpiresByType` directives for images from below).
115 |
116 | # BrowserMatch "MSIE" brokenvary=1
117 | # BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1
118 | # BrowserMatch "Opera" !brokenvary
119 | # SetEnvIf brokenvary 1 force-no-vary
120 |
121 |
122 | # ##############################################################################
123 | # # MIME TYPES AND ENCODING #
124 | # ##############################################################################
125 |
126 | # ------------------------------------------------------------------------------
127 | # | Proper MIME types for all files |
128 | # ------------------------------------------------------------------------------
129 |
130 |
131 |
132 | # Audio
133 | AddType audio/mp4 m4a f4a f4b
134 | AddType audio/ogg oga ogg
135 |
136 | # JavaScript
137 | # Normalize to standard type (it's sniffed in IE anyways):
138 | # http://tools.ietf.org/html/rfc4329#section-7.2
139 | AddType application/javascript js jsonp
140 | AddType application/json json
141 |
142 | # Video
143 | AddType video/mp4 mp4 m4v f4v f4p
144 | AddType video/ogg ogv
145 | AddType video/webm webm
146 | AddType video/x-flv flv
147 |
148 | # Web fonts
149 | AddType application/font-woff woff
150 | AddType application/vnd.ms-fontobject eot
151 |
152 | # Browsers usually ignore the font MIME types and sniff the content,
153 | # however, Chrome shows a warning if other MIME types are used for the
154 | # following fonts.
155 | AddType application/x-font-ttf ttc ttf
156 | AddType font/opentype otf
157 |
158 | # Make SVGZ fonts work on iPad:
159 | # https://twitter.com/FontSquirrel/status/14855840545
160 | AddType image/svg+xml svg svgz
161 | AddEncoding gzip svgz
162 |
163 | # Other
164 | AddType application/octet-stream safariextz
165 | AddType application/x-chrome-extension crx
166 | AddType application/x-opera-extension oex
167 | AddType application/x-shockwave-flash swf
168 | AddType application/x-web-app-manifest+json webapp
169 | AddType application/x-xpinstall xpi
170 | AddType application/xml atom rdf rss xml
171 | AddType image/webp webp
172 | AddType image/x-icon ico
173 | AddType text/cache-manifest appcache manifest
174 | AddType text/vtt vtt
175 | AddType text/x-component htc
176 | AddType text/x-vcard vcf
177 |
178 |
179 |
180 | # ------------------------------------------------------------------------------
181 | # | UTF-8 encoding |
182 | # ------------------------------------------------------------------------------
183 |
184 | # Use UTF-8 encoding for anything served as `text/html` or `text/plain`.
185 | AddDefaultCharset utf-8
186 |
187 | # Force UTF-8 for certain file formats.
188 |
189 | AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml
190 |
191 |
192 |
193 | # ##############################################################################
194 | # # URL REWRITES #
195 | # ##############################################################################
196 |
197 | # ------------------------------------------------------------------------------
198 | # | Rewrite engine |
199 | # ------------------------------------------------------------------------------
200 |
201 | # Turning on the rewrite engine and enabling the `FollowSymLinks` option is
202 | # necessary for the following directives to work.
203 |
204 | # If your web host doesn't allow the `FollowSymlinks` option, you may need to
205 | # comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the
206 | # performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks
207 |
208 | # Also, some cloud hosting services require `RewriteBase` to be set:
209 | # http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site
210 |
211 |
212 | Options +FollowSymlinks
213 | # Options +SymLinksIfOwnerMatch
214 | RewriteEngine On
215 | # RewriteBase /
216 |
217 |
218 | # ------------------------------------------------------------------------------
219 | # | Suppressing / Forcing the "www." at the beginning of URLs |
220 | # ------------------------------------------------------------------------------
221 |
222 | # The same content should never be available under two different URLs especially
223 | # not with and without "www." at the beginning. This can cause SEO problems
224 | # (duplicate content), therefore, you should choose one of the alternatives and
225 | # redirect the other one.
226 |
227 | # By default option 1 (no "www.") is activated:
228 | # http://no-www.org/faq.php?q=class_b
229 |
230 | # If you'd prefer to use option 2, just comment out all the lines from option 1
231 | # and uncomment the ones from option 2.
232 |
233 | # IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME!
234 |
235 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
236 |
237 | # Option 1: rewrite www.example.com → example.com
238 |
239 |
240 | RewriteCond %{HTTPS} !=on
241 | RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
242 | RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
243 |
244 |
245 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
246 |
247 | # Option 2: rewrite example.com → www.example.com
248 |
249 | # Be aware that the following might not be a good idea if you use "real"
250 | # subdomains for certain parts of your website.
251 |
252 | #
253 | # RewriteCond %{HTTPS} !=on
254 | # RewriteCond %{HTTP_HOST} !^www\..+$ [NC]
255 | # RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
256 | #
257 |
258 |
259 | # ##############################################################################
260 | # # SECURITY #
261 | # ##############################################################################
262 |
263 | # ------------------------------------------------------------------------------
264 | # | Content Security Policy (CSP) |
265 | # ------------------------------------------------------------------------------
266 |
267 | # You can mitigate the risk of cross-site scripting and other content-injection
268 | # attacks by setting a Content Security Policy which whitelists trusted sources
269 | # of content for your site.
270 |
271 | # The example header below allows ONLY scripts that are loaded from the current
272 | # site's origin (no inline scripts, no CDN, etc). This almost certainly won't
273 | # work as-is for your site!
274 |
275 | # To get all the details you'll need to craft a reasonable policy for your site,
276 | # read: http://html5rocks.com/en/tutorials/security/content-security-policy (or
277 | # see the specification: http://w3.org/TR/CSP).
278 |
279 | #
280 | # Header set Content-Security-Policy "script-src 'self'; object-src 'self'"
281 | #
282 | # Header unset Content-Security-Policy
283 | #
284 | #
285 |
286 | # ------------------------------------------------------------------------------
287 | # | File access |
288 | # ------------------------------------------------------------------------------
289 |
290 | # Block access to directories without a default document.
291 | # Usually you should leave this uncommented because you shouldn't allow anyone
292 | # to surf through every directory on your server (which may includes rather
293 | # private places like the CMS's directories).
294 |
295 |
296 | Options -Indexes
297 |
298 |
299 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
300 |
301 | # Block access to hidden files and directories.
302 | # This includes directories used by version control systems such as Git and SVN.
303 |
304 |
305 | RewriteCond %{SCRIPT_FILENAME} -d [OR]
306 | RewriteCond %{SCRIPT_FILENAME} -f
307 | RewriteRule "(^|/)\." - [F]
308 |
309 |
310 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
311 |
312 | # Block access to backup and source files.
313 | # These files may be left by some text editors and can pose a great security
314 | # danger when anyone has access to them.
315 |
316 |
317 | Order allow,deny
318 | Deny from all
319 | Satisfy All
320 |
321 |
322 | # ------------------------------------------------------------------------------
323 | # | Secure Sockets Layer (SSL) |
324 | # ------------------------------------------------------------------------------
325 |
326 | # Rewrite secure requests properly to prevent SSL certificate warnings, e.g.:
327 | # prevent `https://www.example.com` when your certificate only allows
328 | # `https://secure.example.com`.
329 |
330 | #
331 | # RewriteCond %{SERVER_PORT} !^443
332 | # RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L]
333 | #
334 |
335 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
336 |
337 | # Force client-side SSL redirection.
338 |
339 | # If a user types "example.com" in his browser, the above rule will redirect him
340 | # to the secure version of the site. That still leaves a window of opportunity
341 | # (the initial HTTP connection) for an attacker to downgrade or redirect the
342 | # request. The following header ensures that browser will ONLY connect to your
343 | # server via HTTPS, regardless of what the users type in the address bar.
344 | # http://www.html5rocks.com/en/tutorials/security/transport-layer-security/
345 |
346 | #
347 | # Header set Strict-Transport-Security max-age=16070400;
348 | #
349 |
350 | # ------------------------------------------------------------------------------
351 | # | Server software information |
352 | # ------------------------------------------------------------------------------
353 |
354 | # Avoid displaying the exact Apache version number, the description of the
355 | # generic OS-type and the information about Apache's compiled-in modules.
356 |
357 | # ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`!
358 |
359 | # ServerTokens Prod
360 |
361 |
362 | # ##############################################################################
363 | # # WEB PERFORMANCE #
364 | # ##############################################################################
365 |
366 | # ------------------------------------------------------------------------------
367 | # | Compression |
368 | # ------------------------------------------------------------------------------
369 |
370 |
371 |
372 | # Force compression for mangled headers.
373 | # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping
374 |
375 |
376 | SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
377 | RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
378 |
379 |
380 |
381 | # Compress all output labeled with one of the following MIME-types
382 | # (for Apache versions below 2.3.7, you don't need to enable `mod_filter`
383 | # and can remove the `` and `` lines
384 | # as `AddOutputFilterByType` is still in the core directives).
385 |
386 | AddOutputFilterByType DEFLATE application/atom+xml \
387 | application/javascript \
388 | application/json \
389 | application/rss+xml \
390 | application/vnd.ms-fontobject \
391 | application/x-font-ttf \
392 | application/x-web-app-manifest+json \
393 | application/xhtml+xml \
394 | application/xml \
395 | font/opentype \
396 | image/svg+xml \
397 | image/x-icon \
398 | text/css \
399 | text/html \
400 | text/plain \
401 | text/x-component \
402 | text/xml
403 |
404 |
405 |
406 |
407 | # ------------------------------------------------------------------------------
408 | # | Content transformations |
409 | # ------------------------------------------------------------------------------
410 |
411 | # Prevent some of the mobile network providers from modifying the content of
412 | # your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5.
413 |
414 | #
415 | # Header set Cache-Control "no-transform"
416 | #
417 |
418 | # ------------------------------------------------------------------------------
419 | # | ETag removal |
420 | # ------------------------------------------------------------------------------
421 |
422 | # Since we're sending far-future expires headers (see below), ETags can
423 | # be removed: http://developer.yahoo.com/performance/rules.html#etags.
424 |
425 | # `FileETag None` is not enough for every server.
426 |
427 | Header unset ETag
428 |
429 |
430 | FileETag None
431 |
432 | # ------------------------------------------------------------------------------
433 | # | Expires headers (for better cache control) |
434 | # ------------------------------------------------------------------------------
435 |
436 | # The following expires headers are set pretty far in the future. If you don't
437 | # control versioning with filename-based cache busting, consider lowering the
438 | # cache time for resources like CSS and JS to something like 1 week.
439 |
440 |
441 |
442 | ExpiresActive on
443 | ExpiresDefault "access plus 1 month"
444 |
445 | # CSS
446 | ExpiresByType text/css "access plus 1 year"
447 |
448 | # Data interchange
449 | ExpiresByType application/json "access plus 0 seconds"
450 | ExpiresByType application/xml "access plus 0 seconds"
451 | ExpiresByType text/xml "access plus 0 seconds"
452 |
453 | # Favicon (cannot be renamed!)
454 | ExpiresByType image/x-icon "access plus 1 week"
455 |
456 | # HTML components (HTCs)
457 | ExpiresByType text/x-component "access plus 1 month"
458 |
459 | # HTML
460 | ExpiresByType text/html "access plus 0 seconds"
461 |
462 | # JavaScript
463 | ExpiresByType application/javascript "access plus 1 year"
464 |
465 | # Manifest files
466 | ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds"
467 | ExpiresByType text/cache-manifest "access plus 0 seconds"
468 |
469 | # Media
470 | ExpiresByType audio/ogg "access plus 1 month"
471 | ExpiresByType image/gif "access plus 1 month"
472 | ExpiresByType image/jpeg "access plus 1 month"
473 | ExpiresByType image/png "access plus 1 month"
474 | ExpiresByType video/mp4 "access plus 1 month"
475 | ExpiresByType video/ogg "access plus 1 month"
476 | ExpiresByType video/webm "access plus 1 month"
477 |
478 | # Web feeds
479 | ExpiresByType application/atom+xml "access plus 1 hour"
480 | ExpiresByType application/rss+xml "access plus 1 hour"
481 |
482 | # Web fonts
483 | ExpiresByType application/font-woff "access plus 1 month"
484 | ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
485 | ExpiresByType application/x-font-ttf "access plus 1 month"
486 | ExpiresByType font/opentype "access plus 1 month"
487 | ExpiresByType image/svg+xml "access plus 1 month"
488 |
489 |
490 |
491 | # ------------------------------------------------------------------------------
492 | # | Filename-based cache busting |
493 | # ------------------------------------------------------------------------------
494 |
495 | # If you're not using a build process to manage your filename version revving,
496 | # you might want to consider enabling the following directives to route all
497 | # requests such as `/css/style.12345.css` to `/css/style.css`.
498 |
499 | # To understand why this is important and a better idea than `*.css?v231`, read:
500 | # http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring
501 |
502 | #
503 | # RewriteCond %{REQUEST_FILENAME} !-f
504 | # RewriteCond %{REQUEST_FILENAME} !-d
505 | # RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L]
506 | #
507 |
508 | # ------------------------------------------------------------------------------
509 | # | File concatenation |
510 | # ------------------------------------------------------------------------------
511 |
512 | # Allow concatenation from within specific CSS and JS files, e.g.:
513 | # Inside of `script.combined.js` you could have
514 | #
515 | #
516 | # and they would be included into this single file.
517 |
518 | #
519 | #
520 | # Options +Includes
521 | # AddOutputFilterByType INCLUDES application/javascript application/json
522 | # SetOutputFilter INCLUDES
523 | #
524 | #
525 | # Options +Includes
526 | # AddOutputFilterByType INCLUDES text/css
527 | # SetOutputFilter INCLUDES
528 | #
529 | #
530 |
531 | # ------------------------------------------------------------------------------
532 | # | Persistent connections |
533 | # ------------------------------------------------------------------------------
534 |
535 | # Allow multiple requests to be sent over the same TCP connection:
536 | # http://httpd.apache.org/docs/current/en/mod/core.html#keepalive.
537 |
538 | # Enable if you serve a lot of static content but, be aware of the
539 | # possible disadvantages!
540 |
541 | #
542 | # Header set Connection Keep-Alive
543 | #
544 |
--------------------------------------------------------------------------------
/demo/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wzr1337/angular-gestures/2b5226b8286436c5bbdb4d82951abc0659f7f187/demo/app/favicon.ico
--------------------------------------------------------------------------------
/demo/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/demo/app/robots.txt:
--------------------------------------------------------------------------------
1 | # robotstxt.org
2 |
3 | User-agent: *
4 |
--------------------------------------------------------------------------------
/demo/app/scripts/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('angularGesturesDemoApp', ['angular-gestures', 'ngRoute'])
4 | .config(function ($routeProvider, hammerDefaultOptsProvider) {
5 | $routeProvider
6 | .when('/', {
7 | templateUrl: 'views/main.html',
8 | controller: 'MainCtrl'
9 | })
10 | .otherwise({
11 | redirectTo: '/'
12 | });
13 | hammerDefaultOptsProvider.set({
14 | recognizers: [
15 | [Hammer.Tap,{ event: 'tap'}],
16 | [Hammer.Tap, { event: 'doubletap', taps: 2 }, [], ['tap']],
17 | [Hammer.Press],
18 | [Hammer.Pan]
19 | ]
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/demo/app/scripts/controllers/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('angularGesturesDemoApp')
4 | .controller('MainCtrl', function ($scope) {
5 | $scope.type = '--';
6 | $scope.handleGesture = function($event) {
7 | console.log('button event', $event);
8 | $scope.type = $event.type;
9 | $event.srcEvent.stopPropagation();
10 | };
11 | $scope.handleParentGesture = function($event) {
12 | console.log('parent event', $event);
13 | $scope.type = $event.type;
14 | };
15 | });
16 |
--------------------------------------------------------------------------------
/demo/app/styles/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: #fafafa;
3 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
4 | color: #333;
5 | }
6 |
7 | .hero-unit {
8 | margin: 4rem auto 0 auto;
9 | width: 90%;
10 | font-size: 1.5rem;
11 | font-weight: 200;
12 | line-height: 2rem;
13 | background-color: #eee;
14 | border-radius: 6px;
15 | padding: 4rem;
16 | }
17 |
18 | .hero-unit h1 {
19 | font-size: 4rem;
20 | line-height: 1;
21 | }
22 |
23 | .hero-unit #container {
24 | width : 10rem;
25 | height : 10rem;
26 | line-height: 5rem;
27 | }
28 |
--------------------------------------------------------------------------------
/demo/app/views/main.html:
--------------------------------------------------------------------------------
1 |
7 |
Angular Gestures Demo
8 |
Please tap, press(hold) and pan the following container
9 |
You can {{ type }} this container
16 |
17 |
--------------------------------------------------------------------------------
/demo/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angularGesturesDemo",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "angular": "~1.2.9",
6 | "angular-gestures": "~0.3.0",
7 | "angular-route": "~1.2.9",
8 | "hammerjs": "~2.0.0"
9 | },
10 | "devDependencies": {
11 | "angular-mocks": "~1.2.9",
12 | "angular-scenario": "~1.2.9"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/demo/gulpfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var gulp = require('gulp');
4 | var jshint = require('gulp-jshint');
5 | var uglify = require('gulp-uglify');
6 | var rev = require('gulp-rev');
7 | var usemin = require('gulp-usemin');
8 | var path = require('path');
9 | var rimraf = require('gulp-rimraf');
10 | var ngAnnotate = require('gulp-ng-annotate');
11 | var server = require('gulp-webserver');
12 |
13 | var bases = {
14 | app: 'app/',
15 | dist: 'dist/'
16 | };
17 |
18 | var paths = {
19 | scripts: ['scripts/**/*.js', '!scripts/libs/**/*.js'],
20 | styles: ['styles/**/*.css'],
21 | html: ['index.html', '404.html'],
22 | views: ['views/**/*.html']
23 | };
24 |
25 | gulp.task('copy', ['clean'], function() {
26 | return gulp.src(paths.views, {cwd: bases.app, read:true})
27 | .pipe(gulp.dest(path.join(bases.dist, 'views')));
28 | });
29 |
30 | gulp.task('clean', function() {
31 | var stream = gulp.src([bases.dist], { read: false }) // much faster
32 | .pipe(rimraf());
33 | return stream;
34 | });
35 |
36 | // Process scripts and concatenate them into one output file
37 | gulp.task('scripts', ['clean'], function() {
38 | return gulp.src(paths.scripts, {cwd: bases.app})
39 | .pipe(jshint('.jshintrc'))
40 | .pipe(jshint.reporter('jshint-stylish'));
41 | });
42 |
43 |
44 | gulp.task('usemin', ['clean', 'scripts'], function () {
45 | return gulp.src('./*.html', {cwd: bases.app})
46 | .pipe(usemin({
47 | js: [ngAnnotate(), uglify(), rev()]
48 | }))
49 | .pipe(gulp.dest(bases.dist));
50 | });
51 |
52 | // Rerun the task when a file changes
53 | gulp.task('watch', function() {
54 | gulp.watch(path.join(bases.app, paths.scripts[0]), ['usemin']);
55 | });
56 |
57 | gulp.task('default', ['clean', 'usemin', 'copy']);
58 |
59 | gulp.task('serve', function() {
60 | gulp.src('./')
61 | .pipe(server({
62 | livereload: true,
63 | directoryListing: true,
64 | open: true
65 | }));
66 | });
67 |
--------------------------------------------------------------------------------
/demo/karma-e2e.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // http://karma-runner.github.io/0.10/config/configuration-file.html
3 |
4 | module.exports = function(config) {
5 | config.set({
6 | // base path, that will be used to resolve files and exclude
7 | basePath: '',
8 |
9 | // testing framework to use (jasmine/mocha/qunit/...)
10 | frameworks: ['ng-scenario'],
11 |
12 | // list of files / patterns to load in the browser
13 | files: [
14 | 'test/e2e/**/*.js'
15 | ],
16 |
17 | // list of files / patterns to exclude
18 | exclude: [],
19 |
20 | // web server port
21 | port: 8080,
22 |
23 | // level of logging
24 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
25 | logLevel: config.LOG_INFO,
26 |
27 |
28 | // enable / disable watching file and executing tests whenever any file changes
29 | autoWatch: false,
30 |
31 |
32 | // Start these browsers, currently available:
33 | // - Chrome
34 | // - ChromeCanary
35 | // - Firefox
36 | // - Opera
37 | // - Safari (only Mac)
38 | // - PhantomJS
39 | // - IE (only Windows)
40 | browsers: ['Chrome'],
41 |
42 |
43 | // Continuous Integration mode
44 | // if true, it capture browsers, run tests and exit
45 | singleRun: false
46 |
47 | // Uncomment the following lines if you are using grunt's server to run the tests
48 | // proxies: {
49 | // '/': 'http://localhost:9000/'
50 | // },
51 | // URL root prevent conflicts with the site root
52 | // urlRoot: '_karma_'
53 | });
54 | };
55 |
--------------------------------------------------------------------------------
/demo/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // http://karma-runner.github.io/0.10/config/configuration-file.html
3 |
4 | module.exports = function(config) {
5 | config.set({
6 | // base path, that will be used to resolve files and exclude
7 | basePath: '',
8 |
9 | // testing framework to use (jasmine/mocha/qunit/...)
10 | frameworks: ['jasmine'],
11 |
12 | // list of files / patterns to load in the browser
13 | files: [
14 | 'app/bower_components/angular/angular.js',
15 | 'app/bower_components/angular-mocks/angular-mocks.js',
16 | 'app/bower_components/angular-gestures/gestures.min.js',
17 | 'app/scripts/*.js',
18 | 'app/scripts/**/*.js',
19 | 'test/mock/**/*.js',
20 | 'test/spec/**/*.js'
21 | ],
22 |
23 | // list of files / patterns to exclude
24 | exclude: [],
25 |
26 | // web server port
27 | port: 8080,
28 |
29 | // level of logging
30 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
31 | logLevel: config.LOG_INFO,
32 |
33 |
34 | // enable / disable watching file and executing tests whenever any file changes
35 | autoWatch: false,
36 |
37 |
38 | // Start these browsers, currently available:
39 | // - Chrome
40 | // - ChromeCanary
41 | // - Firefox
42 | // - Opera
43 | // - Safari (only Mac)
44 | // - PhantomJS
45 | // - IE (only Windows)
46 | browsers: ['Chrome'],
47 |
48 |
49 | // Continuous Integration mode
50 | // if true, it capture browsers, run tests and exit
51 | singleRun: false
52 | });
53 | };
54 |
--------------------------------------------------------------------------------
/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-gestures-demo",
3 | "version": "0.0.0",
4 | "dependencies": {},
5 | "devDependencies": {
6 | "gulp": "^3.8.11",
7 | "gulp-autoprefixer": "^2.1.0",
8 | "gulp-concat": "^2.5.2",
9 | "gulp-jshint": "^1.9.2",
10 | "gulp-minify-css": "^0.5.1",
11 | "gulp-minify-html": "^1.0.1",
12 | "gulp-ng-annotate": "^0.5.2",
13 | "gulp-ngmin": "^0.3.0",
14 | "gulp-rev": "^3.0.1",
15 | "gulp-rimraf": "^0.1.1",
16 | "gulp-uglify": "^1.1.0",
17 | "gulp-usemin": "^0.3.11",
18 | "gulp-webserver": "^0.9.0",
19 | "jshint-stylish": "^1.0.1"
20 | },
21 | "engines": {
22 | "node": ">=0.10.0"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/demo/test/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "bitwise": true,
6 | "camelcase": true,
7 | "curly": true,
8 | "eqeqeq": true,
9 | "immed": true,
10 | "indent": 2,
11 | "latedef": true,
12 | "newcap": true,
13 | "noarg": true,
14 | "quotmark": "single",
15 | "regexp": true,
16 | "undef": true,
17 | "unused": true,
18 | "strict": true,
19 | "trailing": true,
20 | "smarttabs": true,
21 | "globals": {
22 | "after": false,
23 | "afterEach": false,
24 | "angular": false,
25 | "before": false,
26 | "beforeEach": false,
27 | "browser": false,
28 | "describe": false,
29 | "expect": false,
30 | "inject": false,
31 | "it": false,
32 | "spyOn": false
33 | }
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/demo/test/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | End2end Test Runner
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/demo/test/spec/controllers/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Controller: MainCtrl', function () {
4 |
5 | // load the controller's module
6 | beforeEach(module('angularGesturesDemoApp', 'angular-gestures'));
7 |
8 | var MainCtrl,
9 | scope;
10 |
11 | // Initialize the controller and a mock scope
12 | beforeEach(inject(function ($controller, $rootScope) {
13 | scope = $rootScope.$new();
14 | MainCtrl = $controller('MainCtrl', {
15 | $scope: scope
16 | });
17 | }));
18 |
19 | it('should attach a variable "type" to the scope', function () {
20 | expect(scope.type).toBe('--');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/dist/LICENSE.md:
--------------------------------------------------------------------------------
1 | ## The MIT License
2 |
3 | Copyright (c) 2012-2013 Patrick Bartsch
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.
--------------------------------------------------------------------------------
/dist/README.md:
--------------------------------------------------------------------------------
1 | # angular-gestures
2 |
3 | AngularJS directive that adds support for multi touch gestures to your app, based on hammer.js.
4 |
5 | ## Usage
6 |
7 | * Include `gestures.js` or `gestures.min.js` into your page
8 | * Declare `'angular-gestures'` as a dependency for your angular app: `angular.module('myApp', ['angular-gestures']);`
9 | * Config the **recognizers** before you use the directive like this: `hammerDefaultOptsProvider.set({recognizers: [[Hammer.Tap, {time: 250}]] });`
10 | * Use attributes on containers the same way you use `ng-click`: e.g. `hm-tap`
11 | ```HTML
12 |
13 | ```
14 | * You can use angular interpolations like this : `hm-swipe="remove_something({{ id }})"`
15 | * You can also use Hammer.js options by e.g. `hm-tap-opts="{hold: false}"`
16 |
17 | Note that [hammer.js](http://hammerjs.github.io/) is an additional requirement and is not included in `angular-gestures`.
18 |
19 | ### Event data
20 |
21 | Pass the `$event` object in the usual way e.g. `hm-drag="myDrag($event)"` then access its internals like so:
22 | ```JS
23 | $scope.myDrag = function(event) {
24 | console.log(event.gesture);
25 | }
26 | ```
27 | Refer to the [Hammer.js docs](https://github.com/EightMedia/hammer.js/wiki/Getting-Started) for more details on the properties of `event`.
28 |
29 | ## Supported events
30 |
31 |
32 | * hmDoubleTap : 'doubletap',
33 | * hmDragstart : 'dragstart',
34 | * hmDrag : 'drag',
35 | * hmDragUp : 'dragup',
36 | * hmDragDown : 'dragdown',
37 | * hmDragLeft : 'dragleft',
38 | * hmDragRight : 'dragright',
39 | * hmDragend : 'dragend',
40 | * hmHold : 'hold',
41 | * hmPinch : 'pinch',
42 | * hmPinchIn : 'pinchin',
43 | * hmPinchOut : 'pinchout',
44 | * hmRelease : 'release',
45 | * hmRotate : 'rotate',
46 | * hmSwipe : 'swipe',
47 | * hmSwipeUp : 'swipeup',
48 | * hmSwipeDown : 'swipedown',
49 | * hmSwipeLeft : 'swipeleft',
50 | * hmSwipeRight : 'swiperight',
51 | * hmTap : 'tap',
52 | * hmTouch : 'touch',
53 | * hmTransformstart : 'transformstart',
54 | * hmTransform : 'transform',
55 | * hmTransformend : 'transformend'
56 |
57 |
58 | All [Hammerjs events](https://github.com/EightMedia/hammer.js/wiki/Getting-Started) are supported. The corresponding Angularjs attribute has `hm-` prepended to the name. So for example, the 'doubletap' event becomes `hm-double-tap` etc.
59 |
60 | *Attention* : *end and *start events are NOT CamelCased because of issues caused by $animate interference.
61 |
62 | ## Default options
63 | To set recognizer default options you can use `hammerDefaultOptsProvider`. Access it like in the demo:
64 |
65 | ```
66 | angular.module('angularGesturesDemoApp', ['angular-gestures', 'ngRoute'])
67 | .config(function ($routeProvider, hammerDefaultOptsProvider) {
68 | $routeProvider
69 | .when('/', {
70 | templateUrl: 'views/main.html',
71 | controller: 'MainCtrl'
72 | })
73 | .otherwise({
74 | redirectTo: '/'
75 | });
76 | hammerDefaultOptsProvider.set({
77 | recognizers: [[Hammer.Tap, {time: 250}]]
78 | });
79 | });
80 | ```
81 |
82 | ## Bower
83 | If you want to use angular-momentum-scroll with bower, add the following dependency to your component.json
84 |
85 | `"angular-gestures": "latest"`
86 |
87 | ## Require.js/AMD/Node.js
88 | angular-gestures has support for Require.js/AMD/Node.js. When using AMD modules, make sure that you define
89 | hammer.js using `Hammer`, same goes for `node.js`. If you are not using Require.js/AMD/Node.js, angular-gestures
90 | will fall back to using the global `Hammer`/`angular` objects.
--------------------------------------------------------------------------------
/dist/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-gestures",
3 | "description": "AngularJS directive that adds support for multi touch gestures to your app. Based on hammer.js.",
4 | "version": "0.3.1",
5 | "main": [
6 | "gestures.min.js"
7 | ],
8 | "homepage": "http://github.com/wzr1337/angular-gestures",
9 | "repository": {
10 | "type": "git",
11 | "url": "git://github.com/wzr1337/bower-angular-gestures"
12 | },
13 | "author": "wzr1337",
14 | "license": "MIT",
15 | "readmeFilename": "README.md",
16 | "dependencies": {
17 | "angular": ">=1.2.0 <=1.4.0",
18 | "hammerjs": "~2.0.0"
19 | },
20 | "devDependencies": {
21 | "angular-mocks": ">=1.2.0 <=1.4.0"
22 | }
23 | }
--------------------------------------------------------------------------------
/dist/gestures.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Inspired by AngularJS' implementation of "click dblclick mousedown..."
3 | *
4 | * This ties in the Hammer 2 events to attributes like:
5 | *
6 | * hm-tap="add_something()" hm-swipe="remove_something()"
7 | *
8 | * and also has support for Hammer options with:
9 | *
10 | * hm-tap-opts="{hold: false}"
11 | *
12 | * or any other of the "hm-event" listed underneath.
13 | */
14 |
15 | 'use strict';
16 | (function (root, factory) {
17 | // AMD
18 | if (typeof define === 'function' && define.amd) {
19 | define(['angular', 'Hammer'], function (angular, Hammer) {
20 | return factory({}, angular, Hammer);
21 | });
22 | }
23 | // Node.js
24 | else if (typeof exports === 'object') {
25 | module.exports = factory({}, require('angular'), require('Hammer'));
26 | }
27 | // Angular
28 | else if (angular) {
29 | factory(root, root.angular, root.Hammer);
30 | }
31 | }(this,function(global,angular,Hammer){
32 | angular.module('angular-gestures', []);
33 |
34 | var HGESTURES = {
35 | hmDoubleTap: 'doubletap',
36 | hmDragstart: 'panstart', // will bedeprecated soon, us Pan*
37 | hmDrag: 'pan', // will bedeprecated soon, us Pan*
38 | hmDragUp: 'panup', // will bedeprecated soon, us Pan*
39 | hmDragDown: 'pandown', // will bedeprecated soon, us Pan*
40 | hmDragLeft: 'panleft', // will bedeprecated soon, us Pan*
41 | hmDragRight: 'panright', // will bedeprecated soon, us Pan*
42 | hmDragend: 'panend', // will bedeprecated soon, us Pan*
43 | hmPanstart: 'panstart',
44 | hmPan: 'pan',
45 | hmPanUp: 'panup',
46 | hmPanDown: 'pandown',
47 | hmPanLeft: 'panleft',
48 | hmPanRight: 'panright',
49 | hmPanend: 'panend',
50 | hmHold: 'press',
51 | hmPinch: 'pinch',
52 | hmPinchstart: 'pinchstart',
53 | hmPinchend: 'pinchend',
54 | hmPinchIn: 'pinchin',
55 | hmPinchOut: 'pinchout',
56 | hmPress: 'press',
57 | hmPressUp: 'pressup',
58 | hmRelease: 'pressup',
59 | hmRotate: 'rotate',
60 | hmSwipe: 'swipe',
61 | hmSwipeUp: 'swipeup',
62 | hmSwipeDown: 'swipedown',
63 | hmSwipeLeft: 'swipeleft',
64 | hmSwipeRight: 'swiperight',
65 | hmTap: 'tap',
66 | hmTouch: 'touch',
67 | hmTransformstart: 'transformstart',
68 | hmTransform: 'transform',
69 | hmTransformend: 'transformend'
70 | };
71 |
72 | var HRECOGNIZERS = {
73 | hmDoubleTap: [Hammer.Tap, 'Hammer.Tap'],
74 | hmDragstart: [Hammer.Pan, 'Hammer.Pan'],
75 | hmDrag: [Hammer.Pan, 'Hammer.Pan'],
76 | hmDragUp: [Hammer.Pan, 'Hammer.Pan'],
77 | hmDragDown: [Hammer.Pan, 'Hammer.Pan'],
78 | hmDragLeft: [Hammer.Pan, 'Hammer.Pan'],
79 | hmDragRight: [Hammer.Pan, 'Hammer.Pan'],
80 | hmDragend: [Hammer.Pan, 'Hammer.Pan'],
81 | hmPanstart: [Hammer.Pan, 'Hammer.Pan'],
82 | hmPan: [Hammer.Pan, 'Hammer.Pan'],
83 | hmPanUp: [Hammer.Pan, 'Hammer.Pan'],
84 | hmPanDown: [Hammer.Pan, 'Hammer.Pan'],
85 | hmPanLeft: [Hammer.Pan, 'Hammer.Pan'],
86 | hmPanRight: [Hammer.Pan, 'Hammer.Pan'],
87 | hmPanend: [Hammer.Pan, 'Hammer.Pan'],
88 | hmHold: [Hammer.Press, 'Hammer.Press'],
89 | hmPinch: [Hammer.Pinch, 'Hammer.Pinch'],
90 | hmPinchstart: [Hammer.Pinch, 'Hammer.Pinch'],
91 | hmPinchend: [Hammer.Pinch, 'Hammer.Pinch'],
92 | hmPinchIn: [Hammer.Pinch, 'Hammer.Pinch'],
93 | hmPinchOut: [Hammer.Pinch, 'Hammer.Pinch'],
94 | hmPress: [Hammer.Press, 'Hammer.Press'],
95 | hmPressUp: [Hammer.Press, 'Hammer.Press'],
96 | hmRelease: [Hammer.Press, 'Hammer.Press'],
97 | hmRotate: [Hammer.Rotate, 'Hammer.Rotate'],
98 | hmSwipe: [Hammer.Swipe, 'Hammer.Swipe'],
99 | hmSwipeUp: [Hammer.Swipe, 'Hammer.Swipe'],
100 | hmSwipeDown: [Hammer.Swipe, 'Hammer.Swipe'],
101 | hmSwipeLeft: [Hammer.Swipe, 'Hammer.Swipe'],
102 | hmSwipeRight: [Hammer.Swipe, 'Hammer.Swipe'],
103 | hmTap: [Hammer.Tap, 'Hammer.Tap']
104 | };
105 |
106 | var VERBOSE = false;
107 |
108 | angular.forEach(HGESTURES, function(eventName, directiveName) {
109 | angular.module('angular-gestures').directive(directiveName, ['$parse', '$log', '$timeout', 'hammerDefaultOpts', function($parse, $log, $timeout, hammerDefaultOpts) {
110 | return function(scope, element, attr) {
111 | var handler;
112 | attr.$observe(directiveName, function(value) {
113 | var callback = $parse(value);
114 | var opts = $parse(attr[directiveName + 'Opts'])(scope, {});
115 | var defaultOpts = angular.copy(hammerDefaultOpts);
116 |
117 | angular.extend(defaultOpts, opts);
118 |
119 | if (angular.isUndefined(element.hammertime)) {
120 |
121 | // validate that needed recognizer is enabled
122 | var recognizers = angular.isDefined(defaultOpts.recognizers) ? defaultOpts.recognizers : [];
123 | var recognizer = HRECOGNIZERS[directiveName];
124 | if(angular.isDefined(recognizer)) {
125 | var enabled = false;
126 | angular.forEach(recognizers, function(r) {
127 | if (recognizer[0] === r[0]) {
128 | if (angular.isUndefined(r[1].enable) || r[1].enable === true) {
129 | enabled = true;
130 | }
131 | }
132 | });
133 | if (!enabled) {
134 | throw new Error('Directive ' + directiveName + ' requires gesture recognizer [' + recognizer[1] + '] to be enabled');
135 | }
136 | }
137 |
138 | element.hammer = new Hammer.Manager(element[0], defaultOpts);
139 | scope.$on('$destroy', function() {
140 | element.hammer.off(eventName);
141 | element.hammer.destroy();
142 | });
143 | }
144 |
145 | handler = function(event) {
146 | if (VERBOSE) {
147 | $log.debug('angular-gestures: ', eventName, event);
148 | }
149 | var callbackHandler = function () {
150 | var cb = callback(scope, { $event : event});
151 | if (typeof cb === 'function') {
152 | cb.call(scope, event);
153 | }
154 | };
155 |
156 | if (scope.$root.$$phase === '$apply' ||
157 | scope.$root.$$phase === '$digest') {
158 | callbackHandler();
159 | } else {
160 | scope.$apply(callbackHandler);
161 | }
162 |
163 | };
164 | // register actual event
165 | element.hammer.on(eventName, handler);
166 | });
167 | };
168 | }]);
169 | });
170 |
171 | angular.module('angular-gestures').provider('hammerDefaultOpts', function HammerDefaultOptsProvider() {
172 | var opts = {};
173 |
174 | this.set = function(value) {
175 | opts = value;
176 | };
177 |
178 | this.$get = function() {
179 | return opts;
180 | };
181 | });
182 | }));
183 |
--------------------------------------------------------------------------------
/dist/gestures.min.js:
--------------------------------------------------------------------------------
1 | "use strict";!function(a,b){"function"==typeof define&&define.amd?define(["angular","Hammer"],function(a,c){return b({},a,c)}):"object"==typeof exports?module.exports=b({},require("angular"),require("Hammer")):angular&&b(a,a.angular,a.Hammer)}(this,function(a,b,c){b.module("angular-gestures",[]);var d={hmDoubleTap:"doubletap",hmDragstart:"panstart",hmDrag:"pan",hmDragUp:"panup",hmDragDown:"pandown",hmDragLeft:"panleft",hmDragRight:"panright",hmDragend:"panend",hmPanstart:"panstart",hmPan:"pan",hmPanUp:"panup",hmPanDown:"pandown",hmPanLeft:"panleft",hmPanRight:"panright",hmPanend:"panend",hmHold:"press",hmPinch:"pinch",hmPinchstart:"pinchstart",hmPinchend:"pinchend",hmPinchIn:"pinchin",hmPinchOut:"pinchout",hmPress:"press",hmPressUp:"pressup",hmRelease:"pressup",hmRotate:"rotate",hmSwipe:"swipe",hmSwipeUp:"swipeup",hmSwipeDown:"swipedown",hmSwipeLeft:"swipeleft",hmSwipeRight:"swiperight",hmTap:"tap",hmTouch:"touch",hmTransformstart:"transformstart",hmTransform:"transform",hmTransformend:"transformend"},e={hmDoubleTap:[c.Tap,"Hammer.Tap"],hmDragstart:[c.Pan,"Hammer.Pan"],hmDrag:[c.Pan,"Hammer.Pan"],hmDragUp:[c.Pan,"Hammer.Pan"],hmDragDown:[c.Pan,"Hammer.Pan"],hmDragLeft:[c.Pan,"Hammer.Pan"],hmDragRight:[c.Pan,"Hammer.Pan"],hmDragend:[c.Pan,"Hammer.Pan"],hmPanstart:[c.Pan,"Hammer.Pan"],hmPan:[c.Pan,"Hammer.Pan"],hmPanUp:[c.Pan,"Hammer.Pan"],hmPanDown:[c.Pan,"Hammer.Pan"],hmPanLeft:[c.Pan,"Hammer.Pan"],hmPanRight:[c.Pan,"Hammer.Pan"],hmPanend:[c.Pan,"Hammer.Pan"],hmHold:[c.Press,"Hammer.Press"],hmPinch:[c.Pinch,"Hammer.Pinch"],hmPinchstart:[c.Pinch,"Hammer.Pinch"],hmPinchend:[c.Pinch,"Hammer.Pinch"],hmPinchIn:[c.Pinch,"Hammer.Pinch"],hmPinchOut:[c.Pinch,"Hammer.Pinch"],hmPress:[c.Press,"Hammer.Press"],hmPressUp:[c.Press,"Hammer.Press"],hmRelease:[c.Press,"Hammer.Press"],hmRotate:[c.Rotate,"Hammer.Rotate"],hmSwipe:[c.Swipe,"Hammer.Swipe"],hmSwipeUp:[c.Swipe,"Hammer.Swipe"],hmSwipeDown:[c.Swipe,"Hammer.Swipe"],hmSwipeLeft:[c.Swipe,"Hammer.Swipe"],hmSwipeRight:[c.Swipe,"Hammer.Swipe"],hmTap:[c.Tap,"Hammer.Tap"]},f=!1;b.forEach(d,function(a,d){b.module("angular-gestures").directive(d,["$parse","$log","$timeout","hammerDefaultOpts",function(g,h,i,j){return function(i,k,l){var m;l.$observe(d,function(n){var o=g(n),p=g(l[d+"Opts"])(i,{}),q=b.copy(j);if(b.extend(q,p),b.isUndefined(k.hammertime)){var r=b.isDefined(q.recognizers)?q.recognizers:[],s=e[d];if(b.isDefined(s)){var t=!1;if(b.forEach(r,function(a){s[0]===a[0]&&(b.isUndefined(a[1].enable)||a[1].enable===!0)&&(t=!0)}),!t)throw new Error("Directive "+d+" requires gesture recognizer ["+s[1]+"] to be enabled")}k.hammer=new c.Manager(k[0],q),i.$on("$destroy",function(){k.hammer.off(a),k.hammer.destroy()})}m=function(b){f&&h.debug("angular-gestures: ",a,b);var c=function(){var a=o(i,{$event:b});"function"==typeof a&&a.call(i,b)};"$apply"===i.$root.$$phase||"$digest"===i.$root.$$phase?c():i.$apply(c)},k.hammer.on(a,m)})}}])}),b.module("angular-gestures").provider("hammerDefaultOpts",function(){var a={};this.set=function(b){a=b},this.$get=function(){return a}})});
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var karma = require('karma').server;
3 |
4 | /**
5 | * Run test once and exit
6 | */
7 | gulp.task('test', function (done) {
8 | karma.start({
9 | configFile: __dirname + '/karma.conf.js',
10 | singleRun: true
11 | }, done);
12 | });
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Sat Apr 04 2015 10:33:04 GMT+0200 (CEST)
3 |
4 | module.exports = function(config) {
5 | config.set({
6 |
7 | // base path that will be used to resolve all patterns (eg. files, exclude)
8 | basePath: '',
9 |
10 |
11 | // frameworks to use
12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
13 | frameworks: ['jasmine'],
14 |
15 |
16 | // list of files / patterns to load in the browser
17 | files: [
18 | 'components/**/*.min.js', // dependecies
19 | 'components/angular-mocks/angular-mocks.js', // dependecies
20 | 'src/**/*.js',
21 | 'test/**/*.Spec.js'
22 | ],
23 |
24 |
25 | // list of files to exclude
26 | exclude: [
27 | ],
28 |
29 |
30 | // preprocess matching files before serving them to the browser
31 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
32 | preprocessors: {
33 | },
34 |
35 |
36 | // test results reporter to use
37 | // possible values: 'dots', 'progress'
38 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
39 | reporters: ['progress'],
40 |
41 |
42 | // web server port
43 | port: 9876,
44 |
45 |
46 | // enable / disable colors in the output (reporters and logs)
47 | colors: true,
48 |
49 |
50 | // level of logging
51 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
52 | logLevel: config.LOG_INFO,
53 |
54 |
55 | // enable / disable watching file and executing tests whenever any file changes
56 | autoWatch: true,
57 |
58 |
59 | // start these browsers
60 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
61 | browsers: ['Chrome'],
62 |
63 |
64 | // Continuous Integration mode
65 | // if true, Karma captures browsers, runs the tests and exits
66 | singleRun: false
67 | });
68 | };
69 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-gestures",
3 | "version": "0.3.3",
4 | "description": "AngularJS directive that adds support for multi touch gestures to your app. Based on hammer.js.",
5 | "main": "dist/gestures.js",
6 | "scripts": {
7 | "test": "grunt test",
8 | "build": "grunt build"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/wzr1337/angular-gestures.git"
13 | },
14 | "keywords": [
15 | "angularjs",
16 | "hammer.js",
17 | "gestures",
18 | "multitouch"
19 | ],
20 | "author": "wzr1337",
21 | "license": "MIT",
22 | "readmeFilename": "README.md",
23 | "dependencies": {
24 | "angular": ">=1.2.0 <=2.0.0",
25 | "hammerjs": "~2.0.0"
26 | },
27 | "devDependencies": {
28 | "grunt": "~0.4.1",
29 | "grunt-contrib-clean": "~0.4.0",
30 | "grunt-contrib-compress": "~0.4.9",
31 | "grunt-contrib-concat": "~0.1.3",
32 | "grunt-contrib-copy": "~0.4.0",
33 | "grunt-contrib-jshint": "~0.11.2",
34 | "grunt-contrib-uglify": "~0.2.0",
35 | "grunt-contrib-watch": "^0.6.1",
36 | "gulp": "^3.8.11",
37 | "gulp-karma": "0.0.4",
38 | "jasmine-core": "^2.2.0",
39 | "karma": "^0.12.31",
40 | "karma-chrome-launcher": "^0.1.7",
41 | "karma-cli": "0.0.4",
42 | "karma-jasmine": "^0.3.5",
43 | "load-grunt-tasks": "^1.0.0",
44 | "matchdep": "~0.1.1",
45 | "time-grunt": "^1.0.0"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/gestures.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Inspired by AngularJS' implementation of "click dblclick mousedown..."
3 | *
4 | * This ties in the Hammer 2 events to attributes like:
5 | *
6 | * hm-tap="add_something()" hm-swipe="remove_something()"
7 | *
8 | * and also has support for Hammer options with:
9 | *
10 | * hm-tap-opts="{hold: false}"
11 | *
12 | * or any other of the "hm-event" listed underneath.
13 | */
14 |
15 | 'use strict';
16 | (function (root, factory) {
17 | // AMD
18 | if (typeof define === 'function' && define.amd) {
19 | define(['angular', 'Hammer'], function (angular, Hammer) {
20 | return factory({}, angular, Hammer);
21 | });
22 | }
23 | // Node.js
24 | else if (typeof exports === 'object') {
25 | module.exports = factory({}, require('angular'), require('Hammer'));
26 | }
27 | // Angular
28 | else if (angular) {
29 | factory(root, root.angular, root.Hammer);
30 | }
31 | }(this,function(global,angular,Hammer){
32 | angular.module('angular-gestures', []);
33 |
34 | var HGESTURES = {
35 | hmDoubleTap: 'doubletap',
36 | hmDragstart: 'panstart', // will be deprecated soon, use Pan*
37 | hmDrag: 'pan', // will be deprecated soon, use Pan*
38 | hmDragUp: 'panup', // will be deprecated soon, use Pan*
39 | hmDragDown: 'pandown', // will be deprecated soon, use Pan*
40 | hmDragLeft: 'panleft', // will be deprecated soon, use Pan*
41 | hmDragRight: 'panright', // will be deprecated soon, use Pan*
42 | hmDragend: 'panend', // will be deprecated soon, use Pan*
43 | hmPanstart: 'panstart',
44 | hmPan: 'pan',
45 | hmPanUp: 'panup',
46 | hmPanDown: 'pandown',
47 | hmPanLeft: 'panleft',
48 | hmPanRight: 'panright',
49 | hmPanend: 'panend',
50 | hmHold: 'press',
51 | hmPinch: 'pinch',
52 | hmPinchstart: 'pinchstart',
53 | hmPinchend: 'pinchend',
54 | hmPinchIn: 'pinchin',
55 | hmPinchOut: 'pinchout',
56 | hmPress: 'press',
57 | hmPressUp: 'pressup',
58 | hmRelease: 'pressup',
59 | hmRotate: 'rotate',
60 | hmSwipe: 'swipe',
61 | hmSwipeUp: 'swipeup',
62 | hmSwipeDown: 'swipedown',
63 | hmSwipeLeft: 'swipeleft',
64 | hmSwipeRight: 'swiperight',
65 | hmTap: 'tap',
66 | hmTouch: 'touch',
67 | hmTransformstart: 'transformstart',
68 | hmTransform: 'transform',
69 | hmTransformend: 'transformend'
70 | };
71 |
72 | var HRECOGNIZERS = {
73 | hmDoubleTap: [Hammer.Tap, 'Hammer.Tap'],
74 | hmDragstart: [Hammer.Pan, 'Hammer.Pan'],
75 | hmDrag: [Hammer.Pan, 'Hammer.Pan'],
76 | hmDragUp: [Hammer.Pan, 'Hammer.Pan'],
77 | hmDragDown: [Hammer.Pan, 'Hammer.Pan'],
78 | hmDragLeft: [Hammer.Pan, 'Hammer.Pan'],
79 | hmDragRight: [Hammer.Pan, 'Hammer.Pan'],
80 | hmDragend: [Hammer.Pan, 'Hammer.Pan'],
81 | hmPanstart: [Hammer.Pan, 'Hammer.Pan'],
82 | hmPan: [Hammer.Pan, 'Hammer.Pan'],
83 | hmPanUp: [Hammer.Pan, 'Hammer.Pan'],
84 | hmPanDown: [Hammer.Pan, 'Hammer.Pan'],
85 | hmPanLeft: [Hammer.Pan, 'Hammer.Pan'],
86 | hmPanRight: [Hammer.Pan, 'Hammer.Pan'],
87 | hmPanend: [Hammer.Pan, 'Hammer.Pan'],
88 | hmHold: [Hammer.Press, 'Hammer.Press'],
89 | hmPinch: [Hammer.Pinch, 'Hammer.Pinch'],
90 | hmPinchstart: [Hammer.Pinch, 'Hammer.Pinch'],
91 | hmPinchend: [Hammer.Pinch, 'Hammer.Pinch'],
92 | hmPinchIn: [Hammer.Pinch, 'Hammer.Pinch'],
93 | hmPinchOut: [Hammer.Pinch, 'Hammer.Pinch'],
94 | hmPress: [Hammer.Press, 'Hammer.Press'],
95 | hmPressUp: [Hammer.Press, 'Hammer.Press'],
96 | hmRelease: [Hammer.Press, 'Hammer.Press'],
97 | hmRotate: [Hammer.Rotate, 'Hammer.Rotate'],
98 | hmSwipe: [Hammer.Swipe, 'Hammer.Swipe'],
99 | hmSwipeUp: [Hammer.Swipe, 'Hammer.Swipe'],
100 | hmSwipeDown: [Hammer.Swipe, 'Hammer.Swipe'],
101 | hmSwipeLeft: [Hammer.Swipe, 'Hammer.Swipe'],
102 | hmSwipeRight: [Hammer.Swipe, 'Hammer.Swipe'],
103 | hmTap: [Hammer.Tap, 'Hammer.Tap']
104 | };
105 |
106 | var VERBOSE = false;
107 |
108 | angular.forEach(HGESTURES, function(eventName, directiveName) {
109 | angular.module('angular-gestures').directive(directiveName, ['$parse', '$log', '$timeout', 'hammerDefaultOpts', function($parse, $log, $timeout, hammerDefaultOpts) {
110 | return function(scope, element, attr) {
111 | var handler;
112 | attr.$observe(directiveName, function(value) {
113 | var callback = $parse(value);
114 | var opts = $parse(attr[directiveName + 'Opts'])(scope, {});
115 | var defaultOpts = angular.copy(hammerDefaultOpts);
116 |
117 | angular.extend(defaultOpts, opts);
118 |
119 | if (angular.isUndefined(element.hammertime)) {
120 |
121 | // validate that needed recognizer is enabled
122 | var recognizers = angular.isDefined(defaultOpts.recognizers) ? defaultOpts.recognizers : [];
123 | var recognizer = HRECOGNIZERS[directiveName];
124 | if(angular.isDefined(recognizer)) {
125 | var enabled = false;
126 | angular.forEach(recognizers, function(r) {
127 | if (recognizer[0] === r[0]) {
128 | if (angular.isUndefined(r[1].enable) || r[1].enable === true) {
129 | enabled = true;
130 | }
131 | }
132 | });
133 | if (!enabled) {
134 | throw new Error('Directive ' + directiveName + ' requires gesture recognizer [' + recognizer[1] + '] to be enabled');
135 | }
136 | }
137 |
138 | element.hammer = new Hammer.Manager(element[0], defaultOpts);
139 | scope.$on('$destroy', function() {
140 | element.hammer.off(eventName);
141 | element.hammer.destroy();
142 | });
143 | }
144 |
145 | handler = function(event) {
146 | if (VERBOSE) {
147 | $log.debug('angular-gestures: ', eventName, event);
148 | }
149 | var callbackHandler = function () {
150 | var cb = callback(scope, { $event : event});
151 | if (typeof cb === 'function') {
152 | cb.call(scope, event);
153 | }
154 | };
155 |
156 | if (scope.$root.$$phase === '$apply' ||
157 | scope.$root.$$phase === '$digest') {
158 | callbackHandler();
159 | } else {
160 | scope.$apply(callbackHandler);
161 | }
162 |
163 | };
164 | // register actual event
165 | element.hammer.on(eventName, handler);
166 | });
167 | };
168 | }]);
169 | });
170 |
171 | angular.module('angular-gestures').provider('hammerDefaultOpts', function HammerDefaultOptsProvider() {
172 | var opts = {};
173 |
174 | this.set = function(value) {
175 | opts = value;
176 | };
177 |
178 | this.$get = function() {
179 | return opts;
180 | };
181 | });
182 | }));
183 |
--------------------------------------------------------------------------------
/test/gestures.Spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('A suite', function() {
4 | it('contains spec with an expectation', function() {
5 | expect(true).toBe(true);
6 | });
7 | });
8 |
9 | describe('Gesture recognizers validation', function() {
10 |
11 | var $compile,
12 | $rootScope;
13 |
14 | beforeEach(module('angular-gestures', function(hammerDefaultOptsProvider) {
15 | hammerDefaultOptsProvider.set({
16 | recognizers: [
17 | [Hammer.Tap, {}],
18 | [Hammer.Pinch, {
19 | enable: false
20 | }],
21 | [Hammer.Rotate, {
22 | enable: true
23 | }],
24 | ]
25 | });
26 | }));
27 |
28 | beforeEach(inject(function(_$compile_, _$rootScope_) {
29 | $compile = _$compile_;
30 | $rootScope = _$rootScope_;
31 | }));
32 |
33 | it('should throw if no swipe recognizer is not configured and hmSwipe directive is used', function() {
34 | expect(function() {
35 | var element = $compile("")($rootScope);
36 | $rootScope.$digest();
37 | }).toThrow(new Error('Directive hmSwipe requires gesture recognizer [Hammer.Swipe] to be enabled'));
38 | });
39 |
40 | it('should not throw if tap recognizer is configured and hmTap directive is used', function() {
41 | var element = $compile("")($rootScope);
42 | $rootScope.$digest();
43 | });
44 |
45 | it('should throw if pinch recognizer is configured but disabled and hmPinch directive is used', function() {
46 | expect(function() {
47 | var element = $compile("")($rootScope);
48 | $rootScope.$digest();
49 | }).toThrow(new Error('Directive hmPinch requires gesture recognizer [Hammer.Pinch] to be enabled'));
50 | });
51 |
52 | it('should not throw if rotate recognizer is configured and explicitly enabled and hmRotate directive is used', function() {
53 | var element = $compile('')($rootScope);
54 | $rootScope.$digest();
55 | });
56 |
57 | it('should not throw if hmTouch directive is used (no recognizer needed)', function() {
58 | var element = $compile('')($rootScope);
59 | $rootScope.$digest();
60 | });
61 | });
--------------------------------------------------------------------------------