Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
braverock
GitHub Repository: braverock/portfolioanalytics
Path: blob/master/sandbox/RFinance2014/libraries/frameworks/io2012/js/hammer.js
1436 views
1
/*
2
* Hammer.JS
3
* version 0.4
4
* author: Eight Media
5
* https://github.com/EightMedia/hammer.js
6
*/
7
function Hammer(element, options, undefined)
8
{
9
var self = this;
10
11
var defaults = {
12
// prevent the default event or not... might be buggy when false
13
prevent_default : false,
14
css_hacks : true,
15
16
drag : true,
17
drag_vertical : true,
18
drag_horizontal : true,
19
// minimum distance before the drag event starts
20
drag_min_distance : 20, // pixels
21
22
// pinch zoom and rotation
23
transform : true,
24
scale_treshold : 0.1,
25
rotation_treshold : 15, // degrees
26
27
tap : true,
28
tap_double : true,
29
tap_max_interval : 300,
30
tap_double_distance: 20,
31
32
hold : true,
33
hold_timeout : 500
34
};
35
options = mergeObject(defaults, options);
36
37
// some css hacks
38
(function() {
39
if(!options.css_hacks) {
40
return false;
41
}
42
43
var vendors = ['webkit','moz','ms','o',''];
44
var css_props = {
45
"userSelect": "none",
46
"touchCallout": "none",
47
"userDrag": "none",
48
"tapHighlightColor": "rgba(0,0,0,0)"
49
};
50
51
var prop = '';
52
for(var i = 0; i < vendors.length; i++) {
53
for(var p in css_props) {
54
prop = p;
55
if(vendors[i]) {
56
prop = vendors[i] + prop.substring(0, 1).toUpperCase() + prop.substring(1);
57
}
58
element.style[ prop ] = css_props[p];
59
}
60
}
61
})();
62
63
// holds the distance that has been moved
64
var _distance = 0;
65
66
// holds the exact angle that has been moved
67
var _angle = 0;
68
69
// holds the diraction that has been moved
70
var _direction = 0;
71
72
// holds position movement for sliding
73
var _pos = { };
74
75
// how many fingers are on the screen
76
var _fingers = 0;
77
78
var _first = false;
79
80
var _gesture = null;
81
var _prev_gesture = null;
82
83
var _touch_start_time = null;
84
var _prev_tap_pos = {x: 0, y: 0};
85
var _prev_tap_end_time = null;
86
87
var _hold_timer = null;
88
89
var _offset = {};
90
91
// keep track of the mouse status
92
var _mousedown = false;
93
94
var _event_start;
95
var _event_move;
96
var _event_end;
97
98
99
/**
100
* angle to direction define
101
* @param float angle
102
* @return string direction
103
*/
104
this.getDirectionFromAngle = function( angle )
105
{
106
var directions = {
107
down: angle >= 45 && angle < 135, //90
108
left: angle >= 135 || angle <= -135, //180
109
up: angle < -45 && angle > -135, //270
110
right: angle >= -45 && angle <= 45 //0
111
};
112
113
var direction, key;
114
for(key in directions){
115
if(directions[key]){
116
direction = key;
117
break;
118
}
119
}
120
return direction;
121
};
122
123
124
/**
125
* count the number of fingers in the event
126
* when no fingers are detected, one finger is returned (mouse pointer)
127
* @param event
128
* @return int fingers
129
*/
130
function countFingers( event )
131
{
132
// there is a bug on android (until v4?) that touches is always 1,
133
// so no multitouch is supported, e.g. no, zoom and rotation...
134
return event.touches ? event.touches.length : 1;
135
}
136
137
138
/**
139
* get the x and y positions from the event object
140
* @param event
141
* @return array [{ x: int, y: int }]
142
*/
143
function getXYfromEvent( event )
144
{
145
event = event || window.event;
146
147
// no touches, use the event pageX and pageY
148
if(!event.touches) {
149
var doc = document,
150
body = doc.body;
151
152
return [{
153
x: event.pageX || event.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && doc.clientLeft || 0 ),
154
y: event.pageY || event.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && doc.clientTop || 0 )
155
}];
156
}
157
// multitouch, return array with positions
158
else {
159
var pos = [], src;
160
for(var t=0, len=event.touches.length; t<len; t++) {
161
src = event.touches[t];
162
pos.push({ x: src.pageX, y: src.pageY });
163
}
164
return pos;
165
}
166
}
167
168
169
/**
170
* calculate the angle between two points
171
* @param object pos1 { x: int, y: int }
172
* @param object pos2 { x: int, y: int }
173
*/
174
function getAngle( pos1, pos2 )
175
{
176
return Math.atan2(pos2.y - pos1.y, pos2.x - pos1.x) * 180 / Math.PI;
177
}
178
179
/**
180
* trigger an event/callback by name with params
181
* @param string name
182
* @param array params
183
*/
184
function triggerEvent( eventName, params )
185
{
186
// return touches object
187
params.touches = getXYfromEvent(params.originalEvent);
188
params.type = eventName;
189
190
// trigger callback
191
if(isFunction(self["on"+ eventName])) {
192
self["on"+ eventName].call(self, params);
193
}
194
}
195
196
197
/**
198
* cancel event
199
* @param object event
200
* @return void
201
*/
202
203
function cancelEvent(event){
204
event = event || window.event;
205
if(event.preventDefault){
206
event.preventDefault();
207
}else{
208
event.returnValue = false;
209
event.cancelBubble = true;
210
}
211
}
212
213
214
/**
215
* reset the internal vars to the start values
216
*/
217
function reset()
218
{
219
_pos = {};
220
_first = false;
221
_fingers = 0;
222
_distance = 0;
223
_angle = 0;
224
_gesture = null;
225
}
226
227
228
var gestures = {
229
// hold gesture
230
// fired on touchstart
231
hold : function(event)
232
{
233
// only when one finger is on the screen
234
if(options.hold) {
235
_gesture = 'hold';
236
clearTimeout(_hold_timer);
237
238
_hold_timer = setTimeout(function() {
239
if(_gesture == 'hold') {
240
triggerEvent("hold", {
241
originalEvent : event,
242
position : _pos.start
243
});
244
}
245
}, options.hold_timeout);
246
}
247
},
248
249
250
// drag gesture
251
// fired on mousemove
252
drag : function(event)
253
{
254
// get the distance we moved
255
var _distance_x = _pos.move[0].x - _pos.start[0].x;
256
var _distance_y = _pos.move[0].y - _pos.start[0].y;
257
_distance = Math.sqrt(_distance_x * _distance_x + _distance_y * _distance_y);
258
259
// drag
260
// minimal movement required
261
if(options.drag && (_distance > options.drag_min_distance) || _gesture == 'drag') {
262
// calculate the angle
263
_angle = getAngle(_pos.start[0], _pos.move[0]);
264
_direction = self.getDirectionFromAngle(_angle);
265
266
// check the movement and stop if we go in the wrong direction
267
var is_vertical = (_direction == 'up' || _direction == 'down');
268
if(((is_vertical && !options.drag_vertical) || (!is_vertical && !options.drag_horizontal))
269
&& (_distance > options.drag_min_distance)) {
270
return;
271
}
272
273
_gesture = 'drag';
274
275
var position = { x: _pos.move[0].x - _offset.left,
276
y: _pos.move[0].y - _offset.top };
277
278
var event_obj = {
279
originalEvent : event,
280
position : position,
281
direction : _direction,
282
distance : _distance,
283
distanceX : _distance_x,
284
distanceY : _distance_y,
285
angle : _angle
286
};
287
288
// on the first time trigger the start event
289
if(_first) {
290
triggerEvent("dragstart", event_obj);
291
292
_first = false;
293
}
294
295
// normal slide event
296
triggerEvent("drag", event_obj);
297
298
cancelEvent(event);
299
}
300
},
301
302
303
// transform gesture
304
// fired on touchmove
305
transform : function(event)
306
{
307
if(options.transform) {
308
var scale = event.scale || 1;
309
var rotation = event.rotation || 0;
310
311
if(countFingers(event) != 2) {
312
return false;
313
}
314
315
if(_gesture != 'drag' &&
316
(_gesture == 'transform' || Math.abs(1-scale) > options.scale_treshold
317
|| Math.abs(rotation) > options.rotation_treshold)) {
318
_gesture = 'transform';
319
320
_pos.center = { x: ((_pos.move[0].x + _pos.move[1].x) / 2) - _offset.left,
321
y: ((_pos.move[0].y + _pos.move[1].y) / 2) - _offset.top };
322
323
var event_obj = {
324
originalEvent : event,
325
position : _pos.center,
326
scale : scale,
327
rotation : rotation
328
};
329
330
// on the first time trigger the start event
331
if(_first) {
332
triggerEvent("transformstart", event_obj);
333
_first = false;
334
}
335
336
triggerEvent("transform", event_obj);
337
338
cancelEvent(event);
339
340
return true;
341
}
342
}
343
344
return false;
345
},
346
347
348
// tap and double tap gesture
349
// fired on touchend
350
tap : function(event)
351
{
352
// compare the kind of gesture by time
353
var now = new Date().getTime();
354
var touch_time = now - _touch_start_time;
355
356
// dont fire when hold is fired
357
if(options.hold && !(options.hold && options.hold_timeout > touch_time)) {
358
return;
359
}
360
361
// when previous event was tap and the tap was max_interval ms ago
362
var is_double_tap = (function(){
363
if (_prev_tap_pos && options.tap_double && _prev_gesture == 'tap' && (_touch_start_time - _prev_tap_end_time) < options.tap_max_interval) {
364
var x_distance = Math.abs(_prev_tap_pos[0].x - _pos.start[0].x);
365
var y_distance = Math.abs(_prev_tap_pos[0].y - _pos.start[0].y);
366
return (_prev_tap_pos && _pos.start && Math.max(x_distance, y_distance) < options.tap_double_distance);
367
368
}
369
return false;
370
})();
371
372
if(is_double_tap) {
373
_gesture = 'double_tap';
374
_prev_tap_end_time = null;
375
376
triggerEvent("doubletap", {
377
originalEvent : event,
378
position : _pos.start
379
});
380
cancelEvent(event);
381
}
382
383
// single tap is single touch
384
else {
385
_gesture = 'tap';
386
_prev_tap_end_time = now;
387
_prev_tap_pos = _pos.start;
388
389
if(options.tap) {
390
triggerEvent("tap", {
391
originalEvent : event,
392
position : _pos.start
393
});
394
cancelEvent(event);
395
}
396
}
397
398
}
399
400
};
401
402
403
function handleEvents(event)
404
{
405
switch(event.type)
406
{
407
case 'mousedown':
408
case 'touchstart':
409
_pos.start = getXYfromEvent(event);
410
_touch_start_time = new Date().getTime();
411
_fingers = countFingers(event);
412
_first = true;
413
_event_start = event;
414
415
// borrowed from jquery offset https://github.com/jquery/jquery/blob/master/src/offset.js
416
var box = element.getBoundingClientRect();
417
var clientTop = element.clientTop || document.body.clientTop || 0;
418
var clientLeft = element.clientLeft || document.body.clientLeft || 0;
419
var scrollTop = window.pageYOffset || element.scrollTop || document.body.scrollTop;
420
var scrollLeft = window.pageXOffset || element.scrollLeft || document.body.scrollLeft;
421
422
_offset = {
423
top: box.top + scrollTop - clientTop,
424
left: box.left + scrollLeft - clientLeft
425
};
426
427
_mousedown = true;
428
429
// hold gesture
430
gestures.hold(event);
431
432
if(options.prevent_default) {
433
cancelEvent(event);
434
}
435
break;
436
437
case 'mousemove':
438
case 'touchmove':
439
if(!_mousedown) {
440
return false;
441
}
442
_event_move = event;
443
_pos.move = getXYfromEvent(event);
444
445
if(!gestures.transform(event)) {
446
gestures.drag(event);
447
}
448
break;
449
450
case 'mouseup':
451
case 'mouseout':
452
case 'touchcancel':
453
case 'touchend':
454
if(!_mousedown || (_gesture != 'transform' && event.touches && event.touches.length > 0)) {
455
return false;
456
}
457
458
_mousedown = false;
459
_event_end = event;
460
461
// drag gesture
462
// dragstart is triggered, so dragend is possible
463
if(_gesture == 'drag') {
464
triggerEvent("dragend", {
465
originalEvent : event,
466
direction : _direction,
467
distance : _distance,
468
angle : _angle
469
});
470
}
471
472
// transform
473
// transformstart is triggered, so transformed is possible
474
else if(_gesture == 'transform') {
475
triggerEvent("transformend", {
476
originalEvent : event,
477
position : _pos.center,
478
scale : event.scale,
479
rotation : event.rotation
480
});
481
}
482
else {
483
gestures.tap(_event_start);
484
}
485
486
_prev_gesture = _gesture;
487
488
// reset vars
489
reset();
490
break;
491
}
492
}
493
494
495
// bind events for touch devices
496
// except for windows phone 7.5, it doesnt support touch events..!
497
if('ontouchstart' in window) {
498
element.addEventListener("touchstart", handleEvents, false);
499
element.addEventListener("touchmove", handleEvents, false);
500
element.addEventListener("touchend", handleEvents, false);
501
element.addEventListener("touchcancel", handleEvents, false);
502
}
503
// for non-touch
504
else {
505
506
if(element.addEventListener){ // prevent old IE errors
507
element.addEventListener("mouseout", function(event) {
508
if(!isInsideHammer(element, event.relatedTarget)) {
509
handleEvents(event);
510
}
511
}, false);
512
element.addEventListener("mouseup", handleEvents, false);
513
element.addEventListener("mousedown", handleEvents, false);
514
element.addEventListener("mousemove", handleEvents, false);
515
516
// events for older IE
517
}else if(document.attachEvent){
518
element.attachEvent("onmouseout", function(event) {
519
if(!isInsideHammer(element, event.relatedTarget)) {
520
handleEvents(event);
521
}
522
}, false);
523
element.attachEvent("onmouseup", handleEvents);
524
element.attachEvent("onmousedown", handleEvents);
525
element.attachEvent("onmousemove", handleEvents);
526
}
527
}
528
529
530
/**
531
* find if element is (inside) given parent element
532
* @param object element
533
* @param object parent
534
* @return bool inside
535
*/
536
function isInsideHammer(parent, child) {
537
// get related target for IE
538
if(!child && window.event && window.event.toElement){
539
child = window.event.toElement;
540
}
541
542
if(parent === child){
543
return true;
544
}
545
546
// loop over parentNodes of child until we find hammer element
547
if(child){
548
var node = child.parentNode;
549
while(node !== null){
550
if(node === parent){
551
return true;
552
};
553
node = node.parentNode;
554
}
555
}
556
return false;
557
}
558
559
560
/**
561
* merge 2 objects into a new object
562
* @param object obj1
563
* @param object obj2
564
* @return object merged object
565
*/
566
function mergeObject(obj1, obj2) {
567
var output = {};
568
569
if(!obj2) {
570
return obj1;
571
}
572
573
for (var prop in obj1) {
574
if (prop in obj2) {
575
output[prop] = obj2[prop];
576
} else {
577
output[prop] = obj1[prop];
578
}
579
}
580
return output;
581
}
582
583
function isFunction( obj ){
584
return Object.prototype.toString.call( obj ) == "[object Function]";
585
}
586
}
587