hammerjs_rails 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 07e04e93367436529241e395b86d27136fd739ff
4
+ data.tar.gz: f8c7b6a4e8d8a0dec2a0178fd22e6669cdbe69ee
5
+ SHA512:
6
+ metadata.gz: 615c08b00653f78fb62551662205b784932efd2b1c5321f818a48619dc66162cd0ba5ce15594c930404d9924ea70bcadc1bb8f0bdc22fd2998e3c85cec9b6e49
7
+ data.tar.gz: 644b387e8ef92e1d6bc085be1452d7c1320b6f71b85e78604cd64b5f69a913a17f52ec7db4ea2caff70a84e43dc9b1d4b0ceb748c9652315054601e067799661
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hammerjs_rails.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Guy Israeli
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,33 @@
1
+ # HammerjsRails
2
+
3
+ Hammer.js for Ruby on Rails
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'hammerjs_rails'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install hammerjs_rails
18
+
19
+ ## Usage
20
+
21
+ add to manifest (application.js)
22
+
23
+ //= require hammer
24
+
25
+ and then lay down the hammer on https://github.com/EightMedia/hammer.js
26
+
27
+ ## Contributing
28
+
29
+ 1. Fork it ( https://github.com/[my-github-username]/hammerjs_rails/fork )
30
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
31
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
32
+ 4. Push to the branch (`git push origin my-new-feature`)
33
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,2163 @@
1
+ /*! Hammer.JS - v1.1.3 - 2014-05-22
2
+ * http://eightmedia.github.io/hammer.js
3
+ *
4
+ * Copyright (c) 2014 Jorik Tangelder <j.tangelder@gmail.com>;
5
+ * Licensed under the MIT license */
6
+
7
+ (function(window, undefined) {
8
+ 'use strict';
9
+
10
+ /**
11
+ * @main
12
+ * @module hammer
13
+ *
14
+ * @class Hammer
15
+ * @static
16
+ */
17
+
18
+ /**
19
+ * Hammer, use this to create instances
20
+ * ````
21
+ * var hammertime = new Hammer(myElement);
22
+ * ````
23
+ *
24
+ * @method Hammer
25
+ * @param {HTMLElement} element
26
+ * @param {Object} [options={}]
27
+ * @return {Hammer.Instance}
28
+ */
29
+ var Hammer = function Hammer(element, options) {
30
+ return new Hammer.Instance(element, options || {});
31
+ };
32
+
33
+ /**
34
+ * version, as defined in package.json
35
+ * the value will be set at each build
36
+ * @property VERSION
37
+ * @final
38
+ * @type {String}
39
+ */
40
+ Hammer.VERSION = '1.1.3';
41
+
42
+ /**
43
+ * default settings.
44
+ * more settings are defined per gesture at `/gestures`. Each gesture can be disabled/enabled
45
+ * by setting it's name (like `swipe`) to false.
46
+ * You can set the defaults for all instances by changing this object before creating an instance.
47
+ * @example
48
+ * ````
49
+ * Hammer.defaults.drag = false;
50
+ * Hammer.defaults.behavior.touchAction = 'pan-y';
51
+ * delete Hammer.defaults.behavior.userSelect;
52
+ * ````
53
+ * @property defaults
54
+ * @type {Object}
55
+ */
56
+ Hammer.defaults = {
57
+ /**
58
+ * this setting object adds styles and attributes to the element to prevent the browser from doing
59
+ * its native behavior. The css properties are auto prefixed for the browsers when needed.
60
+ * @property defaults.behavior
61
+ * @type {Object}
62
+ */
63
+ behavior: {
64
+ /**
65
+ * Disables text selection to improve the dragging gesture. When the value is `none` it also sets
66
+ * `onselectstart=false` for IE on the element. Mainly for desktop browsers.
67
+ * @property defaults.behavior.userSelect
68
+ * @type {String}
69
+ * @default 'none'
70
+ */
71
+ userSelect: 'none',
72
+
73
+ /**
74
+ * Specifies whether and how a given region can be manipulated by the user (for instance, by panning or zooming).
75
+ * Used by Chrome 35> and IE10>. By default this makes the element blocking any touch event.
76
+ * @property defaults.behavior.touchAction
77
+ * @type {String}
78
+ * @default: 'pan-y'
79
+ */
80
+ touchAction: 'pan-y',
81
+
82
+ /**
83
+ * Disables the default callout shown when you touch and hold a touch target.
84
+ * On iOS, when you touch and hold a touch target such as a link, Safari displays
85
+ * a callout containing information about the link. This property allows you to disable that callout.
86
+ * @property defaults.behavior.touchCallout
87
+ * @type {String}
88
+ * @default 'none'
89
+ */
90
+ touchCallout: 'none',
91
+
92
+ /**
93
+ * Specifies whether zooming is enabled. Used by IE10>
94
+ * @property defaults.behavior.contentZooming
95
+ * @type {String}
96
+ * @default 'none'
97
+ */
98
+ contentZooming: 'none',
99
+
100
+ /**
101
+ * Specifies that an entire element should be draggable instead of its contents.
102
+ * Mainly for desktop browsers.
103
+ * @property defaults.behavior.userDrag
104
+ * @type {String}
105
+ * @default 'none'
106
+ */
107
+ userDrag: 'none',
108
+
109
+ /**
110
+ * Overrides the highlight color shown when the user taps a link or a JavaScript
111
+ * clickable element in Safari on iPhone. This property obeys the alpha value, if specified.
112
+ *
113
+ * If you don't specify an alpha value, Safari on iPhone applies a default alpha value
114
+ * to the color. To disable tap highlighting, set the alpha value to 0 (invisible).
115
+ * If you set the alpha value to 1.0 (opaque), the element is not visible when tapped.
116
+ * @property defaults.behavior.tapHighlightColor
117
+ * @type {String}
118
+ * @default 'rgba(0,0,0,0)'
119
+ */
120
+ tapHighlightColor: 'rgba(0,0,0,0)'
121
+ }
122
+ };
123
+
124
+ /**
125
+ * hammer document where the base events are added at
126
+ * @property DOCUMENT
127
+ * @type {HTMLElement}
128
+ * @default window.document
129
+ */
130
+ Hammer.DOCUMENT = document;
131
+
132
+ /**
133
+ * detect support for pointer events
134
+ * @property HAS_POINTEREVENTS
135
+ * @type {Boolean}
136
+ */
137
+ Hammer.HAS_POINTEREVENTS = navigator.pointerEnabled || navigator.msPointerEnabled;
138
+
139
+ /**
140
+ * detect support for touch events
141
+ * @property HAS_TOUCHEVENTS
142
+ * @type {Boolean}
143
+ */
144
+ Hammer.HAS_TOUCHEVENTS = ('ontouchstart' in window);
145
+
146
+ /**
147
+ * detect mobile browsers
148
+ * @property IS_MOBILE
149
+ * @type {Boolean}
150
+ */
151
+ Hammer.IS_MOBILE = /mobile|tablet|ip(ad|hone|od)|android|silk/i.test(navigator.userAgent);
152
+
153
+ /**
154
+ * detect if we want to support mouseevents at all
155
+ * @property NO_MOUSEEVENTS
156
+ * @type {Boolean}
157
+ */
158
+ Hammer.NO_MOUSEEVENTS = (Hammer.HAS_TOUCHEVENTS && Hammer.IS_MOBILE) || Hammer.HAS_POINTEREVENTS;
159
+
160
+ /**
161
+ * interval in which Hammer recalculates current velocity/direction/angle in ms
162
+ * @property CALCULATE_INTERVAL
163
+ * @type {Number}
164
+ * @default 25
165
+ */
166
+ Hammer.CALCULATE_INTERVAL = 25;
167
+
168
+ /**
169
+ * eventtypes per touchevent (start, move, end) are filled by `Event.determineEventTypes` on `setup`
170
+ * the object contains the DOM event names per type (`EVENT_START`, `EVENT_MOVE`, `EVENT_END`)
171
+ * @property EVENT_TYPES
172
+ * @private
173
+ * @writeOnce
174
+ * @type {Object}
175
+ */
176
+ var EVENT_TYPES = {};
177
+
178
+ /**
179
+ * direction strings, for safe comparisons
180
+ * @property DIRECTION_DOWN|LEFT|UP|RIGHT
181
+ * @final
182
+ * @type {String}
183
+ * @default 'down' 'left' 'up' 'right'
184
+ */
185
+ var DIRECTION_DOWN = Hammer.DIRECTION_DOWN = 'down';
186
+ var DIRECTION_LEFT = Hammer.DIRECTION_LEFT = 'left';
187
+ var DIRECTION_UP = Hammer.DIRECTION_UP = 'up';
188
+ var DIRECTION_RIGHT = Hammer.DIRECTION_RIGHT = 'right';
189
+
190
+ /**
191
+ * pointertype strings, for safe comparisons
192
+ * @property POINTER_MOUSE|TOUCH|PEN
193
+ * @final
194
+ * @type {String}
195
+ * @default 'mouse' 'touch' 'pen'
196
+ */
197
+ var POINTER_MOUSE = Hammer.POINTER_MOUSE = 'mouse';
198
+ var POINTER_TOUCH = Hammer.POINTER_TOUCH = 'touch';
199
+ var POINTER_PEN = Hammer.POINTER_PEN = 'pen';
200
+
201
+ /**
202
+ * eventtypes
203
+ * @property EVENT_START|MOVE|END|RELEASE|TOUCH
204
+ * @final
205
+ * @type {String}
206
+ * @default 'start' 'change' 'move' 'end' 'release' 'touch'
207
+ */
208
+ var EVENT_START = Hammer.EVENT_START = 'start';
209
+ var EVENT_MOVE = Hammer.EVENT_MOVE = 'move';
210
+ var EVENT_END = Hammer.EVENT_END = 'end';
211
+ var EVENT_RELEASE = Hammer.EVENT_RELEASE = 'release';
212
+ var EVENT_TOUCH = Hammer.EVENT_TOUCH = 'touch';
213
+
214
+ /**
215
+ * if the window events are set...
216
+ * @property READY
217
+ * @writeOnce
218
+ * @type {Boolean}
219
+ * @default false
220
+ */
221
+ Hammer.READY = false;
222
+
223
+ /**
224
+ * plugins namespace
225
+ * @property plugins
226
+ * @type {Object}
227
+ */
228
+ Hammer.plugins = Hammer.plugins || {};
229
+
230
+ /**
231
+ * gestures namespace
232
+ * see `/gestures` for the definitions
233
+ * @property gestures
234
+ * @type {Object}
235
+ */
236
+ Hammer.gestures = Hammer.gestures || {};
237
+
238
+ /**
239
+ * setup events to detect gestures on the document
240
+ * this function is called when creating an new instance
241
+ * @private
242
+ */
243
+ function setup() {
244
+ if(Hammer.READY) {
245
+ return;
246
+ }
247
+
248
+ // find what eventtypes we add listeners to
249
+ Event.determineEventTypes();
250
+
251
+ // Register all gestures inside Hammer.gestures
252
+ Utils.each(Hammer.gestures, function(gesture) {
253
+ Detection.register(gesture);
254
+ });
255
+
256
+ // Add touch events on the document
257
+ Event.onTouch(Hammer.DOCUMENT, EVENT_MOVE, Detection.detect);
258
+ Event.onTouch(Hammer.DOCUMENT, EVENT_END, Detection.detect);
259
+
260
+ // Hammer is ready...!
261
+ Hammer.READY = true;
262
+ }
263
+
264
+ /**
265
+ * @module hammer
266
+ *
267
+ * @class Utils
268
+ * @static
269
+ */
270
+ var Utils = Hammer.utils = {
271
+ /**
272
+ * extend method, could also be used for cloning when `dest` is an empty object.
273
+ * changes the dest object
274
+ * @method extend
275
+ * @param {Object} dest
276
+ * @param {Object} src
277
+ * @param {Boolean} [merge=false] do a merge
278
+ * @return {Object} dest
279
+ */
280
+ extend: function extend(dest, src, merge) {
281
+ for(var key in src) {
282
+ if(!src.hasOwnProperty(key) || (dest[key] !== undefined && merge)) {
283
+ continue;
284
+ }
285
+ dest[key] = src[key];
286
+ }
287
+ return dest;
288
+ },
289
+
290
+ /**
291
+ * simple addEventListener wrapper
292
+ * @method on
293
+ * @param {HTMLElement} element
294
+ * @param {String} type
295
+ * @param {Function} handler
296
+ */
297
+ on: function on(element, type, handler) {
298
+ element.addEventListener(type, handler, false);
299
+ },
300
+
301
+ /**
302
+ * simple removeEventListener wrapper
303
+ * @method off
304
+ * @param {HTMLElement} element
305
+ * @param {String} type
306
+ * @param {Function} handler
307
+ */
308
+ off: function off(element, type, handler) {
309
+ element.removeEventListener(type, handler, false);
310
+ },
311
+
312
+ /**
313
+ * forEach over arrays and objects
314
+ * @method each
315
+ * @param {Object|Array} obj
316
+ * @param {Function} iterator
317
+ * @param {any} iterator.item
318
+ * @param {Number} iterator.index
319
+ * @param {Object|Array} iterator.obj the source object
320
+ * @param {Object} context value to use as `this` in the iterator
321
+ */
322
+ each: function each(obj, iterator, context) {
323
+ var i, len;
324
+
325
+ // native forEach on arrays
326
+ if('forEach' in obj) {
327
+ obj.forEach(iterator, context);
328
+ // arrays
329
+ } else if(obj.length !== undefined) {
330
+ for(i = 0, len = obj.length; i < len; i++) {
331
+ if(iterator.call(context, obj[i], i, obj) === false) {
332
+ return;
333
+ }
334
+ }
335
+ // objects
336
+ } else {
337
+ for(i in obj) {
338
+ if(obj.hasOwnProperty(i) &&
339
+ iterator.call(context, obj[i], i, obj) === false) {
340
+ return;
341
+ }
342
+ }
343
+ }
344
+ },
345
+
346
+ /**
347
+ * find if a string contains the string using indexOf
348
+ * @method inStr
349
+ * @param {String} src
350
+ * @param {String} find
351
+ * @return {Boolean} found
352
+ */
353
+ inStr: function inStr(src, find) {
354
+ return src.indexOf(find) > -1;
355
+ },
356
+
357
+ /**
358
+ * find if a array contains the object using indexOf or a simple polyfill
359
+ * @method inArray
360
+ * @param {String} src
361
+ * @param {String} find
362
+ * @return {Boolean|Number} false when not found, or the index
363
+ */
364
+ inArray: function inArray(src, find) {
365
+ if(src.indexOf) {
366
+ var index = src.indexOf(find);
367
+ return (index === -1) ? false : index;
368
+ } else {
369
+ for(var i = 0, len = src.length; i < len; i++) {
370
+ if(src[i] === find) {
371
+ return i;
372
+ }
373
+ }
374
+ return false;
375
+ }
376
+ },
377
+
378
+ /**
379
+ * convert an array-like object (`arguments`, `touchlist`) to an array
380
+ * @method toArray
381
+ * @param {Object} obj
382
+ * @return {Array}
383
+ */
384
+ toArray: function toArray(obj) {
385
+ return Array.prototype.slice.call(obj, 0);
386
+ },
387
+
388
+ /**
389
+ * find if a node is in the given parent
390
+ * @method hasParent
391
+ * @param {HTMLElement} node
392
+ * @param {HTMLElement} parent
393
+ * @return {Boolean} found
394
+ */
395
+ hasParent: function hasParent(node, parent) {
396
+ while(node) {
397
+ if(node == parent) {
398
+ return true;
399
+ }
400
+ node = node.parentNode;
401
+ }
402
+ return false;
403
+ },
404
+
405
+ /**
406
+ * get the center of all the touches
407
+ * @method getCenter
408
+ * @param {Array} touches
409
+ * @return {Object} center contains `pageX`, `pageY`, `clientX` and `clientY` properties
410
+ */
411
+ getCenter: function getCenter(touches) {
412
+ var pageX = [],
413
+ pageY = [],
414
+ clientX = [],
415
+ clientY = [],
416
+ min = Math.min,
417
+ max = Math.max;
418
+
419
+ // no need to loop when only one touch
420
+ if(touches.length === 1) {
421
+ return {
422
+ pageX: touches[0].pageX,
423
+ pageY: touches[0].pageY,
424
+ clientX: touches[0].clientX,
425
+ clientY: touches[0].clientY
426
+ };
427
+ }
428
+
429
+ Utils.each(touches, function(touch) {
430
+ pageX.push(touch.pageX);
431
+ pageY.push(touch.pageY);
432
+ clientX.push(touch.clientX);
433
+ clientY.push(touch.clientY);
434
+ });
435
+
436
+ return {
437
+ pageX: (min.apply(Math, pageX) + max.apply(Math, pageX)) / 2,
438
+ pageY: (min.apply(Math, pageY) + max.apply(Math, pageY)) / 2,
439
+ clientX: (min.apply(Math, clientX) + max.apply(Math, clientX)) / 2,
440
+ clientY: (min.apply(Math, clientY) + max.apply(Math, clientY)) / 2
441
+ };
442
+ },
443
+
444
+ /**
445
+ * calculate the velocity between two points. unit is in px per ms.
446
+ * @method getVelocity
447
+ * @param {Number} deltaTime
448
+ * @param {Number} deltaX
449
+ * @param {Number} deltaY
450
+ * @return {Object} velocity `x` and `y`
451
+ */
452
+ getVelocity: function getVelocity(deltaTime, deltaX, deltaY) {
453
+ return {
454
+ x: Math.abs(deltaX / deltaTime) || 0,
455
+ y: Math.abs(deltaY / deltaTime) || 0
456
+ };
457
+ },
458
+
459
+ /**
460
+ * calculate the angle between two coordinates
461
+ * @method getAngle
462
+ * @param {Touch} touch1
463
+ * @param {Touch} touch2
464
+ * @return {Number} angle
465
+ */
466
+ getAngle: function getAngle(touch1, touch2) {
467
+ var x = touch2.clientX - touch1.clientX,
468
+ y = touch2.clientY - touch1.clientY;
469
+
470
+ return Math.atan2(y, x) * 180 / Math.PI;
471
+ },
472
+
473
+ /**
474
+ * do a small comparision to get the direction between two touches.
475
+ * @method getDirection
476
+ * @param {Touch} touch1
477
+ * @param {Touch} touch2
478
+ * @return {String} direction matches `DIRECTION_LEFT|RIGHT|UP|DOWN`
479
+ */
480
+ getDirection: function getDirection(touch1, touch2) {
481
+ var x = Math.abs(touch1.clientX - touch2.clientX),
482
+ y = Math.abs(touch1.clientY - touch2.clientY);
483
+
484
+ if(x >= y) {
485
+ return touch1.clientX - touch2.clientX > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
486
+ }
487
+ return touch1.clientY - touch2.clientY > 0 ? DIRECTION_UP : DIRECTION_DOWN;
488
+ },
489
+
490
+ /**
491
+ * calculate the distance between two touches
492
+ * @method getDistance
493
+ * @param {Touch}touch1
494
+ * @param {Touch} touch2
495
+ * @return {Number} distance
496
+ */
497
+ getDistance: function getDistance(touch1, touch2) {
498
+ var x = touch2.clientX - touch1.clientX,
499
+ y = touch2.clientY - touch1.clientY;
500
+
501
+ return Math.sqrt((x * x) + (y * y));
502
+ },
503
+
504
+ /**
505
+ * calculate the scale factor between two touchLists
506
+ * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
507
+ * @method getScale
508
+ * @param {Array} start array of touches
509
+ * @param {Array} end array of touches
510
+ * @return {Number} scale
511
+ */
512
+ getScale: function getScale(start, end) {
513
+ // need two fingers...
514
+ if(start.length >= 2 && end.length >= 2) {
515
+ return this.getDistance(end[0], end[1]) / this.getDistance(start[0], start[1]);
516
+ }
517
+ return 1;
518
+ },
519
+
520
+ /**
521
+ * calculate the rotation degrees between two touchLists
522
+ * @method getRotation
523
+ * @param {Array} start array of touches
524
+ * @param {Array} end array of touches
525
+ * @return {Number} rotation
526
+ */
527
+ getRotation: function getRotation(start, end) {
528
+ // need two fingers
529
+ if(start.length >= 2 && end.length >= 2) {
530
+ return this.getAngle(end[1], end[0]) - this.getAngle(start[1], start[0]);
531
+ }
532
+ return 0;
533
+ },
534
+
535
+ /**
536
+ * find out if the direction is vertical *
537
+ * @method isVertical
538
+ * @param {String} direction matches `DIRECTION_UP|DOWN`
539
+ * @return {Boolean} is_vertical
540
+ */
541
+ isVertical: function isVertical(direction) {
542
+ return direction == DIRECTION_UP || direction == DIRECTION_DOWN;
543
+ },
544
+
545
+ /**
546
+ * set css properties with their prefixes
547
+ * @param {HTMLElement} element
548
+ * @param {String} prop
549
+ * @param {String} value
550
+ * @param {Boolean} [toggle=true]
551
+ * @return {Boolean}
552
+ */
553
+ setPrefixedCss: function setPrefixedCss(element, prop, value, toggle) {
554
+ var prefixes = ['', 'Webkit', 'Moz', 'O', 'ms'];
555
+ prop = Utils.toCamelCase(prop);
556
+
557
+ for(var i = 0; i < prefixes.length; i++) {
558
+ var p = prop;
559
+ // prefixes
560
+ if(prefixes[i]) {
561
+ p = prefixes[i] + p.slice(0, 1).toUpperCase() + p.slice(1);
562
+ }
563
+
564
+ // test the style
565
+ if(p in element.style) {
566
+ element.style[p] = (toggle == null || toggle) && value || '';
567
+ break;
568
+ }
569
+ }
570
+ },
571
+
572
+ /**
573
+ * toggle browser default behavior by setting css properties.
574
+ * `userSelect='none'` also sets `element.onselectstart` to false
575
+ * `userDrag='none'` also sets `element.ondragstart` to false
576
+ *
577
+ * @method toggleBehavior
578
+ * @param {HtmlElement} element
579
+ * @param {Object} props
580
+ * @param {Boolean} [toggle=true]
581
+ */
582
+ toggleBehavior: function toggleBehavior(element, props, toggle) {
583
+ if(!props || !element || !element.style) {
584
+ return;
585
+ }
586
+
587
+ // set the css properties
588
+ Utils.each(props, function(value, prop) {
589
+ Utils.setPrefixedCss(element, prop, value, toggle);
590
+ });
591
+
592
+ var falseFn = toggle && function() {
593
+ return false;
594
+ };
595
+
596
+ // also the disable onselectstart
597
+ if(props.userSelect == 'none') {
598
+ element.onselectstart = falseFn;
599
+ }
600
+ // and disable ondragstart
601
+ if(props.userDrag == 'none') {
602
+ element.ondragstart = falseFn;
603
+ }
604
+ },
605
+
606
+ /**
607
+ * convert a string with underscores to camelCase
608
+ * so prevent_default becomes preventDefault
609
+ * @param {String} str
610
+ * @return {String} camelCaseStr
611
+ */
612
+ toCamelCase: function toCamelCase(str) {
613
+ return str.replace(/[_-]([a-z])/g, function(s) {
614
+ return s[1].toUpperCase();
615
+ });
616
+ }
617
+ };
618
+
619
+
620
+ /**
621
+ * @module hammer
622
+ */
623
+ /**
624
+ * @class Event
625
+ * @static
626
+ */
627
+ var Event = Hammer.event = {
628
+ /**
629
+ * when touch events have been fired, this is true
630
+ * this is used to stop mouse events
631
+ * @property prevent_mouseevents
632
+ * @private
633
+ * @type {Boolean}
634
+ */
635
+ preventMouseEvents: false,
636
+
637
+ /**
638
+ * if EVENT_START has been fired
639
+ * @property started
640
+ * @private
641
+ * @type {Boolean}
642
+ */
643
+ started: false,
644
+
645
+ /**
646
+ * when the mouse is hold down, this is true
647
+ * @property should_detect
648
+ * @private
649
+ * @type {Boolean}
650
+ */
651
+ shouldDetect: false,
652
+
653
+ /**
654
+ * simple event binder with a hook and support for multiple types
655
+ * @method on
656
+ * @param {HTMLElement} element
657
+ * @param {String} type
658
+ * @param {Function} handler
659
+ * @param {Function} [hook]
660
+ * @param {Object} hook.type
661
+ */
662
+ on: function on(element, type, handler, hook) {
663
+ var types = type.split(' ');
664
+ Utils.each(types, function(type) {
665
+ Utils.on(element, type, handler);
666
+ hook && hook(type);
667
+ });
668
+ },
669
+
670
+ /**
671
+ * simple event unbinder with a hook and support for multiple types
672
+ * @method off
673
+ * @param {HTMLElement} element
674
+ * @param {String} type
675
+ * @param {Function} handler
676
+ * @param {Function} [hook]
677
+ * @param {Object} hook.type
678
+ */
679
+ off: function off(element, type, handler, hook) {
680
+ var types = type.split(' ');
681
+ Utils.each(types, function(type) {
682
+ Utils.off(element, type, handler);
683
+ hook && hook(type);
684
+ });
685
+ },
686
+
687
+ /**
688
+ * the core touch event handler.
689
+ * this finds out if we should to detect gestures
690
+ * @method onTouch
691
+ * @param {HTMLElement} element
692
+ * @param {String} eventType matches `EVENT_START|MOVE|END`
693
+ * @param {Function} handler
694
+ * @return onTouchHandler {Function} the core event handler
695
+ */
696
+ onTouch: function onTouch(element, eventType, handler) {
697
+ var self = this;
698
+
699
+ var onTouchHandler = function onTouchHandler(ev) {
700
+ var srcType = ev.type.toLowerCase(),
701
+ isPointer = Hammer.HAS_POINTEREVENTS,
702
+ isMouse = Utils.inStr(srcType, 'mouse'),
703
+ triggerType;
704
+
705
+ // if we are in a mouseevent, but there has been a touchevent triggered in this session
706
+ // we want to do nothing. simply break out of the event.
707
+ if(isMouse && self.preventMouseEvents) {
708
+ return;
709
+
710
+ // mousebutton must be down
711
+ } else if(isMouse && eventType == EVENT_START && ev.button === 0) {
712
+ self.preventMouseEvents = false;
713
+ self.shouldDetect = true;
714
+ } else if(isPointer && eventType == EVENT_START) {
715
+ self.shouldDetect = (ev.buttons === 1 || PointerEvent.matchType(POINTER_TOUCH, ev));
716
+ // just a valid start event, but no mouse
717
+ } else if(!isMouse && eventType == EVENT_START) {
718
+ self.preventMouseEvents = true;
719
+ self.shouldDetect = true;
720
+ }
721
+
722
+ // update the pointer event before entering the detection
723
+ if(isPointer && eventType != EVENT_END) {
724
+ PointerEvent.updatePointer(eventType, ev);
725
+ }
726
+
727
+ // we are in a touch/down state, so allowed detection of gestures
728
+ if(self.shouldDetect) {
729
+ triggerType = self.doDetect.call(self, ev, eventType, element, handler);
730
+ }
731
+
732
+ // ...and we are done with the detection
733
+ // so reset everything to start each detection totally fresh
734
+ if(triggerType == EVENT_END) {
735
+ self.preventMouseEvents = false;
736
+ self.shouldDetect = false;
737
+ PointerEvent.reset();
738
+ // update the pointerevent object after the detection
739
+ }
740
+
741
+ if(isPointer && eventType == EVENT_END) {
742
+ PointerEvent.updatePointer(eventType, ev);
743
+ }
744
+ };
745
+
746
+ this.on(element, EVENT_TYPES[eventType], onTouchHandler);
747
+ return onTouchHandler;
748
+ },
749
+
750
+ /**
751
+ * the core detection method
752
+ * this finds out what hammer-touch-events to trigger
753
+ * @method doDetect
754
+ * @param {Object} ev
755
+ * @param {String} eventType matches `EVENT_START|MOVE|END`
756
+ * @param {HTMLElement} element
757
+ * @param {Function} handler
758
+ * @return {String} triggerType matches `EVENT_START|MOVE|END`
759
+ */
760
+ doDetect: function doDetect(ev, eventType, element, handler) {
761
+ var touchList = this.getTouchList(ev, eventType);
762
+ var touchListLength = touchList.length;
763
+ var triggerType = eventType;
764
+ var triggerChange = touchList.trigger; // used by fakeMultitouch plugin
765
+ var changedLength = touchListLength;
766
+
767
+ // at each touchstart-like event we want also want to trigger a TOUCH event...
768
+ if(eventType == EVENT_START) {
769
+ triggerChange = EVENT_TOUCH;
770
+ // ...the same for a touchend-like event
771
+ } else if(eventType == EVENT_END) {
772
+ triggerChange = EVENT_RELEASE;
773
+
774
+ // keep track of how many touches have been removed
775
+ changedLength = touchList.length - ((ev.changedTouches) ? ev.changedTouches.length : 1);
776
+ }
777
+
778
+ // after there are still touches on the screen,
779
+ // we just want to trigger a MOVE event. so change the START or END to a MOVE
780
+ // but only after detection has been started, the first time we actualy want a START
781
+ if(changedLength > 0 && this.started) {
782
+ triggerType = EVENT_MOVE;
783
+ }
784
+
785
+ // detection has been started, we keep track of this, see above
786
+ this.started = true;
787
+
788
+ // generate some event data, some basic information
789
+ var evData = this.collectEventData(element, triggerType, touchList, ev);
790
+
791
+ // trigger the triggerType event before the change (TOUCH, RELEASE) events
792
+ // but the END event should be at last
793
+ if(eventType != EVENT_END) {
794
+ handler.call(Detection, evData);
795
+ }
796
+
797
+ // trigger a change (TOUCH, RELEASE) event, this means the length of the touches changed
798
+ if(triggerChange) {
799
+ evData.changedLength = changedLength;
800
+ evData.eventType = triggerChange;
801
+
802
+ handler.call(Detection, evData);
803
+
804
+ evData.eventType = triggerType;
805
+ delete evData.changedLength;
806
+ }
807
+
808
+ // trigger the END event
809
+ if(triggerType == EVENT_END) {
810
+ handler.call(Detection, evData);
811
+
812
+ // ...and we are done with the detection
813
+ // so reset everything to start each detection totally fresh
814
+ this.started = false;
815
+ }
816
+
817
+ return triggerType;
818
+ },
819
+
820
+ /**
821
+ * we have different events for each device/browser
822
+ * determine what we need and set them in the EVENT_TYPES constant
823
+ * the `onTouch` method is bind to these properties.
824
+ * @method determineEventTypes
825
+ * @return {Object} events
826
+ */
827
+ determineEventTypes: function determineEventTypes() {
828
+ var types;
829
+ if(Hammer.HAS_POINTEREVENTS) {
830
+ if(window.PointerEvent) {
831
+ types = [
832
+ 'pointerdown',
833
+ 'pointermove',
834
+ 'pointerup pointercancel lostpointercapture'
835
+ ];
836
+ } else {
837
+ types = [
838
+ 'MSPointerDown',
839
+ 'MSPointerMove',
840
+ 'MSPointerUp MSPointerCancel MSLostPointerCapture'
841
+ ];
842
+ }
843
+ } else if(Hammer.NO_MOUSEEVENTS) {
844
+ types = [
845
+ 'touchstart',
846
+ 'touchmove',
847
+ 'touchend touchcancel'
848
+ ];
849
+ } else {
850
+ types = [
851
+ 'touchstart mousedown',
852
+ 'touchmove mousemove',
853
+ 'touchend touchcancel mouseup'
854
+ ];
855
+ }
856
+
857
+ EVENT_TYPES[EVENT_START] = types[0];
858
+ EVENT_TYPES[EVENT_MOVE] = types[1];
859
+ EVENT_TYPES[EVENT_END] = types[2];
860
+ return EVENT_TYPES;
861
+ },
862
+
863
+ /**
864
+ * create touchList depending on the event
865
+ * @method getTouchList
866
+ * @param {Object} ev
867
+ * @param {String} eventType
868
+ * @return {Array} touches
869
+ */
870
+ getTouchList: function getTouchList(ev, eventType) {
871
+ // get the fake pointerEvent touchlist
872
+ if(Hammer.HAS_POINTEREVENTS) {
873
+ return PointerEvent.getTouchList();
874
+ }
875
+
876
+ // get the touchlist
877
+ if(ev.touches) {
878
+ if(eventType == EVENT_MOVE) {
879
+ return ev.touches;
880
+ }
881
+
882
+ var identifiers = [];
883
+ var concat = [].concat(Utils.toArray(ev.touches), Utils.toArray(ev.changedTouches));
884
+ var touchList = [];
885
+
886
+ Utils.each(concat, function(touch) {
887
+ if(Utils.inArray(identifiers, touch.identifier) === false) {
888
+ touchList.push(touch);
889
+ }
890
+ identifiers.push(touch.identifier);
891
+ });
892
+
893
+ return touchList;
894
+ }
895
+
896
+ // make fake touchList from mouse position
897
+ ev.identifier = 1;
898
+ return [ev];
899
+ },
900
+
901
+ /**
902
+ * collect basic event data
903
+ * @method collectEventData
904
+ * @param {HTMLElement} element
905
+ * @param {String} eventType matches `EVENT_START|MOVE|END`
906
+ * @param {Array} touches
907
+ * @param {Object} ev
908
+ * @return {Object} ev
909
+ */
910
+ collectEventData: function collectEventData(element, eventType, touches, ev) {
911
+ // find out pointerType
912
+ var pointerType = POINTER_TOUCH;
913
+ if(Utils.inStr(ev.type, 'mouse') || PointerEvent.matchType(POINTER_MOUSE, ev)) {
914
+ pointerType = POINTER_MOUSE;
915
+ } else if(PointerEvent.matchType(POINTER_PEN, ev)) {
916
+ pointerType = POINTER_PEN;
917
+ }
918
+
919
+ return {
920
+ center: Utils.getCenter(touches),
921
+ timeStamp: Date.now(),
922
+ target: ev.target,
923
+ touches: touches,
924
+ eventType: eventType,
925
+ pointerType: pointerType,
926
+ srcEvent: ev,
927
+
928
+ /**
929
+ * prevent the browser default actions
930
+ * mostly used to disable scrolling of the browser
931
+ */
932
+ preventDefault: function() {
933
+ var srcEvent = this.srcEvent;
934
+ srcEvent.preventManipulation && srcEvent.preventManipulation();
935
+ srcEvent.preventDefault && srcEvent.preventDefault();
936
+ },
937
+
938
+ /**
939
+ * stop bubbling the event up to its parents
940
+ */
941
+ stopPropagation: function() {
942
+ this.srcEvent.stopPropagation();
943
+ },
944
+
945
+ /**
946
+ * immediately stop gesture detection
947
+ * might be useful after a swipe was detected
948
+ * @return {*}
949
+ */
950
+ stopDetect: function() {
951
+ return Detection.stopDetect();
952
+ }
953
+ };
954
+ }
955
+ };
956
+
957
+
958
+ /**
959
+ * @module hammer
960
+ *
961
+ * @class PointerEvent
962
+ * @static
963
+ */
964
+ var PointerEvent = Hammer.PointerEvent = {
965
+ /**
966
+ * holds all pointers, by `identifier`
967
+ * @property pointers
968
+ * @type {Object}
969
+ */
970
+ pointers: {},
971
+
972
+ /**
973
+ * get the pointers as an array
974
+ * @method getTouchList
975
+ * @return {Array} touchlist
976
+ */
977
+ getTouchList: function getTouchList() {
978
+ var touchlist = [];
979
+ // we can use forEach since pointerEvents only is in IE10
980
+ Utils.each(this.pointers, function(pointer) {
981
+ touchlist.push(pointer);
982
+ });
983
+
984
+ return touchlist;
985
+ },
986
+
987
+ /**
988
+ * update the position of a pointer
989
+ * @method updatePointer
990
+ * @param {String} eventType matches `EVENT_START|MOVE|END`
991
+ * @param {Object} pointerEvent
992
+ */
993
+ updatePointer: function updatePointer(eventType, pointerEvent) {
994
+ if(eventType == EVENT_END) {
995
+ delete this.pointers[pointerEvent.pointerId];
996
+ } else {
997
+ pointerEvent.identifier = pointerEvent.pointerId;
998
+ this.pointers[pointerEvent.pointerId] = pointerEvent;
999
+ }
1000
+ },
1001
+
1002
+ /**
1003
+ * check if ev matches pointertype
1004
+ * @method matchType
1005
+ * @param {String} pointerType matches `POINTER_MOUSE|TOUCH|PEN`
1006
+ * @param {PointerEvent} ev
1007
+ */
1008
+ matchType: function matchType(pointerType, ev) {
1009
+ if(!ev.pointerType) {
1010
+ return false;
1011
+ }
1012
+
1013
+ var pt = ev.pointerType,
1014
+ types = {};
1015
+
1016
+ types[POINTER_MOUSE] = (pt === (ev.MSPOINTER_TYPE_MOUSE || POINTER_MOUSE));
1017
+ types[POINTER_TOUCH] = (pt === (ev.MSPOINTER_TYPE_TOUCH || POINTER_TOUCH));
1018
+ types[POINTER_PEN] = (pt === (ev.MSPOINTER_TYPE_PEN || POINTER_PEN));
1019
+ return types[pointerType];
1020
+ },
1021
+
1022
+ /**
1023
+ * reset the stored pointers
1024
+ * @method reset
1025
+ */
1026
+ reset: function resetList() {
1027
+ this.pointers = {};
1028
+ }
1029
+ };
1030
+
1031
+
1032
+ /**
1033
+ * @module hammer
1034
+ *
1035
+ * @class Detection
1036
+ * @static
1037
+ */
1038
+ var Detection = Hammer.detection = {
1039
+ // contains all registred Hammer.gestures in the correct order
1040
+ gestures: [],
1041
+
1042
+ // data of the current Hammer.gesture detection session
1043
+ current: null,
1044
+
1045
+ // the previous Hammer.gesture session data
1046
+ // is a full clone of the previous gesture.current object
1047
+ previous: null,
1048
+
1049
+ // when this becomes true, no gestures are fired
1050
+ stopped: false,
1051
+
1052
+ /**
1053
+ * start Hammer.gesture detection
1054
+ * @method startDetect
1055
+ * @param {Hammer.Instance} inst
1056
+ * @param {Object} eventData
1057
+ */
1058
+ startDetect: function startDetect(inst, eventData) {
1059
+ // already busy with a Hammer.gesture detection on an element
1060
+ if(this.current) {
1061
+ return;
1062
+ }
1063
+
1064
+ this.stopped = false;
1065
+
1066
+ // holds current session
1067
+ this.current = {
1068
+ inst: inst, // reference to HammerInstance we're working for
1069
+ startEvent: Utils.extend({}, eventData), // start eventData for distances, timing etc
1070
+ lastEvent: false, // last eventData
1071
+ lastCalcEvent: false, // last eventData for calculations.
1072
+ futureCalcEvent: false, // last eventData for calculations.
1073
+ lastCalcData: {}, // last lastCalcData
1074
+ name: '' // current gesture we're in/detected, can be 'tap', 'hold' etc
1075
+ };
1076
+
1077
+ this.detect(eventData);
1078
+ },
1079
+
1080
+ /**
1081
+ * Hammer.gesture detection
1082
+ * @method detect
1083
+ * @param {Object} eventData
1084
+ * @return {any}
1085
+ */
1086
+ detect: function detect(eventData) {
1087
+ if(!this.current || this.stopped) {
1088
+ return;
1089
+ }
1090
+
1091
+ // extend event data with calculations about scale, distance etc
1092
+ eventData = this.extendEventData(eventData);
1093
+
1094
+ // hammer instance and instance options
1095
+ var inst = this.current.inst,
1096
+ instOptions = inst.options;
1097
+
1098
+ // call Hammer.gesture handlers
1099
+ Utils.each(this.gestures, function triggerGesture(gesture) {
1100
+ // only when the instance options have enabled this gesture
1101
+ if(!this.stopped && inst.enabled && instOptions[gesture.name]) {
1102
+ gesture.handler.call(gesture, eventData, inst);
1103
+ }
1104
+ }, this);
1105
+
1106
+ // store as previous event event
1107
+ if(this.current) {
1108
+ this.current.lastEvent = eventData;
1109
+ }
1110
+
1111
+ if(eventData.eventType == EVENT_END) {
1112
+ this.stopDetect();
1113
+ }
1114
+
1115
+ return eventData;
1116
+ },
1117
+
1118
+ /**
1119
+ * clear the Hammer.gesture vars
1120
+ * this is called on endDetect, but can also be used when a final Hammer.gesture has been detected
1121
+ * to stop other Hammer.gestures from being fired
1122
+ * @method stopDetect
1123
+ */
1124
+ stopDetect: function stopDetect() {
1125
+ // clone current data to the store as the previous gesture
1126
+ // used for the double tap gesture, since this is an other gesture detect session
1127
+ this.previous = Utils.extend({}, this.current);
1128
+
1129
+ // reset the current
1130
+ this.current = null;
1131
+ this.stopped = true;
1132
+ },
1133
+
1134
+ /**
1135
+ * calculate velocity, angle and direction
1136
+ * @method getVelocityData
1137
+ * @param {Object} ev
1138
+ * @param {Object} center
1139
+ * @param {Number} deltaTime
1140
+ * @param {Number} deltaX
1141
+ * @param {Number} deltaY
1142
+ */
1143
+ getCalculatedData: function getCalculatedData(ev, center, deltaTime, deltaX, deltaY) {
1144
+ var cur = this.current,
1145
+ recalc = false,
1146
+ calcEv = cur.lastCalcEvent,
1147
+ calcData = cur.lastCalcData;
1148
+
1149
+ if(calcEv && ev.timeStamp - calcEv.timeStamp > Hammer.CALCULATE_INTERVAL) {
1150
+ center = calcEv.center;
1151
+ deltaTime = ev.timeStamp - calcEv.timeStamp;
1152
+ deltaX = ev.center.clientX - calcEv.center.clientX;
1153
+ deltaY = ev.center.clientY - calcEv.center.clientY;
1154
+ recalc = true;
1155
+ }
1156
+
1157
+ if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) {
1158
+ cur.futureCalcEvent = ev;
1159
+ }
1160
+
1161
+ if(!cur.lastCalcEvent || recalc) {
1162
+ calcData.velocity = Utils.getVelocity(deltaTime, deltaX, deltaY);
1163
+ calcData.angle = Utils.getAngle(center, ev.center);
1164
+ calcData.direction = Utils.getDirection(center, ev.center);
1165
+
1166
+ cur.lastCalcEvent = cur.futureCalcEvent || ev;
1167
+ cur.futureCalcEvent = ev;
1168
+ }
1169
+
1170
+ ev.velocityX = calcData.velocity.x;
1171
+ ev.velocityY = calcData.velocity.y;
1172
+ ev.interimAngle = calcData.angle;
1173
+ ev.interimDirection = calcData.direction;
1174
+ },
1175
+
1176
+ /**
1177
+ * extend eventData for Hammer.gestures
1178
+ * @method extendEventData
1179
+ * @param {Object} ev
1180
+ * @return {Object} ev
1181
+ */
1182
+ extendEventData: function extendEventData(ev) {
1183
+ var cur = this.current,
1184
+ startEv = cur.startEvent,
1185
+ lastEv = cur.lastEvent || startEv;
1186
+
1187
+ // update the start touchlist to calculate the scale/rotation
1188
+ if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) {
1189
+ startEv.touches = [];
1190
+ Utils.each(ev.touches, function(touch) {
1191
+ startEv.touches.push({
1192
+ clientX: touch.clientX,
1193
+ clientY: touch.clientY
1194
+ });
1195
+ });
1196
+ }
1197
+
1198
+ var deltaTime = ev.timeStamp - startEv.timeStamp,
1199
+ deltaX = ev.center.clientX - startEv.center.clientX,
1200
+ deltaY = ev.center.clientY - startEv.center.clientY;
1201
+
1202
+ this.getCalculatedData(ev, lastEv.center, deltaTime, deltaX, deltaY);
1203
+
1204
+ Utils.extend(ev, {
1205
+ startEvent: startEv,
1206
+
1207
+ deltaTime: deltaTime,
1208
+ deltaX: deltaX,
1209
+ deltaY: deltaY,
1210
+
1211
+ distance: Utils.getDistance(startEv.center, ev.center),
1212
+ angle: Utils.getAngle(startEv.center, ev.center),
1213
+ direction: Utils.getDirection(startEv.center, ev.center),
1214
+ scale: Utils.getScale(startEv.touches, ev.touches),
1215
+ rotation: Utils.getRotation(startEv.touches, ev.touches)
1216
+ });
1217
+
1218
+ return ev;
1219
+ },
1220
+
1221
+ /**
1222
+ * register new gesture
1223
+ * @method register
1224
+ * @param {Object} gesture object, see `gestures/` for documentation
1225
+ * @return {Array} gestures
1226
+ */
1227
+ register: function register(gesture) {
1228
+ // add an enable gesture options if there is no given
1229
+ var options = gesture.defaults || {};
1230
+ if(options[gesture.name] === undefined) {
1231
+ options[gesture.name] = true;
1232
+ }
1233
+
1234
+ // extend Hammer default options with the Hammer.gesture options
1235
+ Utils.extend(Hammer.defaults, options, true);
1236
+
1237
+ // set its index
1238
+ gesture.index = gesture.index || 1000;
1239
+
1240
+ // add Hammer.gesture to the list
1241
+ this.gestures.push(gesture);
1242
+
1243
+ // sort the list by index
1244
+ this.gestures.sort(function(a, b) {
1245
+ if(a.index < b.index) {
1246
+ return -1;
1247
+ }
1248
+ if(a.index > b.index) {
1249
+ return 1;
1250
+ }
1251
+ return 0;
1252
+ });
1253
+
1254
+ return this.gestures;
1255
+ }
1256
+ };
1257
+
1258
+
1259
+ /**
1260
+ * @module hammer
1261
+ */
1262
+
1263
+ /**
1264
+ * create new hammer instance
1265
+ * all methods should return the instance itself, so it is chainable.
1266
+ *
1267
+ * @class Instance
1268
+ * @constructor
1269
+ * @param {HTMLElement} element
1270
+ * @param {Object} [options={}] options are merged with `Hammer.defaults`
1271
+ * @return {Hammer.Instance}
1272
+ */
1273
+ Hammer.Instance = function(element, options) {
1274
+ var self = this;
1275
+
1276
+ // setup HammerJS window events and register all gestures
1277
+ // this also sets up the default options
1278
+ setup();
1279
+
1280
+ /**
1281
+ * @property element
1282
+ * @type {HTMLElement}
1283
+ */
1284
+ this.element = element;
1285
+
1286
+ /**
1287
+ * @property enabled
1288
+ * @type {Boolean}
1289
+ * @protected
1290
+ */
1291
+ this.enabled = true;
1292
+
1293
+ /**
1294
+ * options, merged with the defaults
1295
+ * options with an _ are converted to camelCase
1296
+ * @property options
1297
+ * @type {Object}
1298
+ */
1299
+ Utils.each(options, function(value, name) {
1300
+ delete options[name];
1301
+ options[Utils.toCamelCase(name)] = value;
1302
+ });
1303
+
1304
+ this.options = Utils.extend(Utils.extend({}, Hammer.defaults), options || {});
1305
+
1306
+ // add some css to the element to prevent the browser from doing its native behavoir
1307
+ if(this.options.behavior) {
1308
+ Utils.toggleBehavior(this.element, this.options.behavior, true);
1309
+ }
1310
+
1311
+ /**
1312
+ * event start handler on the element to start the detection
1313
+ * @property eventStartHandler
1314
+ * @type {Object}
1315
+ */
1316
+ this.eventStartHandler = Event.onTouch(element, EVENT_START, function(ev) {
1317
+ if(self.enabled && ev.eventType == EVENT_START) {
1318
+ Detection.startDetect(self, ev);
1319
+ } else if(ev.eventType == EVENT_TOUCH) {
1320
+ Detection.detect(ev);
1321
+ }
1322
+ });
1323
+
1324
+ /**
1325
+ * keep a list of user event handlers which needs to be removed when calling 'dispose'
1326
+ * @property eventHandlers
1327
+ * @type {Array}
1328
+ */
1329
+ this.eventHandlers = [];
1330
+ };
1331
+
1332
+ Hammer.Instance.prototype = {
1333
+ /**
1334
+ * bind events to the instance
1335
+ * @method on
1336
+ * @chainable
1337
+ * @param {String} gestures multiple gestures by splitting with a space
1338
+ * @param {Function} handler
1339
+ * @param {Object} handler.ev event object
1340
+ */
1341
+ on: function onEvent(gestures, handler) {
1342
+ var self = this;
1343
+ Event.on(self.element, gestures, handler, function(type) {
1344
+ self.eventHandlers.push({ gesture: type, handler: handler });
1345
+ });
1346
+ return self;
1347
+ },
1348
+
1349
+ /**
1350
+ * unbind events to the instance
1351
+ * @method off
1352
+ * @chainable
1353
+ * @param {String} gestures
1354
+ * @param {Function} handler
1355
+ */
1356
+ off: function offEvent(gestures, handler) {
1357
+ var self = this;
1358
+
1359
+ Event.off(self.element, gestures, handler, function(type) {
1360
+ var index = Utils.inArray({ gesture: type, handler: handler });
1361
+ if(index !== false) {
1362
+ self.eventHandlers.splice(index, 1);
1363
+ }
1364
+ });
1365
+ return self;
1366
+ },
1367
+
1368
+ /**
1369
+ * trigger gesture event
1370
+ * @method trigger
1371
+ * @chainable
1372
+ * @param {String} gesture
1373
+ * @param {Object} [eventData]
1374
+ */
1375
+ trigger: function triggerEvent(gesture, eventData) {
1376
+ // optional
1377
+ if(!eventData) {
1378
+ eventData = {};
1379
+ }
1380
+
1381
+ // create DOM event
1382
+ var event = Hammer.DOCUMENT.createEvent('Event');
1383
+ event.initEvent(gesture, true, true);
1384
+ event.gesture = eventData;
1385
+
1386
+ // trigger on the target if it is in the instance element,
1387
+ // this is for event delegation tricks
1388
+ var element = this.element;
1389
+ if(Utils.hasParent(eventData.target, element)) {
1390
+ element = eventData.target;
1391
+ }
1392
+
1393
+ element.dispatchEvent(event);
1394
+ return this;
1395
+ },
1396
+
1397
+ /**
1398
+ * enable of disable hammer.js detection
1399
+ * @method enable
1400
+ * @chainable
1401
+ * @param {Boolean} state
1402
+ */
1403
+ enable: function enable(state) {
1404
+ this.enabled = state;
1405
+ return this;
1406
+ },
1407
+
1408
+ /**
1409
+ * dispose this hammer instance
1410
+ * @method dispose
1411
+ * @return {Null}
1412
+ */
1413
+ dispose: function dispose() {
1414
+ var i, eh;
1415
+
1416
+ // undo all changes made by stop_browser_behavior
1417
+ Utils.toggleBehavior(this.element, this.options.behavior, false);
1418
+
1419
+ // unbind all custom event handlers
1420
+ for(i = -1; (eh = this.eventHandlers[++i]);) {
1421
+ Utils.off(this.element, eh.gesture, eh.handler);
1422
+ }
1423
+
1424
+ this.eventHandlers = [];
1425
+
1426
+ // unbind the start event listener
1427
+ Event.off(this.element, EVENT_TYPES[EVENT_START], this.eventStartHandler);
1428
+
1429
+ return null;
1430
+ }
1431
+ };
1432
+
1433
+
1434
+ /**
1435
+ * @module gestures
1436
+ */
1437
+ /**
1438
+ * Move with x fingers (default 1) around on the page.
1439
+ * Preventing the default browser behavior is a good way to improve feel and working.
1440
+ * ````
1441
+ * hammertime.on("drag", function(ev) {
1442
+ * console.log(ev);
1443
+ * ev.gesture.preventDefault();
1444
+ * });
1445
+ * ````
1446
+ *
1447
+ * @class Drag
1448
+ * @static
1449
+ */
1450
+ /**
1451
+ * @event drag
1452
+ * @param {Object} ev
1453
+ */
1454
+ /**
1455
+ * @event dragstart
1456
+ * @param {Object} ev
1457
+ */
1458
+ /**
1459
+ * @event dragend
1460
+ * @param {Object} ev
1461
+ */
1462
+ /**
1463
+ * @event drapleft
1464
+ * @param {Object} ev
1465
+ */
1466
+ /**
1467
+ * @event dragright
1468
+ * @param {Object} ev
1469
+ */
1470
+ /**
1471
+ * @event dragup
1472
+ * @param {Object} ev
1473
+ */
1474
+ /**
1475
+ * @event dragdown
1476
+ * @param {Object} ev
1477
+ */
1478
+
1479
+ /**
1480
+ * @param {String} name
1481
+ */
1482
+ (function(name) {
1483
+ var triggered = false;
1484
+
1485
+ function dragGesture(ev, inst) {
1486
+ var cur = Detection.current;
1487
+
1488
+ // max touches
1489
+ if(inst.options.dragMaxTouches > 0 &&
1490
+ ev.touches.length > inst.options.dragMaxTouches) {
1491
+ return;
1492
+ }
1493
+
1494
+ switch(ev.eventType) {
1495
+ case EVENT_START:
1496
+ triggered = false;
1497
+ break;
1498
+
1499
+ case EVENT_MOVE:
1500
+ // when the distance we moved is too small we skip this gesture
1501
+ // or we can be already in dragging
1502
+ if(ev.distance < inst.options.dragMinDistance &&
1503
+ cur.name != name) {
1504
+ return;
1505
+ }
1506
+
1507
+ var startCenter = cur.startEvent.center;
1508
+
1509
+ // we are dragging!
1510
+ if(cur.name != name) {
1511
+ cur.name = name;
1512
+ if(inst.options.dragDistanceCorrection && ev.distance > 0) {
1513
+ // When a drag is triggered, set the event center to dragMinDistance pixels from the original event center.
1514
+ // Without this correction, the dragged distance would jumpstart at dragMinDistance pixels instead of at 0.
1515
+ // It might be useful to save the original start point somewhere
1516
+ var factor = Math.abs(inst.options.dragMinDistance / ev.distance);
1517
+ startCenter.pageX += ev.deltaX * factor;
1518
+ startCenter.pageY += ev.deltaY * factor;
1519
+ startCenter.clientX += ev.deltaX * factor;
1520
+ startCenter.clientY += ev.deltaY * factor;
1521
+
1522
+ // recalculate event data using new start point
1523
+ ev = Detection.extendEventData(ev);
1524
+ }
1525
+ }
1526
+
1527
+ // lock drag to axis?
1528
+ if(cur.lastEvent.dragLockToAxis ||
1529
+ ( inst.options.dragLockToAxis &&
1530
+ inst.options.dragLockMinDistance <= ev.distance
1531
+ )) {
1532
+ ev.dragLockToAxis = true;
1533
+ }
1534
+
1535
+ // keep direction on the axis that the drag gesture started on
1536
+ var lastDirection = cur.lastEvent.direction;
1537
+ if(ev.dragLockToAxis && lastDirection !== ev.direction) {
1538
+ if(Utils.isVertical(lastDirection)) {
1539
+ ev.direction = (ev.deltaY < 0) ? DIRECTION_UP : DIRECTION_DOWN;
1540
+ } else {
1541
+ ev.direction = (ev.deltaX < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;
1542
+ }
1543
+ }
1544
+
1545
+ // first time, trigger dragstart event
1546
+ if(!triggered) {
1547
+ inst.trigger(name + 'start', ev);
1548
+ triggered = true;
1549
+ }
1550
+
1551
+ // trigger events
1552
+ inst.trigger(name, ev);
1553
+ inst.trigger(name + ev.direction, ev);
1554
+
1555
+ var isVertical = Utils.isVertical(ev.direction);
1556
+
1557
+ // block the browser events
1558
+ if((inst.options.dragBlockVertical && isVertical) ||
1559
+ (inst.options.dragBlockHorizontal && !isVertical)) {
1560
+ ev.preventDefault();
1561
+ }
1562
+ break;
1563
+
1564
+ case EVENT_RELEASE:
1565
+ if(triggered && ev.changedLength <= inst.options.dragMaxTouches) {
1566
+ inst.trigger(name + 'end', ev);
1567
+ triggered = false;
1568
+ }
1569
+ break;
1570
+
1571
+ case EVENT_END:
1572
+ triggered = false;
1573
+ break;
1574
+ }
1575
+ }
1576
+
1577
+ Hammer.gestures.Drag = {
1578
+ name: name,
1579
+ index: 50,
1580
+ handler: dragGesture,
1581
+ defaults: {
1582
+ /**
1583
+ * minimal movement that have to be made before the drag event gets triggered
1584
+ * @property dragMinDistance
1585
+ * @type {Number}
1586
+ * @default 10
1587
+ */
1588
+ dragMinDistance: 10,
1589
+
1590
+ /**
1591
+ * Set dragDistanceCorrection to true to make the starting point of the drag
1592
+ * be calculated from where the drag was triggered, not from where the touch started.
1593
+ * Useful to avoid a jerk-starting drag, which can make fine-adjustments
1594
+ * through dragging difficult, and be visually unappealing.
1595
+ * @property dragDistanceCorrection
1596
+ * @type {Boolean}
1597
+ * @default true
1598
+ */
1599
+ dragDistanceCorrection: true,
1600
+
1601
+ /**
1602
+ * set 0 for unlimited, but this can conflict with transform
1603
+ * @property dragMaxTouches
1604
+ * @type {Number}
1605
+ * @default 1
1606
+ */
1607
+ dragMaxTouches: 1,
1608
+
1609
+ /**
1610
+ * prevent default browser behavior when dragging occurs
1611
+ * be careful with it, it makes the element a blocking element
1612
+ * when you are using the drag gesture, it is a good practice to set this true
1613
+ * @property dragBlockHorizontal
1614
+ * @type {Boolean}
1615
+ * @default false
1616
+ */
1617
+ dragBlockHorizontal: false,
1618
+
1619
+ /**
1620
+ * same as `dragBlockHorizontal`, but for vertical movement
1621
+ * @property dragBlockVertical
1622
+ * @type {Boolean}
1623
+ * @default false
1624
+ */
1625
+ dragBlockVertical: false,
1626
+
1627
+ /**
1628
+ * dragLockToAxis keeps the drag gesture on the axis that it started on,
1629
+ * It disallows vertical directions if the initial direction was horizontal, and vice versa.
1630
+ * @property dragLockToAxis
1631
+ * @type {Boolean}
1632
+ * @default false
1633
+ */
1634
+ dragLockToAxis: false,
1635
+
1636
+ /**
1637
+ * drag lock only kicks in when distance > dragLockMinDistance
1638
+ * This way, locking occurs only when the distance has become large enough to reliably determine the direction
1639
+ * @property dragLockMinDistance
1640
+ * @type {Number}
1641
+ * @default 25
1642
+ */
1643
+ dragLockMinDistance: 25
1644
+ }
1645
+ };
1646
+ })('drag');
1647
+
1648
+ /**
1649
+ * @module gestures
1650
+ */
1651
+ /**
1652
+ * trigger a simple gesture event, so you can do anything in your handler.
1653
+ * only usable if you know what your doing...
1654
+ *
1655
+ * @class Gesture
1656
+ * @static
1657
+ */
1658
+ /**
1659
+ * @event gesture
1660
+ * @param {Object} ev
1661
+ */
1662
+ Hammer.gestures.Gesture = {
1663
+ name: 'gesture',
1664
+ index: 1337,
1665
+ handler: function releaseGesture(ev, inst) {
1666
+ inst.trigger(this.name, ev);
1667
+ }
1668
+ };
1669
+
1670
+ /**
1671
+ * @module gestures
1672
+ */
1673
+ /**
1674
+ * Touch stays at the same place for x time
1675
+ *
1676
+ * @class Hold
1677
+ * @static
1678
+ */
1679
+ /**
1680
+ * @event hold
1681
+ * @param {Object} ev
1682
+ */
1683
+
1684
+ /**
1685
+ * @param {String} name
1686
+ */
1687
+ (function(name) {
1688
+ var timer;
1689
+
1690
+ function holdGesture(ev, inst) {
1691
+ var options = inst.options,
1692
+ current = Detection.current;
1693
+
1694
+ switch(ev.eventType) {
1695
+ case EVENT_START:
1696
+ clearTimeout(timer);
1697
+
1698
+ // set the gesture so we can check in the timeout if it still is
1699
+ current.name = name;
1700
+
1701
+ // set timer and if after the timeout it still is hold,
1702
+ // we trigger the hold event
1703
+ timer = setTimeout(function() {
1704
+ if(current && current.name == name) {
1705
+ inst.trigger(name, ev);
1706
+ }
1707
+ }, options.holdTimeout);
1708
+ break;
1709
+
1710
+ case EVENT_MOVE:
1711
+ if(ev.distance > options.holdThreshold) {
1712
+ clearTimeout(timer);
1713
+ }
1714
+ break;
1715
+
1716
+ case EVENT_RELEASE:
1717
+ clearTimeout(timer);
1718
+ break;
1719
+ }
1720
+ }
1721
+
1722
+ Hammer.gestures.Hold = {
1723
+ name: name,
1724
+ index: 10,
1725
+ defaults: {
1726
+ /**
1727
+ * @property holdTimeout
1728
+ * @type {Number}
1729
+ * @default 500
1730
+ */
1731
+ holdTimeout: 500,
1732
+
1733
+ /**
1734
+ * movement allowed while holding
1735
+ * @property holdThreshold
1736
+ * @type {Number}
1737
+ * @default 2
1738
+ */
1739
+ holdThreshold: 2
1740
+ },
1741
+ handler: holdGesture
1742
+ };
1743
+ })('hold');
1744
+
1745
+ /**
1746
+ * @module gestures
1747
+ */
1748
+ /**
1749
+ * when a touch is being released from the page
1750
+ *
1751
+ * @class Release
1752
+ * @static
1753
+ */
1754
+ /**
1755
+ * @event release
1756
+ * @param {Object} ev
1757
+ */
1758
+ Hammer.gestures.Release = {
1759
+ name: 'release',
1760
+ index: Infinity,
1761
+ handler: function releaseGesture(ev, inst) {
1762
+ if(ev.eventType == EVENT_RELEASE) {
1763
+ inst.trigger(this.name, ev);
1764
+ }
1765
+ }
1766
+ };
1767
+
1768
+ /**
1769
+ * @module gestures
1770
+ */
1771
+ /**
1772
+ * triggers swipe events when the end velocity is above the threshold
1773
+ * for best usage, set `preventDefault` (on the drag gesture) to `true`
1774
+ * ````
1775
+ * hammertime.on("dragleft swipeleft", function(ev) {
1776
+ * console.log(ev);
1777
+ * ev.gesture.preventDefault();
1778
+ * });
1779
+ * ````
1780
+ *
1781
+ * @class Swipe
1782
+ * @static
1783
+ */
1784
+ /**
1785
+ * @event swipe
1786
+ * @param {Object} ev
1787
+ */
1788
+ /**
1789
+ * @event swipeleft
1790
+ * @param {Object} ev
1791
+ */
1792
+ /**
1793
+ * @event swiperight
1794
+ * @param {Object} ev
1795
+ */
1796
+ /**
1797
+ * @event swipeup
1798
+ * @param {Object} ev
1799
+ */
1800
+ /**
1801
+ * @event swipedown
1802
+ * @param {Object} ev
1803
+ */
1804
+ Hammer.gestures.Swipe = {
1805
+ name: 'swipe',
1806
+ index: 40,
1807
+ defaults: {
1808
+ /**
1809
+ * @property swipeMinTouches
1810
+ * @type {Number}
1811
+ * @default 1
1812
+ */
1813
+ swipeMinTouches: 1,
1814
+
1815
+ /**
1816
+ * @property swipeMaxTouches
1817
+ * @type {Number}
1818
+ * @default 1
1819
+ */
1820
+ swipeMaxTouches: 1,
1821
+
1822
+ /**
1823
+ * horizontal swipe velocity
1824
+ * @property swipeVelocityX
1825
+ * @type {Number}
1826
+ * @default 0.6
1827
+ */
1828
+ swipeVelocityX: 0.6,
1829
+
1830
+ /**
1831
+ * vertical swipe velocity
1832
+ * @property swipeVelocityY
1833
+ * @type {Number}
1834
+ * @default 0.6
1835
+ */
1836
+ swipeVelocityY: 0.6
1837
+ },
1838
+
1839
+ handler: function swipeGesture(ev, inst) {
1840
+ if(ev.eventType == EVENT_RELEASE) {
1841
+ var touches = ev.touches.length,
1842
+ options = inst.options;
1843
+
1844
+ // max touches
1845
+ if(touches < options.swipeMinTouches ||
1846
+ touches > options.swipeMaxTouches) {
1847
+ return;
1848
+ }
1849
+
1850
+ // when the distance we moved is too small we skip this gesture
1851
+ // or we can be already in dragging
1852
+ if(ev.velocityX > options.swipeVelocityX ||
1853
+ ev.velocityY > options.swipeVelocityY) {
1854
+ // trigger swipe events
1855
+ inst.trigger(this.name, ev);
1856
+ inst.trigger(this.name + ev.direction, ev);
1857
+ }
1858
+ }
1859
+ }
1860
+ };
1861
+
1862
+ /**
1863
+ * @module gestures
1864
+ */
1865
+ /**
1866
+ * Single tap and a double tap on a place
1867
+ *
1868
+ * @class Tap
1869
+ * @static
1870
+ */
1871
+ /**
1872
+ * @event tap
1873
+ * @param {Object} ev
1874
+ */
1875
+ /**
1876
+ * @event doubletap
1877
+ * @param {Object} ev
1878
+ */
1879
+
1880
+ /**
1881
+ * @param {String} name
1882
+ */
1883
+ (function(name) {
1884
+ var hasMoved = false;
1885
+
1886
+ function tapGesture(ev, inst) {
1887
+ var options = inst.options,
1888
+ current = Detection.current,
1889
+ prev = Detection.previous,
1890
+ sincePrev,
1891
+ didDoubleTap;
1892
+
1893
+ switch(ev.eventType) {
1894
+ case EVENT_START:
1895
+ hasMoved = false;
1896
+ break;
1897
+
1898
+ case EVENT_MOVE:
1899
+ hasMoved = hasMoved || (ev.distance > options.tapMaxDistance);
1900
+ break;
1901
+
1902
+ case EVENT_END:
1903
+ if(!Utils.inStr(ev.srcEvent.type, 'cancel') && ev.deltaTime < options.tapMaxTime && !hasMoved) {
1904
+ // previous gesture, for the double tap since these are two different gesture detections
1905
+ sincePrev = prev && prev.lastEvent && ev.timeStamp - prev.lastEvent.timeStamp;
1906
+ didDoubleTap = false;
1907
+
1908
+ // check if double tap
1909
+ if(prev && prev.name == name &&
1910
+ (sincePrev && sincePrev < options.doubleTapInterval) &&
1911
+ ev.distance < options.doubleTapDistance) {
1912
+ inst.trigger('doubletap', ev);
1913
+ didDoubleTap = true;
1914
+ }
1915
+
1916
+ // do a single tap
1917
+ if(!didDoubleTap || options.tapAlways) {
1918
+ current.name = name;
1919
+ inst.trigger(current.name, ev);
1920
+ }
1921
+ }
1922
+ break;
1923
+ }
1924
+ }
1925
+
1926
+ Hammer.gestures.Tap = {
1927
+ name: name,
1928
+ index: 100,
1929
+ handler: tapGesture,
1930
+ defaults: {
1931
+ /**
1932
+ * max time of a tap, this is for the slow tappers
1933
+ * @property tapMaxTime
1934
+ * @type {Number}
1935
+ * @default 250
1936
+ */
1937
+ tapMaxTime: 250,
1938
+
1939
+ /**
1940
+ * max distance of movement of a tap, this is for the slow tappers
1941
+ * @property tapMaxDistance
1942
+ * @type {Number}
1943
+ * @default 10
1944
+ */
1945
+ tapMaxDistance: 10,
1946
+
1947
+ /**
1948
+ * always trigger the `tap` event, even while double-tapping
1949
+ * @property tapAlways
1950
+ * @type {Boolean}
1951
+ * @default true
1952
+ */
1953
+ tapAlways: true,
1954
+
1955
+ /**
1956
+ * max distance between two taps
1957
+ * @property doubleTapDistance
1958
+ * @type {Number}
1959
+ * @default 20
1960
+ */
1961
+ doubleTapDistance: 20,
1962
+
1963
+ /**
1964
+ * max time between two taps
1965
+ * @property doubleTapInterval
1966
+ * @type {Number}
1967
+ * @default 300
1968
+ */
1969
+ doubleTapInterval: 300
1970
+ }
1971
+ };
1972
+ })('tap');
1973
+
1974
+ /**
1975
+ * @module gestures
1976
+ */
1977
+ /**
1978
+ * when a touch is being touched at the page
1979
+ *
1980
+ * @class Touch
1981
+ * @static
1982
+ */
1983
+ /**
1984
+ * @event touch
1985
+ * @param {Object} ev
1986
+ */
1987
+ Hammer.gestures.Touch = {
1988
+ name: 'touch',
1989
+ index: -Infinity,
1990
+ defaults: {
1991
+ /**
1992
+ * call preventDefault at touchstart, and makes the element blocking by disabling the scrolling of the page,
1993
+ * but it improves gestures like transforming and dragging.
1994
+ * be careful with using this, it can be very annoying for users to be stuck on the page
1995
+ * @property preventDefault
1996
+ * @type {Boolean}
1997
+ * @default false
1998
+ */
1999
+ preventDefault: false,
2000
+
2001
+ /**
2002
+ * disable mouse events, so only touch (or pen!) input triggers events
2003
+ * @property preventMouse
2004
+ * @type {Boolean}
2005
+ * @default false
2006
+ */
2007
+ preventMouse: false
2008
+ },
2009
+ handler: function touchGesture(ev, inst) {
2010
+ if(inst.options.preventMouse && ev.pointerType == POINTER_MOUSE) {
2011
+ ev.stopDetect();
2012
+ return;
2013
+ }
2014
+
2015
+ if(inst.options.preventDefault) {
2016
+ ev.preventDefault();
2017
+ }
2018
+
2019
+ if(ev.eventType == EVENT_TOUCH) {
2020
+ inst.trigger('touch', ev);
2021
+ }
2022
+ }
2023
+ };
2024
+
2025
+ /**
2026
+ * @module gestures
2027
+ */
2028
+ /**
2029
+ * User want to scale or rotate with 2 fingers
2030
+ * Preventing the default browser behavior is a good way to improve feel and working. This can be done with the
2031
+ * `preventDefault` option.
2032
+ *
2033
+ * @class Transform
2034
+ * @static
2035
+ */
2036
+ /**
2037
+ * @event transform
2038
+ * @param {Object} ev
2039
+ */
2040
+ /**
2041
+ * @event transformstart
2042
+ * @param {Object} ev
2043
+ */
2044
+ /**
2045
+ * @event transformend
2046
+ * @param {Object} ev
2047
+ */
2048
+ /**
2049
+ * @event pinchin
2050
+ * @param {Object} ev
2051
+ */
2052
+ /**
2053
+ * @event pinchout
2054
+ * @param {Object} ev
2055
+ */
2056
+ /**
2057
+ * @event rotate
2058
+ * @param {Object} ev
2059
+ */
2060
+
2061
+ /**
2062
+ * @param {String} name
2063
+ */
2064
+ (function(name) {
2065
+ var triggered = false;
2066
+
2067
+ function transformGesture(ev, inst) {
2068
+ switch(ev.eventType) {
2069
+ case EVENT_START:
2070
+ triggered = false;
2071
+ break;
2072
+
2073
+ case EVENT_MOVE:
2074
+ // at least multitouch
2075
+ if(ev.touches.length < 2) {
2076
+ return;
2077
+ }
2078
+
2079
+ var scaleThreshold = Math.abs(1 - ev.scale);
2080
+ var rotationThreshold = Math.abs(ev.rotation);
2081
+
2082
+ // when the distance we moved is too small we skip this gesture
2083
+ // or we can be already in dragging
2084
+ if(scaleThreshold < inst.options.transformMinScale &&
2085
+ rotationThreshold < inst.options.transformMinRotation) {
2086
+ return;
2087
+ }
2088
+
2089
+ // we are transforming!
2090
+ Detection.current.name = name;
2091
+
2092
+ // first time, trigger dragstart event
2093
+ if(!triggered) {
2094
+ inst.trigger(name + 'start', ev);
2095
+ triggered = true;
2096
+ }
2097
+
2098
+ inst.trigger(name, ev); // basic transform event
2099
+
2100
+ // trigger rotate event
2101
+ if(rotationThreshold > inst.options.transformMinRotation) {
2102
+ inst.trigger('rotate', ev);
2103
+ }
2104
+
2105
+ // trigger pinch event
2106
+ if(scaleThreshold > inst.options.transformMinScale) {
2107
+ inst.trigger('pinch', ev);
2108
+ inst.trigger('pinch' + (ev.scale < 1 ? 'in' : 'out'), ev);
2109
+ }
2110
+ break;
2111
+
2112
+ case EVENT_RELEASE:
2113
+ if(triggered && ev.changedLength < 2) {
2114
+ inst.trigger(name + 'end', ev);
2115
+ triggered = false;
2116
+ }
2117
+ break;
2118
+ }
2119
+ }
2120
+
2121
+ Hammer.gestures.Transform = {
2122
+ name: name,
2123
+ index: 45,
2124
+ defaults: {
2125
+ /**
2126
+ * minimal scale factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1
2127
+ * @property transformMinScale
2128
+ * @type {Number}
2129
+ * @default 0.01
2130
+ */
2131
+ transformMinScale: 0.01,
2132
+
2133
+ /**
2134
+ * rotation in degrees
2135
+ * @property transformMinRotation
2136
+ * @type {Number}
2137
+ * @default 1
2138
+ */
2139
+ transformMinRotation: 1
2140
+ },
2141
+
2142
+ handler: transformGesture
2143
+ };
2144
+ })('transform');
2145
+
2146
+ /**
2147
+ * @module hammer
2148
+ */
2149
+
2150
+ // AMD export
2151
+ if(typeof define == 'function' && define.amd) {
2152
+ define(function() {
2153
+ return Hammer;
2154
+ });
2155
+ // commonjs export
2156
+ } else if(typeof module !== 'undefined' && module.exports) {
2157
+ module.exports = Hammer;
2158
+ // browser export
2159
+ } else {
2160
+ window.Hammer = Hammer;
2161
+ }
2162
+
2163
+ })(window);