hammerjs-rails 2.0.6 → 2.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1d0f6a1f09711b6a340416c8ceba8dcb6e719272
4
- data.tar.gz: 985935288d286be2d13ab6401b333e0cfacb0ea8
3
+ metadata.gz: 60cbf689d427f28c8443a8488749f0073bcdc507
4
+ data.tar.gz: e4a42dbacaff263b422bf89e985b4365df6d6869
5
5
  SHA512:
6
- metadata.gz: d09d5dfcc07a0958cc7cc25e99416da0c6cd82686b0fd653111877d30ccc524fc530d1c9d0d0ccf7a627c8f204337a94c8e1771b04f21dff4709abf5ca7bb83f
7
- data.tar.gz: 94445121e4c04b33bd1cb541719df0c0e6bbac663b511f3e8b9aa3fb5843ae58f7a8fe01510e6cb87453aef94bc92c5747be62749e4861c46e03aabd2c66f430
6
+ metadata.gz: 70cdbc14231f2dd0e8ea7942c3e15d126755bd3ca17643c71dbcd87e6a6565e8f8c0fe7f121fb83387ac6f43d88167511cf7078801fe3233328c475d737d2930
7
+ data.tar.gz: cab5dc96e5d77b5cd34fb4f321354716ef8f93f384c2cd3bc15b49243286131138967f3a1d68e8977005943b694b2beb29c4fb0da9befe1d47d75b3658336e63
data/README.md CHANGED
@@ -1,7 +1,17 @@
1
1
  hammerjs-rails
2
2
  ==============
3
3
 
