hammerjs-rails 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: eda8b8f0cddf3bdeddfbacffae50d9bf8a8bc875
4
+ data.tar.gz: 5eaf7db8bb193bc6fae659ddf5a78feff9dad17d
5
+ SHA512:
6
+ metadata.gz: 65e5c9b3745e19dffda7cf084f2be41d98916dc08a652c3f9ec4ea4310f32cfc538c4a10f9c5ce240565c340c1bb4cb4c4b850ff36f8fd1a4711a607d6b8537c
7
+ data.tar.gz: 119fb64dcabe4e9e0c3d096e7b880e22ef6915dcd3926cb471330fefabe330bb8bd7f2b898b95bbc6d90f51bbacd98c7649f2123849e0268a430aae2f95c8552
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in graphaeljs-rails.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,26 @@
1
+ hammerjs-rails
2
+ ==============
3
+
4
+ [Hammer.js](http://eightmedia.github.io/hammer.js/) packaged for Rails assets pipeline
5
+
6
+ ## Usage
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'hammerjs-rails'
12
+ ```
13
+
14
+ Add one of the following directive to your Javascript manifest file (application.js):
15
+
16
+ ```js
17
+ //= require hammerjs
18
+ ```
19
+
20
+ ## Contributing
21
+
22
+ 1. Fork it
23
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
24
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
25
+ 4. Push to the branch (`git push origin my-new-feature`)
26
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ desc "Fetch file from https://raw.github.com/EightMedia/hammer.js/master/hammer.js"
4
+ task :fetch do
5
+ source = "https://raw.github.com/EightMedia/hammer.js/master/hammer.js"
6
+ target = "vendor/assets/javascripts/hammer.js"
7
+ sh "curl #{source} > #{target}"
8
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'hammerjs/rails/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "hammerjs-rails"
8
+ spec.version = Hammerjs::Rails::VERSION
9
+ spec.authors = ["Vincent Pochet"]
10
+ spec.email = ["vincent.pochet@gmail.com"]
11
+ spec.description = %q{hammerjs packaged for Rails assets pipeline}
12
+ spec.summary = %q{hammerjs packaged for Rails assets pipeline}
13
+ spec.homepage = "https://github.com/vincent-pochet/hammerjs-rails"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ end
@@ -0,0 +1,6 @@
1
+ require "hammerjs/rails/version"
2
+
3
+ module Hammerjs::Rails
4
+ class Engine < ::Rails::Engine
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Hammerjs
2
+ module Rails
3
+ VERSION = '0.1.0'
4
+ end
5
+ end
@@ -0,0 +1,1356 @@
1
+ /*! Hammer.JS - v1.0.6dev - 2013-11-03
2
+ * http://eightmedia.github.com/hammer.js
3
+ *
4
+ * Copyright (c) 2013 Jorik Tangelder <j.tangelder@gmail.com>;
5
+ * Licensed under the MIT license */
6
+
7
+ (function(window, undefined) {
8
+ 'use strict';
9
+
10
+ /**
11
+ * Hammer
12
+ * use this to create instances
13
+ * @param {HTMLElement} element
14
+ * @param {Object} options
15
+ * @returns {Hammer.Instance}
16
+ * @constructor
17
+ */
18
+ var Hammer = function(element, options) {
19
+ return new Hammer.Instance(element, options || {});
20
+ };
21
+
22
+ // default settings
23
+ Hammer.defaults = {
24
+ // add styles and attributes to the element to prevent the browser from doing
25
+ // its native behavior. this doesnt prevent the scrolling, but cancels
26
+ // the contextmenu, tap highlighting etc
27
+ // set to false to disable this
28
+ stop_browser_behavior: {
29
+ // this also triggers onselectstart=false for IE
30
+ userSelect : 'none',
31
+ // this makes the element blocking in IE10 >, you could experiment with the value
32
+ // see for more options this issue; https://github.com/EightMedia/hammer.js/issues/241
33
+ touchAction : 'none',
34
+ touchCallout : 'none',
35
+ contentZooming : 'none',
36
+ userDrag : 'none',
37
+ tapHighlightColor: 'rgba(0,0,0,0)'
38
+ }
39
+
40
+ //
41
+ // more settings are defined per gesture at gestures.js
42
+ //
43
+ };
44
+
45
+ // detect touchevents
46
+ Hammer.HAS_POINTEREVENTS = window.navigator.pointerEnabled || window.navigator.msPointerEnabled;
47
+ Hammer.HAS_TOUCHEVENTS = ('ontouchstart' in window);
48
+
49
+ // dont use mouseevents on mobile devices
50
+ Hammer.MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android|silk/i;
51
+ Hammer.NO_MOUSEEVENTS = Hammer.HAS_TOUCHEVENTS && window.navigator.userAgent.match(Hammer.MOBILE_REGEX);
52
+
53
+ // eventtypes per touchevent (start, move, end)
54
+ // are filled by Hammer.event.determineEventTypes on setup
55
+ Hammer.EVENT_TYPES = {};
56
+
57
+ // direction defines
58
+ Hammer.DIRECTION_DOWN = 'down';
59
+ Hammer.DIRECTION_LEFT = 'left';
60
+ Hammer.DIRECTION_UP = 'up';
61
+ Hammer.DIRECTION_RIGHT = 'right';
62
+
63
+ // pointer type
64
+ Hammer.POINTER_MOUSE = 'mouse';
65
+ Hammer.POINTER_TOUCH = 'touch';
66
+ Hammer.POINTER_PEN = 'pen';
67
+
68
+ // touch event defines
69
+ Hammer.EVENT_START = 'start';
70
+ Hammer.EVENT_MOVE = 'move';
71
+ Hammer.EVENT_END = 'end';
72
+
73
+ // hammer document where the base events are added at
74
+ Hammer.DOCUMENT = window.document;
75
+
76
+ // plugins and gestures namespaces
77
+ Hammer.plugins = Hammer.plugins || {};
78
+ Hammer.gestures = Hammer.gestures || {};
79
+
80
+ // if the window events are set...
81
+ Hammer.READY = false;
82
+
83
+ /**
84
+ * setup events to detect gestures on the document
85
+ */
86
+ function setup() {
87
+ if(Hammer.READY) {
88
+ return;
89
+ }
90
+
91
+ // find what eventtypes we add listeners to
92
+ Hammer.event.determineEventTypes();
93
+
94
+ // Register all gestures inside Hammer.gestures
95
+ for(var name in Hammer.gestures) {
96
+ if(Hammer.gestures.hasOwnProperty(name)) {
97
+ Hammer.detection.register(Hammer.gestures[name]);
98
+ }
99
+ }
100
+
101
+ // Add touch events on the document
102
+ Hammer.event.onTouch(Hammer.DOCUMENT, Hammer.EVENT_MOVE, Hammer.detection.detect);
103
+ Hammer.event.onTouch(Hammer.DOCUMENT, Hammer.EVENT_END, Hammer.detection.detect);
104
+
105
+ // Hammer is ready...!
106
+ Hammer.READY = true;
107
+ }
108
+
109
+ /**
110
+ * create new hammer instance
111
+ * all methods should return the instance itself, so it is chainable.
112
+ * @param {HTMLElement} element
113
+ * @param {Object} [options={}]
114
+ * @returns {Hammer.Instance}
115
+ * @constructor
116
+ */
117
+ Hammer.Instance = function(element, options) {
118
+ var self = this;
119
+
120
+ // setup HammerJS window events and register all gestures
121
+ // this also sets up the default options
122
+ setup();
123
+
124
+ this.element = element;
125
+
126
+ // start/stop detection option
127
+ this.enabled = true;
128
+
129
+ // merge options
130
+ this.options = Hammer.utils.extend(
131
+ Hammer.utils.extend({}, Hammer.defaults),
132
+ options || {});
133
+
134
+ // add some css to the element to prevent the browser from doing its native behavoir
135
+ if(this.options.stop_browser_behavior) {
136
+ Hammer.utils.stopDefaultBrowserBehavior(this.element, this.options.stop_browser_behavior);
137
+ }
138
+
139
+ // start detection on touchstart
140
+ Hammer.event.onTouch(element, Hammer.EVENT_START, function(ev) {
141
+ if(self.enabled) {
142
+ Hammer.detection.startDetect(self, ev);
143
+ }
144
+ });
145
+
146
+ // return instance
147
+ return this;
148
+ };
149
+
150
+
151
+ Hammer.Instance.prototype = {
152
+ /**
153
+ * bind events to the instance
154
+ * @param {String} gesture
155
+ * @param {Function} handler
156
+ * @returns {Hammer.Instance}
157
+ */
158
+ on: function onEvent(gesture, handler) {
159
+ var gestures = gesture.split(' ');
160
+ for(var t = 0; t < gestures.length; t++) {
161
+ this.element.addEventListener(gestures[t], handler, false);
162
+ }
163
+ return this;
164
+ },
165
+
166
+
167
+ /**
168
+ * unbind events to the instance
169
+ * @param {String} gesture
170
+ * @param {Function} handler
171
+ * @returns {Hammer.Instance}
172
+ */
173
+ off: function offEvent(gesture, handler) {
174
+ var gestures = gesture.split(' ');
175
+ for(var t = 0; t < gestures.length; t++) {
176
+ this.element.removeEventListener(gestures[t], handler, false);
177
+ }
178
+ return this;
179
+ },
180
+
181
+
182
+ /**
183
+ * trigger gesture event
184
+ * @param {String} gesture
185
+ * @param {Object} [eventData]
186
+ * @returns {Hammer.Instance}
187
+ */
188
+ trigger: function triggerEvent(gesture, eventData) {
189
+ // optional
190
+ if(!eventData) {
191
+ eventData = {};
192
+ }
193
+
194
+ // create DOM event
195
+ var event = Hammer.DOCUMENT.createEvent('Event');
196
+ event.initEvent(gesture, true, true);
197
+ event.gesture = eventData;
198
+
199
+ // trigger on the target if it is in the instance element,
200
+ // this is for event delegation tricks
201
+ var element = this.element;
202
+ if(Hammer.utils.hasParent(eventData.target, element)) {
203
+ element = eventData.target;
204
+ }
205
+
206
+ element.dispatchEvent(event);
207
+ return this;
208
+ },
209
+
210
+
211
+ /**
212
+ * enable of disable hammer.js detection
213
+ * @param {Boolean} state
214
+ * @returns {Hammer.Instance}
215
+ */
216
+ enable: function enable(state) {
217
+ this.enabled = state;
218
+ return this;
219
+ }
220
+ };
221
+
222
+
223
+ /**
224
+ * this holds the last move event,
225
+ * used to fix empty touchend issue
226
+ * see the onTouch event for an explanation
227
+ * @type {Object}
228
+ */
229
+ var last_move_event = null;
230
+
231
+
232
+ /**
233
+ * when the mouse is hold down, this is true
234
+ * @type {Boolean}
235
+ */
236
+ var enable_detect = false;
237
+
238
+
239
+ /**
240
+ * when touch events have been fired, this is true
241
+ * @type {Boolean}
242
+ */
243
+ var touch_triggered = false;
244
+
245
+
246
+ Hammer.event = {
247
+ /**
248
+ * simple addEventListener
249
+ * @param {HTMLElement} element
250
+ * @param {String} type
251
+ * @param {Function} handler
252
+ */
253
+ bindDom: function(element, type, handler) {
254
+ var types = type.split(' ');
255
+ for(var t = 0; t < types.length; t++) {
256
+ element.addEventListener(types[t], handler, false);
257
+ }
258
+ },
259
+
260
+
261
+ /**
262
+ * touch events with mouse fallback
263
+ * @param {HTMLElement} element
264
+ * @param {String} eventType like Hammer.EVENT_MOVE
265
+ * @param {Function} handler
266
+ */
267
+ onTouch: function onTouch(element, eventType, handler) {
268
+ var self = this;
269
+
270
+ this.bindDom(element, Hammer.EVENT_TYPES[eventType], function bindDomOnTouch(ev) {
271
+ var sourceEventType = ev.type.toLowerCase();
272
+
273
+ // onmouseup, but when touchend has been fired we do nothing.
274
+ // this is for touchdevices which also fire a mouseup on touchend
275
+ if(sourceEventType.match(/mouse/) && touch_triggered) {
276
+ return;
277
+ }
278
+
279
+ // mousebutton must be down or a touch event
280
+ else if(sourceEventType.match(/touch/) || // touch events are always on screen
281
+ sourceEventType.match(/pointerdown/) || // pointerevents touch
282
+ (sourceEventType.match(/mouse/) && ev.which === 1) // mouse is pressed
283
+ ) {
284
+ enable_detect = true;
285
+ }
286
+
287
+ // mouse isn't pressed
288
+ else if(sourceEventType.match(/mouse/) && ev.which !== 1) {
289
+ enable_detect = false;
290
+ }
291
+
292
+
293
+ // we are in a touch event, set the touch triggered bool to true,
294
+ // this for the conflicts that may occur on ios and android
295
+ if(sourceEventType.match(/touch|pointer/)) {
296
+ touch_triggered = true;
297
+ }
298
+
299
+ // count the total touches on the screen
300
+ var count_touches = 0;
301
+
302
+ // when touch has been triggered in this detection session
303
+ // and we are now handling a mouse event, we stop that to prevent conflicts
304
+ if(enable_detect) {
305
+ // update pointerevent
306
+ if(Hammer.HAS_POINTEREVENTS && eventType != Hammer.EVENT_END) {
307
+ count_touches = Hammer.PointerEvent.updatePointer(eventType, ev);
308
+ }
309
+ // touch
310
+ else if(sourceEventType.match(/touch/)) {
311
+ count_touches = ev.touches.length;
312
+ }
313
+ // mouse
314
+ else if(!touch_triggered) {
315
+ count_touches = sourceEventType.match(/up/) ? 0 : 1;
316
+ }
317
+
318
+ // if we are in a end event, but when we remove one touch and
319
+ // we still have enough, set eventType to move
320
+ if(count_touches > 0 && eventType == Hammer.EVENT_END) {
321
+ eventType = Hammer.EVENT_MOVE;
322
+ }
323
+ // no touches, force the end event
324
+ else if(!count_touches) {
325
+ eventType = Hammer.EVENT_END;
326
+ }
327
+
328
+ // store the last move event
329
+ if(count_touches || last_move_event === null) {
330
+ last_move_event = ev;
331
+ }
332
+
333
+ // trigger the handler
334
+ handler.call(Hammer.detection, self.collectEventData(element, eventType, self.getTouchList(last_move_event, eventType), ev));
335
+
336
+ // remove pointerevent from list
337
+ if(Hammer.HAS_POINTEREVENTS && eventType == Hammer.EVENT_END) {
338
+ count_touches = Hammer.PointerEvent.updatePointer(eventType, ev);
339
+ }
340
+ }
341
+
342
+ // on the end we reset everything
343
+ if(!count_touches) {
344
+ last_move_event = null;
345
+ enable_detect = false;
346
+ touch_triggered = false;
347
+ Hammer.PointerEvent.reset();
348
+ }
349
+ });
350
+ },
351
+
352
+
353
+ /**
354
+ * we have different events for each device/browser
355
+ * determine what we need and set them in the Hammer.EVENT_TYPES constant
356
+ */
357
+ determineEventTypes: function determineEventTypes() {
358
+ // determine the eventtype we want to set
359
+ var types;
360
+
361
+ // pointerEvents magic
362
+ if(Hammer.HAS_POINTEREVENTS) {
363
+ types = Hammer.PointerEvent.getEvents();
364
+ }
365
+ // on Android, iOS, blackberry, windows mobile we dont want any mouseevents
366
+ else if(Hammer.NO_MOUSEEVENTS) {
367
+ types = [
368
+ 'touchstart',
369
+ 'touchmove',
370
+ 'touchend touchcancel'];
371
+ }
372
+ // for non pointer events browsers and mixed browsers,
373
+ // like chrome on windows8 touch laptop
374
+ else {
375
+ types = [
376
+ 'touchstart mousedown',
377
+ 'touchmove mousemove',
378
+ 'touchend touchcancel mouseup'];
379
+ }
380
+
381
+ Hammer.EVENT_TYPES[Hammer.EVENT_START] = types[0];
382
+ Hammer.EVENT_TYPES[Hammer.EVENT_MOVE] = types[1];
383
+ Hammer.EVENT_TYPES[Hammer.EVENT_END] = types[2];
384
+ },
385
+
386
+
387
+ /**
388
+ * create touchlist depending on the event
389
+ * @param {Object} ev
390
+ * @param {String} eventType used by the fakemultitouch plugin
391
+ */
392
+ getTouchList: function getTouchList(ev/*, eventType*/) {
393
+ // get the fake pointerEvent touchlist
394
+ if(Hammer.HAS_POINTEREVENTS) {
395
+ return Hammer.PointerEvent.getTouchList();
396
+ }
397
+ // get the touchlist
398
+ else if(ev.touches) {
399
+ return ev.touches;
400
+ }
401
+ // make fake touchlist from mouse position
402
+ else {
403
+ ev.indentifier = 1;
404
+ return [ev];
405
+ }
406
+ },
407
+
408
+
409
+ /**
410
+ * collect event data for Hammer js
411
+ * @param {HTMLElement} element
412
+ * @param {String} eventType like Hammer.EVENT_MOVE
413
+ * @param {Object} eventData
414
+ */
415
+ collectEventData: function collectEventData(element, eventType, touches, ev) {
416
+ // find out pointerType
417
+ var pointerType = Hammer.POINTER_TOUCH;
418
+ if(ev.type.match(/mouse/) || Hammer.PointerEvent.matchType(Hammer.POINTER_MOUSE, ev)) {
419
+ pointerType = Hammer.POINTER_MOUSE;
420
+ }
421
+
422
+ return {
423
+ center : Hammer.utils.getCenter(touches),
424
+ timeStamp : new Date().getTime(),
425
+ target : ev.target,
426
+ touches : touches,
427
+ eventType : eventType,
428
+ pointerType: pointerType,
429
+ srcEvent : ev,
430
+
431
+ /**
432
+ * prevent the browser default actions
433
+ * mostly used to disable scrolling of the browser
434
+ */
435
+ preventDefault: function() {
436
+ if(this.srcEvent.preventManipulation) {
437
+ this.srcEvent.preventManipulation();
438
+ }
439
+
440
+ if(this.srcEvent.preventDefault) {
441
+ this.srcEvent.preventDefault();
442
+ }
443
+ },
444
+
445
+ /**
446
+ * stop bubbling the event up to its parents
447
+ */
448
+ stopPropagation: function() {
449
+ this.srcEvent.stopPropagation();
450
+ },
451
+
452
+ /**
453
+ * immediately stop gesture detection
454
+ * might be useful after a swipe was detected
455
+ * @return {*}
456
+ */
457
+ stopDetect: function() {
458
+ return Hammer.detection.stopDetect();
459
+ }
460
+ };
461
+ }
462
+ };
463
+
464
+ Hammer.PointerEvent = {
465
+ /**
466
+ * holds all pointers
467
+ * @type {Object}
468
+ */
469
+ pointers: {},
470
+
471
+ /**
472
+ * get a list of pointers
473
+ * @returns {Array} touchlist
474
+ */
475
+ getTouchList: function() {
476
+ var self = this;
477
+ var touchlist = [];
478
+
479
+ // we can use forEach since pointerEvents only is in IE10
480
+ Object.keys(self.pointers).sort().forEach(function(id) {
481
+ touchlist.push(self.pointers[id]);
482
+ });
483
+ return touchlist;
484
+ },
485
+
486
+ /**
487
+ * update the position of a pointer
488
+ * @param {String} type Hammer.EVENT_END
489
+ * @param {Object} pointerEvent
490
+ */
491
+ updatePointer: function(type, pointerEvent) {
492
+ if(type == Hammer.EVENT_END) {
493
+ this.pointers = {};
494
+ }
495
+ else {
496
+ pointerEvent.identifier = pointerEvent.pointerId;
497
+ this.pointers[pointerEvent.pointerId] = pointerEvent;
498
+ }
499
+
500
+ return Object.keys(this.pointers).length;
501
+ },
502
+
503
+ /**
504
+ * check if ev matches pointertype
505
+ * @param {String} pointerType Hammer.POINTER_MOUSE
506
+ * @param {PointerEvent} ev
507
+ */
508
+ matchType: function(pointerType, ev) {
509
+ if(!ev.pointerType) {
510
+ return false;
511
+ }
512
+
513
+ var types = {};
514
+ types[Hammer.POINTER_MOUSE] = (ev.pointerType == ev.MSPOINTER_TYPE_MOUSE || ev.pointerType == Hammer.POINTER_MOUSE);
515
+ types[Hammer.POINTER_TOUCH] = (ev.pointerType == ev.MSPOINTER_TYPE_TOUCH || ev.pointerType == Hammer.POINTER_TOUCH);
516
+ types[Hammer.POINTER_PEN] = (ev.pointerType == ev.MSPOINTER_TYPE_PEN || ev.pointerType == Hammer.POINTER_PEN);
517
+ return types[pointerType];
518
+ },
519
+
520
+
521
+ /**
522
+ * get events
523
+ */
524
+ getEvents: function() {
525
+ return [
526
+ 'pointerdown MSPointerDown',
527
+ 'pointermove MSPointerMove',
528
+ 'pointerup pointercancel MSPointerUp MSPointerCancel'
529
+ ];
530
+ },
531
+
532
+ /**
533
+ * reset the list
534
+ */
535
+ reset: function() {
536
+ this.pointers = {};
537
+ }
538
+ };
539
+
540
+
541
+ Hammer.utils = {
542
+ /**
543
+ * extend method,
544
+ * also used for cloning when dest is an empty object
545
+ * @param {Object} dest
546
+ * @param {Object} src
547
+ * @parm {Boolean} merge do a merge
548
+ * @returns {Object} dest
549
+ */
550
+ extend: function extend(dest, src, merge) {
551
+ for(var key in src) {
552
+ if(dest[key] !== undefined && merge) {
553
+ continue;
554
+ }
555
+ dest[key] = src[key];
556
+ }
557
+ return dest;
558
+ },
559
+
560
+
561
+ /**
562
+ * find if a node is in the given parent
563
+ * used for event delegation tricks
564
+ * @param {HTMLElement} node
565
+ * @param {HTMLElement} parent
566
+ * @returns {boolean} has_parent
567
+ */
568
+ hasParent: function(node, parent) {
569
+ while(node) {
570
+ if(node == parent) {
571
+ return true;
572
+ }
573
+ node = node.parentNode;
574
+ }
575
+ return false;
576
+ },
577
+
578
+
579
+ /**
580
+ * get the center of all the touches
581
+ * @param {Array} touches
582
+ * @returns {Object} center
583
+ */
584
+ getCenter: function getCenter(touches) {
585
+ var valuesX = [], valuesY = [];
586
+
587
+ for(var t = 0, len = touches.length; t < len; t++) {
588
+ valuesX.push(touches[t].pageX);
589
+ valuesY.push(touches[t].pageY);
590
+ }
591
+
592
+ return {
593
+ pageX: ((Math.min.apply(Math, valuesX) + Math.max.apply(Math, valuesX)) / 2),
594
+ pageY: ((Math.min.apply(Math, valuesY) + Math.max.apply(Math, valuesY)) / 2)
595
+ };
596
+ },
597
+
598
+
599
+ /**
600
+ * calculate the velocity between two points
601
+ * @param {Number} delta_time
602
+ * @param {Number} delta_x
603
+ * @param {Number} delta_y
604
+ * @returns {Object} velocity
605
+ */
606
+ getVelocity: function getVelocity(delta_time, delta_x, delta_y) {
607
+ return {
608
+ x: Math.abs(delta_x / delta_time) || 0,
609
+ y: Math.abs(delta_y / delta_time) || 0
610
+ };
611
+ },
612
+
613
+
614
+ /**
615
+ * calculate the angle between two coordinates
616
+ * @param {Touch} touch1
617
+ * @param {Touch} touch2
618
+ * @returns {Number} angle
619
+ */
620
+ getAngle: function getAngle(touch1, touch2) {
621
+ var y = touch2.pageY - touch1.pageY,
622
+ x = touch2.pageX - touch1.pageX;
623
+ return Math.atan2(y, x) * 180 / Math.PI;
624
+ },
625
+
626
+
627
+ /**
628
+ * angle to direction define
629
+ * @param {Touch} touch1
630
+ * @param {Touch} touch2
631
+ * @returns {String} direction constant, like Hammer.DIRECTION_LEFT
632
+ */
633
+ getDirection: function getDirection(touch1, touch2) {
634
+ var x = Math.abs(touch1.pageX - touch2.pageX),
635
+ y = Math.abs(touch1.pageY - touch2.pageY);
636
+
637
+ if(x >= y) {
638
+ return touch1.pageX - touch2.pageX > 0 ? Hammer.DIRECTION_LEFT : Hammer.DIRECTION_RIGHT;
639
+ }
640
+ else {
641
+ return touch1.pageY - touch2.pageY > 0 ? Hammer.DIRECTION_UP : Hammer.DIRECTION_DOWN;
642
+ }
643
+ },
644
+
645
+
646
+ /**
647
+ * calculate the distance between two touches
648
+ * @param {Touch} touch1
649
+ * @param {Touch} touch2
650
+ * @returns {Number} distance
651
+ */
652
+ getDistance: function getDistance(touch1, touch2) {
653
+ var x = touch2.pageX - touch1.pageX,
654
+ y = touch2.pageY - touch1.pageY;
655
+ return Math.sqrt((x * x) + (y * y));
656
+ },
657
+
658
+
659
+ /**
660
+ * calculate the scale factor between two touchLists (fingers)
661
+ * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
662
+ * @param {Array} start
663
+ * @param {Array} end
664
+ * @returns {Number} scale
665
+ */
666
+ getScale: function getScale(start, end) {
667
+ // need two fingers...
668
+ if(start.length >= 2 && end.length >= 2) {
669
+ return this.getDistance(end[0], end[1]) /
670
+ this.getDistance(start[0], start[1]);
671
+ }
672
+ return 1;
673
+ },
674
+
675
+
676
+ /**
677
+ * calculate the rotation degrees between two touchLists (fingers)
678
+ * @param {Array} start
679
+ * @param {Array} end
680
+ * @returns {Number} rotation
681
+ */
682
+ getRotation: function getRotation(start, end) {
683
+ // need two fingers
684
+ if(start.length >= 2 && end.length >= 2) {
685
+ return this.getAngle(end[1], end[0]) -
686
+ this.getAngle(start[1], start[0]);
687
+ }
688
+ return 0;
689
+ },
690
+
691
+
692
+ /**
693
+ * boolean if the direction is vertical
694
+ * @param {String} direction
695
+ * @returns {Boolean} is_vertical
696
+ */
697
+ isVertical: function isVertical(direction) {
698
+ return (direction == Hammer.DIRECTION_UP || direction == Hammer.DIRECTION_DOWN);
699
+ },
700
+
701
+
702
+ /**
703
+ * stop browser default behavior with css props
704
+ * @param {HtmlElement} element
705
+ * @param {Object} css_props
706
+ */
707
+ stopDefaultBrowserBehavior: function stopDefaultBrowserBehavior(element, css_props) {
708
+ var prop,
709
+ vendors = ['webkit', 'khtml', 'moz', 'Moz', 'ms', 'o', ''];
710
+
711
+ if(!css_props || !element.style) {
712
+ return;
713
+ }
714
+
715
+ // with css properties for modern browsers
716
+ for(var i = 0; i < vendors.length; i++) {
717
+ for(var p in css_props) {
718
+ if(css_props.hasOwnProperty(p)) {
719
+ prop = p;
720
+
721
+ // vender prefix at the property
722
+ if(vendors[i]) {
723
+ prop = vendors[i] + prop.substring(0, 1).toUpperCase() + prop.substring(1);
724
+ }
725
+
726
+ // set the style
727
+ element.style[prop] = css_props[p];
728
+ }
729
+ }
730
+ }
731
+
732
+ // also the disable onselectstart
733
+ if(css_props.userSelect == 'none') {
734
+ element.onselectstart = function() {
735
+ return false;
736
+ };
737
+ }
738
+
739
+ // and disable ondragstart
740
+ if(css_props.userDrag == 'none') {
741
+ element.ondragstart = function() {
742
+ return false;
743
+ };
744
+ }
745
+ }
746
+ };
747
+
748
+
749
+ Hammer.detection = {
750
+ // contains all registred Hammer.gestures in the correct order
751
+ gestures: [],
752
+
753
+ // data of the current Hammer.gesture detection session
754
+ current : null,
755
+
756
+ // the previous Hammer.gesture session data
757
+ // is a full clone of the previous gesture.current object
758
+ previous: null,
759
+
760
+ // when this becomes true, no gestures are fired
761
+ stopped : false,
762
+
763
+
764
+ /**
765
+ * start Hammer.gesture detection
766
+ * @param {Hammer.Instance} inst
767
+ * @param {Object} eventData
768
+ */
769
+ startDetect: function startDetect(inst, eventData) {
770
+ // already busy with a Hammer.gesture detection on an element
771
+ if(this.current) {
772
+ return;
773
+ }
774
+
775
+ this.stopped = false;
776
+
777
+ this.current = {
778
+ inst : inst, // reference to HammerInstance we're working for
779
+ startEvent: Hammer.utils.extend({}, eventData), // start eventData for distances, timing etc
780
+ lastEvent : false, // last eventData
781
+ name : '' // current gesture we're in/detected, can be 'tap', 'hold' etc
782
+ };
783
+
784
+ this.detect(eventData);
785
+ },
786
+
787
+
788
+ /**
789
+ * Hammer.gesture detection
790
+ * @param {Object} eventData
791
+ */
792
+ detect: function detect(eventData) {
793
+ if(!this.current || this.stopped) {
794
+ return;
795
+ }
796
+
797
+ // extend event data with calculations about scale, distance etc
798
+ eventData = this.extendEventData(eventData);
799
+
800
+ // instance options
801
+ var inst_options = this.current.inst.options;
802
+
803
+ // call Hammer.gesture handlers
804
+ for(var g=0, len=this.gestures.length; g<len; g++) {
805
+ var gesture = this.gestures[g];
806
+
807
+ // only when the instance options have enabled this gesture
808
+ if(!this.stopped && inst_options[gesture.name] !== false) {
809
+ // if a handler returns false, we stop with the detection
810
+ if(gesture.handler.call(gesture, eventData, this.current.inst) === false) {
811
+ this.stopDetect();
812
+ break;
813
+ }
814
+ }
815
+ }
816
+
817
+ // store as previous event event
818
+ if(this.current) {
819
+ this.current.lastEvent = eventData;
820
+ }
821
+
822
+ // endevent, but not the last touch, so dont stop
823
+ if(eventData.eventType == Hammer.EVENT_END && !eventData.touches.length - 1) {
824
+ this.stopDetect();
825
+ }
826
+
827
+ return eventData;
828
+ },
829
+
830
+
831
+ /**
832
+ * clear the Hammer.gesture vars
833
+ * this is called on endDetect, but can also be used when a final Hammer.gesture has been detected
834
+ * to stop other Hammer.gestures from being fired
835
+ */
836
+ stopDetect: function stopDetect() {
837
+ // clone current data to the store as the previous gesture
838
+ // used for the double tap gesture, since this is an other gesture detect session
839
+ this.previous = Hammer.utils.extend({}, this.current);
840
+
841
+ // reset the current
842
+ this.current = null;
843
+
844
+ // stopped!
845
+ this.stopped = true;
846
+ },
847
+
848
+
849
+ /**
850
+ * extend eventData for Hammer.gestures
851
+ * @param {Object} ev
852
+ * @returns {Object} ev
853
+ */
854
+ extendEventData: function extendEventData(ev) {
855
+ var startEv = this.current.startEvent;
856
+
857
+ // if the touches change, set the new touches over the startEvent touches
858
+ // this because touchevents don't have all the touches on touchstart, or the
859
+ // user must place his fingers at the EXACT same time on the screen, which is not realistic
860
+ // but, sometimes it happens that both fingers are touching at the EXACT same time
861
+ if(startEv && (ev.touches.length != startEv.touches.length || ev.touches === startEv.touches)) {
862
+ // extend 1 level deep to get the touchlist with the touch objects
863
+ startEv.touches = [];
864
+ for(var i = 0, len = ev.touches.length; i < len; i++) {
865
+ startEv.touches.push(Hammer.utils.extend({}, ev.touches[i]));
866
+ }
867
+ }
868
+
869
+ var delta_time = ev.timeStamp - startEv.timeStamp
870
+ , delta_x = ev.center.pageX - startEv.center.pageX
871
+ , delta_y = ev.center.pageY - startEv.center.pageY
872
+ , velocity = Hammer.utils.getVelocity(delta_time, delta_x, delta_y)
873
+ , interimAngle
874
+ , interimDirection;
875
+
876
+ // end events (e.g. dragend) don't have useful values for interimDirection & interimAngle
877
+ // because the previous event has exactly the same coordinates
878
+ // so for end events, take the previous values of interimDirection & interimAngle
879
+ // instead of recalculating them and getting a spurious '0'
880
+ if(ev.eventType === 'end') {
881
+ interimAngle = this.current.lastEvent && this.current.lastEvent.interimAngle;
882
+ interimDirection = this.current.lastEvent && this.current.lastEvent.interimDirection;
883
+ }
884
+ else {
885
+ interimAngle = this.current.lastEvent && Hammer.utils.getAngle(this.current.lastEvent.center, ev.center);
886
+ interimDirection = this.current.lastEvent && Hammer.utils.getDirection(this.current.lastEvent.center, ev.center);
887
+ }
888
+
889
+ Hammer.utils.extend(ev, {
890
+ deltaTime: delta_time,
891
+
892
+ deltaX: delta_x,
893
+ deltaY: delta_y,
894
+
895
+ velocityX: velocity.x,
896
+ velocityY: velocity.y,
897
+
898
+ distance: Hammer.utils.getDistance(startEv.center, ev.center),
899
+
900
+ angle: Hammer.utils.getAngle(startEv.center, ev.center),
901
+ interimAngle: interimAngle,
902
+
903
+ direction: Hammer.utils.getDirection(startEv.center, ev.center),
904
+ interimDirection: interimDirection,
905
+
906
+ scale: Hammer.utils.getScale(startEv.touches, ev.touches),
907
+ rotation: Hammer.utils.getRotation(startEv.touches, ev.touches),
908
+
909
+ startEvent: startEv
910
+ });
911
+
912
+ return ev;
913
+ },
914
+
915
+
916
+ /**
917
+ * register new gesture
918
+ * @param {Object} gesture object, see gestures.js for documentation
919
+ * @returns {Array} gestures
920
+ */
921
+ register: function register(gesture) {
922
+ // add an enable gesture options if there is no given
923
+ var options = gesture.defaults || {};
924
+ if(options[gesture.name] === undefined) {
925
+ options[gesture.name] = true;
926
+ }
927
+
928
+ // extend Hammer default options with the Hammer.gesture options
929
+ Hammer.utils.extend(Hammer.defaults, options, true);
930
+
931
+ // set its index
932
+ gesture.index = gesture.index || 1000;
933
+
934
+ // add Hammer.gesture to the list
935
+ this.gestures.push(gesture);
936
+
937
+ // sort the list by index
938
+ this.gestures.sort(function(a, b) {
939
+ if(a.index < b.index) { return -1; }
940
+ if(a.index > b.index) { return 1; }
941
+ return 0;
942
+ });
943
+
944
+ return this.gestures;
945
+ }
946
+ };
947
+
948
+
949
+ /**
950
+ * Drag
951
+ * Move with x fingers (default 1) around on the page. Blocking the scrolling when
952
+ * moving left and right is a good practice. When all the drag events are blocking
953
+ * you disable scrolling on that area.
954
+ * @events drag, drapleft, dragright, dragup, dragdown
955
+ */
956
+ Hammer.gestures.Drag = {
957
+ name : 'drag',
958
+ index : 50,
959
+ defaults : {
960
+ drag_min_distance : 10,
961
+
962
+ // Set correct_for_drag_min_distance to true to make the starting point of the drag
963
+ // be calculated from where the drag was triggered, not from where the touch started.
964
+ // Useful to avoid a jerk-starting drag, which can make fine-adjustments
965
+ // through dragging difficult, and be visually unappealing.
966
+ correct_for_drag_min_distance: true,
967
+
968
+ // set 0 for unlimited, but this can conflict with transform
969
+ drag_max_touches : 1,
970
+
971
+ // prevent default browser behavior when dragging occurs
972
+ // be careful with it, it makes the element a blocking element
973
+ // when you are using the drag gesture, it is a good practice to set this true
974
+ drag_block_horizontal : false,
975
+ drag_block_vertical : false,
976
+
977
+ // drag_lock_to_axis keeps the drag gesture on the axis that it started on,
978
+ // It disallows vertical directions if the initial direction was horizontal, and vice versa.
979
+ drag_lock_to_axis : false,
980
+
981
+ // drag lock only kicks in when distance > drag_lock_min_distance
982
+ // This way, locking occurs only when the distance has become large enough to reliably determine the direction
983
+ drag_lock_min_distance : 25
984
+ },
985
+
986
+ triggered: false,
987
+ handler : function dragGesture(ev, inst) {
988
+ // current gesture isnt drag, but dragged is true
989
+ // this means an other gesture is busy. now call dragend
990
+ if(Hammer.detection.current.name != this.name && this.triggered) {
991
+ inst.trigger(this.name + 'end', ev);
992
+ this.triggered = false;
993
+ return;
994
+ }
995
+
996
+ // max touches
997
+ if(inst.options.drag_max_touches > 0 &&
998
+ ev.touches.length > inst.options.drag_max_touches) {
999
+ return;
1000
+ }
1001
+
1002
+ switch(ev.eventType) {
1003
+ case Hammer.EVENT_START:
1004
+ this.triggered = false;
1005
+ break;
1006
+
1007
+ case Hammer.EVENT_MOVE:
1008
+ // when the distance we moved is too small we skip this gesture
1009
+ // or we can be already in dragging
1010
+ if(ev.distance < inst.options.drag_min_distance &&
1011
+ Hammer.detection.current.name != this.name) {
1012
+ return;
1013
+ }
1014
+
1015
+ // we are dragging!
1016
+ if(Hammer.detection.current.name != this.name) {
1017
+ Hammer.detection.current.name = this.name;
1018
+ if(inst.options.correct_for_drag_min_distance && ev.distance > 0) {
1019
+ // When a drag is triggered, set the event center to drag_min_distance pixels from the original event center.
1020
+ // Without this correction, the dragged distance would jumpstart at drag_min_distance pixels instead of at 0.
1021
+ // It might be useful to save the original start point somewhere
1022
+ var factor = Math.abs(inst.options.drag_min_distance / ev.distance);
1023
+ Hammer.detection.current.startEvent.center.pageX += ev.deltaX * factor;
1024
+ Hammer.detection.current.startEvent.center.pageY += ev.deltaY * factor;
1025
+
1026
+ // recalculate event data using new start point
1027
+ ev = Hammer.detection.extendEventData(ev);
1028
+ }
1029
+ }
1030
+
1031
+ // lock drag to axis?
1032
+ if(Hammer.detection.current.lastEvent.drag_locked_to_axis || (inst.options.drag_lock_to_axis && inst.options.drag_lock_min_distance <= ev.distance)) {
1033
+ ev.drag_locked_to_axis = true;
1034
+ }
1035
+ var last_direction = Hammer.detection.current.lastEvent.direction;
1036
+ if(ev.drag_locked_to_axis && last_direction !== ev.direction) {
1037
+ // keep direction on the axis that the drag gesture started on
1038
+ if(Hammer.utils.isVertical(last_direction)) {
1039
+ ev.direction = (ev.deltaY < 0) ? Hammer.DIRECTION_UP : Hammer.DIRECTION_DOWN;
1040
+ }
1041
+ else {
1042
+ ev.direction = (ev.deltaX < 0) ? Hammer.DIRECTION_LEFT : Hammer.DIRECTION_RIGHT;
1043
+ }
1044
+ }
1045
+
1046
+ // first time, trigger dragstart event
1047
+ if(!this.triggered) {
1048
+ inst.trigger(this.name + 'start', ev);
1049
+ this.triggered = true;
1050
+ }
1051
+
1052
+ // trigger normal event
1053
+ inst.trigger(this.name, ev);
1054
+
1055
+ // direction event, like dragdown
1056
+ inst.trigger(this.name + ev.direction, ev);
1057
+
1058
+ // block the browser events
1059
+ if((inst.options.drag_block_vertical && Hammer.utils.isVertical(ev.direction)) ||
1060
+ (inst.options.drag_block_horizontal && !Hammer.utils.isVertical(ev.direction))) {
1061
+ ev.preventDefault();
1062
+ }
1063
+ break;
1064
+
1065
+ case Hammer.EVENT_END:
1066
+ // trigger dragend
1067
+ if(this.triggered) {
1068
+ inst.trigger(this.name + 'end', ev);
1069
+ }
1070
+
1071
+ this.triggered = false;
1072
+ break;
1073
+ }
1074
+ }
1075
+ };
1076
+
1077
+ /**
1078
+ * Hold
1079
+ * Touch stays at the same place for x time
1080
+ * @events hold
1081
+ */
1082
+ Hammer.gestures.Hold = {
1083
+ name : 'hold',
1084
+ index : 10,
1085
+ defaults: {
1086
+ hold_timeout : 500,
1087
+ hold_threshold: 1
1088
+ },
1089
+ timer : null,
1090
+ handler : function holdGesture(ev, inst) {
1091
+ switch(ev.eventType) {
1092
+ case Hammer.EVENT_START:
1093
+ // clear any running timers
1094
+ clearTimeout(this.timer);
1095
+
1096
+ // set the gesture so we can check in the timeout if it still is
1097
+ Hammer.detection.current.name = this.name;
1098
+
1099
+ // set timer and if after the timeout it still is hold,
1100
+ // we trigger the hold event
1101
+ this.timer = setTimeout(function() {
1102
+ if(Hammer.detection.current.name == 'hold') {
1103
+ inst.trigger('hold', ev);
1104
+ }
1105
+ }, inst.options.hold_timeout);
1106
+ break;
1107
+
1108
+ // when you move or end we clear the timer
1109
+ case Hammer.EVENT_MOVE:
1110
+ if(ev.distance > inst.options.hold_threshold) {
1111
+ clearTimeout(this.timer);
1112
+ }
1113
+ break;
1114
+
1115
+ case Hammer.EVENT_END:
1116
+ clearTimeout(this.timer);
1117
+ break;
1118
+ }
1119
+ }
1120
+ };
1121
+
1122
+ /**
1123
+ * Release
1124
+ * Called as last, tells the user has released the screen
1125
+ * @events release
1126
+ */
1127
+ Hammer.gestures.Release = {
1128
+ name : 'release',
1129
+ index : Infinity,
1130
+ handler: function releaseGesture(ev, inst) {
1131
+ if(ev.eventType == Hammer.EVENT_END) {
1132
+ inst.trigger(this.name, ev);
1133
+ }
1134
+ }
1135
+ };
1136
+
1137
+ /**
1138
+ * Swipe
1139
+ * triggers swipe events when the end velocity is above the threshold
1140
+ * @events swipe, swipeleft, swiperight, swipeup, swipedown
1141
+ */
1142
+ Hammer.gestures.Swipe = {
1143
+ name : 'swipe',
1144
+ index : 40,
1145
+ defaults: {
1146
+ // set 0 for unlimited, but this can conflict with transform
1147
+ swipe_min_touches: 1,
1148
+ swipe_max_touches: 1,
1149
+ swipe_velocity : 0.7
1150
+ },
1151
+ handler : function swipeGesture(ev, inst) {
1152
+ if(ev.eventType == Hammer.EVENT_END) {
1153
+ // max touches
1154
+ if(inst.options.swipe_max_touches > 0 &&
1155
+ ev.touches.length < inst.options.swipe_min_touches &&
1156
+ ev.touches.length > inst.options.swipe_max_touches) {
1157
+ return;
1158
+ }
1159
+
1160
+ // when the distance we moved is too small we skip this gesture
1161
+ // or we can be already in dragging
1162
+ if(ev.velocityX > inst.options.swipe_velocity ||
1163
+ ev.velocityY > inst.options.swipe_velocity) {
1164
+ // trigger swipe events
1165
+ inst.trigger(this.name, ev);
1166
+ inst.trigger(this.name + ev.direction, ev);
1167
+ }
1168
+ }
1169
+ }
1170
+ };
1171
+
1172
+ /**
1173
+ * Tap/DoubleTap
1174
+ * Quick touch at a place or double at the same place
1175
+ * @events tap, doubletap
1176
+ */
1177
+ Hammer.gestures.Tap = {
1178
+ name : 'tap',
1179
+ index : 100,
1180
+ defaults: {
1181
+ tap_max_touchtime : 250,
1182
+ tap_max_distance : 10,
1183
+ tap_always : true,
1184
+ doubletap_distance: 20,
1185
+ doubletap_interval: 300
1186
+ },
1187
+ handler : function tapGesture(ev, inst) {
1188
+ if(ev.eventType == Hammer.EVENT_END && ev.srcEvent.type != 'touchcancel') {
1189
+ // previous gesture, for the double tap since these are two different gesture detections
1190
+ var prev = Hammer.detection.previous,
1191
+ did_doubletap = false;
1192
+
1193
+ // when the touchtime is higher then the max touch time
1194
+ // or when the moving distance is too much
1195
+ if(ev.deltaTime > inst.options.tap_max_touchtime ||
1196
+ ev.distance > inst.options.tap_max_distance) {
1197
+ return;
1198
+ }
1199
+
1200
+ // check if double tap
1201
+ if(prev && prev.name == 'tap' &&
1202
+ (ev.timeStamp - prev.lastEvent.timeStamp) < inst.options.doubletap_interval &&
1203
+ ev.distance < inst.options.doubletap_distance) {
1204
+ inst.trigger('doubletap', ev);
1205
+ did_doubletap = true;
1206
+ }
1207
+
1208
+ // do a single tap
1209
+ if(!did_doubletap || inst.options.tap_always) {
1210
+ Hammer.detection.current.name = 'tap';
1211
+ inst.trigger(Hammer.detection.current.name, ev);
1212
+ }
1213
+ }
1214
+ }
1215
+ };
1216
+
1217
+ /**
1218
+ * Touch
1219
+ * Called as first, tells the user has touched the screen
1220
+ * @events touch
1221
+ */
1222
+ Hammer.gestures.Touch = {
1223
+ name : 'touch',
1224
+ index : -Infinity,
1225
+ defaults: {
1226
+ // call preventDefault at touchstart, and makes the element blocking by
1227
+ // disabling the scrolling of the page, but it improves gestures like
1228
+ // transforming and dragging.
1229
+ // be careful with using this, it can be very annoying for users to be stuck
1230
+ // on the page
1231
+ prevent_default : false,
1232
+
1233
+ // disable mouse events, so only touch (or pen!) input triggers events
1234
+ prevent_mouseevents: false
1235
+ },
1236
+ handler : function touchGesture(ev, inst) {
1237
+ if(inst.options.prevent_mouseevents && ev.pointerType == Hammer.POINTER_MOUSE) {
1238
+ ev.stopDetect();
1239
+ return;
1240
+ }
1241
+
1242
+ if(inst.options.prevent_default) {
1243
+ ev.preventDefault();
1244
+ }
1245
+
1246
+ if(ev.eventType == Hammer.EVENT_START) {
1247
+ inst.trigger(this.name, ev);
1248
+ }
1249
+ }
1250
+ };
1251
+
1252
+ /**
1253
+ * Transform
1254
+ * User want to scale or rotate with 2 fingers
1255
+ * @events transform, pinch, pinchin, pinchout, rotate
1256
+ */
1257
+ Hammer.gestures.Transform = {
1258
+ name : 'transform',
1259
+ index : 45,
1260
+ defaults : {
1261
+ // factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1
1262
+ transform_min_scale : 0.01,
1263
+ // rotation in degrees
1264
+ transform_min_rotation: 1,
1265
+ // prevent default browser behavior when two touches are on the screen
1266
+ // but it makes the element a blocking element
1267
+ // when you are using the transform gesture, it is a good practice to set this true
1268
+ transform_always_block: false
1269
+ },
1270
+ triggered: false,
1271
+ handler : function transformGesture(ev, inst) {
1272
+ // current gesture isnt drag, but dragged is true
1273
+ // this means an other gesture is busy. now call dragend
1274
+ if(Hammer.detection.current.name != this.name && this.triggered) {
1275
+ inst.trigger(this.name + 'end', ev);
1276
+ this.triggered = false;
1277
+ return;
1278
+ }
1279
+
1280
+ // atleast multitouch
1281
+ if(ev.touches.length < 2) {
1282
+ return;
1283
+ }
1284
+
1285
+ // prevent default when two fingers are on the screen
1286
+ if(inst.options.transform_always_block) {
1287
+ ev.preventDefault();
1288
+ }
1289
+
1290
+ switch(ev.eventType) {
1291
+ case Hammer.EVENT_START:
1292
+ this.triggered = false;
1293
+ break;
1294
+
1295
+ case Hammer.EVENT_MOVE:
1296
+ var scale_threshold = Math.abs(1 - ev.scale);
1297
+ var rotation_threshold = Math.abs(ev.rotation);
1298
+
1299
+ // when the distance we moved is too small we skip this gesture
1300
+ // or we can be already in dragging
1301
+ if(scale_threshold < inst.options.transform_min_scale &&
1302
+ rotation_threshold < inst.options.transform_min_rotation) {
1303
+ return;
1304
+ }
1305
+
1306
+ // we are transforming!
1307
+ Hammer.detection.current.name = this.name;
1308
+
1309
+ // first time, trigger dragstart event
1310
+ if(!this.triggered) {
1311
+ inst.trigger(this.name + 'start', ev);
1312
+ this.triggered = true;
1313
+ }
1314
+
1315
+ inst.trigger(this.name, ev); // basic transform event
1316
+
1317
+ // trigger rotate event
1318
+ if(rotation_threshold > inst.options.transform_min_rotation) {
1319
+ inst.trigger('rotate', ev);
1320
+ }
1321
+
1322
+ // trigger pinch event
1323
+ if(scale_threshold > inst.options.transform_min_scale) {
1324
+ inst.trigger('pinch', ev);
1325
+ inst.trigger('pinch' + ((ev.scale < 1) ? 'in' : 'out'), ev);
1326
+ }
1327
+ break;
1328
+
1329
+ case Hammer.EVENT_END:
1330
+ // trigger dragend
1331
+ if(this.triggered) {
1332
+ inst.trigger(this.name + 'end', ev);
1333
+ }
1334
+
1335
+ this.triggered = false;
1336
+ break;
1337
+ }
1338
+ }
1339
+ };
1340
+
1341
+ // Based off Lo-Dash's excellent UMD wrapper (slightly modified) - https://github.com/bestiejs/lodash/blob/master/lodash.js#L5515-L5543
1342
+ // some AMD build optimizers, like r.js, check for specific condition patterns like the following:
1343
+ if(typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
1344
+ // define as an anonymous module
1345
+ define(function() {
1346
+ return Hammer;
1347
+ });
1348
+ // check for `exports` after `define` in case a build optimizer adds an `exports` object
1349
+ }
1350
+ else if(typeof module === 'object' && typeof module.exports === 'object') {
1351
+ module.exports = Hammer;
1352
+ }
1353
+ else {
1354
+ window.Hammer = Hammer;
1355
+ }
1356
+ })(this);