hammerjs-rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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);