hammerjs-rails 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/Gemfile +4 -0
- data/README.md +26 -0
- data/Rakefile +8 -0
- data/hammerjs-rails.gemspec +23 -0
- data/lib/hammerjs-rails.rb +6 -0
- data/lib/hammerjs/rails/version.rb +5 -0
- data/vendor/assets/javascripts/hammer.js +1356 -0
- metadata +80 -0
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
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,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);
|