hammerjs-rails 0.2.0 → 0.3.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 +4 -4
- data/lib/hammerjs/rails/version.rb +1 -1
- data/vendor/assets/javascripts/hammer.js +2037 -1131
- data/vendor/assets/javascripts/hammer.min.js +9 -0
- data/vendor/assets/javascripts/jquery.hammer.js +23 -113
- metadata +12 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 45866f46e5fbe0c0cf29842a8a6a19dba8735fb3
|
4
|
+
data.tar.gz: 396788421354d38e46e5b097540d68f511bb55d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8e3f603f3a60819e93b6237bfccbed7350a3137d4bc810f9be81d71b5775a442088b21b5ef2015a006617480d3f1ea7ca7baf199e3f2a1d0f5dbe5a28f598e7c
|
7
|
+
data.tar.gz: 4ac1e99b9b8e3baafb0c009f16f7dd50935b26eb819a77702dbc16c503f9e52016b279639ffdce2b5f8199aa6fe757c8a8a47e2f491b9974f1a7c0e6f5d66472
|
@@ -1,1356 +1,2262 @@
|
|
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
1
|
(function(window, undefined) {
|
8
2
|
'use strict';
|
9
3
|
|
4
|
+
var VENDOR_PREFIXES = ['', 'webkit', 'moz', 'MS', 'ms', 'o'];
|
5
|
+
var TEST_ELEMENT = document.createElement('div');
|
6
|
+
|
7
|
+
var TYPE_FUNCTION = 'function';
|
8
|
+
var TYPE_UNDEFINED = 'undefined';
|
9
|
+
|
10
|
+
var round = Math.round;
|
11
|
+
var abs = Math.abs;
|
12
|
+
var now = Date.now;
|
13
|
+
|
10
14
|
/**
|
11
|
-
*
|
12
|
-
*
|
13
|
-
* @param
|
14
|
-
* @param
|
15
|
-
* @returns {
|
16
|
-
* @constructor
|
15
|
+
* set a timeout with a given scope
|
16
|
+
* @param {Function} fn
|
17
|
+
* @param {Number} timeout
|
18
|
+
* @param {Object} context
|
19
|
+
* @returns {number}
|
17
20
|
*/
|
18
|
-
|
19
|
-
|
20
|
-
}
|
21
|
+
function setTimeoutScope(fn, timeout, context) {
|
22
|
+
return setTimeout(bindFn(fn, context), timeout);
|
23
|
+
}
|
21
24
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
}
|
39
|
-
|
40
|
-
//
|
41
|
-
// more settings are defined per gesture at gestures.js
|
42
|
-
//
|
43
|
-
};
|
25
|
+
/**
|
26
|
+
* if the argument is an array, we want to execute the fn on each entry
|
27
|
+
* if it aint an array we don't want to do a thing.
|
28
|
+
* this is used by all the methods that accept a single and array argument.
|
29
|
+
* @param {*|Array} arg
|
30
|
+
* @param {String} fn
|
31
|
+
* @param {Object} [context]
|
32
|
+
* @returns {Boolean}
|
33
|
+
*/
|
34
|
+
function invokeArrayArg(arg, fn, context) {
|
35
|
+
if (Array.isArray(arg)) {
|
36
|
+
each(arg, context[fn], context);
|
37
|
+
return true;
|
38
|
+
}
|
39
|
+
return false;
|
40
|
+
}
|
44
41
|
|
45
|
-
|
46
|
-
|
47
|
-
|
42
|
+
/**
|
43
|
+
* walk objects and arrays
|
44
|
+
* @param {Object} obj
|
45
|
+
* @param {Function} iterator
|
46
|
+
* @param {Object} context
|
47
|
+
*/
|
48
|
+
function each(obj, iterator, context) {
|
49
|
+
var i, len;
|
48
50
|
|
49
|
-
|
50
|
-
|
51
|
-
|
51
|
+
if (!obj) {
|
52
|
+
return;
|
53
|
+
}
|
52
54
|
|
53
|
-
|
54
|
-
|
55
|
-
|
55
|
+
if (obj.forEach) {
|
56
|
+
obj.forEach(iterator, context);
|
57
|
+
} else if (obj.length !== undefined) {
|
58
|
+
for (i = 0, len = obj.length; i < len; i++) {
|
59
|
+
iterator.call(context, obj[i], i, obj);
|
60
|
+
}
|
61
|
+
} else {
|
62
|
+
for (i in obj) {
|
63
|
+
obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
|
64
|
+
}
|
65
|
+
}
|
66
|
+
}
|
56
67
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
68
|
+
/**
|
69
|
+
* extend object.
|
70
|
+
* means that properties in dest will be overwritten by the ones in src.
|
71
|
+
* @param {Object} dest
|
72
|
+
* @param {Object} src
|
73
|
+
* @param {Boolean} [merge]
|
74
|
+
* @returns {Object} dest
|
75
|
+
*/
|
76
|
+
function extend(dest, src, merge) {
|
77
|
+
var keys = Object.keys(src);
|
78
|
+
for (var i = 0, len = keys.length; i < len; i++) {
|
79
|
+
if (!merge || (merge && dest[keys[i]] === undefined)) {
|
80
|
+
dest[keys[i]] = src[keys[i]];
|
81
|
+
}
|
82
|
+
}
|
83
|
+
return dest;
|
84
|
+
}
|
62
85
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
86
|
+
/**
|
87
|
+
* merge the values from src in the dest.
|
88
|
+
* means that properties that exist in dest will not be overwritten by src
|
89
|
+
* @param {Object} dest
|
90
|
+
* @param {Object} src
|
91
|
+
* @returns {Object} dest
|
92
|
+
*/
|
93
|
+
function merge(dest, src) {
|
94
|
+
return extend(dest, src, true);
|
95
|
+
}
|
67
96
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
97
|
+
/**
|
98
|
+
* simple class inheritance
|
99
|
+
* @param {Function} child
|
100
|
+
* @param {Function} base
|
101
|
+
* @param {Object} [properties]
|
102
|
+
*/
|
103
|
+
function inherit(child, base, properties) {
|
104
|
+
var baseP = base.prototype,
|
105
|
+
childP;
|
72
106
|
|
73
|
-
|
74
|
-
|
107
|
+
childP = child.prototype = Object.create(baseP);
|
108
|
+
childP.constructor = child;
|
109
|
+
childP._super = baseP;
|
75
110
|
|
76
|
-
|
77
|
-
|
78
|
-
|
111
|
+
if (properties) {
|
112
|
+
extend(childP, properties);
|
113
|
+
}
|
114
|
+
}
|
79
115
|
|
80
|
-
|
81
|
-
|
116
|
+
/**
|
117
|
+
* simple function bind
|
118
|
+
* @param {Function} fn
|
119
|
+
* @param {Object} context
|
120
|
+
* @returns {Function}
|
121
|
+
*/
|
122
|
+
function bindFn(fn, context) {
|
123
|
+
return function() {
|
124
|
+
return fn.apply(context, arguments);
|
125
|
+
};
|
126
|
+
}
|
82
127
|
|
83
128
|
/**
|
84
|
-
*
|
129
|
+
* let a boolean value also be a function that must return a boolean
|
130
|
+
* this first item in args will be used as the context
|
131
|
+
* @param {Boolean|Function} val
|
132
|
+
* @param {Array} [args]
|
133
|
+
* @returns {Boolean}
|
85
134
|
*/
|
86
|
-
function
|
87
|
-
|
88
|
-
|
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]);
|
135
|
+
function boolOrFn(val, args) {
|
136
|
+
if (typeof val == TYPE_FUNCTION) {
|
137
|
+
return val.apply(args ? args[0] || undefined : undefined, args);
|
98
138
|
}
|
99
|
-
|
139
|
+
return val;
|
140
|
+
}
|
100
141
|
|
101
|
-
|
102
|
-
|
103
|
-
|
142
|
+
/**
|
143
|
+
* use the val2 when val1 is undefined
|
144
|
+
* @param {*} val1
|
145
|
+
* @param {*} val2
|
146
|
+
* @returns {*}
|
147
|
+
*/
|
148
|
+
function ifUndefined(val1, val2) {
|
149
|
+
return (val1 === undefined) ? val2 : val1;
|
150
|
+
}
|
104
151
|
|
105
|
-
|
106
|
-
|
152
|
+
/**
|
153
|
+
* addEventListener with multiple events at once
|
154
|
+
* @param {HTMLElement} element
|
155
|
+
* @param {String} types
|
156
|
+
* @param {Function} handler
|
157
|
+
*/
|
158
|
+
function addEventListeners(element, types, handler) {
|
159
|
+
each(splitStr(types), function(type) {
|
160
|
+
element.addEventListener(type, handler, false);
|
161
|
+
});
|
107
162
|
}
|
108
163
|
|
109
164
|
/**
|
110
|
-
*
|
111
|
-
*
|
112
|
-
* @param
|
113
|
-
* @param
|
114
|
-
* @returns {Hammer.Instance}
|
115
|
-
* @constructor
|
165
|
+
* removeEventListener with multiple events at once
|
166
|
+
* @param {HTMLElement} element
|
167
|
+
* @param {String} types
|
168
|
+
* @param {Function} handler
|
116
169
|
*/
|
117
|
-
|
118
|
-
|
170
|
+
function removeEventListeners(element, types, handler) {
|
171
|
+
each(splitStr(types), function(type) {
|
172
|
+
element.removeEventListener(type, handler, false);
|
173
|
+
});
|
174
|
+
}
|
119
175
|
|
120
|
-
|
121
|
-
|
122
|
-
|
176
|
+
/**
|
177
|
+
* find if a node is in the given parent
|
178
|
+
* @method hasParent
|
179
|
+
* @param {HTMLElement} node
|
180
|
+
* @param {HTMLElement} parent
|
181
|
+
* @return {Boolean} found
|
182
|
+
*/
|
183
|
+
function hasParent(node, parent) {
|
184
|
+
while (node) {
|
185
|
+
if (node == parent) {
|
186
|
+
return true;
|
187
|
+
}
|
188
|
+
node = node.parentNode;
|
189
|
+
}
|
190
|
+
return false;
|
191
|
+
}
|
123
192
|
|
124
|
-
|
193
|
+
/**
|
194
|
+
* small indexOf wrapper
|
195
|
+
* @param {String} str
|
196
|
+
* @param {String} find
|
197
|
+
* @returns {Boolean} found
|
198
|
+
*/
|
199
|
+
function inStr(str, find) {
|
200
|
+
return str.indexOf(find) > -1;
|
201
|
+
}
|
125
202
|
|
126
|
-
|
127
|
-
|
203
|
+
/**
|
204
|
+
* split string on whitespace
|
205
|
+
* @param {String} str
|
206
|
+
* @returns {Array} words
|
207
|
+
*/
|
208
|
+
function splitStr(str) {
|
209
|
+
return str.trim().split(/\s+/g);
|
210
|
+
}
|
128
211
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
212
|
+
/**
|
213
|
+
* find if a array contains the object using indexOf or a simple polyFill
|
214
|
+
* @param {Array} src
|
215
|
+
* @param {String} find
|
216
|
+
* @param {String} [findByKey]
|
217
|
+
* @return {Boolean|Number} false when not found, or the index
|
218
|
+
*/
|
219
|
+
function inArray(src, find, findByKey) {
|
220
|
+
if (src.indexOf && !findByKey) {
|
221
|
+
return src.indexOf(find);
|
222
|
+
} else {
|
223
|
+
for (var i = 0, len = src.length; i < len; i++) {
|
224
|
+
if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) {
|
225
|
+
return i;
|
226
|
+
}
|
227
|
+
}
|
228
|
+
return -1;
|
229
|
+
}
|
230
|
+
}
|
133
231
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
232
|
+
/**
|
233
|
+
* convert array-like objects to real arrays
|
234
|
+
* @param {Object} obj
|
235
|
+
* @returns {Array}
|
236
|
+
*/
|
237
|
+
function toArray(obj) {
|
238
|
+
return Array.prototype.slice.call(obj, 0);
|
239
|
+
}
|
138
240
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
241
|
+
/**
|
242
|
+
* unique array with objects based on a key (like 'id') or just by the array's value
|
243
|
+
* @param {Array} src [{id:1},{id:2},{id:1}]
|
244
|
+
* @param {String} [key]
|
245
|
+
* @returns {Array} [{id:1},{id:2}]
|
246
|
+
*/
|
247
|
+
function uniqueArray(src, key) {
|
248
|
+
var results = [];
|
249
|
+
var values = [];
|
250
|
+
for (var i = 0, len = src.length; i < len; i++) {
|
251
|
+
var val = key ? src[i][key] : src[i];
|
252
|
+
if (inArray(values, val) < 0) {
|
253
|
+
results.push(src[i]);
|
254
|
+
}
|
255
|
+
values[i] = val;
|
143
256
|
}
|
144
|
-
|
257
|
+
return results;
|
258
|
+
}
|
145
259
|
|
146
|
-
|
147
|
-
|
148
|
-
}
|
260
|
+
/**
|
261
|
+
* get the prefixed property
|
262
|
+
* @param {Object} obj
|
263
|
+
* @param {String} property
|
264
|
+
* @returns {String|Undefined} prefixed
|
265
|
+
*/
|
266
|
+
function prefixed(obj, property) {
|
267
|
+
var prefix, prop;
|
268
|
+
var camelProp = property[0].toUpperCase() + property.slice(1);
|
149
269
|
|
270
|
+
for (var i = 0, len = VENDOR_PREFIXES.length; i < len; i++) {
|
271
|
+
prefix = VENDOR_PREFIXES[i];
|
272
|
+
prop = (prefix) ? prefix + camelProp : property;
|
150
273
|
|
151
|
-
|
152
|
-
|
153
|
-
|
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 = {};
|
274
|
+
if (prop in obj) {
|
275
|
+
return prop;
|
276
|
+
}
|
192
277
|
}
|
278
|
+
return undefined;
|
279
|
+
}
|
193
280
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
281
|
+
/**
|
282
|
+
* get a unique id
|
283
|
+
* @returns {number} uniqueId
|
284
|
+
*/
|
285
|
+
var _uniqueId = 1;
|
286
|
+
function uniqueId() {
|
287
|
+
return _uniqueId++;
|
288
|
+
}
|
198
289
|
|
199
|
-
|
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
|
-
}
|
290
|
+
var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
|
205
291
|
|
206
|
-
|
207
|
-
|
208
|
-
|
292
|
+
var SUPPORT_TOUCH = ('ontouchstart' in window);
|
293
|
+
var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;
|
294
|
+
var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
|
209
295
|
|
296
|
+
var INPUT_TYPE_TOUCH = 'touch';
|
297
|
+
var INPUT_TYPE_PEN = 'pen';
|
298
|
+
var INPUT_TYPE_MOUSE = 'mouse';
|
299
|
+
var INPUT_TYPE_KINECT = 'kinect';
|
210
300
|
|
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
|
-
};
|
301
|
+
var COMPUTE_INTERVAL = 25;
|
221
302
|
|
303
|
+
var INPUT_START = 1;
|
304
|
+
var INPUT_MOVE = 2;
|
305
|
+
var INPUT_END = 4;
|
306
|
+
var INPUT_CANCEL = 8;
|
222
307
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
*/
|
229
|
-
var last_move_event = null;
|
308
|
+
var DIRECTION_NONE = 1;
|
309
|
+
var DIRECTION_LEFT = 2;
|
310
|
+
var DIRECTION_RIGHT = 4;
|
311
|
+
var DIRECTION_UP = 8;
|
312
|
+
var DIRECTION_DOWN = 16;
|
230
313
|
|
314
|
+
var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
|
315
|
+
var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
|
316
|
+
var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
|
317
|
+
|
318
|
+
var PROPS_XY = ['x', 'y'];
|
319
|
+
var PROPS_CLIENT_XY = ['clientX', 'clientY'];
|
231
320
|
|
232
321
|
/**
|
233
|
-
*
|
234
|
-
* @
|
322
|
+
* create new input type manager
|
323
|
+
* @param {Manager} manager
|
324
|
+
* @param {Function} callback
|
325
|
+
* @returns {Input}
|
326
|
+
* @constructor
|
235
327
|
*/
|
236
|
-
|
328
|
+
function Input(manager, callback) {
|
329
|
+
var self = this;
|
330
|
+
this.manager = manager;
|
331
|
+
this.callback = callback;
|
332
|
+
|
333
|
+
// smaller wrapper around the handler, for the scope and the enabled state of the manager,
|
334
|
+
// so when disabled the input events are completely bypassed.
|
335
|
+
this.domHandler = function(ev) {
|
336
|
+
if (boolOrFn(self.manager.options.enable, [self.manager])) {
|
337
|
+
self.handler(ev);
|
338
|
+
}
|
339
|
+
};
|
340
|
+
|
341
|
+
this.evEl && addEventListeners(this.manager.element, this.evEl, this.domHandler);
|
342
|
+
this.evWin && addEventListeners(window, this.evWin, this.domHandler);
|
343
|
+
}
|
237
344
|
|
345
|
+
Input.prototype = {
|
346
|
+
/**
|
347
|
+
* should handle the inputEvent data and trigger the callback
|
348
|
+
* @virtual
|
349
|
+
*/
|
350
|
+
handler: function() { },
|
351
|
+
|
352
|
+
/**
|
353
|
+
* unbind the events
|
354
|
+
*/
|
355
|
+
destroy: function() {
|
356
|
+
this.elEvents && removeEventListeners(this.manager.element, this.elEvents, this.domHandler);
|
357
|
+
this.winEvents && removeEventListeners(window, this.winEvents, this.domHandler);
|
358
|
+
}
|
359
|
+
};
|
238
360
|
|
239
361
|
/**
|
240
|
-
*
|
241
|
-
* @
|
362
|
+
* create new input type manager
|
363
|
+
* @param {Hammer} manager
|
364
|
+
* @returns {Input}
|
242
365
|
*/
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
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);
|
366
|
+
function createInputInstance(manager) {
|
367
|
+
var Type;
|
368
|
+
if (SUPPORT_POINTER_EVENTS) {
|
369
|
+
Type = PointerEventInput;
|
370
|
+
} else if (SUPPORT_ONLY_TOUCH) {
|
371
|
+
Type = TouchInput;
|
372
|
+
} else if (!SUPPORT_TOUCH) {
|
373
|
+
Type = MouseInput;
|
374
|
+
} else {
|
375
|
+
Type = TouchMouseInput;
|
257
376
|
}
|
258
|
-
|
377
|
+
return new (Type)(manager, inputHandler);
|
378
|
+
}
|
259
379
|
|
380
|
+
/**
|
381
|
+
* handle input events
|
382
|
+
* @param {Manager} manager
|
383
|
+
* @param {String} eventType
|
384
|
+
* @param {Object} input
|
385
|
+
*/
|
386
|
+
function inputHandler(manager, eventType, input) {
|
387
|
+
var pointersLen = input.pointers.length;
|
388
|
+
var changedPointersLen = input.changedPointers.length;
|
389
|
+
var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));
|
390
|
+
var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));
|
260
391
|
|
261
|
-
|
262
|
-
|
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;
|
392
|
+
input.isFirst = isFirst;
|
393
|
+
input.isFinal = isFinal;
|
269
394
|
|
270
|
-
|
271
|
-
|
395
|
+
if (isFirst) {
|
396
|
+
manager.session = {};
|
397
|
+
}
|
398
|
+
// source event is the normalized value of the domEvents
|
399
|
+
// like 'touchstart, mouseup, pointerdown'
|
400
|
+
input.eventType = eventType;
|
272
401
|
|
273
|
-
|
274
|
-
|
275
|
-
if(sourceEventType.match(/mouse/) && touch_triggered) {
|
276
|
-
return;
|
277
|
-
}
|
402
|
+
// compute scale, rotation etc
|
403
|
+
computeInputData(manager, input);
|
278
404
|
|
279
|
-
|
280
|
-
|
281
|
-
sourceEventType.match(/pointerdown/) || // pointerevents touch
|
282
|
-
(sourceEventType.match(/mouse/) && ev.which === 1) // mouse is pressed
|
283
|
-
) {
|
284
|
-
enable_detect = true;
|
285
|
-
}
|
405
|
+
// emit secret event
|
406
|
+
manager.emit('hammer.input', input);
|
286
407
|
|
287
|
-
|
288
|
-
|
289
|
-
enable_detect = false;
|
290
|
-
}
|
408
|
+
manager.recognize(input);
|
409
|
+
}
|
291
410
|
|
411
|
+
/**
|
412
|
+
* extend the data with some usable properties like scale, rotate, velocity etc
|
413
|
+
* @param {Object} manager
|
414
|
+
* @param {Object} input
|
415
|
+
*/
|
416
|
+
function computeInputData(manager, input) {
|
417
|
+
var session = manager.session;
|
418
|
+
var pointers = input.pointers;
|
419
|
+
var pointersLength = pointers.length;
|
420
|
+
|
421
|
+
// store the first input to calculate the distance and direction
|
422
|
+
if (!session.firstInput) {
|
423
|
+
session.firstInput = simpleCloneInputData(input);
|
424
|
+
}
|
292
425
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
426
|
+
// to compute scale and rotation we need to store the multiple touches
|
427
|
+
if (pointersLength > 1 && !session.firstMultiple) {
|
428
|
+
session.firstMultiple = simpleCloneInputData(input);
|
429
|
+
} else if (pointersLength === 1) {
|
430
|
+
session.firstMultiple = false;
|
431
|
+
}
|
298
432
|
|
299
|
-
|
300
|
-
|
433
|
+
var firstInput = session.firstInput;
|
434
|
+
var firstMultiple = session.firstMultiple;
|
435
|
+
var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
|
436
|
+
var center = getCenter(pointers);
|
301
437
|
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
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
|
-
}
|
438
|
+
input.timeStamp = now();
|
439
|
+
input.deltaTime = input.timeStamp - firstInput.timeStamp;
|
440
|
+
input.deltaX = center.x - offsetCenter.x;
|
441
|
+
input.deltaY = center.y - offsetCenter.y;
|
317
442
|
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
}
|
323
|
-
// no touches, force the end event
|
324
|
-
else if(!count_touches) {
|
325
|
-
eventType = Hammer.EVENT_END;
|
326
|
-
}
|
443
|
+
input.center = center;
|
444
|
+
input.angle = getAngle(offsetCenter, center);
|
445
|
+
input.distance = getDistance(offsetCenter, center);
|
446
|
+
input.offsetDirection = getDirection(input.deltaX, input.deltaY);
|
327
447
|
|
328
|
-
|
329
|
-
|
330
|
-
last_move_event = ev;
|
331
|
-
}
|
448
|
+
input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
|
449
|
+
input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
|
332
450
|
|
333
|
-
|
334
|
-
|
451
|
+
// find the correct target
|
452
|
+
var target = manager.element;
|
453
|
+
if (hasParent(input.srcEvent.target, target)) {
|
454
|
+
target = input.srcEvent.target;
|
455
|
+
}
|
456
|
+
input.target = target;
|
335
457
|
|
336
|
-
|
337
|
-
|
338
|
-
count_touches = Hammer.PointerEvent.updatePointer(eventType, ev);
|
339
|
-
}
|
340
|
-
}
|
458
|
+
computeIntervalInputData(session, input);
|
459
|
+
}
|
341
460
|
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
461
|
+
/**
|
462
|
+
* velocity is calculated every x ms
|
463
|
+
* @param {Object} session
|
464
|
+
* @param {Object} input
|
465
|
+
*/
|
466
|
+
function computeIntervalInputData(session, input) {
|
467
|
+
var last = session.lastInterval;
|
468
|
+
if (!last) {
|
469
|
+
last = session.lastInterval = simpleCloneInputData(input);
|
470
|
+
}
|
351
471
|
|
472
|
+
var deltaTime = input.timeStamp - last.timeStamp,
|
473
|
+
velocity,
|
474
|
+
velocityX,
|
475
|
+
velocityY,
|
476
|
+
direction;
|
477
|
+
|
478
|
+
if (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined) {
|
479
|
+
var deltaX = last.deltaX - input.deltaX;
|
480
|
+
var deltaY = last.deltaY - input.deltaY;
|
481
|
+
|
482
|
+
var v = getVelocity(deltaTime, deltaX, deltaY);
|
483
|
+
velocityX = v.x;
|
484
|
+
velocityY = v.y;
|
485
|
+
velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;
|
486
|
+
direction = getDirection(deltaX, deltaY);
|
487
|
+
} else {
|
488
|
+
// use latest velocity info if it doesn't overtake a minimum period
|
489
|
+
velocity = last.velocity;
|
490
|
+
velocityX = last.velocityX;
|
491
|
+
velocityY = last.velocityY;
|
492
|
+
direction = last.direction;
|
493
|
+
}
|
352
494
|
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
// determine the eventtype we want to set
|
359
|
-
var types;
|
495
|
+
input.velocity = velocity;
|
496
|
+
input.velocityX = velocityX;
|
497
|
+
input.velocityY = velocityY;
|
498
|
+
input.direction = direction;
|
499
|
+
}
|
360
500
|
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
types = [
|
376
|
-
'touchstart mousedown',
|
377
|
-
'touchmove mousemove',
|
378
|
-
'touchend touchcancel mouseup'];
|
501
|
+
/**
|
502
|
+
* create a simple clone from the input used for storage of firstInput and firstMultiple
|
503
|
+
* @param {Object} input
|
504
|
+
* @returns {Object} clonedInputData
|
505
|
+
*/
|
506
|
+
function simpleCloneInputData(input) {
|
507
|
+
// make a simple copy of the pointers because we will get a reference if we don't
|
508
|
+
// we only need clientXY for the calculations
|
509
|
+
var pointers = [];
|
510
|
+
for (var i = 0; i < input.pointers.length; i++) {
|
511
|
+
pointers[i] = {
|
512
|
+
clientX: round(input.pointers[i].clientX),
|
513
|
+
clientY: round(input.pointers[i].clientY)
|
514
|
+
};
|
379
515
|
}
|
380
516
|
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
return [ev];
|
517
|
+
return {
|
518
|
+
timeStamp: now(),
|
519
|
+
pointers: pointers,
|
520
|
+
center: getCenter(pointers),
|
521
|
+
deltaX: input.deltaX,
|
522
|
+
deltaY: input.deltaY
|
523
|
+
};
|
524
|
+
}
|
525
|
+
|
526
|
+
/**
|
527
|
+
* get the center of all the pointers
|
528
|
+
* @param {Array} pointers
|
529
|
+
* @return {Object} center contains `x` and `y` properties
|
530
|
+
*/
|
531
|
+
function getCenter(pointers) {
|
532
|
+
var pointersLength = pointers.length;
|
533
|
+
|
534
|
+
// no need to loop when only one touch
|
535
|
+
if (pointersLength === 1) {
|
536
|
+
return {
|
537
|
+
x: round(pointers[0].clientX),
|
538
|
+
y: round(pointers[0].clientY)
|
539
|
+
};
|
405
540
|
}
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
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;
|
541
|
+
|
542
|
+
var x = 0, y = 0;
|
543
|
+
for (var i = 0; i < pointersLength; i++) {
|
544
|
+
x += pointers[i].clientX;
|
545
|
+
y += pointers[i].clientY;
|
420
546
|
}
|
421
547
|
|
422
548
|
return {
|
423
|
-
|
424
|
-
|
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
|
-
}
|
549
|
+
x: round(x / pointersLength),
|
550
|
+
y: round(y / pointersLength)
|
460
551
|
};
|
461
|
-
|
462
|
-
};
|
552
|
+
}
|
463
553
|
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
var touchlist = [];
|
554
|
+
/**
|
555
|
+
* calculate the velocity between two points. unit is in px per ms.
|
556
|
+
* @param {Number} deltaTime
|
557
|
+
* @param {Number} x
|
558
|
+
* @param {Number} y
|
559
|
+
* @return {Object} velocity `x` and `y`
|
560
|
+
*/
|
561
|
+
function getVelocity(deltaTime, x, y) {
|
562
|
+
return {
|
563
|
+
x: x / deltaTime || 0,
|
564
|
+
y: y / deltaTime || 0
|
565
|
+
};
|
566
|
+
}
|
478
567
|
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
if(type == Hammer.EVENT_END) {
|
493
|
-
this.pointers = {};
|
568
|
+
/**
|
569
|
+
* get the direction between two points
|
570
|
+
* @param {Number} x
|
571
|
+
* @param {Number} y
|
572
|
+
* @return {Number} direction
|
573
|
+
*/
|
574
|
+
function getDirection(x, y) {
|
575
|
+
if (x === y) {
|
576
|
+
return DIRECTION_NONE;
|
577
|
+
}
|
578
|
+
|
579
|
+
if (abs(x) >= abs(y)) {
|
580
|
+
return x > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
|
494
581
|
}
|
495
|
-
|
496
|
-
|
497
|
-
|
582
|
+
return y > 0 ? DIRECTION_UP : DIRECTION_DOWN;
|
583
|
+
}
|
584
|
+
|
585
|
+
/**
|
586
|
+
* calculate the absolute distance between two points
|
587
|
+
* @param {Object} p1 {x, y}
|
588
|
+
* @param {Object} p2 {x, y}
|
589
|
+
* @param {Array} [props] containing x and y keys
|
590
|
+
* @return {Number} distance
|
591
|
+
*/
|
592
|
+
function getDistance(p1, p2, props) {
|
593
|
+
if (!props) {
|
594
|
+
props = PROPS_XY;
|
498
595
|
}
|
596
|
+
var x = p2[props[0]] - p1[props[0]],
|
597
|
+
y = p2[props[1]] - p1[props[1]];
|
598
|
+
|
599
|
+
return Math.sqrt((x * x) + (y * y));
|
600
|
+
}
|
499
601
|
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
return false;
|
602
|
+
/**
|
603
|
+
* calculate the angle between two coordinates
|
604
|
+
* @param {Object} p1
|
605
|
+
* @param {Object} p2
|
606
|
+
* @param {Array} [props] containing x and y keys
|
607
|
+
* @return {Number} angle
|
608
|
+
*/
|
609
|
+
function getAngle(p1, p2, props) {
|
610
|
+
if (!props) {
|
611
|
+
props = PROPS_XY;
|
511
612
|
}
|
613
|
+
var x = p2[props[0]] - p1[props[0]],
|
614
|
+
y = p2[props[1]] - p1[props[1]];
|
615
|
+
return Math.atan2(y, x) * 180 / Math.PI;
|
616
|
+
}
|
512
617
|
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
618
|
+
/**
|
619
|
+
* calculate the rotation degrees between two pointersets
|
620
|
+
* @param {Array} start array of pointers
|
621
|
+
* @param {Array} end array of pointers
|
622
|
+
* @return {Number} rotation
|
623
|
+
*/
|
624
|
+
function getRotation(start, end) {
|
625
|
+
return getAngle(end[1], end[0], PROPS_CLIENT_XY) - getAngle(start[1], start[0], PROPS_CLIENT_XY);
|
626
|
+
}
|
519
627
|
|
628
|
+
/**
|
629
|
+
* calculate the scale factor between two pointersets
|
630
|
+
* no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
|
631
|
+
* @param {Array} start array of pointers
|
632
|
+
* @param {Array} end array of pointers
|
633
|
+
* @return {Number} scale
|
634
|
+
*/
|
635
|
+
function getScale(start, end) {
|
636
|
+
return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
|
637
|
+
}
|
520
638
|
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
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
|
-
}
|
639
|
+
var MOUSE_INPUT_MAP = {
|
640
|
+
mousedown: INPUT_START,
|
641
|
+
mousemove: INPUT_MOVE,
|
642
|
+
mouseup: INPUT_END,
|
643
|
+
mouseout: INPUT_CANCEL
|
538
644
|
};
|
539
645
|
|
646
|
+
var MOUSE_ELEMENT_EVENTS = 'mousedown';
|
647
|
+
var MOUSE_WINDOW_EVENTS = 'mousemove mouseout mouseup';
|
540
648
|
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
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
|
-
},
|
649
|
+
/**
|
650
|
+
* Mouse events input
|
651
|
+
* @constructor
|
652
|
+
* @extends Input
|
653
|
+
*/
|
654
|
+
function MouseInput() {
|
655
|
+
this.evEl = MOUSE_ELEMENT_EVENTS;
|
656
|
+
this.evWin = MOUSE_WINDOW_EVENTS;
|
577
657
|
|
658
|
+
this.allow = true; // used by Input.TouchMouse to disable mouse events
|
659
|
+
this.pressed = false; // mousedown state
|
578
660
|
|
579
|
-
|
580
|
-
|
581
|
-
* @param {Array} touches
|
582
|
-
* @returns {Object} center
|
583
|
-
*/
|
584
|
-
getCenter: function getCenter(touches) {
|
585
|
-
var valuesX = [], valuesY = [];
|
661
|
+
Input.apply(this, arguments);
|
662
|
+
}
|
586
663
|
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
664
|
+
inherit(MouseInput, Input, {
|
665
|
+
/**
|
666
|
+
* handle mouse events
|
667
|
+
* @param {Object} ev
|
668
|
+
*/
|
669
|
+
handler: function(ev) {
|
670
|
+
var eventType = MOUSE_INPUT_MAP[ev.type];
|
671
|
+
|
672
|
+
// on start we want to have the left mouse button down
|
673
|
+
if (eventType & INPUT_START && ev.button === 0) {
|
674
|
+
this.pressed = true;
|
675
|
+
}
|
591
676
|
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
};
|
596
|
-
},
|
677
|
+
if (eventType & INPUT_MOVE && ev.which !== 1) {
|
678
|
+
eventType = INPUT_END;
|
679
|
+
}
|
597
680
|
|
681
|
+
// mouse must be down, and mouse events are allowed (see the TouchMouse input)
|
682
|
+
if (!this.pressed || !this.allow) {
|
683
|
+
return;
|
684
|
+
}
|
598
685
|
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
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
|
-
},
|
686
|
+
// out of the window?
|
687
|
+
var target = ev.relatedTarget || ev.toElement || ev.target;
|
688
|
+
if (ev.type == 'mouseout' && target.nodeName != 'HTML') {
|
689
|
+
eventType = INPUT_MOVE;
|
690
|
+
}
|
625
691
|
|
692
|
+
if (eventType & (INPUT_END | INPUT_CANCEL)) {
|
693
|
+
this.pressed = false;
|
694
|
+
}
|
626
695
|
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
696
|
+
this.callback(this.manager, eventType, {
|
697
|
+
pointers: [ev],
|
698
|
+
changedPointers: [ev],
|
699
|
+
pointerType: INPUT_TYPE_MOUSE,
|
700
|
+
srcEvent: ev
|
701
|
+
});
|
702
|
+
},
|
703
|
+
});
|
704
|
+
|
705
|
+
var POINTER_INPUT_MAP = {
|
706
|
+
pointerdown: INPUT_START,
|
707
|
+
pointermove: INPUT_MOVE,
|
708
|
+
pointerup: INPUT_END,
|
709
|
+
pointercancel: INPUT_CANCEL,
|
710
|
+
pointerout: INPUT_CANCEL
|
711
|
+
};
|
636
712
|
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
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
|
-
}
|
713
|
+
// in IE10 the pointer types is defined as an enum
|
714
|
+
var IE10_POINTER_TYPE_ENUM = {
|
715
|
+
2: INPUT_TYPE_TOUCH,
|
716
|
+
3: INPUT_TYPE_PEN,
|
717
|
+
4: INPUT_TYPE_MOUSE,
|
718
|
+
5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816
|
719
|
+
};
|
714
720
|
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
721
|
+
var POINTER_ELEMENT_EVENTS = 'pointerdown';
|
722
|
+
var POINTER_WINDOW_EVENTS = 'pointermove pointerout pointerup pointercancel';
|
723
|
+
|
724
|
+
// IE10 has prefixed support, and case-sensitive
|
725
|
+
if (window.MSPointerEvent) {
|
726
|
+
POINTER_ELEMENT_EVENTS = 'MSPointerDown';
|
727
|
+
POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerOut MSPointerUp MSPointerCancel';
|
728
|
+
}
|
720
729
|
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
730
|
+
/**
|
731
|
+
* Pointer events input
|
732
|
+
* @constructor
|
733
|
+
* @extends Input
|
734
|
+
*/
|
735
|
+
function PointerEventInput() {
|
736
|
+
this.evEl = POINTER_ELEMENT_EVENTS;
|
737
|
+
this.evWin = POINTER_WINDOW_EVENTS;
|
725
738
|
|
726
|
-
|
727
|
-
|
739
|
+
Input.apply(this, arguments);
|
740
|
+
|
741
|
+
this.store = (this.manager.session.pointerEvents = []);
|
742
|
+
}
|
743
|
+
|
744
|
+
inherit(PointerEventInput, Input, {
|
745
|
+
/**
|
746
|
+
* handle mouse events
|
747
|
+
* @param {Object} ev
|
748
|
+
*/
|
749
|
+
handler: function(ev) {
|
750
|
+
var store = this.store;
|
751
|
+
var removePointer = false;
|
752
|
+
|
753
|
+
var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
|
754
|
+
var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
|
755
|
+
var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
|
756
|
+
|
757
|
+
// out of the window?
|
758
|
+
var target = ev.relatedTarget || ev.toElement || ev.target;
|
759
|
+
if (eventTypeNormalized == 'pointerout' && target.nodeName != 'HTML') {
|
760
|
+
eventType = INPUT_MOVE;
|
728
761
|
}
|
729
|
-
}
|
730
|
-
}
|
731
762
|
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
763
|
+
// start and mouse must be down
|
764
|
+
if (eventType & INPUT_START && (ev.button === 0 || pointerType == INPUT_TYPE_TOUCH)) {
|
765
|
+
store.push(ev);
|
766
|
+
} else if (eventType & (INPUT_END | INPUT_CANCEL)) {
|
767
|
+
removePointer = true;
|
768
|
+
}
|
738
769
|
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
770
|
+
// get index of the event in the store
|
771
|
+
// it not found, so the pointer hasn't been down (so it's probably a hover)
|
772
|
+
var storeIndex = inArray(store, ev.pointerId, 'pointerId');
|
773
|
+
if (storeIndex < 0) {
|
774
|
+
return;
|
775
|
+
}
|
776
|
+
|
777
|
+
// update the event in the store
|
778
|
+
store[storeIndex] = ev;
|
779
|
+
|
780
|
+
this.callback(this.manager, eventType, {
|
781
|
+
pointers: store,
|
782
|
+
changedPointers: [ev],
|
783
|
+
pointerType: pointerType,
|
784
|
+
srcEvent: ev
|
785
|
+
});
|
786
|
+
|
787
|
+
if (removePointer) {
|
788
|
+
// remove from the store
|
789
|
+
store.splice(storeIndex, 1);
|
790
|
+
}
|
744
791
|
}
|
745
|
-
|
792
|
+
});
|
793
|
+
|
794
|
+
var TOUCH_INPUT_MAP = {
|
795
|
+
touchstart: INPUT_START,
|
796
|
+
touchmove: INPUT_MOVE,
|
797
|
+
touchend: INPUT_END,
|
798
|
+
touchcancel: INPUT_CANCEL
|
746
799
|
};
|
747
800
|
|
801
|
+
var TOUCH_EVENTS = 'touchstart touchmove touchend touchcancel';
|
748
802
|
|
749
|
-
|
750
|
-
|
751
|
-
|
803
|
+
/**
|
804
|
+
* Touch events input
|
805
|
+
* @constructor
|
806
|
+
* @extends Input
|
807
|
+
*/
|
808
|
+
function TouchInput() {
|
809
|
+
this.evEl = TOUCH_EVENTS;
|
810
|
+
this.targetIds = {};
|
752
811
|
|
753
|
-
|
754
|
-
|
812
|
+
Input.apply(this, arguments);
|
813
|
+
}
|
755
814
|
|
756
|
-
|
757
|
-
|
758
|
-
|
815
|
+
inherit(TouchInput, Input, {
|
816
|
+
/**
|
817
|
+
* handle touch events
|
818
|
+
* @param {Object} ev
|
819
|
+
*/
|
820
|
+
handler: function(ev) {
|
821
|
+
var touches = normalizeTouches(ev, this);
|
822
|
+
this.callback(this.manager, TOUCH_INPUT_MAP[ev.type], {
|
823
|
+
pointers: touches[0],
|
824
|
+
changedPointers: touches[1],
|
825
|
+
pointerType: INPUT_TYPE_TOUCH,
|
826
|
+
srcEvent: ev
|
827
|
+
});
|
828
|
+
}
|
829
|
+
});
|
759
830
|
|
760
|
-
|
761
|
-
|
831
|
+
/**
|
832
|
+
* make sure all browsers return the same touches
|
833
|
+
* @param {Object} ev
|
834
|
+
* @param {TouchInput} touchInput
|
835
|
+
* @returns {Array} [all, changed]
|
836
|
+
*/
|
837
|
+
function normalizeTouches(ev, touchInput) {
|
838
|
+
var i, len;
|
839
|
+
|
840
|
+
var targetIds = touchInput.targetIds;
|
841
|
+
var targetTouches = toArray(ev.targetTouches);
|
842
|
+
var changedTouches = toArray(ev.changedTouches);
|
843
|
+
var changedTargetTouches = [];
|
844
|
+
|
845
|
+
// collect touches
|
846
|
+
if (ev.type == 'touchstart') {
|
847
|
+
for (i = 0, len = targetTouches.length; i < len; i++) {
|
848
|
+
targetIds[targetTouches[i].identifier] = true;
|
849
|
+
}
|
850
|
+
}
|
762
851
|
|
852
|
+
// filter changed touches to only contain touches that exist in the collected target ids
|
853
|
+
for (i = 0, len = changedTouches.length; i < len; i++) {
|
854
|
+
if (targetIds[changedTouches[i].identifier]) {
|
855
|
+
changedTargetTouches.push(changedTouches[i]);
|
856
|
+
}
|
763
857
|
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
*/
|
769
|
-
startDetect: function startDetect(inst, eventData) {
|
770
|
-
// already busy with a Hammer.gesture detection on an element
|
771
|
-
if(this.current) {
|
772
|
-
return;
|
858
|
+
// cleanup removed touches
|
859
|
+
if (ev.type == 'touchend'|| ev.type == 'touchcancel') {
|
860
|
+
delete targetIds[changedTouches[i].identifier];
|
861
|
+
}
|
773
862
|
}
|
774
863
|
|
775
|
-
|
864
|
+
return [
|
865
|
+
// merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
|
866
|
+
// also removed the duplicates
|
867
|
+
uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier'),
|
868
|
+
|
869
|
+
// only the changed :-)
|
870
|
+
changedTargetTouches
|
871
|
+
];
|
872
|
+
}
|
776
873
|
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
874
|
+
/**
|
875
|
+
* Combined touch and mouse input
|
876
|
+
*
|
877
|
+
* Touch has a higher priority then mouse, and while touching no mouse events are allowed.
|
878
|
+
* This because touch devices also emit mouse events while doing a touch.
|
879
|
+
*
|
880
|
+
* @constructor
|
881
|
+
* @extends Input
|
882
|
+
*/
|
883
|
+
function TouchMouseInput() {
|
884
|
+
Input.apply(this, arguments);
|
885
|
+
|
886
|
+
var handler = bindFn(this.handler, this);
|
887
|
+
this.touch = new TouchInput(this.manager, handler);
|
888
|
+
this.mouse = new MouseInput(this.manager, handler);
|
889
|
+
}
|
890
|
+
|
891
|
+
inherit(TouchMouseInput, Input, {
|
892
|
+
/**
|
893
|
+
* handle mouse and touch events
|
894
|
+
* @param {Hammer} manager
|
895
|
+
* @param {String} inputEvent
|
896
|
+
* @param {Object} inputData
|
897
|
+
*/
|
898
|
+
handler: function(manager, inputEvent, inputData) {
|
899
|
+
var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH),
|
900
|
+
isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE);
|
901
|
+
|
902
|
+
// when we're in a touch event, so block all upcoming mouse events
|
903
|
+
// most mobile browser also emit mouseevents, right after touchstart
|
904
|
+
if (isTouch) {
|
905
|
+
this.mouse.allow = false;
|
906
|
+
} else if (isMouse && !this.mouse.allow) {
|
907
|
+
return;
|
908
|
+
}
|
783
909
|
|
784
|
-
|
785
|
-
|
910
|
+
// reset the allowMouse when we're done
|
911
|
+
if (inputEvent & (INPUT_END | INPUT_CANCEL)) {
|
912
|
+
this.mouse.allow = true;
|
913
|
+
}
|
786
914
|
|
915
|
+
this.callback(manager, inputEvent, inputData);
|
916
|
+
},
|
787
917
|
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
return;
|
918
|
+
/**
|
919
|
+
* remove the event listeners
|
920
|
+
*/
|
921
|
+
destroy: function() {
|
922
|
+
this.touch.destroy();
|
923
|
+
this.mouse.destroy();
|
795
924
|
}
|
925
|
+
});
|
926
|
+
|
927
|
+
var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
|
928
|
+
var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;
|
929
|
+
|
930
|
+
// magical touchAction value
|
931
|
+
var TOUCH_ACTION_COMPUTE = 'compute';
|
932
|
+
var TOUCH_ACTION_AUTO = 'auto';
|
933
|
+
var TOUCH_ACTION_MANIPULATION = 'manipulation';
|
934
|
+
var TOUCH_ACTION_NONE = 'none';
|
935
|
+
var TOUCH_ACTION_PAN_X = 'pan-x';
|
936
|
+
var TOUCH_ACTION_PAN_Y = 'pan-y';
|
937
|
+
|
938
|
+
/**
|
939
|
+
* Touch Action
|
940
|
+
* sets the touchAction property or uses the js alternative
|
941
|
+
* @param {Manager} manager
|
942
|
+
* @param {String} value
|
943
|
+
* @constructor
|
944
|
+
*/
|
945
|
+
function TouchAction(manager, value) {
|
946
|
+
this.manager = manager;
|
947
|
+
this.set(value);
|
948
|
+
}
|
796
949
|
|
797
|
-
|
798
|
-
|
950
|
+
TouchAction.prototype = {
|
951
|
+
/**
|
952
|
+
* set the touchAction value on the element or enable the polyfill
|
953
|
+
* @param {String} value
|
954
|
+
*/
|
955
|
+
set: function(value) {
|
956
|
+
// find out the touch-action by the event handlers
|
957
|
+
if (value == TOUCH_ACTION_COMPUTE) {
|
958
|
+
value = this.compute();
|
959
|
+
}
|
799
960
|
|
800
|
-
|
801
|
-
|
961
|
+
if (NATIVE_TOUCH_ACTION) {
|
962
|
+
this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
|
963
|
+
}
|
964
|
+
this.actions = value.toLowerCase().trim();
|
965
|
+
},
|
966
|
+
|
967
|
+
/**
|
968
|
+
* just re-set the touchAction value
|
969
|
+
*/
|
970
|
+
update: function() {
|
971
|
+
this.set(this.manager.options.touchAction);
|
972
|
+
},
|
973
|
+
|
974
|
+
/**
|
975
|
+
* compute the value for the touchAction property based on the recognizer's settings
|
976
|
+
* @returns {String} value
|
977
|
+
*/
|
978
|
+
compute: function() {
|
979
|
+
var actions = [];
|
980
|
+
each(this.manager.recognizers, function(recognizer) {
|
981
|
+
if (boolOrFn(recognizer.options.enable, [recognizer])) {
|
982
|
+
actions = actions.concat(recognizer.getTouchAction());
|
983
|
+
}
|
984
|
+
});
|
985
|
+
return cleanTouchActions(actions.join(' '));
|
986
|
+
},
|
987
|
+
|
988
|
+
/**
|
989
|
+
* this method is called on each input cycle and provides the preventing of the browser behavior
|
990
|
+
* @param {Object} input
|
991
|
+
*/
|
992
|
+
preventDefaults: function(input) {
|
993
|
+
// not needed with native support for the touchAction property
|
994
|
+
if (NATIVE_TOUCH_ACTION) {
|
995
|
+
return;
|
996
|
+
}
|
802
997
|
|
803
|
-
|
804
|
-
|
805
|
-
var gesture = this.gestures[g];
|
998
|
+
var srcEvent = input.srcEvent;
|
999
|
+
var direction = input.offsetDirection;
|
806
1000
|
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
this.stopDetect();
|
812
|
-
break;
|
1001
|
+
// if the touch action did prevented once this session
|
1002
|
+
if (this.manager.session.prevented) {
|
1003
|
+
srcEvent.preventDefault();
|
1004
|
+
return;
|
813
1005
|
}
|
814
|
-
}
|
815
|
-
}
|
816
1006
|
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
1007
|
+
var actions = this.actions;
|
1008
|
+
var hasNone = inStr(actions, TOUCH_ACTION_NONE);
|
1009
|
+
var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
|
1010
|
+
var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
|
821
1011
|
|
822
|
-
|
823
|
-
|
824
|
-
|
1012
|
+
if (hasNone || (hasPanY && hasPanX) ||
|
1013
|
+
(hasPanY && direction & DIRECTION_HORIZONTAL) ||
|
1014
|
+
(hasPanX && direction & DIRECTION_VERTICAL)) {
|
1015
|
+
return this.preventSrc(srcEvent);
|
1016
|
+
}
|
1017
|
+
},
|
1018
|
+
|
1019
|
+
/**
|
1020
|
+
* call preventDefault to prevent the browser's default behavior (scrolling in most cases)
|
1021
|
+
* @param {Object} srcEvent
|
1022
|
+
*/
|
1023
|
+
preventSrc: function(srcEvent) {
|
1024
|
+
this.manager.session.prevented = true;
|
1025
|
+
srcEvent.preventDefault();
|
825
1026
|
}
|
1027
|
+
};
|
826
1028
|
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
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
|
-
}
|
1029
|
+
/**
|
1030
|
+
* when the touchActions are collected they are not a valid value, so we need to clean things up. *
|
1031
|
+
* @param {String} actions
|
1032
|
+
* @returns {*}
|
1033
|
+
*/
|
1034
|
+
function cleanTouchActions(actions) {
|
1035
|
+
// none
|
1036
|
+
if (inStr(actions, TOUCH_ACTION_NONE)) {
|
1037
|
+
return TOUCH_ACTION_NONE;
|
867
1038
|
}
|
868
1039
|
|
869
|
-
var
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
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;
|
1040
|
+
var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
|
1041
|
+
var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
|
1042
|
+
|
1043
|
+
// pan-x and pan-y can be combined
|
1044
|
+
if (hasPanX && hasPanY) {
|
1045
|
+
return TOUCH_ACTION_PAN_X + ' ' + TOUCH_ACTION_PAN_Y;
|
883
1046
|
}
|
884
|
-
|
885
|
-
|
886
|
-
|
1047
|
+
|
1048
|
+
// pan-x OR pan-y
|
1049
|
+
if (hasPanX || hasPanY) {
|
1050
|
+
return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
|
887
1051
|
}
|
888
1052
|
|
889
|
-
|
890
|
-
|
1053
|
+
// manipulation
|
1054
|
+
if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
|
1055
|
+
return TOUCH_ACTION_MANIPULATION;
|
1056
|
+
}
|
891
1057
|
|
892
|
-
|
893
|
-
|
1058
|
+
return TOUCH_ACTION_AUTO;
|
1059
|
+
}
|
894
1060
|
|
895
|
-
|
896
|
-
|
1061
|
+
/**
|
1062
|
+
* Recognizer flow explained; *
|
1063
|
+
* All recognizers have the initial state of POSSIBLE when a input session starts.
|
1064
|
+
* The definition of a input session is from the first input until the last input, with all it's movement in it. *
|
1065
|
+
* Example session for mouse-input: mousedown -> mousemove -> mouseup
|
1066
|
+
*
|
1067
|
+
* On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
|
1068
|
+
* which determines with state it should be.
|
1069
|
+
*
|
1070
|
+
* If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
|
1071
|
+
* POSSIBLE to give it another change on the next cycle.
|
1072
|
+
*
|
1073
|
+
* Possible
|
1074
|
+
* |
|
1075
|
+
* +-----+---------------+
|
1076
|
+
* | |
|
1077
|
+
* +-----+-----+ |
|
1078
|
+
* | | |
|
1079
|
+
* Failed Cancelled |
|
1080
|
+
* +-------+------+
|
1081
|
+
* | |
|
1082
|
+
* Recognized Began
|
1083
|
+
* |
|
1084
|
+
* Changed
|
1085
|
+
* |
|
1086
|
+
* Ended/Recognized
|
1087
|
+
*/
|
1088
|
+
var STATE_POSSIBLE = 1;
|
1089
|
+
var STATE_BEGAN = 2;
|
1090
|
+
var STATE_CHANGED = 4;
|
1091
|
+
var STATE_ENDED = 8;
|
1092
|
+
var STATE_RECOGNIZED = STATE_ENDED;
|
1093
|
+
var STATE_CANCELLED = 16;
|
1094
|
+
var STATE_FAILED = 32;
|
897
1095
|
|
898
|
-
|
1096
|
+
/**
|
1097
|
+
* Recognizer
|
1098
|
+
* Every recognizer needs to extend from this class.
|
1099
|
+
* @constructor
|
1100
|
+
* @param {Object} options
|
1101
|
+
*/
|
1102
|
+
function Recognizer(options) {
|
1103
|
+
this.id = uniqueId();
|
899
1104
|
|
900
|
-
|
901
|
-
|
1105
|
+
this.manager = null;
|
1106
|
+
this.options = merge(options || {}, this.defaults);
|
902
1107
|
|
903
|
-
|
904
|
-
|
1108
|
+
// default is enable true
|
1109
|
+
this.options.enable = ifUndefined(this.options.enable, true);
|
905
1110
|
|
906
|
-
|
907
|
-
rotation: Hammer.utils.getRotation(startEv.touches, ev.touches),
|
1111
|
+
this.state = STATE_POSSIBLE;
|
908
1112
|
|
909
|
-
|
910
|
-
|
1113
|
+
this.simultaneous = {};
|
1114
|
+
this.requireFail = [];
|
1115
|
+
}
|
911
1116
|
|
912
|
-
|
913
|
-
|
1117
|
+
Recognizer.prototype = {
|
1118
|
+
/**
|
1119
|
+
* @virtual
|
1120
|
+
* @type {Object}
|
1121
|
+
*/
|
1122
|
+
defaults: {},
|
1123
|
+
|
1124
|
+
/**
|
1125
|
+
* set options
|
1126
|
+
* @param {Object} options
|
1127
|
+
* @return {Recognizer}
|
1128
|
+
*/
|
1129
|
+
set: function(options) {
|
1130
|
+
extend(this.options, options);
|
1131
|
+
|
1132
|
+
// also update the touchAction, in case something changed about the directions/enabled state
|
1133
|
+
this.manager && this.manager.touchAction.update();
|
1134
|
+
return this;
|
1135
|
+
},
|
1136
|
+
|
1137
|
+
/**
|
1138
|
+
* recognize simultaneous with an other recognizer.
|
1139
|
+
* @param {Recognizer} otherRecognizer
|
1140
|
+
* @returns {Recognizer} this
|
1141
|
+
*/
|
1142
|
+
recognizeWith: function(otherRecognizer) {
|
1143
|
+
if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
|
1144
|
+
return this;
|
1145
|
+
}
|
914
1146
|
|
1147
|
+
var simultaneous = this.simultaneous;
|
1148
|
+
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
|
1149
|
+
if (!simultaneous[otherRecognizer.id]) {
|
1150
|
+
simultaneous[otherRecognizer.id] = otherRecognizer;
|
1151
|
+
otherRecognizer.recognizeWith(this);
|
1152
|
+
}
|
1153
|
+
return this;
|
1154
|
+
},
|
1155
|
+
|
1156
|
+
/**
|
1157
|
+
* drop the simultaneous link. it doesnt remove the link on the other recognizer.
|
1158
|
+
* @param {Recognizer} otherRecognizer
|
1159
|
+
* @returns {Recognizer} this
|
1160
|
+
*/
|
1161
|
+
dropRecognizeWith: function(otherRecognizer) {
|
1162
|
+
if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
|
1163
|
+
return this;
|
1164
|
+
}
|
915
1165
|
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
1166
|
+
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
|
1167
|
+
delete this.simultaneous[otherRecognizer.id];
|
1168
|
+
return this;
|
1169
|
+
},
|
1170
|
+
|
1171
|
+
/**
|
1172
|
+
* recognizer can only run when an other is failing
|
1173
|
+
* @param {Recognizer} otherRecognizer
|
1174
|
+
* @returns {Recognizer} this
|
1175
|
+
*/
|
1176
|
+
requireFailure: function(otherRecognizer) {
|
1177
|
+
if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
|
1178
|
+
return this;
|
1179
|
+
}
|
927
1180
|
|
928
|
-
|
929
|
-
|
1181
|
+
var requireFail = this.requireFail;
|
1182
|
+
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
|
1183
|
+
if (inArray(requireFail, otherRecognizer) === -1) {
|
1184
|
+
requireFail.push(otherRecognizer);
|
1185
|
+
otherRecognizer.requireFailure(this);
|
1186
|
+
}
|
1187
|
+
return this;
|
1188
|
+
},
|
1189
|
+
|
1190
|
+
/**
|
1191
|
+
* drop the requireFailure link. it does not remove the link on the other recognizer.
|
1192
|
+
* @param {Recognizer} otherRecognizer
|
1193
|
+
* @returns {Recognizer} this
|
1194
|
+
*/
|
1195
|
+
dropRequireFailure: function(otherRecognizer) {
|
1196
|
+
if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
|
1197
|
+
return this;
|
1198
|
+
}
|
930
1199
|
|
931
|
-
|
932
|
-
|
1200
|
+
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
|
1201
|
+
var index = inArray(this.requireFail, otherRecognizer);
|
1202
|
+
if (index > -1) {
|
1203
|
+
this.requireFail.splice(index, 1);
|
1204
|
+
}
|
1205
|
+
return this;
|
1206
|
+
},
|
1207
|
+
|
1208
|
+
/**
|
1209
|
+
* has require failures boolean
|
1210
|
+
* @returns {boolean}
|
1211
|
+
*/
|
1212
|
+
hasRequireFailures: function() {
|
1213
|
+
return this.requireFail.length > 0;
|
1214
|
+
},
|
1215
|
+
|
1216
|
+
/**
|
1217
|
+
* if the recognizer can recognize simultaneous with an other recognizer
|
1218
|
+
* @param {Recognizer} otherRecognizer
|
1219
|
+
* @returns {Boolean}
|
1220
|
+
*/
|
1221
|
+
canRecognizeWith: function(otherRecognizer) {
|
1222
|
+
return !!this.simultaneous[otherRecognizer.id];
|
1223
|
+
},
|
1224
|
+
|
1225
|
+
/**
|
1226
|
+
* You should use `tryEmit` instead of `emit` directly to check
|
1227
|
+
* that all the needed recognizers has failed before emitting.
|
1228
|
+
* @param {Object} input
|
1229
|
+
*/
|
1230
|
+
emit: function(input) {
|
1231
|
+
this.manager.emit(this.options.event, input); // simple 'eventName' events
|
1232
|
+
this.manager.emit(this.options.event + stateStr(this.state), input); // like 'panmove' and 'panstart'
|
1233
|
+
},
|
1234
|
+
|
1235
|
+
/**
|
1236
|
+
* Check that all the require failure recognizers has failed,
|
1237
|
+
* if true, it emits a gesture event,
|
1238
|
+
* otherwise, setup the state to FAILED.
|
1239
|
+
* @param {Object} input
|
1240
|
+
*/
|
1241
|
+
tryEmit: function(input) {
|
1242
|
+
if (this.canEmit()) {
|
1243
|
+
return this.emit(input);
|
1244
|
+
}
|
1245
|
+
// it's failing anyway
|
1246
|
+
this.state = STATE_FAILED;
|
1247
|
+
},
|
1248
|
+
|
1249
|
+
/**
|
1250
|
+
* can we emit?
|
1251
|
+
* @returns {boolean}
|
1252
|
+
*/
|
1253
|
+
canEmit: function() {
|
1254
|
+
for (var i = 0; i < this.requireFail.length; i++) {
|
1255
|
+
if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
|
1256
|
+
return false;
|
1257
|
+
}
|
1258
|
+
}
|
1259
|
+
return true;
|
1260
|
+
},
|
1261
|
+
|
1262
|
+
/**
|
1263
|
+
* update the recognizer
|
1264
|
+
* @param {Object} inputData
|
1265
|
+
*/
|
1266
|
+
recognize: function(inputData) {
|
1267
|
+
// make a new copy of the inputData
|
1268
|
+
// so we can change the inputData without messing up the other recognizers
|
1269
|
+
var inputDataClone = extend({}, inputData);
|
1270
|
+
|
1271
|
+
// is is enabled and allow recognizing?
|
1272
|
+
if (!boolOrFn(this.options.enable, [this, inputDataClone])) {
|
1273
|
+
this.reset();
|
1274
|
+
this.state = STATE_FAILED;
|
1275
|
+
return;
|
1276
|
+
}
|
933
1277
|
|
934
|
-
|
935
|
-
|
1278
|
+
// reset when we've reached the end
|
1279
|
+
if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
|
1280
|
+
this.state = STATE_POSSIBLE;
|
1281
|
+
}
|
936
1282
|
|
937
|
-
|
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
|
-
});
|
1283
|
+
this.state = this.process(inputDataClone);
|
943
1284
|
|
944
|
-
|
945
|
-
|
1285
|
+
// the recognizer has recognized a gesture
|
1286
|
+
// so trigger an event
|
1287
|
+
if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
|
1288
|
+
this.tryEmit(inputDataClone);
|
1289
|
+
}
|
1290
|
+
},
|
1291
|
+
|
1292
|
+
/**
|
1293
|
+
* return the state of the recognizer
|
1294
|
+
* the actual recognizing happens in this method
|
1295
|
+
* @virtual
|
1296
|
+
* @param {Object} inputData
|
1297
|
+
* @returns {Const} STATE
|
1298
|
+
*/
|
1299
|
+
process: function(inputData) { }, // jshint ignore:line
|
1300
|
+
|
1301
|
+
/**
|
1302
|
+
* return the preferred touch-action
|
1303
|
+
* @virtual
|
1304
|
+
* @returns {Array}
|
1305
|
+
*/
|
1306
|
+
getTouchAction: function() { },
|
1307
|
+
|
1308
|
+
/**
|
1309
|
+
* called when the gesture isn't allowed to recognize
|
1310
|
+
* like when another is being recognized or it is disabled
|
1311
|
+
* @virtual
|
1312
|
+
*/
|
1313
|
+
reset: function() { }
|
946
1314
|
};
|
947
1315
|
|
1316
|
+
/**
|
1317
|
+
* get a usable string, used as event postfix
|
1318
|
+
* @param {Const} state
|
1319
|
+
* @returns {String} state
|
1320
|
+
*/
|
1321
|
+
function stateStr(state) {
|
1322
|
+
if (state & STATE_CANCELLED) {
|
1323
|
+
return 'cancel';
|
1324
|
+
} else if (state & STATE_ENDED) {
|
1325
|
+
return 'end';
|
1326
|
+
} else if (state & STATE_CHANGED) {
|
1327
|
+
return 'move';
|
1328
|
+
} else if (state & STATE_BEGAN) {
|
1329
|
+
return 'start';
|
1330
|
+
}
|
1331
|
+
return '';
|
1332
|
+
}
|
948
1333
|
|
949
1334
|
/**
|
950
|
-
*
|
951
|
-
*
|
952
|
-
*
|
953
|
-
* you disable scrolling on that area.
|
954
|
-
* @events drag, drapleft, dragright, dragup, dragdown
|
1335
|
+
* direction cons to string
|
1336
|
+
* @param {Const} direction
|
1337
|
+
* @returns {String}
|
955
1338
|
*/
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
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;
|
1339
|
+
function directionStr(direction) {
|
1340
|
+
if (direction == DIRECTION_DOWN) {
|
1341
|
+
return 'down';
|
1342
|
+
} else if (direction == DIRECTION_UP) {
|
1343
|
+
return 'up';
|
1344
|
+
} else if (direction == DIRECTION_LEFT) {
|
1345
|
+
return 'left';
|
1346
|
+
} else if (direction == DIRECTION_RIGHT) {
|
1347
|
+
return 'right';
|
994
1348
|
}
|
1349
|
+
return '';
|
1350
|
+
}
|
995
1351
|
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1352
|
+
/**
|
1353
|
+
* get a recognizer by name if it is bound to a manager
|
1354
|
+
* @param {Recognizer|String} otherRecognizer
|
1355
|
+
* @param {Recognizer} recognizer
|
1356
|
+
* @returns {Recognizer}
|
1357
|
+
*/
|
1358
|
+
function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
|
1359
|
+
var manager = recognizer.manager;
|
1360
|
+
if (manager) {
|
1361
|
+
return manager.get(otherRecognizer);
|
1000
1362
|
}
|
1363
|
+
return otherRecognizer;
|
1364
|
+
}
|
1001
1365
|
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1366
|
+
/**
|
1367
|
+
* This recognizer is just used as a base for the simple attribute recognizers.
|
1368
|
+
* @constructor
|
1369
|
+
* @extends Recognizer
|
1370
|
+
*/
|
1371
|
+
function AttrRecognizer() {
|
1372
|
+
Recognizer.apply(this, arguments);
|
1373
|
+
}
|
1006
1374
|
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1375
|
+
inherit(AttrRecognizer, Recognizer, {
|
1376
|
+
/**
|
1377
|
+
* @namespace
|
1378
|
+
* @memberof AttrRecognizer
|
1379
|
+
*/
|
1380
|
+
defaults: {
|
1381
|
+
/**
|
1382
|
+
* @type {Number}
|
1383
|
+
* @default 1
|
1384
|
+
*/
|
1385
|
+
pointers: 1
|
1386
|
+
},
|
1387
|
+
|
1388
|
+
/**
|
1389
|
+
* Used to check if it the recognizer receives valid input, like input.distance > 10.
|
1390
|
+
* @memberof AttrRecognizer
|
1391
|
+
* @param {Object} input
|
1392
|
+
* @returns {Boolean} recognized
|
1393
|
+
*/
|
1394
|
+
attrTest: function(input) {
|
1395
|
+
var optionPointers = this.options.pointers;
|
1396
|
+
return optionPointers === 0 || input.pointers.length === optionPointers;
|
1397
|
+
},
|
1398
|
+
|
1399
|
+
/**
|
1400
|
+
* Process the input and return the state for the recognizer
|
1401
|
+
* @memberof AttrRecognizer
|
1402
|
+
* @param {Object} input
|
1403
|
+
* @returns {*} State
|
1404
|
+
*/
|
1405
|
+
process: function(input) {
|
1406
|
+
var state = this.state;
|
1407
|
+
var eventType = input.eventType;
|
1408
|
+
|
1409
|
+
var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
|
1410
|
+
var isValid = this.attrTest(input);
|
1411
|
+
|
1412
|
+
// on cancel input and we've recognized before, return STATE_CANCELLED
|
1413
|
+
if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
|
1414
|
+
return state | STATE_CANCELLED;
|
1415
|
+
} else if (isRecognized || isValid) {
|
1416
|
+
if (eventType & INPUT_END) {
|
1417
|
+
return state | STATE_ENDED;
|
1418
|
+
} else if (!(state & STATE_BEGAN)) {
|
1419
|
+
return STATE_BEGAN;
|
1420
|
+
}
|
1421
|
+
return state | STATE_CHANGED;
|
1013
1422
|
}
|
1423
|
+
return STATE_FAILED;
|
1424
|
+
}
|
1425
|
+
});
|
1014
1426
|
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1427
|
+
/**
|
1428
|
+
* Pan
|
1429
|
+
* Recognized when the pointer is down and moved in the allowed direction.
|
1430
|
+
* @constructor
|
1431
|
+
* @extends AttrRecognizer
|
1432
|
+
*/
|
1433
|
+
function PanRecognizer() {
|
1434
|
+
AttrRecognizer.apply(this, arguments);
|
1435
|
+
|
1436
|
+
this.pX = null;
|
1437
|
+
this.pY = null;
|
1438
|
+
}
|
1025
1439
|
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1440
|
+
inherit(PanRecognizer, AttrRecognizer, {
|
1441
|
+
/**
|
1442
|
+
* @namespace
|
1443
|
+
* @memberof PanRecognizer
|
1444
|
+
*/
|
1445
|
+
defaults: {
|
1446
|
+
event: 'pan',
|
1447
|
+
threshold: 10,
|
1448
|
+
pointers: 1,
|
1449
|
+
direction: DIRECTION_ALL
|
1450
|
+
},
|
1451
|
+
|
1452
|
+
getTouchAction: function() {
|
1453
|
+
var direction = this.options.direction;
|
1454
|
+
|
1455
|
+
if (direction === DIRECTION_ALL) {
|
1456
|
+
return [TOUCH_ACTION_NONE];
|
1029
1457
|
}
|
1030
1458
|
|
1031
|
-
|
1032
|
-
if
|
1033
|
-
|
1459
|
+
var actions = [];
|
1460
|
+
if (direction & DIRECTION_HORIZONTAL) {
|
1461
|
+
actions.push(TOUCH_ACTION_PAN_Y);
|
1034
1462
|
}
|
1035
|
-
|
1036
|
-
|
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
|
-
}
|
1463
|
+
if (direction & DIRECTION_VERTICAL) {
|
1464
|
+
actions.push(TOUCH_ACTION_PAN_X);
|
1044
1465
|
}
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1466
|
+
return actions;
|
1467
|
+
},
|
1468
|
+
|
1469
|
+
directionTest: function(input) {
|
1470
|
+
var options = this.options;
|
1471
|
+
var hasMoved = true;
|
1472
|
+
var distance = input.distance;
|
1473
|
+
var direction = input.direction;
|
1474
|
+
var x = input.deltaX;
|
1475
|
+
var y = input.deltaY;
|
1476
|
+
|
1477
|
+
// lock to axis?
|
1478
|
+
if (!(direction & options.direction)) {
|
1479
|
+
if (options.direction & DIRECTION_HORIZONTAL) {
|
1480
|
+
direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;
|
1481
|
+
hasMoved = x != this.pX;
|
1482
|
+
distance = Math.abs(input.deltaX);
|
1483
|
+
} else {
|
1484
|
+
direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN;
|
1485
|
+
hasMoved = y != this.pY;
|
1486
|
+
distance = Math.abs(input.deltaY);
|
1487
|
+
}
|
1050
1488
|
}
|
1489
|
+
input.direction = direction;
|
1490
|
+
return hasMoved && distance > options.threshold && direction & options.direction;
|
1491
|
+
},
|
1051
1492
|
|
1052
|
-
|
1053
|
-
|
1493
|
+
attrTest: function(input) {
|
1494
|
+
return AttrRecognizer.prototype.attrTest.call(this, input) &&
|
1495
|
+
(this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input)));
|
1496
|
+
},
|
1054
1497
|
|
1055
|
-
|
1056
|
-
|
1498
|
+
emit: function(input) {
|
1499
|
+
this.pX = input.deltaX;
|
1500
|
+
this.pY = input.deltaY;
|
1057
1501
|
|
1058
|
-
|
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;
|
1502
|
+
this._super.emit.call(this, input);
|
1064
1503
|
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
inst.trigger(this.name + 'end', ev);
|
1504
|
+
var direction = directionStr(input.direction);
|
1505
|
+
if (direction) {
|
1506
|
+
this.manager.emit(this.options.event + direction, input);
|
1069
1507
|
}
|
1508
|
+
}
|
1509
|
+
});
|
1070
1510
|
|
1071
|
-
|
1072
|
-
|
1511
|
+
/**
|
1512
|
+
* Pinch
|
1513
|
+
* Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
|
1514
|
+
* @constructor
|
1515
|
+
* @extends AttrRecognizer
|
1516
|
+
*/
|
1517
|
+
function PinchRecognizer() {
|
1518
|
+
AttrRecognizer.apply(this, arguments);
|
1519
|
+
}
|
1520
|
+
|
1521
|
+
inherit(PinchRecognizer, AttrRecognizer, {
|
1522
|
+
/**
|
1523
|
+
* @namespace
|
1524
|
+
* @memberof PinchRecognizer
|
1525
|
+
*/
|
1526
|
+
defaults: {
|
1527
|
+
event: 'pinch',
|
1528
|
+
threshold: 0,
|
1529
|
+
pointers: 2
|
1530
|
+
},
|
1531
|
+
|
1532
|
+
getTouchAction: function() {
|
1533
|
+
return [TOUCH_ACTION_NONE];
|
1534
|
+
},
|
1535
|
+
|
1536
|
+
attrTest: function(input) {
|
1537
|
+
return this._super.attrTest.call(this, input) &&
|
1538
|
+
(Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
|
1539
|
+
},
|
1540
|
+
|
1541
|
+
emit: function(input) {
|
1542
|
+
this._super.emit.call(this, input);
|
1543
|
+
if (input.scale !== 1) {
|
1544
|
+
var inOut = input.scale < 1 ? 'in' : 'out';
|
1545
|
+
this.manager.emit(this.options.event + inOut, input);
|
1546
|
+
}
|
1073
1547
|
}
|
1074
|
-
|
1075
|
-
};
|
1548
|
+
});
|
1076
1549
|
|
1077
1550
|
/**
|
1078
|
-
*
|
1079
|
-
*
|
1080
|
-
* @
|
1551
|
+
* Press
|
1552
|
+
* Recognized when the pointer is down for x ms without any movement.
|
1553
|
+
* @constructor
|
1554
|
+
* @extends Recognizer
|
1081
1555
|
*/
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1556
|
+
function PressRecognizer() {
|
1557
|
+
Recognizer.apply(this, arguments);
|
1558
|
+
|
1559
|
+
this._timer = null;
|
1560
|
+
this._input = null;
|
1561
|
+
}
|
1562
|
+
|
1563
|
+
inherit(PressRecognizer, Recognizer, {
|
1564
|
+
/**
|
1565
|
+
* @namespace
|
1566
|
+
* @memberof PressRecognizer
|
1567
|
+
*/
|
1568
|
+
defaults: {
|
1569
|
+
event: 'press',
|
1570
|
+
pointers: 1,
|
1571
|
+
time: 500, // minimal time of the pointer to be pressed
|
1572
|
+
threshold: 5 // a minimal movement is ok, but keep it low
|
1573
|
+
},
|
1574
|
+
|
1575
|
+
getTouchAction: function() {
|
1576
|
+
return [TOUCH_ACTION_AUTO];
|
1577
|
+
},
|
1578
|
+
|
1579
|
+
process: function(input) {
|
1580
|
+
var options = this.options;
|
1581
|
+
|
1582
|
+
var validPointers = input.pointers.length === options.pointers;
|
1583
|
+
var validMovement = input.distance < options.threshold;
|
1584
|
+
var validTime = input.deltaTime > options.time;
|
1585
|
+
|
1586
|
+
this._input = input;
|
1587
|
+
|
1588
|
+
// we only allow little movement
|
1589
|
+
// and we've reached an end event, so a tap is possible
|
1590
|
+
if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) {
|
1591
|
+
this.reset();
|
1592
|
+
} else if (input.eventType & INPUT_START) {
|
1593
|
+
this.reset();
|
1594
|
+
this._timer = setTimeoutScope(function() {
|
1595
|
+
this.state = STATE_RECOGNIZED;
|
1596
|
+
this.tryEmit();
|
1597
|
+
}, options.time, this);
|
1598
|
+
} else if (input.eventType & INPUT_END) {
|
1599
|
+
return STATE_RECOGNIZED;
|
1600
|
+
}
|
1601
|
+
return STATE_FAILED;
|
1602
|
+
},
|
1603
|
+
|
1604
|
+
reset: function() {
|
1605
|
+
clearTimeout(this._timer);
|
1606
|
+
},
|
1607
|
+
|
1608
|
+
emit: function(input) {
|
1609
|
+
if (this.state !== STATE_RECOGNIZED) {
|
1610
|
+
return;
|
1611
|
+
}
|
1612
|
+
|
1613
|
+
if (input && (input.eventType & INPUT_END)) {
|
1614
|
+
this.manager.emit(this.options.event + 'up', input);
|
1615
|
+
} else {
|
1616
|
+
this._input.timeStamp = now();
|
1617
|
+
this.manager.emit(this.options.event, this._input);
|
1618
|
+
}
|
1118
1619
|
}
|
1119
|
-
|
1120
|
-
};
|
1620
|
+
});
|
1121
1621
|
|
1122
1622
|
/**
|
1123
|
-
*
|
1124
|
-
*
|
1125
|
-
* @
|
1623
|
+
* Rotate
|
1624
|
+
* Recognized when two or more pointer are moving in a circular motion.
|
1625
|
+
* @constructor
|
1626
|
+
* @extends AttrRecognizer
|
1126
1627
|
*/
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1628
|
+
function RotateRecognizer() {
|
1629
|
+
AttrRecognizer.apply(this, arguments);
|
1630
|
+
}
|
1631
|
+
|
1632
|
+
inherit(RotateRecognizer, AttrRecognizer, {
|
1633
|
+
/**
|
1634
|
+
* @namespace
|
1635
|
+
* @memberof RotateRecognizer
|
1636
|
+
*/
|
1637
|
+
defaults: {
|
1638
|
+
event: 'rotate',
|
1639
|
+
threshold: 0,
|
1640
|
+
pointers: 2
|
1641
|
+
},
|
1642
|
+
|
1643
|
+
getTouchAction: function() {
|
1644
|
+
return [TOUCH_ACTION_NONE];
|
1645
|
+
},
|
1646
|
+
|
1647
|
+
attrTest: function(input) {
|
1648
|
+
return this._super.attrTest.call(this, input) &&
|
1649
|
+
(Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
|
1133
1650
|
}
|
1134
|
-
|
1135
|
-
};
|
1651
|
+
});
|
1136
1652
|
|
1137
1653
|
/**
|
1138
1654
|
* Swipe
|
1139
|
-
*
|
1140
|
-
* @
|
1655
|
+
* Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
|
1656
|
+
* @constructor
|
1657
|
+
* @extends AttrRecognizer
|
1141
1658
|
*/
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1659
|
+
function SwipeRecognizer() {
|
1660
|
+
AttrRecognizer.apply(this, arguments);
|
1661
|
+
}
|
1662
|
+
|
1663
|
+
inherit(SwipeRecognizer, AttrRecognizer, {
|
1664
|
+
/**
|
1665
|
+
* @namespace
|
1666
|
+
* @memberof SwipeRecognizer
|
1667
|
+
*/
|
1668
|
+
defaults: {
|
1669
|
+
event: 'swipe',
|
1670
|
+
threshold: 10,
|
1671
|
+
velocity: 0.65,
|
1672
|
+
direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
|
1673
|
+
pointers: 1
|
1674
|
+
},
|
1675
|
+
|
1676
|
+
getTouchAction: function() {
|
1677
|
+
return PanRecognizer.prototype.getTouchAction.call(this);
|
1678
|
+
},
|
1679
|
+
|
1680
|
+
attrTest: function(input) {
|
1681
|
+
var direction = this.options.direction;
|
1682
|
+
var velocity;
|
1683
|
+
|
1684
|
+
if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
|
1685
|
+
velocity = input.velocity;
|
1686
|
+
} else if (direction & DIRECTION_HORIZONTAL) {
|
1687
|
+
velocity = input.velocityX;
|
1688
|
+
} else if (direction & DIRECTION_VERTICAL) {
|
1689
|
+
velocity = input.velocityY;
|
1690
|
+
}
|
1691
|
+
|
1692
|
+
return this._super.attrTest.call(this, input) &&
|
1693
|
+
direction & input.direction &&
|
1694
|
+
abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
|
1695
|
+
},
|
1696
|
+
|
1697
|
+
emit: function(input) {
|
1698
|
+
this.manager.emit(this.options.event, input);
|
1699
|
+
|
1700
|
+
var direction = directionStr(input.direction);
|
1701
|
+
if (direction) {
|
1702
|
+
this.manager.emit(this.options.event + direction, input);
|
1703
|
+
}
|
1168
1704
|
}
|
1169
|
-
|
1170
|
-
};
|
1705
|
+
});
|
1171
1706
|
|
1172
1707
|
/**
|
1173
|
-
*
|
1174
|
-
*
|
1175
|
-
*
|
1708
|
+
* A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
|
1709
|
+
* between the given interval and position. The delay option can be used to recognize multi-taps without firing
|
1710
|
+
* a single tap.
|
1711
|
+
*
|
1712
|
+
* The eventData from the emitted event contains the property `tapCount`, which contains the amount of
|
1713
|
+
* multi-taps being recognized.
|
1714
|
+
* @constructor
|
1715
|
+
* @extends Recognizer
|
1176
1716
|
*/
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1717
|
+
function TapRecognizer() {
|
1718
|
+
Recognizer.apply(this, arguments);
|
1719
|
+
|
1720
|
+
// previous time and center,
|
1721
|
+
// used for tap counting
|
1722
|
+
this.pTime = false;
|
1723
|
+
this.pCenter = false;
|
1724
|
+
|
1725
|
+
this._timer = null;
|
1726
|
+
this._input = null;
|
1727
|
+
this.count = 0;
|
1728
|
+
}
|
1729
|
+
|
1730
|
+
inherit(TapRecognizer, Recognizer, {
|
1731
|
+
/**
|
1732
|
+
* @namespace
|
1733
|
+
* @memberof PinchRecognizer
|
1734
|
+
*/
|
1735
|
+
defaults: {
|
1736
|
+
event: 'tap',
|
1737
|
+
pointers: 1,
|
1738
|
+
taps: 1,
|
1739
|
+
interval: 300, // max time between the multi-tap taps
|
1740
|
+
time: 250, // max time of the pointer to be down (like finger on the screen)
|
1741
|
+
threshold: 2, // a minimal movement is ok, but keep it low
|
1742
|
+
posThreshold: 10 // a multi-tap can be a bit off the initial position
|
1743
|
+
},
|
1744
|
+
|
1745
|
+
getTouchAction: function() {
|
1746
|
+
return [TOUCH_ACTION_MANIPULATION];
|
1747
|
+
},
|
1748
|
+
|
1749
|
+
process: function(input) {
|
1750
|
+
var options = this.options;
|
1751
|
+
|
1752
|
+
var validPointers = input.pointers.length === options.pointers;
|
1753
|
+
var validMovement = input.distance < options.threshold;
|
1754
|
+
var validTouchTime = input.deltaTime < options.time;
|
1755
|
+
|
1756
|
+
this.reset();
|
1757
|
+
|
1758
|
+
if ((input.eventType & INPUT_START) && (this.count === 0)) {
|
1759
|
+
return this._failTimeout();
|
1760
|
+
}
|
1761
|
+
|
1762
|
+
// we only allow little movement
|
1763
|
+
// and we've reached an end event, so a tap is possible
|
1764
|
+
if (validMovement && validTouchTime && validPointers) {
|
1765
|
+
if (input.eventType != INPUT_END) {
|
1766
|
+
return this._failTimeout();
|
1767
|
+
}
|
1768
|
+
|
1769
|
+
var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true;
|
1770
|
+
var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;
|
1771
|
+
|
1772
|
+
this.pTime = input.timeStamp;
|
1773
|
+
this.pCenter = input.center;
|
1774
|
+
|
1775
|
+
if (!validMultiTap || !validInterval) {
|
1776
|
+
this.count = 1;
|
1777
|
+
} else {
|
1778
|
+
this.count += 1;
|
1779
|
+
}
|
1780
|
+
|
1781
|
+
this._input = input;
|
1782
|
+
|
1783
|
+
// if tap count matches we have recognized it,
|
1784
|
+
// else it has began recognizing...
|
1785
|
+
var tapCount = this.count % options.taps;
|
1786
|
+
if (tapCount === 0) {
|
1787
|
+
// no failing requirements, immediately trigger the tap event
|
1788
|
+
// or wait as long as the multitap interval to trigger
|
1789
|
+
if (!this.hasRequireFailures()) {
|
1790
|
+
return STATE_RECOGNIZED;
|
1791
|
+
} else {
|
1792
|
+
this._timer = setTimeoutScope(function() {
|
1793
|
+
this.state = STATE_RECOGNIZED;
|
1794
|
+
this.tryEmit();
|
1795
|
+
}, options.interval, this);
|
1796
|
+
return STATE_BEGAN;
|
1797
|
+
}
|
1798
|
+
}
|
1799
|
+
}
|
1800
|
+
return STATE_FAILED;
|
1801
|
+
},
|
1802
|
+
|
1803
|
+
_failTimeout: function() {
|
1804
|
+
this._timer = setTimeoutScope(function() {
|
1805
|
+
this.state = STATE_FAILED;
|
1806
|
+
}, this.options.interval, this);
|
1807
|
+
return STATE_FAILED;
|
1808
|
+
},
|
1809
|
+
|
1810
|
+
reset: function() {
|
1811
|
+
clearTimeout(this._timer);
|
1812
|
+
},
|
1813
|
+
|
1814
|
+
emit: function() {
|
1815
|
+
if (this.state == STATE_RECOGNIZED ) {
|
1816
|
+
this._input.tapCount = this.count;
|
1817
|
+
this.manager.emit(this.options.event, this._input);
|
1818
|
+
}
|
1213
1819
|
}
|
1214
|
-
|
1215
|
-
};
|
1820
|
+
});
|
1216
1821
|
|
1217
1822
|
/**
|
1218
|
-
*
|
1219
|
-
*
|
1220
|
-
* @
|
1823
|
+
* Simple way to create an manager with a default set of recognizers.
|
1824
|
+
* @param {HTMLElement} element
|
1825
|
+
* @param {Object} [options]
|
1826
|
+
* @constructor
|
1221
1827
|
*/
|
1222
|
-
Hammer
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
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
|
-
}
|
1828
|
+
function Hammer(element, options) {
|
1829
|
+
options = options || {};
|
1830
|
+
options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
|
1831
|
+
return new Manager(element, options);
|
1832
|
+
}
|
1241
1833
|
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1834
|
+
/**
|
1835
|
+
* @const {string}
|
1836
|
+
*/
|
1837
|
+
Hammer.VERSION = '2.0.1';
|
1245
1838
|
|
1246
|
-
|
1247
|
-
|
1839
|
+
/**
|
1840
|
+
* default settings
|
1841
|
+
* @namespace
|
1842
|
+
*/
|
1843
|
+
Hammer.defaults = {
|
1844
|
+
/**
|
1845
|
+
* set if DOM events are being triggered.
|
1846
|
+
* But this is slower and unused by simple implementations, so disabled by default.
|
1847
|
+
* @type {Boolean}
|
1848
|
+
* @default false
|
1849
|
+
*/
|
1850
|
+
domEvents: false,
|
1851
|
+
|
1852
|
+
/**
|
1853
|
+
* The value for the touchAction property/fallback.
|
1854
|
+
* When set to `compute` it will magically set the correct value based on the added recognizers.
|
1855
|
+
* @type {String}
|
1856
|
+
* @default compute
|
1857
|
+
*/
|
1858
|
+
touchAction: TOUCH_ACTION_COMPUTE,
|
1859
|
+
|
1860
|
+
/**
|
1861
|
+
* @type {Boolean}
|
1862
|
+
* @default true
|
1863
|
+
*/
|
1864
|
+
enable: true,
|
1865
|
+
|
1866
|
+
/**
|
1867
|
+
* Default recognizer setup when calling `Hammer()`
|
1868
|
+
* When creating a new Manager these will be skipped.
|
1869
|
+
* @type {Array}
|
1870
|
+
*/
|
1871
|
+
preset: [
|
1872
|
+
// RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
|
1873
|
+
[RotateRecognizer, { enable: false }],
|
1874
|
+
[PinchRecognizer, { enable: false }, ['rotate']],
|
1875
|
+
[SwipeRecognizer,{ direction: DIRECTION_HORIZONTAL }],
|
1876
|
+
[PanRecognizer, { direction: DIRECTION_HORIZONTAL }, ['swipe']],
|
1877
|
+
[TapRecognizer],
|
1878
|
+
[TapRecognizer, { event: 'doubletap', taps: 2 }, ['tap']],
|
1879
|
+
[PressRecognizer]
|
1880
|
+
],
|
1881
|
+
|
1882
|
+
/**
|
1883
|
+
* Some CSS properties can be used to improve the working of Hammer.
|
1884
|
+
* Add them to this method and they will be set when creating a new Manager.
|
1885
|
+
* @namespace
|
1886
|
+
*/
|
1887
|
+
cssProps: {
|
1888
|
+
/**
|
1889
|
+
* Disables text selection to improve the dragging gesture. When the value is `none` it also sets
|
1890
|
+
* `onselectstart=false` for IE9 on the element. Mainly for desktop browsers.
|
1891
|
+
* @type {String}
|
1892
|
+
* @default 'none'
|
1893
|
+
*/
|
1894
|
+
userSelect: 'none',
|
1895
|
+
|
1896
|
+
/**
|
1897
|
+
* Disable the Windows Phone grippers when pressing an element.
|
1898
|
+
* @type {String}
|
1899
|
+
* @default 'none'
|
1900
|
+
*/
|
1901
|
+
touchSelect: 'none',
|
1902
|
+
|
1903
|
+
/**
|
1904
|
+
* Disables the default callout shown when you touch and hold a touch target.
|
1905
|
+
* On iOS, when you touch and hold a touch target such as a link, Safari displays
|
1906
|
+
* a callout containing information about the link. This property allows you to disable that callout.
|
1907
|
+
* @type {String}
|
1908
|
+
* @default 'none'
|
1909
|
+
*/
|
1910
|
+
touchCallout: 'none',
|
1911
|
+
|
1912
|
+
/**
|
1913
|
+
* Specifies whether zooming is enabled. Used by IE10>
|
1914
|
+
* @type {String}
|
1915
|
+
* @default 'none'
|
1916
|
+
*/
|
1917
|
+
contentZooming: 'none',
|
1918
|
+
|
1919
|
+
/**
|
1920
|
+
* Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
|
1921
|
+
* @type {String}
|
1922
|
+
* @default 'none'
|
1923
|
+
*/
|
1924
|
+
userDrag: 'none',
|
1925
|
+
|
1926
|
+
/**
|
1927
|
+
* Overrides the highlight color shown when the user taps a link or a JavaScript
|
1928
|
+
* clickable element in iOS. This property obeys the alpha value, if specified.
|
1929
|
+
* @type {String}
|
1930
|
+
* @default 'rgba(0,0,0,0)'
|
1931
|
+
*/
|
1932
|
+
tapHighlightColor: 'rgba(0,0,0,0)'
|
1248
1933
|
}
|
1249
|
-
}
|
1250
1934
|
};
|
1251
1935
|
|
1936
|
+
var STOP = 1;
|
1937
|
+
var FORCED_STOP = 2;
|
1938
|
+
|
1252
1939
|
/**
|
1253
|
-
*
|
1254
|
-
*
|
1255
|
-
* @
|
1940
|
+
* Manager
|
1941
|
+
* @param {HTMLElement} element
|
1942
|
+
* @param {Object} [options]
|
1943
|
+
* @constructor
|
1256
1944
|
*/
|
1257
|
-
|
1258
|
-
|
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
|
-
}
|
1945
|
+
function Manager(element, options) {
|
1946
|
+
options = options || {};
|
1279
1947
|
|
1280
|
-
|
1281
|
-
if(ev.touches.length < 2) {
|
1282
|
-
return;
|
1283
|
-
}
|
1948
|
+
this.options = merge(options, Hammer.defaults);
|
1284
1949
|
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1950
|
+
this.handlers = {};
|
1951
|
+
this.session = {};
|
1952
|
+
this.recognizers = [];
|
1953
|
+
|
1954
|
+
this.element = element;
|
1955
|
+
this.input = createInputInstance(this);
|
1956
|
+
this.touchAction = new TouchAction(this, this.options.touchAction);
|
1957
|
+
|
1958
|
+
toggleCssProps(this, true);
|
1289
1959
|
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1960
|
+
each(options.recognizers, function(item) {
|
1961
|
+
var recognizer = this.add(new (item[0])(item[1]));
|
1962
|
+
item[2] && recognizer.recognizeWith(item[2]);
|
1963
|
+
item[3] && recognizer.requireFailure(item[2]);
|
1964
|
+
}, this);
|
1965
|
+
}
|
1966
|
+
|
1967
|
+
Manager.prototype = {
|
1968
|
+
/**
|
1969
|
+
* set options
|
1970
|
+
* @param {Object} options
|
1971
|
+
* @returns {Manager}
|
1972
|
+
*/
|
1973
|
+
set: function(options) {
|
1974
|
+
extend(this.options, options);
|
1975
|
+
return this;
|
1976
|
+
},
|
1977
|
+
|
1978
|
+
/**
|
1979
|
+
* stop recognizing for this session.
|
1980
|
+
* This session will be discarded, when a new [input]start event is fired.
|
1981
|
+
* When forced, the recognizer cycle is stopped immediately.
|
1982
|
+
* @param {Boolean} [force]
|
1983
|
+
*/
|
1984
|
+
stop: function(force) {
|
1985
|
+
this.session.stopped = force ? FORCED_STOP : STOP;
|
1986
|
+
},
|
1987
|
+
|
1988
|
+
/**
|
1989
|
+
* run the recognizers!
|
1990
|
+
* called by the inputHandler function on every movement of the pointers (touches)
|
1991
|
+
* it walks through all the recognizers and tries to detect the gesture that is being made
|
1992
|
+
* @param {Object} inputData
|
1993
|
+
*/
|
1994
|
+
recognize: function(inputData) {
|
1995
|
+
if (this.session.stopped) {
|
1996
|
+
return;
|
1997
|
+
}
|
1294
1998
|
|
1295
|
-
|
1296
|
-
|
1297
|
-
var rotation_threshold = Math.abs(ev.rotation);
|
1999
|
+
// run the touch-action polyfill
|
2000
|
+
this.touchAction.preventDefaults(inputData);
|
1298
2001
|
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
2002
|
+
var recognizer;
|
2003
|
+
var session = this.session;
|
2004
|
+
|
2005
|
+
// this holds the recognizer that is being recognized.
|
2006
|
+
// so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
|
2007
|
+
// if no recognizer is detecting a thing, it is set to `null`
|
2008
|
+
var curRecognizer = session.curRecognizer;
|
2009
|
+
|
2010
|
+
// reset when the last recognizer is recognized
|
2011
|
+
// or when we're in a new session
|
2012
|
+
if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) {
|
2013
|
+
curRecognizer = session.curRecognizer = null;
|
1304
2014
|
}
|
1305
2015
|
|
1306
|
-
|
1307
|
-
|
2016
|
+
for (var i = 0, len = this.recognizers.length; i < len; i++) {
|
2017
|
+
recognizer = this.recognizers[i];
|
2018
|
+
|
2019
|
+
// find out if we are allowed try to recognize the input for this one.
|
2020
|
+
// 1. allow if the session is NOT forced stopped (see the .stop() method)
|
2021
|
+
// 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
|
2022
|
+
// that is being recognized.
|
2023
|
+
// 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
|
2024
|
+
// this can be setup with the `recognizeWith()` method on the recognizer.
|
2025
|
+
if (this.session.stopped !== FORCED_STOP && ( // 1
|
2026
|
+
!curRecognizer || recognizer == curRecognizer || // 2
|
2027
|
+
recognizer.canRecognizeWith(curRecognizer))) { // 3
|
2028
|
+
recognizer.recognize(inputData);
|
2029
|
+
} else {
|
2030
|
+
recognizer.reset();
|
2031
|
+
}
|
2032
|
+
|
2033
|
+
// if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
|
2034
|
+
// current active recognizer. but only if we don't already have an active recognizer
|
2035
|
+
if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
|
2036
|
+
curRecognizer = session.curRecognizer = recognizer;
|
2037
|
+
}
|
2038
|
+
}
|
2039
|
+
},
|
2040
|
+
|
2041
|
+
/**
|
2042
|
+
* get a recognizer by its event name.
|
2043
|
+
* @param {Recognizer|String} recognizer
|
2044
|
+
* @returns {Recognizer|Null}
|
2045
|
+
*/
|
2046
|
+
get: function(recognizer) {
|
2047
|
+
if (recognizer instanceof Recognizer) {
|
2048
|
+
return recognizer;
|
2049
|
+
}
|
1308
2050
|
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
|
2051
|
+
var recognizers = this.recognizers;
|
2052
|
+
for (var i = 0; i < recognizers.length; i++) {
|
2053
|
+
if (recognizers[i].options.event == recognizer) {
|
2054
|
+
return recognizers[i];
|
2055
|
+
}
|
2056
|
+
}
|
2057
|
+
return null;
|
2058
|
+
},
|
2059
|
+
|
2060
|
+
/**
|
2061
|
+
* add a recognizer to the manager
|
2062
|
+
* existing recognizers with the same event name will be removed
|
2063
|
+
* @param {Recognizer} recognizer
|
2064
|
+
* @returns {Recognizer|Manager}
|
2065
|
+
*/
|
2066
|
+
add: function(recognizer) {
|
2067
|
+
if (invokeArrayArg(recognizer, 'add', this)) {
|
2068
|
+
return this;
|
1313
2069
|
}
|
1314
2070
|
|
1315
|
-
|
2071
|
+
// remove existing
|
2072
|
+
var existing = this.get(recognizer.options.event);
|
2073
|
+
if (existing) {
|
2074
|
+
this.remove(existing);
|
2075
|
+
}
|
1316
2076
|
|
1317
|
-
|
1318
|
-
|
1319
|
-
|
2077
|
+
this.recognizers.push(recognizer);
|
2078
|
+
recognizer.manager = this;
|
2079
|
+
|
2080
|
+
this.touchAction.update();
|
2081
|
+
return recognizer;
|
2082
|
+
},
|
2083
|
+
|
2084
|
+
/**
|
2085
|
+
* remove a recognizer by name or instance
|
2086
|
+
* @param {Recognizer|String} recognizer
|
2087
|
+
* @returns {Manager}
|
2088
|
+
*/
|
2089
|
+
remove: function(recognizer) {
|
2090
|
+
if (invokeArrayArg(recognizer, 'remove', this)) {
|
2091
|
+
return this;
|
1320
2092
|
}
|
1321
2093
|
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
2094
|
+
var recognizers = this.recognizers;
|
2095
|
+
recognizer = this.get(recognizer);
|
2096
|
+
recognizers.splice(inArray(recognizers, recognizer), 1);
|
2097
|
+
|
2098
|
+
this.touchAction.update();
|
2099
|
+
return this;
|
2100
|
+
},
|
2101
|
+
|
2102
|
+
/**
|
2103
|
+
* bind event
|
2104
|
+
* @param {String} events
|
2105
|
+
* @param {Function} handler
|
2106
|
+
* @returns {EventEmitter} this
|
2107
|
+
*/
|
2108
|
+
on: function(events, handler) {
|
2109
|
+
var handlers = this.handlers;
|
2110
|
+
each(splitStr(events), function(event) {
|
2111
|
+
handlers[event] = handlers[event] || [];
|
2112
|
+
handlers[event].push(handler);
|
2113
|
+
});
|
2114
|
+
return this;
|
2115
|
+
},
|
2116
|
+
|
2117
|
+
/**
|
2118
|
+
* unbind event, leave emit blank to remove all handlers
|
2119
|
+
* @param {String} events
|
2120
|
+
* @param {Function} [handler]
|
2121
|
+
* @returns {EventEmitter} this
|
2122
|
+
*/
|
2123
|
+
off: function(events, handler) {
|
2124
|
+
var handlers = this.handlers;
|
2125
|
+
each(splitStr(events), function(event) {
|
2126
|
+
if (!handler) {
|
2127
|
+
delete handlers[event];
|
2128
|
+
} else {
|
2129
|
+
handlers[event].splice(inArray(handlers[event], handler), 1);
|
2130
|
+
}
|
2131
|
+
});
|
2132
|
+
return this;
|
2133
|
+
},
|
2134
|
+
|
2135
|
+
/**
|
2136
|
+
* emit event to the listeners
|
2137
|
+
* @param {String} event
|
2138
|
+
* @param {Object} data
|
2139
|
+
*/
|
2140
|
+
emit: function(event, data) {
|
2141
|
+
// we also want to trigger dom events
|
2142
|
+
if (this.options.domEvents) {
|
2143
|
+
triggerDomEvent(event, data);
|
1326
2144
|
}
|
1327
|
-
break;
|
1328
2145
|
|
1329
|
-
|
1330
|
-
|
1331
|
-
if(
|
1332
|
-
|
2146
|
+
// no handlers, so skip it all
|
2147
|
+
var handlers = this.handlers[event];
|
2148
|
+
if (!handlers || !handlers.length) {
|
2149
|
+
return;
|
1333
2150
|
}
|
1334
2151
|
|
1335
|
-
|
1336
|
-
|
2152
|
+
data.type = event;
|
2153
|
+
data.preventDefault = function() {
|
2154
|
+
data.srcEvent.preventDefault();
|
2155
|
+
};
|
2156
|
+
|
2157
|
+
for (var i = 0, len = handlers.length; i < len; i++) {
|
2158
|
+
handlers[i](data);
|
2159
|
+
}
|
2160
|
+
},
|
2161
|
+
|
2162
|
+
/**
|
2163
|
+
* destroy the manager and unbinds all events
|
2164
|
+
* it doesn't unbind dom events, that is the user own responsibility
|
2165
|
+
*/
|
2166
|
+
destroy: function() {
|
2167
|
+
this.element && toggleCssProps(this, false);
|
2168
|
+
|
2169
|
+
this.handlers = {};
|
2170
|
+
this.session = {};
|
2171
|
+
this.input.destroy();
|
2172
|
+
this.element = null;
|
1337
2173
|
}
|
1338
|
-
}
|
1339
2174
|
};
|
1340
2175
|
|
1341
|
-
|
1342
|
-
|
1343
|
-
|
1344
|
-
|
2176
|
+
/**
|
2177
|
+
* add/remove the css properties as defined in manager.options.cssProps
|
2178
|
+
* @param {Manager} manager
|
2179
|
+
* @param {Boolean} add
|
2180
|
+
*/
|
2181
|
+
function toggleCssProps(manager, add) {
|
2182
|
+
var element = manager.element;
|
2183
|
+
var cssProps = manager.options.cssProps;
|
2184
|
+
|
2185
|
+
each(cssProps, function(value, name) {
|
2186
|
+
element.style[prefixed(element.style, name)] = add ? value : '';
|
2187
|
+
});
|
2188
|
+
|
2189
|
+
var falseFn = add && function() { return false; };
|
2190
|
+
if (cssProps.userSelect == 'none') { element.onselectstart = falseFn; }
|
2191
|
+
if (cssProps.userDrag == 'none') { element.ondragstart = falseFn; }
|
2192
|
+
}
|
2193
|
+
|
2194
|
+
/**
|
2195
|
+
* trigger dom event
|
2196
|
+
* @param {String} event
|
2197
|
+
* @param {Object} data
|
2198
|
+
*/
|
2199
|
+
function triggerDomEvent(event, data) {
|
2200
|
+
var gestureEvent = document.createEvent('Event');
|
2201
|
+
gestureEvent.initEvent(event, true, true);
|
2202
|
+
gestureEvent.gesture = data;
|
2203
|
+
data.target.dispatchEvent(gestureEvent);
|
2204
|
+
}
|
2205
|
+
|
2206
|
+
extend(Hammer, {
|
2207
|
+
INPUT_START: INPUT_START,
|
2208
|
+
INPUT_MOVE: INPUT_MOVE,
|
2209
|
+
INPUT_END: INPUT_END,
|
2210
|
+
INPUT_CANCEL: INPUT_CANCEL,
|
2211
|
+
|
2212
|
+
STATE_POSSIBLE: STATE_POSSIBLE,
|
2213
|
+
STATE_BEGAN: STATE_BEGAN,
|
2214
|
+
STATE_CHANGED: STATE_CHANGED,
|
2215
|
+
STATE_ENDED: STATE_ENDED,
|
2216
|
+
STATE_RECOGNIZED: STATE_RECOGNIZED,
|
2217
|
+
STATE_CANCELLED: STATE_CANCELLED,
|
2218
|
+
STATE_FAILED: STATE_FAILED,
|
2219
|
+
|
2220
|
+
DIRECTION_NONE: DIRECTION_NONE,
|
2221
|
+
DIRECTION_LEFT: DIRECTION_LEFT,
|
2222
|
+
DIRECTION_RIGHT: DIRECTION_RIGHT,
|
2223
|
+
DIRECTION_UP: DIRECTION_UP,
|
2224
|
+
DIRECTION_DOWN: DIRECTION_DOWN,
|
2225
|
+
DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
|
2226
|
+
DIRECTION_VERTICAL: DIRECTION_VERTICAL,
|
2227
|
+
DIRECTION_ALL: DIRECTION_ALL,
|
2228
|
+
|
2229
|
+
Manager: Manager,
|
2230
|
+
Input: Input,
|
2231
|
+
TouchAction: TouchAction,
|
2232
|
+
|
2233
|
+
Recognizer: Recognizer,
|
2234
|
+
AttrRecognizer: AttrRecognizer,
|
2235
|
+
Tap: TapRecognizer,
|
2236
|
+
Pan: PanRecognizer,
|
2237
|
+
Swipe: SwipeRecognizer,
|
2238
|
+
Pinch: PinchRecognizer,
|
2239
|
+
Rotate: RotateRecognizer,
|
2240
|
+
Press: PressRecognizer,
|
2241
|
+
|
2242
|
+
on: addEventListeners,
|
2243
|
+
off: removeEventListeners,
|
2244
|
+
each: each,
|
2245
|
+
merge: merge,
|
2246
|
+
extend: extend,
|
2247
|
+
inherit: inherit,
|
2248
|
+
bindFn: bindFn,
|
2249
|
+
prefixed: prefixed
|
2250
|
+
});
|
2251
|
+
|
2252
|
+
if (typeof define == TYPE_FUNCTION && define.amd) {
|
1345
2253
|
define(function() {
|
1346
|
-
|
2254
|
+
return Hammer;
|
1347
2255
|
});
|
1348
|
-
|
1349
|
-
}
|
1350
|
-
else if(typeof module === 'object' && typeof module.exports === 'object') {
|
2256
|
+
} else if (typeof module != TYPE_UNDEFINED && module.exports) {
|
1351
2257
|
module.exports = Hammer;
|
1352
|
-
|
1353
|
-
else {
|
2258
|
+
} else {
|
1354
2259
|
window.Hammer = Hammer;
|
1355
|
-
|
1356
|
-
|
2260
|
+
}
|
2261
|
+
|
2262
|
+
})(window);
|