4
- [Hammer.js](http://eightmedia.github.io/hammer.js/) packaged for Rails assets pipeline
4
+ [Hammer.js](http://hammerjs.github.io/) packaged for Rails assets pipeline
5
+
6
+ ## Includes
7
+
8
+ - [Hammer.js](https://github.com/hammerjs/hammer.js/tree/master/)
9
+ - [Hammer.js jQuery Extension](http://hammerjs.github.io/jquery-plugin/)
10
+ - [Hammer.js Angular Extension](http://ryanmullins.github.io/angular-hammer/)
11
+ - [HammerTime](https://github.com/hammerjs/hammer-time)
12
+ - [TouchEmulator](https://github.com/hammerjs/touchemulator)
13
+
14
+ Plus source map files for Hammer.js & the Angular extension.
5
15
 
6
16
  ## Usage
7
17
 
@@ -11,10 +21,21 @@ Add this line to your application's Gemfile:
11
21
  gem 'hammerjs-rails'
12
22
  ```
13
23
 
14
- Add update your Javascript manifest file (application.js):
24
+ And choose what you'd like to add to your JavaScript manifest file (application.js):
15
25
 
16
26
  ```js
17
27
  //= require hammer
28
+ //= require hammer.min
29
+
30
+ //= require jquery.hammer
31
+
32
+ //= require angular.hammer
33
+ //= require angular.hammer.min
34
+
35
+ //= require hammer-time
36
+ //= require hammer-time.min
37
+
38
+ //= require touch-emulator
18
39
  ```
19
40
 
20
41
  ## Contributing
@@ -23,4 +44,4 @@ Add update your Javascript manifest file (application.js):
23
44
  2. Create your feature branch (`git checkout -b my-new-feature`)
24
45
  3. Commit your changes (`git commit -am 'Add some feature'`)
25
46
  4. Push to the branch (`git push origin my-new-feature`)
26
- 5. Create new Pull Request
47
+ 5. Create new Pull Request
@@ -1,5 +1,5 @@
1
1
  module Hammerjs
2
2
  module Rails
3
- VERSION = '2.0.6'
3
+ VERSION = '2.0.8'
4
4
  end
5
5
  end
@@ -0,0 +1,611 @@
1
+ // ---- Angular Hammer ----
2
+
3
+ // Copyright (c) 2015 Ryan S Mullins <ryan@ryanmullins.org>
4
+ // Licensed under the MIT Software License
5
+ //
6
+ // (fairly heavy) modifications by James Wilson <me@unbui.lt>
7
+ //
8
+
9
+ (function (angular, Hammer) {
10
+ 'use strict';
11
+
12
+ // Checking to make sure Hammer and Angular are defined
13
+
14
+ if (typeof angular === 'undefined') {
15
+ throw Error("angular-hammer: AngularJS (angular) is undefined but is necessary.");
16
+ }
17
+ if (typeof Hammer === 'undefined') {
18
+ throw Error("angular-hammer: HammerJS (Hammer) is undefined but is necessary.");
19
+ }
20
+
21
+ /**
22
+ * Mapping of the gesture event names with the Angular attribute directive
23
+ * names. Follows the form: <directiveName>:<eventName>.
24
+ *
25
+ * @type {Array}
26
+ */
27
+ var gestureTypes = [
28
+ 'hmCustom:custom',
29
+ 'hmSwipe:swipe',
30
+ 'hmSwipeleft:swipeleft',
31
+ 'hmSwiperight:swiperight',
32
+ 'hmSwipeup:swipeup',
33
+ 'hmSwipedown:swipedown',
34
+ 'hmPan:pan',
35
+ 'hmPanstart:panstart',
36
+ 'hmPanmove:panmove',
37
+ 'hmPanend:panend',
38
+ 'hmPancancel:pancancel',
39
+ 'hmPanleft:panleft',
40
+ 'hmPanright:panright',
41
+ 'hmPanup:panup',
42
+ 'hmPandown:pandown',
43
+ 'hmPress:press',
44
+ 'hmPressup:pressup',
45
+ 'hmRotate:rotate',
46
+ 'hmRotatestart:rotatestart',
47
+ 'hmRotatemove:rotatemove',
48
+ 'hmRotateend:rotateend',
49
+ 'hmRotatecancel:rotatecancel',
50
+ 'hmPinch:pinch',
51
+ 'hmPinchstart:pinchstart',
52
+ 'hmPinchmove:pinchmove',
53
+ 'hmPinchend:pinchend',
54
+ 'hmPinchcancel:pinchcancel',
55
+ 'hmPinchin:pinchin',
56
+ 'hmPinchout:pinchout',
57
+ 'hmTap:tap',
58
+ 'hmDoubletap:doubletap'
59
+ ];
60
+
61
+ // ---- Module Definition ----
62
+
63
+ /**
64
+ * @module hmTouchEvents
65
+ * @description Angular.js module for adding Hammer.js event listeners to HTML
66
+ * elements using attribute directives
67
+ * @requires angular
68
+ * @requires hammer
69
+ */
70
+ var NAME = 'hmTouchEvents';
71
+ var hmTouchEvents = angular.module('hmTouchEvents', []);
72
+
73
+ /**
74
+ * Provides a common interface for configuring global manager and recognizer
75
+ * options. Allows things like tap duration etc to be defaulted globally and
76
+ * overridden on a per-directive basis as needed.
77
+ *
78
+ * @return {Object} functions to add manager and recognizer options.
79
+ */
80
+ hmTouchEvents.provider(NAME, function(){
81
+
82
+ var self = this;
83
+ var defaultRecognizerOpts = false;
84
+ var recognizerOptsHash = {};
85
+ var managerOpts = {};
86
+
87
+ //
88
+ // In order to use the Hamme rpresets provided, we need
89
+ // to map the recognizer fn to some name:
90
+ //
91
+ var recognizerFnToName = {};
92
+ recognizerFnToName[ Hammer.Tap.toString() ] = "tap";
93
+ recognizerFnToName[ Hammer.Pan.toString() ] = "pan";
94
+ recognizerFnToName[ Hammer.Pinch.toString() ] = "pinch";
95
+ recognizerFnToName[ Hammer.Press.toString() ] = "press";
96
+ recognizerFnToName[ Hammer.Rotate.toString() ] = "rotate";
97
+ recognizerFnToName[ Hammer.Swipe.toString() ] = "swipe";
98
+
99
+ //
100
+ // normalize opts, setting its name as it should be keyed by
101
+ // and any must-have options. currently only doubletap is treated
102
+ // specially. each _name leads to a new recognizer.
103
+ //
104
+ function normalizeRecognizerOptions(opts){
105
+ opts = angular.copy(opts);
106
+
107
+ if(opts.event){
108
+
109
+ if(opts.event == "doubletap"){
110
+ opts.type = "tap";
111
+ if(!opts.taps) opts.taps = 2;
112
+ opts._name = "doubletap";
113
+ } else {
114
+ opts._name = false;
115
+ }
116
+
117
+ } else {
118
+ opts._name = opts.type || false;
119
+ }
120
+
121
+ return opts;
122
+ }
123
+ //
124
+ // create default opts for some eventName.
125
+ // again, treat doubletap specially.
126
+ //
127
+ function defaultOptionsForEvent(eventName){
128
+ if(eventName == "custom"){
129
+ throw Error(NAME+"Provider: no defaults exist for custom events");
130
+ }
131
+ var ty = getRecognizerTypeFromeventName(eventName);
132
+ return normalizeRecognizerOptions(
133
+ eventName == "doubletap"
134
+ ? {type:ty, event:"doubletap"}
135
+ : {type:ty}
136
+ );
137
+ }
138
+
139
+ //
140
+ // Make use of presets from Hammer.defaults.preset array
141
+ // in angular-hammer events.
142
+ //
143
+ self.applyHammerPresets = function(){
144
+ var hammerPresets = Hammer.defaults.preset;
145
+
146
+ //add every preset that, when normalized, has a _name.
147
+ //this precludes most custom events.
148
+ angular.forEach(hammerPresets, function(presetArr){
149
+
150
+ var data = presetArr[1];
151
+ if(!data.type) data.type = recognizerFnToName[presetArr[0]];
152
+ data = normalizeRecognizerOptions(data);
153
+ if(!data._name) return;
154
+ recognizerOptsHash[data._name] = data;
155
+ });
156
+ }
157
+
158
+ //
159
+ // Add a manager option (key/val to extend or object to set all):
160
+ //
161
+ self.addManagerOption = function(name, val){
162
+ if(typeof name == "object"){
163
+ angular.extend(managerOpts, name);
164
+ }
165
+ else {
166
+ managerOpts[name] = val;
167
+ }
168
+ }
169
+
170
+ //
171
+ // Add a recognizer option:
172
+ //
173
+ self.addRecognizerOption = function(val){
174
+ if(Array.isArray(val)){
175
+ for(var i = 0; i < val.length; i++) self.addRecognizerOption(val[i]);
176
+ return;
177
+ }
178
+ if(typeof val !== "object"){
179
+ throw Error(NAME+"Provider: addRecognizerOption: should be object or array of objects");
180
+ }
181
+ val = normalizeRecognizerOptions(val);
182
+
183
+ //hash by name if present, else if no event name,
184
+ //set as defaults.
185
+ if(val._name){
186
+ recognizerOptsHash[val.type] = val;
187
+ } else if(!val.event){
188
+ defaultRecognizerOpts = val;
189
+ }
190
+
191
+ }
192
+
193
+ //provide an interface to this that the hm-* directives use
194
+ //to extend their recognizer/manager opts.
195
+ self.$get = function(){
196
+ return {
197
+ extendWithDefaultManagerOpts: function(opts){
198
+ if(typeof opts != "object"){
199
+ opts = {};
200
+ } else {
201
+ opts = angular.copy(opts);
202
+ }
203
+ for(var name in managerOpts) {
204
+ if(!opts[name]) opts[name] = angular.copy(managerOpts[name]);
205
+ }
206
+ return opts;
207
+ },
208
+ extendWithDefaultRecognizerOpts: function(eventName, opts){
209
+ if(typeof opts !== "object"){
210
+ opts = [];
211
+ }
212
+ if(!Array.isArray(opts)){
213
+ opts = [opts];
214
+ }
215
+
216
+ //dont apply anything if this is custom event
217
+ //(beyond normalizing opts to an array):
218
+ if(eventName == "custom") return opts;
219
+
220
+ var recognizerType = getRecognizerTypeFromeventName(eventName);
221
+ var specificOpts = recognizerOptsHash[eventName] || recognizerOptsHash[recognizerType];
222
+
223
+ //get the last opt provided that matches the type or eventName
224
+ //that we have. normalizing removes any eventnames we dont care about
225
+ //(everything but doubletap at the moment).
226
+ var foundOpt;
227
+ var isExactMatch = false;
228
+ var defaults = angular.extend({}, defaultRecognizerOpts || {}, specificOpts || {});
229
+ opts.forEach(function(opt){
230
+
231
+ if(!opt.event && !opt.type){
232
+ return angular.extend(defaults, opt);
233
+ }
234
+ if(isExactMatch){
235
+ return;
236
+ }
237
+
238
+ //more specific wins over less specific.
239
+ if(opt.event == eventName){
240
+ foundOpt = opt;
241
+ isExactMatch = true;
242
+ } else if(!opt.event && opt.type == recognizerType){
243
+ foundOpt = opt;
244
+ }
245
+
246
+ });
247
+ if(!foundOpt) foundOpt = defaultOptionsForEvent(eventName);
248
+ else foundOpt = normalizeRecognizerOptions(foundOpt);
249
+
250
+
251
+ return [angular.extend(defaults, foundOpt)];
252
+ }
253
+ };
254
+ };
255
+
256
+ });
257
+
258
+ /**
259
+ * Iterates through each gesture type mapping and creates a directive for
260
+ * each of the
261
+ *
262
+ * @param {String} type Mapping in the form of <directiveName>:<eventName>
263
+ * @return None
264
+ */
265
+ angular.forEach(gestureTypes, function (type) {
266
+ var directive = type.split(':'),
267
+ directiveName = directive[0],
268
+ eventName = directive[1];
269
+
270
+ hmTouchEvents.directive(directiveName, ['$parse', '$window', NAME, function ($parse, $window, defaultEvents) {
271
+ return {
272
+ restrict: 'A',
273
+ scope: false,
274
+ link: function (scope, element, attrs) {
275
+
276
+ // Check for Hammer and required functionality.
277
+ // error if they arent found as unexpected behaviour otherwise
278
+ if (!Hammer || !$window.addEventListener) {
279
+ throw Error(NAME+": window.Hammer or window.addEventListener not found, can't add event "+directiveName);
280
+ }
281
+
282
+ var hammer = element.data('hammer'),
283
+ managerOpts = defaultEvents.extendWithDefaultManagerOpts( scope.$eval(attrs.hmManagerOptions) ),
284
+ recognizerOpts = defaultEvents.extendWithDefaultRecognizerOpts( eventName, scope.$eval(attrs.hmRecognizerOptions) );
285
+
286
+ // Check for a manager, make one if needed and destroy it when
287
+ // the scope is destroyed
288
+ if (!hammer) {
289
+ hammer = new Hammer.Manager(element[0], managerOpts);
290
+ element.data('hammer', hammer);
291
+ scope.$on('$destroy', function () {
292
+ hammer.destroy();
293
+ });
294
+ }
295
+
296
+ // Obtain and wrap our handler function to do a couple of bits for
297
+ // us if options provided.
298
+ var handlerExpr = $parse(attrs[directiveName]).bind(null,scope);
299
+ var handler = function (event) {
300
+ event.element = element;
301
+
302
+ // Default invokeApply to true, overridden by recognizer option
303
+ var invokeApply = true;
304
+
305
+ var recognizer = hammer.get(getRecognizerTypeFromeventName(event.type));
306
+ if (recognizer) {
307
+ var opts = recognizer.options;
308
+ if (opts.preventDefault) {
309
+ event.preventDefault();
310
+ }
311
+ if (opts.stopPropagation) {
312
+ event.srcEvent.stopPropagation();
313
+ }
314
+
315
+ invokeApply = angular.isUndefined(opts.invokeApply) || opts.invokeApply;
316
+ }
317
+
318
+ if (invokeApply) {
319
+ scope.$apply(function(){
320
+ handlerExpr({ '$event': event });
321
+ });
322
+ } else {
323
+ handlerExpr({ '$event': event });
324
+ }
325
+ };
326
+
327
+ // The recognizer options are normalized to an array. This array
328
+ // contains whatever events we wish to add (our prior extending
329
+ // takes care of that), but we do a couple of specific things
330
+ // depending on this directive so that events play nice together.
331
+ angular.forEach(recognizerOpts, function (options) {
332
+
333
+ if(eventName !== 'custom'){
334
+
335
+ if (eventName === 'doubletap' && hammer.get('tap')) {
336
+ options.recognizeWith = 'tap';
337
+ }
338
+ else if (options.type == "pan" && hammer.get('swipe')) {
339
+ options.recognizeWith = 'swipe';
340
+ }
341
+ else if (options.type == "pinch" && hammer.get('rotate')) {
342
+ options.recognizeWith = 'rotate';
343
+ }
344
+
345
+ }
346
+
347
+ //add the recognizer with these options:
348
+ setupRecognizerWithOptions(
349
+ hammer,
350
+ applyManagerOptions(managerOpts, options),
351
+ element
352
+ );
353
+
354
+ //if custom there may be multiple events to apply, which
355
+ //we do here. else, we'll only ever add one.
356
+ hammer.on(eventName, handler);
357
+
358
+ });
359
+
360
+ }
361
+ };
362
+ }]);
363
+ });
364
+
365
+ // ---- Private Functions -----
366
+
367
+ /**
368
+ * Adds a gesture recognizer to a given manager. The type of recognizer to
369
+ * add is determined by the value of the options.type property.
370
+ *
371
+ * @param {Object} manager Hammer.js manager object assigned to an element
372
+ * @param {String} type Options that define the recognizer to add
373
+ * @return {Object} Reference to the new gesture recognizer, if
374
+ * successful, null otherwise.
375
+ */
376
+ function addRecognizer (manager, name) {
377
+ if (manager === undefined || name === undefined) { return null; }
378
+
379
+ var recognizer;
380
+
381
+ if (name.indexOf('pan') > -1) {
382
+ recognizer = new Hammer.Pan();
383
+ } else if (name.indexOf('pinch') > -1) {
384
+ recognizer = new Hammer.Pinch();
385
+ } else if (name.indexOf('press') > -1) {
386
+ recognizer = new Hammer.Press();
387
+ } else if (name.indexOf('rotate') > -1) {
388
+ recognizer = new Hammer.Rotate();
389
+ } else if (name.indexOf('swipe') > -1) {
390
+ recognizer = new Hammer.Swipe();
391
+ } else {
392
+ recognizer = new Hammer.Tap();
393
+ }
394
+
395
+ manager.add(recognizer);
396
+ return recognizer;
397
+ }
398
+
399
+ /**
400
+ * Applies certain manager options to individual recognizer options.
401
+ *
402
+ * @param {Object} managerOpts Manager options
403
+ * @param {Object} recognizerOpts Recognizer options
404
+ * @return None
405
+ */
406
+ function applyManagerOptions (managerOpts, recognizerOpts) {
407
+ if (managerOpts) {
408
+ recognizerOpts.preventGhosts = managerOpts.preventGhosts;
409
+ }
410
+
411
+ return recognizerOpts;
412
+ }
413
+
414
+ /**
415
+ * Extracts the type of recognizer that should be instantiated from a given
416
+ * event name. Used only when no recognizer options are provided.
417
+ *
418
+ * @param {String} eventName Name to derive the recognizer type from
419
+ * @return {string} Type of recognizer that fires events with that name
420
+ */
421
+ function getRecognizerTypeFromeventName (eventName) {
422
+ if (eventName.indexOf('pan') > -1) {
423
+ return 'pan';
424
+ } else if (eventName.indexOf('pinch') > -1) {
425
+ return 'pinch';
426
+ } else if (eventName.indexOf('press') > -1) {
427
+ return 'press';
428
+ } else if (eventName.indexOf('rotate') > -1) {
429
+ return 'rotate';
430
+ } else if (eventName.indexOf('swipe') > -1) {
431
+ return 'swipe';
432
+ } else if (eventName.indexOf('tap') > -1) {
433
+ return 'tap';
434
+ } else {
435
+ return "custom";
436
+ }
437
+ }
438
+
439
+ /**
440
+ * Applies the passed options object to the appropriate gesture recognizer.
441
+ * Recognizers are created if they do not already exist. See the README for a
442
+ * description of the options object that can be passed to this function.
443
+ *
444
+ * @param {Object} manager Hammer.js manager object assigned to an element
445
+ * @param {Object} options Options applied to a recognizer managed by manager
446
+ * @return None
447
+ */
448
+ function setupRecognizerWithOptions (manager, options, element) {
449
+ if (manager == null || options == null || options.type == null) {
450
+ return console.error('ERROR: Angular Hammer could not setup the' +
451
+ ' recognizer. Values of the passed manager and options: ', manager, options);
452
+ }
453
+
454
+ var recognizer = manager.get(options._name);
455
+ if (!recognizer) {
456
+ recognizer = addRecognizer(manager, options._name);
457
+ }
458
+
459
+ if (!options.directions) {
460
+ if (options._name === 'pan' || options._name === 'swipe') {
461
+ options.directions = 'DIRECTION_ALL';
462
+ } else if (options._name.indexOf('left') > -1) {
463
+ options.directions = 'DIRECTION_LEFT';
464
+ } else if (options._name.indexOf('right') > -1) {
465
+ options.directions = 'DIRECTION_RIGHT';
466
+ } else if (options._name.indexOf('up') > -1) {
467
+ options.directions = 'DIRECTION_UP';
468
+ } else if (options._name.indexOf('down') > -1) {
469
+ options.directions = 'DIRECTION_DOWN';
470
+ } else {
471
+ options.directions = '';
472
+ }
473
+ }
474
+
475
+ options.direction = parseDirections(options.directions);
476
+ recognizer.set(options);
477
+
478
+ if (typeof options.recognizeWith === 'string') {
479
+ var recognizeWithRecognizer;
480
+
481
+ if (manager.get(options.recognizeWith) == null){
482
+ recognizeWithRecognizer = addRecognizer(manager, options.recognizeWith);
483
+ }
484
+
485
+ if (recognizeWithRecognizer != null) {
486
+ recognizer.recognizeWith(recognizeWithRecognizer);
487
+ }
488
+ }
489
+
490
+ if (typeof options.dropRecognizeWith === 'string' &&
491
+ manager.get(options.dropRecognizeWith) != null) {
492
+ recognizer.dropRecognizeWith(manager.get(options.dropRecognizeWith));
493
+ }
494
+
495
+ if (typeof options.requireFailure === 'string') {
496
+ var requireFailureRecognizer;
497
+
498
+ if (manager.get(options.requireFailure) == null){
499
+ requireFailureRecognizer = addRecognizer(manager, {type:options.requireFailure});
500
+ }
501
+
502
+ if (requireFailureRecognizer != null) {
503
+ recognizer.requireFailure(requireFailureRecognizer);
504
+ }
505
+ }
506
+
507
+ if (typeof options.dropRequireFailure === 'string' &&
508
+ manager.get(options.dropRequireFailure) != null) {
509
+ recognizer.dropRequireFailure(manager.get(options.dropRequireFailure));
510
+ }
511
+
512
+ if (options.preventGhosts === true && element != null) {
513
+ preventGhosts(element);
514
+ }
515
+ }
516
+
517
+ /**
518
+ * Parses the value of the directions property of any Angular Hammer options
519
+ * object and converts them into the standard Hammer.js directions values.
520
+ *
521
+ * @param {String} dirs Direction names separated by '|' characters
522
+ * @return {Number} Hammer.js direction value
523
+ */
524
+ function parseDirections (dirs) {
525
+ var directions = 0;
526
+
527
+ angular.forEach(dirs.split('|'), function (direction) {
528
+ if (Hammer.hasOwnProperty(direction)) {
529
+ directions = directions | Hammer[direction];
530
+ }
531
+ });
532
+
533
+ return directions;
534
+ }
535
+
536
+ // ---- Preventing Ghost Clicks ----
537
+
538
+ /**
539
+ * Modified from: https://gist.github.com/jtangelder/361052976f044200ea17
540
+ *
541
+ * Prevent click events after a touchend.
542
+ *
543
+ * Inspired/copy-paste from this article of Google by Ryan Fioravanti
544
+ * https://developers.google.com/mobile/articles/fast_buttons#ghost
545
+ */
546
+
547
+ function preventGhosts (element) {
548
+ if (!element) { return; }
549
+
550
+ var coordinates = [],
551
+ threshold = 25,
552
+ timeout = 2500;
553
+
554
+ if ('ontouchstart' in window) {
555
+ element[0].addEventListener('touchstart', resetCoordinates, true);
556
+ element[0].addEventListener('touchend', registerCoordinates, true);
557
+ element[0].addEventListener('click', preventGhostClick, true);
558
+ element[0].addEventListener('mouseup', preventGhostClick, true);
559
+ }
560
+
561
+ /**
562
+ * prevent clicks if they're in a registered XY region
563
+ * @param {MouseEvent} ev
564
+ */
565
+ function preventGhostClick (ev) {
566
+ for (var i = 0; i < coordinates.length; i++) {
567
+ var x = coordinates[i][0];
568
+ var y = coordinates[i][1];
569
+
570
+ // within the range, so prevent the click
571
+ if (Math.abs(ev.clientX - x) < threshold &&
572
+ Math.abs(ev.clientY - y) < threshold) {
573
+ ev.stopPropagation();
574
+ ev.preventDefault();
575
+ break;
576
+ }
577
+ }
578
+ }
579
+
580
+ /**
581
+ * reset the coordinates array
582
+ */
583
+ function resetCoordinates () {
584
+ coordinates = [];
585
+ }
586
+
587
+ /**
588
+ * remove the first coordinates set from the array
589
+ */
590
+ function popCoordinates () {
591
+ coordinates.splice(0, 1);
592
+ }
593
+
594
+ /**
595
+ * if it is an final touchend, we want to register it's place
596
+ * @param {TouchEvent} ev
597
+ */
598
+ function registerCoordinates (ev) {
599
+ // touchend is triggered on every releasing finger
600
+ // changed touches always contain the removed touches on a touchend
601
+ // the touches object might contain these also at some browsers (firefox os)
602
+ // so touches - changedTouches will be 0 or lower, like -1, on the final touchend
603
+ if(ev.touches.length - ev.changedTouches.length <= 0) {
604
+ var touch = ev.changedTouches[0];
605
+ coordinates.push([touch.clientX, touch.clientY]);
606
+
607
+ setTimeout(popCoordinates, timeout);
608
+ }
609
+ }
610
+ }
611
+ })(angular, Hammer);